Compare commits

...
415 Commits
Author SHA1 Message Date
Dan Mulloy 16f4870714 Update version to 5.0.0 for release 2023-05-12 10:10:49 -04:00
Jinyu Yu e77ed96957 Add 1.17 support to TinyProtocol (#194) 2023-05-06 17:26:49 -05:00
Hasan Demirtaş 46f6e76f91 Add BlockEntityType support. (#2111) 2023-05-06 17:18:09 -05:00
Lukas Alt 38bbd764cc Fixed creation of packet bundles (#2383) 2023-05-06 17:12:47 -05:00
Lukas Alt 2931af58db Added wrappers for MessageSignature (#2362) 2023-04-30 22:28:19 -05:00
Lukas Alt 08ea2da642 Improved Wrapping of PlayerInfoData and support chat session data (#2361)
* Improved Wrapping of PlayerInfoData and support chat session data
* added constructor for unambiguous creation of playerinfodata without signature
2023-04-29 19:49:51 +00:00
Lukas Alt 448e9369de JSON Parsing for WrappedServerPing and fixed modifying favicon (#2265)
* Fix WrappedServerPing access and ensure legacy compatability for JSON parsing
* added wrappers for mojang codecs and allow serializing server pings
2023-04-29 14:45:47 -05:00
Nassim Jahnke ac6f911f15 Fix bundle packet processing errors (#2339) 2023-04-15 18:27:52 -05:00
Dan Mulloy c69bcc36f5 Migrate to Gradle (#2319) 2023-04-15 16:09:15 -05:00
Lukas Alt 0ee93acd65 Fixed null packet handles in Bundle (#2328) 2023-04-11 08:13:03 -05:00
LOOHP d83dd9ab8a Get around expensive validate calls (#2318) 2023-04-09 16:40:57 -05:00
Pasqual Koschmieder ab32f938d7 resolve packet instance construction concurrency issue (#2312) 2023-04-07 20:57:49 -05:00
Dan Mulloy c7753a9d5b Better server ping concurrency
Addresses #2289
2023-04-03 22:55:32 -04:00
libraryaddict b51812655a Correct the version from 1.19.4 to 1.19.3 (#2299) 2023-04-03 18:27:38 -05:00
libraryaddict a910edc16b Fix WrappedRegistry for versions older than 1.19.4 (#2298) 2023-04-03 11:27:12 -05:00
Dan Mulloy fb2075b774 Fix exception for null sounds
Fixes #2276
2023-03-30 17:07:34 -05:00
Dan Mulloy 4b78bf6a34 Update dependencies to support Java 20
Fixes #2274
Fixes #2270
Fixes #2251
2023-03-28 12:40:32 -05:00
libraryaddict edce5b6d0f Fix /packet arguments, fixes #1847 (#2271)
Pretty simple, it's deleting the argument twice. Which means you have to use `/packet add add <remaining>`
2023-03-27 22:03:27 -05:00
Dan Mulloy 7217b11ba7 Fix reading class aliases
Fixes #2263
2023-03-26 14:09:45 -05:00
Dan Mulloy 18c2b389a4 Improve unit tests for server ping 2023-03-26 13:58:46 -05:00
Dan Mulloy 1912a9c871 Fix setting ping player sample in 1.19.4 2023-03-26 13:31:39 -05:00
Dan Mulloy 0c6fa46871 Optimize class lookups 2023-03-25 23:16:04 -05:00
Dan Mulloy df3b68df4c Some cleanup
Fixes super critical issue #2255
2023-03-25 21:45:29 -05:00
Lukas Alt aebefded86 Packet filtering for bundled packets in 1.19.4 (#2258)
Since Minecraft 1.19.4, the protocol supports bundling consecutive packets to ensure the client processes them in one tick. However, Packet Events are not called for the individual packets in such a bundle in the current dev build of ProtocolLib. For example, no packet events are currently sent for the ENTITY_METADATA packet when an entity is first spawned as the packet is bundled with the ENTITY_SPAWN packet. However, if the entity metadata is changed later on, the event will be called.
This PR proposes to fix this by unpacking the bundled packets and invoking the packet filtering for each packet.

I also want to briefly explain how the bundling works. A bundle starts with a PACKET_DELIMITER (0x00, net.minecraft.network.protocol.BundleDelimiterPacket) packet followed by all packets that should be bundled and finished with another PACKET_DELIMITER (0x00). Within the Netty pipeline, this sequence is transformed into one synthesized packet found in net.minecraft.network.protocol.game.ClientboundBundlePacket, which is essentially just a list of packets. At the stage at which ProtocolLib injects into the clientbound netty pipeline, this packet has not been unpacked yet. Thus, we need to handle the ClientboundBundlePacket, which unfortunately is not registered in ProtocolLib. The fact that two different classes map to the same packet currently requires a dirty remapping in the packet structure modifier.
2023-03-25 21:08:31 -05:00
Dan Mulloy 64e1e7de24 Fix some issues with server ping in 1.19.4 2023-03-23 20:23:10 -05:00
Dan Mulloy 05fa147b48 WIP: 1.19.4 update 2023-03-23 13:43:22 -05:00
libraryaddict bba534d694 Fix listeners removing each other (#2236) 2023-03-16 16:52:10 -05:00
Aseeefandaseef 4f0fe72add Use ConcurrentHashMap to improve performance (#2226)
* modified hashset collections for small performance gains

* reverted load factor modification since no noticeable performance gain was seen

---------

Co-authored-by: aseef <contact@aseef.dev>
2023-03-12 13:05:19 -05:00
armagidon-exception 365bb66d2a Fixed backwards compatibility with WrappedDataValue (#2172) 2023-02-16 02:31:26 +00:00
Richy 0a41cf9353 Add a soft-cache to getFields method (#2152)
* Added a soft-cache to getFields method

* Added cache purge on cache miss
2023-02-15 21:28:02 -05:00
Lennart Lösche d0b274249c Print exception in MethodHandleHelper (#2097)
print exception caused by xyz
2023-01-14 01:00:56 +00:00
Nassim Jahnke bdbbb7cb9c Fix SynchedEntityData$DataValue mapping (#2101)
Add SynchedEntityData$DataValue mapping
2023-01-13 19:56:51 -05:00
Dan Mulloy 531f28cbaf Fix sounds in 1.19.3
Fixes #2049
2023-01-10 16:36:01 -05:00
Mathéo Cimbaro 30b69d3ecf Added PacketContainer#getUUIDLists() (#2072)
The 1.19.3 PlayerListRemove is the first one using List<UUID>

Fixes #2048
2023-01-10 03:43:39 +00:00
Elioby 9d183e85d7 Force login disconnect packet to be run async (#2021) 2022-12-18 15:32:45 +00:00
Dan MulloyandPasqual Koschmieder eebb99fa37 1.19.3 update
Co-authored-by: Pasqual Koschmieder <pasqual.koschmieder@gmail.com>
2022-12-13 20:38:37 -05:00
Dan Mulloy 6aaf0ec26b Normalize line endings to LF 2022-12-07 13:52:09 -05:00
lennoxlotl 86e586da26 Fix not being able to read effects from RemoveEntityEffect packet (#1908)
fix being unable to read from RemoveEntityEffect due to wrong alias order
2022-11-26 22:54:33 +00:00
Nassim Jahnke 069783a353 Fix getEntityClass full qualifier (#1967) 2022-11-26 17:49:35 -05:00
opl- cc95e19ba4 Fix Magma support (#1953)
Tested with Magma v1.16.5 and Paper v1.18.2
2022-11-25 15:13:33 -05:00
Pasqual Koschmieder c31133c20f Improve performance by removing hash computations of packets (#1933) 2022-10-16 20:31:42 +00:00
caoli5288 69ae3656b5 Fix StreamSerializer backwards compatibility (#1868)
Fixes #1813
2022-10-16 18:37:08 +00:00
Pasqual Koschmieder 43145bd478 cleanup MonitorAdapter (#1831) 2022-08-24 00:53:55 +00:00
Nassim Jahnke b7c1e096c4 Support Mojang mapped servers (#1869) 2022-08-23 20:44:48 -04:00
Pasqual Koschmieder 6707c4811e only run inbound listeners on the main thread if requested (#1851) 2022-08-16 23:51:54 -04:00
Rodney 2092b8f48e Make getRandomKey() properly random (#1834) 2022-08-13 15:08:01 +00:00
Pasqual Koschmieder 20e73369fa mark some critical packets as forced-async (#1840) 2022-08-13 11:04:17 -04:00
Pasqual Koschmieder abc0db8281 remove off-event-loop logic from inbound packet listeners (#1836) 2022-08-12 17:29:07 -04:00
Pasqual Koschmieder 1beb95115f Improve cloning tests (#1822) 2022-08-12 02:07:38 +00:00
Pasqual Koschmieder 7fcfcdc365 fix fix of fix of getEntityFromId (#1827) 2022-08-11 22:04:27 -04:00
Pasqual Koschmieder 2be216899a use correct packet for chat extensions on 1.19+ (#1824) 2022-08-10 20:59:03 +00:00
Pasqual Koschmieder 8876ce323b add support for sync & async receiving listeners (#1815) 2022-08-10 20:50:33 +00:00
Pasqual Koschmieder 575174580e Small update for 1.19.2 (#1814) 2022-08-10 16:49:01 -04:00
Miklas 7fd4ec3172 Support for 1.18+ ClientboundLevelChunkWithLightPacket (#1592) 2022-08-06 18:52:42 -04:00
Pasqual Koschmieder a75d383001 fix injection exception when plugin is disabled (#1798) 2022-08-02 20:59:33 -04:00
Pasqual Koschmieder 7ddfd4f347 Fix & improve PacketContainer serialization & cloning (#1794) 2022-07-31 11:54:26 -04:00
Pasqual Koschmieder 7e137cbfc5 dependency updates (#1790) 2022-07-30 20:01:11 -04:00
Pasqual Koschmieder d40762e69d Update to 1.19.1 (#1699)
* add support for enforceSecureChat in ServerPing
* remove security exception check from test
2022-07-27 17:09:25 -04:00
Snowiiii ae19478007 Updated Dependencies (#1769) 2022-07-26 17:31:58 +00:00
games647 11a8184c3e Add StructureModifier for extracting the signature data in chat and login packets (#1742) 2022-07-26 13:29:34 -04:00
Pasqual Koschmieder c3dc00de05 fix two smaller issues (#1774) 2022-07-26 13:20:07 -04:00
Dan Mulloy 84cb541866 Merge branch 'pr-1659' 2022-07-24 11:07:39 -04:00
Photon-Github 624f6aaca6 Anonymous Class Replacement 2022-07-24 11:02:56 -04:00
Pasqual Koschmieder c5f0550953 Use MethodHandles for reflection (#1561)
* don't enforce async calls for thread-safe listeners (closes #1551)
* cleanups, remove structure compiling
* improve cloning a bit
* fix small issue in no-op structure modifier
* remove last usages of FieldUtils
* improve and fix equality check in container test
2022-07-24 10:16:05 -04:00
Pasqual Koschmieder 7f0bc7fd24 don't suppress deserialization issues (#1759) 2022-07-24 10:08:35 -04:00
Pasqual Koschmieder 96155b1065 Improve getEntityFromID (#1740) 2022-07-16 11:09:07 -04:00
Pasqual Koschmieder 23dac3287b detect server reloads and print a big warning message (#1733) 2022-07-16 11:00:01 -04:00
Pasqual Koschmieder 0bbbd961aa synchronize collections with possible concurrent accesses (#1723) 2022-07-07 07:51:06 +00:00
Pasqual Koschmieder dd19e1040a [ci skip] cleanup & improve readme a bit (#1724)
* [ci skip] cleanup & improve readme a bit

* [ci skip] improve code indention
2022-07-07 08:49:41 +01:00
Dan Mulloy e45c16d490 Missed a hyphen 2022-06-29 19:23:21 -04:00
Dan Mulloy a19959cea4 Update issue templates 2022-06-29 19:22:25 -04:00
Pasqual Koschmieder 4f18d37832 fix exceptions when reloading the server (#1689) 2022-06-29 19:08:14 -04:00
Photon-GitHubandDan Mulloy a2bf242097 Cleanup (#1603)
* Replace guava Charsets with StandardCharsets.

* Use try-with-resources

* Faster Util asList, inline getOnlinePlayers.

* Use direct ArrayList allocation.

* Use new instead of Lists#...

* Use new instead of Lists#...

* Faster looping.

* Use switch.

* Remove diamond operators.

* Use ArrayDeque instead of LinkedList.

* Actually conform to the documentation and always use an ArrayList as backing list.

* Potentially breaking change: Use switch instead of startsWith as this e.g. causes "n" to be interpreted as "names" and any future new commands starting with "n" will be ignored.

* Use addAll().

* Remove IntegerSet. Unused and also covered by fastutils IntSet.

* Much faster boolean parsing. Might have breaking changes as parameterName is now checked after the false block.

* Make most fields final, fix JavaDoc and remove diamond operators.

* Make fields final.

* Much cleaner getAllInterfaces() method.

Co-authored-by: Dan Mulloy <dev@dmulloy2.net>
2022-06-25 11:32:42 -04:00
Pasqual Koschmieder 4e105c59ed fix exceptions on older server versions (#1645) 2022-06-14 12:54:45 -04:00
Pasqual Koschmieder 59ca841ed5 correctly mark changed packet as processed (#1639) 2022-06-13 10:01:27 -04:00
Dan Mulloy 5e8f044a18 Run codeql analysis (#1630) 2022-06-11 13:03:03 -04:00
Pasqual Koschmieder 4db1e39ac7 fix packet listener calling when processed in event loop (#1621) 2022-06-11 12:36:46 -04:00
Pasqual Koschmieder e202503c09 allow setting if chat preview is enabled in WrappedServerPing (#1623) 2022-06-11 12:35:12 -04:00
Pasqual Koschmieder aed98abac6 fix registration of parameterized registry types (#1628) 2022-06-11 12:32:59 -04:00
Pasqual Koschmieder 250f94e9cd fix invalid packet id setting (#1629) 2022-06-11 12:30:38 -04:00
Pasqual Koschmieder d7bf43001f expand and document WrappedProfilePublicKey (#1618) 2022-06-11 11:58:08 -04:00
Pasqual Koschmieder 868b357527 add missing enum constants added in 1.19 (#1617) 2022-06-11 11:57:50 -04:00
Pasqual Koschmieder 9a609c2053 fix compile issues introduced by latest spigot api changes (#1622) 2022-06-11 11:57:25 -04:00
Pasqual Koschmieder f3acce99d8 improve and fix equality check in container test (#1614) 2022-06-08 16:33:22 -04:00
Pasqual Koschmieder 374e6cd5ee allow easier access to registries (#1613) 2022-06-08 16:32:52 -04:00
Pasqual Koschmieder 84a0b5ffdd allow easier access to converters (#1610) 2022-06-08 13:34:01 -04:00
Pasqual Koschmieder 4cc3957723 bring back raw text component creation (#1612) 2022-06-08 13:33:07 -04:00
Pasqual Koschmieder a0a5469988 Update to Minecraft 1.19 (#1601) 2022-06-07 21:24:31 -04:00
Photon-GitHub 240920d642 Diamond inference cleanup (#1593) 2022-05-25 23:30:08 -04:00
Pasqual Koschmieder 764195bd55 switch to a queue for queued scheduled packets (#1598) 2022-05-25 23:24:59 -04:00
Pasqual Koschmieder 7bfee67a29 fix packet listener invocation when packet is sent async (#1587) 2022-05-04 00:22:00 -04:00
Pasqual Koschmieder c87604cf0c don't enforce async calls for thread-safe listeners (#1555)
closes #1551
2022-03-26 13:00:22 -04:00
Pasqual Koschmieder 4096952c16 remove setCancelled / isCancelled from PacketInjector (#1553)
closes #1552
2022-03-26 13:00:12 -04:00
Dan Mulloy b4eff32213 Allow chat error messages to be disabled
Fixes #1549
2022-03-26 10:32:03 -04:00
Pasqual Koschmieder d361526371 Fix missing temp player in pre-join channel injectors (#1535)
Closes #1534
2022-03-12 14:33:24 -05:00
Pasqual Koschmieder 0d4e4c818f fix duplicate element creation when wrapping the bootstrap list (#1532)
closes #1530
2022-03-09 23:17:03 -05:00
Pasqual Koschmieder 073bfa2b86 Out/In bound protocol injection improvements (#1524)
* Clear up some stuff, fix location of wire packet encoder
* Ensure that the player injection cache is always up-to-date
* Make uninjection from a channel more reliable
* Don't schedule an empty runnable if there is no need to do that
* Remove unnecessary throw declarations from some methods
* Adjust uninjection to remove the injector reference as well
* improve channel future injection in network manager
2022-03-07 22:09:04 -05:00
Dan Mulloy f0059f39f6 Update version to 5.0.0-SNAPSHOT 2022-03-07 22:07:54 -05:00
Dan Mulloy 41bb4bacb2 Update version to 4.8.0 for release 2022-03-04 16:28:13 -05:00
Pasqual Koschmieder 9487c42985 Update to 1.18.2 (#1521) 2022-02-28 13:36:20 -05:00
derklaro 55f7b67f9d Update to junit 5, remove powermock 2022-02-25 00:11:26 -05:00
Pasqual Koschmieder e44d1e6051 Improve auto wrapper handling (#1518) 2022-02-25 00:02:11 -05:00
Pasqual Koschmieder baecaf4ca4 Remove unsafe reflection trying to access the modifiers field (#1517) 2022-02-24 23:56:22 -05:00
Pasqual Koschmieder 151d4a289f Prevent memory leaks with loads of PacketMarker objects (#1511)
closes #1509
2022-02-24 16:41:43 -05:00
Pasqual Koschmieder 1b4d79b302 Fix compile issues with the latest spigot version (#1510) 2022-02-24 16:18:56 -05:00
Pasqual Koschmieder 74833f8680 Correctly resolve protocol version of player (#1473) 2022-01-02 12:32:46 -06:00
Pasqual Koschmieder e77f8ced4c Update to 1.18.1 (#1461)
No changes required except for registering the protocol version
2021-12-19 20:50:22 -05:00
Dan Mulloy 1cd83e493d Update version to 4.8.0-SNAPSHOT 2021-11-30 14:14:50 -05:00
Pasqual Koschmieder 40b6c66491 Update to minecraft 1.18 (#1446) 2021-11-30 14:10:03 -05:00
LewUwU 723fc2c7ec Update NettyVersion.java (#1438)
The issue was caused due to a change in the pom of netty (for dependencies)

(#Generated by netty-parent/pom.xml
#Mon, 11 Oct 2021 13:22:16 +0200

netty-all.version=4.1.69.Final
netty-all.buildDate=2021-10-11 13\:22\:16 +0200
netty-all.commitDate=2021-10-11 09\:21\:07 +0000
netty-all.shortCommitHash=34a31522f0
netty-all.longCommitHash=34a31522f0145e2d434aaea2ef8ac5ed8d1a91a0
netty-all.repoStatus=dirty)

Is not more netty-common, now is netty-all
2021-11-14 12:09:34 -05:00
Dan Mulloy dd687ce175 Fix NPE in PlayerInfoData equals
Fixes #1400
Closes #1106
2021-11-14 00:41:20 -05:00
Nassim Jahnke 8774d87d59 Add 1.17 long jump pose (#1409) 2021-11-13 23:51:37 -05:00
Dan Mulloy 8361cf078f Fix GH actions build 2021-09-22 13:29:23 -04:00
Rothes dbedab0c14 Check entityID whether is negative or not. (#1362) 2021-09-22 10:51:47 -04:00
Pim van der Loos 5dda8c8ab1 Fix entity tracker methods for 1.17 (#1354)
- In 1.17, EntityTrackerEntries use ServerPlayerConnections instead of EntityPlayers as they did before. This caused the updateEntity to silently fail when removing the players from the trackedPlayers collection (of connections). This was resolved by retrieving the connections of the players before removing them from the list on 1.17+.
The getEntityTrackers method failed because it could not find any players for the same reason. This was resolved by retrieving the player from the connection before retrieving the Bukkit player from the EntityPlayer object when running on 1.17+.
- This fixes dmulloy2/ProtocolLib#1340
2021-07-26 23:51:24 -07:00
Dan Mulloy 4c0c18d7c6 Remove stack trace for list converter
Addresses #1331
2021-07-10 11:09:56 -04:00
Dan Mulloy 9c20455bf6 Update version to 4.7.1-SNAPSHOT 2021-07-10 11:08:35 -04:00
Dan Mulloy 8175443da0 Update to 4.7.0 2021-07-09 17:16:07 -04:00
Dan Mulloy 95a884974a Only set chat UUID if not already set
Fixes #1310
2021-07-08 23:33:17 -04:00
Dan Mulloy 153dd61994 Update to 1.17.1
Fixes #1315
2021-07-08 23:22:56 -04:00
Dan Mulloy 263ec8a36e Add FLYING packet back for backwards compat
Fixes #1275
2021-06-27 18:13:40 -04:00
Julian af46ba4d1a Fix support for Minecraft versions under the bee update (#1276) 2021-06-27 17:57:14 -04:00
Dan Mulloy dd85904642 Metrics: only track each plugin once 2021-06-26 21:22:52 -04:00
Dan Mulloy 7a8fce224e Set mc object regex in 1.17
Fixes #1271
2021-06-26 20:57:39 -04:00
Dan Mulloy 9ca7c91a76 Simplify packet registry 2021-06-24 17:14:00 -04:00
Dan Mulloy 466354cd2c Cache list constructors to save on exceptions 2021-06-24 17:13:40 -04:00
Dan Mulloy 99504dab8f Abstract out structure modifiers to allow internal structures to be read 2021-06-24 17:13:05 -04:00
Dan Mulloy 9b6603e2eb Use fuzzy reflection for effect types 2021-06-21 16:10:58 -04:00
Dan Mulloy 4fc476a125 Use right int 2 object map class for Paper
Addresses #1217
2021-06-20 12:42:41 -04:00
Dan Mulloy 583ed4b58a Update scoreboard team class
Addresses #1232
2021-06-20 12:35:03 -04:00
Dan Mulloy 638e81b9ce Merge branch 'master' of https://github.com/dmulloy2/ProtocolLib 2021-06-20 12:08:52 -04:00
Dan Mulloy 9a0703d05d Fix attribute builder in 1.17
Addresses #1224
2021-06-20 12:08:47 -04:00
Pasqual Koschmieder 9de096f783 Fix sdk command for jitpack builds (#1241) 2021-06-20 11:51:55 -04:00
Dan Mulloy c54a99945d Try fixing some of the unit tests 2021-06-19 22:45:17 -04:00
Pasqual Koschmieder 90a38cc15c Restore backwards compatibility (#1235)
* Remove usages of net.minecraft and craftbukkit
* Restore packet type backward compatibility (tested on 1.8)
* Re-add last removed packets
* Fix sub class naming for newer minecraft versions
2021-06-19 22:20:54 -04:00
Dan Mulloy 76930ae6e8 Remove travis
It's gonna be a lot of work to build, so we're just gonna use GitHub actions
2021-06-19 22:15:03 -04:00
Pasqual Koschmieder d745cfb184 Switch to GH-Workflow & use java 16 on jitpack builds (#1236)
* Add github actions workflow
* Add jitpack.yml to configure java version
2021-06-19 22:11:12 -04:00
Dan Mulloy 4be2bf38ff Switch from travis-ci.org to .com 2021-06-18 10:53:17 -04:00
Pasqual Koschmieder 0a32f24f08 Fix EntityUseAction & Hand read for minecraft 1.17 (#1230) 2021-06-18 10:47:48 -04:00
Dan Mulloy 1c2bc274dd Fix differently mapped fastutil classes in Paper
Fixes #1228
2021-06-16 18:22:53 -04:00
Dan Mulloy 9b54794f6b Fix cloning in 1.17
Addresses #1222
2021-06-15 23:58:40 -04:00
Dan Mulloy 42bec5a858 Fix entity trackers in 1.17
Addresses #1217
2021-06-15 18:39:34 -04:00
Dan Mulloy fa317c1167 Working on live server 2021-06-14 16:36:22 -04:00
Dan Mulloy cc17b9ee6e Finish up unit tests 2021-06-13 17:18:36 -04:00
Dan Mulloy 190ca1ff6a Almost there, /theoretically/ 2021-06-13 15:57:23 -04:00
Dan Mulloy c51930121f Fix a bunch more classpaths
Also worked on fixing some wrappers
2021-06-13 11:36:44 -04:00
Dan Mulloy c7a8d734d4 Fix some more class paths 2021-06-12 23:53:09 -04:00
Dan Mulloy b446cf2183 Update unit tests to 1.17, fix packet lookups 2021-06-12 15:28:01 -04:00
Dan Mulloy f11c246276 Deprecate SocketInjector#getSocket (see #1188) 2021-06-04 18:24:30 -04:00
Camotoy 6f91bd23de Remove inferences of SocketChannel presence in temporary player (#1188)
To note: this is yet another compatibility change for my Geyser work, but https://github.com/PaperMC/Paper/pull/5611 will also break without these changes as Unix domain sockets don't implement SocketChannel.

The temporary player method delegation directed the isOnline and getName methods to functions that require the channel to be an instance of SocketChannel, when this won't always be the case. To solve this, this PR redirects `getSocket().getRemoteSocketAddress()` to `injector.getAddress()` which returns the same value. To determine if the player is online, a new method is created in SocketInjector to determine if a connection is online (which also returns the same value as before this commit).
2021-06-04 18:21:48 -04:00
Pim van der Loos 45c293df7d Update ByteBuddy dependency (#1156)
- Updated ByteBuddy dependency to add support for Java 17 (introduced in 1.10.21) and to take advantage of any fixes and improvements made between versions.
2021-05-29 13:01:45 -04:00
Camotoy 5acdb2b3c5 Mark connectionHandler as sharable (#1179)
Fixes #1170.

The reasoning for this PR is largely outlined in the above issue. As the ChannelInboundHandlerAdapter has no private class, a race condition cannot occur and therefore marking the class as sharable should incur no cost.
2021-05-29 13:00:58 -04:00
Camotoy 72c1f3e26c Redirect #mcdevs link to libera.chat (#1181)
The #mcdevs IRC channel is in the process of migrating away from Freenode.
2021-05-29 12:59:26 -04:00
Minecrell b3ccf82597 WrappedServerPing: Properly translate MotD to components for RGB colors (#1152)
In previous Minecraft versions, using WrappedServerPing.setMotD(String)
behaved exactly like using Bukkit's ServerListPingEvent.setMotd(String).

With the addition of RGB colors in Minecraft 1.16, Spigot's
ServerListPingEvent was patched to translate the MotD string to the
chat component equivalent to make it possible to use RGB colors in MotDs.
In general, using raw legacy color codes (e.g. §c) within a (JSON) text
component tends to cause weird issues on newer Minecraft versions,
so it's better to translate them to the JSON equivalents on the server.

However, the WrappedServerPing implementation in ProtocolLib was never
updated with the same change, which makes it behave differently from
Spigot's ServerListPingEvent now. Using ServerListPingEvent RGB color
codes work, using ProtocolLib they do not work.

To fix this, this commit changes WrappedServerPing.setMotD(String) to
use the same method as Spigot for translating the legacy text to the
JSON/chat component equivalent.

This allows for example ServerListPlus to use Spigot's RGB color codes
(e.g. &x&7&9&b&8&f&fHello) without requiring any changes in ServerListPlus.
2021-05-02 17:43:32 -04:00
LewUwU 0c01a11755 Fixed protocol injection with latest netty on minecraft 1.11 and below (#1067) 2021-05-01 15:51:52 -04:00
Pim van der Loos 7ce3f471bf Add support for Java 16 (#1120)
Switched from the now-unavailable ClassLoader::defineClass method to the java.lang.invoke.MethodHandles.Lookup::defineClass. This is available on Java 9+.
2021-04-04 23:03:05 -04:00
Dan Mulloy 97972acee8 Add converter for game state ids
Fixes #1041
2021-02-27 15:38:05 -05:00
LOOHP a0bb11e1bd Add support for PaperMC's new Adventure API Component (#1103) 2021-02-27 14:15:43 -05:00
Dan Mulloy ba74fceed6 Update version to 4.6.1-SNAPSHOT 2021-02-27 14:15:01 -05:00
Dan Mulloy ab0faab396 Update version to 4.6.0 for release 2021-02-15 13:59:14 -05:00
Dan Mulloy 765fd9e987 Broaden throwable for misbehaving metrics plugins (#1078)
Fixes #1078
2021-02-03 09:39:04 -05:00
Dan Mulloy 8f7b530613 Tested with 1.16.5 2021-01-24 17:14:32 -05:00
Dan Mulloy 30fe81d366 Move getPacketDescription from the command to HexDumper as a public API
Related to filoghost/HolographicDisplays#385
2021-01-04 13:35:58 -05:00
PimvanderLoos b54dd49426 Replace CGLib with ByteBuddy (#984)
- The gclib dependency in the EnchancerFactory has been removed. All classes that used the actual factory part of it have been updated to use bytebuddy instead. This class will have to be removed at some point, but at the moment it is still used for accessing its class loader.
- Renamed EnhancerFactory to ByteBuddyFactory. All ByteBuddy actions should go through this now. Every subclass created here implements the ByteBuddyGenerated interface. This makes it possible to recognize classes generated using ByteBuddy (by default, it doesn't leave such a trace).
- Removed the method DefaultInstances#forEnhancer(Enhancer). This method isn't used anywhere; the last trace of usage of the method I could find was in 2013 (in the NetworkServerInjector). External plugins (I couldn't find any that used it), they should really have their own implementation, given that they already require an instance of an Enchancer. As such, I feel it is safe to remove rather than update it.
2021-01-04 00:24:34 -05:00
Dan Mulloy 26274fed52 ✨backwards compat✨ 2021-01-02 23:59:13 -05:00
Jan Lindner aa555f792e add sound converter for versions greater 1.16.4 (#1027) 2021-01-02 23:55:23 -05:00
Dan Mulloy 0d50905754 Merge branch 'master' of https://github.com/dmulloy2/ProtocolLib 2021-01-02 23:19:28 -05:00
Dan Mulloy b7132196fb Fix ChatExtensions exception
Fixes #1045
2021-01-02 23:19:20 -05:00
Dan Mulloy eb54b99856 Update funding info 2020-12-30 15:31:39 -06:00
Dan Mulloy 13b3d8679d Update ChatExtensions for 1.16.4
Fixes #1044
2020-12-27 00:04:11 -05:00
dependabot[bot] 1545a88e06 Bump junit from 4.13 to 4.13.1 (#989)
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-23 14:59:46 -05:00
Thibaut Gautier 2b22999b94 Fixed "protocollib_encoder" attempting to read non-nms packets (#959)
Added an override to the encoder as such would process any non-wire packets as NMS packets. In the case of the use of different APIs, such as Artemis Packet API, such would cause a conflict and would spit out casting exceptions. It is quite easy to resolve by exclusively accepting Wire Packets and packets which are assignable to the packet class. This solves the issue and tada happy ending.
2020-12-23 14:59:07 -05:00
PimvanderLoosandMark Vainomaa bbb053aa4e Fix Java 15 (#1025)
- Implemented a fix for the incompatibility with Java 15. This incompatibility was caused by the fact that the lambda generated in the NMS.NetworkManager is a hidden class in J15. Starting in Java 15, final fields in hidden classes can no longer be modified regardless of the 'accessible' flag. (see https://openjdk.java.net/jeps/371 "Using a hidden class", point 3). To circumvent this issue, this retrieves the data from the existing fields in the hidden class (a runnable or a callable) other than the packet. It then retrieves the constructor of the hidden class and instantiates it using the previously-retrieved data and the modified packet instance (this code is only used if the packet instance changed).
- Introduced a new ObjectReconstructor class that does all the fields/constructor discovering/accessing etc. The Runnable and Callable methods each get one instance of this class so that we can avoid having to get the fields/constructors and set them accessible every time we want to replace a packet.

Co-authored-by: Mark Vainomaa <mikroskeem@mikroskeem.eu>
2020-12-23 14:57:16 -05:00
Aurora 7bac4ec634 Update to 1.16.4 (#1007) 2020-11-09 15:11:48 -05:00
Dan Mulloy 4bc9e8b7b7 Fix a class cast exception with array wrappers
(Kinda surprised there isn't an issue to link here)
2020-10-19 16:30:42 -04:00
Dan Mulloy f381f0a2f7 Fix multi block change cloning
Fixes #990
2020-10-19 11:04:11 -04:00
Dan Mulloy 553e4b6813 Update to 1.16.3 2020-10-19 11:02:32 -04:00
Dan Mulloy bdaa843f2d Update to 1.16.2
Fixes #941
Fixes #944
Fixes #934
2020-08-24 13:19:49 -04:00
Dan Mulloy 13f5c14599 Fix bytecode breakage with StructureModifier
Fixes #935
2020-08-24 11:40:47 -04:00
Dan Mulloy b871eb3d54 Fix data watcher cloning in 1.16
Fixes #925
2020-08-06 14:26:27 -04:00
Dan Mulloy 12e3a895b3 Remove stray debug 2020-08-06 13:54:16 -04:00
Dan Mulloy 8c5fbe3298 Merge remote-tracking branch 'origin/master' 2020-08-04 19:04:22 -04:00
Dan Mulloy 0512215007 Add modifiers for world keys and moving block position
Fixes #906
Fixes #927
2020-08-04 19:03:59 -04:00
Tarrant 80f4c7b9a7 Support for MerchantRecipeList (#894)
* Added support for MerchantRecipeList found in OPEN_WINDOW_MERCHANT of MC versions 1.13+
2020-07-26 19:33:05 -04:00
Dan Mulloy 8c51b175c4 Merge remote-tracking branch 'origin/master' 2020-07-05 23:47:25 -04:00
Dan Mulloy 8d991ad5a7 Support dimensions in 1.16 (#893)
Fixes #893
2020-07-05 23:47:06 -04:00
Dan Mulloy f19bfc613e Add support for entity equipment pair in 1.16 2020-07-05 13:29:52 -04:00
Lewys Davies a7e702899a Implementing EntityPose (#873) 2020-06-30 23:18:04 -04:00
RERERE deb192b04d Support 1.16 PlayerDigType enumeration variable change (#889) 2020-06-30 23:16:52 -04:00
Dan Mulloy fbe46f7bac Force all chat packets to have empty UUID 2020-06-29 20:35:41 -04:00
Dan Mulloy fd93c1c553 Try to fix reload, give more detailed error (#874)
Fixes #874
2020-06-28 17:06:12 -04:00
Dan Mulloy b2f6a56843 Update tile entities for 1.16 (#880)
Ended up being simpler than I thought
2020-06-28 17:02:22 -04:00
Dan Mulloy 658da31d46 Initial support for 1.16 (#880)
Note that things may not initially work as expected. There are known issues (see #880) relating to dimensions, chat packets, and tile entities. There were also internal changes to attributes, though hopefully those are fixed. As always, report issues on GitHub.
2020-06-28 15:59:30 -04:00
xxDark 77feaa857e Cache perm gen bean (#876) 2020-06-23 16:26:30 -04:00
Niklas Seyfarth 5f204d798c StreamSerializer: Reduce duplicated code and allow (de)serialization to and from byte arrays. (#875) 2020-06-21 12:58:37 -04:00
NewbieOrange 5183bd53b5 Add MCP packet names for Bukkit-Forge hybrid server (#862)
Adds mappings to support MCP packets

Fixes #858
2020-06-16 13:35:38 -04:00
Dan Mulloy fdd30a7b87 Remove a bunch of legacy (<1.8) code
Shouldn't break any servers running 1.8+, but this version is all about code cleanup baby
2020-06-06 15:13:29 -04:00
Dan Mulloy 7ac4ac696f Fold BukkitExecutors into ProtocolLib (#721)
Fixes #721
2020-06-06 13:49:26 -04:00
Dan Mulloy c203fda391 Update version to 4.6.0-SNAPSHOT for development 2020-06-06 13:38:11 -04:00
Dan Mulloy 9a65ddbc43 Update version to 4.5.1 for release 2020-06-03 21:21:31 -04:00
Dan Mulloy 54c252a354 Improve WrappedDataWatcher hasIndex performance
Use the map to check for indices instead of getting the object

Addresses #850
2020-05-27 21:56:22 -04:00
Dan Mulloy e92abda187 Try to fix forge compatibility
Addresses #825
2020-05-27 21:41:56 -04:00
Dan Mulloy 3f7b7f4bb3 Make sure all Spigot forks are included for updater purposes
Addresses #835
2020-05-24 20:35:25 -04:00
Andrew Steinborn 944b3f8280 Cache PacketType#hashCode (#818)
This should result in a general performance improvement, as PacketType is often used as map keys across ProtocolLib.
2020-05-24 15:45:42 -04:00
Dan Mulloy b04fca8324 Fix packet interception error with ViaVersion (#724)
Thanks to @KennyTV and @MedicOP for their help in tracking this bug down. Essentially the decode method we were using could be different depending on when the player logged in, which clashed with PL's static handling of it.

Fixes #724
Fixes #791
Fixes #803
Fixes #811
Fixes #813
Fixes #819
...and probably some others...
2020-05-24 15:29:01 -04:00
Dan Mulloy bfa0eee91e Fix direction and dimension enums 2020-05-03 15:59:55 -04:00
Dan Mulloy 308e3d3417 Update to 1.15.2 2020-05-03 13:15:42 -04:00
Dan Mulloy 42e48aa9b8 Be more specific in which sendPacket method we choose
Related to PaperMC/Paper#3282
2020-05-03 12:44:59 -04:00
Dan Mulloy 18d0193288 Merge remote-tracking branch 'origin/master' 2020-04-30 18:47:06 -04:00
Dan Mulloy 0ab552a6ef Fix issue with legacy block data (#809)
Fixes #809
2020-04-30 18:46:29 -04:00
Dan Mulloy d9fef94bc6 Merge pull request #782 from bergerkiller/master
Replace ThreadLocal scheduleProcessPackets with queue, fixes #763
2020-03-03 09:15:24 -05:00
Irmo van den Berge ab5fb40f8f Replace ThreadLocal scheduleProcessPackets with queue, fixes #763 2020-03-01 13:07:14 +01:00
libraryaddict 4baa4aa724 AttributeModifier$Operation only to be fetched if viable (#773) 2020-02-18 16:12:24 -05:00
Dan Mulloy 7a4a285935 Add some more useful metrics 2020-01-11 13:49:36 -05:00
Dan Mulloy f38c393d6f Use new Spigot updater
Fixes #746
2020-01-11 13:47:49 -05:00
Dan Mulloy 22c2a4abcc Fix compatibility with CraftBukkit
Still recommend Spigot, for what it's worth
2019-12-26 11:39:23 -05:00
Dan Mulloy 1a434e9ea2 Try changing visibility on ProtocolLogger#init
Addresses #740
2019-12-26 11:34:33 -05:00
Dan Mulloy 25a5f1d07f Update version to 4.5.1-SNAPSHOT 2019-12-26 11:28:14 -05:00
Dan Mulloy e2f949c3a7 Update version to 4.5.0 for release 2019-12-24 15:00:28 -05:00
Dan Mulloy c3c59337ff Update to 1.15.1 2019-12-24 14:57:15 -05:00
Dan Mulloy fdf315ba7d Update URLs to HTTPS
Finally got around to it on my ci and nexus
2019-12-24 14:23:05 -05:00
Dan Mulloy f46bd56e8f Fix name of deprecated BED packet
Might fix compatibility with 1.8
2019-12-17 19:42:31 -05:00
Dan Mulloy b1efed0c0c Fix get entity by ID with Citizens and HD
Fixes #729
Addresses filoghost/HolographicDisplays#135
2019-12-17 19:41:53 -05:00
MiniDigger 524fb1ba68 Support multiple EntityTrackerEntryImpls, fixes #732 (#733) 2019-12-16 12:32:26 -05:00
Dan Mulloy 3ff2ccf1b3 Add build number back to version, update maven plugins 2019-12-15 14:50:39 -05:00
Dan Mulloy e915fd0f9a Fix player action enum compatibility with 1.14 and below 2019-12-15 14:50:02 -05:00
Dan Mulloy 6f8b2377b1 Fix last wrapped exception
Fixes #711
2019-12-14 16:54:18 -05:00
Dan Mulloy c893a3f11e Fix player action enum in 1.15 2019-12-13 18:57:21 -05:00
Dan Mulloy 73c71e0198 Update to Minecraft 1.15 2019-12-13 16:19:40 -05:00
Dan Mulloy bb305fdaad Remove legacy packet id 2019-12-11 21:37:21 -05:00
Dan Mulloy b4d4eb29af Fix error filtering reports
Fixes #718
2019-12-05 17:02:16 -05:00
Dan Mulloy 6d9fe45fb4 Tested with 1.14.4 2019-12-05 16:49:22 -05:00
Dan Mulloy b292c8485b Disable strict JavaDoc checking 2019-12-05 16:48:56 -05:00
Dan Mulloy 1e4da2c93b Update issue templates 2019-11-13 12:12:03 -05:00
Dan Mulloy 84e31d032b Fix villager data breaking versions below 1.14 2019-10-30 12:02:15 -04:00
Dan Mulloy 59c8c8c9db Fix WrappedBlockData deep clone
Fixes #661
Fixes #662
2019-10-29 17:01:15 -04:00
Dan Mulloy a76ceb94cc Add modifier for EntityTypes
Fixes #710
2019-10-29 16:52:30 -04:00
Dan Mulloy 9a108af219 Update dependency info in readme 2019-10-24 09:57:58 -04:00
Dan Mulloy 2f09dec1c5 Move back to Maven
Didn't really gain anything from Gradle but it broke some stuff
2019-10-23 19:17:37 -04:00
Felix Klauke 4c43f125f9 Use gradle wrapper instead of travis ci's inbuilt gradle version. (#706)
At the moment travis will always use its pre installed gradle version what leads to unpredictable build behaviour as the version can change at any time. As this project has already defined a gradle wrapper the CI should use it.
2019-10-16 12:07:07 -04:00
Dan Mulloy b5cff49358 Update issue templates 2019-10-01 17:28:36 -04:00
Dan Mulloy 446a1c8326 Clone OptionalInts too 2019-08-07 11:38:50 -04:00
Dan Mulloy 8c20d19339 Try to fix a null pointer
Addresses #665
2019-08-07 11:34:59 -04:00
Dan Mulloy 9dfffe5366 Move Travis to openjdk8
Something about Oracle and licenses. Who woulda thought
2019-08-03 13:10:20 -04:00
Dan Mulloy 19dd81ce94 Fix EntityUtilities updateEntity on 1.14
Fixes #651
2019-08-03 13:03:16 -04:00
Dan Mulloy dd9eac3d6d Add wrapper for villager data
Fixes #637
2019-08-03 12:35:58 -04:00
Dan Mulloy 916434251d Add support for long array nbt type
Fixes #648
2019-08-03 11:46:58 -04:00
Dan Mulloy 62e8d82e16 Update to 1.14.4, add BLOCK_BREAK packet
Also called Acknowledge Player Digging, but we go by what Spigot calls it
2019-08-03 11:41:55 -04:00
BuildTools d7be712959 Add api-version, tweak version pattern to support pre releases
Fixes #638
Fixes #636
Fixes #626
2019-06-23 15:28:40 +02:00
Dan Mulloy 490b1dadcb Fix logger debug when testing 2019-05-07 23:11:05 -04:00
Dan Mulloy 502c5960e3 Fix join/quit exceptions when wrappers break 2019-05-07 22:58:59 -04:00
Dan Mulloy d297e373b4 Specifically clone Spigot's chat components to fix stack overflow
Addresses #601
2019-05-06 20:32:20 -04:00
Dan Mulloy 9f5d95f617 Hopefully fix chat cloning issues
Addresses #601
2019-05-06 16:20:19 -04:00
Dan Mulloy b14b4cc345 Fix entity tracking in 1.14
Fixes #600
2019-05-04 22:41:32 -04:00
Dan Mulloy e41bb8753b Backport some fixes, fix version in plugin yml
Still need to get automatic filtering fixed
2019-05-04 01:45:41 -04:00
Dan Mulloy 565f169e94 Update to Minecraft 1.14 2019-05-03 23:22:44 -04:00
Dan Mulloy 7b7449ee15 Migrate to Gradle with a single module 2018-09-22 17:48:06 -04:00
Dan Mulloy 95bb026fa9 Update parent pom version
Can't wait to move to gradle with just one build file
2018-09-22 17:27:53 -04:00
Dan Mulloy 4713d320f8 Update version to 4.4.0 for release 2018-09-22 12:21:17 -04:00
Dan Mulloy 083c8e1481 Mark ParticleTypes as immutable, update tested version 2018-09-22 12:17:02 -04:00
Dan Mulloy 6ff6976e85 Merge branch 'master' of https://github.com/aadnk/ProtocolLib 2018-09-18 17:04:10 -04:00
Dan Mulloy 7e87c90b8d Add method to create wrapped block data from the spigot api
Fixes #525
2018-09-18 17:02:19 -04:00
Dan Mulloy 6fdf0f8255 Add modifier for dimensions 2018-09-18 17:01:55 -04:00
Dan Mulloy 7e12f57eda Update testing nms package 2018-09-15 14:48:40 -04:00
Dan Mulloy 776ec56a2d Update to 1.13.1, rework cloning, fix a particle NPE 2018-09-15 14:32:18 -04:00
Dan Mulloy 8ec31d83db Fix #508 2018-08-24 10:44:37 -04:00
diffblue-assistant 1373911ca2 Use try-with-resources (#158)
Replace try statements that close a resource in the finally block with a
try-with-resources statement [1].

This commit was created automatically by Diffblue refactorings (https://www.diffblue.com/).

[1] https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
2018-08-23 12:51:21 -04:00
Dan Mulloy cf5d9dfc7d Account for new network manager send packet method
Fixes #497
2018-08-03 22:18:32 -04:00
Dan Mulloy 9df0dd48e4 Fix backwards compat with nullable classes
Fixes #499
2018-08-03 22:07:25 -04:00
Dan Mulloy d112e9b1dc Remove extraneous debug
#494
2018-08-03 21:58:54 -04:00
Dan Mulloy 206dc30e17 How did those get there 2018-08-01 17:44:25 -04:00
Dan Mulloy 4c3a0afd7e Shiny new 1.13 particle support
Hopefully this doesn't break 1.8-1.12
2018-08-01 17:38:56 -04:00
Dan Mulloy 8d9bf7d91f Clone Java optionals
Fixes #498
2018-08-01 16:08:53 -04:00
Dan Mulloy 3ef537242f Fix some issues with item equality
Seems to be an internal Bukkit bug, hopefully nothing more serious
2018-07-24 17:05:30 -04:00
Dan Mulloy 976e4b4217 Update to 1.13 2018-07-23 19:11:07 -04:00
Dan Mulloy e827a3dd1f Fix class not found exception on versions < 1.13 2018-07-22 00:49:03 -04:00
Dan Mulloy 778031c105 Change block converter to use reflection again 2018-07-19 13:29:20 -04:00
Dan Mulloy 6c3654ef40 Update to 1.13-pre7 2018-07-19 13:13:06 -04:00
Dan Mulloy 5048f6f34b Try a less expensive map impl for packet markers
This might be a concurrency nightmare or a nice solution. I'm hopeful
based on initial testing
Addresses #489
2018-07-09 22:32:21 -04:00
Dan Mulloy c9dd6e2ba7 Merge remote-tracking branch 'origin/master' 2018-07-09 22:31:23 -04:00
Dan Mulloy fc76af9d56 Use broader catch statement when dealing with other metrics plugins
Fixes #488
2018-07-09 22:31:03 -04:00
dextonanderson aed2285bcb Properly cleanup ByteBuf in WirePacket (#487)
* Properly cleanup ByteBuf

* Release store ByteBuf also
2018-07-09 12:07:43 -04:00
Alvin-LB 1d11908af1 Make sure getMinecraftItemStack(ItemStack) handles Material.AIR properly (#485)
Fixes #483 and adds a test case to make sure the issue doesn't occur again.
2018-06-29 12:07:48 -04:00
Dan Mulloy 3ccc56fef9 Update repository links and versions 2018-06-12 14:45:36 -04:00
Dan Mulloy 39e0c974ee Merge branch 'master' of https://github.com/aadnk/ProtocolLib 2018-06-04 12:59:28 -04:00
Dan Mulloy e52c69ca22 Merge pull request #152 from MatrixTunnel/master
Fix maven/gradle repo url
2018-06-04 12:58:52 -04:00
MatrixTunnel d7d53bd905 Fix maven/gradle repo url 2018-06-02 21:26:31 -07:00
Dan Mulloy fb2d5016e5 Fix index out of bounds exception
Also make /all/ the IntelliJ code changes

Fixes #469
2018-05-31 20:15:47 -04:00
Dan Mulloy 015099b92e Clone NBT tags
Fixes #366
2018-05-29 10:53:07 -04:00
Dan Mulloy 7e8356f0bb Remove zip functionality from the updater
I don't upload zip files anyways
2018-05-29 10:41:13 -04:00
Dan Mulloy 72b8c4ced8 👀 2018-05-28 23:21:03 -04:00
Dan Mulloy 2d22df404a Fix Gradle builds 2018-05-28 23:19:56 -04:00
MiniDigger 0f13a32925 add paper to the "isUsingSpigot" check (#468)
partially fixes #467
will make protocollib use the spigot updater on paper servers.
2018-05-18 14:10:46 -04:00
Dan Mulloy 1ddb7a0014 Fix a null pointer with the Bukkit updater 2018-04-14 13:53:54 -04:00
Dan Mulloy f9376432c4 Metrics improvements 2017-10-08 16:02:10 -04:00
Dan Mulloy d195fb6990 Update version constants to 1.12.2 2017-10-08 15:56:13 -04:00
Dan Mulloy e4be102a77 Update to 1.12.2 2017-09-27 19:44:34 -04:00
Dan Mulloy 5f14ccdc34 Add back EnumConverters#getGenericConverter(Class)
It should work, but is deprecated and not recommended
Addresses #394
2017-09-27 19:43:38 -04:00
Mark Hughes 7573459a48 Update build information (#409)
- Switch to 4.3.0 version
- Include Gradle information
2017-09-27 19:19:42 -04:00
Dan Mulloy 01b68cf8e5 Revert unnecessarily breaking changes to converters
Fixes #394
2017-08-30 21:51:21 -04:00
Gabriele C f3b1a07189 Replace MCstats with bStats (#389)
#388
2017-08-19 11:25:54 -04:00
Dan Mulloy c70eb27653 Update ISSUE_TEMPLATE.md 2017-08-19 11:12:21 -04:00
Dan Mulloy 4483ce0d79 Add NbtFactory#fromItemOptional
This new method won't create tags if they don't already exists

Fixes #376
2017-08-13 23:00:51 -04:00
Dan Mulloy 1b23e9ec22 Rewrite the packet metadata API based on the NMS packet
Meta, if created, will persist between creation and listening.
2017-08-13 22:47:45 -04:00
Dan Mulloy 1f5692a0c7 Merge remote-tracking branch 'origin/master' 2017-08-04 14:01:24 -04:00
Dan Mulloy 0ac68dc179 Update to 1.12.1
Also contains changes for the packet wrapper update coming soonTM
2017-08-04 14:01:06 -04:00
dadus33 d891a057c0 Make #deserializeLegacy() able to access private members (#386) 2017-07-30 22:31:18 -04:00
Dan Mulloy 9e5bdf4124 Major rework of underlying conversion logic
This shouldn't affect plugins utlilizing the API, but will make updates and adding wrappers to PacketWrapper easier. In case you're wondering, major changes include:

- Added AutoWrapper
- Added Covnerters utility class
- Removed generic type parameter from EquivalentConverter
2017-07-24 14:15:56 -04:00
Dan Mulloy e1ea295600 Fix one of many issues with reloading
I'm only fixing this because it was relatively simple and I got a lot of reports for it. Seriously people, Stop. Reloading.

Fixes #373, fixes #368, fixes #360, fixes #359
2017-07-08 02:10:24 -04:00
蒋天蓓 59171c5443 Fix issue with component array converter (#372) 2017-07-07 11:42:17 -04:00
Dan Mulloy 501244cb3c Attempt to fix Jenkins build
Oddly enough it builds with no errors on my computer
2017-07-06 14:32:35 -04:00
Dan Mulloy 390c21f6d5 Add PacketEvent#isPlayerTemporary, check for player updates
This should address issues with temporary players by hopefully returning them less often
2017-07-06 13:56:30 -04:00
Dan Mulloy 20d78832b0 Update dependency versions to hopefully work with Java 9 2017-06-14 15:41:00 -04:00
Dan Mulloy adb3c5392c Release version 4.3.0 2017-06-14 14:30:46 -04:00
Dan Mulloy cd2f56d7c7 Update TinyProtocol to 1.12 2017-06-14 14:30:31 -04:00
Dan Mulloy 7c9bbc3f6c Add 1.12 protocol version 2017-06-09 15:08:04 -04:00
Dan Mulloy 52e396b25d Update to Spigot 1.12 2017-06-08 13:30:57 -04:00
Dan Mulloy aeb2cfb65e Update to 1.12-pre6
No real changes this time
2017-05-30 14:31:13 -04:00
Dan Mulloy 7f5ea6e493 Merge remote-tracking branch 'origin/master' 2017-05-29 22:14:28 -04:00
Dan Mulloy 1be94aad78 Rework packet type deprecation to actually work properly
Also fix compatibility with 1.8.0
2017-05-29 22:14:08 -04:00
dadus33 6bb169ac01 Fix 1.8 compatibility (#339)
Use proper method name ('setLenient' instead of 'setLenienent') in the deserializeLegacy method for 1.8 compatibility.
2017-05-29 12:59:01 -04:00
Dan Mulloy ea7900d64f Better ensure packet types are correct, deprecate legacy IDs 2017-05-24 17:20:27 -04:00
Dan Mulloy 123abdff30 Code cleanup, remove some ancient compatibility 2017-05-24 14:35:22 -04:00
Dan Mulloy 11247f1c01 Return an empty modifier if a class does not exist
Addresses #336
2017-05-23 18:52:28 -04:00
Dan Mulloy f093c9196d Ignore IntelliJ files 2017-05-19 11:17:31 -04:00
Dan Mulloy 81daa62071 Update to 1.12-pre5 2017-05-19 11:11:30 -04:00
Dan Mulloy f99b977888 Restore backwards compatibility, restructure poms 2017-05-18 12:02:47 -04:00
Dan Mulloy b00ae2ff15 Remove Java 8 warning
It's required, so the JVM will warn you for me
2017-05-14 21:46:18 -04:00
Dan Mulloy b4f9c501b3 1.12 hotfix: delay injection for network manager
Fixes #332, fixes #330
2017-05-14 21:37:36 -04:00
Dan Mulloy 1738c6f243 Stop testing with Java 7 2017-05-14 14:37:55 -04:00
Dan Mulloy abea7441a4 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-05-14 14:30:21 -04:00
Dan Mulloy 3887ba27d2 Update to the 1.12 pre-release
- Now requires Java 8
- Added new packets
- Added ChatType enum (replaces magic value)
- Missing protocol version
2017-05-14 14:28:56 -04:00
games647 76a3af84a1 Strictly match the sendPacket method
Related to #322
2017-04-27 20:49:37 -04:00
Dan Mulloy be5d42ee19 Minecraft 1.12 will use Java 8 2017-04-27 20:48:02 -04:00
Dan Mulloy 790711c16e Update version to 4.2.2-SNAPSHOT 2017-04-27 20:46:04 -04:00
Dan Mulloy 8c3127dee1 Release version 4.2.1 2017-04-09 11:56:53 -04:00
Dan Mulloy 5ec87c9d76 Fix issues with logging custom payload packets 2017-03-20 21:42:46 -04:00
Dan Mulloy 2205ce639e Merge pull request #305 from aadnk/master
Branch hiccup
2017-03-18 19:33:14 -04:00
Dan Mulloy 2844d75607 Merge pull request #127 from dmulloy2/master
Branch hiccup
2017-03-18 19:32:12 -04:00
Dan Mulloy a6810ea874 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-03-18 19:29:43 -04:00
Dan Mulloy aaf1af8e41 Don't worry if we can't determine the Java version 2017-03-18 19:29:16 -04:00
Dan Mulloy b8dd43aeed Merge pull request #126 from joeypoulissen/master
Added missing fallingdust particle as seen at http://wiki.vg/Protocol#Particle
2017-03-12 18:42:29 -04:00
Joey e959efc5af Added fallingdust particle as seen at http://wiki.vg/Protocol#Particle 2017-03-11 13:59:25 +01:00
Dan Mulloy 73ce01bbde Make packet logging more robust 2017-03-09 10:53:06 -05:00
Dan Mulloy b30628f6db Make hex dump more readable, match more packet names 2017-03-08 09:08:29 -05:00
Dan Mulloy 9289825d87 Add packet logging for debugging 2017-03-01 17:02:46 -05:00
Dan Mulloy 57cfbc4f81 Merge branch 'master' of git+ssh://git@github.com/dmulloy2/ProtocolLib 2017-02-04 12:12:54 -05:00
Dan Mulloy 3ab2026d62 People don't care if updater sites are down 2017-02-04 12:12:31 -05:00
Dan Mulloy e439c778a6 Use the correct JsonReader constructor in 1.8
Fixes aadnk#121
2017-01-01 16:13:47 -05:00
Dan Mulloy 98413c917e Update packet IDs 2016-12-22 12:24:18 -05:00
Dan Mulloy 540a5e53c4 Make Sound handling more robust
Fixes aadnk#119
2016-12-22 11:42:52 -05:00
Dan Mulloy 27047f83a5 Fix erroneous startup message 2016-12-22 11:33:55 -05:00
Dan Mulloy 222b535c15 Update version to 4.2.1-SNAPSHOT 2016-12-22 11:33:10 -05:00
Dan Mulloy 3f31af6b70 I'll look into signing releases later 2016-12-21 14:20:56 -05:00
Dan Mulloy b6a054b766 Release version 4.2.0 2016-12-21 14:16:57 -05:00
Dan Mulloy 3b7b692e80 Update to 1.11.2 2016-12-21 14:02:32 -05:00
Dan Mulloy c6788addeb Mark 1.11.1 as officially supported 2016-12-20 23:37:35 -05:00
Dan Mulloy 5b38bc7f38 Update to 1.11.1 2016-12-20 16:14:13 -05:00
Dan Mulloy 33bb12713d Restore ItemStack to CraftItemStack conversion 2016-12-16 12:36:35 -05:00
Dan Mulloy d175a7655e Refactor package caching, account for some edge cases 2016-11-29 19:39:11 -05:00
Dan Mulloy 4afb38aae1 Merge pull request #115 from Mast3rPlan/master
Fix performance issue
2016-11-27 12:41:02 -05:00
Emiel Tasseel 36b5670b3d Fix performance issue where classes that don't exist in the current environment endlessly get looked up over and over again 2016-11-26 12:50:27 +01:00
Dan Mulloy 7b61796506 Add mechanism to clone NonNullLists 2016-11-22 17:36:49 -05:00
Dan Mulloy 684b687e42 Rework ItemStack handling for 1.11
Note to developers: You should never supply a null ItemStack to any
method. ProtocolLib will never return a null ItemStack. Update your
plugins accordingly.
2016-11-20 16:29:23 -05:00
Dan Mulloy 7a9b9afe2e Add modifier for item lists 2016-11-19 19:18:35 -05:00
Dan Mulloy 7d382279ad Update to Minecraft 1.11 2016-11-16 21:56:46 -05:00
Dan Mulloy b4f5f18283 Strongly recommend Java 8
If you must use Java 7, set 'ignoreJava: true' in the config.
2016-10-14 19:28:17 -04:00
Dan Mulloy 17e150393a Avoid throwing errors with invalid entities 2016-10-14 19:20:47 -04:00
Dan Mulloy b5ecfb9c5f Update version to 4.2.0-SNAPSHOT 2016-10-14 19:20:16 -04:00
Dan Mulloy 31a0c9b10c Update version in plugin.yml 2016-09-18 15:27:49 -04:00
Dan Mulloy e4d95e09cf Update version to 4.1.0 for release 2016-09-18 15:27:01 -04:00
Dan Mulloy d173be0db5 Actually use UpdateSign when it exists 2016-09-18 14:42:40 -04:00
Dan Mulloy 051989a396 Merge pull request #265 from faradize/master
Added the ability of reading/writing lists of NBT classes!
2016-09-11 17:00:25 -04:00
Dan Mulloy 795c77e943 Fix minor example issue
Closes aadnk#113
2016-09-11 16:56:05 -04:00
Dan Mulloy 1de6bbc9dc Make sure we use the update sign packet when it exists
Should fix #263
2016-09-11 16:52:45 -04:00
verbuchselt 474c74b1f9 Added the ability of reading/writing lists of NBT classes!
Works perfectly with MapChunk-Packet's TileEntity-Data!
2016-09-07 11:37:30 +02:00
Dan Mulloy caaa57c915 Add basic TileEntityTest 2016-08-27 13:08:22 -04:00
Dan Mulloy 588f736348 Fix missing TileEntity write methods
Fixes #249
2016-08-27 13:07:49 -04:00
Dan Mulloy 828302150a Deal with plugins that create proxy player objects
As I was writing this I realized it sounded like Bungee support, but it
isn't
2016-08-13 13:19:13 -04:00
Dan Mulloy 144723af18 Ensure we're always using leniency with components
Fixes #252
2016-08-06 14:40:49 -04:00
Dan Mulloy 087913ab3a Standardize equals and hashCode for wrappers 2016-08-06 13:19:14 -04:00
Dan Mulloy 64942cbb5e Make much of ProtocolLib locale independent
Fixes #242
2016-07-25 14:09:23 -04:00
Dan Mulloy 97430a8e42 Add renamed OPEN_SIGN_ENTITY packet for backwards compat
Fixes #243
2016-07-25 13:47:56 -04:00
Dan Mulloy 1bdd200794 Try to fix a weird null case 2016-07-16 16:03:01 -04:00
Dan Mulloy 679035c3bf Fix an error with wire packets
Addresses #224
2016-07-16 15:46:23 -04:00
Dan Mulloy bd4352f041 Fix cloning data watchers
Addresses #224
2016-07-12 16:15:46 -04:00
Dan Mulloy 05ffeb8e7f Restore compatibility with 1.8
1.8.8 is still one of the most popular server versions. As a result,
many servers were not receiving important fixes and new APIs.

I was able to accomplish this with minimal bloat, making it worth it.

Upon release, 4.1.0 will become the recommended version for 1.8 thru the
current Spigot build.
2016-07-12 13:37:55 -04:00
Dan Mulloy 6c982a83f0 Cache null classes
This should prevent multiple unnecessary calls to the expensive
loadClass method

Fixes #236
2016-07-09 15:32:35 -04:00
Dan Mulloy 9860fbaec6 Fix API versioning 2016-07-07 12:19:10 -04:00
Dan Mulloy 26c29d9c85 Add conversion methods to WirePacket 2016-07-06 15:47:43 -04:00
Dan Mulloy 242b25eb1e Wrap all serializable objects in data watchers 2016-07-06 15:47:24 -04:00
Dan Mulloy c13b8d4fc6 Add modifiers for UUIDs and Directions 2016-07-06 15:46:42 -04:00
Dan Mulloy 12e9c08a1a Update version to 4.1.0-SNAPSHOT 2016-07-06 15:44:41 -04:00
Dan Mulloy 98a0528806 Update version to 4.0.2 for release 2016-07-06 13:42:57 -04:00
Dan Mulloy 015a6067fd Replace some debug info with the actual cause 2016-07-05 22:54:25 -04:00
Dan Mulloy e34105e754 Rework the component array modifier to work in 1.9.4 and up
Fixes #215
2016-07-03 13:12:24 -04:00
Dan Mulloy a995866d5b Add block data to the cloning mechanism 2016-06-30 12:34:13 -04:00
Dan Mulloy a88347dfe6 Add some more specific error messages for entity tracking
Addresses #229
2016-06-30 12:22:48 -04:00
Dan Mulloy 4f404a41c6 Update to 1.10.2
No changes, yay!
2016-06-26 23:06:00 -04:00
Dan Mulloy 88444dcf74 Fix another entity issue with Paper
Fixes #220
2016-06-20 20:50:26 -04:00
Dan Mulloy 4f5ab9eb8c Wrap and unwrap values in Optionals 2016-06-20 20:41:40 -04:00
Dan Mulloy f51427b63c Update to 1.10 2016-06-08 22:28:53 -04:00
Dan Mulloy cdcb6a6007 Update version to 4.0.2-SNAPSHOT 2016-06-08 11:12:46 -04:00
Dan Mulloy ac7e17d0a6 Update version to 4.0.1 for release 2016-05-22 17:31:42 -04:00
Dan Mulloy 402390866a Fix a few issues with debug logging
Also add a warning if the updater section is missing
2016-05-22 17:26:36 -04:00
Dan Mulloy cc362a1b7f Ensure the modification count is checked correctly
Fixes #202
2016-05-21 17:44:25 -04:00
Dan Mulloy 1ca7973b77 Correct line endings
Also ensure every file ends with a new line for consistency
2016-05-21 17:37:13 -04:00
Dan Mulloy 9ecbea68ca Disable auto download by default 2016-05-21 16:04:57 -04:00
Dan Mulloy e5bc602af3 Fix a few issues with the registry 2016-05-18 21:03:14 -04:00
Dan Mulloy a9aa406d29 Missing return statement
Also, why is git showing whole-class diffs? Must be the dang line
endings again
2016-05-18 20:48:59 -04:00
Dan Mulloy c1ae6f14fc Add some debug info for #202
Also /really/ make sure it's only called once
2016-05-18 20:47:08 -04:00
Dan Mulloy 411b7a2446 Catch the right exception 2016-05-17 17:04:19 -04:00
Dan Mulloy 869b457810 Fix a false positive with the updater 2016-05-16 17:43:58 -04:00
Dan Mulloy 4330bae47f Add some debug info for #208 2016-05-16 16:27:05 -04:00
Dan Mulloy 1c36c41050 Update version to 4.0.1-SNAPSHOT 2016-05-16 16:26:40 -04:00
470 changed files with 35196 additions and 41498 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+2 -2
View File
@@ -19,7 +19,7 @@ This page will detail specific things that must be done if you intend to contrib
#### Ready? Let's get started!
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [freenode.net](http://freenode.net)
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [libera.chat](http://libera.chat)
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
2. Search for usages of the now-defunct NMS package guard and change them.
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
@@ -35,4 +35,4 @@ and the release date.
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
If the protocol version has been incremented, add a new line to the map.
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
the build fails, debug!
the build fails, debug!
+2
View File
@@ -0,0 +1,2 @@
github: [dmulloy2]
custom: ["https://paypal.me/dmulloy2"]
+3 -1
View File
@@ -2,9 +2,11 @@ _Follow this template except for feature requests. Use pastebin when providing /
Make sure you've done the following:
- [ ] You're using the latest build for your server version
- [ ] This isn't fixed in a recent development build
- [ ] This isn't an issue caused by another plugin
- [ ] You've checked for duplicate issues
- [ ] You didn't use `/reload`
Debug paste link:
Description and relevant errors:
Description and relevant errors:
+29
View File
@@ -0,0 +1,29 @@
---
name: API question
about: Questions related to using ProtocolLib in your plugins
title: ''
labels: ''
assignees: ''
---
**Make sure you're doing the following**
- [ ] You're using the latest build for your server version
- [ ] This isn't an issue caused by another plugin
- [ ] You've checked for duplicate issues
- [ ] You didn't use `/reload`
**Describe the question**
A clear and concise description of what your question is.
**API method(s) used**
List what API method(s) you're using
**Expected behavior**
A clear and concise description of what you expected to happen.
**Code**
If applicable, add relevant code from your project
**Additional context**
Add any other context about the problem here.
+32
View File
@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
- [ ] This issue is not solved in a development build
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Version Info**
Provide your ProtocolLib install info with `/protocol dump` through pastebin.
**Additional context**
Add any other context about the problem here.
+22
View File
@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
- [ ] This feature is not currently present in a development build
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+28
View File
@@ -0,0 +1,28 @@
name: ProtocolLib full build lifecycle
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
cache: 'gradle'
- name: Run gradle build lifecycle
run: ./gradlew build shadowJar --no-daemon
- name: Upload plugin file
uses: actions/upload-artifact@v2
with:
name: ProtocolLib
path: build/libs/ProtocolLib.jar
+41
View File
@@ -0,0 +1,41 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '28 9 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
cache: 'gradle'
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: 'java'
- name: Run gradle build lifecycle
run: ./gradlew build -x test --no-daemon
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+7 -2
View File
@@ -8,7 +8,7 @@
.settings/
bin/
.idea/
.iml
*.iml
.ipr
.iws
@@ -175,4 +175,9 @@ pip-log.txt
.DS_Store
# Log4J files
logs/
logs/
.gradle/
build/
logs/
.java-version
-13
View File
@@ -1,13 +0,0 @@
language: java
jdk:
- oraclejdk8
- oraclejdk7
script: mvn clean test
#before_install: cd ProtocolLib
install: true
notifications:
email:
recipients:
- "dmulloy2@live.com"
on_success: change
on_failure: always
+74 -70
View File
@@ -1,32 +1,33 @@
# ProtocolLib [![Travis Status](https://travis-ci.org/dmulloy2/ProtocolLib.svg?branch=master)](https://travis-ci.org/dmulloy2/ProtocolLib)
# ProtocolLib
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modify Minecraft directly. A common technique is to modify incoming
and outgoing [packets](http://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modifying Minecraft directly. A common technique is to modify incoming
and outgoing [packets](https://www.wiki.vg/Protocol), or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
Critically, different plugins that use this approach may _hook_ into the same classes,
with unpredictable outcomes. More than often this causes plugins to crash, but it may also
lead to more subtle bugs.
Currently maintained by dmulloy2 on behalf of [Spigot](http://www.spigotmc.org/).
### Resources
* [Resource Page](http://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](http://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](http://ci.dmulloy2.net/job/ProtocolLib/javadoc)
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc/index.html)
### Compilation
ProtocolLib is built with Maven and requires Spigot and SpigotAPI, which can be found [here](http://www.spigotmc.org/wiki/buildtools/).
ProtocolLib is built with [Gradle](https://gradle.org/). If you have it installed, just run
`./gradlew build` in the root project folder. Other gradle targets you may be interested in
include `clean`, `test`, and `shadowJar`. `shadowJar` will create a jar with all dependencies
(ByteBuddy) included.
### A new API
__ProtocolLib__ attempts to solve this problem by providing a event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
__ProtocolLib__ attempts to solve this problem by providing an event API, much like Bukkit,
that allows plugins to monitor, modify, or cancel packets sent and received. But, more importantly,
the API also hides all the gritty, obfuscated classes with a simple index based read/write system.
You no longer have to reference CraftBukkit!
### Using ProtocolLib
@@ -35,7 +36,7 @@ To use this library, first add ProtocolLib.jar to your Java build path. Then, ad
as a dependency or soft dependency to your plugin.yml file like any other plugin:
````yml
depend: [ProtocolLib]
depend: [ ProtocolLib ]
````
You can also add ProtocolLib as a Maven dependency:
@@ -44,20 +45,32 @@ You can also add ProtocolLib as a Maven dependency:
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
...
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5</version>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0'
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
@@ -72,16 +85,14 @@ To listen for packets sent by the server to a client, add a server-side listener
````java
// Disable all sound effects
protocolManager.addPacketListener(
new PacketAdapter(this, ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Server.NAMED_SOUND_EFFECT
) {
@Override
public void onPacketSending(PacketEvent event) {
// Item packets (id: 0x29)
if (event.getPacketType() ==
PacketType.Play.Server.NAMED_SOUND_EFFECT) {
event.setCancelled(true);
}
event.setCancelled(true);
}
});
````
@@ -91,20 +102,19 @@ censor by listening for Packet3Chat events:
````java
// Censor
protocolManager.addPacketListener(new PacketAdapter(this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT) {
protocolManager.addPacketListener(new PacketAdapter(
this,
ListenerPriority.NORMAL,
PacketType.Play.Client.CHAT
) {
@Override
public void onPacketReceiving(PacketEvent event) {
if (event.getPacketType() == PacketType.Play.Client.CHAT) {
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);
if (message.contains("shit")
|| message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
if (message.contains("shit") || message.contains("damn")) {
event.setCancelled(true);
event.getPlayer().sendMessage("Bad manners!");
}
}
});
@@ -115,49 +125,43 @@ protocolManager.addPacketListener(new PacketAdapter(this,
Normally, you might have to do something ugly like the following:
````java
Packet60Explosion fakeExplosion = new Packet60Explosion();
fakeExplosion.a = player.getLocation().getX();
fakeExplosion.b = player.getLocation().getY();
fakeExplosion.c = player.getLocation().getZ();
fakeExplosion.d = 3.0F;
fakeExplosion.e = new ArrayList<Object>();
PacketPlayOutExplosion fakeExplosion = new PacketPlayOutExplosion(
player.getLocation().getX(),
player.getLocation().getY(),
player.getLocation().getZ(),
3.0F,
new ArrayList<>(),
new Vec3D(
player.getVelocity().getX() + 1,
player.getVelocity().getY() + 1,
player.getVelocity().getZ() + 1
)
);
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(fakeExplosion);
((CraftPlayer) player).getHandle().b.a(fakeExplosion);
````
But with ProtocolLib, you can turn that into something more manageable. Notice that
you don't have to create an ArrayList with this version:
But with ProtocolLib, you can turn that into something more manageable:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles().
write(0, player.getLocation().getX()).
write(1, player.getLocation().getY()).
write(2, player.getLocation().getZ());
fakeExplosion.getDoubles()
.write(0, player.getLocation().getX())
.write(1, player.getLocation().getY())
.write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);
fakeExplosion.getBlockPositionCollectionModifier().write(0, new ArrayList<>());
fakeExplosion.getVectors().write(0, player.getVelocity().add(new Vector(1, 1, 1)));
try {
protocolManager.sendServerPacket(player, fakeExplosion);
} catch (InvocationTargetException e) {
throw new RuntimeException(
"Cannot send packet " + fakeExplosion, e);
}
protocolManager.sendServerPacket(player, fakeExplosion);
````
### Compatibility
One of the main goals of this project was to achieve maximum compatibility with CraftBukkit. And the end
result is quite flexible. Aside from netty package changes, it should be resilient against future changes.
It's likely that I won't have to update ProtocolLib for anything but bug fixes and new features.
result is quite flexible. It's likely that I won't have to update ProtocolLib for anything but bug fixes
and new features.
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
### Incompatibility
The following plugins (to be expanded) are not compatible with ProtocolLib:
* TagAPI
@@ -14,8 +14,8 @@
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v3-SNAPSHOT</version>
<relativePath>../</relativePath>
<version>v4-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<repositories>
@@ -25,7 +25,7 @@
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
@@ -42,8 +42,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
@@ -77,20 +77,6 @@
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release-sign-artifacts</id>
@@ -114,4 +114,4 @@ public class ExamplePlugin extends JavaPlugin {
throw new RuntimeException("Cannot send packet.", e);
}
}
}
}
@@ -3,6 +3,8 @@ package com.comphenix.tinyprotocol;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -178,7 +180,32 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Retrieves a field with a given type and parameters. This is most useful
* when dealing with Collections.
*
* @param target the target class.
* @param fieldType Type of the field
* @param params Variable length array of type parameters
* @return The field
*
* @throws IllegalArgumentException If the field cannot be found
*/
public static Field getParameterizedField(Class<?> target, Class<?> fieldType, Class<?>... params) {
for (Field field : target.getDeclaredFields()) {
if (field.getType().equals(fieldType)) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
return field;
}
}
}
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
}
/**
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param className - lookup name of the class, see {@link #getClass(String)}.
* @param methodName - the method name, or NULL to skip.
@@ -191,7 +218,7 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
@@ -204,7 +231,7 @@ public final class Reflection {
}
/**
* Search for the first publically and privately defined method of the given name and parameter count.
* Search for the first publicly and privately defined method of the given name and parameter count.
*
* @param clazz - a class to start with.
* @param methodName - the method name, or NULL to skip.
@@ -301,6 +328,23 @@ public final class Reflection {
return clazz;
}
/**
* Retrieve a class from its full name with alternatives, without knowing its type on compile time.
* <p>
* This is useful when looking up fields by a NMS or OBC type.
* <p>
*
* @see {@link #getClass()} for more information.
* @param lookupName - the class name with variables.
* @param aliases - alternative names for this class.
* @return The class.
*/
public static Class<Object> getUntypedClass(String lookupName, String... aliases) {
@SuppressWarnings({ "rawtypes", "unchecked" })
Class<Object> clazz = (Class) getClass(lookupName, aliases);
return clazz;
}
/**
* Retrieve a class from its full name.
* <p>
@@ -333,6 +377,61 @@ public final class Reflection {
return getCanonicalClass(expandVariables(lookupName));
}
/**
* Retrieve the first class that matches the full class name.
* <p>
* Strings enclosed with curly brackets - such as {TEXT} - will be replaced according to the following table:
* <p>
* <table border="1">
* <tr>
* <th>Variable</th>
* <th>Content</th>
* </tr>
* <tr>
* <td>{nms}</td>
* <td>Actual package name of net.minecraft.server.VERSION</td>
* </tr>
* <tr>
* <td>{obc}</td>
* <td>Actual pacakge name of org.bukkit.craftbukkit.VERSION</td>
* </tr>
* <tr>
* <td>{version}</td>
* <td>The current Minecraft package VERSION, if any.</td>
* </tr>
* </table>
*
* @param lookupName - the class name with variables.
* @param aliases - alternative names for this class.
* @return Class object.
* @throws RuntimeException If we are unable to find any of the given classes.
*/
public static Class<?> getClass(String lookupName, String... aliases) {
try {
// Try the main class first
return getClass(lookupName);
} catch (RuntimeException e) {
Class<?> success = null;
// Try every alias too
for (String alias : aliases) {
try {
success = getClass(alias);
break;
} catch (RuntimeException e1) {
// e1.printStackTrace();
}
}
if (success != null) {
return success;
} else {
// Hack failed
throw new RuntimeException(String.format("Unable to find %s (%s)", lookupName, String.join(",", aliases)));
}
}
}
/**
* Retrieve a class in the net.minecraft.server.VERSION.* package.
*
@@ -400,4 +499,4 @@ public final class Reflection {
matcher.appendTail(output);
return output.toString();
}
}
}
@@ -9,6 +9,7 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,21 +45,25 @@ import com.mojang.authlib.GameProfile;
public abstract class TinyProtocol {
private static final AtomicInteger ID = new AtomicInteger(0);
// Required Minecraft classes
private static final Class<?> entityPlayerClass = Reflection.getClass("{nms}.EntityPlayer", "net.minecraft.server.level.EntityPlayer");
private static final Class<?> playerConnectionClass = Reflection.getClass("{nms}.PlayerConnection", "net.minecraft.server.network.PlayerConnection");
private static final Class<?> networkManagerClass = Reflection.getClass("{nms}.NetworkManager", "net.minecraft.network.NetworkManager");
// Used in order to lookup a channel
private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle");
private static final FieldAccessor<Object> getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class);
private static final FieldAccessor<Object> getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class);
private static final FieldAccessor<Channel> getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0);
private static final FieldAccessor<?> getConnection = Reflection.getField(entityPlayerClass, null, playerConnectionClass);
private static final FieldAccessor<?> getManager = Reflection.getField(playerConnectionClass, null, networkManagerClass);
private static final FieldAccessor<Channel> getChannel = Reflection.getField(networkManagerClass, Channel.class, 0);
// Looking up ServerConnection
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection");
private static final Class<Object> minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer", "net.minecraft.server.MinecraftServer");
private static final Class<Object> serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection", "net.minecraft.server.network.ServerConnection");
private static final FieldAccessor<Object> getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0);
private static final FieldAccessor<Object> getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0);
private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart");
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
// Speedup channel lookup
@@ -72,7 +77,7 @@ public abstract class TinyProtocol {
private List<Object> networkManagers;
// Injected channel handlers
private List<Channel> serverChannels = Lists.newArrayList();
private List<Channel> serverChannels = new ArrayList<>();
private ChannelInboundHandlerAdapter serverChannelHandler;
private ChannelInitializer<Channel> beginInitProtocol;
private ChannelInitializer<Channel> endInitProtocol;
@@ -105,8 +110,7 @@ public abstract class TinyProtocol {
} catch (IllegalArgumentException ex) {
// Damn you, late bind
plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind.");
// Damn you, late bind
new BukkitRunnable() {
@Override
public void run() {
@@ -129,7 +133,7 @@ public abstract class TinyProtocol {
synchronized (networkManagers) {
// Stop injecting channels
if (!closed) {
injectChannelInternal(channel);
channel.eventLoop().submit(() -> injectChannelInternal(channel));
}
}
} catch (Exception e) {
@@ -200,8 +204,22 @@ public abstract class TinyProtocol {
Object serverConnection = getServerConnection.get(mcServer);
boolean looking = true;
try {
Field field = Reflection.getParameterizedField(serverConnectionClass, List.class, networkManagerClass);
field.setAccessible(true);
networkManagers = (List<Object>) field.get(serverConnection);
} catch (Exception ex) {
plugin.getLogger().info("Encountered an exception checking list fields" + ex);
MethodInvoker method = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass);
networkManagers = (List<Object>) method.invoke(null, serverConnection);
}
if (networkManagers == null) {
throw new IllegalArgumentException("Failed to obtain list of network managers");
}
// We need to synchronize against this list
networkManagers = (List<Object>) getNetworkMarkers.invoke(null, serverConnection);
createServerChannelHandler();
// Find the correct list, or implicitly throw an exception
@@ -256,13 +274,12 @@ public abstract class TinyProtocol {
* <p>
* Note that this is not executed on the main thread.
*
* @param reciever - the receiving player, NULL for early login/status packets.
* @param receiver - the receiving player, NULL for early login/status packets.
* @param channel - the channel that received the packet. Never NULL.
* @param remoteAddress - remote address of the sending client. Never NULL.
* @param packet - the packet being sent.
* @return The packet to send instead, or NULL to cancel the transmission.
*/
public Object onPacketOutAsync(Player reciever, Channel channel, Object packet) {
public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) {
return packet;
}
@@ -283,7 +300,7 @@ public abstract class TinyProtocol {
/**
* Send a packet to a particular player.
* <p>
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the destination player.
* @param packet - the packet to send.
@@ -295,7 +312,7 @@ public abstract class TinyProtocol {
/**
* Send a packet to a particular client.
* <p>
* Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet to send.
@@ -307,7 +324,7 @@ public abstract class TinyProtocol {
/**
* Pretend that a given packet has been received from a player.
* <p>
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param player - the player that sent the packet.
* @param packet - the packet that will be received by the server.
@@ -319,7 +336,7 @@ public abstract class TinyProtocol {
/**
* Pretend that a given packet has been received from a given client.
* <p>
* Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet.
* Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet.
*
* @param channel - client identified by a channel.
* @param packet - the packet that will be received by the server.
@@ -353,7 +370,7 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
* @param player - the channel to inject.
* @param channel - the channel to inject.
* @return The intercepted channel, or NULL if it has already been injected.
*/
public void injectChannel(Channel channel) {
@@ -363,7 +380,7 @@ public abstract class TinyProtocol {
/**
* Add a custom channel handler to the given channel.
*
* @param player - the channel to inject.
* @param channel - the channel to inject.
* @return The packet interceptor.
*/
private PacketInterceptor injectChannelInternal(Channel channel) {
@@ -521,4 +538,4 @@ public abstract class TinyProtocol {
}
}
}
}
}
+114
View File
@@ -0,0 +1,114 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
group = 'com.comphenix.protocol'
version = '5.0.0'
description = 'Provides access to the Minecraft protocol'
def isSnapshot = version.endsWith('-SNAPSHOT')
repositories {
// mavenLocal() // can speed up build, but may fail in CI
mavenCentral()
maven {
url 'https://repo.dmulloy2.net/repository/public/'
}
maven {
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
}
maven {
url 'https://libraries.minecraft.net/'
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.3'
compileOnly 'org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.0.23.Final'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.13.0'
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-inline:4.11.0'
testImplementation 'io.netty:netty-common:4.1.77.Final'
testImplementation 'io.netty:netty-transport:4.1.77.Final'
testImplementation 'org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.13.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.13.1'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
withJavadocJar()
withSourcesJar()
}
shadowJar {
dependencies {
include(dependency('net.bytebuddy:byte-buddy:.*'))
}
relocate 'net.bytebuddy', 'com.comphenix.net.bytebuddy'
archiveFileName = 'ProtocolLib.jar'
}
test {
useJUnitPlatform()
}
processResources {
def includeBuild = isSnapshot && System.getenv('BUILD_NUMBER')
def fullVersion = includeBuild
? version + '-' + System.getenv('BUILD_NUMBER')
: version
eachFile { expand(['version': fullVersion]) }
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
afterEvaluate {
artifactId = 'ProtocolLib'
}
}
}
repositories {
maven {
url isSnapshot
? 'https://repo.dmulloy2.net/repository/snapshots/'
: 'https://repo.dmulloy2.net/repository/releases/'
credentials {
username project.nexusUsername
password project.nexusPassword
}
}
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
+2
View File
@@ -0,0 +1,2 @@
nexusUsername=""
nexusPassword=""
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+244
View File
@@ -0,0 +1,244 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+92
View File
@@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+5
View File
@@ -0,0 +1,5 @@
before_install:
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk update
- sdk install java 17.0.4-tem
- sdk use java 17.0.4-tem
-245
View File
@@ -1,245 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
<packaging>jar</packaging>
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.version}${project.build.number}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>ProtocolLib</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
<dependencies>
<!-- Included with Minecraft
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,114 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
/**
* Represents a object capable of sending or receiving packets.
*
* @author Kristian
*/
public interface PacketStream {
/**
* Send a packet to the given player.
* @param receiver - the reciever.
* @param packet - packet to send.
* @throws InvocationTargetException - if an error occured when sending the packet.
*/
public void sendServerPacket(Player receiver, PacketContainer packet)
throws InvocationTargetException;
/**
* Send a packet to the given player.
* @param receiver - the reciever.
* @param packet - packet to send.
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
* @throws InvocationTargetException - if an error occured when sending the packet.
*/
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters)
throws InvocationTargetException;
/**
* Send a packet to the given player.
* @param receiver - the receiver.
* @param packet - packet to send.
* @param marker - the network marker to use.
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
* @throws InvocationTargetException - if an error occured when sending the packet.
*/
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters)
throws InvocationTargetException;
/**
* Send a wire packet to the given player.
* @param receiver - the receiver.
* @param id - packet id.
* @param bytes - packet bytes.
* @throws InvocationTargetException if an error occured when sending the packet.
*/
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException;
/**
* Send a wire packet to the given player.
* @param receiver - the receiver.
* @param packet - packet to send.
* @throws InvocationTargetException if an error occured when sending the packet.
*/
public void sendWirePacket(Player receiver, WirePacket packet) throws InvocationTargetException;
/**
* Simulate recieving a certain packet from a given player.
* @param sender - the sender.
* @param packet - the packet that was sent.
* @throws InvocationTargetException If the reflection machinery failed.
* @throws IllegalAccessException If the underlying method caused an error.
*/
public void recieveClientPacket(Player sender, PacketContainer packet)
throws IllegalAccessException, InvocationTargetException;
/**
* Simulate recieving a certain packet from a given player.
* @param sender - the sender.
* @param packet - the packet that was sent.
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
* @throws InvocationTargetException If the reflection machinery failed.
* @throws IllegalAccessException If the underlying method caused an error.
*/
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters)
throws IllegalAccessException, InvocationTargetException;
/**
* Simulate recieving a certain packet from a given player.
* @param sender - the sender.
* @param packet - the packet that was sent.
* @param marker - the network marker to use.
* @param filters - whether or not to invoke any packet filters below {@link ListenerPriority#MONITOR}.
* @throws InvocationTargetException If the reflection machinery failed.
* @throws IllegalAccessException If the underlying method caused an error.
*/
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters)
throws IllegalAccessException, InvocationTargetException;
}
File diff suppressed because it is too large. Load diff
@@ -1,300 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import com.comphenix.protocol.reflect.IntEnum;
/**
* List of known packet IDs since 1.3.2.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public final class Packets {
/**
* The highest possible packet ID. It's unlikely that this value will ever change.
*/
public static final int MAXIMUM_PACKET_ID = 255;
/**
* The maximum number of unique packet IDs. It's unlikely this will ever change.
*/
public static final int PACKET_COUNT = 256;
/**
* List of packets sent only by the server.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public static final class Server extends IntEnum {
/**
* The singleton instance. Can also be retrieved from the parent class.
*/
private static Server INSTANCE = new Server();
public static final int KEEP_ALIVE = 0;
public static final int LOGIN = 1;
public static final int CHAT = 3;
public static final int UPDATE_TIME = 4;
public static final int ENTITY_EQUIPMENT = 5;
public static final int SPAWN_POSITION = 6;
public static final int UPDATE_HEALTH = 8;
public static final int RESPAWN = 9;
public static final int FLYING = 10;
public static final int PLAYER_POSITION = 11;
public static final int PLAYER_LOOK = 12;
public static final int PLAYER_LOOK_MOVE = 13;
/**
* Made bi-directional in 1.4.6.
*/
public static final int BLOCK_ITEM_SWITCH = 16;
public static final int ENTITY_LOCATION_ACTION = 17;
public static final int ARM_ANIMATION = 18;
public static final int NAMED_ENTITY_SPAWN = 20;
/**
* Removed in 1.4.6 and replaced with VEHICLE_SPAWN.
* @see <a href="http://www.wiki.vg/Protocol_History#2012-12-20">Protocol History - MinecraftCoalition</a>
*/
@Deprecated()
public static final int PICKUP_SPAWN = 21;
public static final int COLLECT = 22;
public static final int VEHICLE_SPAWN = 23;
public static final int MOB_SPAWN = 24;
public static final int ENTITY_PAINTING = 25;
public static final int ADD_EXP_ORB = 26;
public static final int ENTITY_VELOCITY = 28;
public static final int DESTROY_ENTITY = 29;
public static final int ENTITY = 30;
public static final int REL_ENTITY_MOVE = 31;
public static final int ENTITY_LOOK = 32;
public static final int REL_ENTITY_MOVE_LOOK = 33;
public static final int ENTITY_TELEPORT = 34;
public static final int ENTITY_HEAD_ROTATION = 35;
public static final int ENTITY_STATUS = 38;
public static final int ATTACH_ENTITY = 39;
/**
* Sent when an entities DataWatcher is updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int ENTITY_METADATA = 40;
public static final int MOB_EFFECT = 41;
public static final int REMOVE_MOB_EFFECT = 42;
public static final int SET_EXPERIENCE = 43;
public static final int UPDATE_ATTRIBUTES = 44;
public static final int MAP_CHUNK = 51;
public static final int MULTI_BLOCK_CHANGE = 52;
public static final int BLOCK_CHANGE = 53;
public static final int PLAY_NOTE_BLOCK = 54;
public static final int BLOCK_BREAK_ANIMATION = 55;
public static final int MAP_CHUNK_BULK = 56;
public static final int EXPLOSION = 60;
public static final int WORLD_EVENT = 61;
public static final int NAMED_SOUND_EFFECT = 62;
public static final int WORLD_PARTICLES = 63;
public static final int BED = 70;
public static final int WEATHER = 71;
public static final int OPEN_WINDOW = 100;
public static final int CLOSE_WINDOW = 101;
public static final int SET_SLOT = 103;
public static final int WINDOW_ITEMS = 104;
public static final int CRAFT_PROGRESS_BAR = 105;
public static final int TRANSACTION = 106;
public static final int SET_CREATIVE_SLOT = 107;
public static final int UPDATE_SIGN = 130;
public static final int ITEM_DATA = 131;
/**
* Sent the first time a tile entity (chest inventory, etc.) is withing range of the player, or has been updated.
* <p>
* Remember to clone the packet if you are modifying it.
*/
public static final int TILE_ENTITY_DATA = 132;
public static final int OPEN_TILE_ENTITY = 133;
public static final int STATISTIC = 200;
public static final int PLAYER_INFO = 201;
public static final int ABILITIES = 202;
public static final int TAB_COMPLETE = 203;
public static final int SCOREBOARD_OBJECTIVE = 206;
public static final int UPDATE_SCORE = 207;
public static final int DISPLAY_SCOREBOARD = 208;
public static final int TEAMS = 209;
public static final int CUSTOM_PAYLOAD = 250;
public static final int KEY_RESPONSE = 252;
public static final int KEY_REQUEST = 253;
public static final int KICK_DISCONNECT = 255;
/**
* This packet was introduced in 1.7.2.
*/
public static final int PING_TIME = 230;
/**
* This packet was introduced in 1.7.2.
*/
public static final int LOGIN_SUCCESS = 232;
/**
* A registry that parses between names and packet IDs.
* @return The current server registry.
*/
public static Server getRegistry() {
return INSTANCE;
}
// We only allow a single instance of this class
private Server() {
super();
}
}
/**
* List of packets sent by the client.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @author Kristian
*/
@Deprecated
public static class Client extends IntEnum {
/**
* The singleton instance. Can also be retrieved from the parent class.
*/
private static Client INSTANCE = new Client();
public static final int KEEP_ALIVE = 0;
public static final int LOGIN = 1;
public static final int HANDSHAKE = 2;
public static final int CHAT = 3;
public static final int USE_ENTITY = 7;
/**
* Since 1.3.1, the client no longer sends a respawn packet. Moved to CLIENT_COMMAND.
*/
@Deprecated
public static final int RESPAWN = 9;
public static final int FLYING = 10;
public static final int PLAYER_POSITION = 11;
public static final int PLAYER_LOOK = 12;
public static final int PLAYER_LOOK_MOVE = 13;
public static final int BLOCK_DIG = 14;
public static final int PLACE = 15;
public static final int BLOCK_ITEM_SWITCH = 16;
public static final int ARM_ANIMATION = 18;
public static final int ENTITY_ACTION = 19;
public static final int PLAYER_INPUT = 27;
public static final int CLOSE_WINDOW = 101;
public static final int WINDOW_CLICK = 102;
public static final int TRANSACTION = 106;
public static final int SET_CREATIVE_SLOT = 107;
public static final int BUTTON_CLICK = 108;
public static final int UPDATE_SIGN = 130;
public static final int ABILITIES = 202;
public static final int TAB_COMPLETE = 203;
public static final int LOCALE_AND_VIEW_DISTANCE = 204;
public static final int CLIENT_COMMAND = 205;
public static final int CUSTOM_PAYLOAD = 250;
public static final int KEY_RESPONSE = 252;
public static final int GET_INFO = 254;
public static final int KICK_DISCONNECT = 255;
/**
* This packet was introduced in 1.7.2.
*/
public static final int PING_TIME = 230;
/**
* This packet was introduced in 1.7.2.
*/
public static final int LOGIN_START = 231;
/**
* A registry that parses between names and packet IDs.
* @return The current client registry.
*/
public static Client getRegistry() {
return INSTANCE;
}
// Like above
private Client() {
super();
}
}
/**
* A registry that parses between names and packet IDs.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @return The current client registry.
*/
@Deprecated
public static Server getServerRegistry() {
return Server.getRegistry();
}
/**
* A registry that parses between names and packet IDs.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @return The current server registry.
*/
@Deprecated
public static Client getClientRegistry() {
return Client.INSTANCE;
}
/**
* Find a packet by name. Must be capitalized and use underscores.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @param name - name of packet to find.
* @return The packet ID found.
*/
@Deprecated
public static int valueOf(String name) {
Integer serverAttempt = Server.INSTANCE.valueOf(name);
if (serverAttempt != null)
return serverAttempt;
else
return Client.INSTANCE.valueOf(name);
}
/**
* Retrieves the name of a packet.
* <p>
* Deprecated: Use {@link PacketType} instead.
* @param packetID - packet to retrieve name.
* @return The name, or NULL if unable to find such a packet.
*/
@Deprecated
public static String getDeclaredName(int packetID) {
String serverAttempt = Server.INSTANCE.getDeclaredName(packetID);
if (serverAttempt != null)
return serverAttempt;
else
return Client.INSTANCE.getDeclaredName(packetID);
}
}
@@ -1,144 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.Validate;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
/**
* The main entry point for ProtocolLib.
* @author dmulloy2
*/
public class ProtocolLibrary {
/**
* The minimum version ProtocolLib has been tested with.
*/
public static final String MINIMUM_MINECRAFT_VERSION = "1.9";
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.9.4";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.9.4) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2016-05-10";
/**
* Plugins that are currently incompatible with ProtocolLib.
*/
public static final List<String> INCOMPATIBLE = Arrays.asList("TagAPI");
private static Plugin plugin;
private static ProtocolConfig config;
private static ProtocolManager manager;
private static ErrorReporter reporter = new BasicErrorReporter();
private static ListeningScheduledExecutorService executorAsync;
private static ListeningScheduledExecutorService executorSync;
private static boolean updatesDisabled;
private static boolean initialized;
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager, ErrorReporter reporter,
ListeningScheduledExecutorService executorAsync, ListeningScheduledExecutorService executorSync) {
Validate.isTrue(!initialized, "ProtocolLib has already been initialized.");
ProtocolLibrary.plugin = plugin;
ProtocolLibrary.config = config;
ProtocolLibrary.manager = manager;
ProtocolLibrary.reporter = reporter;
ProtocolLibrary.executorAsync = executorAsync;
ProtocolLibrary.executorSync = executorSync;
ProtocolLogger.init(plugin);
initialized = true;
}
/**
* Gets the ProtocolLib plugin instance.
* @return The plugin instance
*/
public static Plugin getPlugin() {
return plugin;
}
/**
* Gets ProtocolLib's configuration
* @return The config
*/
public static ProtocolConfig getConfig() {
return config;
}
/**
* Retrieves the packet protocol manager.
* @return Packet protocol manager
*/
public static ProtocolManager getProtocolManager() {
return manager;
}
/**
* Retrieve the current error reporter.
* @return Current error reporter.
*/
public static ErrorReporter getErrorReporter() {
return reporter;
}
/**
* Retrieve an executor service for performing asynchronous tasks on the behalf of ProtocolLib.
* <p>
* Note that this service is NULL if ProtocolLib has not been initialized yet.
* @return The executor service, or NULL.
*/
public static ListeningScheduledExecutorService getExecutorAsync() {
return executorAsync;
}
/**
* Retrieve an executor service for performing synchronous tasks (main thread) on the behalf of ProtocolLib.
* <p>
* Note that this service is NULL if ProtocolLib has not been initialized yet.
* @return The executor service, or NULL.
*/
public static ListeningScheduledExecutorService getExecutorSync() {
return executorSync;
}
/**
* Disables the ProtocolLib update checker.
*/
public static void disableUpdates() {
updatesDisabled = true;
}
/**
* Whether or not updates are currently disabled.
* @return True if it is, false if not
*/
public static boolean updatesDisabled() {
return updatesDisabled;
}
}
@@ -1,116 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.concurrency;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Represents a very quick integer set that uses a lookup table to store membership.
* <p>
* This class is intentionally missing a size method.
* @author Kristian
*/
public class IntegerSet {
private final boolean[] array;
/**
* Initialize a lookup table with the given maximum number of elements.
* <p>
* This creates a set for elements in the range [0, count).
* <p>
* Formally, the current set must be a subset of [0, 1, 2, ..., count - 1].
* @param maximumCount - maximum element value and count.
*/
public IntegerSet(int maximumCount) {
this.array = new boolean[maximumCount];
}
/**
* Initialize a lookup table with a given maximum and value list.
* <p>
* The provided elements must be in the range [0, count).
* @param maximumCount - the maximum element value and count.
* @param values - the elements to add to the set.
*/
public IntegerSet(int maximumCount, Collection<Integer> values) {
this.array = new boolean[maximumCount];
addAll(values);
}
/**
* Determine whether or not the given element exists in the set.
* @param element - the element to check. Must be in the range [0, count).
* @return TRUE if the given element exists, FALSE otherwise.
*/
public boolean contains(int element) {
return array[element];
}
/**
* Add the given element to the set, or do nothing if it already exists.
* @param element - element to add.
* @throws ArrayIndexOutOfBoundsException If the given element is not in the range [0, count).
*/
public void add(int element) {
array[element] = true;
}
/**
* Add the given collection of elements to the set.
* @param packets - elements to add.
*/
public void addAll(Collection<Integer> packets) {
for (Integer id : packets) {
add(id);
}
}
/**
* Remove the given element from the set, or do nothing if it's already removed.
* @param element - element to remove.
*/
public void remove(int element) {
// We don't actually care if the caller tries to remove an element outside the valid set
if (element >= 0 && element < array.length)
array[element] = false;
}
/**
* Remove every element from the set.
*/
public void clear() {
Arrays.fill(array, false);
}
/**
* Convert the current IntegerSet to an equivalent HashSet.
* @return The resulting HashSet.
*/
public Set<Integer> toSet() {
Set<Integer> elements = new HashSet<Integer>();
for (int i = 0; i < array.length; i++) {
if (array[i])
elements.add(i);
}
return elements;
}
}
@@ -1,244 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.concurrency;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
/**
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
* @param <T> - type of the elements in the list.
*/
public class SortedCopyOnWriteArray<T extends Comparable<T>> implements Collection<T> {
// Prevent reordering
private volatile List<T> list;
/**
* Construct an empty sorted array.
*/
public SortedCopyOnWriteArray() {
list = new ArrayList<T>();
}
/**
* Create a sorted array from the given list. The elements will be automatically sorted.
* @param wrapped - the collection whose elements are to be placed into the list.
*/
public SortedCopyOnWriteArray(Collection<T> wrapped) {
this.list = new ArrayList<T>(wrapped);
}
/**
* Create a sorted array from the given list.
* @param wrapped - the collection whose elements are to be placed into the list.
* @param sort - TRUE to automatically sort the collection, FALSE if it is already sorted.
*/
public SortedCopyOnWriteArray(Collection<T> wrapped, boolean sort) {
this.list = new ArrayList<T>(wrapped);
if (sort) {
Collections.sort(list);
}
}
/**
* Inserts the given element in the proper location.
* @param value - element to insert.
*/
@Override
public synchronized boolean add(T value) {
// We use NULL as a special marker, so we don't allow it
if (value == null)
throw new IllegalArgumentException("value cannot be NULL");
List<T> copy = new ArrayList<T>();
for (T element : list) {
// If the value is now greater than the current element, it should be placed right before it
if (value != null && value.compareTo(element) < 0) {
copy.add(value);
value = null;
}
copy.add(element);
}
// Don't forget to add it
if (value != null)
copy.add(value);
list = copy;
return true;
}
@Override
public synchronized boolean addAll(Collection<? extends T> values) {
if (values == null)
throw new IllegalArgumentException("values cannot be NULL");
if (values.size() == 0)
return false;
List<T> copy = new ArrayList<T>();
// Insert the new content and sort it
copy.addAll(list);
copy.addAll(values);
Collections.sort(copy);
list = copy;
return true;
}
/**
* Removes from the list by making a new list with every element except the one given.
* <p>
* Objects will be compared using the given objects equals() method.
* @param value - value to remove.
*/
@Override
public synchronized boolean remove(Object value) {
List<T> copy = new ArrayList<T>();
boolean result = false;
// Note that there's not much to be gained from using BinarySearch, as we
// have to copy (and thus read) the entire list regardless.
// Copy every element except the one given to us.
for (T element : list) {
if (!Objects.equal(value, element)) {
copy.add(element);
} else {
result = true;
}
}
list = copy;
return result;
}
@Override
public boolean removeAll(Collection<?> values) {
// Special cases
if (values == null)
throw new IllegalArgumentException("values cannot be NULL");
if (values.size() == 0)
return false;
List<T> copy = new ArrayList<T>();
copy.addAll(list);
copy.removeAll(values);
list = copy;
return true;
}
@Override
public boolean retainAll(Collection<?> values) {
// Special cases
if (values == null)
throw new IllegalArgumentException("values cannot be NULL");
if (values.size() == 0)
return false;
List<T> copy = new ArrayList<T>();
copy.addAll(list);
copy.removeAll(values);
list = copy;
return true;
}
/**
* Removes from the list by making a copy of every element except the one with the given index.
* @param index - index of the element to remove.
*/
public synchronized void remove(int index) {
List<T> copy = new ArrayList<T>(list);
copy.remove(index);
list = copy;
}
/**
* Retrieves an element by index.
* @param index - index of element to retrieve.
* @return The element at the given location.
*/
public T get(int index) {
return list.get(index);
}
/**
* Retrieve the size of the list.
* @return Size of the list.
*/
public int size() {
return list.size();
}
/**
* Retrieves an iterator over the elements in the given list.
* Warning: No not attempt to remove elements using the iterator.
*/
public Iterator<T> iterator() {
return Iterables.unmodifiableIterable(list).iterator();
}
@Override
public void clear() {
list = new ArrayList<T>();
}
@Override
public boolean contains(Object value) {
return list.contains(value);
}
@Override
public boolean containsAll(Collection<?> values) {
return list.containsAll(values);
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@SuppressWarnings("hiding")
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public String toString() {
return list.toString();
}
}
@@ -1,96 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
/**
* Represents a listener that is notified of every sent and received packet.
*
* @author Kristian
*/
public abstract class MonitorAdapter implements PacketListener {
private Plugin plugin;
private ListeningWhitelist sending = ListeningWhitelist.EMPTY_WHITELIST;
private ListeningWhitelist receiving = ListeningWhitelist.EMPTY_WHITELIST;
public MonitorAdapter(Plugin plugin, ConnectionSide side) {
initialize(plugin, side, getLogger(plugin));
}
public MonitorAdapter(Plugin plugin, ConnectionSide side, Logger logger) {
initialize(plugin, side, logger);
}
@SuppressWarnings("deprecation")
private void initialize(Plugin plugin, ConnectionSide side, Logger logger) {
this.plugin = plugin;
// Recover in case something goes wrong
try {
if (side.isForServer())
this.sending = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getServerPacketTypes()).gamePhaseBoth().build();
if (side.isForClient())
this.receiving = ListeningWhitelist.newBuilder().monitor().types(PacketRegistry.getClientPacketTypes()).gamePhaseBoth().build();
} catch (FieldAccessException e) {
if (side.isForServer())
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
if (side.isForClient())
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
}
}
/**
* Retrieve a logger, even if we're running in a CraftBukkit version that doesn't support it.
* @param plugin - the plugin to retrieve.
* @return The logger.
*/
private Logger getLogger(Plugin plugin) {
try {
return plugin.getLogger();
} catch (NoSuchMethodError e) {
return Logger.getLogger("Minecraft");
}
}
@Override
public ListeningWhitelist getSendingWhitelist() {
return sending;
}
@Override
public ListeningWhitelist getReceivingWhitelist() {
return receiving;
}
@Override
public Plugin getPlugin() {
return plugin;
}
}
@@ -1,421 +0,0 @@
package com.comphenix.protocol.events;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import javax.annotation.Nonnull;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.utility.ByteBufferInputStream;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
/**
* Marker containing the serialized packet data seen from the network,
* or output handlers that will serialize the current packet.
*
* @author Kristian
*/
public abstract class NetworkMarker {
public static class EmptyBufferMarker extends NetworkMarker {
public EmptyBufferMarker(@Nonnull ConnectionSide side) {
super(side, (byte[]) null, null);
}
@Override
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
throw new IllegalStateException("Buffer is empty.");
}
@Override
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
throw new IllegalStateException("Buffer is empty.");
}
@Override
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
throw new IllegalStateException("Buffer is empty.");
}
}
// Custom network handler
private PriorityQueue<PacketOutputHandler> outputHandlers;
// Post listeners
private List<PacketPostListener> postListeners;
// Post packets
private List<ScheduledPacket> scheduledPackets;
// The input buffer
private ByteBuffer inputBuffer;
private final ConnectionSide side;
private final PacketType type;
// Cache serializer too
private StreamSerializer serializer;
/**
* Construct a new network marker.
* @param side - which side this marker belongs to.
* @param inputBuffer - the read serialized packet data.
* @param type - packet type
*/
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
this.inputBuffer = inputBuffer;
this.type = type;
}
/**
* Construct a new network marker.
* <p>
* The input buffer is only non-null for client-side packets.
* @param side - which side this marker belongs to.
* @param inputBuffer - the read serialized packet data.
* @param type - packet type
*/
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
this.type = type;
if (inputBuffer != null) {
this.inputBuffer = ByteBuffer.wrap(inputBuffer);
}
}
/**
* Retrieve whether or not this marker belongs to a client or a server side packet.
* @return The side the parent packet belongs to.
*/
public ConnectionSide getSide() {
return side;
}
/**
* Retrieve a utility class for serializing and deserializing Minecraft objects.
* @return Serialization utility class.
*/
public StreamSerializer getSerializer() {
if (serializer == null)
serializer = new StreamSerializer();
return serializer;
}
/**
* Retrieve the serialized packet data (excluding the header by default) from the network input stream.
* <p>
* The returned buffer is read-only. If the parent event is a server side packet this
* method throws {@link IllegalStateException}.
* <p>
* It returns NULL if the packet was transmitted by a plugin locally.
* @return A byte buffer containing the raw packet data read from the network.
*/
public ByteBuffer getInputBuffer() {
return getInputBuffer(true);
}
/**
* Retrieve the serialized packet data from the network input stream.
* <p>
* The returned buffer is read-only. If the parent event is a server side packet this
* method throws {@link IllegalStateException}.
* <p>
* It returns NULL if the packet was transmitted by a plugin locally.
* @param excludeHeader - whether or not to exclude the packet ID header.
* @return A byte buffer containing the raw packet data read from the network.
*/
public ByteBuffer getInputBuffer(boolean excludeHeader) {
if (side.isForServer())
throw new IllegalStateException("Server-side packets have no input buffer.");
if (inputBuffer != null) {
ByteBuffer result = inputBuffer.asReadOnlyBuffer();
try {
if (excludeHeader)
result = skipHeader(result);
else
result = addHeader(result, type);
} catch (IOException e) {
throw new RuntimeException("Cannot skip packet header.", e);
}
return result;
}
return null;
}
/**
* Retrieve the serialized packet data (excluding the header by default) as an input stream.
* <p>
* The data is exactly the same as in {@link #getInputBuffer()}.
* @see #getInputBuffer()
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
*/
public DataInputStream getInputStream() {
return getInputStream(true);
}
/**
* Retrieve the serialized packet data as an input stream.
* <p>
* The data is exactly the same as in {@link #getInputBuffer()}.
* @see #getInputBuffer()
* @param excludeHeader - whether or not to exclude the packet ID header.
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
*/
@SuppressWarnings("resource")
public DataInputStream getInputStream(boolean excludeHeader) {
if (side.isForServer())
throw new IllegalStateException("Server-side packets have no input buffer.");
if (inputBuffer == null)
return null;
DataInputStream input = new DataInputStream(
new ByteArrayInputStream(inputBuffer.array())
);
try {
if (excludeHeader)
input = skipHeader(input);
else
input = addHeader(input, type);
} catch (IOException e) {
throw new RuntimeException("Cannot skip packet header.", e);
}
return input;
}
/**
* Whether or not the output handlers have to write a packet header.
* @return TRUE if they do, FALSE otherwise.
*/
public boolean requireOutputHeader() {
return MinecraftReflection.isUsingNetty();
}
/**
* Enqueue the given output handler for managing how the current packet will be written to the network stream.
* <p>
* Note that output handlers are not serialized, as most consumers will probably implement them using anonymous classes.
* It is not safe to serialize anonymous classes, as their name depend on the order in which they are declared in the parent class.
* <p>
* This can only be invoked on server side packet events.
* @param handler - the handler that will take part in serializing the packet.
* @return TRUE if it was added, FALSE if it has already been added.
*/
public boolean addOutputHandler(@Nonnull PacketOutputHandler handler) {
checkServerSide();
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
// Lazy initialization - it's imperative that we save space and time here
if (outputHandlers == null) {
outputHandlers = new PriorityQueue<PacketOutputHandler>(10, new Comparator<PacketOutputHandler>() {
@Override
public int compare(PacketOutputHandler o1, PacketOutputHandler o2) {
return Ints.compare(o1.getPriority().getSlot(), o2.getPriority().getSlot());
}
});
}
return outputHandlers.add(handler);
}
/**
* Remove a given output handler from the serialization queue.
* <p>
* This can only be invoked on server side packet events.
* @param handler - the handler to remove.
* @return TRUE if the handler was removed, FALSE otherwise.
*/
public boolean removeOutputHandler(@Nonnull PacketOutputHandler handler) {
checkServerSide();
Preconditions.checkNotNull(handler, "handler cannot be NULL.");
if (outputHandlers != null) {
return outputHandlers.remove(handler);
}
return false;
}
/**
* Retrieve every registered output handler in no particular order.
* @return Every registered output handler.
*/
@Nonnull
public Collection<PacketOutputHandler> getOutputHandlers() {
if (outputHandlers != null) {
return outputHandlers;
} else {
return Collections.emptyList();
}
}
/**
* Add a listener that is invoked after a packet has been successfully sent to the client, or received
* by the server.
* <p>
* Received packets are not guarenteed to have been fully processed, but packets passed
* to {@link ProtocolManager#recieveClientPacket(Player, PacketContainer)} will be processed after the
* current packet event.
* <p>
* Note that post listeners will be executed asynchronously off the main thread. They are not executed
* in any defined order.
* @param listener - the listener that will be invoked.
* @return TRUE if it was added.
*/
public boolean addPostListener(PacketPostListener listener) {
if (postListeners == null)
postListeners = Lists.newArrayList();
return postListeners.add(listener);
}
/**
* Remove the first instance of the given listener.
* @param listener - listener to remove.
* @return TRUE if it was removed, FALSE otherwise.
*/
public boolean removePostListener(PacketPostListener listener) {
if (postListeners != null) {
return postListeners.remove(listener);
}
return false;
}
/**
* Retrieve an immutable view of all the listeners that will be invoked once the packet has been sent or received.
* @return Every post packet listener. Never NULL.
*/
public List<PacketPostListener> getPostListeners() {
return postListeners != null ? Collections.unmodifiableList(postListeners) : Collections.<PacketPostListener>emptyList();
}
/**
* Retrieve a list of packets that will be schedule (in-order) when the current packet has been successfully transmitted.
* <p>
* This list is modifiable.
* @return List of packets that will be scheduled.
*/
public List<ScheduledPacket> getScheduledPackets() {
if (scheduledPackets == null)
scheduledPackets = Lists.newArrayList();
return scheduledPackets;
}
/**
* Ensure that the packet event is server side.
*/
private void checkServerSide() {
if (side.isForClient()) {
throw new IllegalStateException("Must be a server side packet.");
}
}
/**
* Return a byte buffer without the header in the current packet.
* <p>
* It's safe to modify the position of the buffer.
* @param buffer - a read-only byte source.
* @return A byte buffer without the header in the current packet.
* @throws IOException If integer reading fails
*/
protected ByteBuffer skipHeader(ByteBuffer buffer) throws IOException {
skipHeader(new DataInputStream(new ByteBufferInputStream(buffer)));
return buffer;
}
/**
* Return an input stream without the header in the current packet.
* <p>
* It's safe to modify the input stream.
* @param input - input stream
* @return An input stream without the header
* @throws IOException If integer reading fails
*/
protected abstract DataInputStream skipHeader(DataInputStream input) throws IOException;
/**
* Return the byte buffer prepended with the packet header.
* @param buffer - the read-only byte buffer.
* @param type - the current packet.
* @return The byte buffer.
*/
protected abstract ByteBuffer addHeader(ByteBuffer buffer, PacketType type);
/**
* Return the input stream prepended with the packet header.
* @param input - the input stream.
* @param type - the current packet.
* @return The byte buffer.
*/
protected abstract DataInputStream addHeader(DataInputStream input, PacketType type);
/**
* Determine if the given marker has any output handlers.
* @param marker - the marker to check.
* @return TRUE if it does, FALSE otherwise.
*/
public static boolean hasOutputHandlers(NetworkMarker marker) {
return marker != null && !marker.getOutputHandlers().isEmpty();
}
/**
* Determine if the given marker has any post listeners.
* @param marker - the marker to check.
* @return TRUE if it does, FALSE otherwise.
*/
public static boolean hasPostListeners(NetworkMarker marker) {
return marker != null && !marker.getPostListeners().isEmpty();
}
/**
* Retrieve the byte buffer stored in the given marker.
* @param marker - the marker.
* @return The byte buffer, or NULL if not found.
*/
public static byte[] getByteBuffer(NetworkMarker marker) {
if (marker != null) {
ByteBuffer buffer = marker.getInputBuffer();
if (buffer != null) {
byte[] data = new byte[buffer.remaining()];
buffer.get(data, 0, data.length);
return data;
}
}
return null;
}
/**
* Retrieve the network marker of a particular event without creating it.
* <p>
* This is an internal method that should not be used by API users.
* @param event - the event.
* @return The network marker.
*/
public static NetworkMarker getNetworkMarker(PacketEvent event) {
return event.networkMarker;
}
/**
* Retrieve the scheduled packets of a particular network marker without constructing the list.
* <p>
* This is an internal method that should not be used by API users.
* @param marker - the marker.
* @return The list, or NULL if not found or initialized.
*/
public static List<ScheduledPacket> readScheduledPackets(NetworkMarker marker) {
return marker.scheduledPackets;
}
}
File diff suppressed because it is too large. Load diff
@@ -1,243 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import com.comphenix.protocol.utility.EnhancerFactory;
/**
* Represents a player object that can be serialized by Java.
*
* @author Kristian
*/
class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
/**
* Generated by Eclipse.
*/
private static final long serialVersionUID = -2728976288470282810L;
private transient Location bedSpawnLocation;
// Relevant data about an offline player
private String name;
private UUID uuid;
private long firstPlayed;
private long lastPlayed;
private boolean operator;
private boolean banned;
private boolean playedBefore;
private boolean online;
private boolean whitelisted;
// Proxy helper
private static Map<String, Method> lookup = new ConcurrentHashMap<String, Method>();
/**
* Constructor used by serialization.
*/
public SerializedOfflinePlayer() {
// Do nothing
}
/**
* Initialize this serializable offline player from another player.
* @param offline - another player.
*/
public SerializedOfflinePlayer(OfflinePlayer offline) {
this.name = offline.getName();
this.uuid = offline.getUniqueId();
this.firstPlayed = offline.getFirstPlayed();
this.lastPlayed = offline.getLastPlayed();
this.operator = offline.isOp();
this.banned = offline.isBanned();
this.playedBefore = offline.hasPlayedBefore();
this.online = offline.isOnline();
this.whitelisted = offline.isWhitelisted();
}
@Override
public boolean isOp() {
return operator;
}
@Override
public void setOp(boolean operator) {
this.operator = operator;
}
@Override
public Map<String, Object> serialize() {
throw new UnsupportedOperationException();
}
@Override
public Location getBedSpawnLocation() {
return bedSpawnLocation;
}
@Override
public long getFirstPlayed() {
return firstPlayed;
}
@Override
public long getLastPlayed() {
return lastPlayed;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String getName() {
return name;
}
@Override
public boolean hasPlayedBefore() {
return playedBefore;
}
@Override
public boolean isBanned() {
return banned;
}
@Override
public void setBanned(boolean banned) {
this.banned = banned;
}
@Override
public boolean isOnline() {
return online;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
}
@Override
public void setWhitelisted(boolean whitelisted) {
this.whitelisted = whitelisted;
}
private void writeObject(ObjectOutputStream output) throws IOException {
output.defaultWriteObject();
// Serialize the bed spawn location
output.writeUTF(bedSpawnLocation.getWorld().getName());
output.writeDouble(bedSpawnLocation.getX());
output.writeDouble(bedSpawnLocation.getY());
output.writeDouble(bedSpawnLocation.getZ());
}
private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
input.defaultReadObject();
// Well, this is a problem
bedSpawnLocation = new Location(
getWorld(input.readUTF()),
input.readDouble(),
input.readDouble(),
input.readDouble()
);
}
private World getWorld(String name) {
try {
// Try to get the world at least
return Bukkit.getServer().getWorld(name);
} catch (Exception e) {
// Screw it
return null;
}
}
@Override
public Player getPlayer() {
try {
// Try to get the real player underneath
return Bukkit.getServer().getPlayerExact(name);
} catch (Exception e) {
return getProxyPlayer();
}
}
/**
* Retrieve a player object that implements OfflinePlayer by refering to this object.
* <p>
* All other methods cause an exception.
* @return Proxy object.
*/
public Player getProxyPlayer() {
// Remember to initialize the method filter
if (lookup.size() == 0) {
// Add all public methods
for (Method method : OfflinePlayer.class.getMethods()) {
lookup.put(method.getName(), method);
}
}
// MORE CGLIB magic!
Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
ex.setSuperclass(Player.class);
ex.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// There's no overloaded methods, so we don't care
Method offlineMethod = lookup.get(method.getName());
// Ignore all other methods
if (offlineMethod == null) {
throw new UnsupportedOperationException(
"The method " + method.getName() + " is not supported for offline players.");
}
// Invoke our on method
return offlineMethod.invoke(SerializedOfflinePlayer.this, args);
}
});
return (Player) ex.create();
}
}
@@ -1,31 +0,0 @@
package com.comphenix.protocol.injector;
/**
* Sets the inject hook type. Different types allow for maximum compatibility.
* @author Kristian
*/
public enum PlayerInjectHooks {
/**
* The injection hook that does nothing. Set when every other inject hook fails.
*/
NONE,
/**
* Override the network handler object itself. Only works in 1.3.
* <p>
* Cannot intercept MapChunk packets.
*/
NETWORK_MANAGER_OBJECT,
/**
* Override the packet queue lists in NetworkHandler.
* <p>
* Cannot intercept MapChunk packets.
*/
NETWORK_HANDLER_FIELDS,
/**
* Override the server handler object. Versatile, but a tad slower.
*/
NETWORK_SERVER_OBJECT;
}
@@ -1,58 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
/**
* Invoked when attempting to use a player that has already logged out.
*
* @author Kristian
*/
public class PlayerLoggedOutException extends RuntimeException {
/**
* Generated by Eclipse.
*/
private static final long serialVersionUID = 4889257862160145234L;
public PlayerLoggedOutException() {
// Default error message
super("Cannot inject a player that has already logged out.");
}
public PlayerLoggedOutException(String message, Throwable cause) {
super(message, cause);
}
public PlayerLoggedOutException(String message) {
super(message);
}
public PlayerLoggedOutException(Throwable cause) {
super(cause);
}
/**
* Construct an exception from a formatted message.
* @param message - the message to format.
* @param params - parameters.
* @return The formated exception
*/
public static PlayerLoggedOutException fromFormat(String message, Object... params) {
return new PlayerLoggedOutException(String.format(message, params));
}
}
@@ -1,175 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.CompileListener;
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Caches structure modifiers.
* @author Kristian
*/
public class StructureCache {
// Structure modifiers
private static ConcurrentMap<PacketType, StructureModifier<Object>> structureModifiers =
new ConcurrentHashMap<PacketType, StructureModifier<Object>>();
private static Set<PacketType> compiling = new HashSet<PacketType>();
/**
* Creates an empty Minecraft packet of the given id.
* <p>
* Decreated: Use {@link #newPacket(PacketType)} instead.
* @param legacyId - legacy (1.6.4) packet id.
* @return Created packet.
*/
@Deprecated
public static Object newPacket(int legacyId) {
return newPacket(PacketType.findLegacy(legacyId));
}
/**
* Creates an empty Minecraft packet of the given type.
* @param type - packet type.
* @return Created packet.
*/
public static Object newPacket(PacketType type) {
Class<?> clazz = PacketRegistry.getPacketClassFromType(type, true);
// Check the return value
if (clazz != null) {
// TODO: Optimize DefaultInstances
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result != null) {
return result;
}
}
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
}
/**
* Retrieve a cached structure modifier for the given packet id.
* <p>
* Deprecated: Use {@link #getStructure(PacketType)} instead.
* @param legacyId - the legacy (1.6.4) packet ID.
* @return A structure modifier.
*/
@Deprecated
public static StructureModifier<Object> getStructure(int legacyId) {
return getStructure(PacketType.findLegacy(legacyId));
}
/**
* Retrieve a cached structure modifier for the given packet type.
* @param type - packet type.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(PacketType type) {
// Compile structures by default
return getStructure(type, true);
}
/**
* Retrieve a cached structure modifier given a packet type.
* @param packetType - packet type.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(Class<?> packetType) {
// Compile structures by default
return getStructure(packetType, true);
}
/**
* Retrieve a cached structure modifier given a packet type.
* @param packetType - packet type.
* @param compile - whether or not to asynchronously compile the structure modifier.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(Class<?> packetType, boolean compile) {
// Get the ID from the class
return getStructure(PacketRegistry.getPacketType(packetType), compile);
}
/**
* Retrieve a cached structure modifier for the given packet ID.
* <p>
* Deprecated: Use {@link #getStructure(PacketType, boolean)} instead.
* @param legacyId - the legacy (1.6.4) packet ID.
* @param compile - whether or not to asynchronously compile the structure modifier.
* @return A structure modifier.
*/
@Deprecated
public static StructureModifier<Object> getStructure(final int legacyId, boolean compile) {
return getStructure(PacketType.findLegacy(legacyId), compile);
}
/**
* Retrieve a cached structure modifier for the given packet type.
* @param type - packet type.
* @param compile - whether or not to asynchronously compile the structure modifier.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(final PacketType type, boolean compile) {
StructureModifier<Object> result = structureModifiers.get(type);
// We don't want to create this for every lookup
if (result == null) {
// Use the vanilla class definition
final StructureModifier<Object> value = new StructureModifier<Object>(
PacketRegistry.getPacketClassFromType(type, true), MinecraftReflection.getPacketClass(), true);
result = structureModifiers.putIfAbsent(type, value);
// We may end up creating multiple modifiers, but we'll agree on which to use
if (result == null) {
result = value;
}
}
// Automatically compile the structure modifier
if (compile && !(result instanceof CompiledStructureModifier)) {
// Compilation is many orders of magnitude slower than synchronization
synchronized (compiling) {
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
if (!compiling.contains(type) && compiler != null) {
compiler.scheduleCompilation(result, new CompileListener<Object>() {
@Override
public void onCompiled(StructureModifier<Object> compiledModifier) {
structureModifiers.put(type, compiledModifier);
}
});
compiling.add(type);
}
}
}
return result;
}
}
@@ -1,104 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import java.util.Map;
import java.util.Map.Entry;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.injector.packet.MapContainer;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.common.collect.Maps;
/**
* @author dmulloy2
*/
public class NettyProtocolRegistry extends ProtocolRegistry {
@Override
protected synchronized void initialize() {
Object[] protocols = enumProtocol.getEnumConstants();
// ID to Packet class maps
Map<Object, Map<Integer, Class<?>>> serverMaps = Maps.newLinkedHashMap();
Map<Object, Map<Integer, Class<?>>> clientMaps = Maps.newLinkedHashMap();
Register result = new Register();
StructureModifier<Object> modifier = null;
// Iterate through the protocols
for (Object protocol : protocols) {
if (modifier == null)
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
for (Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
String direction = entry.getKey().toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, entry.getValue());
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, entry.getValue());
}
}
}
// Maps we have to occationally check have changed
for (Map<Integer, Class<?>> map : serverMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (Map<Integer, Class<?>> map : clientMaps.values()) {
result.containers.add(new MapContainer(map));
}
for (int i = 0; i < protocols.length; i++) {
Object protocol = protocols[i];
Enum<?> enumProtocol = (Enum<?>) protocol;
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol))
associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER);
if (clientMaps.containsKey(protocol))
associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT);
}
// Exchange (thread safe, as we have only one writer)
this.register = result;
}
@Override
protected void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender) {
for (Entry<Integer, Class<?>> entry : lookup.entrySet()) {
PacketType type = PacketType.fromCurrent(protocol, sender, entry.getKey(), entry.getValue());
try {
register.typeToClass.put(type, entry.getValue());
if (sender == Sender.SERVER)
register.serverPackets.add(type);
if (sender == Sender.CLIENT)
register.clientPackets.add(type);
} catch (IllegalArgumentException ex) {
// Sometimes this happens with fake packets, just ignore it
}
}
}
}
@@ -1,125 +0,0 @@
package com.comphenix.protocol.injector.netty;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.packet.MapContainer;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* Represents a way of accessing the new netty Protocol enum.
* @author Kristian
*/
// TODO: Handle modifications to the BiMap
public abstract class ProtocolRegistry {
/**
* Represents a register we are currently building.
* @author Kristian
*/
protected static class Register {
// The main lookup table
public BiMap<PacketType, Class<?>> typeToClass = HashBiMap.create();
public volatile Set<PacketType> serverPackets = Sets.newHashSet();
public volatile Set<PacketType> clientPackets = Sets.newHashSet();
public List<MapContainer> containers = Lists.newArrayList();
public Register() {
}
/**
* Determine if the current register is outdated.
* @return TRUE if it is, FALSE otherwise.
*/
public boolean isOutdated() {
for (MapContainer container : containers) {
if (container.hasChanged()) {
return true;
}
}
return false;
}
}
protected Class<?> enumProtocol;
// Current register
protected volatile Register register;
public ProtocolRegistry() {
enumProtocol = MinecraftReflection.getEnumProtocolClass();
initialize();
}
/**
* Retrieve an immutable view of the packet type lookup.
* @return The packet type lookup.
*/
public Map<PacketType, Class<?>> getPacketTypeLookup() {
return Collections.unmodifiableMap(register.typeToClass);
}
/**
* Retrieve an immutable view of the class to packet type lookup.
* @return The packet type lookup.
*/
public Map<Class<?>, PacketType> getPacketClassLookup() {
return Collections.unmodifiableMap(register.typeToClass.inverse());
}
/**
* Retrieve every known client packet, from every protocol.
* @return Every client packet.
*/
public Set<PacketType> getClientPackets() {
return Collections.unmodifiableSet(register.clientPackets);
}
/**
* Retrieve every known server packet, from every protocol.
* @return Every server packet.
*/
public Set<PacketType> getServerPackets() {
return Collections.unmodifiableSet(register.serverPackets);
}
/**
* Ensure that our local register is up-to-date with Minecraft.
* <p>
* This operation may block the calling thread.
*/
public synchronized void synchronize() {
// See if the register is outdated
if (register.isOutdated()) {
initialize();
}
}
/**
* Load the packet lookup tables in each protocol.
*/
protected abstract void initialize();
protected abstract void associatePackets(Register register, Map<Integer, Class<?>> lookup, Protocol protocol, Sender sender);
/**
* Retrieve the number of mapping in all the maps.
* @param maps - iterable of maps.
* @return The sum of all the entries.
*/
protected final int sum(Iterable<? extends Map<Integer, Class<?>>> maps) {
int count = 0;
for (Map<Integer, Class<?>> map : maps)
count += map.size();
return count;
}
}
@@ -1,55 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import io.netty.buffer.ByteBuf;
/**
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
public int getId() {
return id;
}
public byte[] getBytes() {
return bytes;
}
public void writeId(ByteBuf output) {
int i = id;
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
}
output.writeByte(i);
}
public void writeBytes(ByteBuf output) {
output.writeBytes(bytes);
}
}
@@ -1,322 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.packet;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.netty.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Static packet registry in Minecraft.
* @author Kristian
*/
@SuppressWarnings("rawtypes")
public class PacketRegistry {
public static final ReportType REPORT_CANNOT_CORRECT_TROVE_MAP = new ReportType("Unable to correct no entry value.");
public static final ReportType REPORT_INSUFFICIENT_SERVER_PACKETS = new ReportType("Too few server packets detected: %s");
public static final ReportType REPORT_INSUFFICIENT_CLIENT_PACKETS = new ReportType("Too few client packets detected: %s");
// Two different packet registry
private static volatile ProtocolRegistry NETTY;
// Cached for Netty
private static volatile Set<Integer> LEGACY_SERVER_PACKETS;
private static volatile Set<Integer> LEGACY_CLIENT_PACKETS;
private static volatile Map<Integer, Class> LEGACY_PREVIOUS_PACKETS;
// Whether or not the registry has
private static boolean INITIALIZED;
/**
* Initialize the packet registry.
*/
private static void initialize() {
if (INITIALIZED) {
// Make sure they were initialized
if (NETTY == null)
throw new IllegalStateException("No initialized registry.");
return;
}
// Check for netty
NETTY = new NettyProtocolRegistry();
}
/**
* Determine if the given packet type is supported on the current server.
* @param type - the type to check.
* @return TRUE if it is, FALSE otherwise.
*/
public static boolean isSupported(PacketType type) {
initialize();
return NETTY.getPacketTypeLookup().containsKey(type);
}
/**
* Retrieve a map of every packet class to every ID.
* <p>
* Deprecated: Use {@link #getPacketToType()} instead.
* @return A map of packet classes and their corresponding ID.
*/
@Deprecated
public static Map<Class, Integer> getPacketToID() {
initialize();
@SuppressWarnings("unchecked")
Map<Class, Integer> result = (Map) Maps.transformValues(
NETTY.getPacketClassLookup(),
new Function<PacketType, Integer>() {
@Override
public Integer apply(PacketType type) {
return type.getLegacyId();
};
});
return result;
}
/**
* Retrieve a map of every packet class to the respective packet type.
* @return A map of packet classes and their corresponding packet type.
*/
public static Map<Class, PacketType> getPacketToType() {
initialize();
@SuppressWarnings("unchecked")
Map<Class, PacketType> result = (Map) NETTY.getPacketClassLookup();
return result;
}
/**
* Retrieve the injected proxy classes handlig each packet ID.
* <p>
* This is not supported in 1.7.2 and later.
* @return Injected classes.
*/
@Deprecated
public static Map<Integer, Class> getOverwrittenPackets() {
initialize();
throw new IllegalStateException("Not supported on Netty.");
}
/**
* Retrieve the vanilla classes handling each packet ID.
* @return Vanilla classes.
*/
@Deprecated
public static Map<Integer, Class> getPreviousPackets() {
initialize();
// Construct it first
if (LEGACY_PREVIOUS_PACKETS == null) {
Map<Integer, Class> map = Maps.newHashMap();
for (Entry<PacketType, Class<?>> entry : NETTY.getPacketTypeLookup().entrySet()) {
map.put(entry.getKey().getLegacyId(), entry.getValue());
}
LEGACY_PREVIOUS_PACKETS = Collections.unmodifiableMap(map);
}
return LEGACY_PREVIOUS_PACKETS;
}
/**
* Retrieve every known and supported server packet.
* <p>
* Deprecated: Use {@link #getServerPacketTypes()} instead.
* @return An immutable set of every known server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
@Deprecated
public static Set<Integer> getServerPackets() throws FieldAccessException {
initialize();
if (LEGACY_SERVER_PACKETS == null) {
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
}
return LEGACY_SERVER_PACKETS;
}
/**
* Retrieve every known and supported server packet type.
* @return Every server packet type.
*/
public static Set<PacketType> getServerPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getServerPackets();
}
/**
* Retrieve every known and supported client packet.
* <p>
* Deprecated: Use {@link #getClientPacketTypes()} instead.
* @return An immutable set of every known client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
@Deprecated
public static Set<Integer> getClientPackets() throws FieldAccessException {
initialize();
if (LEGACY_CLIENT_PACKETS == null) {
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
}
return LEGACY_CLIENT_PACKETS;
}
/**
* Retrieve every known and supported server packet type.
* @return Every server packet type.
*/
public static Set<PacketType> getClientPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getClientPackets();
}
/**
* Convert a set of packet types to a set of integers based on the legacy packet ID.
* @param types - packet type.
* @return Set of integers.
*/
public static Set<Integer> toLegacy(Set<PacketType> types) {
Set<Integer> result = Sets.newHashSet();
for (PacketType type : types)
result.add(type.getLegacyId());
return Collections.unmodifiableSet(result);
}
/**
* Convert a set of legacy packet IDs to packet types.
* @param ids - legacy packet IDs.
* @return Set of packet types.
*/
public static Set<PacketType> toPacketTypes(Set<Integer> ids) {
return toPacketTypes(ids, null);
}
/**
* Convert a set of legacy packet IDs to packet types.
* @param ids - legacy packet IDs.
* @param preference - the sender preference, if any.
* @return Set of packet types.
*/
public static Set<PacketType> toPacketTypes(Set<Integer> ids, Sender preference) {
Set<PacketType> result = Sets.newHashSet();
for (int id : ids)
result.add(PacketType.fromLegacy(id, preference));
return Collections.unmodifiableSet(result);
}
/**
* Retrieves the correct packet class from a given packet ID.
* <p>
* Deprecated: Use {@link #getPacketClassFromType(PacketType)} instead.
* @param packetID - the packet ID.
* @return The associated class.
*/
@Deprecated
public static Class getPacketClassFromID(int packetID) {
initialize();
return NETTY.getPacketTypeLookup().get(PacketType.findLegacy(packetID));
}
/**
* Retrieves the correct packet class from a given type.
* @param type - the packet type.
* @return The associated class.
*/
public static Class getPacketClassFromType(PacketType type) {
return getPacketClassFromType(type, false);
}
/**
* Retrieves the correct packet class from a given type.
* <p>
* Note that forceVanillla will be ignored on MC 1.7.2 and later.
* @param type - the packet type.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
*/
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
initialize();
return NETTY.getPacketTypeLookup().get(type);
}
/**
* Retrieves the correct packet class from a given packet ID.
* <p>
* This method has been deprecated.
* @param packetID - the packet ID.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
*/
@Deprecated
public static Class getPacketClassFromID(int packetID, boolean forceVanilla) {
initialize();
return getPacketClassFromID(packetID);
}
/**
* Retrieve the packet ID of a given packet.
* <p>
* Deprecated: Use {@link #getPacketType(Class)}.
* @param packet - the type of packet to check.
* @return The legacy ID of the given packet.
* @throws IllegalArgumentException If this is not a valid packet.
*/
@Deprecated
public static int getPacketID(Class<?> packet) {
initialize();
return NETTY.getPacketClassLookup().get(packet).getLegacyId();
}
/**
* Retrieve the packet type of a given packet.
* @param packet - the class of the packet.
* @return The packet type, or NULL if not found.
*/
public static PacketType getPacketType(Class<?> packet) {
return getPacketType(packet, null);
}
/**
* Retrieve the packet type of a given packet.
* @param packet - the class of the packet.
* @param sender - the sender of the packet, or NULL.
* @return The packet type, or NULL if not found.
*/
public static PacketType getPacketType(Class<?> packet, Sender sender) {
initialize();
return NETTY.getPacketClassLookup().get(packet);
}
}
@@ -1,138 +0,0 @@
package com.comphenix.protocol.reflect;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.google.common.collect.Lists;
public class ClassAnalyser {
/**
* Represents a method in ASM.
* <p>
* Keep in mind that this may also invoke a constructor.
* @author Kristian
*/
public static class AsmMethod {
public enum AsmOpcodes {
INVOKE_VIRTUAL,
INVOKE_SPECIAL,
INVOKE_STATIC,
INVOKE_INTERFACE,
INVOKE_DYNAMIC;
public static AsmOpcodes fromIntOpcode(int opcode) {
switch (opcode) {
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
}
}
}
private final AsmOpcodes opcode;
private final String ownerClass;
private final String methodName;
private final String signature;
public AsmMethod(AsmOpcodes opcode, String ownerClass, String methodName, String signature) {
this.opcode = opcode;
this.ownerClass = ownerClass;
this.methodName = methodName;
this.signature = signature;
}
public String getOwnerName() {
return ownerClass;
}
/**
* Retrieve the opcode used to invoke this method or constructor.
* @return The opcode.
*/
public AsmOpcodes getOpcode() {
return opcode;
}
/**
* Retrieve the associated owner class.
* @return The owner class.
* @throws ClassNotFoundException If the class was not found
*/
public Class<?> getOwnerClass() throws ClassNotFoundException {
return AsmMethod.class.getClassLoader().loadClass(getOwnerName().replace('/', '.'));
}
public String getMethodName() {
return methodName;
}
public String getSignature() {
return signature;
}
}
private static final ClassAnalyser DEFAULT = new ClassAnalyser();
/**
* Retrieve the default instance.
* @return The default.
*/
public static ClassAnalyser getDefault() {
return DEFAULT;
}
/**
* Retrieve every method calls in the given method.
* @param method - the method to analyse.
* @return The method calls.
* @throws IOException Cannot access the parent class.
*/
public List<AsmMethod> getMethodCalls(Method method) throws IOException {
return getMethodCalls(method.getDeclaringClass(), method);
}
/**
* Retrieve every method calls in the given method.
* @param clazz - the parent class.
* @param method - the method to analyse.
* @return The method calls.
* @throws IOException Cannot access the parent class.
*/
public List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
final ClassReader reader = new ClassReader(clazz.getCanonicalName());
final List<AsmMethod> output = Lists.newArrayList();
// The method we are looking for
final String methodName = method.getName();
final String methodDescription = Type.getMethodDescriptor(method);
reader.accept(new EmptyClassVisitor() {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Check method
if (methodName.equals(name) && methodDescription.equals(desc)) {
return new EmptyMethodVisitor() {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
}
};
}
return null;
}
}, ClassReader.EXPAND_FRAMES);
return output;
}
}
@@ -1,143 +0,0 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import javax.annotation.Nonnull;
import com.google.common.base.Preconditions;
public class ExactReflection {
// The class we're actually representing
private Class<?> source;
private boolean forceAccess;
private ExactReflection(Class<?> source, boolean forceAccess) {
this.source = Preconditions.checkNotNull(source, "source class cannot be NULL");
this.forceAccess = forceAccess;
}
/**
* Retrieves an exact reflection instance from a given class.
* @param source - the class we'll use.
* @return A fuzzy reflection instance.
*/
public static ExactReflection fromClass(Class<?> source) {
return fromClass(source, false);
}
/**
* Retrieves an exact reflection instance from a given class.
* @param source - the class we'll use.
* @param forceAccess - whether or not to override scope restrictions.
* @return A fuzzy reflection instance.
*/
public static ExactReflection fromClass(Class<?> source, boolean forceAccess) {
return new ExactReflection(source, forceAccess);
}
/**
* Retrieves an exact reflection instance from an object.
* @param reference - the object we'll use.
* @return A fuzzy reflection instance that uses the class of the given object.
*/
public static ExactReflection fromObject(Object reference) {
return new ExactReflection(reference.getClass(), false);
}
/**
* Retrieves an exact reflection instance from an object.
* @param reference - the object we'll use.
* @param forceAccess - whether or not to override scope restrictions.
* @return A fuzzy reflection instance that uses the class of the given object.
*/
public static ExactReflection fromObject(Object reference, boolean forceAccess) {
return new ExactReflection(reference.getClass(), forceAccess);
}
/**
* Retrieve the first method in the class hierachy with the given name and parameters.
* <p>
* If {@link #isForceAccess()} is TRUE, we will also search for protected and private methods.
* @param methodName - the method name to find, or NULL to look for everything.
* @param parameters - the parameters.
* @return The first matched method.
* @throws IllegalArgumentException If we cannot find a method by this name.
*/
public Method getMethod(String methodName, Class<?>... parameters) {
return getMethod(source, methodName, parameters);
}
// For recursion
private Method getMethod(Class<?> instanceClass, String methodName, Class<?>... parameters) {
for (Method method : instanceClass.getDeclaredMethods()) {
if ((forceAccess || Modifier.isPublic(method.getModifiers())) &&
(methodName == null || method.getName().equals(methodName)) &&
Arrays.equals(method.getParameterTypes(), parameters)) {
method.setAccessible(true);
return method;
}
}
// Search in every superclass
if (instanceClass.getSuperclass() != null)
return getMethod(instanceClass.getSuperclass(), methodName, parameters);
throw new IllegalArgumentException(String.format(
"Unable to find method %s (%s) in %s.", methodName, Arrays.asList(parameters), source));
}
/**
* Retrieve a field in the class hierachy by the given name.
* <p>
* If {@link #isForceAccess()} is TRUE, we will also search for protected and private fields.
* @param fieldName - the field name. Cannot be NULL.
* @return The first matched field.
*/
public Field getField(String fieldName) {
return getField(source, fieldName);
}
// For recursion
private Field getField(Class<?> instanceClass, @Nonnull String fieldName) {
// Ignore access rules
for (Field field : instanceClass.getDeclaredFields()) {
if (field.getName().equals(fieldName)) {
field.setAccessible(true);
return field;
}
}
// Recursively fild the correct field
if (instanceClass.getSuperclass() != null)
return getField(instanceClass.getSuperclass(), fieldName);
throw new IllegalArgumentException(String.format(
"Unable to find field %s in %s.", fieldName, source));
}
/**
* Retrieve an {@link ExactReflection} object where scope restrictions are ignored.
* @return A copy of the current object.
*/
public ExactReflection forceAccess() {
return new ExactReflection(source, true);
}
/**
* Determine if we are overriding scope restrictions and will also find
* private, protected or package members.
* @return TRUE if we are, FALSE otherwise.
*/
public boolean isForceAccess() {
return forceAccess;
}
/**
* Retrieve the source class we are searching.
* @return The source.
*/
public Class<?> getSource() {
return source;
}
}
@@ -1,527 +0,0 @@
package com.comphenix.protocol.reflect;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Utilities for working with fields by reflection. Adapted and refactored from
* the dormant [reflect] Commons sandbox component.
* <p>
* The ability is provided to break the scoping restrictions coded by the
* programmer. This can allow fields to be changed that shouldn't be. This
* facility should be used with care.
*
* @author Apache Software Foundation
* @author Matt Benson
* @since 2.5
* @version $Id: FieldUtils.java 1057009 2011-01-09 19:48:06Z niallp $
*/
@SuppressWarnings("rawtypes")
public class FieldUtils {
/**
* FieldUtils instances should NOT be constructed in standard programming.
* <p>
* This constructor is public to permit tools that require a JavaBean
* instance to operate.
*/
public FieldUtils() {
super();
}
/**
* Gets an accessible <code>Field</code> by name respecting scope.
* Superclasses/interfaces will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getField(Class cls, String fieldName) {
Field field = getField(cls, fieldName, false);
MemberUtils.setAccessibleWorkaround(field);
return field;
}
/**
* Gets an accessible <code>Field</code> by name breaking scope if
* requested. Superclasses/interfaces will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
*/
public static Field getField(final Class cls, String fieldName, boolean forceAccess) {
if (cls == null) {
throw new IllegalArgumentException("The class must not be null");
}
if (fieldName == null) {
throw new IllegalArgumentException("The field name must not be null");
}
// Sun Java 1.3 has a bugged implementation of getField hence we write
// the
// code ourselves
// getField() will return the Field object with the declaring class
// set correctly to the class that declares the field. Thus requesting
// the
// field on a subclass will return the field from the superclass.
//
// priority order for lookup:
// searchclass private/protected/package/public
// superclass protected/package/public
// private/different package blocks access to further superclasses
// implementedinterface public
// check up the superclass hierarchy
for (Class acls = cls; acls != null; acls = acls.getSuperclass()) {
try {
Field field = acls.getDeclaredField(fieldName);
// getDeclaredField checks for non-public scopes as well
// and it returns accurate results
if (!Modifier.isPublic(field.getModifiers())) {
if (forceAccess) {
field.setAccessible(true);
} else {
continue;
}
}
return field;
} catch (NoSuchFieldException ex) {
// ignore
}
}
// check the public interface case. This must be manually searched for
// in case there is a public supersuperclass field hidden by a
// private/package
// superclass field.
Field match = null;
for (Iterator intf = getAllInterfaces(cls).iterator(); intf.hasNext();) {
try {
Field test = ((Class) intf.next()).getField(fieldName);
if (match != null) {
throw new IllegalArgumentException("Reference to field " + fieldName
+ " is ambiguous relative to " + cls
+ "; a matching field exists on two or more implemented interfaces.");
}
match = test;
} catch (NoSuchFieldException ex) {
// ignore
}
}
return match;
}
/**
* <p>Gets a <code>List</code> of all interfaces implemented by the given
* class and its superclasses.</p>
*
* <p>The order is determined by looking through each interface in turn as
* declared in the source file and following its hierarchy up. Then each
* superclass is considered in the same way. Later duplicates are ignored,
* so the order is maintained.</p>
*
* @param cls the class to look up, may be <code>null</code>
* @return the <code>List</code> of interfaces in order,
* <code>null</code> if null input
*/
private static List getAllInterfaces(Class cls) {
if (cls == null) {
return null;
}
List<Class> list = new ArrayList<Class>();
while (cls != null) {
Class[] interfaces = cls.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (list.contains(interfaces[i]) == false) {
list.add(interfaces[i]);
}
List superInterfaces = getAllInterfaces(interfaces[i]);
for (Iterator it = superInterfaces.iterator(); it.hasNext();) {
Class intface = (Class) it.next();
if (list.contains(intface) == false) {
list.add(intface);
}
}
}
cls = cls.getSuperclass();
}
return list;
}
/**
* Read an accessible static Field.
*
* @param field to read
* @return the field value
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readStaticField(Field field) throws IllegalAccessException {
return readStaticField(field, false);
}
/**
* Read a static Field.
*
* @param field to read
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method.
* @return the field value
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readStaticField(Field field, boolean forceAccess)
throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
}
return readField(field, (Object) null, forceAccess);
}
/**
* Read the named public static field. Superclasses will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException {
return readStaticField(cls, fieldName, false);
}
/**
* Read the named static field. Superclasses will be considered.
*
* @param cls the class to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @return the Field object
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readStaticField(Class cls, String fieldName, boolean forceAccess)
throws IllegalAccessException {
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
// already forced access above, don't repeat it here:
return readStaticField(field, false);
}
/**
* Read an accessible Field.
*
* @param field the field to use
* @param target the object to call on, may be null for static fields
* @return the field value
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not accessible
*/
public static Object readField(Field field, Object target) throws IllegalAccessException {
return readField(field, target, false);
}
/**
* Read a Field.
*
* @param field the field to use
* @param target the object to call on, may be null for static fields
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method.
* @return the field value
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException {
if (field == null)
throw new IllegalArgumentException("The field must not be null");
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
return field.get(target);
}
/**
* Read the named public field. Superclasses will be considered.
*
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @return the value of the field
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the named field is not public
*/
public static Object readField(Object target, String fieldName) throws IllegalAccessException {
return readField(target, fieldName, false);
}
/**
* Read the named field. Superclasses will be considered.
*
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @return the field value
* @throws IllegalArgumentException if the class or field name is null
* @throws IllegalAccessException if the named field is not made accessible
*/
public static Object readField(Object target, String fieldName, boolean forceAccess)
throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
// already forced access above, don't repeat it here:
return readField(field, target);
}
/**
* Write a public static Field.
*
* @param field to write
* @param value to set
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not public or is final
*/
public static void writeStaticField(Field field, Object value) throws IllegalAccessException {
writeStaticField(field, value, false);
}
/**
* Write a static Field.
*
* @param field to write
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @throws IllegalArgumentException if the field is null or not static
* @throws IllegalAccessException if the field is not made accessible or is
* final
*/
public static void writeStaticField(Field field, Object value, boolean forceAccess)
throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (!Modifier.isStatic(field.getModifiers())) {
throw new IllegalArgumentException("The field '" + field.getName() + "' is not static");
}
writeField(field, (Object) null, value, forceAccess);
}
/**
* Write a named public static Field. Superclasses will be considered.
*
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @throws IllegalArgumentException if the field cannot be located or is not
* static
* @throws IllegalAccessException if the field is not public or is final
*/
public static void writeStaticField(Class cls, String fieldName, Object value)
throws IllegalAccessException {
writeStaticField(cls, fieldName, value, false);
}
/**
* Write a named static Field. Superclasses will be considered.
*
* @param cls Class on which the Field is to be found
* @param fieldName to write
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @throws IllegalArgumentException if the field cannot be located or is not
* static
* @throws IllegalAccessException if the field is not made accessible or is
* final
*/
public static void writeStaticField(Class cls, String fieldName, Object value,
boolean forceAccess) throws IllegalAccessException {
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls);
}
// already forced access above, don't repeat it here:
writeStaticField(field, value);
}
public static void writeStaticFinalField(Class<?> clazz, String fieldName, Object value, boolean forceAccess) throws Exception {
Field field = getField(clazz, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate field " + fieldName + " in " + clazz);
}
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.setAccessible(true);
field.set(null, value);
}
/**
* Write an accessible field.
*
* @param field to write
* @param target the object to call on, may be null for static fields
* @param value to set
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not accessible or is final
*/
public static void writeField(Field field, Object target, Object value)
throws IllegalAccessException {
writeField(field, target, value, false);
}
/**
* Write a field.
*
* @param field to write
* @param target the object to call on, may be null for static fields
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @throws IllegalArgumentException if the field is null
* @throws IllegalAccessException if the field is not made accessible or is
* final
*/
public static void writeField(Field field, Object target, Object value, boolean forceAccess)
throws IllegalAccessException {
if (field == null) {
throw new IllegalArgumentException("The field must not be null");
}
if (forceAccess && !field.isAccessible()) {
field.setAccessible(true);
} else {
MemberUtils.setAccessibleWorkaround(field);
}
field.set(target, value);
}
/**
* Write a public field. Superclasses will be considered.
*
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @throws IllegalArgumentException if <code>target</code> or
* <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not accessible
*/
public static void writeField(Object target, String fieldName, Object value)
throws IllegalAccessException {
writeField(target, fieldName, value, false);
}
/**
* Write a field. Superclasses will be considered.
*
* @param target the object to reflect, must not be null
* @param fieldName the field name to obtain
* @param value to set
* @param forceAccess whether to break scope restrictions using the
* <code>setAccessible</code> method. <code>False</code> will
* only match public fields.
* @throws IllegalArgumentException if <code>target</code> or
* <code>fieldName</code> is null
* @throws IllegalAccessException if the field is not made accessible
*/
public static void writeField(Object target, String fieldName, Object value, boolean forceAccess)
throws IllegalAccessException {
if (target == null) {
throw new IllegalArgumentException("target object must not be null");
}
Class cls = target.getClass();
Field field = getField(cls, fieldName, forceAccess);
if (field == null) {
throw new IllegalArgumentException("Cannot locate declared field " + cls.getName()
+ "." + fieldName);
}
// already forced access above, don't repeat it here:
writeField(field, target, value);
}
// Useful member methods
private static class MemberUtils {
private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED
| Modifier.PRIVATE;
public static void setAccessibleWorkaround(AccessibleObject o) {
if (o == null || o.isAccessible()) {
return;
}
Member m = (Member) o;
if (Modifier.isPublic(m.getModifiers())
&& isPackageAccess(m.getDeclaringClass().getModifiers())) {
try {
o.setAccessible(true);
} catch (SecurityException e) { // NOPMD
// ignore in favor of subsequent IllegalAccessException
}
}
}
/**
* Returns whether a given set of modifiers implies package access.
*
* @param modifiers to test
* @return true unless package/protected/private modifier detected
*/
public static boolean isPackageAccess(int modifiers) {
return (modifiers & ACCESS_TEST) == 0;
}
}
}
@@ -1,106 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
/**
* Represents a traditional int field enum.
*
* @author Kristian
*/
public class IntEnum {
// Used to convert between IDs and names
protected BiMap<Integer, String> members = HashBiMap.create();
/**
* Registers every declared integer field.
*/
public IntEnum() {
registerAll();
}
/**
* Registers every public int field as a member.
*/
protected void registerAll() {
try {
// Register every int field
for (Field entry : this.getClass().getFields()) {
if (entry.getType().equals(int.class)) {
registerMember(entry.getInt(this), entry.getName());
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* Registers a member.
* @param id - id of member.
* @param name - name of member.
*/
protected void registerMember(int id, String name) {
members.put(id, name);
}
/**
* Determines whether or not the given member exists.
* @param id - the ID of the member to find.
* @return TRUE if a member with the given ID exists, FALSE otherwise.
*/
public boolean hasMember(int id) {
return members.containsKey(id);
}
/**
* Retrieve the ID of the member with the given name.
* @param name - name of member to retrieve.
* @return ID of the member, or NULL if not found.
*/
public Integer valueOf(String name) {
return members.inverse().get(name);
}
/**
* Retrieve the name of the member with the given id.
* @param id - id of the member to retrieve.
* @return Declared name of the member, or NULL if not found.
*/
public String getDeclaredName(Integer id) {
return members.get(id);
}
/**
* Retrieve the ID of every registered member.
* @return Enumeration of every value.
*/
public Set<Integer> values() {
return new HashSet<Integer>(members.keySet());
}
}
File diff suppressed because it is too large. Load diff
@@ -1,685 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.error.PluginContext;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.instances.BannedGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
* Provides list-oriented access to the fields of a Minecraft packet.
* <p>
* Implemented by using reflection. Use a CompiledStructureModifier, if speed is essential.
*
* @author Kristian
* @param <TField> Type of the fields to retrieve.
*/
@SuppressWarnings("rawtypes")
public class StructureModifier<TField> {
// Object and its type
protected Class targetType;
protected Object target;
// Converter. May be NULL.
protected EquivalentConverter<TField> converter;
// The fields to read in order
protected Class fieldType;
protected List<Field> data = new ArrayList<Field>();
// Improved default values
protected Map<Field, Integer> defaultFields;
// Cache of previous types
protected Map<Class, StructureModifier> subtypeCache;
// Whether or subclasses should handle conversion
protected boolean customConvertHandling;
// Whether or not to automatically compile the structure modifier
protected boolean useStructureCompiler;
// Instance generator we wil use
private static DefaultInstances DEFAULT_GENERATOR = getDefaultGenerator();
private static DefaultInstances getDefaultGenerator() {
List<InstanceProvider> providers = Lists.newArrayList();
// Prevent certain classes from being generated
providers.add(new BannedGenerator(MinecraftReflection.getItemStackClass(), MinecraftReflection.getBlockClass()));
providers.addAll(DefaultInstances.DEFAULT.getRegistered());
return DefaultInstances.fromCollection(providers);
}
/**
* Creates a structure modifier.
* @param targetType - the structure to modify.
*/
public StructureModifier(Class targetType) {
this(targetType, null, true);
}
/**
* Creates a structure modifier.
* @param targetType - the structure to modify.
* @param useStructureCompiler - whether or not to use a structure compiler.
*/
public StructureModifier(Class targetType, boolean useStructureCompiler) {
this(targetType, null, true, useStructureCompiler);
}
/**
* Creates a structure modifier.
* @param targetType - the structure to modify.
* @param superclassExclude - a superclass to exclude.
* @param requireDefault - whether or not we will be using writeDefaults().
*/
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault) {
this(targetType, superclassExclude, requireDefault, true);
}
/**
* Creates a structure modifier.
* @param targetType - the structure to modify.
* @param superclassExclude - a superclass to exclude.
* @param requireDefault - whether or not we will be using writeDefaults().
* @param useStructureCompiler - whether or not to automatically compile this structure modifier.
*/
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault, boolean useStructureCompiler) {
List<Field> fields = getFields(targetType, superclassExclude);
Map<Field, Integer> defaults = requireDefault ? generateDefaultFields(fields) : new HashMap<Field, Integer>();
initialize(targetType, Object.class, fields, defaults, null,
new ConcurrentHashMap<Class, StructureModifier>(), useStructureCompiler);
}
/**
* Consumers of this method should call "initialize".
*/
protected StructureModifier() {
}
/**
* Initialize using the same field types.
* @param other - information to set.
*/
protected void initialize(StructureModifier<TField> other) {
initialize(other.targetType, other.fieldType, other.data,
other.defaultFields, other.converter, other.subtypeCache,
other.useStructureCompiler);
}
/**
* Initialize every field of this class.
* @param targetType - type of the object we're reading and writing from.
* @param fieldType - the common type of the fields we're modifying.
* @param data - list of fields to modify.
* @param defaultFields - list of fields that will be automatically initialized.
* @param converter - converts between the common field type and the actual type the consumer expects.
* @param subTypeCache - a structure modifier cache.
*/
protected void initialize(Class targetType, Class fieldType,
List<Field> data, Map<Field, Integer> defaultFields,
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache) {
initialize(targetType, fieldType, data, defaultFields, converter, subTypeCache, true);
}
/**
* Initialize every field of this class.
* @param targetType - type of the object we're reading and writing from.
* @param fieldType - the common type of the fields we're modifying.
* @param data - list of fields to modify.
* @param defaultFields - list of fields that will be automatically initialized.
* @param converter - converts between the common field type and the actual type the consumer expects.
* @param subTypeCache - a structure modifier cache.
* @param useStructureCompiler - whether or not to automatically compile this structure modifier.
*/
protected void initialize(Class targetType, Class fieldType,
List<Field> data, Map<Field, Integer> defaultFields,
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache,
boolean useStructureCompiler) {
this.targetType = targetType;
this.fieldType = fieldType;
this.data = data;
this.defaultFields = defaultFields;
this.converter = converter;
this.subtypeCache = subTypeCache;
this.useStructureCompiler = useStructureCompiler;
}
/**
* Reads the value of a field given its index.
* <p>
* Note: This method is prone to exceptions (there are currently 5 total throw statements). It is recommended that you
* use {@link #readSafely(int)}, which returns {@code null} if the field doesn't exist, instead of throwing an exception.
*
* @param fieldIndex - index of the field.
* @return Value of the field.
* @throws FieldAccessException if the field doesn't exist, or it cannot be accessed under the current security contraints.
*/
public TField read(int fieldIndex) throws FieldAccessException {
try {
return readInternal(fieldIndex);
} catch (FieldAccessException ex) {
String plugin = PluginContext.getPluginCaller(ex);
if (ProtocolLibrary.INCOMPATIBLE.contains(plugin)) {
ProtocolLogger.log(Level.WARNING, "Encountered an exception caused by incompatible plugin {0}.", plugin);
ProtocolLogger.log(Level.WARNING, "It is advised that you remove it.");
}
throw ex;
}
}
@SuppressWarnings("unchecked")
private TField readInternal(int fieldIndex) throws FieldAccessException {
if (target == null)
throw new IllegalStateException("Cannot read from a null target!");
if (fieldIndex < 0)
throw new FieldAccessException(String.format("Field index (%s) cannot be negative.", fieldIndex));
if (data.size() == 0)
throw new FieldAccessException(String.format("No field with type %s exists in class %s.", fieldType.getName(),
target.getClass().getSimpleName()));
if (fieldIndex >= data.size())
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
try {
Object result = FieldUtils.readField(data.get(fieldIndex), target, true);
// Use the converter, if we have it
if (needConversion()) {
return converter.getSpecific(result);
} else {
return (TField) result;
}
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot read field due to a security limitation.", e);
}
}
/**
* Reads the value of a field only if it exists. If the field does not exist, {@code null} is returned.
* <p>
* As its name implies, this method is a much safer alternative to {@link #read(int)}.
* In addition to throwing less exceptions and thereby causing less console spam, this
* method makes providing backwards compatiblity signficiantly easier, as shown below:
*
* <pre><code>
* BlockPosition position = packet.getBlockPositionModifier().readSafely(0);
* if (position != null) {
* // Handle 1.8+
* } else {
* // Handle 1.7-
* }
* </code></pre>
*
* @param fieldIndex - index of the field.
* @return Value of the field, or NULL if it doesn't exist.
* @throws FieldAccessException if the field cannot be accessed under the current security constraints.
*/
public TField readSafely(int fieldIndex) throws FieldAccessException {
if (fieldIndex >= 0 && fieldIndex < data.size()) {
return read(fieldIndex);
} else {
return null;
}
}
/**
* Determine whether or not a field is read-only (final).
* @param fieldIndex - index of the field.
* @return TRUE if the field by the given index is read-only, FALSE otherwise.
*/
public boolean isReadOnly(int fieldIndex) {
return Modifier.isFinal(getField(fieldIndex).getModifiers());
}
/**
* Determine if a given field is public or not.
* @param fieldIndex - field index.
* @return TRUE if the field is public, FALSE otherwise.
*/
public boolean isPublic(int fieldIndex) {
return Modifier.isPublic(getField(fieldIndex).getModifiers());
}
/**
* Set whether or not a field should be treated as read only.
* <p>
* Note that changing the read-only state to TRUE will only work if the current
* field was recently read-only or the current structure modifier hasn't been compiled yet.
*
* @param fieldIndex - index of the field.
* @param value - TRUE if this field should be read only, FALSE otherwise.
* @throws FieldAccessException If we cannot modify the read-only status.
*/
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
if (fieldIndex < 0 || fieldIndex >= data.size())
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
try {
StructureModifier.setFinalState(data.get(fieldIndex), value);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot write read only status due to a security limitation.", e);
}
}
/**
* Alter the final status of a field.
* @param field - the field to change.
* @param isReadOnly - TRUE if the field should be read only, FALSE otherwise.
* @throws IllegalAccessException If an error occured.
*/
protected static void setFinalState(Field field, boolean isReadOnly) throws IllegalAccessException {
if (isReadOnly)
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() | Modifier.FINAL, true);
else
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() & ~Modifier.FINAL, true);
}
/**
* Writes the value of a field given its index.
* @param fieldIndex - index of the field.
* @param value - new value of the field.
* @return This structure modifier - for chaining.
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
*/
public StructureModifier<TField> write(int fieldIndex, TField value) throws FieldAccessException {
try {
return writeInternal(fieldIndex, value);
} catch (FieldAccessException ex) {
String plugin = PluginContext.getPluginCaller(ex);
if (ProtocolLibrary.INCOMPATIBLE.contains(plugin)) {
ProtocolLogger.log(Level.WARNING, "Encountered an exception caused by incompatible plugin {0}.", plugin);
ProtocolLogger.log(Level.WARNING, "It is advised that you remove it.");
}
throw ex;
}
}
private StructureModifier<TField> writeInternal(int fieldIndex, TField value) throws FieldAccessException {
if (target == null)
throw new IllegalStateException("Cannot read from a null target!");
if (fieldIndex < 0)
throw new FieldAccessException(String.format("Field index (%s) cannot be negative.", fieldIndex));
if (data.size() == 0)
throw new FieldAccessException(String.format("No field with type %s exists in class %s.", fieldType.getName(),
target.getClass().getSimpleName()));
if (fieldIndex >= data.size())
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
// Use the converter, if it exists
Object obj = needConversion() ? converter.getGeneric(getFieldType(fieldIndex), value) : value;
try {
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot read field due to a security limitation.", e);
}
// Make this method chainable
return this;
}
/**
* Retrieve the type of a specified field.
* @param index - the index.
* @return The type of the given field.
*/
protected Class<?> getFieldType(int index) {
return data.get(index).getType();
}
/**
* Whether or not we should use the converter instance.
* @return TRUE if we should, FALSE otherwise.
*/
private final boolean needConversion() {
return converter != null && !customConvertHandling;
}
/**
* Writes the value of a given field IF and ONLY if it exists.
* @param fieldIndex - index of the potential field.
* @param value - new value of the field.
* @return This structure modifer - for chaining.
* @throws FieldAccessException The field cannot be accessed under the current security contraints.
*/
public StructureModifier<TField> writeSafely(int fieldIndex, TField value) throws FieldAccessException {
if (fieldIndex >= 0 && fieldIndex < data.size()) {
write(fieldIndex, value);
}
return this;
}
/**
* Correctly modifies the value of a field.
* @param fieldIndex - index of the field to modify.
* @param select - the function that modifies the field value.
* @return This structure modifier - for chaining.
* @throws FieldAccessException The field cannot be accessed under the current security contraints.
*/
public StructureModifier<TField> modify(int fieldIndex, Function<TField, TField> select) throws FieldAccessException {
TField value = read(fieldIndex);
return write(fieldIndex, select.apply(value));
}
/**
* Retrieves a structure modifier that only reads and writes fields of a given type.
* @param <T> Type
* @param fieldType - the type, or supertype, of every field to modify.
* @return A structure modifier for fields of this type.
*/
public <T> StructureModifier<T> withType(Class fieldType) {
return withType(fieldType, null);
}
/**
* Sets all non-primitive fields to a more fitting default value. See {@link DefaultInstances#getDefault(Class)}.
* @return The current structure modifier - for chaining.
* @throws FieldAccessException If we're unable to write to the fields due to a security limitation.
*/
public StructureModifier<TField> writeDefaults() throws FieldAccessException {
DefaultInstances generator = DefaultInstances.DEFAULT;
// Write a default instance to every field
for (Field field : defaultFields.keySet()) {
try {
// Special case for Spigot's custom chat components
// They must be null or messages will be blank
if (field.getType().getCanonicalName().equals("net.md_5.bungee.api.chat.BaseComponent[]")) {
FieldUtils.writeField(field, target, null, true);
continue;
}
FieldUtils.writeField(field, target, generator.getDefault(field.getType()), true);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot write to field due to a security limitation.", e);
}
}
return this;
}
/**
* Retrieves a structure modifier that only reads and writes fields of a given type.
* @param <T> Type
* @param fieldType - the type, or supertype, of every field to modify.
* @param converter - converts objects into the given type.
* @return A structure modifier for fields of this type.
*/
@SuppressWarnings("unchecked")
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
StructureModifier<T> result = subtypeCache.get(fieldType);
// Do we need to update the cache?
if (result == null) {
List<Field> filtered = new ArrayList<Field>();
Map<Field, Integer> defaults = new HashMap<Field, Integer>();
int index = 0;
for (Field field : data) {
if (fieldType != null && fieldType.isAssignableFrom(field.getType())) {
filtered.add(field);
// Don't use the original index
if (defaultFields.containsKey(field))
defaults.put(field, index);
}
// Keep track of the field index
index++;
}
// Cache structure modifiers
result = withFieldType(fieldType, filtered, defaults);
if (fieldType != null) {
subtypeCache.put(fieldType, result);
// Automatically compile the structure modifier
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
}
}
// Add the target too
result = result.withTarget(target);
// And the converter, if it's needed
if (!Objects.equal(result.converter, converter)) {
result = result.withConverter(converter);
}
return result;
}
/**
* Retrieves the common type of each field.
* @return Common type of each field.
*/
public Class getFieldType() {
return fieldType;
}
/**
* Retrieves the type of the object we're modifying.
* @return Type of the object.
*/
public Class getTargetType() {
return targetType;
}
/**
* Retrieves the object we're currently modifying.
* @return Object we're modifying.
*/
public Object getTarget() {
return target;
}
/**
* Retrieve the number of readable types.
* @return Readable types.
*/
public int size() {
return data.size();
}
/**
* Create a new structure modifier for the new field type.
* @param <T> Type
* @param fieldType - common type of each field.
* @param filtered - list of fields after filtering the original modifier.
* @param defaults - list of default values after filtering the original.
* @return A new structure modifier.
*/
protected <T> StructureModifier<T> withFieldType(
Class fieldType, List<Field> filtered, Map<Field, Integer> defaults) {
return withFieldType(fieldType, filtered, defaults, null);
}
/**
* Create a new structure modifier for the new field type.
* @param <T> Type
* @param fieldType - common type of each field.
* @param filtered - list of fields after filtering the original modifier.
* @param defaults - list of default values after filtering the original.
* @param converter - the new converter, or NULL.
* @return A new structure modifier.
*/
protected <T> StructureModifier<T> withFieldType(
Class fieldType, List<Field> filtered,
Map<Field, Integer> defaults, EquivalentConverter<T> converter) {
StructureModifier<T> result = new StructureModifier<T>();
result.initialize(targetType, fieldType, filtered, defaults,
converter, new ConcurrentHashMap<Class, StructureModifier>(),
useStructureCompiler);
return result;
}
/**
* Retrieves a structure modifier of the same type for a different object target.
* @param target - different target of the same type.
* @return Structure modifier with the new target.
*/
public StructureModifier<TField> withTarget(Object target) {
StructureModifier<TField> copy = new StructureModifier<TField>();
// Create a new instance
copy.initialize(this);
copy.target = target;
return copy;
}
/**
* Retrieves a structure modifier with the same type and target, but using a new object converter.
* @param converter - the object converter to use.
* @return Structure modifier with the new converter.
*/
@SuppressWarnings("unchecked")
private <T> StructureModifier<T> withConverter(EquivalentConverter<T> converter) {
StructureModifier copy = withTarget(target);
copy.setConverter(converter);
return copy;
}
/**
* Set the current object converter. Should only be called during construction.
* @param converter - current object converter.
*/
protected void setConverter(EquivalentConverter<TField> converter) {
this.converter = converter;
}
/**
* Retrieves a list of the fields matching the constraints of this structure modifier.
* @return List of fields.
*/
public List<Field> getFields() {
return ImmutableList.copyOf(data);
}
/**
* Retrieve a field by index.
* @param fieldIndex - index of the field to retrieve.
* @return The field represented with the given index.
* @throws IllegalArgumentException If no field with the given index can be found.
*/
public Field getField(int fieldIndex) {
if (fieldIndex < 0 || fieldIndex >= data.size())
throw new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
return data.get(fieldIndex);
}
/**
* Retrieve every value stored in the fields of the current type.
* @return Every field value.
* @throws FieldAccessException Unable to access one or all of the fields
*/
public List<TField> getValues() throws FieldAccessException {
List<TField> values = new ArrayList<TField>();
for (int i = 0; i < size(); i++) {
values.add(read(i));
}
return values;
}
// Used to generate plausible default values
private static Map<Field, Integer> generateDefaultFields(List<Field> fields) {
Map<Field, Integer> requireDefaults = new HashMap<Field, Integer>();
DefaultInstances generator = DEFAULT_GENERATOR;
int index = 0;
for (Field field : fields) {
Class<?> type = field.getType();
int modifier = field.getModifiers();
// First, ignore primitive fields and final fields
if (!type.isPrimitive() && !Modifier.isFinal(modifier)) {
// Next, see if we actually can generate a default value
if (generator.getDefault(type) != null) {
// If so, require it
requireDefaults.put(field, index);
}
}
// Increment field index
index++;
}
return requireDefaults;
}
// Used to filter out irrelevant fields
private static List<Field> getFields(Class type, Class superclassExclude) {
List<Field> result = new ArrayList<Field>();
// Retrieve every private and public field
for (Field field : FuzzyReflection.fromClass(type, true).getDeclaredFields(superclassExclude)) {
int mod = field.getModifiers();
// Ignore static and "abstract packet" fields
if (!Modifier.isStatic(mod) &&
(superclassExclude == null || !field.getDeclaringClass().equals(superclassExclude)
)) {
result.add(field);
}
}
return result;
}
@Override
public String toString() {
return "StructureModifier[fieldType=" + fieldType + ", data=" + data + "]";
}
}
@@ -1,243 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Objects;
/**
* Represents a field that will revert to its original state when this class is garbaged collected.
*
* @author Kristian
*/
public class VolatileField {
private FieldAccessor accessor;
private Object container;
// The current and previous values
private Object previous;
private Object current;
// Whether or not we must reset or load
private boolean previousLoaded;
private boolean currentSet;
// Whether or not to break access restrictions
private boolean forceAccess;
/**
* Initializes a volatile field with an associated object.
* @param field - the field.
* @param container - the object this field belongs to.
*/
public VolatileField(Field field, Object container) {
this.accessor = Accessors.getFieldAccessor(field);
this.container = container;
}
/**
* Initializes a volatile field with an associated object.
* @param field - the field.
* @param container - the object this field belongs to.
* @param forceAccess - whether or not to override any scope restrictions.
*/
public VolatileField(Field field, Object container, boolean forceAccess) {
this.accessor = Accessors.getFieldAccessor(field, true);
this.container = container;
this.forceAccess = forceAccess;
}
/**
* Initializes a volatile field with the given accessor and associated object.
* @param accessor - the field accessor.
* @param container - the object this field belongs to.
*/
public VolatileField(FieldAccessor accessor, Object container) {
this.accessor = accessor;
this.container = container;
}
/**
* Retrieves the current field.
* @return The stored field.
*/
public Field getField() {
return accessor.getField();
}
/**
* Retrieves the object the field is stored.
* @return The reference object.
*/
public Object getContainer() {
return container;
}
/**
* Retrieves whether or not not to override any scope restrictions.
* @return TRUE if we override scope, FALSE otherwise.
*/
public boolean isForceAccess() {
return forceAccess;
}
/**
* Sets whether or not not to override any scope restrictions.
* @param forceAccess - TRUE if we override scope, FALSE otherwise.
*/
public void setForceAccess(boolean forceAccess) {
this.forceAccess = forceAccess;
}
/**
* Retrieves the current field value.
* @return The current field value.
*/
public Object getValue() {
// Retrieve the correct value
if (!currentSet) {
ensureLoaded();
return previous;
} else {
return current;
}
}
/**
* Retrieves the field value before the previous setValue(), unless saveValue() has been called.
* @return Previous value.
*/
public Object getOldValue() {
ensureLoaded();
return previous;
}
/**
* Sets the current value. This will be reverted unless saveValue() is called.
* @param newValue - new field value.
*/
public void setValue(Object newValue) {
// Remember to safe the previous value
ensureLoaded();
writeFieldValue(newValue);
current = newValue;
currentSet = true;
}
/**
* Reapply the current changed value.
* <p>
* Also refresh the previously set value.
*/
public void refreshValue() {
Object fieldValue = readFieldValue();
if (currentSet) {
// If they differ, we need to set them again
if (!Objects.equal(current, fieldValue)) {
previous = readFieldValue();
previousLoaded = true;
writeFieldValue(current);
}
} else if (previousLoaded) {
// Update that too
previous = fieldValue;
}
}
/**
* Ensure that the current value is still set after this class has been garbaged collected.
*/
public void saveValue() {
previous = current;
currentSet = false;
}
/**
* Revert to the previously set value.
*/
public void revertValue() {
// Reset value if it hasn't been changed by anyone else
if (currentSet) {
if (getValue() == current) {
setValue(previous);
currentSet = false;
} else {
// This can be a bad sign
ProtocolLogger.log("Unable to switch {0} to {1}. Expected {2}, but got {3}.", getField().toGenericString(), previous, current, getValue());
}
}
}
/**
* Retrieve a synchronized version of the current field.
* @return A synchronized volatile field.
*/
public VolatileField toSynchronized() {
return new VolatileField(Accessors.getSynchronized(accessor), container);
}
/**
* Determine whether or not we'll need to revert the value.
* @return True if it is set, false if not.
*/
public boolean isCurrentSet() {
return currentSet;
}
private void ensureLoaded() {
// Load the value if we haven't already
if (!previousLoaded) {
previous = readFieldValue();
previousLoaded = true;
}
}
/**
* Read the content of the underlying field.
* @return The field value.
*/
private Object readFieldValue() {
return accessor.get(container);
}
/**
* Write the given value to the underlying field.
* @param newValue - the new value.
*/
private void writeFieldValue(Object newValue) {
accessor.set(container, newValue);
}
@Override
protected void finalize() throws Throwable {
revertValue();
}
@Override
public String toString() {
return "VolatileField [accessor=" + accessor + ", container=" + container + ", previous="
+ previous + ", current=" + current + ", previousLoaded=" + previousLoaded
+ ", currentSet=" + currentSet + ", forceAccess=" + forceAccess + "]";
}
}
@@ -1,298 +0,0 @@
package com.comphenix.protocol.reflect.accessors;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import com.comphenix.protocol.reflect.ExactReflection;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.google.common.base.Joiner;
public final class Accessors {
/**
* Represents a field accessor that synchronizes access to the underlying field.
* @author Kristian
*/
public static final class SynchronizedFieldAccessor implements FieldAccessor {
private final FieldAccessor accessor;
private SynchronizedFieldAccessor(FieldAccessor accessor) {
this.accessor = accessor;
}
@Override
public void set(Object instance, Object value) {
Object lock = accessor.get(instance);
if (lock != null) {
synchronized (lock) {
accessor.set(instance, value);
}
} else {
accessor.set(instance, value);
}
}
@Override
public Object get(Object instance) {
return accessor.get(instance);
}
@Override
public Field getField() {
return accessor.getField();
}
}
/**
* Retrieve an accessor for the first field of the given type.
* @param instanceClass - the type of the instance to retrieve.
* @param fieldClass - type of the field to retrieve.
* @param forceAccess - whether or not to look for private and protected fields.
* @return The field accessor.
* @throws IllegalArgumentException If the field cannot be found.
*/
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
// Get a field accessor
Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass);
return Accessors.getFieldAccessor(field);
}
/**
* Retrieve an accessor (in declared order) for every field of the givne type.
* @param instanceClass - the type of the instance to retrieve.
* @param fieldClass - type of the field(s) to retrieve.
* @param forceAccess - whether or not to look for private and protected fields.
* @return The accessors.
*/
public static FieldAccessor[] getFieldAccessorArray(Class<?> instanceClass, Class<?> fieldClass, boolean forceAccess) {
List<Field> fields = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldListByType(fieldClass);
FieldAccessor[] accessors = new FieldAccessor[fields.size()];
for (int i = 0; i < accessors.length; i++) {
accessors[i] = getFieldAccessor(fields.get(i));
}
return accessors;
}
/**
* Retrieve an accessor for the first field of the given type.
* @param instanceClass - the type of the instance to retrieve.
* @param fieldName - name of the field to retrieve.
* @param forceAccess - whether or not to look for private and protected fields.
* @return The value of that field.
* @throws IllegalArgumentException If the field cannot be found.
*/
public static FieldAccessor getFieldAccessor(Class<?> instanceClass, String fieldName, boolean forceAccess) {
return Accessors.getFieldAccessor(ExactReflection.fromClass(instanceClass, true).getField(fieldName));
}
/**
* Retrieve a field accessor from a given field that uses unchecked exceptions.
* @param field - the field.
* @return The field accessor.
*/
public static FieldAccessor getFieldAccessor(final Field field) {
return Accessors.getFieldAccessor(field, true);
}
/**
* Retrieve a field accessor from a given field that uses unchecked exceptions.
* @param field - the field.
* @param forceAccess - whether or not to skip Java access checking.
* @return The field accessor.
*/
public static FieldAccessor getFieldAccessor(final Field field, boolean forceAccess) {
field.setAccessible(true);
return new DefaultFieldAccessor(field);
}
/**
* Retrieve a field accessor for a field with the given name and equivalent type, or NULL.
* @param clazz - the declaration class.
* @param fieldName - the field name.
* @param fieldType - assignable field type.
* @return The field accessor, or NULL if not found.
*/
public static FieldAccessor getFieldAcccessorOrNull(Class<?> clazz, String fieldName, Class<?> fieldType) {
try {
FieldAccessor accessor = Accessors.getFieldAccessor(clazz, fieldName, true);
// Verify the type
if (fieldType.isAssignableFrom(accessor.getField().getType())) {
return accessor;
}
return null;
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Retrieve a method accessor for a field with the given name and equivalent type, or NULL.
* @param clazz - the declaration class.
* @param methodName - the method name.
* @return The method accessor, or NULL if not found.
*/
public static MethodAccessor getMethodAcccessorOrNull(Class<?> clazz, String methodName) {
try {
return Accessors.getMethodAccessor(clazz, methodName);
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Find a specific constructor in a class.
* @param clazz - the class.
* @param parameters - the signature of the constructor to find.
* @return The constructor, or NULL if not found.
*/
public static ConstructorAccessor getConstructorAccessorOrNull(Class<?> clazz, Class<?>... parameters) {
try {
return Accessors.getConstructorAccessor(clazz, parameters);
} catch (IllegalArgumentException e) {
return null; // Not found
}
}
/**
* Retrieve a field accessor that will cache the content of the field.
* <p>
* Note that we don't check if the underlying field has changed after the value has been cached,
* so it's best to use this on final fields.
* @param inner - the accessor.
* @return A cached field accessor.
*/
public static FieldAccessor getCached(final FieldAccessor inner) {
return new FieldAccessor() {
private final Object EMPTY = new Object();
private volatile Object value = EMPTY;
@Override
public void set(Object instance, Object value) {
inner.set(instance, value);
update(value);
}
@Override
public Object get(Object instance) {
Object cache = value;
if (cache != EMPTY)
return cache;
return update(inner.get(instance));
}
/**
* Update the cached value.
* @param value - the value to cache.
* @return The cached value.
*/
private Object update(Object value) {
return this.value = value;
}
@Override
public Field getField() {
return inner.getField();
}
};
}
/**
* Retrieve a field accessor where the write operation is synchronized on the current field value.
* @param accessor - the accessor.
* @return The field accessor.
*/
public static FieldAccessor getSynchronized(final FieldAccessor accessor) {
// Only wrap once
if (accessor instanceof SynchronizedFieldAccessor)
return accessor;
return new SynchronizedFieldAccessor(accessor);
}
/**
* Retrieve a method accessor that always return a constant value, regardless if input.
* @param returnValue - the constant return value.
* @param method - the method.
* @return A constant method accessor.
*/
public static MethodAccessor getConstantAccessor(final Object returnValue, final Method method) {
return new MethodAccessor() {
@Override
public Object invoke(Object target, Object... args) {
return returnValue;
}
@Override
public Method getMethod() {
return method;
}
};
}
/**
* Retrieve a method accessor for a method with the given name and signature.
* @param instanceClass - the parent class.
* @param methodName - the method name.
* @param parameters - the parameters.
* @return The method accessor.
*/
public static MethodAccessor getMethodAccessor(Class<?> instanceClass, String methodName, Class<?>... parameters) {
return new DefaultMethodAccessor(ExactReflection.fromClass(instanceClass, true).getMethod(methodName, parameters));
}
/**
* Retrieve a method accessor for a particular method, avoding checked exceptions.
* @param method - the method to access.
* @return The method accessor.
*/
public static MethodAccessor getMethodAccessor(final Method method) {
return getMethodAccessor(method, true);
}
/**
* Retrieve a method accessor for a particular method, avoding checked exceptions.
* @param method - the method to access.
* @param forceAccess - whether or not to skip Java access checking.
* @return The method accessor.
*/
public static MethodAccessor getMethodAccessor(final Method method, boolean forceAccess) {
method.setAccessible(forceAccess);
return new DefaultMethodAccessor(method);
}
/**
* Retrieve a constructor accessor for a constructor with the given signature.
* @param instanceClass - the parent class.
* @param parameters - the parameters.
* @return The constructor accessor.
* @throws IllegalArgumentException If we cannot find this constructor.
* @throws IllegalStateException If we cannot access reflection.
*/
public static ConstructorAccessor getConstructorAccessor(Class<?> instanceClass, Class<?>... parameters) {
try {
return getConstructorAccessor(instanceClass.getDeclaredConstructor(parameters));
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(String.format(
"Unable to find constructor %s(%s).", instanceClass, Joiner.on(",").join(parameters))
);
} catch (SecurityException e) {
throw new IllegalStateException("Cannot access constructors.", e);
}
}
/**
* Retrieve a constructor accessor for a particular constructor, avoding checked exceptions.
* @param constructor - the constructor to access.
* @return The method accessor.
*/
public static ConstructorAccessor getConstructorAccessor(final Constructor<?> constructor) {
return new DefaultConstrutorAccessor(constructor);
}
// Seal this class
private Accessors() {
}
}
@@ -1,54 +0,0 @@
package com.comphenix.protocol.reflect.accessors;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
final class DefaultConstrutorAccessor implements ConstructorAccessor {
private final Constructor<?> constructor;
public DefaultConstrutorAccessor(Constructor<?> method) {
this.constructor = method;
}
@Override
public Object invoke(Object... args) {
try {
return constructor.newInstance(args);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot use reflection.", e);
} catch (IllegalArgumentException e) {
throw e;
} catch (InvocationTargetException e) {
throw new RuntimeException("An internal error occured.", e.getCause());
} catch (InstantiationException e) {
throw new RuntimeException("Cannot instantiate object.", e);
}
}
@Override
public Constructor<?> getConstructor() {
return constructor;
}
@Override
public int hashCode() {
return constructor != null ? constructor.hashCode() : 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof DefaultConstrutorAccessor) {
DefaultConstrutorAccessor other = (DefaultConstrutorAccessor) obj;
return other.constructor == constructor;
}
return true;
}
@Override
public String toString() {
return "DefaultConstrutorAccessor [constructor=" + constructor + "]";
}
}
@@ -1,60 +0,0 @@
package com.comphenix.protocol.reflect.accessors;
import java.lang.reflect.Field;
final class DefaultFieldAccessor implements FieldAccessor {
private final Field field;
public DefaultFieldAccessor(Field field) {
this.field = field;
}
@Override
public Object get(Object instance) {
try {
return field.get(instance);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Cannot read " + field, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot use reflection.", e);
}
}
@Override
public void set(Object instance, Object value) {
try {
field.set(instance, value);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Cannot set field " + field + " to value " + value, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot use reflection.", e);
}
}
@Override
public Field getField() {
return field;
}
@Override
public int hashCode() {
return field != null ? field.hashCode() : 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof DefaultFieldAccessor) {
DefaultFieldAccessor other = (DefaultFieldAccessor) obj;
return other.field == field;
}
return true;
}
@Override
public String toString() {
return "DefaultFieldAccessor [field=" + field + "]";
}
}
@@ -1,52 +0,0 @@
package com.comphenix.protocol.reflect.accessors;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
final class DefaultMethodAccessor implements MethodAccessor {
private final Method method;
public DefaultMethodAccessor(Method method) {
this.method = method;
}
@Override
public Object invoke(Object target, Object... args) {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot use reflection.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("An internal error occured.", e.getCause());
} catch (IllegalArgumentException e) {
throw e;
}
}
@Override
public Method getMethod() {
return method;
}
@Override
public int hashCode() {
return method != null ? method.hashCode() : 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof DefaultMethodAccessor) {
DefaultMethodAccessor other = (DefaultMethodAccessor) obj;
return other.method == method;
}
return true;
}
@Override
public String toString() {
return "DefaultMethodAccessor [method=" + method + "]";
}
}
@@ -1,8 +0,0 @@
package com.comphenix.protocol.reflect.accessors;
public abstract class ReadOnlyFieldAccessor implements FieldAccessor {
@Override
public final void set(Object instance, Object value) {
throw new UnsupportedOperationException("Cannot update the content of a read-only field accessor.");
}
}
@@ -1,120 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Map;
import java.util.Map.Entry;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.google.common.collect.Maps;
/**
* Represents an object that can clone a specific list of Bukkit- and Minecraft-related objects.
*
* @author Kristian
*/
public class BukkitCloner implements Cloner {
// List of classes we support
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
public BukkitCloner() {
addClass(0, MinecraftReflection.getItemStackClass());
addClass(1, MinecraftReflection.getDataWatcherClass());
// Try to add position classes
try {
addClass(2, MinecraftReflection.getBlockPositionClass());
} catch (Throwable ex) {
}
try {
addClass(3, MinecraftReflection.getChunkPositionClass());
} catch (Throwable ex) {
}
if (MinecraftReflection.isUsingNetty()) {
addClass(4, MinecraftReflection.getServerPingClass());
}
if (MinecraftReflection.watcherObjectExists()) {
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
addClass(6, MinecraftReflection.getMinecraftKeyClass());
}
}
private void addClass(int id, Class<?> clazz) {
if (clazz != null)
clonableClasses.put(id, clazz);
}
private int findMatchingClass(Class<?> type) {
// See if is a subclass of any of our supported superclasses
for (Entry<Integer, Class<?>> entry : clonableClasses.entrySet()) {
if (entry.getValue().isAssignableFrom(type)) {
return entry.getKey();
}
}
return -1;
}
@Override
public boolean canClone(Object source) {
if (source == null)
return false;
return findMatchingClass(source.getClass()) >= 0;
}
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
// Convert to a wrapper
switch (findMatchingClass(source.getClass())) {
case 0:
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
case 1:
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
return dataConverter.getGeneric(clonableClasses.get(1), dataConverter.getSpecific(source).deepClone());
case 2:
EquivalentConverter<BlockPosition> blockConverter = BlockPosition.getConverter();
return blockConverter.getGeneric(clonableClasses.get(2), blockConverter.getSpecific(source));
case 3:
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
return chunkConverter.getGeneric(clonableClasses.get(3), chunkConverter.getSpecific(source));
case 4:
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
case 5:
return source;
case 6:
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
return keyConverter.getGeneric(clonableClasses.get(5), keyConverter.getSpecific(source));
default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
}
}
}
@@ -1,373 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.StructureCompiler.StructureKey;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
* Compiles structure modifiers on a background thread.
* <p>
* This is necessary as we cannot block the main thread.
*
* @author Kristian
*/
public class BackgroundCompiler {
public static final ReportType REPORT_CANNOT_COMPILE_STRUCTURE_MODIFIER = new ReportType("Cannot compile structure. Disabing compiler.");
public static final ReportType REPORT_CANNOT_SCHEDULE_COMPILATION = new ReportType("Unable to schedule compilation task.");
/**
* The default format for the name of new worker threads.
*/
public static final String THREAD_FORMAT = "ProtocolLib-StructureCompiler %s";
// How long to wait for a shutdown
public static final int SHUTDOWN_DELAY_MS = 2000;
/**
* The default fraction of perm gen space after which the background compiler will be disabled.
*/
public static final double DEFAULT_DISABLE_AT_PERM_GEN = 0.65;
// The single background compiler we're using
private static BackgroundCompiler backgroundCompiler;
// Classes we're currently compiling
private Map<StructureKey, List<CompileListener<?>>> listeners = Maps.newHashMap();
private Object listenerLock = new Object();
private StructureCompiler compiler;
private boolean enabled;
private boolean shuttingDown;
private ExecutorService executor;
private ErrorReporter reporter;
private double disablePermGenFraction = DEFAULT_DISABLE_AT_PERM_GEN;
/**
* Retrieves the current background compiler.
* @return Current background compiler.
*/
public static BackgroundCompiler getInstance() {
return backgroundCompiler;
}
/**
* Sets the single background compiler we're using.
* @param backgroundCompiler - current background compiler, or NULL if the library is not loaded.
*/
public static void setInstance(BackgroundCompiler backgroundCompiler) {
BackgroundCompiler.backgroundCompiler = backgroundCompiler;
}
/**
* Initialize a background compiler.
* <p>
* Uses the default {@link #THREAD_FORMAT} to name worker threads.
* @param loader - class loader from Bukkit.
* @param reporter - current error reporter.
*/
public BackgroundCompiler(ClassLoader loader, ErrorReporter reporter) {
ThreadFactory factory = new ThreadFactoryBuilder().
setDaemon(true).
setNameFormat(THREAD_FORMAT).
build();
initializeCompiler(loader, reporter, Executors.newSingleThreadExecutor(factory));
}
/**
* Initialize a background compiler utilizing the given thread pool.
* @param loader - class loader from Bukkit.
* @param reporter - current error reporter.
* @param executor - thread pool we'll use.
*/
public BackgroundCompiler(ClassLoader loader, ErrorReporter reporter, ExecutorService executor) {
initializeCompiler(loader, reporter, executor);
}
// Avoid "Constructor call must be the first statement".
private void initializeCompiler(ClassLoader loader, ErrorReporter reporter, ExecutorService executor) {
if (loader == null)
throw new IllegalArgumentException("loader cannot be NULL");
if (executor == null)
throw new IllegalArgumentException("executor cannot be NULL");
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
this.compiler = new StructureCompiler(loader);
this.reporter = reporter;
this.executor = executor;
this.enabled = true;
}
/**
* Ensure that the indirectly given structure modifier is eventually compiled.
* @param cache - store of structure modifiers.
* @param key - key of the structure modifier to compile.
*/
@SuppressWarnings("rawtypes")
public void scheduleCompilation(final Map<Class, StructureModifier> cache, final Class key) {
@SuppressWarnings("unchecked")
final StructureModifier<Object> uncompiled = cache.get(key);
if (uncompiled != null) {
scheduleCompilation(uncompiled, new CompileListener<Object>() {
@Override
public void onCompiled(StructureModifier<Object> compiledModifier) {
// Update cache
cache.put(key, compiledModifier);
}
});
}
}
/**
* Ensure that the given structure modifier is eventually compiled.
* @param <TKey> Type
* @param uncompiled - structure modifier to compile.
* @param listener - listener responsible for responding to the compilation.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public <TKey> void scheduleCompilation(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
// Only schedule if we're enabled
if (enabled && !shuttingDown) {
// Check perm gen
if (getPermGenUsage() > disablePermGenFraction)
return;
// Don't try to schedule anything
if (executor == null || executor.isShutdown())
return;
// Use to look up structure modifiers
final StructureKey key = new StructureKey(uncompiled);
// Allow others to listen in too
synchronized (listenerLock) {
List list = listeners.get(key);
if (!listeners.containsKey(key)) {
listeners.put(key, (List) Lists.newArrayList(listener));
} else {
// We're currently compiling
list.add(listener);
return;
}
}
// Create the worker that will compile our modifier
Callable<?> worker = new Callable<Object>() {
@Override
public Object call() throws Exception {
StructureModifier<TKey> modifier = uncompiled;
List list = null;
// Do our compilation
try {
modifier = compiler.compile(modifier);
synchronized (listenerLock) {
list = listeners.get(key);
// Prevent ConcurrentModificationExceptions
if (list != null) {
list = Lists.newArrayList(list);
}
}
// Only execute the listeners if there is a list
if (list != null) {
for (Object compileListener : list) {
((CompileListener<TKey>) compileListener).onCompiled(modifier);
}
// Remove it when we're done
synchronized (listenerLock) {
list = listeners.remove(key);
}
}
} catch (OutOfMemoryError e) {
setEnabled(false);
throw e;
} catch (ThreadDeath e) {
setEnabled(false);
throw e;
} catch (Throwable e) {
// Disable future compilations!
setEnabled(false);
// Inform about this error as best as we can
reporter.reportDetailed(BackgroundCompiler.this,
Report.newBuilder(REPORT_CANNOT_COMPILE_STRUCTURE_MODIFIER).callerParam(uncompiled).error(e)
);
}
// We'll also return the new structure modifier
return modifier;
}
};
try {
// Lookup the previous class name on the main thread.
// This is necessary as the Bukkit class loaders are not thread safe
if (compiler.lookupClassLoader(uncompiled)) {
try {
worker.call();
} catch (Exception e) {
// Impossible!
e.printStackTrace();
}
} else {
// Perform the compilation on a seperate thread
executor.submit(worker);
}
} catch (RejectedExecutionException e) {
// Occures when the underlying queue is overflowing. Since the compilation
// is only an optmization and not really essential we'll just log this failure
// and move on.
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SCHEDULE_COMPILATION).error(e));
}
}
}
/**
* Add a compile listener if we are still waiting for the structure modifier to be compiled.
* @param <TKey> Type
* @param uncompiled - the structure modifier that may get compiled.
* @param listener - the listener to invoke in that case.
*/
@SuppressWarnings("unchecked")
public <TKey> void addListener(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
synchronized (listenerLock) {
StructureKey key = new StructureKey(uncompiled);
@SuppressWarnings("rawtypes")
List list = listeners.get(key);
if (list != null) {
list.add(listener);
}
}
}
/**
* Retrieve the current usage of the Perm Gen space in percentage.
* @return Usage of the perm gen space.
*/
private double getPermGenUsage() {
for (MemoryPoolMXBean item : ManagementFactory.getMemoryPoolMXBeans()) {
if (item.getName().contains("Perm Gen")) {
MemoryUsage usage = item.getUsage();
return usage.getUsed() / (double) usage.getCommitted();
}
}
// Unknown
return 0;
}
/**
* Clean up after ourselves using the default timeout.
*/
public void shutdownAll() {
shutdownAll(SHUTDOWN_DELAY_MS, TimeUnit.MILLISECONDS);
}
/**
* Clean up after ourselves.
* @param timeout - the maximum time to wait.
* @param unit - the time unit of the timeout argument.
*/
public void shutdownAll(long timeout, TimeUnit unit) {
setEnabled(false);
shuttingDown = true;
executor.shutdown();
try {
executor.awaitTermination(timeout, unit);
} catch (InterruptedException e) {
// Unlikely to ever occur - it's the main thread
e.printStackTrace();
}
}
/**
* Retrieve whether or not the background compiler is enabled.
* @return TRUE if it is enabled, FALSE otherwise.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Sets whether or not the background compiler is enabled.
* @param enabled - TRUE to enable it, FALSE otherwise.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Retrieve the fraction of perm gen space used after which the background compiler will be disabled.
* @return The fraction after which the background compiler is disabled.
*/
public double getDisablePermGenFraction() {
return disablePermGenFraction;
}
/**
* Set the fraction of perm gen space used after which the background compiler will be disabled.
* @param fraction - the maximum use of perm gen space.
*/
public void setDisablePermGenFraction(double fraction) {
this.disablePermGenFraction = fraction;
}
/**
* Retrieve the current structure compiler.
* @return Current structure compiler.
*/
public StructureCompiler getCompiler() {
return compiler;
}
}
@@ -1,294 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
/**
* Used by the compiler to automatically box and unbox values.
*/
class BoxingHelper {
private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()");
private final static MethodDescriptor INT_VALUE = MethodDescriptor.getMethod("int intValue()");
private final static MethodDescriptor FLOAT_VALUE = MethodDescriptor.getMethod("float floatValue()");
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
private MethodVisitor mv;
public BoxingHelper(MethodVisitor mv) {
this.mv = mv;
}
/**
* Generates the instructions to box the top stack value. This value is
* replaced by its boxed equivalent on top of the stack.
*
* @param type the type of the top stack value.
*/
public void box(final Type type){
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if(type == Type.VOID_TYPE) {
push((String) null);
} else {
Type boxed = type;
switch(type.getSort()) {
case Type.BYTE:
boxed = BYTE_TYPE;
break;
case Type.BOOLEAN:
boxed = BOOLEAN_TYPE;
break;
case Type.SHORT:
boxed = SHORT_TYPE;
break;
case Type.CHAR:
boxed = CHARACTER_TYPE;
break;
case Type.INT:
boxed = INTEGER_TYPE;
break;
case Type.FLOAT:
boxed = FLOAT_TYPE;
break;
case Type.LONG:
boxed = LONG_TYPE;
break;
case Type.DOUBLE:
boxed = DOUBLE_TYPE;
break;
}
newInstance(boxed);
if(type.getSize() == 2) {
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
dupX2();
dupX2();
pop();
} else {
// p -> po -> opo -> oop -> o
dupX1();
swap();
}
invokeConstructor(boxed, new MethodDescriptor("<init>", Type.VOID_TYPE, new Type[] {type}));
}
}
/**
* Generates the instruction to invoke a constructor.
*
* @param type the class in which the constructor is defined.
* @param method the constructor to be invoked.
*/
public void invokeConstructor(final Type type, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKESPECIAL, type, method);
}
/**
* Generates a DUP_X1 instruction.
*/
public void dupX1(){
mv.visitInsn(Opcodes.DUP_X1);
}
/**
* Generates a DUP_X2 instruction.
*/
public void dupX2(){
mv.visitInsn(Opcodes.DUP_X2);
}
/**
* Generates a POP instruction.
*/
public void pop(){
mv.visitInsn(Opcodes.POP);
}
/**
* Generates a SWAP instruction.
*/
public void swap(){
mv.visitInsn(Opcodes.SWAP);
}
/**
* Generates the instruction to push the given value on the stack.
*
* @param value the value to be pushed on the stack.
*/
public void push(final boolean value){
push(value ? 1 : 0);
}
/**
* Generates the instruction to push the given value on the stack.
*
* @param value the value to be pushed on the stack.
*/
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
/**
* Generates the instruction to create a new object.
*
* @param type the class of the object to be created.
*/
public void newInstance(final Type type){
typeInsn(Opcodes.NEW, type);
}
/**
* Generates the instruction to push the given value on the stack.
*
* @param value the value to be pushed on the stack. May be <tt>null</tt>.
*/
public void push(final String value) {
if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
} else {
mv.visitLdcInsn(value);
}
}
/**
* Generates the instructions to unbox the top stack value. This value is
* replaced by its unboxed equivalent on top of the stack.
*
* @param type
* the type of the top stack value.
*/
public void unbox(final Type type){
Type t = NUMBER_TYPE;
MethodDescriptor sig = null;
switch(type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
t = CHARACTER_TYPE;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = BOOLEAN_TYPE;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
sig = INT_VALUE;
}
if(sig == null) {
checkCast(type);
} else {
checkCast(t);
invokeVirtual(t, sig);
}
}
/**
* Generates the instruction to check that the top stack value is of the
* given type.
*
* @param type a class or interface type.
*/
public void checkCast(final Type type){
if(!type.equals(OBJECT_TYPE)) {
typeInsn(Opcodes.CHECKCAST, type);
}
}
/**
* Generates the instruction to invoke a normal method.
*
* @param owner the class in which the method is defined.
* @param method the method to be invoked.
*/
public void invokeVirtual(final Type owner, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
}
/**
* Generates an invoke method instruction.
*
* @param opcode the instruction's opcode.
* @param type the class in which the method is defined.
* @param method the method to be invoked.
*/
private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor());
}
/**
* Generates a type dependent instruction.
*
* @param opcode the instruction's opcode.
* @param type the instruction's operand.
*/
private void typeInsn(final int opcode, final Type type){
String desc;
if(type.getSort() == Type.ARRAY) {
desc = type.getDescriptor();
} else {
desc = type.getInternalName();
}
mv.visitTypeInsn(opcode, desc);
}
}
@@ -1,34 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import com.comphenix.protocol.reflect.StructureModifier;
/**
* Used to save the result of an compilation.
*
* @author Kristian
* @param <TKey> - type of the structure modifier field.
*/
public interface CompileListener<TKey> {
/**
* Invoked when a structure modifier has been successfully compiled.
* @param compiledModifier - the compiled structure modifier.
*/
public void onCompiled(StructureModifier<TKey> compiledModifier);
}
@@ -1,136 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.google.common.collect.Sets;
/**
* Represents a compiled structure modifier.
*
* @author Kristian
*/
public abstract class CompiledStructureModifier extends StructureModifier<Object> {
// Used to compile instances of structure modifiers
protected StructureCompiler compiler;
// Fields that originally were read only
private Set<Integer> exempted;
public CompiledStructureModifier() {
super();
customConvertHandling = true;
}
@Override
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
// We can remove the read-only status
if (isReadOnly(fieldIndex) && !value) {
if (exempted == null)
exempted = Sets.newHashSet();
exempted.add(fieldIndex);
}
// We can only make a certain kind of field read only
if (!isReadOnly(fieldIndex) && value) {
if (exempted == null || !exempted.contains(fieldIndex)) {
throw new IllegalStateException("Cannot make compiled field " + fieldIndex + " read only.");
}
}
super.setReadOnly(fieldIndex, value);
}
// Speed up the default writer
@Override
public StructureModifier<Object> writeDefaults() throws FieldAccessException {
DefaultInstances generator = DefaultInstances.DEFAULT;
// Write a default instance to every field
for (Map.Entry<Field, Integer> entry : defaultFields.entrySet()) {
Integer index = entry.getValue();
Field field = entry.getKey();
// Special case for Spigot's custom chat components
// They must be null or messages will be blank
if (field.getType().getCanonicalName().equals("net.md_5.bungee.api.chat.BaseComponent[]")) {
write(index, null);
continue;
}
write(index, generator.getDefault(field.getType()));
}
return this;
}
@Override
public final Object read(int fieldIndex) throws FieldAccessException {
Object result = readGenerated(fieldIndex);
if (converter != null)
return converter.getSpecific(result);
else
return result;
}
/**
* Read the given field index using reflection.
* @param index - index of field.
* @return Resulting value.
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
*/
protected Object readReflected(int index) throws FieldAccessException {
return super.read(index);
}
protected abstract Object readGenerated(int fieldIndex) throws FieldAccessException;
@Override
public StructureModifier<Object> write(int index, Object value) throws FieldAccessException {
if (converter != null)
value = converter.getGeneric(getFieldType(index), value);
return writeGenerated(index, value);
}
/**
* Write the given field using reflection.
* @param index - index of field.
* @param value - new value.
* @throws FieldAccessException The field doesn't exist, or it cannot be accessed under the current security contraints.
*/
protected void writeReflected(int index, Object value) throws FieldAccessException {
super.write(index, value);
}
protected abstract StructureModifier<Object> writeGenerated(int index, Object value) throws FieldAccessException;
@Override
public StructureModifier<Object> withTarget(Object target) {
if (compiler != null)
return compiler.compile(super.withTarget(target));
else
return super.withTarget(target);
}
}
@@ -1,57 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.ClassVisitor;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.MethodVisitor;
public abstract class EmptyClassVisitor implements ClassVisitor {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// NOP
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// NOP
return null;
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// NOP
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// NOP
return null;
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
// NOP
}
@Override
public void visitSource(String source, String debug) {
// NOP
}
}
@@ -1,132 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
public class EmptyMethodVisitor implements MethodVisitor {
@Override
public AnnotationVisitor visitAnnotationDefault() {
// NOP
return null;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitCode() {
// NOP
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
// NOP
}
@Override
public void visitInsn(int opcode) {
// NOP
}
@Override
public void visitIntInsn(int opcode, int operand) {
// NOP
}
@Override
public void visitVarInsn(int opcode, int var) {
// NOP
}
@Override
public void visitTypeInsn(int opcode, String type) {
// NOP
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitJumpInsn(int opcode, Label label) {
// NOP
}
@Override
public void visitLabel(Label label) {
// NOP
}
@Override
public void visitLdcInsn(Object cst) {
// NOP
}
@Override
public void visitIincInsn(int var, int increment) {
// NOP
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
// NOP
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
// NOP
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
// NOP
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
// NOP
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
// NOP
}
@Override
public void visitLineNumber(int line, Label start) {
// NOP
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
}
@@ -1,237 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import java.util.HashMap;
import java.util.Map;
import net.sf.cglib.asm.Type;
/**
* Represents a method.
*/
class MethodDescriptor {
/**
* The method name.
*/
private final String name;
/**
* The method descriptor.
*/
private final String desc;
/**
* Maps primitive Java type names to their descriptors.
*/
private static final Map<String, String> DESCRIPTORS;
static {
DESCRIPTORS = new HashMap<String, String>();
DESCRIPTORS.put("void", "V");
DESCRIPTORS.put("byte", "B");
DESCRIPTORS.put("char", "C");
DESCRIPTORS.put("double", "D");
DESCRIPTORS.put("float", "F");
DESCRIPTORS.put("int", "I");
DESCRIPTORS.put("long", "J");
DESCRIPTORS.put("short", "S");
DESCRIPTORS.put("boolean", "Z");
}
/**
* Creates a new {@link Method}.
*
* @param name the method's name.
* @param desc the method's descriptor.
*/
public MethodDescriptor(final String name, final String desc) {
this.name = name;
this.desc = desc;
}
/**
* Creates a new {@link Method}.
*
* @param name the method's name.
* @param returnType the method's return type.
* @param argumentTypes the method's argument types.
*/
public MethodDescriptor(
final String name,
final Type returnType,
final Type[] argumentTypes)
{
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
}
/**
* Returns a {@link Method} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
* form "returnType name (argumentType1, ... argumentTypeN)", where
* the types are in plain Java (e.g. "int", "float",
* "java.util.List", ...). Classes of the java.lang package can be
* specified by their unqualified name; all other classes names must
* be fully qualified.
* @return a {@link Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
*/
public static MethodDescriptor getMethod(final String method)
throws IllegalArgumentException
{
return getMethod(method, false);
}
/**
* Returns a {@link Method} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
* form "returnType name (argumentType1, ... argumentTypeN)", where
* the types are in plain Java (e.g. "int", "float",
* "java.util.List", ...). Classes of the java.lang package may be
* specified by their unqualified name, depending on the
* defaultPackage argument; all other classes names must be fully
* qualified.
* @param defaultPackage true if unqualified class names belong to the
* default package, or false if they correspond to java.lang classes.
* For instance "Object" means "Object" if this option is true, or
* "java.lang.Object" otherwise.
* @return a {@link Method} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
*/
public static MethodDescriptor getMethod(
final String method,
final boolean defaultPackage) throws IllegalArgumentException
{
int space = method.indexOf(' ');
int start = method.indexOf('(', space) + 1;
int end = method.indexOf(')', start);
if (space == -1 || start == -1 || end == -1) {
throw new IllegalArgumentException();
}
String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim();
StringBuffer sb = new StringBuffer();
sb.append('(');
int p;
do {
String s;
p = method.indexOf(',', start);
if (p == -1) {
s = map(method.substring(start, end).trim(), defaultPackage);
} else {
s = map(method.substring(start, p).trim(), defaultPackage);
start = p + 1;
}
sb.append(s);
} while (p != -1);
sb.append(')');
sb.append(map(returnType, defaultPackage));
return new MethodDescriptor(methodName, sb.toString());
}
private static String map(final String type, final boolean defaultPackage) {
if ("".equals(type)) {
return type;
}
StringBuffer sb = new StringBuffer();
int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('[');
}
String t = type.substring(0, type.length() - sb.length() * 2);
String desc = DESCRIPTORS.get(t);
if (desc != null) {
sb.append(desc);
} else {
sb.append('L');
if (t.indexOf('.') < 0) {
if (!defaultPackage) {
sb.append("java/lang/");
}
sb.append(t);
} else {
sb.append(t.replace('.', '/'));
}
sb.append(';');
}
return sb.toString();
}
/**
* Returns the name of the method described by this object.
*
* @return the name of the method described by this object.
*/
public String getName() {
return name;
}
/**
* Returns the descriptor of the method described by this object.
*
* @return the descriptor of the method described by this object.
*/
public String getDescriptor() {
return desc;
}
/**
* Returns the return type of the method described by this object.
*
* @return the return type of the method described by this object.
*/
public Type getReturnType() {
return Type.getReturnType(desc);
}
/**
* Returns the argument types of the method described by this object.
*
* @return the argument types of the method described by this object.
*/
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(desc);
}
public String toString() {
return name + desc;
}
public boolean equals(final Object o) {
if (!(o instanceof MethodDescriptor)) {
return false;
}
MethodDescriptor other = (MethodDescriptor) o;
return name.equals(other.name) && desc.equals(other.desc);
}
public int hashCode() {
return name.hashCode() ^ desc.hashCode();
}
}
@@ -1,548 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.compiler;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.common.base.Objects;
import com.google.common.primitives.Primitives;
// public class CompiledStructureModifierPacket20<TField> extends CompiledStructureModifier<TField> {
//
// private Packet20NamedEntitySpawn typedTarget;
//
// public CompiledStructureModifierPacket20(StructureModifier<TField> other, StructureCompiler compiler) {
// super();
// initialize(other);
// this.target = other.getTarget();
// this.typedTarget = (Packet20NamedEntitySpawn) target;
// this.compiler = compiler;
// }
//
// @Override
// protected Object readGenerated(int fieldIndex) throws FieldAccessException {
//
// Packet20NamedEntitySpawn target = typedTarget;
//
// switch (fieldIndex) {
// case 0: return (Object) target.a;
// case 1: return (Object) target.b;
// case 2: return (Object) target.c;
// case 3: return super.readReflected(fieldIndex);
// case 4: return super.readReflected(fieldIndex);
// case 5: return (Object) target.f;
// case 6: return (Object) target.g;
// case 7: return (Object) target.h;
// default:
// throw new FieldAccessException("Invalid index " + fieldIndex);
// }
// }
//
// @Override
// protected StructureModifier<TField> writeGenerated(int index, Object value) throws FieldAccessException {
//
// Packet20NamedEntitySpawn target = typedTarget;
//
// switch (index) {
// case 0: target.a = (Integer) value; break;
// case 1: target.b = (String) value; break;
// case 2: target.c = (Integer) value; break;
// case 3: target.d = (Integer) value; break;
// case 4: super.writeReflected(index, value); break;
// case 5: super.writeReflected(index, value); break;
// case 6: target.g = (Byte) value; break;
// case 7: target.h = (Integer) value; break;
// default:
// throw new FieldAccessException("Invalid index " + index);
// }
//
// // Chaining
// return this;
// }
// }
/**
* Represents a StructureModifier compiler.
*
* @author Kristian
*/
public final class StructureCompiler {
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)");
// Used to store generated classes of different types
@SuppressWarnings("rawtypes")
static class StructureKey {
private Class targetType;
private Class fieldType;
public StructureKey(StructureModifier<?> source) {
this(source.getTargetType(), source.getFieldType());
}
public StructureKey(Class targetType, Class fieldType) {
this.targetType = targetType;
this.fieldType = fieldType;
}
@Override
public int hashCode() {
return Objects.hashCode(targetType, fieldType);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof StructureKey) {
StructureKey other = (StructureKey) obj;
return Objects.equal(targetType, other.targetType) &&
Objects.equal(fieldType, other.fieldType);
}
return false;
}
}
// Used to load classes
private volatile static Method defineMethod;
@SuppressWarnings("rawtypes")
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
// The class loader we'll store our classes
private ClassLoader loader;
// References to other classes
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
private static String COMPILED_CLASS = PACKAGE_NAME + "/CompiledStructureModifier";
private static String FIELD_EXCEPTION_CLASS = "com/comphenix/protocol/reflect/FieldAccessException";
public static boolean attemptClassLoad = false;
/**
* Construct a structure compiler.
* @param loader - main class loader.
*/
StructureCompiler(ClassLoader loader) {
this.loader = loader;
}
/**
* Lookup the current class loader for any previously generated classes before we attempt to generate something.
* @param <TField> Type
* @param source - the structure modifier to look up.
* @return TRUE if we successfully found a previously generated class, FALSE otherwise.
*/
public <TField> boolean lookupClassLoader(StructureModifier<TField> source) {
StructureKey key = new StructureKey(source);
// See if there's a need to lookup the class name
if (compiledCache.containsKey(key)) {
return true;
}
if (! attemptClassLoad) {
return false;
}
// This causes a ton of lag and doesn't seem to work
try {
String className = getCompiledName(source);
// This class might have been generated before. Try to load it.
Class<?> before = loader.loadClass(PACKAGE_NAME.replace('/', '.') + "." + className);
if (before != null) {
compiledCache.put(key, before);
return true;
}
} catch (ClassNotFoundException e) {
// That's ok.
}
// We need to compile the class
return false;
}
/**
* Compiles the given structure modifier.
* <p>
* WARNING: Do NOT call this method in the main thread. Compiling may easily take 10 ms, which is already
* over 1/4 of a tick (50 ms). Let the background thread automatically compile the structure modifiers instead.
* @param <TField> Type
* @param source - structure modifier to compile.
* @return A compiled structure modifier.
*/
@SuppressWarnings("unchecked")
public synchronized <TField> StructureModifier<TField> compile(StructureModifier<TField> source) {
// We cannot optimize a structure modifier with no public fields
if (!isAnyPublic(source.getFields())) {
return source;
}
StructureKey key = new StructureKey(source);
Class<?> compiledClass = compiledCache.get(key);
if (!compiledCache.containsKey(key)) {
compiledClass = generateClass(source);
compiledCache.put(key, compiledClass);
}
// Next, create an instance of this class
try {
return (StructureModifier<TField>) compiledClass.getConstructor(
StructureModifier.class, StructureCompiler.class).
newInstance(source, this);
} catch (OutOfMemoryError e) {
// Print the number of generated classes by the current instances
ProtocolLibrary.getErrorReporter().reportWarning(
this, Report.newBuilder(REPORT_TOO_MANY_GENERATED_CLASSES).messageParam(compiledCache.size())
);
throw e;
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Used invalid parameters in instance creation", e);
} catch (SecurityException e) {
throw new RuntimeException("Security limitation!", e);
} catch (InstantiationException e) {
throw new RuntimeException("Error occured while instancing generated class.", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Security limitation! Cannot create instance of dynamic class.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Error occured while instancing generated class.", e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Cannot happen.", e);
}
}
/**
* Retrieve a variable identifier that can uniquely represent the given type.
* @param type - a type.
* @return A unique and legal identifier for the given type.
*/
private String getSafeTypeName(Class<?> type) {
return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
}
/**
* Retrieve the compiled name of a given structure modifier.
* @param source - the structure modifier.
* @return The unique, compiled name of a compiled structure modifier.
*/
private String getCompiledName(StructureModifier<?> source) {
Class<?> targetType = source.getTargetType();
// Concat class and field type
return "CompiledStructure$" +
getSafeTypeName(targetType) + "$" +
getSafeTypeName(source.getFieldType());
}
/**
* Compile a structure modifier.
* @param source - structure modifier.
* @return The compiled structure modifier.
*/
private <TField> Class<?> generateClass(StructureModifier<TField> source) {
ClassWriter cw = new ClassWriter(0);
Class<?> targetType = source.getTargetType();
String className = getCompiledName(source);
String targetSignature = Type.getDescriptor(targetType);
String targetName = targetType.getName().replace('.', '/');
// Define class
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
null, COMPILED_CLASS, null);
createFields(cw, targetSignature);
createConstructor(cw, className, targetSignature, targetName);
createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
cw.visitEnd();
byte[] data = cw.toByteArray();
// Call the define method
try {
if (defineMethod == null) {
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
new Class<?>[] { String.class, byte[].class, int.class, int.class });
// Awesome. Now, create and return it.
defined.setAccessible(true);
defineMethod = defined;
}
@SuppressWarnings("rawtypes")
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
// DEBUG CODE: Print the content of the generated class.
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
return clazz;
} catch (SecurityException e) {
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Incompatible JVM.", e);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Cannot call defineMethod - wrong JVM?", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Security limitation! Cannot dynamically load class.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Error occured in code generator.", e);
}
}
/**
* Determine if at least one of the given fields is public.
* @param fields - field to test.
* @return TRUE if one or more field is publically accessible, FALSE otherwise.
*/
private boolean isAnyPublic(List<Field> fields) {
// Are any of the fields public?
for (int i = 0; i < fields.size(); i++) {
if (isPublic(fields.get(i))) {
return true;
}
}
return false;
}
private boolean isPublic(Field field) {
return Modifier.isPublic(field.getModifiers());
}
private boolean isNonFinal(Field field) {
return !Modifier.isFinal(field.getModifiers());
}
private void createFields(ClassWriter cw, String targetSignature) {
FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
typedField.visitEnd();
}
private void createWriteMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;";
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
new String[] { FIELD_EXCEPTION_CLASS });
BoxingHelper boxingHelper = new BoxingHelper(mv);
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ASTORE, 3);
mv.visitVarInsn(Opcodes.ILOAD, 1);
// The last label is for the default switch
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label();
Label returnLabel = new Label();
// Generate labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
}
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
Class<?> outputType = field.getType();
Class<?> inputType = Primitives.wrap(outputType);
String typeDescriptor = Type.getDescriptor(outputType);
String inputPath = inputType.getName().replace('.', '/');
mv.visitLabel(labels[i]);
// Push the compare object
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
// Only write to public non-final fields
if (isPublic(field) && isNonFinal(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 2);
if (!outputType.isPrimitive())
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath);
else
boxingHelper.unbox(Type.getType(outputType));
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
} else {
// Use reflection. We don't have a choice, unfortunately.
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
}
mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
}
mv.visitLabel(errorLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW);
mv.visitLabel(returnLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(5, 4);
mv.visitEnd();
}
private void createReadMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
BoxingHelper boxingHelper = new BoxingHelper(mv);
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ASTORE, 2);
mv.visitVarInsn(Opcodes.ILOAD, 1);
// The last label is for the default switch
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label();
// Generate labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
}
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, labels);
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
Class<?> outputType = field.getType();
String typeDescriptor = Type.getDescriptor(outputType);
mv.visitLabel(labels[i]);
// Push the compare object
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
// Note that byte code cannot access non-public fields
if (isPublic(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
boxingHelper.box(Type.getType(outputType));
} else {
// We have to use reflection for private and protected fields.
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
}
mv.visitInsn(Opcodes.ARETURN);
}
mv.visitLabel(errorLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW);
mv.visitMaxs(5, 3);
mv.visitEnd();
}
private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
String fullClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitTypeInsn(Opcodes.CHECKCAST, targetName);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 3);
mv.visitEnd();
}
}
@@ -1,149 +0,0 @@
package com.comphenix.protocol.reflect.fuzzy;
import com.google.common.primitives.Ints;
/**
* Represents a matcher for fields, methods, constructors and classes.
* <p>
* This class should ideally never expose mutable state. Its round number must be immutable.
* @author Kristian
*/
public abstract class AbstractFuzzyMatcher<T> implements Comparable<AbstractFuzzyMatcher<T>> {
private Integer roundNumber;
/**
* Determine if the given value is a match.
* @param value - the value to match.
* @param parent - the parent container, or NULL if this value is the root.
* @return TRUE if it is a match, FALSE otherwise.
*/
public abstract boolean isMatch(T value, Object parent);
/**
* Calculate the round number indicating when this matcher should be applied.
* <p>
* Matchers with a lower round number are applied before matchers with a higher round number.
* <p>
* By convention, this round number should be negative, except for zero in the case of a matcher
* that accepts any value. A good implementation should return the inverted tree depth (class hierachy)
* of the least specified type used in the matching. Thus {@link Integer} will have a lower round number than
* {@link Number}.
*
* @return A number (positive or negative) that is used to order matchers.
*/
protected abstract int calculateRoundNumber();
/**
* Retrieve the cached round number. This should never change once calculated.
* <p>
* Matchers with a lower round number are applied before matchers with a higher round number.
* @return The round number.
* @see #calculateRoundNumber()
*/
public final int getRoundNumber() {
if (roundNumber == null) {
return roundNumber = calculateRoundNumber();
} else {
return roundNumber;
}
}
/**
* Combine two round numbers by taking the highest non-zero number, or return zero.
* @param roundA - the first round number.
* @param roundB - the second round number.
* @return The combined round number.
*/
protected final int combineRounds(int roundA, int roundB) {
if (roundA == 0)
return roundB;
else if (roundB == 0)
return roundA;
else
return Math.max(roundA, roundB);
}
/**
* Combine n round numbers by taking the highest non-zero number, or return zero.
* @param rounds - the round numbers.
* @return The combined round number.
*/
protected final int combineRounds(Integer... rounds) {
if (rounds.length < 2)
throw new IllegalArgumentException("Must supply at least two arguments.");
// Get the seed
int reduced = combineRounds(rounds[0], rounds[1]);
// Aggregate it all
for (int i = 2; i < rounds.length; i++) {
reduced = combineRounds(reduced, rounds[i]);
}
return reduced;
}
@Override
public int compareTo(AbstractFuzzyMatcher<T> obj) {
return Ints.compare(getRoundNumber(), obj.getRoundNumber());
}
/**
* Create a fuzzy matcher that returns the opposite result of the current matcher.
* @return An inverted fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> inverted() {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
return !AbstractFuzzyMatcher.this.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return -2;
}
};
}
/**
* Require that this and the given matcher be TRUE.
* @param other - the other fuzzy matcher.
* @return A combined fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> and(final AbstractFuzzyMatcher<T> other) {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
// They both have to be true
return AbstractFuzzyMatcher.this.isMatch(value, parent) &&
other.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
}
};
}
/**
* Require that either this or the other given matcher be TRUE.
* @param other - the other fuzzy matcher.
* @return A combined fuzzy matcher.
*/
public AbstractFuzzyMatcher<T> or(final AbstractFuzzyMatcher<T> other) {
return new AbstractFuzzyMatcher<T>() {
@Override
public boolean isMatch(T value, Object parent) {
// Either can be true
return AbstractFuzzyMatcher.this.isMatch(value, parent) ||
other.isMatch(value, parent);
}
@Override
protected int calculateRoundNumber() {
return combineRounds(AbstractFuzzyMatcher.this.getRoundNumber(), other.getRoundNumber());
}
};
}
}
@@ -1,139 +0,0 @@
package com.comphenix.protocol.reflect.fuzzy;
import com.google.common.base.Objects;
/**
* Used to check class equality.
*
* @author Kristian
*/
class ClassExactMatcher extends AbstractFuzzyMatcher<Class<?>> {
/**
* Different matching rules.
*/
enum Options {
/**
* Match classes exactly.
*/
MATCH_EXACT,
/**
* A match if the input class is a superclass of the matcher class, or the same class.
*/
MATCH_SUPER,
/**
* A match if the input class is a derived class of the matcher class, or the same class.
*/
MATCH_DERIVED
}
/**
* Match any class.
*/
public static final ClassExactMatcher MATCH_ALL = new ClassExactMatcher(null, Options.MATCH_SUPER);
private final Class<?> matcher;
private final Options option;
/**
* Constructs a new class matcher.
* @param matcher - the matching class, or NULL to represent anything.
* @param option - options specifying the matching rules.
*/
ClassExactMatcher(Class<?> matcher, Options option) {
this.matcher = matcher;
this.option = option;
}
/**
* Determine if a given class is equivalent.
* <p>
* If the matcher is NULL, the result will only be TRUE if we're not matching exactly.
* @param input - the input class defined in the source file.
* @param parent - the container that holds a reference to this class.
* @return TRUE if input matches according to the rules in {@link #getOptions()}, FALSE otherwise.
*/
@Override
public boolean isMatch(Class<?> input, Object parent) {
if (input == null)
throw new IllegalArgumentException("Input class cannot be NULL.");
// Do our checking
if (matcher == null)
return option != Options.MATCH_EXACT;
else if (option == Options.MATCH_SUPER)
return input.isAssignableFrom(matcher); // matcher instanceof input
else if (option == Options.MATCH_DERIVED)
return matcher.isAssignableFrom(input); // input instanceof matcher
else
return input.equals(matcher);
}
@Override
protected int calculateRoundNumber() {
return -getClassNumber(matcher);
}
/**
* Retrieve the number of superclasses of the specific class.
* <p>
* Object is represented as one. All interfaces are one, unless they're derived.
* @param clazz - the class to test.
* @return The number of superclasses.
*/
public static int getClassNumber(Class<?> clazz) {
int count = 0;
// Move up the hierachy
while (clazz != null) {
count++;
clazz = clazz.getSuperclass();
}
return count;
}
/**
* Retrieve the class we're comparing against.
* @return Class to compare against.
*/
public Class<?> getMatcher() {
return matcher;
}
/**
* The matching rules for this class matcher.
* @return The current matching option.
*/
public Options getOptions() {
return option;
}
@Override
public String toString() {
if (option == Options.MATCH_SUPER)
return matcher + " instanceof input";
else if (option == Options.MATCH_DERIVED)
return "input instanceof " + matcher;
else
return "Exact " + matcher;
}
@Override
public int hashCode() {
return Objects.hashCode(matcher, option);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof ClassExactMatcher) {
ClassExactMatcher other = (ClassExactMatcher) obj;
return Objects.equal(matcher, other.matcher) &&
Objects.equal(option, other.option);
}
return false;
}
}
@@ -1,58 +0,0 @@
package com.comphenix.protocol.reflect.fuzzy;
import java.util.regex.Pattern;
import com.google.common.base.Objects;
/**
* Determine if a class matches based on its name using a regular expression.
*
* @author Kristian
*/
class ClassRegexMatcher extends AbstractFuzzyMatcher<Class<?>> {
private final Pattern regex;
private final int priority;
public ClassRegexMatcher(Pattern regex, int priority) {
if (regex == null)
throw new IllegalArgumentException("Regular expression pattern cannot be NULL.");
this.regex = regex;
this.priority = priority;
}
@Override
public boolean isMatch(Class<?> value, Object parent) {
if (value != null)
return regex.matcher(value.getCanonicalName()).matches();
else
return false;
}
@Override
protected int calculateRoundNumber() {
return -priority;
}
@Override
public String toString() {
return "class name of " + regex.toString();
}
@Override
public int hashCode() {
return Objects.hashCode(regex, priority);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof ClassRegexMatcher) {
ClassRegexMatcher other = (ClassRegexMatcher) obj;
return priority == other.priority &&
FuzzyMatchers.checkPattern(regex, other.regex);
}
return false;
}
}
@@ -1,57 +0,0 @@
package com.comphenix.protocol.reflect.fuzzy;
import java.util.Set;
import com.google.common.base.Objects;
/**
* Represents a class matcher that checks for equality using a given set of classes.
*
* @author Kristian
*/
class ClassSetMatcher extends AbstractFuzzyMatcher<Class<?>> {
private final Set<Class<?>> classes;
public ClassSetMatcher(Set<Class<?>> classes) {
if (classes == null)
throw new IllegalArgumentException("Set of classes cannot be NULL.");
this.classes = classes;
}
@Override
public boolean isMatch(Class<?> value, Object parent) {
return classes.contains(value);
}
@Override
protected int calculateRoundNumber() {
int roundNumber = 0;
// The highest round number (except zero).
for (Class<?> clazz : classes) {
roundNumber = combineRounds(roundNumber, -ClassExactMatcher.getClassNumber(clazz));
}
return roundNumber;
}
@Override
public String toString() {
return "match any: " + classes;
}
@Override
public int hashCode() {
return classes.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof ClassSetMatcher) {
// See if the sets are equal
return Objects.equal(classes, ((ClassSetMatcher) obj).classes);
}
return true;
}
}
@@ -1,155 +0,0 @@
package com.comphenix.protocol.timing;
/**
* Represents an online algortihm for computing the mean and standard deviation without storing every value.
* @author Kristian
*/
public class StatisticsStream extends OnlineComputation {
// This algorithm is due to Donald Knuth, as described in:
// Donald E. Knuth (1998). The Art of Computer Programming, volume 2:
// Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.
private int count = 0;
private double mean = 0;
private double m2 = 0;
// Also keep track of minimum and maximum observation
private double minimum = Double.MAX_VALUE;
private double maximum = 0;
/**
* Construct a new stream with no observations.
*/
public StatisticsStream() {
}
/**
* Construct a copy of the given stream.
* @param other - copy of the stream.
*/
public StatisticsStream(StatisticsStream other) {
this.count = other.count;
this.mean = other.mean;
this.m2 = other.m2;
this.minimum = other.minimum;
this.maximum = other.maximum;
}
@Override
public StatisticsStream copy() {
return new StatisticsStream(this);
}
/**
* Observe a value.
* @param value - the observed value.
*/
@Override
public void observe(double value) {
double delta = value - mean;
// As per Knuth
count++;
mean += delta / count;
m2 += delta * (value - mean);
// Update extremes
if (value < minimum)
minimum = value;
if (value > maximum)
maximum = value;
}
/**
* Retrieve the average of all the observations.
* @return The average.
*/
public double getMean() {
checkCount();
return mean;
}
/**
* Retrieve the variance of all the observations.
* @return The variance.
*/
public double getVariance() {
checkCount();
return m2 / (count - 1);
}
/**
* Retrieve the standard deviation of all the observations.
* @return The STDV.
*/
public double getStandardDeviation() {
return Math.sqrt(getVariance());
}
/**
* Retrieve the minimum observation yet observed.
* @return The minimum observation.
*/
public double getMinimum() {
checkCount();
return minimum;
}
/**
* Retrieve the maximum observation yet observed.
* @return The maximum observation.
*/
public double getMaximum() {
checkCount();
return maximum;
}
/**
* Combine the two statistics.
* @param other - the other statistics.
* @return Combined statistics
*/
public StatisticsStream add(StatisticsStream other) {
// Special cases
if (count == 0)
return other;
else if (other.count == 0)
return this;
StatisticsStream stream = new StatisticsStream();
double delta = other.mean - mean;
double n = count + other.count;
stream.count = (int) n;
stream.mean = mean + delta * (other.count / n);
stream.m2 = m2 + other.m2 + ((delta * delta) * (count * other.count) / n);
stream.minimum = Math.min(minimum, other.minimum);
stream.maximum = Math.max(maximum, other.maximum);
return stream;
}
/**
* Retrieve the number of observations.
* @return Number of observations.
*/
@Override
public int getCount() {
return count;
}
private void checkCount() {
if (count == 0) {
throw new IllegalStateException("No observations in stream.");
}
}
@Override
public String toString() {
if (count == 0)
return "StatisticsStream [Nothing recorded]";
return String.format("StatisticsStream [Average: %.3f, SD: %.3f, Min: %.3f, Max: %.3f, Count: %s]",
getMean(), getStandardDeviation(),
getMinimum(), getMaximum(), getCount());
}
}
@@ -1,35 +0,0 @@
package com.comphenix.protocol.utility;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* Represents an input stream that delegates to a byte buffer.
* @author Kristian
*/
public class ByteBufferInputStream extends InputStream {
private ByteBuffer buf;
public ByteBufferInputStream(ByteBuffer buf) {
this.buf = buf;
}
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xFF;
}
public int read(byte[] bytes, int off, int len)
throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
}
@@ -1,26 +0,0 @@
package com.comphenix.protocol.utility;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Represents an output stream that is backed by a ByteBuffer.
* @author Kristian
*/
public class ByteBufferOutputStream extends OutputStream {
ByteBuffer buf;
public ByteBufferOutputStream(ByteBuffer buf) {
this.buf = buf;
}
public void write(int b) throws IOException {
buf.put((byte) b);
}
public void write(byte[] bytes, int off, int len)
throws IOException {
buf.put(bytes, off, len);
}
}
@@ -1,95 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.util.Map;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
/**
* Represents a dynamic package and an arbitrary number of cached classes.
*
* @author Kristian
*/
class CachedPackage {
private final Map<String, Class<?>> cache;
private final String packageName;
private final ClassSource source;
/**
* Construct a new cached package.
* @param packageName - the name of the current package.
* @param source - the class source.
*/
public CachedPackage(String packageName, ClassSource source) {
this.packageName = packageName;
this.cache = Maps.newConcurrentMap();
this.source = source;
}
/**
* Associate a given class with a class name.
* @param className - class name.
* @param clazz - type of class.
*/
public void setPackageClass(String className, Class<?> clazz) {
cache.put(className, clazz);
}
/**
* Retrieve the class object of a specific class in the current package.
* @param className - the specific class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
public Class<?> getPackageClass(String className) {
try {
Class<?> result = cache.get(Preconditions.checkNotNull(className, "className cannot be NULL"));
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {
// Look up the class dynamically
result = source.loadClass(combine(packageName, className));
if (result == null)
throw new IllegalArgumentException("Source " + source + " returned NULL for " + className);
cache.put(className, result);
}
return result;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find class " + combine(packageName, className), e);
}
}
/**
* Correctly combine a package name and the child class we're looking for.
* @param packageName - name of the package, or an empty string for the default package.
* @param className - the class name.
* @return We full class path.
*/
public static String combine(String packageName, String className) {
if (Strings.isNullOrEmpty(packageName))
return className;
if (Strings.isNullOrEmpty(className))
return packageName;
return packageName + "." + className;
}
}
@@ -1,229 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
/**
* Utility methods for sending chat messages.
*
* @author Kristian
*/
public class ChatExtensions {
// Used to sent chat messages
private ProtocolManager manager;
// The chat packet constructor
private static volatile PacketConstructor chatConstructor;
// Whether or not we have to use the post-1.6.1 chat format
private static volatile Constructor<?> jsonConstructor = getJsonFormatConstructor();
private static volatile MethodAccessor messageFactory;
public ChatExtensions(ProtocolManager manager) {
this.manager = manager;
}
/**
* Send a message without invoking the packet listeners.
* @param receiver - the receiver.
* @param message - the message to send.
* @throws InvocationTargetException If we were unable to send the message.
*/
public void sendMessageSilently(CommandSender receiver, String message) throws InvocationTargetException {
if (receiver == null)
throw new IllegalArgumentException("receiver cannot be NULL.");
if (message == null)
throw new IllegalArgumentException("message cannot be NULL.");
// Handle the player case by manually sending packets
if (receiver instanceof Player) {
sendMessageSilently((Player) receiver, message);
} else {
receiver.sendMessage(message);
}
}
/**
* Send a message without invoking the packet listeners.
* @param player - the player to send it to.
* @param message - the message to send.
* @throws InvocationTargetException If we were unable to send the message.
*/
private void sendMessageSilently(Player player, String message) throws InvocationTargetException {
try {
for (PacketContainer packet : createChatPackets(message)) {
manager.sendServerPacket(player, packet, false);
}
} catch (FieldAccessException e) {
throw new InvocationTargetException(e);
}
}
/**
* Construct chat packet to send in order to display a given message.
* @param message - the message to send.
* @return The packets.
*/
public static PacketContainer[] createChatPackets(String message) {
if (jsonConstructor != null) {
if (chatConstructor == null) {
Class<?> messageClass = jsonConstructor.getParameterTypes()[0];
chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { messageClass });
// Try one of the string constructors
if (MinecraftReflection.isUsingNetty()) {
messageFactory = Accessors.getMethodAccessor(
MinecraftReflection.getCraftMessageClass(), "fromString", String.class);
} else {
messageFactory = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(messageClass).getMethod(
FuzzyMethodContract.newBuilder().
requireModifier(Modifier.STATIC).
parameterCount(1).
parameterExactType(String.class).
returnTypeMatches(FuzzyMatchers.matchParent()).
build())
);
}
}
// Minecraft 1.7.2 and later
if (MinecraftReflection.isUsingNetty()) {
Object[] components = (Object[]) messageFactory.invoke(null, message);
PacketContainer[] packets = new PacketContainer[components.length];
for (int i = 0; i < components.length; i++) {
packets[i] = chatConstructor.createPacket(components[i]);
}
return packets;
// Minecraft 1.6.1 - 1.6.4
} else {
return new PacketContainer[] { chatConstructor.createPacket(messageFactory.invoke(null, message)) };
}
} else {
if (chatConstructor == null) {
chatConstructor = PacketConstructor.DEFAULT.withPacket(PacketType.Play.Server.CHAT, new Object[] { message });
}
// Minecraft 1.6.0 and earlier
return new PacketContainer[] { chatConstructor.createPacket(message) };
}
}
/**
* Broadcast a message without invoking any packet listeners.
* @param message - message to send.
* @param permission - permission required to receieve the message. NULL to target everyone.
* @throws InvocationTargetException If we were unable to send the message.
*/
public void broadcastMessageSilently(String message, String permission) throws InvocationTargetException {
if (message == null)
throw new IllegalArgumentException("message cannot be NULL.");
// Send this message to every online player
for (Player player : Util.getOnlinePlayers()) {
if (permission == null || player.hasPermission(permission)) {
sendMessageSilently(player, message);
}
}
}
/**
* Print a flower box around a given message.
* @param message - the message to print.
* @param marginChar - the character to use as margin.
* @param marginWidth - the width (in characters) of the left and right margin.
* @param marginHeight - the height (in characters) of the top and buttom margin.
* @return Flowerboxed message
*/
public static String[] toFlowerBox(String[] message, String marginChar, int marginWidth, int marginHeight) {
String[] output = new String[message.length + marginHeight * 2];
int width = getMaximumLength(message);
// Margins
String topButtomMargin = Strings.repeat(marginChar, width + marginWidth * 2);
String leftRightMargin = Strings.repeat(marginChar, marginWidth);
// Add left and right margin
for (int i = 0; i < message.length; i++) {
output[i + marginHeight] = leftRightMargin + Strings.padEnd(message[i], width, ' ') + leftRightMargin;
}
// Insert top and bottom margin
for (int i = 0; i < marginHeight; i++) {
output[i] = topButtomMargin;
output[output.length - i - 1] = topButtomMargin;
}
return output;
}
/**
* Retrieve the longest line lenght in a list of strings.
* @param lines - the lines.
* @return Longest line lenght.
*/
private static int getMaximumLength(String[] lines) {
int current = 0;
// Find the longest line
for (int i = 0; i < lines.length; i++) {
if (current < lines[i].length())
current = lines[i].length();
}
return current;
}
/**
* Retrieve a constructor for post-1.6.1 chat packets.
* @return A constructor for JSON-based packets.
*/
static Constructor<?> getJsonFormatConstructor() {
Class<?> chatPacket = PacketRegistry.getPacketClassFromType(PacketType.Play.Server.CHAT, true);
List<Constructor<?>> list = FuzzyReflection.fromClass(chatPacket).getConstructorList(
FuzzyMethodContract.newBuilder().
parameterCount(1).
parameterMatches(MinecraftReflection.getMinecraftObjectMatcher()).
build()
);
// First element or NULL
return Iterables.getFirst(list, null);
}
}
@@ -1,149 +0,0 @@
package com.comphenix.protocol.utility;
import java.util.Collections;
import java.util.Map;
/**
* Represents an abstract class loader that can only retrieve classes by their canonical name.
* @author Kristian
*/
public abstract class ClassSource {
/**
* Construct a class source from the default class loader.
* @return A class source.
*/
public static ClassSource fromClassLoader() {
return fromClassLoader(ClassSource.class.getClassLoader());
}
/**
* Construct a class source from the default class loader and package.
* @param packageName - the package that is prepended to every lookup.
* @return A package source.
*/
public static ClassSource fromPackage(String packageName) {
return fromClassLoader().usingPackage(packageName);
}
/**
* Construct a class source from the given class loader.
* @param loader - the class loader.
* @return The corresponding class source.
*/
public static ClassSource fromClassLoader(final ClassLoader loader) {
return new ClassSource() {
@Override
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
return loader.loadClass(canonicalName);
}
};
}
/**
* Construct a class source from a mapping of canonical names and the corresponding classes.
* If the map is null, it will be interpreted as an empty map. If the map does not contain a Class with the specified name, or that string maps to NULL explicitly, a {@link ClassNotFoundException} will be thrown.
* @param map - map of class names and classes.
* @return The class source.
*/
public static ClassSource fromMap(final Map<String, Class<?>> map) {
return new ClassSource() {
@Override
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
Class<?> loaded = map == null ? null : map.get(canonicalName);
if(loaded == null){
// Throw the appropriate exception if we can't load the class
throw new ClassNotFoundException("The specified class could not be found by this ClassLoader.");
}
return loaded;
}
};
}
/**
* @return A ClassLoader which will never successfully load a class.
*/
public static ClassSource empty(){
return fromMap(Collections.<String, Class<?>>emptyMap());
}
/**
* Retrieve a class source that will attempt lookups in each of the given sources in the order they are in the array, and return the first value that is found.
* If the sources array is null or composed of any null elements, an exception will be thrown.
* @param sources - the class sources.
* @return A new class source.
*/
public static ClassSource attemptLoadFrom(final ClassSource... sources) {
if(sources.length == 0){ // Throws NPE if sources is null, which is what we want
return ClassSource.empty();
}
ClassSource source = null;
for(int i = 0; i < sources.length; i++){
if(sources[i] == null){
throw new IllegalArgumentException("Null values are not permitted as ClassSources.");
}
source = source == null ? sources[i] : source.retry(sources[i]);
}
return source;
}
/**
* Retrieve a class source that will retry failed lookups in the given source.
* @param other - the other class source.
* @return A new class source.
*/
public ClassSource retry(final ClassSource other) {
return new ClassSource() {
@Override
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
try {
return ClassSource.this.loadClass(canonicalName);
} catch (ClassNotFoundException e) {
return other.loadClass(canonicalName);
}
}
};
}
/**
* Retrieve a class source that prepends a specific package name to every lookup.
* @param packageName - the package name to prepend.
* @return The class source.
*/
public ClassSource usingPackage(final String packageName) {
return new ClassSource() {
@Override
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
return ClassSource.this.loadClass(append(packageName, canonicalName));
}
};
}
/**
* Append to canonical names together.
* @param a - the name to the left.
* @param b - the name to the right.
* @return The full canonical name, with a dot seperator.
*/
protected static String append(String a, String b) {
boolean left = a.endsWith(".");
boolean right = b.endsWith(".");
// Only add a dot if necessary
if (left && right)
return a.substring(0, a.length() - 1) + b;
else if (left != right)
return a + b;
else
return a + "." + b;
}
/**
* Retrieve a class by name.
* @param canonicalName - the full canonical name of the class.
* @return The corresponding class. If the class is not found, NULL should <b>not</b> be returned, instead a {@code ClassNotFoundException} exception should be thrown.
* @throws ClassNotFoundException If the class could not be found.
*/
public abstract Class<?> loadClass(String canonicalName) throws ClassNotFoundException;
}
@@ -1,27 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
/**
* @author dmulloy2
*/
public final class Constants {
public static final String PACKAGE_VERSION = "v1_9_R2";
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
}
@@ -1,44 +0,0 @@
package com.comphenix.protocol.utility;
import net.sf.cglib.proxy.Enhancer;
/**
* Represents a shared enchancer factory.
* @author Kristian
*/
public class EnhancerFactory {
private static EnhancerFactory INSTANCE = new EnhancerFactory();
// The current class loader
private ClassLoader loader = EnhancerFactory.class.getClassLoader();
public static EnhancerFactory getInstance() {
return INSTANCE;
}
/**
* Create a new CGLib enhancer.
* @return The new enhancer.
*/
public Enhancer createEnhancer() {
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(loader);
return enhancer;
}
/**
* Set the current class loader to use when constructing enhancers.
* @param loader - the class loader
*/
public void setClassLoader(ClassLoader loader) {
this.loader = loader;
}
/**
* Get the current class loader we are using.
* @return The current class loader.
*/
public ClassLoader getClassLoader() {
return loader;
}
}
@@ -1,235 +0,0 @@
package com.comphenix.protocol.utility;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.GenericFutureListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
/**
* Static methods for accessing Minecraft methods.
*
* @author Kristian
*/
public class MinecraftMethods {
// For player connection
private volatile static Method sendPacketMethod;
// For network manager
private volatile static Method networkManagerHandle;
private volatile static Method networkManagerPacketRead;
// For packet
private volatile static Method packetReadByteBuf;
private volatile static Method packetWriteByteBuf;
/**
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
* @return The send packet method.
*/
public static Method getSendPacketMethod() {
if (sendPacketMethod == null) {
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
try {
sendPacketMethod = FuzzyReflection.fromClass(serverHandlerClass).getMethodByName("sendPacket.*");
} catch (IllegalArgumentException e) {
// We can't use the method below on Netty
if (MinecraftReflection.isUsingNetty()) {
sendPacketMethod = FuzzyReflection.fromClass(serverHandlerClass).
getMethodByParameters("sendPacket", MinecraftReflection.getPacketClass());
return sendPacketMethod;
}
Map<String, Method> netServer = getMethodList(
serverHandlerClass, MinecraftReflection.getPacketClass());
Map<String, Method> netHandler = getMethodList(
MinecraftReflection.getNetHandlerClass(), MinecraftReflection.getPacketClass());
// Remove every method in net handler from net server
for (String methodName : netHandler.keySet()) {
netServer.remove(methodName);
}
// The remainder is the send packet method
if (netServer.size() == 1) {
Method[] methods = netServer.values().toArray(new Method[0]);
sendPacketMethod = methods[0];
} else {
throw new IllegalArgumentException("Unable to find the sendPacket method in NetServerHandler/PlayerConnection.");
}
}
}
return sendPacketMethod;
}
/**
* Retrieve the disconnect method for a given player connection.
* @param playerConnection - the player connection.
* @return The
*/
public static Method getDisconnectMethod(Class<? extends Object> playerConnection) {
try {
return FuzzyReflection.fromClass(playerConnection).getMethodByName("disconnect.*");
} catch (IllegalArgumentException e) {
// Just assume it's the first String method
return FuzzyReflection.fromObject(playerConnection).getMethodByParameters("disconnect", String.class);
}
}
/**
* Retrieve the handle(Packet, GenericFutureListener[]) method of network manager.
* <p>
* This only exists in version 1.7.2 and above.
* @return The handle method.
*/
public static Method getNetworkManagerHandleMethod() {
if (networkManagerHandle == null) {
networkManagerHandle = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
getMethodByParameters("handle", MinecraftReflection.getPacketClass(), GenericFutureListener[].class);
networkManagerHandle.setAccessible(true);
}
return networkManagerHandle;
}
/**
* Retrieve the packetRead(ChannelHandlerContext, Packet) method of NetworkMananger.
* <p>
* This only exists in version 1.7.2 and above.
* @return The packetRead method.
*/
public static Method getNetworkManagerReadPacketMethod() {
if (networkManagerPacketRead == null) {
networkManagerPacketRead = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
getMethodByParameters("packetRead", ChannelHandlerContext.class, MinecraftReflection.getPacketClass());
networkManagerPacketRead.setAccessible(true);
}
return networkManagerPacketRead;
}
/**
* Retrieve a method mapped list of every method with the given signature.
* @param source - class source.
* @param params - parameters.
* @return Method mapped list.
*/
private static Map<String, Method> getMethodList(Class<?> source, Class<?>... params) {
FuzzyReflection reflect = FuzzyReflection.fromClass(source, true);
return reflect.getMappedMethods(
reflect.getMethodListByParameters(Void.TYPE, params)
);
}
/**
* Retrieve the Packet.read(PacketDataSerializer) method.
* <p>
* This only exists in version 1.7.2 and above.
* @return The packet read method.
*/
public static Method getPacketReadByteBufMethod() {
initializePacket();
return packetReadByteBuf;
}
/**
* Retrieve the Packet.write(PacketDataSerializer) method.
* <p>
* This only exists in version 1.7.2 and above.
* @return The packet write method.
*/
public static Method getPacketWriteByteBufMethod() {
initializePacket();
return packetWriteByteBuf;
}
/**
* Initialize the two read() and write() methods.
*/
private static void initializePacket() {
// Initialize the methods
if (packetReadByteBuf == null || packetWriteByteBuf == null) {
// This object will allow us to detect which methods were called
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().contains("read"))
throw new ReadMethodException();
if (method.getName().contains("write"))
throw new WriteMethodException();
return proxy.invokeSuper(obj, args);
}
});
// Create our proxy object
Object javaProxy = enhancer.create(
new Class<?>[] { MinecraftReflection.getByteBufClass() },
new Object[] { Unpooled.buffer() }
);
Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle();
List<Method> candidates = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass())
.getMethodListByParameters(Void.TYPE, new Class<?>[] { MinecraftReflection.getPacketDataSerializerClass() });
// Look through all the methods
for (Method method : candidates) {
try {
method.invoke(lookPacket, javaProxy);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof ReadMethodException) {
// Must be the reader
packetReadByteBuf = method;
} else if (e.getCause() instanceof WriteMethodException) {
packetWriteByteBuf = method;
} else {
// throw new RuntimeException("Inner exception.", e);
}
} catch (Exception e) {
throw new RuntimeException("Generic reflection error.", e);
}
}
if (packetReadByteBuf == null)
throw new IllegalStateException("Unable to find Packet.read(PacketDataSerializer)");
if (packetWriteByteBuf == null)
throw new IllegalStateException("Unable to find Packet.write(PacketDataSerializer)");
}
}
/**
* An internal exception used to detect read methods.
* @author Kristian
*/
private static class ReadMethodException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ReadMethodException() {
super("A read method was executed.");
}
}
/**
* An internal exception used to detect write methods.
* @author Kristian
*/
private static class WriteMethodException extends RuntimeException {
private static final long serialVersionUID = 1L;
public WriteMethodException() {
super("A write method was executed.");
}
}
}
@@ -1,65 +0,0 @@
package com.comphenix.protocol.utility;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.Maps;
/**
* A lookup of the associated protocol version of a given Minecraft server.
* @author Kristian
*/
public class MinecraftProtocolVersion {
private static final NavigableMap<MinecraftVersion, Integer> lookup = createLookup();
private static NavigableMap<MinecraftVersion, Integer> createLookup() {
TreeMap<MinecraftVersion, Integer> map = Maps.newTreeMap();
// Source: http://wiki.vg/Protocol_version_numbers
// Doesn't include pre-releases
map.put(new MinecraftVersion(1, 0, 0), 22);
map.put(new MinecraftVersion(1, 1, 0), 23);
map.put(new MinecraftVersion(1, 2, 2), 28);
map.put(new MinecraftVersion(1, 2, 4), 29);
map.put(new MinecraftVersion(1, 3, 1), 39);
map.put(new MinecraftVersion(1, 4, 2), 47);
map.put(new MinecraftVersion(1, 4, 3), 48);
map.put(new MinecraftVersion(1, 4, 4), 49);
map.put(new MinecraftVersion(1, 4, 6), 51);
map.put(new MinecraftVersion(1, 5, 0), 60);
map.put(new MinecraftVersion(1, 5, 2), 61);
map.put(new MinecraftVersion(1, 6, 0), 72);
map.put(new MinecraftVersion(1, 6, 1), 73);
map.put(new MinecraftVersion(1, 6, 2), 74);
map.put(new MinecraftVersion(1, 6, 4), 78);
// After Netty
map.put(new MinecraftVersion(1, 7, 1), 4);
map.put(new MinecraftVersion(1, 7, 6), 5);
map.put(new MinecraftVersion(1, 8, 0), 47);
map.put(new MinecraftVersion(1, 9, 0), 107);
map.put(new MinecraftVersion(1, 9, 2), 109);
map.put(new MinecraftVersion(1, 9, 4), 110);
return map;
}
/**
* Retrieve the version of the Minecraft protocol for the current version of Minecraft.
* @return The version number.
*/
public static int getCurrentVersion() {
return getVersion(ProtocolLibrary.getProtocolManager().getMinecraftVersion());
}
/**
* Retrieve the version of the Minecraft protocol for this version of Minecraft.
* @param version - the version.
* @return The version number.
*/
public static int getVersion(MinecraftVersion version) {
Entry<MinecraftVersion, Integer> result = lookup.floorEntry(version);
return result != null ? result.getValue() : Integer.MIN_VALUE;
}
}
File diff suppressed because it is too large. Load diff
@@ -1,155 +0,0 @@
package com.comphenix.protocol.utility;
// Thanks to Bergerkiller for his excellent hack. :D
// Copyright (C) 2013 bergerkiller
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
class RemappedClassSource extends ClassSource {
private Object classRemapper;
private Method mapType;
private ClassLoader loader;
/**
* Construct a new remapped class source using the default class loader.
*/
public RemappedClassSource() {
this(RemappedClassSource.class.getClassLoader());
}
/**
* Construct a new renampped class source with the provided class loader.
* @param loader - the class loader.
*/
public RemappedClassSource(ClassLoader loader) {
this.loader = loader;
}
/**
* Attempt to load the MCPC remapper.
* @return TRUE if we succeeded, FALSE otherwise.
* @throws RemapperUnavaibleException If the remapper is not present.
*/
public RemappedClassSource initialize() {
try {
Server server = Bukkit.getServer();
if (server == null) {
throw new IllegalStateException("Bukkit not initialized.");
}
String version = server.getVersion();
if (!version.contains("MCPC") && !version.contains("Cauldron")) {
throw new RemapperUnavaibleException(Reason.MCPC_NOT_PRESENT);
}
// Obtain the Class remapper used by MCPC+/Cauldron
this.classRemapper = FieldUtils.readField(getClass().getClassLoader(), "remapper", true);
if (this.classRemapper == null) {
throw new RemapperUnavaibleException(Reason.REMAPPER_DISABLED);
}
// Initialize some fields and methods used by the Jar Remapper
Class<?> renamerClazz = classRemapper.getClass();
this.mapType = MethodUtils.getAccessibleMethod(renamerClazz, "map",
new Class<?>[] { String.class });
return this;
} catch (RemapperUnavaibleException e) {
throw e;
} catch (Exception e) {
// Damn it
throw new RuntimeException("Cannot access MCPC remapper.", e);
}
}
@Override
public Class<?> loadClass(String canonicalName) throws ClassNotFoundException {
final String remapped = getClassName(canonicalName);
try {
return loader.loadClass(remapped);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException("Cannot find " + canonicalName + "(Remapped: " + remapped + ")");
}
}
/**
* Retrieve the obfuscated class name given an unobfuscated canonical class name.
* @param path - the canonical class name.
* @return The obfuscated class name.
*/
public String getClassName(String path) {
try {
String remapped = (String) mapType.invoke(classRemapper, path.replace('.', '/'));
return remapped.replace('/', '.');
} catch (Exception e) {
throw new RuntimeException("Cannot remap class name.", e);
}
}
public static class RemapperUnavaibleException extends RuntimeException {
private static final long serialVersionUID = 1L;
public enum Reason {
MCPC_NOT_PRESENT("The server is not running MCPC+/Cauldron"),
REMAPPER_DISABLED("Running an MCPC+/Cauldron server but the remapper is unavailable. Please turn it on!");
private final String message;
private Reason(String message) {
this.message = message;
}
/**
* Retrieve a human-readable version of this reason.
* @return The human-readable verison.
*/
public String getMessage() {
return message;
}
}
private final Reason reason;
public RemapperUnavaibleException(Reason reason) {
super(reason.getMessage());
this.reason = reason;
}
/**
* Retrieve the reasont he remapper is unavailable.
* @return The reason.
*/
public Reason getReason() {
return reason;
}
}
}
@@ -1,471 +0,0 @@
package com.comphenix.protocol.utility;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import com.comphenix.protocol.injector.netty.NettyByteBufAdapter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtType;
import com.google.common.base.Preconditions;
/**
* Utility methods for reading and writing Minecraft objects to streams.
*
* @author Kristian
*/
public class StreamSerializer {
private static final StreamSerializer DEFAULT = new StreamSerializer();
// Cached methods
private static MethodAccessor READ_ITEM_METHOD;
private static MethodAccessor WRITE_ITEM_METHOD;
private static MethodAccessor READ_NBT_METHOD;
private static MethodAccessor WRITE_NBT_METHOD;
private static MethodAccessor READ_STRING_METHOD;
private static MethodAccessor WRITE_STRING_METHOD;
/**
* Retrieve a default stream serializer.
* @return A serializer.
*/
public static StreamSerializer getDefault() {
return DEFAULT;
}
/**
* Write a variable integer to an output stream.
* @param destination - the destination.
* @param value - the value to write.
* @throws IOException The destination stream threw an exception.
*/
public void serializeVarInt(@Nonnull DataOutputStream destination, int value) throws IOException {
Preconditions.checkNotNull(destination, "source cannot be NULL");
while ((value & 0xFFFFFF80) != 0) {
destination.writeByte(value & 0x7F | 0x80);
value >>>= 7;
}
destination.writeByte(value);
}
/**
* Read a variable integer from an input stream.
* @param source - the source.
* @return The integer.
* @throws IOException The source stream threw an exception.
*/
public int deserializeVarInt(@Nonnull DataInputStream source) throws IOException {
Preconditions.checkNotNull(source, "source cannot be NULL");
int result = 0;
int length = 0;
byte currentByte;
do {
currentByte = source.readByte();
result |= (currentByte & 0x7F) << length++ * 7;
if (length > 5)
throw new RuntimeException("VarInt too big");
} while ((currentByte & 0x80) == 0x80);
return result;
}
/**
* Write or serialize a NBT compound to the given output stream.
* <p>
* Note: An NBT compound can be written to a stream even if it's NULL.
*
* @param output - the target output stream.
* @param compound - the NBT compound to be serialized, or NULL to represent nothing.
* @throws IOException If the operation fails due to reflection problems.
*/
public void serializeCompound(@Nonnull DataOutputStream output, NbtCompound compound) throws IOException {
if (output == null)
throw new IllegalArgumentException("Output stream cannot be NULL.");
// Get the NMS version of the compound
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
if (MinecraftReflection.isUsingNetty()) {
if (WRITE_NBT_METHOD == null) {
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("writeNbtCompound", /* a */
MinecraftReflection.getNBTCompoundClass())
);
}
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
WRITE_NBT_METHOD.invoke(buf, handle);
} else {
if (WRITE_NBT_METHOD == null) {
WRITE_NBT_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(MinecraftReflection.getNBTBaseClass(), 0).
parameterDerivedOf(DataOutput.class, 1).
returnTypeVoid().
build())
);
}
WRITE_NBT_METHOD.invoke(null, handle, output);
}
}
/**
* Read or deserialize an NBT compound from a input stream.
* @param input - the target input stream.
* @return The resulting compound, or NULL.
* @throws IOException If the operation failed due to reflection or corrupt data.
*/
public NbtCompound deserializeCompound(@Nonnull DataInputStream input) throws IOException {
if (input == null)
throw new IllegalArgumentException("Input stream cannot be NULL.");
Object nmsCompound = null;
// Invoke the correct method
if (MinecraftReflection.isUsingNetty()) {
if (READ_NBT_METHOD == null) {
READ_NBT_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("readNbtCompound", /* h */
MinecraftReflection.getNBTCompoundClass(), new Class<?>[0])
);
}
ByteBuf buf = NettyByteBufAdapter.packetReader(input);
nmsCompound = READ_NBT_METHOD.invoke(buf);
} else {
if (READ_NBT_METHOD == null) {
READ_NBT_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(1).
parameterDerivedOf(DataInput.class).
returnDerivedOf(MinecraftReflection.getNBTBaseClass()).
build())
);
}
try {
nmsCompound = READ_NBT_METHOD.invoke(null, input);
} catch (Exception e) {
throw new IOException("Cannot read item stack.", e);
}
}
// Convert back to an NBT Compound
if (nmsCompound != null)
return NbtFactory.fromNMSCompound(nmsCompound);
else
return null;
}
/**
* Serialize a string using the standard Minecraft UTF-16 encoding.
* <p>
* Note that strings cannot exceed 32767 characters, regardless if maximum lenght.
* @param output - the output stream.
* @param text - the string to serialize.
* @throws IOException If the data in the string cannot be written.
*/
public void serializeString(@Nonnull DataOutputStream output, String text) throws IOException {
if (output == null)
throw new IllegalArgumentException("output stream cannot be NULL.");
if (text == null)
throw new IllegalArgumentException("text cannot be NULL.");
if (MinecraftReflection.isUsingNetty()) {
if (WRITE_STRING_METHOD == null) {
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("writeString", /* a */
String.class)
);
}
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
WRITE_STRING_METHOD.invoke(buf, text);
} else {
if (WRITE_STRING_METHOD == null) {
WRITE_STRING_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterExactType(String.class, 0).
parameterDerivedOf(DataOutput.class, 1).
returnTypeVoid().
build())
);
}
WRITE_STRING_METHOD.invoke(null, text, output);
}
}
/**
* Deserialize a string using the standard Minecraft UTF-16 encoding.
* <p>
* Note that strings cannot exceed 32767 characters, regardless if maximum length.
* @param input - the input stream.
* @param maximumLength - the maximum length of the string.
* @return The deserialized string.
* @throws IOException If deserializing fails
*/
public String deserializeString(@Nonnull DataInputStream input, int maximumLength) throws IOException {
if (input == null)
throw new IllegalArgumentException("Input stream cannot be NULL.");
if (maximumLength > 32767)
throw new IllegalArgumentException("Maximum length cannot exceed 32767 characters.");
if (maximumLength < 0)
throw new IllegalArgumentException("Maximum length cannot be negative.");
if (MinecraftReflection.isUsingNetty()) {
if (READ_STRING_METHOD == null) {
READ_STRING_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("readString", /* c */
String.class, new Class<?>[] { int.class })
);
}
ByteBuf buf = NettyByteBufAdapter.packetReader(input);
return (String) READ_STRING_METHOD.invoke(buf, maximumLength);
} else {
if (READ_STRING_METHOD == null) {
READ_STRING_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(DataInput.class, 0).
parameterExactType(int.class, 1).
returnTypeExact(String.class).
build())
);
}
return (String) READ_STRING_METHOD.invoke(null, input, maximumLength);
}
}
/**
* Serialize an item stack as a base-64 encoded string.
* <p>
* Note: An ItemStack can be written to the serialized text even if it's NULL.
*
* @param stack - the item stack to serialize, or NULL to represent air/nothing.
* @return A base-64 representation of the given item stack.
* @throws IOException If the operation fails due to reflection problems.
*/
public String serializeItemStack(ItemStack stack) throws IOException {
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
byte[] bytes = null;
if (MinecraftReflection.isUsingNetty()) {
ByteBuf buf = Unpooled.buffer();
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
if (WRITE_ITEM_METHOD == null) {
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("writeStack", // a()
MinecraftReflection.getItemStackClass()));
}
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
bytes = buf.array();
} else {
if (WRITE_ITEM_METHOD == null) {
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
parameterDerivedOf(DataOutput.class, 1).
build())
);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DataOutputStream dataOutput = new DataOutputStream(outputStream);
WRITE_ITEM_METHOD.invoke(null, nmsItem, dataOutput);
bytes = outputStream.toByteArray();
}
return Base64Coder.encodeLines(bytes);
}
/**
* Deserialize an item stack from a base-64 encoded string.
* @param input - base-64 encoded string.
* @return A deserialized item stack, or NULL if the serialized ItemStack was also NULL.
* @throws IOException If the operation failed due to reflection or corrupt data.
*/
public ItemStack deserializeItemStack(String input) throws IOException {
Validate.notNull(input, "input cannot be null!");
Object nmsItem = null;
byte[] bytes = Base64Coder.decodeLines(input);
if (MinecraftReflection.isUsingNetty()) {
ByteBuf buf = Unpooled.copiedBuffer(bytes);
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("readItemStack", // i(ItemStack)
MinecraftReflection.getItemStackClass(), new Class<?>[0]));
}
nmsItem = READ_ITEM_METHOD.invoke(serializer);
} else {
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(1).
parameterDerivedOf(DataInput.class).
returnDerivedOf(MinecraftReflection.getItemStackClass()).
build())
);
}
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
DataInputStream inputStream = new DataInputStream(byteStream);
nmsItem = READ_ITEM_METHOD.invoke(null, inputStream);
}
return nmsItem != null ? MinecraftReflection.getBukkitItemStack(nmsItem) : null;
}
/**
* Write or serialize an item stack to the given output stream.
* <p>
* To supply a byte array, wrap it in a {@link java.io.ByteArrayOutputStream ByteArrayOutputStream}
* and {@link java.io.DataOutputStream DataOutputStream}.
* <p>
* Note: An ItemStack can be written to a stream even if it's NULL.
*
* @param output - the target output stream.
* @param stack - the item stack that will be written, or NULL to represent air/nothing.
* @throws IOException If the operation fails due to reflection problems.
*/
public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException {
Validate.notNull("output cannot be null!");
// Get the NMS version of the ItemStack
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
if (MinecraftReflection.isUsingNetty()) {
if (WRITE_ITEM_METHOD == null) {
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("writeStack", /* a */
MinecraftReflection.getItemStackClass())
);
}
ByteBuf buf = Unpooled.buffer();
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
output.write(buf.array());
} else {
if (WRITE_ITEM_METHOD == null)
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(2).
parameterDerivedOf(MinecraftReflection.getItemStackClass(), 0).
parameterDerivedOf(DataOutput.class, 1).
build())
);
WRITE_ITEM_METHOD.invoke(null, nmsItem, output);
}
}
/**
* Read or deserialize an item stack from an underlying input stream.
* <p>
* To supply a byte array, wrap it in a {@link java.io.ByteArrayInputStream ByteArrayInputStream}
* and {@link java.io.DataInputStream DataInputStream}.
*
* @param input - the target input stream.
* @return The resulting item stack, or NULL if the serialized item stack was NULL.
* @throws IOException If the operation failed due to reflection or corrupt data.
* @deprecated This is a pretty hacky solution for backwards compatibility. See {@link #deserializeItemStack(DataInputStream)}
*/
@Deprecated
public ItemStack deserializeItemStack(DataInputStream input) throws IOException {
Validate.notNull(input, "input cannot be null!");
Object nmsItem = null;
if (MinecraftReflection.isUsingNetty()) {
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true).
getMethodByParameters("readItemStack", /* i */
MinecraftReflection.getItemStackClass(), new Class<?>[0])
);
}
byte[] bytes = new byte[8192];
input.read(bytes);
ByteBuf buf = Unpooled.copiedBuffer(bytes);
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
nmsItem = READ_ITEM_METHOD.invoke(serializer);
} else {
if (READ_ITEM_METHOD == null) {
READ_ITEM_METHOD = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod(
FuzzyMethodContract.newBuilder().
parameterCount(1).
parameterDerivedOf(DataInput.class).
returnDerivedOf(MinecraftReflection.getItemStackClass()).
build())
);
}
nmsItem = READ_ITEM_METHOD.invoke(null, input);
}
// Convert back to a Bukkit item stack
if (nmsItem != null)
return MinecraftReflection.getBukkitItemStack(nmsItem);
else
return null;
}
}
@@ -1,63 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* General utility class
* @author dmulloy2
*/
public class Util {
/**
* Gets a list of currently online Players.
* @return The list
*/
@SuppressWarnings("unchecked")
public static List<Player> getOnlinePlayers() {
return (List<Player>) Bukkit.getOnlinePlayers();
}
/**
* Converts a variable argument array into a List.
* @param elements Array to convert
* @return The list
*/
@SafeVarargs
public static <E> List<E> asList(E... elements) {
List<E> list = new ArrayList<E>(elements.length);
for (E element : elements) {
list.add(element);
}
return list;
}
/**
* Whether or not this server is running Spigot. This works by checking
* the server version for the String "Spigot"
* @return True if it is, false if not.
*/
public static boolean isUsingSpigot() {
return Bukkit.getServer().getVersion().contains("Spigot");
}
}
@@ -1,92 +0,0 @@
package com.comphenix.protocol.utility;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
/**
* Allows us to stay backwards compatible with older versions of Bukkit.
*
* @author Kristian
*/
public class WrappedScheduler {
/**
* Represents a backwards compatible Bukkit task.
*/
public static interface TaskWrapper {
/**
* Cancel the current task.
*/
public void cancel();
}
/**
* Schedule a given task for a single asynchronous execution.
* @param plugin - the owner plugin.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyOnce(final Plugin plugin, Runnable runnable, long firstDelay) {
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, -1L);
}
/**
* Schedule a given task for multiple asynchronous executions.
* @param plugin - the owner plugin.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, Runnable runnable, long firstDelay, long repeatDelay) {
return runAsynchronouslyRepeat(plugin, plugin.getServer().getScheduler(), runnable, firstDelay, repeatDelay);
}
/**
* Schedule a given task for asynchronous execution.
* @param plugin - the owner plugin.
* @param scheduler - the current Bukkit scheduler.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
public static TaskWrapper runAsynchronouslyRepeat(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
try {
@SuppressWarnings("deprecation")
final int taskID = scheduler.scheduleAsyncRepeatingTask(plugin, runnable, firstDelay, repeatDelay);
// Return the cancellable object
return new TaskWrapper() {
@Override
public void cancel() {
scheduler.cancelTask(taskID);
}
};
} catch (NoSuchMethodError e) {
return tryUpdatedVersion(plugin, scheduler, runnable, firstDelay, repeatDelay);
}
}
/**
* Attempt to do the same with the updated scheduling method.
* @param plugin - the owner plugin.
* @param scheduler - the current Bukkit scheduler.
* @param runnable - the task to run.
* @param firstDelay - the amount of time to wait until executing the task for the first time.
* @param repeatDelay - the amount of time inbetween each execution. If less than zero, the task is only executed once.
* @return A cancel token.
*/
private static TaskWrapper tryUpdatedVersion(final Plugin plugin, final BukkitScheduler scheduler, Runnable runnable, long firstDelay, long repeatDelay) {
final BukkitTask task = scheduler.runTaskTimerAsynchronously(plugin, runnable, firstDelay, repeatDelay);
return new TaskWrapper() {
@Override
public void cancel() {
task.cancel();
}
};
}
}
File diff suppressed because it is too large. Load diff
@@ -1,234 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.util.Vector;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Copies a immutable net.minecraft.server.ChunkPosition, which represents a integer 3D vector.
*
* @author Kristian
*/
public class ChunkPosition {
/**
* Represents the null (0, 0, 0) origin.
*/
public static ChunkPosition ORIGIN = new ChunkPosition(0, 0, 0);
private static Constructor<?> chunkPositionConstructor;
// Use protected members, like Bukkit
protected final int x;
protected final int y;
protected final int z;
// Used to access a ChunkPosition, in case it's names are changed
private static StructureModifier<Integer> intModifier;
/**
* Construct an immutable 3D vector.
* @param x - x coordinate
* @param y - y coordinate
* @param z - z coordinate
*/
public ChunkPosition(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Construct an immutable integer 3D vector from a mutable Bukkit vector.
* @param vector - the mutable real Bukkit vector to copy.
*/
public ChunkPosition(Vector vector) {
if (vector == null)
throw new IllegalArgumentException("Vector cannot be NULL.");
this.x = vector.getBlockX();
this.y = vector.getBlockY();
this.z = vector.getBlockZ();
}
/**
* Convert this instance to an equivalent real 3D vector.
* @return Real 3D vector.
*/
public Vector toVector() {
return new Vector(x, y, z);
}
/**
* Retrieve the x-coordinate.
* @return X coordinate.
*/
public int getX() {
return x;
}
/**
* Retrieve the y-coordinate.
* @return Y coordinate.
*/
public int getY() {
return y;
}
/**
* Retrieve the z-coordinate.
* @return Z coordinate.
*/
public int getZ() {
return z;
}
/**
* Adds the current position and a given position together, producing a result position.
* @param other - the other position.
* @return The new result position.
*/
public ChunkPosition add(ChunkPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new ChunkPosition(x + other.x, y + other.y, z + other.z);
}
/**
* Adds the current position and a given position together, producing a result position.
* @param other - the other position.
* @return The new result position.
*/
public ChunkPosition subtract(ChunkPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new ChunkPosition(x - other.x, y - other.y, z - other.z);
}
/**
* Multiply each dimension in the current position by the given factor.
* @param factor - multiplier.
* @return The new result.
*/
public ChunkPosition multiply(int factor) {
return new ChunkPosition(x * factor, y * factor, z * factor);
}
/**
* Divide each dimension in the current position by the given divisor.
* @param divisor - the divisor.
* @return The new result.
*/
public ChunkPosition divide(int divisor) {
if (divisor == 0)
throw new IllegalArgumentException("Cannot divide by null.");
return new ChunkPosition(x / divisor, y / divisor, z / divisor);
}
/**
* Used to convert between NMS ChunkPosition and the wrapper instance.
* @return A new converter.
*/
public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() {
@Override
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
if (chunkPositionConstructor == null) {
try {
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
getConstructor(int.class, int.class, int.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find chunk position constructor.", e);
}
}
// Construct the underlying ChunkPosition
try {
Object result = chunkPositionConstructor.newInstance(specific.x, specific.y, specific.z);
return result;
} catch (Exception e) {
throw new RuntimeException("Cannot construct ChunkPosition.", e);
}
}
@Override
public ChunkPosition getSpecific(Object generic) {
if (MinecraftReflection.isChunkPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
// Damn it all
if (intModifier.size() < 3) {
throw new IllegalStateException("Cannot read class " + generic.getClass() + " for its integer fields.");
}
if (intModifier.size() >= 3) {
try {
StructureModifier<Integer> instance = intModifier.withTarget(generic);
ChunkPosition result = new ChunkPosition(instance.read(0), instance.read(1), instance.read(2));
return result;
} catch (FieldAccessException e) {
// This is an exeptional work-around, so we don't want to burden the caller with the messy details
throw new RuntimeException("Field access error.", e);
}
}
}
// Otherwise, return NULL
return null;
}
// Thanks Java Generics!
@Override
public Class<ChunkPosition> getSpecificType() {
return ChunkPosition.class;
}
};
}
@Override
public boolean equals(Object obj) {
// Fast checks
if (this == obj) return true;
if (obj == null) return false;
// Only compare objects of similar type
if (obj instanceof ChunkPosition) {
ChunkPosition other = (ChunkPosition) obj;
return x == other.x && y == other.y && z == other.z;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(x, y, z);
}
@Override
public String toString() {
return "WrappedChunkPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
}
}
@@ -1,189 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.google.common.base.Objects;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
* @author dmulloy2
*/
public class PlayerInfoData {
private static Constructor<?> constructor;
private final int latency;
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
// This is the same order as the NMS class, minus the packet (which isn't a field)
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName) {
this.profile = profile;
this.latency = latency;
this.gameMode = gameMode;
this.displayName = displayName;
}
/**
* Gets the GameProfile of the player represented by this data.
* @return The GameProfile
*/
public WrappedGameProfile getProfile() {
return profile;
}
/**
* @deprecated Replaced by {@link #getLatency()}
*/
@Deprecated
public int getPing() {
return latency;
}
/**
* Gets the latency between the client and the server.
* @return The latency
*/
public int getLatency() {
return latency;
}
/**
* Gets the GameMode of the player represented by this data.
* @return The GameMode
*/
public NativeGameMode getGameMode() {
return gameMode;
}
/**
* Gets the display name of the player represented by this data.
* @return The display name
*/
public WrappedChatComponent getDisplayName() {
return displayName;
}
/**
* Used to convert between NMS PlayerInfoData and the wrapper instance.
* @return A new converter.
*/
public static EquivalentConverter<PlayerInfoData> getConverter() {
return new EquivalentConverter<PlayerInfoData>() {
@Override
public Object getGeneric(Class<?> genericType, PlayerInfoData specific) {
if (constructor == null) {
try {
// public PlayerInfoData(Packet, GameProfile, int, GameMode, ChatComponent)
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(
MinecraftReflection.getMinecraftClass("PacketPlayOutPlayerInfo"),
MinecraftReflection.getGameProfileClass(),
int.class,
EnumWrappers.getGameModeClass(),
MinecraftReflection.getIChatBaseComponentClass()
);
} catch (Exception e) {
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
}
}
// Attempt to construct the underlying PlayerInfoData
try {
// public PlayerInfoData(null, GameProfile, ping, GameMode, ChatComponent)
// The packet isn't a field, so it can be null
Object result = constructor.newInstance(
null,
specific.profile.handle,
specific.latency,
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode),
specific.displayName != null ? specific.displayName.handle : null
);
return result;
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
}
}
@Override
public PlayerInfoData getSpecific(Object generic) {
if (MinecraftReflection.isPlayerInfoData(generic)) {
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false)
.withTarget(generic);
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
WrappedGameProfile gameProfile = gameProfiles.read(0);
StructureModifier<Integer> ints = modifier.withType(int.class);
int latency = ints.read(0);
StructureModifier<NativeGameMode> gameModes = modifier.withType(
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
NativeGameMode gameMode = gameModes.read(0);
StructureModifier<WrappedChatComponent> displayNames = modifier.withType(
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
WrappedChatComponent displayName = displayNames.read(0);
return new PlayerInfoData(gameProfile, latency, gameMode, displayName);
}
// Otherwise, return null
return null;
}
// Thanks Java Generics!
@Override
public Class<PlayerInfoData> getSpecificType() {
return PlayerInfoData.class;
}
};
}
@Override
public boolean equals(Object obj) {
// Fast checks
if (this == obj) return true;
if (obj == null) return false;
// Only compare objects of similar type
if (obj instanceof PlayerInfoData) {
PlayerInfoData other = (PlayerInfoData) obj;
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
&& displayName.equals(other.displayName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(latency, gameMode, profile, displayName);
}
@Override
public String toString() {
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s",
latency, gameMode, profile, displayName);
}
}
@@ -1,166 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import org.bukkit.Material;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a wrapper around IBlockData.
*
* @author dmulloy2
*/
public class WrappedBlockData extends AbstractWrapper {
private static final Class<?> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
private static final Class<?> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
private static final Class<?> BLOCK = MinecraftReflection.getBlockClass();
private static MethodAccessor FROM_LEGACY_DATA = null;
private static MethodAccessor TO_LEGACY_DATA = null;
private static MethodAccessor GET_NMS_BLOCK = null;
private static MethodAccessor GET_BLOCK = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(int.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(int.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[] { Material.class }));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[0]));
}
public WrappedBlockData(Object handle) {
super(IBLOCK_DATA);
setHandle(handle);
}
/**
* Retrieves the type of this BlockData.
* @return The type of this BlockData.
*/
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockConverter().getSpecific(block);
}
/**
* Retrieves the type id of this BlockData.
* @return The type id of this BlockData.
* @deprecated ID's are deprecated
*/
@Deprecated
public int getTypeId() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockIDConverter().getSpecific(block);
}
/**
* Retrieves the data of this BlockData.
* @return The data of this BlockData.
*/
public int getData() {
Object block = GET_BLOCK.invoke(handle);
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
}
/**
* Sets the type of this BlockData.
* @param type New type
*/
public void setType(Material type) {
setTypeAndData(type, 0);
}
/**
* Sets the data of this BlockData.
* @param data New data
*/
public void setData(int data) {
setTypeAndData(getType(), data);
}
/**
* Sets the type and data of this BlockData.
* @param type New type
* @param data New data
*/
public void setTypeAndData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
setHandle(blockData);
}
/**
* Creates a new BlockData instance with the given type and no data.
* @param type Block type
* @return New BlockData
*/
public static WrappedBlockData createData(Material type) {
return createData(type, 0);
}
/**
* Creates a new BlockData instance with the given type and data.
* @param type Block type
* @param data Block data
* @return New BlockData
*/
public static WrappedBlockData createData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
return new WrappedBlockData(blockData);
}
@Override
public String toString() {
return "WrappedBlockData[handle=" + handle + "]";
}
@Override
public boolean equals(Object o) {
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.getType() == that.getType();
}
return false;
}
}
@@ -1,186 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Allows access to a chunk coordinate.
*
* @author Kristian
*/
public class WrappedChunkCoordinate extends AbstractWrapper implements Comparable<WrappedChunkCoordinate> {
/**
* If TRUE, NULLs should be put before non-null instances of this class.
*/
private static final boolean LARGER_THAN_NULL = true;
// Used to access a ChunkCoordinate
private static StructureModifier<Integer> SHARED_MODIFIER;
// The current modifier
private StructureModifier<Integer> handleModifier;
/**
* Create a new empty wrapper.
*/
public WrappedChunkCoordinate() {
super(MinecraftReflection.getChunkCoordinatesClass());
try {
setHandle(getHandleType().newInstance());
} catch (Exception e) {
throw new RuntimeException("Cannot construct chunk coordinate.");
}
}
/**
* Create a wrapper for a specific chunk coordinates.
* @param handle - the NMS chunk coordinates.
*/
@SuppressWarnings("rawtypes")
public WrappedChunkCoordinate(Comparable handle) {
super(MinecraftReflection.getChunkCoordinatesClass());
setHandle(handle);
}
// Ensure that the structure modifier is initialized
private StructureModifier<Integer> getModifier() {
if (SHARED_MODIFIER == null)
SHARED_MODIFIER = new StructureModifier<Object>(handle.getClass(), null, false).withType(int.class);
if (handleModifier == null)
handleModifier = SHARED_MODIFIER.withTarget(handle);
return handleModifier;
}
/**
* Create a wrapper with specific values.
* @param x - the x coordinate.
* @param y - the y coordinate.
* @param z - the z coordinate.
*/
public WrappedChunkCoordinate(int x, int y, int z) {
this();
setX(x);
setY(y);
setZ(z);
}
/**
* Create a chunk coordinate wrapper from a given position.
* @param position - the given position.
*/
public WrappedChunkCoordinate(ChunkPosition position) {
this(position.getX(), position.getY(), position.getZ());
}
public Object getHandle() {
return handle;
}
/**
* Retrieve the x coordinate of the underlying coordinate.
* @return The x coordinate.
*/
public int getX() {
return getModifier().read(0);
}
/**
* Set the x coordinate of the underlying coordinate.
* @param newX - the new x coordinate.
*/
public void setX(int newX) {
getModifier().write(0, newX);
}
/**
* Retrieve the y coordinate of the underlying coordinate.
* @return The y coordinate.
*/
public int getY() {
return getModifier().read(1);
}
/**
* Set the y coordinate of the underlying coordinate.
* @param newY - the new y coordinate.
*/
public void setY(int newY) {
getModifier().write(1, newY);
}
/**
* Retrieve the z coordinate of the underlying coordinate.
* @return The z coordinate.
*/
public int getZ() {
return getModifier().read(2);
}
/**
* Set the z coordinate of the underlying coordiate.
* @param newZ - the new z coordinate.
*/
public void setZ(int newZ) {
getModifier().write(2, newZ);
}
/**
* Create an immutable chunk position from this coordinate.
* @return The new immutable chunk position.
*/
public ChunkPosition toPosition() {
return new ChunkPosition(getX(), getY(), getZ());
}
@SuppressWarnings("unchecked")
@Override
public int compareTo(WrappedChunkCoordinate other) {
// We'll handle NULL objects too, unlike ChunkCoordinates
if (other.handle == null)
return LARGER_THAN_NULL ? -1 : 1;
else
return ((Comparable<Object>) handle).compareTo(other.handle);
}
@Override
public boolean equals(Object other) {
if (other instanceof WrappedChunkCoordinate) {
WrappedChunkCoordinate wrapper = (WrappedChunkCoordinate) other;
return Objects.equal(handle, wrapper.handle);
}
// It's tempting to handle the ChunkCoordinate case too, but then
// the equals() method won't be commutative, causing a.equals(b) to
// be different to b.equals(a).
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return String.format("ChunkCoordinate [x: %s, y: %s, z: %s]", getX(), getY(), getZ());
}
}
@@ -1,232 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
/**
* Represents a DataWatcher Item in 1.9.
* @author dmulloy2
*/
public class WrappedWatchableObject extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass();
private static ConstructorAccessor constructor;
private final StructureModifier<Object> modifier;
/**
* Constructs a wrapped watchable object around an existing NMS data watcher item.
* @param handle Data watcher item
*/
public WrappedWatchableObject(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
this.modifier = new StructureModifier<Object>(handleType).withTarget(handle);
}
/**
* Constructs a wrapped watchable object with a given watcher object and initial value.
* @param watcherObject Watcher object
* @param value Initial value
*/
public WrappedWatchableObject(WrappedDataWatcherObject watcherObject, Object value) {
this(newHandle(watcherObject, value));
}
private static Object newHandle(WrappedDataWatcherObject watcherObject, Object value) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
return constructor.invoke(watcherObject.getHandle(), value);
}
// ---- Getter methods
/**
* Gets this Item's watcher object, which contains the index and serializer.
* @return The watcher object
*/
public WrappedDataWatcherObject getWatcherObject() {
return new WrappedDataWatcherObject(modifier.read(0));
}
/**
* Gets this Item's index from the watcher object
* @return The index
*/
public int getIndex() {
return getWatcherObject().getIndex();
}
/**
* Gets the wrapped value of this data watcher item.
* @return The wrapped value
*/
public Object getValue() {
return getWrapped(getRawValue());
}
/**
* Gets the raw value of this data watcher item.
* @return Raw value
*/
public Object getRawValue() {
return modifier.readSafely(1);
}
/**
* Retrieve the wrapped object value, if needed.
*
* @param value - the raw NMS object to wrap.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Handle the special cases
if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else {
return value;
}
}
/**
* Retrieve the wrapped type, if needed.
*
* @param wrapped - the wrapped class type.
* @return The wrapped class type.
*/
static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class;
else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass()))
return BlockPosition.class;
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class;
else if (unwrapped.equals(MinecraftReflection.getItemStackClass()))
return ItemStack.class;
else
return unwrapped;
}
/**
* Sets the value of this item.
* @param value New value
* @param updateClient Whether or not to update the client
*/
public void setValue(Object value, boolean updateClient) {
modifier.write(1, getUnwrapped(value));
if (updateClient) {
setDirtyState(true);
}
}
/**
* Sets the value of this item.
* @param value New value
*/
public void setValue(Object value) {
setValue(value, false);
}
/**
* Retrieve the raw NMS value.
*
* @param wrapped - the wrapped position.
* @return The raw NMS object.
*/
static Object getUnwrapped(Object wrapped) {
// Convert special cases
if (wrapped instanceof ChunkPosition)
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
else if (wrapped instanceof BlockPosition)
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
else if (wrapped instanceof WrappedChunkCoordinate)
return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else
return wrapped;
}
/**
* Retrieve the unwrapped type, if needed.
*
* @param wrapped - the unwrapped class type.
* @return The unwrapped class type.
*/
static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class))
return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(BlockPosition.class))
return MinecraftReflection.getBlockPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class))
return MinecraftReflection.getChunkCoordinatesClass();
else if (ItemStack.class.isAssignableFrom(wrapped))
return MinecraftReflection.getItemStackClass();
else
return wrapped;
}
/**
* Whether or not the value must be synchronized with the client.
* @return True if it must, false if not
*/
public boolean getDirtyState() {
return (boolean) modifier.read(2);
}
/**
* Sets this item's dirty state
* @param dirty New state
*/
public void setDirtyState(boolean dirty) {
modifier.write(2, dirty);
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedWatchableObject) {
// watcher object, value, dirty state
WrappedWatchableObject other = (WrappedWatchableObject) obj;
return getWatcherObject().equals(other.getWatcherObject())
&& getRawValue().equals(other.getRawValue())
&& getDirtyState() == other.getDirtyState();
}
return false;
}
@Override
public String toString() {
return "DataWatcherItem[object=" + getWatcherObject() + ", value=" + getValue() + ", dirty=" + getDirtyState() + "]";
}
}
@@ -1,250 +0,0 @@
package com.comphenix.protocol.wrappers.nbt;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.block.BlockState;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
/**
* Manipulate tile entities.
* @author Kristian
*/
class TileEntityAccessor<T extends BlockState> {
/**
* Token indicating that the given block state doesn't contany any tile entities.
*/
private static final TileEntityAccessor<BlockState> EMPTY_ACCESSOR = new TileEntityAccessor<BlockState>();
/**
* Cached field accessors - {@link #EMPTY_ACCESSOR} represents no valid tile entity.
*/
private static final ConcurrentMap<Class<?>, TileEntityAccessor<?>> cachedAccessors = Maps.newConcurrentMap();
private FieldAccessor tileEntityField;
private MethodAccessor readCompound;
private MethodAccessor writeCompound;
// For CGLib detection
private boolean writeDetected;
private boolean readDetected;
private TileEntityAccessor() {
// Do nothing
}
/**
* Construct a new tile entity accessor.
* @param tileEntityField - the tile entity field.
* @param state - the block state.
*/
private TileEntityAccessor(FieldAccessor tileEntityField, T state) {
if (tileEntityField != null) {
this.tileEntityField = tileEntityField;
Class<?> type = tileEntityField.getField().getType();
// Possible read/write methods
try {
findMethodsUsingASM();
} catch (IOException ex1) {
try {
// Much slower though
findMethodUsingCGLib(state);
} catch (Exception ex2) {
throw new RuntimeException("Cannot find read/write methods in " + type, ex2);
}
}
// Ensure we found them
if (readCompound == null)
throw new RuntimeException("Unable to find read method in " + type);
if (writeCompound == null)
throw new RuntimeException("Unable to find write method in " + type);
}
}
/**
* Find the read/write methods in TileEntity.
* @throws IOException If we cannot find these methods.
*/
private void findMethodsUsingASM() throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)V";
reader.accept(new EmptyClassVisitor() {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
final String methodName = name;
// Detect read/write calls to NBTTagCompound
if (expectedDesc.equals(desc)) {
return new EmptyMethodVisitor() {
private int readMethods;
private int writeMethods;
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// This must be a virtual call on NBTTagCompound that accepts a String
if (opcode == Opcodes.INVOKEVIRTUAL &&
tagCompoundName.equals(owner) &&
desc.startsWith("(Ljava/lang/String")) {
// Is this a write call?
if (desc.endsWith(")V")) {
writeMethods++;
} else {
readMethods++;
}
}
}
@Override
public void visitEnd() {
if (readMethods > writeMethods) {
readCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
} else if (writeMethods > readMethods) {
writeCompound = Accessors.getMethodAccessor(tileEntityClass, methodName, nbtCompoundClass);
}
super.visitEnd();
}
};
}
return null;
}
}, 0);
}
/**
* Find the read/write methods in TileEntity.
* @param blockState - the block state.
* @throws IOException If we cannot find these methods.
*/
private void findMethodUsingCGLib(T blockState) throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
// This is a much slower method, but it is necessary in MCPC
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
enhancer.setSuperclass(nbtCompoundClass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getReturnType().equals(Void.TYPE)) {
// Write method
writeDetected = true;
} else {
// Read method
readDetected = true;
}
throw new RuntimeException("Stop execution.");
}
});
Object compound = enhancer.create();
Object tileEntity = tileEntityField.get(blockState);
// Look in every read/write like method
for (Method method : FuzzyReflection.fromObject(tileEntity, true).
getMethodListByParameters(Void.TYPE, new Class<?>[] { nbtCompoundClass })) {
try {
readDetected = false;
writeDetected = false;
method.invoke(tileEntity, compound);
} catch (Exception e) {
// Okay - see if we detected a write or read
if (readDetected)
readCompound = Accessors.getMethodAccessor(method, true);
if (writeDetected)
writeCompound = Accessors.getMethodAccessor(method, true);
}
}
}
/**
* Retrieve the JAR name (slash instead of dots) of the given clas.
* @param clazz - the class.
* @return The JAR name.
*/
private static String getJarName(Class<?> clazz) {
return clazz.getCanonicalName().replace('.', '/');
}
/**
* Read the NBT compound that represents a given tile entity.
* @param state - tile entity represented by a block state.
* @return The compound.
*/
public NbtCompound readBlockState(T state) {
NbtCompound output = NbtFactory.ofCompound("");
Object tileEntity = tileEntityField.get(state);
// Write the block state to the output compound
writeCompound.invoke(tileEntity, NbtFactory.fromBase(output).getHandle());
return output;
}
/**
* Write the NBT compound as a tile entity.
* @param state - target block state.
* @param compound - the compound.
*/
public void writeBlockState(T state, NbtCompound compound) {
Object tileEntity = tileEntityField.get(state);
// Ensure the block state is set to the compound
readCompound.invoke(tileEntity, NbtFactory.fromBase(compound).getHandle());
}
/**
* Retrieve an accessor for the tile entity at a specific location.
* @param state - the block state.
* @return The accessor, or NULL if this block state doesn't contain any tile entities.
*/
@SuppressWarnings("unchecked")
public static <T extends BlockState> TileEntityAccessor<T> getAccessor(T state) {
Class<?> craftBlockState = state.getClass();
TileEntityAccessor<?> accessor = cachedAccessors.get(craftBlockState);
// Attempt to construct the accessor
if (accessor == null ) {
TileEntityAccessor<?> created = null;
FieldAccessor field = null;
try {
field = Accessors.getFieldAccessor(craftBlockState, MinecraftReflection.getTileEntityClass(), true);
} catch (Exception e) {
created = EMPTY_ACCESSOR;
}
if (field != null) {
created = new TileEntityAccessor<T>(field, state);
}
accessor = cachedAccessors.putIfAbsent(craftBlockState, created);
// We won the race
if (accessor == null) {
accessor = created;
}
}
return (TileEntityAccessor<T>) (accessor != EMPTY_ACCESSOR ? accessor : null);
}
}
@@ -1,169 +0,0 @@
package com.comphenix.protocol.wrappers.nbt.io;
import java.io.DataInput;
import java.io.DataOutput;
import java.lang.reflect.Method;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtList;
import com.comphenix.protocol.wrappers.nbt.NbtWrapper;
public class NbtBinarySerializer {
private static final Class<?> NBT_BASE_CLASS = MinecraftReflection.getNBTBaseClass();
private interface LoadMethod {
/**
* Load an NBT compound from a given stream.
* @param input - the input stream.
* @return The loaded NBT compound.
*/
public abstract Object loadNbt(DataInput input);
}
/**
* Load an NBT compound from the NBTBase static method pre-1.7.2.
*/
private static class LoadMethodNbtClass implements LoadMethod {
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input);
}
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.2 - 1.7.5
*/
private static class LoadMethodWorldUpdate implements LoadMethod {
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input, 0);
}
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method in 1.7.8
*/
private static class LoadMethodSkinUpdate implements LoadMethod {
private Class<?> readLimitClass = MinecraftReflection.getNBTReadLimiterClass();
private Object readLimiter = FuzzyReflection.fromClass(readLimitClass).getSingleton();
private MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class, readLimitClass);
@Override
public Object loadNbt(DataInput input) {
return accessor.invoke(null, input, 0, readLimiter);
}
}
// Used to read and write NBT
private static Method methodWrite;
/**
* Method selected for loading NBT compounds.
*/
private static LoadMethod loadMethod;
/**
* Retrieve a default instance of the NBT binary serializer.
*/
public static final NbtBinarySerializer DEFAULT = new NbtBinarySerializer();
/**
* Write the content of a wrapped NBT tag to a stream.
* @param <TType> Type
* @param value - the NBT tag to write.
* @param destination - the destination stream.
*/
public <TType> void serialize(NbtBase<TType> value, DataOutput destination) {
if (methodWrite == null) {
Class<?> base = MinecraftReflection.getNBTBaseClass();
// Use the base class
methodWrite = getUtilityClass().
getMethodByParameters("writeNBT", base, DataOutput.class);
methodWrite.setAccessible(true);
}
try {
methodWrite.invoke(null, NbtFactory.fromBase(value).getHandle(), destination);
} catch (Exception e) {
throw new FieldAccessException("Unable to write NBT " + value, e);
}
}
/**
* Load an NBT tag from a stream.
* @param <TType> Type
* @param source - the input stream.
* @return An NBT tag.
*/
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
LoadMethod method = loadMethod;
if (loadMethod == null) {
if (MinecraftReflection.isUsingNetty()) {
try {
method = new LoadMethodWorldUpdate();
} catch (IllegalArgumentException e) {
// Cannot find that method - must be in 1.7.8
method = new LoadMethodSkinUpdate();
}
} else {
method = new LoadMethodNbtClass();
}
// Save the selected method
loadMethod = method;
}
try {
return NbtFactory.fromNMS(method.loadNbt(source), null);
} catch (Exception e) {
throw new FieldAccessException("Unable to read NBT from " + source, e);
}
}
private static MethodAccessor getNbtLoadMethod(Class<?>... parameters) {
return Accessors.getMethodAccessor(getUtilityClass().getMethodByParameters("load", NBT_BASE_CLASS, parameters), true);
}
private static FuzzyReflection getUtilityClass() {
if (MinecraftReflection.isUsingNetty()) {
return FuzzyReflection.fromClass(MinecraftReflection.getNbtCompressedStreamToolsClass(), true);
} else {
return FuzzyReflection.fromClass(MinecraftReflection.getNBTBaseClass(), true);
}
}
/**
* Load an NBT compound from a stream.
* @param source - the input stream.
* @return An NBT compound.
*/
@SuppressWarnings("rawtypes")
public NbtCompound deserializeCompound(DataInput source) {
// I always seem to override generics ...
return (NbtCompound) (NbtBase) deserialize(source);
}
/**
* Load an NBT list from a stream.
* @param <T> Type
* @param source - the input stream.
* @return An NBT list.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> NbtList<T> deserializeList(DataInput source) {
return (NbtList<T>) (NbtBase) deserialize(source);
}
}
-283
View File
@@ -1,283 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>4.0.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
<packaging>jar</packaging>
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.sf</pattern>
<shadedPattern>com.comphenix.net.sf</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.comphenix.protocol.Application</Main-Class>
<Implementation-Title>ProtocolLib</Implementation-Title>
<Implementation-Version>${project.version}${project.build.number}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
<finalName>ProtocolLib</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jenkins</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
</properties>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<scm>
<connection>scm:git:git://github.com/dmulloy2/ProtocolLib.git</connection>
<developerConnection>scm:git:git@github.com:dmulloy2/ProtocolLib.git</developerConnection>
<url>https://github.com/dmulloy2/ProtocolLib</url>
</scm>
<licenses>
<license>
<name>GNU GENERAL PUBLIC LICENSE - Version 2, June 1991</name>
<url>http://www.gnu.org/licenses/gpl-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
</roles>
</developer>
<developer>
<id>aadnk</id>
<name>Kristian S. Stangeland</name>
<email>kr_stang@hotmail.com</email>
<url>http://comphenix.net/</url>
<roles>
<role>former author</role>
</roles>
<timezone>1</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId>
<version>4.0</version>
</dependency>
<!-- Included with Minecraft
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
</dependency>
-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.9.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.comphenix.executors</groupId>
<artifactId>BukkitExecutors</artifactId>
<version>1.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -1,58 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import org.bukkit.Bukkit;
/**
* Ignore this class.
*
* @author Kristian
*/
public class Application {
private static Thread mainThread;
private static boolean primaryMethod = true;
public static void main(String[] args) {
// For now, though we might consider making a proper application
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
}
/**
* Determine if we are running on the main thread.
* @return TRUE if we are, FALSE otherwise.
*/
public static boolean isPrimaryThread() {
if (primaryMethod) {
try {
return Bukkit.isPrimaryThread();
} catch (LinkageError e) {
primaryMethod = false;
}
}
// Fallback method
return Thread.currentThread().equals(mainThread);
}
/**
* Register the calling thread as the primary thread.
*/
static void registerPrimaryThread() {
mainThread = Thread.currentThread();
}
}
@@ -1,170 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import com.comphenix.protocol.async.AsyncListenerHandler;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.injector.server.AbstractInputStreamLookup;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.StructureCompiler;
import com.comphenix.protocol.reflect.instances.CollectionGenerator;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.reflect.instances.PrimitiveGenerator;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
/**
* Used to fix ClassLoader leaks that may lead to filling up the permanent generation.
*
* @author Kristian
*/
class CleanupStaticMembers {
// Reports
public final static ReportType REPORT_CANNOT_RESET_FIELD = new ReportType("Unable to reset field %s: %s");
public final static ReportType REPORT_CANNOT_UNLOAD_CLASS = new ReportType("Unable to unload class %s.");
private ClassLoader loader;
private ErrorReporter reporter;
public CleanupStaticMembers(ClassLoader loader, ErrorReporter reporter) {
this.loader = loader;
this.reporter = reporter;
}
/**
* Ensure that the previous ClassLoader is not leaking.
*/
public void resetAll() {
// This list must always be updated
@SuppressWarnings("deprecation")
Class<?>[] publicClasses = {
AsyncListenerHandler.class, ListeningWhitelist.class, PacketContainer.class,
BukkitUnwrapper.class, DefaultInstances.class, CollectionGenerator.class,
PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class,
BackgroundCompiler.class, StructureCompiler.class,
ObjectWriter.class, Packets.Server.class, Packets.Client.class,
ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class,
AbstractInputStreamLookup.class, TemporaryPlayerFactory.class, SpigotPacketInjector.class,
MinecraftReflection.class, NbtBinarySerializer.class
};
String[] internalClasses = {
"com.comphenix.protocol.events.SerializedOfflinePlayer",
"com.comphenix.protocol.injector.player.InjectedServerConnection",
"com.comphenix.protocol.injector.player.NetworkFieldInjector",
"com.comphenix.protocol.injector.player.NetworkObjectInjector",
"com.comphenix.protocol.injector.player.NetworkServerInjector",
"com.comphenix.protocol.injector.player.PlayerInjector",
"com.comphenix.protocol.injector.EntityUtilities",
"com.comphenix.protocol.injector.packet.PacketRegistry",
"com.comphenix.protocol.injector.packet.PacketInjector",
"com.comphenix.protocol.injector.packet.ReadPacketModifier",
"com.comphenix.protocol.injector.StructureCache",
"com.comphenix.protocol.reflect.compiler.BoxingHelper",
"com.comphenix.protocol.reflect.compiler.MethodDescriptor",
"com.comphenix.protocol.wrappers.nbt.WrappedElement",
};
resetClasses(publicClasses);
resetClasses(getClasses(loader, internalClasses));
}
private void resetClasses(Class<?>[] classes) {
// Reset each class one by one
for (Class<?> clazz : classes) {
resetClass(clazz);
}
}
private void resetClass(Class<?> clazz) {
for (Field field : clazz.getFields()) {
Class<?> type = field.getType();
// Only check static non-primitive fields. We also skip strings.
if (Modifier.isStatic(field.getModifiers()) &&
!type.isPrimitive() && !type.equals(String.class) &&
!type.equals(ReportType.class)) {
try {
setFinalStatic(field, null);
} catch (IllegalAccessException e) {
// Just inform the player
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_RESET_FIELD).error(e).messageParam(field.getName(), e.getMessage())
);
e.printStackTrace();
}
}
}
}
// HACK! HAACK!
private static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException {
int modifier = field.getModifiers();
boolean isFinal = Modifier.isFinal(modifier);
Field modifiersField = isFinal ? FieldUtils.getField(Field.class, "modifiers", true) : null;
// We have to remove the final field first
if (isFinal) {
FieldUtils.writeField(modifiersField, field, modifier & ~Modifier.FINAL, true);
}
// Now we can safely modify the field
FieldUtils.writeStaticField(field, newValue, true);
// Revert modifier
if (isFinal) {
FieldUtils.writeField(modifiersField, field, modifier, true);
}
}
private Class<?>[] getClasses(ClassLoader loader, String[] names) {
List<Class<?>> output = new ArrayList<Class<?>>();
for (String name : names) {
try {
output.add(loader.loadClass(name));
} catch (ClassNotFoundException e) {
// Warn the user
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_UNLOAD_CLASS).error(e).messageParam(name));
}
}
return output.toArray(new Class<?>[0]);
}
}
@@ -1,524 +0,0 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* A protocol manager that delays all packet listener registrations and unregistrations until
* an underlying protocol manager can be constructed.
*
* @author Kristian
*/
public class DelayedPacketManager implements InternalManager {
// Registering packet IDs that are not supported
public static final ReportType REPORT_CANNOT_SEND_QUEUED_PACKET = new ReportType("Cannot send queued packet %s.");
public static final ReportType REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET = new ReportType("Cannot send queued wire packet %s.");
public static final ReportType REPORT_CANNOT_REGISTER_QUEUED_LISTENER = new ReportType("Cannot register queued listener %s.");
private volatile InternalManager delegate;
// Queued actions
private final List<Runnable> queuedActions = Collections.synchronizedList(Lists.<Runnable>newArrayList());
private final List<PacketListener> queuedListeners = Collections.synchronizedList(Lists.<PacketListener>newArrayList());
private AsynchronousManager asyncManager;
private ErrorReporter reporter;
// The current hook
private PlayerInjectHooks hook = PlayerInjectHooks.NETWORK_SERVER_OBJECT;
// If we have been closed
private boolean closed;
private boolean debug;
// Queued registration
private PluginManager queuedManager;
private Plugin queuedPlugin;
private MinecraftVersion version;
public DelayedPacketManager(@Nonnull ErrorReporter reporter, @Nonnull MinecraftVersion version) {
Preconditions.checkNotNull(reporter, "reporter cannot be NULL.");
Preconditions.checkNotNull(version, "version cannot be NULL.");
this.reporter = reporter;
this.version = version;
}
/**
* Retrieve the underlying protocol manager.
* @return The underlying manager.
*/
public InternalManager getDelegate() {
return delegate;
}
@Override
public int getProtocolVersion(Player player) {
if (delegate != null)
return delegate.getProtocolVersion(player);
else
return Integer.MIN_VALUE;
}
@Override
public MinecraftVersion getMinecraftVersion() {
if (delegate != null)
return delegate.getMinecraftVersion();
else
return version;
}
/**
* Update the delegate to the underlying manager.
* <p>
* This will prompt this packet manager to immediately transmit and
* register all queued packets an listeners.
* @param delegate - delegate to the new manager.
*/
protected void setDelegate(InternalManager delegate) {
this.delegate = delegate;
if (delegate != null) {
// Update the hook if needed
if (!Objects.equal(delegate.getPlayerHook(), hook)) {
delegate.setPlayerHook(hook);
}
// Register events as well
if (queuedManager != null && queuedPlugin != null) {
delegate.registerEvents(queuedManager, queuedPlugin);
}
// And update the debug mode
delegate.setDebug(debug);
// Add any pending listeners
synchronized (queuedListeners) {
for (PacketListener listener : queuedListeners) {
try {
delegate.addPacketListener(listener);
} catch (IllegalArgumentException e) {
// Inform about this plugin error
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_QUEUED_LISTENER).
callerParam(delegate).messageParam(listener).error(e));
}
}
}
// Execute any delayed actions
synchronized (queuedActions) {
for (Runnable action : queuedActions) {
action.run();
}
}
// Don't keep this around anymore
queuedListeners.clear();
queuedActions.clear();
}
}
private Runnable queuedAddPacket(final ConnectionSide side, final Player player, final PacketContainer packet,
final NetworkMarker marker, final boolean filtered) {
return new Runnable() {
@Override
public void run() {
try {
// Attempt to send it now
switch (side) {
case CLIENT_SIDE:
delegate.recieveClientPacket(player, packet, marker, filtered);
break;
case SERVER_SIDE:
delegate.sendServerPacket(player, packet, marker, filtered);
break;
default:
throw new IllegalArgumentException("side cannot be " + side);
}
} catch (Exception e) {
// Inform about this plugin error
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_PACKET).
callerParam(delegate).messageParam(packet).error(e));
}
}
};
}
@Override
public void setPlayerHook(PlayerInjectHooks playerHook) {
this.hook = playerHook;
}
@Override
public PlayerInjectHooks getPlayerHook() {
return hook;
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet) throws InvocationTargetException {
sendServerPacket(receiver, packet, null, true);
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, boolean filters) throws InvocationTargetException {
sendServerPacket(receiver, packet, null, filters);
}
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) throws InvocationTargetException {
if (delegate != null) {
delegate.sendServerPacket(receiver, packet, marker, filters);
} else {
queuedActions.add(queuedAddPacket(ConnectionSide.SERVER_SIDE, receiver, packet, marker, filters));
}
}
@Override
public void sendWirePacket(Player receiver, int id, byte[] bytes) throws InvocationTargetException {
WirePacket packet = new WirePacket(id, bytes);
sendWirePacket(receiver, packet);
}
@Override
public void sendWirePacket(final Player receiver, final WirePacket packet) throws InvocationTargetException {
if (delegate != null) {
delegate.sendWirePacket(receiver, packet);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
try {
delegate.sendWirePacket(receiver, packet);
} catch (Throwable ex) {
// Inform about this plugin error
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_QUEUED_WIRE_PACKET)
.callerParam(delegate)
.messageParam(packet)
.error(ex));
}
}
});
}
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, true);
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, boolean filters) throws IllegalAccessException, InvocationTargetException {
recieveClientPacket(sender, packet, null, filters);
}
@Override
public void recieveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) throws IllegalAccessException, InvocationTargetException {
if (delegate != null) {
delegate.recieveClientPacket(sender, packet, marker, filters);
} else {
queuedActions.add(queuedAddPacket(ConnectionSide.CLIENT_SIDE, sender, packet, marker, filters));
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet, final Entity entity, final boolean includeTracker) {
if (delegate != null) {
delegate.broadcastServerPacket(packet, entity, includeTracker);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet, entity, includeTracker);
}
});
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet, final Location origin, final int maxObserverDistance) {
if (delegate != null) {
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet, origin, maxObserverDistance);
}
});
}
}
@Override
public void broadcastServerPacket(final PacketContainer packet) {
if (delegate != null) {
delegate.broadcastServerPacket(packet);
} else {
queuedActions.add(new Runnable() {
@Override
public void run() {
delegate.broadcastServerPacket(packet);
}
});
}
}
@Override
public ImmutableSet<PacketListener> getPacketListeners() {
if (delegate != null)
return delegate.getPacketListeners();
else
return ImmutableSet.copyOf(queuedListeners);
}
@Override
public void addPacketListener(PacketListener listener) {
if (delegate != null)
delegate.addPacketListener(listener);
else
queuedListeners.add(listener);
}
@Override
public void removePacketListener(PacketListener listener) {
if (delegate != null)
delegate.removePacketListener(listener);
else
queuedListeners.remove(listener);
}
@Override
public void removePacketListeners(Plugin plugin) {
if (delegate != null) {
delegate.removePacketListeners(plugin);
} else {
for (Iterator<PacketListener> it = queuedListeners.iterator(); it.hasNext(); ) {
// Remove listeners of the same plugin
if (Objects.equal(it.next().getPlugin(), plugin)) {
it.remove();
}
}
}
}
@Override
@Deprecated
public PacketContainer createPacket(int id) {
if (delegate != null)
return delegate.createPacket(id);
return createPacket(id, true);
}
@Override
@Deprecated
public PacketContainer createPacket(int id, boolean forceDefaults) {
if (delegate != null) {
return delegate.createPacket(id);
} else {
// Fallback implementation
PacketContainer packet = new PacketContainer(id);
// Use any default values if possible
if (forceDefaults) {
try {
packet.getModifier().writeDefaults();
} catch (FieldAccessException e) {
throw new RuntimeException("Security exception.", e);
}
}
return packet;
}
}
@SuppressWarnings("deprecation")
@Override
public PacketConstructor createPacketConstructor(int id, Object... arguments) {
if (delegate != null)
return delegate.createPacketConstructor(id, arguments);
else
return PacketConstructor.DEFAULT.withPacket(id, arguments);
}
@Override
public PacketConstructor createPacketConstructor(PacketType type, Object... arguments) {
if (delegate != null)
return delegate.createPacketConstructor(type, arguments);
else
return PacketConstructor.DEFAULT.withPacket(type, arguments);
}
@Override
@Deprecated
public Set<Integer> getSendingFilters() {
if (delegate != null) {
return delegate.getSendingFilters();
} else {
// Linear scan is fast enough here
Set<Integer> sending = Sets.newHashSet();
for (PacketListener listener : queuedListeners) {
sending.addAll(listener.getSendingWhitelist().getWhitelist());
}
return sending;
}
}
@Override
@Deprecated
public Set<Integer> getReceivingFilters() {
if (delegate != null) {
return delegate.getReceivingFilters();
} else {
Set<Integer> recieving = Sets.newHashSet();
for (PacketListener listener : queuedListeners) {
recieving.addAll(listener.getReceivingWhitelist().getWhitelist());
}
return recieving;
}
}
@Override
public PacketContainer createPacket(PacketType type) {
return createPacket(type.getLegacyId());
}
@Override
public PacketContainer createPacket(PacketType type, boolean forceDefaults) {
return createPacket(type.getLegacyId(), forceDefaults);
}
@Override
public Set<PacketType> getSendingFilterTypes() {
return PacketRegistry.toPacketTypes(getSendingFilters(), Sender.SERVER);
}
@Override
public Set<PacketType> getReceivingFilterTypes() {
return PacketRegistry.toPacketTypes(getReceivingFilters(), Sender.CLIENT);
}
@Override
public void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
if (delegate != null)
delegate.updateEntity(entity, observers);
else
EntityUtilities.updateEntity(entity, observers);
}
@Override
public Entity getEntityFromID(World container, int id) throws FieldAccessException {
if (delegate != null)
return delegate.getEntityFromID(container, id);
else
return EntityUtilities.getEntityFromID(container, id);
}
@Override
public List<Player> getEntityTrackers(Entity entity) throws FieldAccessException {
if (delegate != null)
return delegate.getEntityTrackers(entity);
else
return EntityUtilities.getEntityTrackers(entity);
}
@Override
public boolean isClosed() {
return closed || (delegate != null && delegate.isClosed());
}
@Override
public AsynchronousManager getAsynchronousManager() {
if (delegate != null)
return delegate.getAsynchronousManager();
else
return asyncManager;
}
@Override
public boolean isDebug() {
return debug;
}
@Override
public void setDebug(boolean debug) {
this.debug = debug;
if (delegate != null) {
delegate.setDebug(debug);
}
}
/**
* Update the asynchronous manager. This must be set.
* @param asyncManager - the asynchronous manager.
*/
public void setAsynchronousManager(AsynchronousManager asyncManager) {
this.asyncManager = asyncManager;
}
@Override
public void registerEvents(PluginManager manager, Plugin plugin) {
if (delegate != null) {
delegate.registerEvents(manager, plugin);
} else {
queuedManager = manager;
queuedPlugin = plugin;
}
}
@Override
public void close() {
if (delegate != null)
delegate.close();
closed = true;
}
@Override
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist) {
for (PacketType type : whitelist.getTypes()) {
if (type == null) {
throw new IllegalArgumentException(String.format("Packet type in in listener %s was NULL.",
PacketAdapter.getPluginName(listener))
);
}
}
}
}
@@ -1,149 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
/**
* Represents a single delayed task.
*
* @author Kristian
*/
public class DelayedSingleTask {
protected int taskID = -1;
protected Plugin plugin;
protected BukkitScheduler scheduler;
protected boolean closed;
/**
* Create a single task scheduler.
* @param plugin - owner plugin.
*/
public DelayedSingleTask(Plugin plugin) {
this.plugin = plugin;
this.scheduler = plugin.getServer().getScheduler();
}
/**
* Create a single task scheduler.
* @param plugin - owner plugin.
* @param scheduler - specialized scheduler.
*/
public DelayedSingleTask(Plugin plugin, BukkitScheduler scheduler) {
this.plugin = plugin;
this.scheduler = scheduler;
}
/**
* Schedule a single task for execution.
* <p>
* Any previously scheduled task will be automatically cancelled.
* <p>
* Note that a tick delay of zero will execute the task immediately.
*
* @param ticksDelay - number of ticks before the task is executed.
* @param task - the task to schedule.
* @return TRUE if the task was successfully scheduled or executed, FALSE otherwise.
*/
public boolean schedule(long ticksDelay, Runnable task) {
if (ticksDelay < 0)
throw new IllegalArgumentException("Tick delay cannot be negative.");
if (task == null)
throw new IllegalArgumentException("task cannot be NULL");
if (closed)
return false;
// Special case
if (ticksDelay == 0) {
task.run();
return true;
}
// Boilerplate, boilerplate
final Runnable dispatch = task;
// Don't run multiple tasks!
cancel();
taskID = scheduler.scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
public void run() {
dispatch.run();
taskID = -1;
}
}, ticksDelay);
return isRunning();
}
/**
* Whether or not a future task is scheduled to be executed.
* @return TRUE if a current task has been scheduled for execution, FALSE otherwise.
*/
public boolean isRunning() {
return taskID >= 0;
}
/**
* Cancel a future task from being executed.
* @return TRUE if a task was cancelled, FALSE otherwise.
*/
public boolean cancel() {
if (isRunning()) {
scheduler.cancelTask(taskID);
taskID = -1;
return true;
} else {
return false;
}
}
/**
* Retrieve the raw task ID.
* @return Raw task ID, or negative one if no task has been scheduled.
*/
public int getTaskID() {
return taskID;
}
/**
* Retrieve the plugin this task belongs to.
* @return The plugin scheduling the current taks.
*/
public Plugin getPlugin() {
return plugin;
}
/**
* Stop the current task and all future tasks scheduled by this instance.
*/
public synchronized void close() {
if (!closed) {
cancel();
plugin = null;
scheduler = null;
closed = true;
}
}
@Override
protected void finalize() throws Throwable {
close();
}
}
@@ -1,275 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
import com.google.common.collect.Lists;
/**
* Used to perform certain operations on entities.
*
* @author Kristian
*/
class EntityUtilities {
private static Field entityTrackerField;
private static Field trackedEntitiesField;
private static Field trackedPlayersField;
private static Field trackerField;
private static Method scanPlayersMethod;
/*
* While this function may look pretty bad, it's essentially just a reflection-warped
* version of the following:
*
* @SuppressWarnings("unchecked")
* public static void updateEntity2(Entity entity, List<Player> observers) {
*
* World world = entity.getWorld();
* WorldServer worldServer = ((CraftWorld) world).getHandle();
*
* EntityTracker tracker = worldServer.tracker;
* EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
*
* List<EntityPlayer> nmsPlayers = getNmsPlayers(observers);
*
* entry.trackedPlayers.removeAll(nmsPlayers);
* entry.scanPlayers(nmsPlayers);
* }
*
* private static List<EntityPlayer> getNmsPlayers(List<Player> players) {
* List<EntityPlayer> nsmPlayers = new ArrayList<EntityPlayer>();
*
* for (Player bukkitPlayer : players) {
* CraftPlayer craftPlayer = (CraftPlayer) bukkitPlayer;
* nsmPlayers.add(craftPlayer.getHandle());
* }
*
* return nsmPlayers;
* }
*
*/
public static void updateEntity(Entity entity, List<Player> observers) throws FieldAccessException {
try {
//EntityTrackerEntry trackEntity = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId());
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackedPlayersField == null) {
// This one is fairly easy
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
// Phew, finally there.
Collection<?> trackedPlayers = (Collection<?>) FieldUtils.readField(trackedPlayersField, trackerEntry, false);
List<Object> nmsPlayers = unwrapBukkit(observers);
// trackEntity.trackedPlayers.clear();
trackedPlayers.removeAll(nmsPlayers);
// We have to rely on a NAME once again. Damn it.
// TODO: Make sure this stays up to date with version changes - 1.9
if (scanPlayersMethod == null) {
scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class);
}
//trackEntity.scanPlayers(server.players);
scanPlayersMethod.invoke(trackerEntry, nmsPlayers);
} catch (IllegalArgumentException e) {
throw e;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Security limitation prevents access to 'get' method in IntHashMap", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Exception occurred in Minecraft.", e);
} catch (SecurityException e) {
throw new FieldAccessException("Security limitation prevents access to 'scanPlayers' method in trackerEntry.", e);
} catch (NoSuchMethodException e) {
throw new FieldAccessException("Cannot find 'scanPlayers' method. Is ProtocolLib up to date?", e);
}
}
/**
* Retrieve every client that is receiving information about a given entity.
* @param entity - the entity that is being tracked.
* @return Every client/player that is tracking the given entity.
* @throws FieldAccessException If reflection failed.
*/
public static List<Player> getEntityTrackers(Entity entity) {
try {
List<Player> result = new ArrayList<Player>();
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
if (trackerEntry == null) {
throw new IllegalArgumentException("Cannot find entity trackers for " + entity + (entity.isDead() ? " - entity is dead." : "."));
}
if (trackedPlayersField == null) {
trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*");
}
Collection<?> trackedPlayers = null;
Object value = FieldUtils.readField(trackedPlayersField, trackerEntry, false);
if (value instanceof Collection) {
trackedPlayers = (Collection<?>) value;
} else if (value instanceof Map) { // PaperSpigot
trackedPlayers = ((Map<?, ?>) value).keySet();
} else { // Please, no more changes
throw new IllegalStateException("trackedPlayers field was an unknown type: expected Set or Map, but got " + value.getClass());
}
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
return result;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Security limitation prevented access to the list of tracked players.", e);
}
}
/**
* Retrieve the entity tracker entry given a ID.
* @param world - world server.
* @param entityID - entity ID.
* @return The entity tracker entry.
* @throws FieldAccessException
*/
private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException {
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
Object worldServer = unwrapper.unwrapItem(world);
if (entityTrackerField == null)
entityTrackerField = FuzzyReflection.fromObject(worldServer).
getFieldByType("tracker", MinecraftReflection.getEntityTrackerClass());
// Get the tracker
Object tracker = null;
try {
tracker = FieldUtils.readField(entityTrackerField, worldServer, false);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'tracker' field due to security limitations.", e);
}
if (trackedEntitiesField == null) {
@SuppressWarnings("rawtypes")
Set<Class> ignoredTypes = new HashSet<Class>();
// Well, this is more difficult. But we're looking for a Minecraft object that is not
// created by the constructor(s).
for (Constructor<?> constructor : tracker.getClass().getConstructors()) {
for (Class<?> type : constructor.getParameterTypes()) {
ignoredTypes.add(type);
}
}
// The Minecraft field that's NOT filled in by the constructor
trackedEntitiesField = FuzzyReflection.fromObject(tracker, true).
getFieldByType(MinecraftReflection.getMinecraftObjectRegex(), ignoredTypes);
}
// Read the entity hashmap
Object trackedEntities = null;
try {
trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e);
}
return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID);
}
/**
* Retrieve entity from a ID, even it it's newly created.
* @return The asssociated entity.
* @throws FieldAccessException Reflection error.
*/
public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException {
try {
Object trackerEntry = getEntityTrackerEntry(world, entityID);
Object tracker = null;
// Handle NULL cases
if (trackerEntry != null) {
if (trackerField == null) {
try {
Class<?> entryClass = MinecraftReflection.getMinecraftClass("EntityTrackerEntry");
trackerField = entryClass.getDeclaredField("tracker");
} catch (NoSuchFieldException e) {
// Assume it's the first entity field then
trackerField = FuzzyReflection.fromObject(trackerEntry, true)
.getFieldByType("tracker", MinecraftReflection.getEntityClass());
}
}
tracker = FieldUtils.readField(trackerField, trackerEntry, true);
}
// If the tracker is NULL, we'll just assume this entity doesn't exist
if (tracker != null)
return (Entity) MinecraftReflection.getBukkitEntity(tracker);
else
return null;
} catch (Exception e) {
throw new FieldAccessException("Cannot find entity from ID " + entityID + ".", e);
}
}
private static List<Object> unwrapBukkit(List<Player> players) {
List<Object> output = Lists.newArrayList();
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
// Get the NMS equivalent
for (Player player : players) {
Object result = unwrapper.unwrapItem(player);
if (result != null)
output.add(result);
else
throw new IllegalArgumentException("Cannot unwrap item " + player);
}
return output;
}
}
@@ -1,94 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.packet.InterceptWritePacket;
/**
* Represents an object that initiate the packet listeners.
*
* @author Kristian
*/
public interface ListenerInvoker {
/**
* Invokes the given packet event for every registered listener.
* @param event - the packet event to invoke.
*/
public abstract void invokePacketRecieving(PacketEvent event);
/**
* Invokes the given packet event for every registered listener.
* @param event - the packet event to invoke.
*/
public abstract void invokePacketSending(PacketEvent event);
/**
* Retrieve the associated ID of a packet.
* @param packet - the packet.
* @return The packet ID.
*/
@Deprecated
public abstract int getPacketID(Object packet);
/**
* Retrieve the associated type of a packet.
* @param packet - the packet.
* @return The packet type.
*/
public abstract PacketType getPacketType(Object packet);
/**
* Retrieve the object responsible for intercepting write packets.
* @return Object that intercepts write packets.
*/
public InterceptWritePacket getInterceptWritePacket();
/**
* Determine if a given packet requires input buffering.
* @param packetId - the packet to check.
* @return TRUE if it does, FALSE otherwise.
*/
@Deprecated
public boolean requireInputBuffer(int packetId);
/**
* Associate a given class with the given packet ID. Internal method.
* @param clazz - class to associate.
*/
public abstract void unregisterPacketClass(Class<?> clazz);
/**
* Register a given class in the packet registry. Internal method.
* @param clazz - class to register.
* @param packetID - the the new associated packet ID.
*/
@Deprecated
public abstract void registerPacketClass(Class<?> clazz, int packetID);
/**
* Retrieves the correct packet class from a given packet ID.
* @param packetID - the packet ID.
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
* @return The associated class.
*/
@Deprecated
public abstract Class<?> getPacketClassFromID(int packetID, boolean forceVanilla);
}
@@ -1,92 +0,0 @@
package com.comphenix.protocol.injector;
import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.Packets;
import com.comphenix.protocol.concurrency.IntegerSet;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
/**
* Packets that are known to be transmitted during login.
* <p>
* This may be dynamically extended later.
* @author Kristian
*/
class LoginPackets {
private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
@SuppressWarnings("deprecation")
public LoginPackets(MinecraftVersion version) {
// Ordinary login
clientSide.add(Packets.Client.HANDSHAKE);
serverSide.add(Packets.Server.KEY_REQUEST);
clientSide.add(Packets.Client.KEY_RESPONSE);
serverSide.add(Packets.Server.KEY_RESPONSE);
clientSide.add(Packets.Client.CLIENT_COMMAND);
serverSide.add(Packets.Server.LOGIN);
// List ping
clientSide.add(Packets.Client.GET_INFO);
// In 1.6.2, Minecraft started sending CUSTOM_PAYLOAD in the server list protocol
// MCPC+/Cauldron contains Forge, which uses CUSTOM_PAYLOAD during login
if (version.isAtLeast(MinecraftVersion.HORSE_UPDATE) || isCauldronOrMCPC()) {
clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
}
if (isCauldronOrMCPC()) {
serverSide.add(Packets.Server.CUSTOM_PAYLOAD);
}
serverSide.add(Packets.Server.KICK_DISCONNECT);
}
/**
* Determine if we are running MCPC or Cauldron.
* @return TRUE if we are, FALSE otherwise.
*/
private static boolean isCauldronOrMCPC() {
String version = Bukkit.getServer().getVersion();
return version.contains("MCPC") || version.contains("Cauldron");
}
/**
* Determine if a packet may be sent during login from a given direction.
* @param packetId - the ID of the packet.
* @param side - the direction.
* @return TRUE if it may, FALSE otherwise.
*/
@Deprecated
public boolean isLoginPacket(int packetId, ConnectionSide side) {
switch (side) {
case CLIENT_SIDE:
return clientSide.contains(packetId);
case SERVER_SIDE:
return serverSide.contains(packetId);
case BOTH:
return clientSide.contains(packetId) ||
serverSide.contains(packetId);
default:
throw new IllegalArgumentException("Unknown connection side: " + side);
}
}
/**
* Determine if a given packet may be sent during login.
* @param type - the packet type.
* @return TRUE if it may, FALSE otherwise.
*/
public boolean isLoginPacket(PacketType type) {
if (!MinecraftReflection.isUsingNetty())
return isLoginPacket(type.getLegacyId(), type.getSender().toSide());
return PacketType.Login.Client.getInstance().hasMember(type) ||
PacketType.Login.Server.getInstance().hasMember(type) ||
PacketType.Status.Client.getInstance().hasMember(type) ||
PacketType.Status.Server.getInstance().hasMember(type);
}
}
@@ -1,108 +0,0 @@
package com.comphenix.protocol.injector;
import java.util.List;
import java.util.PriorityQueue;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketOutputHandler;
import com.comphenix.protocol.events.PacketPostListener;
import com.comphenix.protocol.events.ScheduledPacket;
/**
* Represents a processor for network markers.
* @author Kristian
*/
public class NetworkProcessor {
private ErrorReporter reporter;
/**
* Construct a new network processor.
* @param reporter - the reporter.
*/
public NetworkProcessor(ErrorReporter reporter) {
this.reporter = reporter;
}
/**
* Process the serialized packet byte array with the given network marker.
* @param event - current packet event.
* @param marker - the network marker.
* @param input - the input array.
* @return The output array.
*/
public byte[] processOutput(PacketEvent event, NetworkMarker marker, final byte[] input) {
// Bit of a hack - but we need the performance
PriorityQueue<PacketOutputHandler> handlers = (PriorityQueue<PacketOutputHandler>)
marker.getOutputHandlers();
byte[] output = input;
// Let each handler prepare the actual output
while (!handlers.isEmpty()) {
PacketOutputHandler handler = handlers.poll();
try {
byte[] changed = handler.handle(event, output);
// Don't break just because a plugin returned NULL
if (changed != null) {
output = changed;
} else {
throw new IllegalStateException("Handler cannot return a NULL array.");
}
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportMinimal(handler.getPlugin(), "PacketOutputHandler.handle()", e);
}
}
return output;
}
/**
* Invoke the post listeners and packet transmission, if any.
* @param event - PacketEvent
* @param marker - the network marker, or NULL.
*/
public void invokePostEvent(PacketEvent event, NetworkMarker marker) {
if (marker == null)
return;
if (NetworkMarker.hasPostListeners(marker)) {
// Invoke every sent listener
for (PacketPostListener listener : marker.getPostListeners()) {
try {
listener.onPostEvent(event);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
}
}
}
sendScheduledPackets(marker);
}
/**
* Send any scheduled packets.
* @param marker - the network marker.
*/
private void sendScheduledPackets(NetworkMarker marker) {
// Next, invoke post packet transmission
List<ScheduledPacket> scheduled = NetworkMarker.readScheduledPackets(marker);
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
if (scheduled != null) {
for (ScheduledPacket packet : scheduled) {
packet.schedule(manager);
}
}
}
}
@@ -1,256 +0,0 @@
package com.comphenix.protocol.injector;
import javax.annotation.Nonnull;
import org.bukkit.Server;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.Plugin;
import com.comphenix.executors.BukkitFutures;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.player.InjectedServerConnection;
import com.comphenix.protocol.injector.spigot.SpigotPacketInjector;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
public class PacketFilterBuilder {
public static final ReportType REPORT_TEMPORARY_EVENT_ERROR = new ReportType("Unable to register or handle temporary event.");
public static final ReportType REPORT_SPIGOT_IS_DELAYING_INJECTOR = new ReportType("Delaying due to Spigot.");
private ClassLoader classLoader;
private Server server;
private Plugin library;
private MinecraftVersion mcVersion;
private DelayedSingleTask unhookTask;
private ErrorReporter reporter;
// Whether or not we need to enable Netty
private AsyncFilterManager asyncManager;
private boolean nettyEnabled;
/**
* Update the current class loader.
* @param classLoader - current class loader.
* @return This builder, for chaining.
*/
public PacketFilterBuilder classLoader(@Nonnull ClassLoader classLoader) {
if (classLoader == null)
throw new IllegalArgumentException("classLoader cannot be NULL.");
this.classLoader = classLoader;
return this;
}
/**
* Set the current server.
* @param server - current server.
* @return This builder, for chaining.
*/
public PacketFilterBuilder server(@Nonnull Server server) {
if (server == null)
throw new IllegalArgumentException("server cannot be NULL.");
this.server = server;
return this;
}
/**
* Set a reference to the plugin instance of ProtocolLib.
* @param library - plugin instance.
* @return This builder, for chaining.
*/
public PacketFilterBuilder library(@Nonnull Plugin library) {
if (library == null)
throw new IllegalArgumentException("library cannot be NULL.");
this.library = library;
return this;
}
/**
* Set the current Minecraft version.
* @param mcVersion - Minecraft version.
* @return This builder, for chaining.
*/
public PacketFilterBuilder minecraftVersion(@Nonnull MinecraftVersion mcVersion) {
if (mcVersion == null)
throw new IllegalArgumentException("minecraftVersion cannot be NULL.");
this.mcVersion = mcVersion;
return this;
}
/**
* Set the task used to delay unhooking when ProtocolLib is no in use.
* @param unhookTask - the unhook task.
* @return This builder, for chaining.
*/
public PacketFilterBuilder unhookTask(@Nonnull DelayedSingleTask unhookTask) {
if (unhookTask == null)
throw new IllegalArgumentException("unhookTask cannot be NULL.");
this.unhookTask = unhookTask;
return this;
}
/**
* Set the error reporter.
* @param reporter - new error reporter.
* @return This builder, for chaining.
*/
public PacketFilterBuilder reporter(@Nonnull ErrorReporter reporter) {
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
this.reporter = reporter;
return this;
}
/**
* Determine if we should prepare to hook Netty in Spigot.
* <p>
* This is calculated in the {@link #build()} method.
* @return TRUE if we should, FALSE otherwise.
*/
public boolean isNettyEnabled() {
return nettyEnabled;
}
/**
* Retrieve the class loader set in this builder.
* @return The class loader.
*/
public ClassLoader getClassLoader() {
return classLoader;
}
/**
* Retrieve the current CraftBukkit server.
* @return Current server.
*/
public Server getServer() {
return server;
}
/**
* Retrieve a reference to the current ProtocolLib instance.
* @return ProtocolLib.
*/
public Plugin getLibrary() {
return library;
}
/**
* Retrieve the current Minecraft version.
* @return Current version.
*/
public MinecraftVersion getMinecraftVersion() {
return mcVersion;
}
/**
* Retrieve the task that is used to delay unhooking when ProtocolLib is no in use.
* @return The unhook task.
*/
public DelayedSingleTask getUnhookTask() {
return unhookTask;
}
/**
* Retrieve the error reporter.
* @return Error reporter.
*/
public ErrorReporter getReporter() {
return reporter;
}
/**
* Retrieve the asynchronous manager.
* <p>
* This is first constructed the {@link #build()} method.
* @return The asynchronous manager.
*/
public AsyncFilterManager getAsyncManager() {
return asyncManager;
}
/**
* Create a new packet filter manager.
* @return A new packet filter manager.
*/
public InternalManager build() {
if (reporter == null)
throw new IllegalArgumentException("reporter cannot be NULL.");
if (classLoader == null)
throw new IllegalArgumentException("classLoader cannot be NULL.");
asyncManager = new AsyncFilterManager(reporter, server.getScheduler());
nettyEnabled = false;
// Spigot
if (SpigotPacketInjector.canUseSpigotListener()) {
// If the server hasn't loaded yet - wait
if (InjectedServerConnection.getServerConnection(reporter, server) == null) {
// We need to delay this until we know if Netty is enabled
final DelayedPacketManager delayed = new DelayedPacketManager(reporter, mcVersion);
// They must reference each other
delayed.setAsynchronousManager(asyncManager);
asyncManager.setManager(delayed);
Futures.addCallback(BukkitFutures.nextEvent(library, WorldInitEvent.class),
new FutureCallback<WorldInitEvent>() {
@Override
public void onSuccess(WorldInitEvent event) {
// Nevermind
if (delayed.isClosed())
return;
try {
registerSpigot(delayed);
} catch (Exception e) {
onFailure(e);
}
}
@Override
public void onFailure(Throwable error) {
reporter.reportWarning(PacketFilterBuilder.this, Report
.newBuilder(REPORT_TEMPORARY_EVENT_ERROR).error(error));
}
});
reporter.reportWarning(this, Report.newBuilder(REPORT_SPIGOT_IS_DELAYING_INJECTOR));
// Let plugins use this version instead
return delayed;
} else {
nettyEnabled = !MinecraftReflection.isMinecraftObject(
InjectedServerConnection.getServerConnection(reporter, server));
}
}
// Otherwise - construct the packet filter manager right away
return buildInternal();
}
private void registerSpigot(DelayedPacketManager delayed) {
// Use netty if we have a non-standard ServerConnection class
nettyEnabled = !MinecraftReflection.isMinecraftObject(
InjectedServerConnection.getServerConnection(reporter, server));
// Switch to the standard manager
delayed.setDelegate(buildInternal());
}
/**
* Construct a new packet filter manager without checking for Netty.
* @return A new packet filter manager.
*/
private PacketFilterManager buildInternal() {
PacketFilterManager manager = new PacketFilterManager(this);
// It's a cyclic reference, but it's too late to fix now
asyncManager.setManager(manager);
return manager;
}
}
Loaded 100 of 470 files, more files were not shown because too many files have changed in this diff. Show more