Compare commits

..
157 Commits
Author SHA1 Message Date
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
171 changed files with 11730 additions and 9512 deletions

No files matched your search

+2 -1
View File
@@ -4,7 +4,8 @@ Make sure you've done 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`
Debug paste link:
Description and relevant errors:
Description and relevant errors:
+1 -1
View File
@@ -8,7 +8,7 @@
.settings/
bin/
.idea/
.iml
*.iml
.ipr
.iws
-1
View File
@@ -1,7 +1,6 @@
language: java
jdk:
- oraclejdk8
- oraclejdk7
script: mvn clean test
#before_install: cd ProtocolLib
install: true
+16 -10
View File
@@ -44,20 +44,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>http://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
...
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5</version>
<artifactId>ProtocolLib-API</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "http://repo.dmulloy2.net/nexus/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.4.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
@@ -155,9 +167,3 @@ It's likely that I won't have to update ProtocolLib for anything but bug fixes a
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
+15 -67
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>4.0</version>
<version>4.4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -12,19 +12,19 @@
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
@@ -38,23 +38,23 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.7.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.1.0</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-Version>${project.fullVersion}</Implementation-Version>
<Implementation-Vendor>dmulloy2</Implementation-Vendor>
</manifestEntries>
</archive>
@@ -65,7 +65,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
@@ -88,6 +88,7 @@
</activation>
<properties>
<project.build.number>-b${env.BUILD_NUMBER}</project.build.number>
<project.fullVersion>${project.version}${project.build.number}</project.fullVersion>
</properties>
</profile>
@@ -99,6 +100,9 @@
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
@@ -129,6 +133,7 @@
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
@@ -143,6 +148,7 @@
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
@@ -184,62 +190,4 @@
<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>
@@ -152,4 +152,4 @@ public interface AsynchronousManager {
* @return Set of every asynchronous packet listener.
*/
public abstract Set<PacketListener> getAsyncHandlers();
}
}
@@ -1,9 +1,14 @@
package com.comphenix.protocol;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
@@ -15,7 +20,6 @@ import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.ObjectEnum;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Objects;
@@ -31,12 +35,12 @@ import com.google.common.util.concurrent.Futures;
* Note that vanilla Minecraft reuses packet IDs per protocol (ping, game, login) and IDs are subject to change, so they are not reliable.
* @author Kristian
*/
public class PacketType implements Serializable, Comparable<PacketType> {
public class PacketType implements Serializable, Cloneable, Comparable<PacketType> {
// Increment whenever the type changes
private static final long serialVersionUID = 1L;
/**
* Represents an unknown legacy packet ID.
* Represents an unknown packet ID.
*/
public static final int UNKNOWN_PACKET = -1;
@@ -51,7 +55,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SetProtocol");
@@ -59,7 +63,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Client getInstance() {
return INSTANCE;
@@ -73,10 +77,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* An empty enum, as the server will not send any packets in this protocol.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
private final static Server INSTANCE = new Server();
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Server getInstance() {
return INSTANCE;
@@ -102,115 +106,155 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x0E, "SpawnEntity");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x11, "SpawnEntityExperienceOrb");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x2C, "SpawnEntityWeather");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x0F, "SpawnEntityLiving");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x10, "SpawnEntityPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x0C, "NamedEntitySpawn");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x0B, "Animation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x37, "Statistic");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x25, "BlockBreakAnimation");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x35, "TileEntityData");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x24, "BlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x23, "BlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0xFF, "Boss");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x41, "ServerDifficulty");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x3A, "TabComplete");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0F, 0x02, "Chat");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x10, 0x22, "MultiBlockChange");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x11, 0x32, "Transaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, 0x2E, "CloseWindow");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x2D, "OpenWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x14, 0x30, "WindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x15, 0x31, "WindowData");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x16, 0x2F, "SetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, 0xFC, "SetCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, 0x3F, "CustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x19, 0xFD, "CustomSoundEffect");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x40, "KickDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "EntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1C, 0x27, "Explosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1D, 0xFA, "UnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1E, 0x2B, "GameStateChange");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, 0x00, "KeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, 0x21, "MapChunk");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, 0x28, "WorldEvent");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, 0x2A, "WorldParticles");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x23, 0x01, "Login");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x24, 0x34, "Map");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x25, 0x15, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x26, 0x17, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x27, 0x16, "EntityLook");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x28, 0x14, "Entity");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x29, 0xFE, "VehicleMove");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2A, 0x36, "OpenSignEditor");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2B, 0x39, "Abilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2C, 0x42, "CombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x2D, 0x38, "PlayerInfo");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x2E, 0x08, "Position");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x2F, 0x0A, "Bed");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x30, 0x13, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x31, 0x1E, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x32, 0x48, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x33, 0x07, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x34, 0x19, "EntityHeadRotation");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x35, 0x44, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x36, 0x43, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x37, 0x09, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x38, 0x3D, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x39, 0x1C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3A, 0x1B, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x3B, 0x12, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x3C, 0x04, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x3D, 0x1F, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x3E, 0x06, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3B, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x40, 0xFB, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x42, 0x3C, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x43, 0x05, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x44, 0x03, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x45, 0x45, "Title");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x47, 0x29, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x48, 0x47, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x49, 0x0D, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x4A, 0x18, "EntityTeleport");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4B, 0x20, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4C, 0x1D, "EntityEffect");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SpawnEntity");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "SpawnEntityExperienceOrb");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "SpawnEntityWeather");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SpawnEntityLiving");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "SpawnEntityPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x05, "NamedEntitySpawn");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x06, "Animation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x07, "Statistic");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x08, "BlockBreakAnimation");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x09, "TileEntityData");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x0A, "BlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x0B, "BlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0x0C, "Boss");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0D, "ServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0F, "Chat");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0F, 0x10, "MultiBlockChange");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, 0x0E, "TabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, -0x01, "Commands");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x12, 0x11, "Transaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x12, "CloseWindow");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x14, 0x13, "OpenWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x15, 0x14, "WindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x16, 0x15, "WindowData");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x16, "SetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x18, 0x17, "SetCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, 0x18, "CustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x19, "CustomSoundEffect");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "KickDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, 0x1B, "EntityStatus");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x1D, -0x01, "NBTQuery");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1E, 0x1C, "Explosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1F, 0x1D, "UnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x20, 0x1E, "GameStateChange");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x21, 0x1F, "KeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x22, 0x20, "MapChunk");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x23, 0x21, "WorldEvent");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x24, 0x22, "WorldParticles");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x25, 0x23, "Login");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x26, 0x24, "Map");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x27, 0x25, "Entity");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x28, 0x26, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, 0x27, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, 0x28, "EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, 0x29, "VehicleMove");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2C, 0x2A, "OpenSignEditor");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2D, 0x2B, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2E, 0x2C, "Abilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2F, 0x2D, "CombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x30, 0x2E, "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x31, -0x01, "LookAt");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x32, 0x2F, "Position");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, 0x30, "Bed");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x34, 0x31, "Recipes");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x35, 0x32, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x36, 0x33, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x37, 0x34, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x38, 0x35, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x39, 0x36, "EntityHeadRotation");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3A, 0x37, "SelectAdvancementTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3B, 0x38, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3C, 0x39, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3D, 0x3A, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3E, 0x3B, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x40, 0x3D, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x42, 0x3F, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x43, 0x40, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x44, 0x41, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x45, 0x42, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x46, 0x43, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x47, 0x44, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, 0x45, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x49, 0x46, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4A, 0x47, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4B, 0x48, "Title");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x4C, -0x01, "StopSound");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4D, 0x49, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x4E, 0x4A, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x4F, 0x4B, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x50, 0x4C, "EntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x51, 0x4D, "Advancements");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x52, 0x4E, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x53, 0x4F, "EntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x54, 0x54, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x55, 0x55, "Tags");
// ---- Removed in 1.9
/**
* @deprecated Replaced by {@link WINDOW_DATA}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA;
public static final PacketType MAP_CHUNK_BULK = new PacketType(PROTOCOL, SENDER, 255, 255, "MapChunkBulk");
/**
* @deprecated Replaced by {@link REL_ENTITY_MOVE_LOOK}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK;
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 254, 254, "SetCompression");
/**
* @deprecated Replaced by {@link STATISTIC}
* @deprecated Removed in 1.9
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC;
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, 253, "UpdateEntityNBT");
// ----- Renamed packets
/**
* @deprecated Replaced by {@link TILE_ENTITY_DATA}
* @deprecated Renamed to {@link #WINDOW_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = TILE_ENTITY_DATA;
public static final PacketType CRAFT_PROGRESS_BAR = WINDOW_DATA.clone();
/**
* @deprecated Renamed to {@link #REL_ENTITY_MOVE_LOOK}
*/
@Deprecated
public static final PacketType ENTITY_MOVE_LOOK = REL_ENTITY_MOVE_LOOK.clone();
/**
* @deprecated Renamed to {@link #STATISTIC}
*/
@Deprecated
public static final PacketType STATISTICS = STATISTIC.clone();
/**
* @deprecated Renamed to {@link #OPEN_SIGN_EDITOR}
*/
@Deprecated
public static final PacketType OPEN_SIGN_ENTITY = OPEN_SIGN_EDITOR.clone();
// ----- Replaced in 1.9.4
/**
* @deprecated Replaced by {@link #TILE_ENTITY_DATA}
*/
@Deprecated
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, 252, "UpdateSign") :
TILE_ENTITY_DATA.clone();
// The instance must
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -224,44 +268,57 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0xF9, "TeleportAccept");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x01, 0x14, "TabComplete");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x01, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x16, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x15, "Settings");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x05, 0x0F, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x06, 0x11, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x07, 0x0E, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x08, 0x0D, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x09, 0x17, "CustomPayload");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0A, 0x02, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x00, "KeepAlive");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x0C, 0x04, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x0D, 0x06, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x0E, 0x05, "Look");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x03, "Flying");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x10, 0xF8, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x11, 0xF7, "BoatMove");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x12, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x13, 0x07, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x14, 0x0B, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x15, 0x0C, "SteerVehicle");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x16, 0x19, "ResourcePackStatus");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x09, "HeldItemSlot");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x18, 0x10, "SetCreativeSlot");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x19, 0x12, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x1A, 0x0A, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x1B, 0x18, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x1C, 0xF6, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x1D, 0x08, "BlockPlace");
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "TeleportAccept");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, -0x01, "TileNBTQuery");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "Settings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x05, 0x01, "TabComplete");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x06, 0x05, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, 0x06, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, 0x07, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, 0x08, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, 0x09, "CustomPayload");
public static final PacketType BOOK_EDIT = new PacketType(PROTOCOL, SENDER, 0x0B, -0x01, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0C, -0x01, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0A, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0B, "KeepAlive");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x0C, "Flying");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x10, 0x0D, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x11, 0x0E, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x12, 0x0F, "Look");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x13, 0x10, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x14, 0x11, "BoatMove");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x15, -0x01, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x16, 0x12, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x17, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x18, 0x14, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x19, 0x15, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1A, 0x16, "SteerVehicle");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1B, 0x17, "RecipeDisplayed");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x1C, -0x01, "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x1D, 0x18, "ResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x1E, 0x19, "Advancements");
public static final PacketType TRADE_SELECT = new PacketType(PROTOCOL, SENDER, 0x1F, -0x01, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x20, -0x01, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x21, 0x1A, "HeldItemSlot");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x22, 0x22, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x23, 0x23, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x24, 0x1B, "SetCreativeSlot");
public static final PacketType STRUCTURE_BLOCK = new PacketType(PROTOCOL, SENDER, 0x25, 0x25, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x26, 0x1C, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x27, 0x1D, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x28, 0x1E, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x29, 0x1F, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2A, 0x20, "BlockPlace");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -287,22 +344,24 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "ServerInfo").forceAsync(true);
@ForceAsync
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "ServerInfo");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "Pong");
/**
* @deprecated Replaced by {@link SERVER_INFO}
* @deprecated Renamed to {@link #SERVER_INFO}
*/
@Deprecated
public static final PacketType OUT_SERVER_INFO = SERVER_INFO;
@ForceAsync
public static final PacketType OUT_SERVER_INFO = SERVER_INFO.clone();
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -316,7 +375,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
@@ -325,7 +384,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -351,18 +410,19 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Outgoing packets.
* @author Kristian
*/
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Disconnect");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Success");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SetCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, -0x01, "CustomPayload");
private final static Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(PacketType.class); }
private Server() { super(); }
public static Sender getSender() {
return SENDER;
@@ -376,16 +436,17 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Incoming packets.
* @author Kristian
*/
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, -0x01, "CustomPayload");
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -412,7 +473,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @author Kristian
*/
// Missing server packets: [10, 11, 12, 21, 107, 252]
public static class Server extends ObjectEnum<PacketType> {
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType PLAYER_FLYING = PacketType.newLegacy(SENDER, 10);
@@ -436,7 +497,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
// Prevent accidental construction
private Server() {
super(PacketType.class);
super();
}
public static Sender getSender() {
@@ -452,7 +513,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @author Kristian
*/
// Missing client packets: [1, 9, 255]
public static class Client extends ObjectEnum<PacketType> {
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType LOGIN = PacketType.newLegacy(SENDER, 1);
@@ -462,7 +523,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final static Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(PacketType.class); }
private Client() { super(); }
public static Sender getSender() {
return SENDER;
@@ -512,7 +573,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
public String getPacketName() {
return WordUtils.capitalize(name().toLowerCase());
return WordUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
}
}
@@ -545,13 +606,20 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
}
/**
* Whether or not packets of this type must be handled asynchronously.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForceAsync { }
// Lookup of packet types
private static PacketTypeLookup LOOKUP;
/**
* Protocol version of all the current IDs.
*/
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.BOUNTIFUL_UPDATE;
private static final MinecraftVersion PROTOCOL_VERSION = MinecraftVersion.EXPLORATION_UPDATE;
private final Protocol protocol;
private final Sender sender;
@@ -560,7 +628,10 @@ public class PacketType implements Serializable, Comparable<PacketType> {
private final MinecraftVersion version;
private final String[] classNames;
private String name;
private boolean deprecated;
private boolean forceAsync;
private boolean dynamic;
/**
@@ -612,7 +683,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param packetId - the legacy packet ID.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the legacy packet could not be found.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType findLegacy(int packetId) {
PacketType type = getLookup().getFromLegacy(packetId);
@@ -627,7 +700,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param preference - the preferred sender, or NULL for any arbitrary sender.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the legacy packet could not be found.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType findLegacy(int packetId, Sender preference) {
if (preference == null)
return findLegacy(packetId);
@@ -642,7 +717,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* Determine if the given legacy packet exists.
* @param packetId - the legacy packet ID.
* @return TRUE if it does, FALSE otherwise.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static boolean hasLegacy(int packetId) {
return getLookup().getFromLegacy(packetId) != null;
}
@@ -650,18 +727,19 @@ public class PacketType implements Serializable, Comparable<PacketType> {
/**
* Retrieve a packet type from a protocol, sender and packet ID.
* <p>
* It is usually better to access the packet types statically, like so:
* It is almost always better to access the packet types statically, like so:
* <ul>
* <li>{@link PacketType.Play.Server#SPAWN_ENTITY}
* </ul>
* However there are some valid uses for packet IDs. Please note that IDs
* change almost every Minecraft version.
*
* @param protocol - the current protocol.
* @param sender - the sender.
* @param packetId - the packet ID.
* @return The corresponding packet type.
* @throws IllegalArgumentException If the current packet could not be found.
* @deprecated IDs are no longer reliable
*/
@Deprecated
public static PacketType findCurrent(Protocol protocol, Sender sender, int packetId) {
PacketType type = getLookup().getFromCurrent(protocol, sender, packetId);
@@ -687,7 +765,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
if (name.contains("Packet"))
return name;
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), name);
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), WordUtils.capitalize(name));
}
/**
@@ -696,9 +774,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param sender - the sender.
* @param packetId - the packet ID.
* @return TRUE if it exists, FALSE otherwise.
* @deprecated IDs are no longer reliable
*/
@Deprecated
public static boolean hasCurrent(Protocol protocol, Sender sender, int packetId) {
return getLookup().getFromCurrent(protocol, sender, packetId) != null;
}
@@ -711,7 +787,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param sender - the sender of the packet, or NULL if unknown.
* @return The packet type.
* @throws IllegalArgumentException If the sender is NULL and the packet doesn't exist.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public static PacketType fromLegacy(int id, Sender sender) {
PacketType type = getLookup().getFromLegacy(id, sender);
@@ -765,24 +843,11 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Map<String, PacketType> map = lookup.getMap(protocol, sender);
// Check the map first
String className = packetClass.getSimpleName();
PacketType type = map.get(className);
String clazz = packetClass.getSimpleName();
PacketType type = find(map, clazz);
if (type == null) {
// Then check any aliases
for (PacketType check : map.values()) {
String[] aliases = check.getClassNames();
if (aliases.length > 1) {
for (String alias : aliases) {
if (alias.equals(className)) {
// We have a match!
type = check;
}
}
}
}
// Guess we don't support this packet :/
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, className);
type = new PacketType(protocol, sender, packetId, -1, PROTOCOL_VERSION, clazz);
type.dynamic = true;
// Many may be scheduled, but only the first will be executed
@@ -792,6 +857,28 @@ public class PacketType implements Serializable, Comparable<PacketType> {
return type;
}
private static PacketType find(Map<String, PacketType> map, String clazz) {
PacketType ret = map.get(clazz);
if (ret != null) {
return ret;
}
// Check any aliases
for (PacketType check : map.values()) {
String[] aliases = check.getClassNames();
if (aliases.length > 1) {
for (String alias : aliases) {
if (alias.equals(clazz)) {
// We have a match!
return check;
}
}
}
}
return null;
}
/**
* Lookup a packet type from a packet class.
* @param packetClass - the packet class.
@@ -837,7 +924,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Callable<Boolean> callable = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
ObjectEnum<PacketType> objEnum;
PacketTypeEnum objEnum;
// A bit ugly, but performance is critical
objEnum = getObjectEnum(type);
@@ -866,7 +953,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* @param type - the packet type.
* @return The corresponding object enum.
*/
public static ObjectEnum<PacketType> getObjectEnum(final PacketType type) {
public static PacketTypeEnum getObjectEnum(final PacketType type) {
switch (type.getProtocol()) {
case HANDSHAKING:
return type.isClient() ? Handshake.Client.getInstance() : Handshake.Server.getInstance();
@@ -913,7 +1000,6 @@ public class PacketType implements Serializable, Comparable<PacketType> {
for (int i = 0; i < classNames.length; i++) {
classNames[i] = format(protocol, sender, names[i]);
}
//System.out.println(Arrays.toString(classNames));
}
/**
@@ -971,11 +1057,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* <p>
* This is only unique within a specific protocol and target.
* <p>
* It is only unknown if the packet was removed in Minecraft 1.7.2.
* It is unknown if the packet was removed at any point.
* @return The current ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated IDs are subject to change
*/
@Deprecated
public int getCurrentId() {
return currentId;
}
@@ -996,12 +1080,47 @@ public class PacketType implements Serializable, Comparable<PacketType> {
}
}
// Only used by Enum processor
void setName(String name) {
this.name = name;
}
/**
* Retrieve the declared enum name of this packet type.
* @return The enum name.
*/
public String name() {
return getObjectEnum(this).getDeclaredName(this);
return name;
}
// Only used by enum processor
void setDeprecated() {
this.deprecated = true;
}
/**
* Whether or not this packet is deprecated. Deprecated packet types have either been renamed, replaced, or removed.
* Kind of like the thing they use to tell children to recycle except with packets you probably shouldn't be using.
*
* @return True if the type is deprecated, false if not
*/
public boolean isDeprecated() {
return deprecated;
}
// Only used by enum processor
void forceAsync() {
this.forceAsync = true;
}
/**
* Whether or not the processing of this packet must take place on a thread different than the main thread. You don't
* get a choice. If this is false it's up to you.
*
* @return True if async processing is forced, false if not.
*/
public boolean isAsyncForced() {
return forceAsync;
}
/**
@@ -1017,7 +1136,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
* <p>
* This ID is globally unique.
* @return The legacy ID, or {@link #UNKNOWN_PACKET} if unknown.
* @deprecated Legacy IDs haven't functioned properly for some time
*/
@Deprecated
public int getLegacyId() {
return legacyId;
}
@@ -1030,22 +1151,9 @@ public class PacketType implements Serializable, Comparable<PacketType> {
return dynamic;
}
private PacketType forceAsync(boolean forceAsync) {
this.forceAsync = forceAsync;
return this;
}
/**
* Whether or not this packet must be processed asynchronously.
* @return True if it must be, false if not.
*/
public boolean forceAsync() {
return forceAsync;
}
@Override
public int hashCode() {
return Objects.hashCode(protocol, sender, currentId, legacyId);
return Objects.hashCode(protocol, sender, currentId);
}
@Override
@@ -1057,8 +1165,7 @@ public class PacketType implements Serializable, Comparable<PacketType> {
PacketType other = (PacketType) obj;
return protocol == other.protocol &&
sender == other.sender &&
currentId == other.currentId &&
legacyId == other.legacyId;
currentId == other.currentId;
}
return false;
}
@@ -1069,7 +1176,6 @@ public class PacketType implements Serializable, Comparable<PacketType> {
compare(protocol, other.getProtocol()).
compare(sender, other.getSender()).
compare(currentId, other.getCurrentId()).
compare(legacyId, other.getLegacyId()).
result();
}
@@ -1078,8 +1184,17 @@ public class PacketType implements Serializable, Comparable<PacketType> {
Class<?> clazz = getPacketClass();
if (clazz == null)
return "UNREGISTERED[" + protocol + ", " + sender + ", " + currentId + ", legacy: " + legacyId + ", classNames: " + Arrays.toString(classNames) + "]";
return name() + "[" + protocol + ", " + sender + ", " + currentId + ", classNames: " + Arrays.toString(classNames) + " (unregistered)]";
else
return clazz.getSimpleName() + "[" + currentId + ", legacy: " + legacyId + "]";
return name() + "[class=" + clazz.getSimpleName() + ", id=" + currentId + "]";
}
}
@Override
public PacketType clone() {
try {
return (PacketType) super.clone();
} catch (CloneNotSupportedException ex) {
throw new Error("This shouldn't happen", ex);
}
}
}
@@ -15,7 +15,7 @@
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect;
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -23,8 +23,10 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
/**
* Represents a more modern object-based enum.
@@ -33,41 +35,53 @@ import com.google.common.collect.HashBiMap;
* want to prevent the creation of additional members dynamically.
* @author Kristian
*/
public class ObjectEnum<T> implements Iterable<T> {
public class PacketTypeEnum implements Iterable<PacketType> {
// Used to convert between IDs and names
protected BiMap<T, String> members = HashBiMap.create();
protected Set<PacketType> members = Sets.newHashSet();
/**
* Registers every declared integer field.
* @param fieldType Field type
* Registers every declared PacketType field.
*/
public ObjectEnum(Class<T> fieldType) {
registerAll(fieldType);
public PacketTypeEnum() {
registerAll();
}
/**
* Registers every public assignable static field as a member.
* @param fieldType Field type
*/
@SuppressWarnings("unchecked")
protected void registerAll(Class<T> fieldType) {
protected void registerAll() {
try {
// Register every int field
// Register every non-deprecated field
for (Field entry : this.getClass().getFields()) {
if (Modifier.isStatic(entry.getModifiers()) && fieldType.isAssignableFrom(entry.getType())) {
T value = (T) entry.get(null);
if (value == null)
throw new IllegalArgumentException("Field " + entry + " was NULL. Remember to " +
"construct the object after the field has been declared.");
registerMember(value, entry.getName());
if (Modifier.isStatic(entry.getModifiers()) && PacketType.class.isAssignableFrom(entry.getType())) {
PacketType value = (PacketType) entry.get(null);
if (value == null) {
throw new IllegalArgumentException("Field " + entry.getName() + " was null!");
}
value.setName(entry.getName());
if (entry.getAnnotation(PacketType.ForceAsync.class) != null) {
value.forceAsync();
}
boolean deprecated = entry.getAnnotation(Deprecated.class) != null;
if (deprecated) value.setDeprecated();
if (members.contains(value)) {
// Replace potentially deprecated packet types with non-deprecated ones
if (!deprecated) {
members.remove(value);
members.add(value);
}
} else {
members.add(value);
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
@@ -77,11 +91,14 @@ public class ObjectEnum<T> implements Iterable<T> {
* @param name - name of member.
* @return TRUE if the member was registered, FALSE otherwise.
*/
public boolean registerMember(T instance, String name) {
if (!members.containsKey(instance)) {
members.put(instance, name);
public boolean registerMember(PacketType instance, String name) {
instance.setName(name);
if (!members.contains(instance)) {
members.add(instance);
return true;
}
return false;
}
@@ -90,38 +107,36 @@ public class ObjectEnum<T> implements Iterable<T> {
* @param member - the member to check.
* @return TRUE if the given member has been registered, FALSE otherwise.
*/
public boolean hasMember(T member) {
return members.containsKey(member);
public boolean hasMember(PacketType member) {
return members.contains(member);
}
/**
* Retrieve a member by name,
* @param name - name of member to retrieve.
* @return The member, or NULL if not found.
* @deprecated Don't use this
*/
public T valueOf(String name) {
return members.inverse().get(name);
@Deprecated
public PacketType valueOf(String name) {
for (PacketType member : members) {
if (member.name().equals(name))
return member;
}
return null;
}
/**
* Retrieve the name of the given member.
* @param member - the member to retrieve.
* @return Declared name of the member, or NULL if not found.
*/
public String getDeclaredName(T member) {
return members.get(member);
}
/**
* Retrieve every registered member.
* @return Enumeration of every value.
*/
public Set<T> values() {
return new HashSet<T>(members.keySet());
public Set<PacketType> values() {
return new HashSet<>(members);
}
@Override
public Iterator<T> iterator() {
return members.keySet().iterator();
public Iterator<PacketType> iterator() {
return members.iterator();
}
}
@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
@@ -157,6 +158,9 @@ public class ProtocolConfig {
}
if (global != null) {
updater = global.getConfigurationSection(SECTION_AUTOUPDATER);
if (updater.getValues(true).isEmpty()) {
plugin.getLogger().warning("Updater section is missing, regenerate your config!");
}
}
// Automatically copy defaults
@@ -256,7 +260,7 @@ public class ProtocolConfig {
* @return TRUE if it should, FALSE otherwise.
*/
public boolean isAutoDownload() {
return updater != null && getUpdaterValue(UPDATER_DOWNLAD, true);
return updater != null && getUpdaterValue(UPDATER_DOWNLAD, false);
}
/**
@@ -461,7 +465,7 @@ public class ProtocolConfig {
PlayerInjectHooks hook = getDefaultMethod();
if (text != null)
hook = PlayerInjectHooks.valueOf(text.toUpperCase().replace(" ", "_"));
hook = PlayerInjectHooks.valueOf(text.toUpperCase(Locale.ENGLISH).replace(" ", "_"));
return hook;
}
@@ -497,4 +501,4 @@ public class ProtocolConfig {
valuesChanged = false;
configChanged = false;
}
}
}
@@ -1,144 +1,143 @@
/**
* 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;
}
}
/**
* 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.8";
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.13.1";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.13.1) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22";
/**
* 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;
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,63 +1,82 @@
/**
* 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.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
/**
* @author dmulloy2
*/
public class ProtocolLogger {
private static Logger logger;
protected static void init(Plugin plugin) {
ProtocolLogger.logger = plugin.getLogger();
}
/**
* Logs a message to console with a given level.
* @param level Logging level
* @param message Message to log
* @param args Arguments to format in
*/
public static void log(Level level, String message, Object... args) {
logger.log(level, MessageFormat.format(message, args));
}
/**
* Logs a method to console with the INFO level.
* @param message Message to log
* @param args Arguments to format in
*/
public static void log(String message, Object... args) {
log(Level.INFO, message, args);
}
/**
* Logs a message to console with a given level and exception.
* @param level Logging level
* @param message Message to log
* @param ex Exception to log
*/
public static void log(Level level, String message, Throwable ex) {
logger.log(level, message, ex);
}
}
/**
* 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.text.MessageFormat;
import java.util.logging.Level;
import org.bukkit.plugin.Plugin;
/**
* @author dmulloy2
*/
public class ProtocolLogger {
private static Plugin plugin;
protected static void init(Plugin plugin) {
ProtocolLogger.plugin = plugin;
}
public static boolean isDebugEnabled() {
try {
return plugin.getConfig().getBoolean("global.debug", false);
} catch (Throwable ex) { // Enable in testing environments
return true;
}
}
/**
* Logs a message to console with a given level.
* @param level Logging level
* @param message Message to log
* @param args Arguments to format in
*/
public static void log(Level level, String message, Object... args) {
plugin.getLogger().log(level, MessageFormat.format(message, args));
}
/**
* Logs a method to console with the INFO level.
* @param message Message to log
* @param args Arguments to format in
*/
public static void log(String message, Object... args) {
log(Level.INFO, message, args);
}
/**
* Logs a message to console with a given level and exception.
* @param level Logging level
* @param message Message to log
* @param ex Exception to log
*/
public static void log(Level level, String message, Throwable ex) {
plugin.getLogger().log(level, message, ex);
}
public static void debug(String message, Object... args) {
if (isDebugEnabled()) {
log("[Debug] " + message, args);
}
}
public static void debug(String message, Throwable ex) {
if (isDebugEnabled()) {
plugin.getLogger().log(Level.WARNING, "[Debug] " + message, ex);
}
}
}
@@ -282,4 +282,4 @@ public interface ProtocolManager extends PacketStream {
public AsynchronousManager getAsynchronousManager();
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
}
}
@@ -24,8 +24,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
@@ -36,10 +34,11 @@ import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.timing.TimedListenerManager;
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
import com.comphenix.protocol.timing.TimedTracker;
import com.comphenix.protocol.utility.WrappedScheduler;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import org.bukkit.plugin.Plugin;
/**
* Represents a handler for an asynchronous event.
* <p>
@@ -345,8 +344,7 @@ public class AsyncListenerHandler {
}
private void scheduleAsync(Runnable runnable) {
// Handle deprecation
WrappedScheduler.runAsynchronouslyRepeat(listener.getPlugin(), filterManager.getScheduler(), runnable, 0L, -1L);
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
}
/**
@@ -243,4 +243,4 @@ public class ExpireHashMap<K, V> {
return valueView.toString();
}
}
@@ -87,4 +87,4 @@ public interface ErrorReporter {
* @param reportBuilder - an error report builder that will be used to get the report.
*/
public abstract void reportDetailed(Object sender, ReportBuilder reportBuilder);
}
}
@@ -238,12 +238,7 @@ public class ListeningWhitelist {
if (this == EMPTY_WHITELIST)
return "EMPTY_WHITELIST";
else
return Objects.toStringHelper(this).
add("priority", priority).
add("packets", types).
add("gamephase", gamePhase).
add("options", options).
toString();
return "ListeningWhitelist[priority=" + priority + ", packets=" + types + ", gamephase=" + gamePhase + ", options=" + options + "]";
}
/**
File diff suppressed because it is too large. Load diff
@@ -33,6 +33,7 @@ import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.PluginContext;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.server.TemporaryPlayer;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
@@ -321,7 +322,41 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The player associated with this event.
*/
public Player getPlayer() {
return playerReference.get();
Player player = playerReference.get();
// Check if the player has been updated so we can do more stuff
if (player instanceof TemporaryPlayer) {
Player updated = player.getPlayer();
if (updated != null && !(updated instanceof TemporaryPlayer)) {
playerReference.clear();
playerReference = new WeakReference<>(updated);
return updated;
}
}
return player;
}
/**
* Whether or not the player in this PacketEvent is temporary (i.e. they don't have a true player instance yet).
* Temporary players have a limited subset of methods that may be used:
* <ul>
* <li>getPlayer</li>
* <li>getAddress</li>
* <li>getServer</li>
* <li>chat</li>
* <li>sendMessage</li>
* <li>kickPlayer</li>
* <li>isOnline</li>
* </ul>
*
* Anything else will throw an UnsupportedOperationException. Use this check before calling other methods when
* dealing with packets early in the login sequence or if you get the aforementioned exception.
*
* @return True if the player is temporary, false if not.
*/
public boolean isPlayerTemporary() {
return getPlayer() instanceof TemporaryPlayer;
}
/**
@@ -453,4 +488,4 @@ public class PacketEvent extends EventObject implements Cancellable {
public String toString() {
return "PacketEvent[player=" + getPlayer() + ", packet=" + packet + "]";
}
}
}
@@ -0,0 +1,143 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 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.events;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang3.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
* @author dmulloy2
*/
class PacketMetadata {
private static class MetaObject<T> {
private final String key;
private final T value;
private MetaObject(String key, T value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof MetaObject) {
MetaObject that = (MetaObject) o;
return that.key.equals(this.key) &&
that.value.equals(this.value);
}
return false;
}
@Override
public String toString() {
return "MetaObject[" + key + "=" + value + "]";
}
}
// Packet meta cache
private static Cache<Object, List<MetaObject>> META_CACHE;
public static <T> Optional<T> get(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
if (meta == null) {
return Optional.empty();
}
for (MetaObject object : meta) {
if (object.key.equals(key)) {
return Optional.of((T) object.value);
}
}
return Optional.empty();
}
private static void createCache() {
META_CACHE = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
}
public static <T> void set(Object packet, String key, T value) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
createCache();
}
List<MetaObject> packetMeta;
try {
packetMeta = META_CACHE.get(packet, ArrayList::new);
} catch (ExecutionException ex) {
// Not possible, but let's humor the array list constructor having an issue
packetMeta = new ArrayList<>();
}
packetMeta.removeIf(meta -> meta.key.equals(key));
packetMeta.add(new MetaObject<>(key, value));
META_CACHE.put(packet, packetMeta);
}
public static <T> Optional<T> remove(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
if (packetMeta == null) {
return Optional.empty();
}
Optional<T> value = Optional.empty();
Iterator<MetaObject> iter = packetMeta.iterator();
while (iter.hasNext()) {
MetaObject meta = iter.next();
if (meta.key.equals(key)) {
value = Optional.of((T) meta.value);
iter.remove();
}
}
return value;
}
}
@@ -7,7 +7,6 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
/**
@@ -138,10 +137,6 @@ public class ScheduledPacket {
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("packet", packet)
.add("target", target)
.add("filtered", filtered)
.toString();
return "ScheduledPacket[packet=" + packet + ", target=" + target + ", filtered=" + filtered + "]";
}
}
@@ -139,7 +139,6 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return banned;
}
@Override
public void setBanned(boolean banned) {
this.banned = banned;
}
@@ -24,6 +24,9 @@ import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
@@ -31,6 +34,7 @@ import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.primitives.Primitives;
/**
@@ -182,20 +186,55 @@ public class BukkitUnwrapper implements Unwrapper {
Report.newBuilder(REPORT_SECURITY_LIMITATION).error(e).callerParam(type)
);
} catch (NoSuchMethodException e) {
// Maybe it's a proxy?
Unwrapper proxyUnwrapper = getProxyUnwrapper(type);
if (proxyUnwrapper != null)
return proxyUnwrapper;
// Try getting the field unwrapper too
Unwrapper fieldUnwrapper = getFieldUnwrapper(type);
if (fieldUnwrapper != null)
return fieldUnwrapper;
else
reporter.reportDetailed(this,
Report.newBuilder(REPORT_CANNOT_FIND_UNWRAP_METHOD).error(e).callerParam(type));
}
// Default method
return null;
}
// Players should /always/ be able to be unwrapped
// We should only get here if the 'Player' is a proxy
private Unwrapper getProxyUnwrapper(final Class<?> type) {
try {
if (Player.class.isAssignableFrom(type)) {
final Method getHandle = MinecraftReflection.getCraftPlayerClass().getMethod("getHandle");
Unwrapper unwrapper = new Unwrapper() {
@Override
public Object unwrapItem(Object wrapped) {
try {
return getHandle.invoke(((Player) wrapped).getPlayer());
} catch (Throwable ex) {
try {
return getHandle.invoke(Bukkit.getPlayer(((Player) wrapped).getUniqueId()));
} catch (ReflectiveOperationException ex1) {
throw new RuntimeException("Failed to unwrap proxy " + wrapped, ex);
}
}
}
};
unwrapperCache.put(type, unwrapper);
return unwrapper;
}
} catch (Throwable ignored) {
}
return null;
}
/**
* Retrieve a cached unwrapper using the handle field.
* @param type - a cached field unwrapper.
@@ -1,31 +1,31 @@
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;
}
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;
}
@@ -67,9 +67,11 @@ public class StructureCache {
// TODO: Optimize DefaultInstances
Object result = DefaultInstances.DEFAULT.create(clazz);
if (result != null) {
return result;
if (result == null) {
throw new IllegalArgumentException("Failed to create packet for type: " + type);
}
return result;
}
throw new IllegalArgumentException("Cannot find associated packet class: " + type);
}
@@ -172,4 +174,4 @@ public class StructureCache {
}
return result;
}
}
}
@@ -28,6 +28,7 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.channels.WritableByteChannel;
@@ -44,6 +45,7 @@ import com.google.common.io.ByteStreams;
* all indexing in the byte buffer.
* @author Kristian
*/
@SuppressWarnings("unused")
public class NettyByteBufAdapter extends AbstractByteBuf {
private DataInputStream input;
private DataOutputStream output;
@@ -214,7 +216,7 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
@Override
public ByteBufAllocator alloc() {
return null;
return ByteBufAllocator.DEFAULT;
}
@Override
@@ -389,4 +391,48 @@ public class NettyByteBufAdapter extends AbstractByteBuf {
public ByteBuf retain() {
return this;
}
}
protected int _getIntLE(int arg0) {
return 0;
}
protected long _getLongLE(int arg0) {
return 0;
}
protected short _getShortLE(int arg0) {
return 0;
}
protected int _getUnsignedMediumLE(int arg0) {
return 0;
}
protected void _setIntLE(int arg0, int arg1) {
}
protected void _setLongLE(int arg0, long arg1) {
}
protected void _setMediumLE(int arg0, int arg1) {
}
protected void _setShortLE(int arg0, int arg1) {
}
public int getBytes(int arg0, FileChannel arg1, long arg2, int arg3) throws IOException {
return 0;
}
public int setBytes(int arg0, FileChannel arg1, long arg2, int arg3) throws IOException {
return 0;
}
public ByteBuf touch() {
return null;
}
public ByteBuf touch(Object arg0) {
return null;
}
}
@@ -1,104 +1,108 @@
/**
* 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
}
}
}
}
/**
* 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.ProtocolLogger;
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 {
public NettyProtocolRegistry() {
super();
}
@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 occasionally 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 (Object protocol : protocols) {
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 (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
}
}
}
@@ -19,7 +19,7 @@ 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.
@@ -97,7 +97,7 @@ public abstract class ProtocolRegistry {
* This operation may block the calling thread.
*/
public synchronized void synchronize() {
// See if the register is outdated
// Check if the packet registry has changed
if (register.isOutdated()) {
initialize();
}
@@ -122,4 +122,4 @@ public abstract class ProtocolRegistry {
count += map.size();
return count;
}
}
}
@@ -16,31 +16,224 @@
*/
package com.comphenix.protocol.injector.netty;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* A packet represented only by its id and bytes.
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
/**
* Constructs a new WirePacket with a given type and contents
* @param type Type of the packet
* @param bytes Contents of the packet
*/
public WirePacket(PacketType type, byte[] bytes) {
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
this.bytes = bytes;
}
/**
* Constructs a new WirePacket with a given id and contents
* @param id ID of the packet
* @param bytes Contents of the packet
*/
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
/**
* Gets this packet's ID
* @return The ID
*/
public int getId() {
return id;
}
/**
* Gets this packet's contents as a byte array
* @return The contents
*/
public byte[] getBytes() {
return bytes;
}
/**
* Writes the id of this packet to a given output
* @param output Output to write to
*/
public void writeId(ByteBuf output) {
int i = id;
writeVarInt(output, id);
}
/**
* Writes the contents of this packet to a given output
* @param output Output to write to
*/
public void writeBytes(ByteBuf output) {
checkNotNull(output, "output cannot be null!");
output.writeBytes(bytes);
}
/**
* Fully writes the ID and contents of this packet to a given output
* @param output Output to write to
*/
public void writeFully(ByteBuf output) {
writeId(output);
writeBytes(output);
}
/**
* Serializes this packet into a byte buffer
* @return The buffer
*/
public ByteBuf serialize() {
ByteBuf buffer = Unpooled.buffer();
writeFully(buffer);
return buffer;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof WirePacket) {
WirePacket that = (WirePacket) obj;
return this.id == that.id &&
Arrays.equals(this.bytes, that.bytes);
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
result = prime * result + id;
return result;
}
@Override
public String toString() {
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
}
private static byte[] getBytes(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
buffer.readBytes(array);
return array;
}
/**
* Creates a WirePacket from an existing PacketContainer
* @param packet Existing packet
* @return The resulting WirePacket
*/
public static WirePacket fromPacket(PacketContainer packet) {
int id = packet.getType().getCurrentId();
return new WirePacket(id, bytesFromPacket(packet));
}
/**
* Creates a byte array from an existing PacketContainer containing all the
* bytes from that packet
*
* @param packet Existing packet
* @return the byte array
*/
public static byte[] bytesFromPacket(PacketContainer packet) {
checkNotNull(packet, "packet cannot be null!");
ByteBuf buffer = PacketContainer.createPacketBuffer();
ByteBuf store = PacketContainer.createPacketBuffer();
// Read the bytes once
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet.getHandle(), buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read packet contents.", ex);
}
byte[] bytes = getBytes(buffer);
buffer.release();
// Rewrite them to the packet to avoid issues with certain packets
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
// Make a copy of the array before writing
byte[] ret = Arrays.copyOf(bytes, bytes.length);
store.writeBytes(bytes);
Method read = MinecraftMethods.getPacketReadByteBufMethod();
try {
read.invoke(packet.getHandle(), store);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to rewrite packet contents.", ex);
}
return ret;
}
store.release();
return bytes;
}
/**
* Creates a WirePacket from an existing Minecraft packet
* @param packet Existing Minecraft packet
* @return The resulting WirePacket
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
*/
public static WirePacket fromPacket(Object packet) {
checkNotNull(packet, "packet cannot be null!");
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
PacketType type = PacketType.fromClass(packet.getClass());
int id = type.getCurrentId();
ByteBuf buffer = PacketContainer.createPacketBuffer();
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet, buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to serialize packet contents.", ex);
}
byte[] bytes = getBytes(buffer);
buffer.release();
return new WirePacket(id, bytes);
}
public static void writeVarInt(ByteBuf output, int i) {
checkNotNull(output, "output cannot be null!");
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
@@ -49,7 +242,22 @@ public class WirePacket {
output.writeByte(i);
}
public void writeBytes(ByteBuf output) {
output.writeBytes(bytes);
public static int readVarInt(ByteBuf input) {
checkNotNull(input, "input cannot be null!");
int i = 0;
int j = 0;
byte b0;
do {
b0 = input.readByte();
i |= (b0 & 127) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((b0 & 128) == 128);
return i;
}
}
}
@@ -3,6 +3,7 @@ package com.comphenix.protocol.injector.packet;
import java.lang.reflect.Field;
import com.comphenix.protocol.reflect.FieldUtils;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents a class that can detect if a map has changed.
@@ -10,17 +11,20 @@ import com.comphenix.protocol.reflect.FieldUtils;
*/
public class MapContainer {
// For detecting changes
private Field modCountField;
private final Field modCountField;
private int lastModCount;
// The object along with whether or not this is the initial run
private Object source;
private final Object source;
private boolean changed;
public MapContainer(Object source) {
this.source = source;
this.changed = true;
this.modCountField = FieldUtils.getField(source.getClass(), "modCount", true);
this.changed = false;
Field modCountField = FieldUtils.getField(source.getClass(), "modCount", true);
this.modCountField = checkNotNull(modCountField, "Could not obtain modCount field");
this.lastModCount = getModificationCount();
}
/**
@@ -55,13 +59,13 @@ public class MapContainer {
/**
* Retrieve the current modification count.
* @return The current count, or something different than lastModCount if not accessible.
* @return The current count
*/
private int getModificationCount() {
try {
return modCountField != null ? modCountField.getInt(source) : lastModCount + 1;
} catch (Exception e) {
throw new RuntimeException("Unable to retrieve modCount.", e);
return modCountField.getInt(source);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Unable to retrieve modCount.", ex);
}
}
}
}
@@ -18,6 +18,7 @@
package com.comphenix.protocol.injector.packet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -28,6 +29,7 @@ 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.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -43,30 +45,30 @@ public class PacketRegistry {
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
// The Netty 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;
// Whether or not the registry has been initialized
private static volatile boolean INITIALIZED = false;
/**
* Initialize the packet registry.
* Initializes the packet registry.
*/
private static void initialize() {
if (INITIALIZED) {
// Make sure they were initialized
if (NETTY == null)
throw new IllegalStateException("No initialized registry.");
if (NETTY == null) {
throw new IllegalStateException("Failed to initialize packet registry.");
}
return;
}
// Check for netty
NETTY = new NettyProtocolRegistry();
INITIALIZED = true;
}
/**
@@ -154,11 +156,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getServerPackets() throws FieldAccessException {
initialize();
if (LEGACY_SERVER_PACKETS == null) {
LEGACY_SERVER_PACKETS = toLegacy(NETTY.getServerPackets());
LEGACY_SERVER_PACKETS = toLegacy(getServerPacketTypes());
}
return LEGACY_SERVER_PACKETS;
}
@@ -168,8 +169,8 @@ public class PacketRegistry {
*/
public static Set<PacketType> getServerPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getServerPackets();
}
@@ -182,11 +183,10 @@ public class PacketRegistry {
*/
@Deprecated
public static Set<Integer> getClientPackets() throws FieldAccessException {
initialize();
if (LEGACY_CLIENT_PACKETS == null) {
LEGACY_CLIENT_PACKETS = toLegacy(NETTY.getClientPackets());
LEGACY_CLIENT_PACKETS = toLegacy(getClientPacketTypes());
}
return LEGACY_CLIENT_PACKETS;
}
@@ -196,8 +196,8 @@ public class PacketRegistry {
*/
public static Set<PacketType> getClientPacketTypes() {
initialize();
NETTY.synchronize();
return NETTY.getClientPackets();
}
@@ -269,7 +269,24 @@ public class PacketRegistry {
*/
public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) {
initialize();
return NETTY.getPacketTypeLookup().get(type);
// Try the lookup first
Class<?> clazz = NETTY.getPacketTypeLookup().get(type);
if (clazz != null) {
return clazz;
}
// Then try looking up the class names
for (String name : type.getClassNames()) {
try {
clazz = MinecraftReflection.getMinecraftClass(name);
break;
} catch (Exception ex) {
}
}
// TODO Cache the result?
return clazz;
}
/**
@@ -319,4 +336,4 @@ public class PacketRegistry {
initialize();
return NETTY.getPacketClassLookup().get(packet);
}
}
}
@@ -4,10 +4,10 @@ import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
/**
* Represents an injector that only gives access to a player's socket.
*
@@ -68,4 +68,4 @@ public interface SocketInjector {
* @param updatedPlayer - the real Bukkit player.
*/
public abstract void setUpdatedPlayer(Player updatedPlayer);
}
}
@@ -1,21 +1,21 @@
package com.comphenix.protocol.injector.server;
/**
* Able to store a socket injector.
* A temporary player created by ProtocolLib when a true player instance does not exist.
* <p>
* A necessary hack.
* @author Kristian
* Also able to store a socket injector
* </p>
*/
class InjectorContainer {
public class TemporaryPlayer {
private volatile SocketInjector injector;
public SocketInjector getInjector() {
SocketInjector getInjector() {
return injector;
}
public void setInjector(SocketInjector injector) {
void setInjector(SocketInjector injector) {
if (injector == null)
throw new IllegalArgumentException("Injector cannot be NULL.");
this.injector = injector;
}
}
}
@@ -11,4 +11,4 @@
* }
* </pre>
*/
package com.comphenix.protocol;
package com.comphenix.protocol;
@@ -4,16 +4,11 @@ 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;
import net.sf.cglib.asm.*;
public class ClassAnalyser {
/**
* Represents a method in ASM.
@@ -31,11 +26,11 @@ public class ClassAnalyser {
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;
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);
}
}
@@ -109,30 +104,29 @@ public class ClassAnalyser {
* @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());
private 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() {
final String methodDescription = $Type.getMethodDescriptor(method);
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Check method
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (methodName.equals(name) && methodDescription.equals(desc)) {
return new EmptyMethodVisitor() {
return new $MethodVisitor($Opcodes.ASM5) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
}
};
}
return null;
}
}, ClassReader.EXPAND_FRAMES);
}, $ClassReader.EXPAND_FRAMES);
return output;
}
}
@@ -21,32 +21,30 @@ package com.comphenix.protocol.reflect;
* Interface that converts generic objects into types and back.
*
* @author Kristian
* @param <TType> The specific type.
* @param <T> The specific type.
*/
public interface EquivalentConverter<TType> {
/**
* Retrieve a copy of the specific type using an instance of the generic type.
* <p>
* This is usually a wrapper type in the Bukkit API.
* @param generic - the generic type.
* @return The new specific type.
*/
public TType getSpecific(Object generic);
public interface EquivalentConverter<T> {
/**
* Retrieve a copy of the generic type from a specific type.
* <p>
* This is usually a native net.minecraft.server type in Minecraft.
* @param genericType - class or super class of the generic type.
* @param specific - the specific type we need to copy.
* @return A copy of the specific type.
*/
public Object getGeneric(Class<?> genericType, TType specific);
Object getGeneric(T specific);
/**
* Due to type erasion, we need to explicitly keep a reference to the specific type.
* Retrieve a copy of the specific type using an instance of the generic type.
* <p>
* This is usually a wrapper type in the Bukkit API or ProtocolLib API.
* @param generic - the generic type.
* @return The new specific type.
*/
T getSpecific(Object generic);
/**
* Due to type erasure, we need to explicitly keep a reference to the specific type.
* @return The specific type.
*/
public Class<TType> getSpecificType();
}
Class<T> getSpecificType();
}
@@ -17,10 +17,7 @@
package com.comphenix.protocol.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -29,6 +26,8 @@ import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
@@ -419,6 +418,30 @@ public class FuzzyReflection {
return fields;
}
/**
* Retrieves a field with a given type and parameters. This is most useful
* when dealing with Collections.
*
* @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 Field getParameterizedField(Class<?> fieldType, Class<?>... params) {
for (Field field : getFields()) {
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));
}
/**
* Retrieve the first field that matches.
@@ -577,6 +600,8 @@ public class FuzzyReflection {
* @return Every field.
*/
public Set<Field> getFields() {
Validate.notNull(source, "source cannot be null!");
// We will only consider private fields in the declared class
if (forceAccess)
return setUnion(source.getDeclaredFields(), source.getFields());
@@ -17,7 +17,6 @@ import com.google.common.collect.Lists;
*
* @author Kristian
*/
@SuppressWarnings("unused") // Java 8 compat
public abstract class MethodInfo implements GenericDeclaration, Member {
/**
* Wraps a method as a MethodInfo object.
@@ -19,10 +19,7 @@ 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.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
@@ -263,6 +260,24 @@ public class StructureModifier<TField> {
return null;
}
}
/**
* Reads the value of a field only if it exists. If the field does not exist, an empty {@link Optional} is returned.
* <p>
* This method has the same functionality as {@link #readSafely(int)}, but enforces null checks by way of an Optional.
* It will eventually become the preferred method of reading fields.
*
* @param fieldIndex index of the field
* @return An optional that may contain the value of the field
* @see #readSafely(int)
*/
public Optional<TField> optionRead(int fieldIndex) {
try {
return Optional.ofNullable(read(fieldIndex));
} catch (FieldAccessException ex) {
return Optional.empty();
}
}
/**
* Determine whether or not a field is read-only (final).
@@ -352,7 +367,7 @@ public class StructureModifier<TField> {
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;
Object obj = needConversion() ? converter.getGeneric(value) : value;
try {
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
@@ -453,6 +468,17 @@ public class StructureModifier<TField> {
*/
@SuppressWarnings("unchecked")
public <T> StructureModifier<T> withType(Class fieldType, EquivalentConverter<T> converter) {
if (fieldType == null) {
// It's not supported in this version, so return an empty modifier
return new StructureModifier<T>() {
@Override
public T read(int index) { return null; }
@Override
public StructureModifier<T> write(int index, T value) { return this; }
};
}
StructureModifier<T> result = subtypeCache.get(fieldType);
// Do we need to update the cache?
@@ -462,7 +488,7 @@ public class StructureModifier<TField> {
int index = 0;
for (Field field : data) {
if (fieldType != null && fieldType.isAssignableFrom(field.getType())) {
if (fieldType.isAssignableFrom(field.getType())) {
filtered.add(field);
// Don't use the original index
@@ -476,14 +502,12 @@ public class StructureModifier<TField> {
// Cache structure modifiers
result = withFieldType(fieldType, filtered, defaults);
if (fieldType != null) {
subtypeCache.put(fieldType, result);
subtypeCache.put(fieldType, result);
// Automatically compile the structure modifier
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
}
// Automatically compile the structure modifier
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
}
// Add the target too
@@ -682,4 +706,4 @@ public class StructureModifier<TField> {
public String toString() {
return "StructureModifier[fieldType=" + fieldType + ", data=" + data + "]";
}
}
}
@@ -51,4 +51,4 @@ final class DefaultConstrutorAccessor implements ConstructorAccessor {
public String toString() {
return "DefaultConstrutorAccessor [constructor=" + constructor + "]";
}
}
}
@@ -57,4 +57,4 @@ final class DefaultFieldAccessor implements FieldAccessor {
public String toString() {
return "DefaultFieldAccessor [field=" + field + "]";
}
}
}
@@ -49,4 +49,4 @@ final class DefaultMethodAccessor implements MethodAccessor {
public String toString() {
return "DefaultMethodAccessor [method=" + method + "]";
}
}
}
@@ -27,4 +27,4 @@ public interface FieldAccessor {
* @return The field.
*/
public Field getField();
}
}
@@ -20,4 +20,4 @@ public interface MethodAccessor {
* @return The method.
*/
public Method getMethod();
}
}
@@ -254,10 +254,16 @@ public class AggregateCloner implements Cloner {
// Make sure the object is valid
if (index < cloners.size()) {
return cloners.get(index).clone(source);
Cloner cloner = cloners.get(index);
try {
return cloner.clone(source);
} catch (Exception ex) {
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ") with " + cloner, ex);
}
}
// Damn - failure
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is suitable.");
throw new IllegalArgumentException("Cannot clone " + source + " ( " + source.getClass() + "): No cloner is suitable.");
}
}
@@ -16,17 +16,19 @@
*/
package com.comphenix.protocol.reflect.cloning;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Supplier;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
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.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Maps;
/**
@@ -35,48 +37,56 @@ import com.google.common.collect.Maps;
* @author Kristian
*/
public class BukkitCloner implements Cloner {
// List of classes we support
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
private static final Map<Class<?>, Function<Object, Object>> CLONERS = Maps.newConcurrentMap();
public BukkitCloner() {
addClass(0, MinecraftReflection.getItemStackClass());
addClass(1, MinecraftReflection.getDataWatcherClass());
// Try to add position classes
private static void fromWrapper(Supplier<Class<?>> getClass, Function<Object, ClonableWrapper> fromHandle) {
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());
}
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, nmsObject -> fromHandle.apply(nmsObject).deepClone().getHandle());
}
} catch (RuntimeException ignored) { }
}
private void addClass(int id, Class<?> clazz) {
if (clazz != null)
clonableClasses.put(id, clazz);
private static void fromConverter(Supplier<Class<?>> getClass, EquivalentConverter converter) {
try {
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, nmsObject -> converter.getGeneric(converter.getSpecific(nmsObject)));
}
} catch (RuntimeException ignored) { }
}
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();
private static void fromManual(Supplier<Class<?>> getClass, Function<Object, Object> cloner) {
try {
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, cloner);
}
} catch (RuntimeException ignored) { }
}
static {
fromManual(MinecraftReflection::getItemStackClass, source ->
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
fromConverter(MinecraftReflection::getChunkPositionClass, ChunkPosition.getConverter());
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
fromWrapper(MinecraftReflection::getIBlockDataClass, WrappedBlockData::fromHandle);
fromManual(MinecraftReflection::getNonNullListClass, source -> nonNullListCloner().clone(source));
fromWrapper(MinecraftReflection::getNBTBaseClass, NbtFactory::fromNMS);
}
private Function<Object, Object> findCloner(Class<?> type) {
for (Entry<Class<?>, Function<Object, Object>> entry : CLONERS.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
return entry.getValue();
}
}
return -1;
return null;
}
@Override
@@ -84,7 +94,7 @@ public class BukkitCloner implements Cloner {
if (source == null)
return false;
return findMatchingClass(source.getClass()) >= 0;
return findCloner(source.getClass()) != null;
}
@Override
@@ -92,29 +102,39 @@ public class BukkitCloner implements Cloner {
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());
}
return findCloner(source.getClass()).apply(source);
}
}
private static Constructor<?> nonNullList = null;
private static Cloner nonNullListCloner() {
return new Cloner() {
@Override
public boolean canClone(Object source) {
return MinecraftReflection.is(MinecraftReflection.getNonNullListClass(), source);
}
@Override
public Object clone(Object source) {
StructureModifier<Object> modifier = new StructureModifier<>(source.getClass(), true).withTarget(source);
List<?> list = (List<?>) modifier.read(0);
Object empty = modifier.read(1);
if (nonNullList == null) {
try {
nonNullList = source.getClass().getDeclaredConstructor(List.class, Object.class);
nonNullList.setAccessible(true);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not find NonNullList constructor", ex);
}
}
try {
return nonNullList.newInstance(new ArrayList<>(list), empty);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not create new NonNullList", ex);
}
}
};
}
}
@@ -20,10 +20,13 @@ package com.comphenix.protocol.reflect.cloning;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.BiMap;
/**
* Attempts to clone collection and array classes.
*
@@ -48,72 +51,89 @@ public class CollectionCloner implements Cloner {
Class<?> clazz = source.getClass();
return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || clazz.isArray();
}
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
Class<?> clazz = source.getClass();
if (source instanceof Collection) {
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
try {
if (source instanceof Collection) {
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
// Next, clone each element in the collection
try {
copy.clear();
for (Object element : (Collection<Object>) source) {
copy.add(getClone(element, source));
// Next, clone each element in the collection
try {
copy.clear();
for (Object element : (Collection<Object>) source) {
copy.add(getClone(element, source));
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
return copy;
} else if (source instanceof Map) {
Map<Object, Object> copy = cloneConstructor(Map.class, clazz, source);
// Next, clone each element in the collection
try {
copy.clear();
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
Object key = getClone(element.getKey(), source);
Object value = getClone(element.getValue(), source);
copy.put(key, value);
return copy;
} else if (source instanceof Map) {
Map<Object, Object> copy;
if (source instanceof BiMap) {
BiMap original = (BiMap) source;
if (clazz.getDeclaringClass() != null) {
Method create = clazz.getDeclaringClass().getMethod("create", int.class);
copy = ((BiMap) create.invoke(null, original.size())).inverse();
} else {
Method create = clazz.getMethod("create", int.class);
copy = (Map) create.invoke(null, original.size());
}
} else {
copy = cloneConstructor(Map.class, clazz, source);
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
// Next, clone each element in the collection
try {
copy.clear();
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
Object key = getClone(element.getKey(), source);
Object value = getClone(element.getValue(), source);
copy.put(key, value);
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
return copy;
} else if (clazz.isArray()) {
// Get the length
int length = Array.getLength(source);
Class<?> component = clazz.getComponentType();
// Can we speed things up by making a shallow copy instead?
if (ImmutableDetector.isImmutable(component)) {
return clonePrimitive(component, source);
}
// Create a new copy
Object copy = Array.newInstance(clazz.getComponentType(), length);
// Set each element
for (int i = 0; i < length; i++) {
Object element = Array.get(source, i);
if (defaultCloner.canClone(element))
Array.set(copy, i, defaultCloner.clone(element));
else
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
}
// And we're done
return copy;
}
return copy;
} else if (clazz.isArray()) {
// Get the length
int lenght = Array.getLength(source);
Class<?> component = clazz.getComponentType();
// Can we speed things up by making a shallow copy instead?
if (ImmutableDetector.isImmutable(component)) {
return clonePrimitive(component, source);
}
// Create a new copy
Object copy = Array.newInstance(clazz.getComponentType(), lenght);
// Set each element
for (int i = 0; i < lenght; i++) {
Object element = Array.get(source, i);
if (defaultCloner.canClone(element))
Array.set(copy, i, defaultCloner.clone(element));
else
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
}
// And we're done
return copy;
} catch (Exception ex) {
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ")", ex);
}
throw new IllegalArgumentException(source + " is not an array nor a Collection.");
@@ -171,7 +191,7 @@ public class CollectionCloner implements Cloner {
private <T> T cloneConstructor(Class<?> superclass, Class<?> clazz, Object source) {
// Not all collections or maps implement "clone", but most *do* implement the "copy constructor" pattern
try {
Constructor<?> constructCopy = clazz.getConstructor(Collection.class);
Constructor<?> constructCopy = clazz.getConstructor(superclass);
return (T) constructCopy.newInstance(source);
} catch (NoSuchMethodException e) {
if (source instanceof Serializable)
@@ -186,7 +206,7 @@ public class CollectionCloner implements Cloner {
/**
* Clone an object by calling "clone" using reflection.
* @param clazz - the class type.
* @param obj - the object to clone.
* @param source - the object to clone.
* @return The cloned object.
*/
private Object cloneObject(Class<?> clazz, Object source) {
@@ -1,39 +1,39 @@
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import com.google.common.base.Optional;
/**
* A cloner that can clone Optional objects
* @author dmulloy2
*/
public class OptionalCloner implements Cloner {
protected Cloner wrapped;
public OptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional;
}
@Override
public Object clone(Object source) {
Optional<?> optional = (Optional<?>) source;
if (!optional.isPresent()) {
return Optional.absent();
}
// Clone the inner value
return Optional.of(wrapped.clone(optional.get()));
}
public Cloner getWrapped() {
return wrapped;
}
}
/**
* (c) 2016 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import com.google.common.base.Optional;
/**
* A cloner that can clone Guava Optional objects
* @author dmulloy2
*/
public class GuavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public GuavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional;
}
@Override
public Object clone(Object source) {
Optional<?> optional = (Optional<?>) source;
if (!optional.isPresent()) {
return Optional.absent();
}
// Clone the inner value
return Optional.of(wrapped.clone(optional.get()));
}
public Cloner getWrapped() {
return wrapped;
}
}
@@ -26,11 +26,16 @@ import java.net.URI;
import java.net.URL;
import java.security.PublicKey;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.crypto.SecretKey;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;
/**
@@ -42,19 +47,45 @@ import com.google.common.primitives.Primitives;
*/
public class ImmutableDetector implements Cloner {
// Notable immutable classes we might encounter
private static final Class<?>[] immutableClasses = {
StackTraceElement.class, BigDecimal.class,
BigInteger.class, Locale.class, UUID.class,
URL.class, URI.class, Inet4Address.class,
Inet6Address.class, InetSocketAddress.class,
SecretKey.class, PublicKey.class
};
private static final Set<Class<?>> immutableClasses = ImmutableSet.of(
StackTraceElement.class, BigDecimal.class,
BigInteger.class, Locale.class, UUID.class,
URL.class, URI.class, Inet4Address.class,
Inet6Address.class, InetSocketAddress.class,
SecretKey.class, PublicKey.class
);
private static final Set<Class<?>> immutableNMS = Sets.newConcurrentHashSet();
static {
add(MinecraftReflection::getGameProfileClass);
add(MinecraftReflection::getDataWatcherSerializerClass);
add(() -> MinecraftReflection.getMinecraftClass("SoundEffect"));
add(MinecraftReflection::getBlockClass);
add(MinecraftReflection::getItemClass);
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
add(() -> MinecraftReflection.getMinecraftClass("Particle"));
add(MinecraftReflection::getFluidTypeClass);
add(MinecraftReflection::getParticleTypeClass);
}
}
private static void add(Supplier<Class<?>> getClass) {
try {
Class<?> clazz = getClass.get();
if (clazz != null) {
immutableNMS.add(clazz);
}
} catch (RuntimeException ignored) { }
}
@Override
public boolean canClone(Object source) {
// Don't accept NULL
if (source == null)
if (source == null) {
return false;
}
return isImmutable(source.getClass());
}
@@ -66,32 +97,31 @@ public class ImmutableDetector implements Cloner {
*/
public static boolean isImmutable(Class<?> type) {
// Cases that are definitely not true
if (type.isArray())
if (type.isArray()) {
return false;
}
// All primitive types
if (Primitives.isWrapperType(type) || String.class.equals(type))
if (Primitives.isWrapperType(type) || String.class.equals(type)) {
return true;
// May not be true, but if so, that kind of code is broken anyways
if (isEnumWorkaround(type))
return true;
for (Class<?> clazz : immutableClasses)
if (clazz.equals(type))
return true;
// Check for known immutable classes in 1.7.2
if (MinecraftReflection.isUsingNetty()) {
if (type.equals(MinecraftReflection.getGameProfileClass())) {
return true;
}
}
// Check for known immutable classes in 1.9
if (MinecraftReflection.watcherObjectExists()) {
if (type.equals(MinecraftReflection.getDataWatcherSerializerClass())
|| type.equals(MinecraftReflection.getMinecraftClass("SoundEffect"))) {
// May not be true, but if so, that kind of code is broken anyways
if (isEnumWorkaround(type)) {
return true;
}
// No good way to clone lambdas
if (type.getName().contains("$$Lambda$")) {
return true;
}
if (immutableClasses.contains(type)) {
return true;
}
for (Class<?> clazz : immutableNMS) {
if (MinecraftReflection.is(clazz, type)) {
return true;
}
}
@@ -103,10 +133,13 @@ public class ImmutableDetector implements Cloner {
// This is just great. Just great.
private static boolean isEnumWorkaround(Class<?> enumClass) {
while (enumClass != null) {
if (enumClass.isEnum())
if (enumClass.isEnum()) {
return true;
}
enumClass = enumClass.getSuperclass();
}
return false;
}
@@ -0,0 +1,33 @@
/**
* (c) 2018 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Optional;
/**
* A cloner that can clone Java Optional objects
* @author dmulloy2
*/
public class JavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public JavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional;
}
@Override
public Object clone(Object source) {
Optional<?> optional = (Optional<?>) source;
return optional.map(o -> wrapped.clone(o));
}
public Cloner getWrapped() {
return wrapped;
}
}
@@ -17,25 +17,25 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
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 $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()");
@@ -44,9 +44,9 @@ class BoxingHelper {
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
private MethodVisitor mv;
private $MethodVisitor mv;
public BoxingHelper(MethodVisitor mv) {
public BoxingHelper($MethodVisitor mv) {
this.mv = mv;
}
@@ -54,42 +54,42 @@ class BoxingHelper {
* 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.
* @param type the $Type of the top stack value.
*/
public void box(final Type type){
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
public void box(final $Type type){
if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
return;
}
if(type == Type.VOID_TYPE) {
if(type == $Type.VOID_TYPE) {
push((String) null);
} else {
Type boxed = type;
$Type boxed = type;
switch(type.getSort()) {
case Type.BYTE:
boxed = BYTE_TYPE;
case $Type.BYTE:
boxed = BYTE_$Type;
break;
case Type.BOOLEAN:
boxed = BOOLEAN_TYPE;
case $Type.BOOLEAN:
boxed = BOOLEAN_$Type;
break;
case Type.SHORT:
boxed = SHORT_TYPE;
case $Type.SHORT:
boxed = SHORT_$Type;
break;
case Type.CHAR:
boxed = CHARACTER_TYPE;
case $Type.CHAR:
boxed = CHARACTER_$Type;
break;
case Type.INT:
boxed = INTEGER_TYPE;
case $Type.INT:
boxed = INTEGER_$Type;
break;
case Type.FLOAT:
boxed = FLOAT_TYPE;
case $Type.FLOAT:
boxed = FLOAT_$Type;
break;
case Type.LONG:
boxed = LONG_TYPE;
case $Type.LONG:
boxed = LONG_$Type;
break;
case Type.DOUBLE:
boxed = DOUBLE_TYPE;
case $Type.DOUBLE:
boxed = DOUBLE_$Type;
break;
}
@@ -105,46 +105,46 @@ class BoxingHelper {
swap();
}
invokeConstructor(boxed, new MethodDescriptor("<init>", Type.VOID_TYPE, new Type[] {type}));
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 $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);
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);
mv.visitInsn($Opcodes.DUP_X1);
}
/**
* Generates a DUP_X2 instruction.
*/
public void dupX2(){
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn($Opcodes.DUP_X2);
}
/**
* Generates a POP instruction.
*/
public void pop(){
mv.visitInsn(Opcodes.POP);
mv.visitInsn($Opcodes.POP);
}
/**
* Generates a SWAP instruction.
*/
public void swap(){
mv.visitInsn(Opcodes.SWAP);
mv.visitInsn($Opcodes.SWAP);
}
/**
@@ -163,11 +163,11 @@ class BoxingHelper {
*/
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
mv.visitInsn($Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
mv.visitIntInsn($Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
mv.visitIntInsn($Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
@@ -176,10 +176,10 @@ class BoxingHelper {
/**
* Generates the instruction to create a new object.
*
* @param type the class of the object to be created.
* @param $Type the class of the object to be created.
*/
public void newInstance(final Type type){
typeInsn(Opcodes.NEW, type);
public void newInstance(final $Type $Type){
$TypeInsn($Opcodes.NEW, $Type);
}
/**
@@ -189,7 +189,7 @@ class BoxingHelper {
*/
public void push(final String value) {
if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn($Opcodes.ACONST_NULL);
} else {
mv.visitLdcInsn(value);
}
@@ -200,35 +200,35 @@ class BoxingHelper {
* replaced by its unboxed equivalent on top of the stack.
*
* @param type
* the type of the top stack value.
* the $Type of the top stack value.
*/
public void unbox(final Type type){
Type t = NUMBER_TYPE;
public void unbox(final $Type type){
$Type t = NUMBER_$Type;
MethodDescriptor sig = null;
switch(type.getSort()) {
case Type.VOID:
case $Type.VOID:
return;
case Type.CHAR:
t = CHARACTER_TYPE;
case $Type.CHAR:
t = CHARACTER_$Type;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = BOOLEAN_TYPE;
case $Type.BOOLEAN:
t = BOOLEAN_$Type;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
case $Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
case $Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
case $Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
case $Type.INT:
case $Type.SHORT:
case $Type.BYTE:
sig = INT_VALUE;
}
@@ -242,13 +242,13 @@ class BoxingHelper {
/**
* Generates the instruction to check that the top stack value is of the
* given type.
* given $Type.
*
* @param type a class or interface type.
* @param $Type a class or interface $Type.
*/
public void checkCast(final Type type){
if(!type.equals(OBJECT_TYPE)) {
typeInsn(Opcodes.CHECKCAST, type);
public void checkCast(final $Type $Type){
if(!$Type.equals(OBJECT_$Type)) {
$TypeInsn($Opcodes.CHECKCAST, $Type);
}
}
@@ -258,35 +258,35 @@ class BoxingHelper {
* @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);
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 $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();
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.
* Generates a $Type dependent instruction.
*
* @param opcode the instruction's opcode.
* @param type the instruction's operand.
* @param $Type the instruction's operand.
*/
private void typeInsn(final int opcode, final Type type){
private void $TypeInsn(final int opcode, final $Type $Type){
String desc;
if(type.getSort() == Type.ARRAY) {
desc = type.getDescriptor();
if($Type.getSort() == $Type.ARRAY) {
desc = $Type.getDescriptor();
} else {
desc = type.getInternalName();
desc = $Type.getInternalName();
}
mv.visitTypeInsn(opcode, desc);
@@ -110,7 +110,7 @@ public abstract class CompiledStructureModifier extends StructureModifier<Object
@Override
public StructureModifier<Object> write(int index, Object value) throws FieldAccessException {
if (converter != null)
value = converter.getGeneric(getFieldType(index), value);
value = converter.getGeneric(value);
return writeGenerated(index, value);
}
@@ -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
}
}
@@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler;
import java.util.HashMap;
import java.util.Map;
import net.sf.cglib.asm.Type;
import net.sf.cglib.asm.$Type;
/**
* Represents a method.
@@ -56,7 +56,7 @@ class MethodDescriptor {
}
/**
* Creates a new {@link Method}.
* Creates a new {@link MethodDescriptor}.
*
* @param name the method's name.
* @param desc the method's descriptor.
@@ -67,7 +67,7 @@ class MethodDescriptor {
}
/**
* Creates a new {@link Method}.
* Creates a new {@link MethodDescriptor}.
*
* @param name the method's name.
* @param returnType the method's return type.
@@ -75,14 +75,14 @@ class MethodDescriptor {
*/
public MethodDescriptor(
final String name,
final Type returnType,
final Type[] argumentTypes)
final $Type returnType,
final $Type[] argumentTypes)
{
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
this(name, $Type.getMethodDescriptor(returnType, argumentTypes));
}
/**
* Returns a {@link Method} corresponding to the given Java method
* Returns a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
@@ -91,7 +91,7 @@ class MethodDescriptor {
* "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
* @return a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
@@ -103,7 +103,7 @@ class MethodDescriptor {
}
/**
* Returns a {@link Method} corresponding to the given Java method
* Returns a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
@@ -117,7 +117,7 @@ class MethodDescriptor {
* 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
* @return a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
@@ -134,7 +134,7 @@ class MethodDescriptor {
}
String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim();
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
sb.append('(');
int p;
do {
@@ -158,7 +158,7 @@ class MethodDescriptor {
return type;
}
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('[');
@@ -206,8 +206,8 @@ class MethodDescriptor {
*
* @return the return type of the method described by this object.
*/
public Type getReturnType() {
return Type.getReturnType(desc);
public $Type getReturnType() {
return $Type.getReturnType(desc);
}
/**
@@ -215,8 +215,8 @@ class MethodDescriptor {
*
* @return the argument types of the method described by this object.
*/
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(desc);
public $Type[] getArgumentTypes() {
return $Type.getArgumentTypes(desc);
}
public String toString() {
@@ -25,12 +25,12 @@ 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 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;
@@ -95,22 +95,22 @@ import com.google.common.primitives.Primitives;
/**
* 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;
@@ -120,7 +120,7 @@ public final class StructureCompiler {
public int hashCode() {
return Objects.hashCode(targetType, fieldType);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof StructureKey) {
@@ -131,16 +131,16 @@ public final class StructureCompiler {
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";
@@ -156,7 +156,7 @@ public final class StructureCompiler {
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
@@ -194,7 +194,7 @@ public final class StructureCompiler {
// We need to compile the class
return false;
}
/**
* Compiles the given structure modifier.
* <p>
@@ -211,15 +211,15 @@ public final class StructureCompiler {
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(
@@ -245,7 +245,7 @@ public final class StructureCompiler {
throw new IllegalStateException("Cannot happen.", e);
}
}
/**
* Retrieve a variable identifier that can uniquely represent the given type.
* @param type - a type.
@@ -254,7 +254,7 @@ public final class StructureCompiler {
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.
@@ -262,29 +262,29 @@ public final class StructureCompiler {
*/
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);
$ClassWriter cw = new $ClassWriter(0);
Class<?> targetType = source.getTargetType();
String className = getCompiledName(source);
String targetSignature = Type.getDescriptor(targetType);
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,
cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
null, COMPILED_CLASS, null);
createFields(cw, targetSignature);
@@ -292,29 +292,29 @@ public final class StructureCompiler {
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) {
@@ -327,7 +327,7 @@ public final class StructureCompiler {
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.
@@ -340,208 +340,208 @@ public final class StructureCompiler {
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);
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) {
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,
$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
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 error$Label = new $Label();
$Label return$Label = new $Label();
// Generate $Labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
$Labels[i] = new $Label();
}
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);
mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $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 typeDescriptor = $Type.getDescriptor(outputType);
String inputPath = inputType.getName().replace('.', '/');
mv.visitLabel(labels[i]);
mv.visitLabel($Labels[i]);
// Push the compare object
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
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);
mv.visitVarInsn($Opcodes.ALOAD, 3);
mv.visitVarInsn($Opcodes.ALOAD, 2);
if (!outputType.isPrimitive())
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath);
mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
else
boxingHelper.unbox(Type.getType(outputType));
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
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.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.visitJumpInsn($Opcodes.GOTO, return$Label);
}
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.visitLabel(error$Label);
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.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(return$Label);
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,
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
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 error$Label = new $Label();
// Generate $Labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
$Labels[i] = new $Label();
}
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, labels);
mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $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]);
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);
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
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));
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.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
}
mv.visitInsn(Opcodes.ARETURN);
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.visitLabel(error$Label);
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.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>",
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.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();
}
@@ -55,4 +55,4 @@ class ClassRegexMatcher extends AbstractFuzzyMatcher<Class<?>> {
}
return false;
}
}
}
@@ -54,4 +54,4 @@ class ClassSetMatcher extends AbstractFuzzyMatcher<Class<?>> {
}
return true;
}
}
}
@@ -65,4 +65,4 @@ public class CollectionGenerator implements InstanceProvider {
// Cannot provide an instance
return null;
}
}
}
@@ -32,4 +32,4 @@ public interface InstanceProvider {
* @throws NotConstructableException Thrown to indicate that this type cannot or should never be constructed.
*/
public abstract Object create(@Nullable Class<?> type);
}
}
@@ -77,4 +77,4 @@ public class PrimitiveGenerator implements InstanceProvider {
// Cannot handle this type
return null;
}
}
}
@@ -122,4 +122,4 @@ public class HistogramStream extends OnlineComputation {
public int getCount() {
return count;
}
}
}
@@ -46,4 +46,4 @@ public abstract class OnlineComputation {
}
};
}
}
}
@@ -152,4 +152,4 @@ public class StatisticsStream extends OnlineComputation {
getMean(), getStandardDeviation(),
getMinimum(), getMaximum(), getCount());
}
}
}
@@ -1,23 +1,23 @@
/*
/**
* 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
* 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.
* 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
* 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 java.util.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@@ -29,10 +29,10 @@ import com.google.common.collect.Maps;
* @author Kristian
*/
class CachedPackage {
private final Map<String, Class<?>> cache;
private final Map<String, Optional<Class<?>>> cache;
private final String packageName;
private final ClassSource source;
/**
* Construct a new cached package.
* @param packageName - the name of the current package.
@@ -43,42 +43,38 @@ class CachedPackage {
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);
if (clazz != null) {
cache.put(className, Optional.of(clazz));
} else {
cache.remove(className);
}
}
/**
* 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);
public Optional<Class<?>> getPackageClass(final String className) {
Preconditions.checkNotNull(className, "className cannot be null!");
cache.put(className, result);
return cache.computeIfAbsent(className, x -> {
try {
return Optional.ofNullable(source.loadClass(combine(packageName, className)));
} catch (ClassNotFoundException ex) {
return Optional.empty();
}
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.
@@ -1,57 +1,57 @@
/**
* 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.io.Closeable;
import java.util.ArrayList;
import java.util.List;
/**
* @author dmulloy2
*/
// TODO Switch to AutoCloseable w/ Java 7
public class Closer implements Closeable {
private final List<Closeable> list;
private Closer() {
this.list = new ArrayList<Closeable>();
}
public static Closer create() {
return new Closer();
}
public <C extends Closeable> C register(C close) {
list.add(close);
return close;
}
@Override
public void close() {
for (Closeable close : list) {
closeQuietly(close);
}
}
public static void closeQuietly(Closeable close) {
try {
close.close();
} catch (Throwable ex) { }
}
}
/**
* 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.io.Closeable;
import java.util.ArrayList;
import java.util.List;
/**
* @author dmulloy2
*/
// TODO Switch to AutoCloseable w/ Java 7
public class Closer implements Closeable {
private final List<Closeable> list;
private Closer() {
this.list = new ArrayList<Closeable>();
}
public static Closer create() {
return new Closer();
}
public <C extends Closeable> C register(C close) {
list.add(close);
return close;
}
@Override
public void close() {
for (Closeable close : list) {
closeQuietly(close);
}
}
public static void closeQuietly(Closeable close) {
try {
close.close();
} catch (Throwable ex) { }
}
}
@@ -1,27 +1,27 @@
/**
* 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;
}
/**
* 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_13_R2";
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
}
@@ -47,6 +47,7 @@ public class MinecraftFields {
* @return The player connection.
*/
public static Object getPlayerConnection(Player player) {
Preconditions.checkNotNull(player, "player cannot be null!");
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
}
@@ -1,21 +1,21 @@
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;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.GenericFutureListener;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**
* Static methods for accessing Minecraft methods.
@@ -43,7 +43,13 @@ public class MinecraftMethods {
Class<?> serverHandlerClass = MinecraftReflection.getPlayerConnectionClass();
try {
sendPacketMethod = FuzzyReflection.fromClass(serverHandlerClass).getMethodByName("sendPacket.*");
sendPacketMethod = FuzzyReflection
.fromClass(serverHandlerClass)
.getMethod(FuzzyMethodContract.newBuilder()
.nameRegex("sendPacket.*")
.returnTypeVoid()
.parameterCount(1)
.build());
} catch (IllegalArgumentException e) {
// We can't use the method below on Netty
if (MinecraftReflection.isUsingNetty()) {
@@ -96,10 +102,12 @@ public class MinecraftMethods {
*/
public static Method getNetworkManagerHandleMethod() {
if (networkManagerHandle == null) {
networkManagerHandle = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
getMethodByParameters("handle", MinecraftReflection.getPacketClass(), GenericFutureListener[].class);
networkManagerHandle = FuzzyReflection
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
.getMethodByParameters("handle", MinecraftReflection.getPacketClass());
networkManagerHandle.setAccessible(true);
}
return networkManagerHandle;
}
@@ -163,15 +171,16 @@ public class MinecraftMethods {
// 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);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
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
@@ -4,7 +4,6 @@ import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.Maps;
/**
@@ -42,6 +41,14 @@ public class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 9, 0), 107);
map.put(new MinecraftVersion(1, 9, 2), 109);
map.put(new MinecraftVersion(1, 9, 4), 110);
map.put(new MinecraftVersion(1, 10, 0), 210);
map.put(new MinecraftVersion(1, 11, 0), 315);
map.put(new MinecraftVersion(1, 11, 1), 316);
map.put(new MinecraftVersion(1, 12, 0), 335);
map.put(new MinecraftVersion(1, 12, 1), 338);
map.put(new MinecraftVersion(1, 12, 2), 340);
map.put(new MinecraftVersion(1, 13, 0), 393);
map.put(new MinecraftVersion(1, 13, 1), 401);
return map;
}
@@ -50,7 +57,7 @@ public class MinecraftProtocolVersion {
* @return The version number.
*/
public static int getCurrentVersion() {
return getVersion(ProtocolLibrary.getProtocolManager().getMinecraftVersion());
return getVersion(MinecraftVersion.getCurrentVersion());
}
/**
File diff suppressed because it is too large. Load diff
@@ -44,6 +44,26 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
*/
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)");
/**
* Version 1.13 - update aquatic.
*/
public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
/**
* Version 1.12 - the world of color update.
*/
public static final MinecraftVersion COLOR_UPDATE = new MinecraftVersion("1.12");
/**
* Version 1.11 - the exploration update.
*/
public static final MinecraftVersion EXPLORATION_UPDATE = new MinecraftVersion("1.11");
/**
* Version 1.10 - the frostburn update.
*/
public static final MinecraftVersion FROSTBURN_UPDATE = new MinecraftVersion("1.10");
/**
* Version 1.9 - the combat update.
*/
@@ -336,4 +356,8 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
return currentVersion;
}
public static boolean atOrAbove(MinecraftVersion version) {
return getCurrentVersion().isAtLeast(version);
}
}
@@ -28,7 +28,7 @@ import org.bukkit.Server;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavaibleException.Reason;
import com.comphenix.protocol.utility.RemappedClassSource.RemapperUnavailableException.Reason;
class RemappedClassSource extends ClassSource {
private Object classRemapper;
@@ -53,7 +53,7 @@ class RemappedClassSource extends ClassSource {
/**
* Attempt to load the MCPC remapper.
* @return TRUE if we succeeded, FALSE otherwise.
* @throws RemapperUnavaibleException If the remapper is not present.
* @throws RemapperUnavailableException If the remapper is not present.
*/
public RemappedClassSource initialize() {
try {
@@ -64,14 +64,14 @@ class RemappedClassSource extends ClassSource {
String version = server.getVersion();
if (!version.contains("MCPC") && !version.contains("Cauldron")) {
throw new RemapperUnavaibleException(Reason.MCPC_NOT_PRESENT);
throw new RemapperUnavailableException(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);
throw new RemapperUnavailableException(Reason.REMAPPER_DISABLED);
}
// Initialize some fields and methods used by the Jar Remapper
@@ -82,7 +82,7 @@ class RemappedClassSource extends ClassSource {
return this;
} catch (RemapperUnavaibleException e) {
} catch (RemapperUnavailableException e) {
throw e;
} catch (Exception e) {
// Damn it
@@ -115,7 +115,7 @@ class RemappedClassSource extends ClassSource {
}
}
public static class RemapperUnavaibleException extends RuntimeException {
public static class RemapperUnavailableException extends RuntimeException {
private static final long serialVersionUID = 1L;
public enum Reason {
@@ -139,7 +139,7 @@ class RemappedClassSource extends ClassSource {
private final Reason reason;
public RemapperUnavaibleException(Reason reason) {
public RemapperUnavailableException(Reason reason) {
super(reason.getMessage());
this.reason = reason;
}
@@ -468,4 +468,4 @@ public class StreamSerializer {
else
return null;
}
}
}
@@ -1,63 +1,63 @@
/**
* 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");
}
}
/**
* 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 or a Spigot fork. This works by checking
* the server version for the Strings "Spigot" or "Paper".
* @return True if it is, false if not.
*/
public static boolean isUsingSpigot() {
return Bukkit.getServer().getVersion().contains("Spigot") || Bukkit.getServer().getVersion().contains("Paper");
}
}
@@ -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();
}
};
}
}
@@ -47,4 +47,26 @@ public abstract class AbstractWrapper {
public Class<?> getHandleType() {
return handleType;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof AbstractWrapper) {
AbstractWrapper that = (AbstractWrapper) obj;
return this.handle.equals(that.handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[handle=" + handle + "]";
}
}
@@ -0,0 +1,175 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* 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.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
* Requirements:
* <ul>
* <li>The wrapper must be public</li>
* <li>If the wrapper is an internal class, it must be static</li>
* <li>The wrapper must have one public constructor with no arguments (the default constructor is acceptable)</li>
* <li>The wrapper must have the the same number of fields as the NMS class</li>
* <li>Each field should correspond, in order, to its NMS counterpart</li>
* <li>Non-generic fields must have a converter</li>
* </ul>
*
* @author dmulloy2
*/
public class AutoWrapper<T> implements EquivalentConverter<T> {
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
private Class<T> wrapperClass;
private Class<?> nmsClass;
private AutoWrapper(Class<T> wrapperClass, Class<?> nmsClass) {
this.wrapperClass = wrapperClass;
this.nmsClass = nmsClass;
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, Class<?> nmsClass) {
return new AutoWrapper<>(wrapperClass, nmsClass);
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
}
public AutoWrapper<T> field(int index, Function<Object, Object> wrapper, Function<Object, Object> unwrapper) {
wrappers.put(index, wrapper);
unwrappers.put(index, unwrapper);
return this;
}
public AutoWrapper<T> field(int index, EquivalentConverter converter) {
return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
}
public T wrap(Object nmsObject) {
T instance;
try {
instance = wrapperClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
}
Field[] wrapperFields = wrapperClass.getDeclaredFields();
Field[] nmsFields = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.toArray(Field[]::new);
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
Field nmsField = nmsFields[i];
if (!nmsField.isAccessible())
nmsField.setAccessible(true);
Object value = nmsField.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
wrapperField.set(instance, value);
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to wrap field", ex);
}
}
return instance;
}
public Object unwrap(Object wrapper) {
Object instance;
try {
instance = nmsClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to construct new " + nmsClass.getSimpleName(), ex);
}
Field[] wrapperFields = wrapperClass.getDeclaredFields();
Field[] nmsFields = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.toArray(Field[]::new);
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
Field nmsField = nmsFields[i];
if (!nmsField.isAccessible())
nmsField.setAccessible(true);
if (Modifier.isFinal(nmsField.getModifiers()))
unsetFinal(nmsField);
Object value = wrapperField.get(wrapper);
if (unwrappers.containsKey(i))
value = unwrappers.get(i).apply(value);
nmsField.set(instance, value);
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to unwrap field", ex);
}
}
return instance;
}
private void unsetFinal(Field field) throws ReflectiveOperationException {
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
// ---- Equivalent conversion
@Override
public T getSpecific(Object generic) {
return wrap(generic);
}
@Override
public Object getGeneric(Object specific) {
return unwrap(specific);
}
@Override
public Class<T> getSpecificType() {
return wrapperClass;
}
public static class InvalidWrapperException extends RuntimeException {
private InvalidWrapperException(String message, Throwable cause) {
super(message, cause);
}
}
}
@@ -1,244 +1,244 @@
/**
* 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 org.bukkit.Location;
import org.bukkit.World;
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.BlockPosition, which represents a integer 3D vector.
*
* @author dmulloy2
*/
public class BlockPosition {
/**
* Represents the null (0, 0, 0) origin.
*/
public static BlockPosition ORIGIN = new BlockPosition(0, 0, 0);
private static Constructor<?> blockPositionConstructor;
// Use protected members, like Bukkit
protected final int x;
protected final int y;
protected final int z;
// Used to access a BlockPosition, 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 BlockPosition(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 BlockPosition(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);
}
/**
* Convert this instance to an equivalent Location.
* @param world World for the location
* @return Location
*/
public Location toLocation(World world) {
return new Location(world, 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 BlockPosition add(BlockPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new BlockPosition(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 BlockPosition subtract(BlockPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new BlockPosition(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 BlockPosition multiply(int factor) {
return new BlockPosition(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 BlockPosition divide(int divisor) {
if (divisor == 0)
throw new IllegalArgumentException("Cannot divide by null.");
return new BlockPosition(x / divisor, y / divisor, z / divisor);
}
/**
* Used to convert between NMS ChunkPosition and the wrapper instance.
* @return A new converter.
*/
public static EquivalentConverter<BlockPosition> getConverter() {
return new EquivalentConverter<BlockPosition>() {
@Override
public Object getGeneric(Class<?> genericType, BlockPosition specific) {
if (blockPositionConstructor == null) {
try {
blockPositionConstructor = MinecraftReflection.getBlockPositionClass().
getConstructor(int.class, int.class, int.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find block position constructor.", e);
}
}
// Construct the underlying BlockPosition
try {
Object result = blockPositionConstructor.newInstance(specific.x, specific.y, specific.z);
return result;
} catch (Exception e) {
throw new RuntimeException("Cannot construct BlockPosition.", e);
}
}
@Override
public BlockPosition getSpecific(Object generic) {
if (MinecraftReflection.isBlockPosition(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);
BlockPosition result = new BlockPosition(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<BlockPosition> getSpecificType() {
return BlockPosition.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 BlockPosition) {
BlockPosition other = (BlockPosition) 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 "BlockPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
}
}
/**
* 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 org.bukkit.Location;
import org.bukkit.World;
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.BlockPosition, which represents a integer 3D vector.
*
* @author dmulloy2
*/
public class BlockPosition {
/**
* Represents the null (0, 0, 0) origin.
*/
public static BlockPosition ORIGIN = new BlockPosition(0, 0, 0);
private static Constructor<?> blockPositionConstructor;
// Use protected members, like Bukkit
protected final int x;
protected final int y;
protected final int z;
// Used to access a BlockPosition, 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 BlockPosition(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 BlockPosition(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);
}
/**
* Convert this instance to an equivalent Location.
* @param world World for the location
* @return Location
*/
public Location toLocation(World world) {
return new Location(world, 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 BlockPosition add(BlockPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new BlockPosition(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 BlockPosition subtract(BlockPosition other) {
if (other == null)
throw new IllegalArgumentException("other cannot be NULL");
return new BlockPosition(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 BlockPosition multiply(int factor) {
return new BlockPosition(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 BlockPosition divide(int divisor) {
if (divisor == 0)
throw new IllegalArgumentException("Cannot divide by null.");
return new BlockPosition(x / divisor, y / divisor, z / divisor);
}
/**
* Used to convert between NMS ChunkPosition and the wrapper instance.
* @return A new converter.
*/
public static EquivalentConverter<BlockPosition> getConverter() {
return new EquivalentConverter<BlockPosition>() {
@Override
public Object getGeneric(BlockPosition specific) {
if (blockPositionConstructor == null) {
try {
blockPositionConstructor = MinecraftReflection.getBlockPositionClass().
getConstructor(int.class, int.class, int.class);
} catch (Exception e) {
throw new RuntimeException("Cannot find block position constructor.", e);
}
}
// Construct the underlying BlockPosition
try {
Object result = blockPositionConstructor.newInstance(specific.x, specific.y, specific.z);
return result;
} catch (Exception e) {
throw new RuntimeException("Cannot construct BlockPosition.", e);
}
}
@Override
public BlockPosition getSpecific(Object generic) {
if (MinecraftReflection.isBlockPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<>(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);
BlockPosition result = new BlockPosition(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<BlockPosition> getSpecificType() {
return BlockPosition.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 BlockPosition) {
BlockPosition other = (BlockPosition) 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 "BlockPosition [x=" + x + ", y=" + y + ", z=" + z + "]";
}
}
File diff suppressed because it is too large. Load diff
@@ -67,7 +67,7 @@ public class ChunkCoordIntPair {
public static EquivalentConverter<ChunkCoordIntPair> getConverter() {
return new EquivalentConverter<ChunkCoordIntPair>() {
@Override
public Object getGeneric(Class<?> genericType, ChunkCoordIntPair specific) {
public Object getGeneric(ChunkCoordIntPair specific) {
if (COORD_CONSTRUCTOR == null) {
COORD_CONSTRUCTOR = Accessors.getConstructorAccessor(COORD_PAIR_CLASS, int.class, int.class);
}
@@ -154,7 +154,7 @@ public class ChunkPosition {
public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() {
@Override
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
public Object getGeneric(ChunkPosition specific) {
if (chunkPositionConstructor == null) {
try {
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
@@ -177,7 +177,7 @@ public class ChunkPosition {
public ChunkPosition getSpecific(Object generic) {
if (MinecraftReflection.isChunkPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
// Damn it all
if (intModifier.size() < 3) {
@@ -0,0 +1,7 @@
package com.comphenix.protocol.wrappers;
public interface ClonableWrapper {
Object getHandle();
ClonableWrapper deepClone();
}
@@ -1,51 +1,50 @@
/**
* 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 net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
/**
* Utility class for converting between the BungeeCord Chat API and ProtocolLib's wrapper
* <p>
* Note: The BungeeCord Chat API is not included in CraftBukkit.
* @author dmulloy2
*/
public final class ComponentConverter {
private ComponentConverter() {
}
/**
* Converts a {@link WrappedChatComponent} into an array of {@link BaseComponent}s
* @param wrapper ProtocolLib wrapper
* @return BaseComponent array
*/
public static BaseComponent[] fromWrapper(WrappedChatComponent wrapper) {
return ComponentSerializer.parse(wrapper.getJson());
}
/**
* Converts an array of {@link BaseComponent}s into a ProtocolLib wrapper
* @param components BaseComponent array
* @return ProtocolLib wrapper
*/
public static WrappedChatComponent fromBaseComponent(BaseComponent... components) {
return WrappedChatComponent.fromJson(ComponentSerializer.toString(components));
}
}
/**
* 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 net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
/**
* Utility class for converting between the BungeeCord Chat API and ProtocolLib's wrapper
* <p>
* Note: The BungeeCord Chat API is not included in CraftBukkit.
* @author dmulloy2
*/
public final class ComponentConverter {
private ComponentConverter() {
}
/**
* Converts a {@link WrappedChatComponent} into an array of {@link BaseComponent}s
* @param wrapper ProtocolLib wrapper
* @return BaseComponent array
*/
public static BaseComponent[] fromWrapper(WrappedChatComponent wrapper) {
return ComponentSerializer.parse(wrapper.getJson());
}
/**
* Converts an array of {@link BaseComponent}s into a ProtocolLib wrapper
* @param components BaseComponent array
* @return ProtocolLib wrapper
*/
public static WrappedChatComponent fromBaseComponent(BaseComponent... components) {
return WrappedChatComponent.fromJson(ComponentSerializer.toString(components));
}
}
@@ -0,0 +1,74 @@
/**
* 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 java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private static Constructor readerConstructor;
private static Method setLenient;
private static Method getAdapter;
private static Method read;
private ComponentParser() {
}
public static Object deserialize(Object gson, Class<?> component, StringReader str) {
try {
com.google.gson.stream.JsonReader reader = new com.google.gson.stream.JsonReader(str);
reader.setLenient(true);
return ((com.google.gson.Gson) gson).getAdapter(component).read(reader);
} catch (IOException ex) {
throw new RuntimeException("Failed to read JSON", ex);
} catch (LinkageError er) {
return deserializeLegacy(gson, component, str);
}
}
// Should only be needed on 1.8.
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
try {
if(readerConstructor == null){
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
readerConstructor.setAccessible(true);
setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
setLenient.setAccessible(true);
getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
getAdapter.setAccessible(true);
Object adapter = getAdapter.invoke(gson, component);
read = adapter.getClass().getDeclaredMethod("read", readerClass);
read.setAccessible(true);
}
Object reader = readerConstructor.newInstance(str);
setLenient.invoke(reader, true);
Object adapter = getAdapter.invoke(gson, component);
return read.invoke(adapter, reader);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read JSON", ex);
}
}
}
@@ -0,0 +1,106 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* 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.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
/**
* Utility class for converters
* @author dmulloy2
*/
public class Converters {
/**
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
* @param converter Underlying converter
* @param <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return generic != null ? converter.getSpecific(generic) : null;
}
@Override
public Object getGeneric(T specific) {
return specific != null ? converter.getGeneric(specific) : null;
}
@Override
public Class<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> getSpecificType() {
return clazz;
}
});
}
/**
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
* @param <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> getSpecificType() {
return null;
}
};
}
}
@@ -1,17 +1,20 @@
package com.comphenix.protocol.wrappers;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.bukkit.GameMode;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
import org.bukkit.EntityEffect;
import org.bukkit.GameMode;
/**
* Represents a generic enum converter.
* @author Kristian
@@ -20,26 +23,26 @@ public abstract class EnumWrappers {
public enum ClientCommand {
PERFORM_RESPAWN,
REQUEST_STATS,
OPEN_INVENTORY_ACHIEVEMENT;
OPEN_INVENTORY_ACHIEVEMENT
}
public enum ChatVisibility {
FULL,
SYSTEM,
HIDDEN;
HIDDEN
}
public enum Difficulty {
PEACEFUL,
EASY,
NORMAL,
HARD;
HARD
}
public enum EntityUseAction {
INTERACT,
ATTACK,
INTERACT_AT;
INTERACT_AT
}
/**
@@ -109,7 +112,7 @@ public abstract class EnumWrappers {
SUCCESSFULLY_LOADED,
DECLINED,
FAILED_DOWNLOAD,
ACCEPTED;
ACCEPTED
}
public enum PlayerInfoAction {
@@ -117,15 +120,16 @@ public abstract class EnumWrappers {
UPDATE_GAME_MODE,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER;
REMOVE_PLAYER
}
public enum TitleAction {
TITLE,
SUBTITLE,
ACTIONBAR,
TIMES,
CLEAR,
RESET;
RESET
}
public enum WorldBorderAction {
@@ -134,13 +138,13 @@ public abstract class EnumWrappers {
SET_CENTER,
INITIALIZE,
SET_WARNING_TIME,
SET_WARNING_BLOCKS;
SET_WARNING_BLOCKS
}
public enum CombatEventType {
ENTER_COMBAT,
END_COMBAT,
ENTITY_DIED;
ENTITY_DIED
}
public enum PlayerDigType {
@@ -150,7 +154,7 @@ public abstract class EnumWrappers {
DROP_ALL_ITEMS,
DROP_ITEM,
RELEASE_USE_ITEM,
SWAP_HELD_ITEMS;
SWAP_HELD_ITEMS
}
public enum PlayerAction {
@@ -162,12 +166,12 @@ public abstract class EnumWrappers {
START_RIDING_JUMP,
STOP_RIDING_JUMP,
OPEN_INVENTORY,
START_FALL_FLYING;
START_FALL_FLYING
}
public enum ScoreboardAction {
CHANGE,
REMOVE;
REMOVE
}
public enum Particle {
@@ -216,17 +220,20 @@ public abstract class EnumWrappers {
DRAGON_BREATH("dragonbreath", 42, false),
END_ROD("endRod", 43, false),
DAMAGE_INDICATOR("damageIndicator", 44, true),
SWEEP_ATTACK("sweepAttack", 45, true);
SWEEP_ATTACK("sweepAttack", 45, true),
FALLING_DUST("fallingdust", 46, false, 1),
TOTEM("totem", 47, false),
SPIT("spit", 48, true);
private static final Map<String, Particle> BY_NAME;
private static final Map<Integer, Particle> BY_ID;
static {
BY_ID = new HashMap<Integer, Particle>();
BY_NAME = new HashMap<String, Particle>();
BY_ID = new HashMap<>();
BY_NAME = new HashMap<>();
for (Particle particle : values()) {
BY_NAME.put(particle.getName().toLowerCase(), particle);
BY_NAME.put(particle.getName().toLowerCase(Locale.ENGLISH), particle);
BY_ID.put(particle.getId(), particle);
}
}
@@ -236,11 +243,11 @@ public abstract class EnumWrappers {
private final boolean longDistance;
private final int dataLength;
private Particle(String name, int id, boolean longDistance) {
Particle(String name, int id, boolean longDistance) {
this(name, id, longDistance, 0);
}
private Particle(String name, int id, boolean longDistance, int dataLength) {
Particle(String name, int id, boolean longDistance, int dataLength) {
this.name = name;
this.id = id;
this.longDistance = longDistance;
@@ -264,7 +271,7 @@ public abstract class EnumWrappers {
}
public static Particle getByName(String name) {
return BY_NAME.get(name.toLowerCase());
return BY_NAME.get(name.toLowerCase(Locale.ENGLISH));
}
public static Particle getById(int id) {
@@ -293,7 +300,8 @@ public abstract class EnumWrappers {
}
private final String key;
private SoundCategory(String key) {
SoundCategory(String key) {
this.key = key;
}
@@ -302,7 +310,7 @@ public abstract class EnumWrappers {
}
public static SoundCategory getByKey(String key) {
return LOOKUP.get(key.toLowerCase());
return LOOKUP.get(key.toLowerCase(Locale.ENGLISH));
}
}
@@ -312,12 +320,31 @@ public abstract class EnumWrappers {
FEET,
LEGS,
CHEST,
HEAD;
HEAD
}
public enum Hand {
MAIN_HAND,
OFF_HAND;
OFF_HAND
}
public enum Direction {
DOWN,
UP,
NORTH,
SOUTH,
WEST,
EAST
}
public enum ChatType {
CHAT,
SYSTEM,
GAME_INFO;
public byte getId() {
return (byte) ordinal();
}
}
private static Class<?> PROTOCOL_CLASS = null;
@@ -338,6 +365,8 @@ public abstract class EnumWrappers {
private static Class<?> SOUND_CATEGORY_CLASS = null;
private static Class<?> ITEM_SLOT_CLASS = null;
private static Class<?> HAND_CLASS = null;
private static Class<?> DIRECTION_CLASS = null;
private static Class<?> CHAT_TYPE_CLASS = null;
private static boolean INITIALIZED = false;
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = Maps.newHashMap();
@@ -373,6 +402,8 @@ public abstract class EnumWrappers {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0);
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
DIRECTION_CLASS = getEnum(PacketType.Play.Client.USE_ITEM.getPacketClass(), 0);
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@@ -392,6 +423,8 @@ public abstract class EnumWrappers {
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
INITIALIZED = true;
}
@@ -399,6 +432,8 @@ public abstract class EnumWrappers {
if (nativeClass != null) {
FROM_NATIVE.put(nativeClass, converter);
FROM_WRAPPER.put(wrapperClass, converter);
} else if (ProtocolLogger.isDebugEnabled()) {
// new ClassNotFoundException(wrapperClass.getSimpleName()).printStackTrace();
}
}
@@ -412,7 +447,11 @@ public abstract class EnumWrappers {
try {
return FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class).get(index).getType();
} catch (Throwable ex) {
return null; // Unsupported in this version
/* if (ProtocolLogger.isDebugEnabled()) {
ex.printStackTrace();
} */
return null;
}
}
@@ -515,105 +554,133 @@ public abstract class EnumWrappers {
return HAND_CLASS;
}
public static Class<?> getDirectionClass() {
initialize();
return DIRECTION_CLASS;
}
public static Class<?> getChatTypeClass() {
initialize();
return CHAT_TYPE_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<Protocol>(Protocol.class);
return new EnumConverter<>(getProtocolClass(), Protocol.class);
}
public static EquivalentConverter<ClientCommand> getClientCommandConverter() {
return new EnumConverter<ClientCommand>(ClientCommand.class);
return new EnumConverter<>(getClientCommandClass(), ClientCommand.class);
}
public static EquivalentConverter<ChatVisibility> getChatVisibilityConverter() {
return new EnumConverter<ChatVisibility>(ChatVisibility.class);
return new EnumConverter<>(getChatVisibilityClass(), ChatVisibility.class);
}
public static EquivalentConverter<Difficulty> getDifficultyConverter() {
return new EnumConverter<Difficulty>(Difficulty.class);
return new EnumConverter<>(getDifficultyClass(), Difficulty.class);
}
public static EquivalentConverter<EntityUseAction> getEntityUseActionConverter() {
return new EnumConverter<EntityUseAction>(EntityUseAction.class);
return new EnumConverter<>(getEntityUseActionClass(), EntityUseAction.class);
}
public static EquivalentConverter<NativeGameMode> getGameModeConverter() {
return new EnumConverter<NativeGameMode>(NativeGameMode.class);
return new EnumConverter<>(getGameModeClass(), NativeGameMode.class);
}
public static EquivalentConverter<ResourcePackStatus> getResourcePackStatusConverter() {
return new EnumConverter<ResourcePackStatus>(ResourcePackStatus.class);
return new EnumConverter<>(getResourcePackStatusClass(), ResourcePackStatus.class);
}
public static EquivalentConverter<PlayerInfoAction> getPlayerInfoActionConverter() {
return new EnumConverter<PlayerInfoAction>(PlayerInfoAction.class);
return new EnumConverter<>(getPlayerInfoActionClass(), PlayerInfoAction.class);
}
public static EquivalentConverter<TitleAction> getTitleActionConverter() {
return new EnumConverter<TitleAction>(TitleAction.class);
return new EnumConverter<>(getTitleActionClass(), TitleAction.class);
}
public static EquivalentConverter<WorldBorderAction> getWorldBorderActionConverter() {
return new EnumConverter<WorldBorderAction>(WorldBorderAction.class);
return new EnumConverter<>(getWorldBorderActionClass(), WorldBorderAction.class);
}
public static EquivalentConverter<CombatEventType> getCombatEventTypeConverter() {
return new EnumConverter<CombatEventType>(CombatEventType.class);
return new EnumConverter<>(getCombatEventTypeClass(), CombatEventType.class);
}
public static EquivalentConverter<PlayerDigType> getPlayerDiggingActionConverter() {
return new EnumConverter<PlayerDigType>(PlayerDigType.class);
return new EnumConverter<>(getPlayerDigTypeClass(), PlayerDigType.class);
}
public static EquivalentConverter<PlayerAction> getEntityActionConverter() {
return new EnumConverter<PlayerAction>(PlayerAction.class);
return new EnumConverter<>(getPlayerActionClass(), PlayerAction.class);
}
public static EquivalentConverter<ScoreboardAction> getUpdateScoreActionConverter() {
return new EnumConverter<ScoreboardAction>(ScoreboardAction.class);
return new EnumConverter<>(getScoreboardActionClass(), ScoreboardAction.class);
}
public static EquivalentConverter<Particle> getParticleConverter() {
return new EnumConverter<Particle>(Particle.class);
return new EnumConverter<>(getParticleClass(), Particle.class);
}
public static EquivalentConverter<SoundCategory> getSoundCategoryConverter() {
return new EnumConverter<SoundCategory>(SoundCategory.class);
return new EnumConverter<>(getSoundCategoryClass(), SoundCategory.class);
}
public static EquivalentConverter<ItemSlot> getItemSlotConverter() {
return new EnumConverter<ItemSlot>(ItemSlot.class);
return new EnumConverter<>(getItemSlotClass(), ItemSlot.class);
}
public static EquivalentConverter<Hand> getHandConverter() {
return new EnumConverter<Hand>(Hand.class);
return new EnumConverter<>(getHandClass(), Hand.class);
}
public static EquivalentConverter<Direction> getDirectionConverter() {
return new EnumConverter<>(getDirectionClass(), Direction.class);
}
public static EquivalentConverter<ChatType> getChatTypeConverter() {
return new EnumConverter<>(getChatTypeClass(), ChatType.class);
}
/**
* Retrieve a generic enum converter for use with StructureModifiers.
* @param enumClass - Enum class
* @param genericClass - Generic nms enum class
* @param specificType - Specific enum class
* @return A generic enum converter
*/
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> enumClass) {
return new EnumConverter<T>(enumClass);
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<?> genericClass, Class<T> specificType) {
return new EnumConverter<>(genericClass, specificType);
}
/**
* @deprecated Replaced with {@link #getGenericConverter(Class, Class)}
*/
@Deprecated
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> specificType) {
return new EnumConverter<>(null, specificType);
}
// The common enum converter
@SuppressWarnings({ "rawtypes", "unchecked" })
public static class EnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
private Class<?> genericType;
private Class<T> specificType;
public EnumConverter(Class<T> specificType) {
public EnumConverter(Class<?> genericType, Class<T> specificType) {
this.genericType = genericType;
this.specificType = specificType;
}
@Override
public T getSpecific(Object generic) {
// We know its an enum already!
return Enum.valueOf(specificType, ((Enum) generic).name());
}
@Override
public Object getGeneric(Class<?> genericType, T specific) {
public Object getGeneric(T specific) {
return Enum.valueOf((Class) genericType, specific.name());
}
@@ -621,5 +688,9 @@ public abstract class EnumWrappers {
public Class<T> getSpecificType() {
return specificType;
}
void setGenericType(Class<?> genericType) {
this.genericType = genericType;
}
}
}
@@ -1,142 +1,147 @@
/**
* 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 java.lang.reflect.Constructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a MinecraftKey in 1.9.
* <p>
* Keys are in the format {@code prefix:key}
*
* @author dmulloy2
*/
public class MinecraftKey {
private final String prefix;
private final String key;
/**
* Constructs a new key with a given prefix and key.
*
* @param prefix The prefix, usually minecraft.
* @param key The key, the part we care about
*/
public MinecraftKey(String prefix, String key) {
this.prefix = prefix;
this.key = key;
}
/**
* Constructs a new key with minecraft prefix and a key.
* @param key The key
*/
public MinecraftKey(String key) {
this("minecraft", key);
}
/**
* Creates a MinecraftKey wrapper from a Minecraft handle.
* @param handle The handle
* @return The resulting key
*/
public static MinecraftKey fromHandle(Object handle) {
StructureModifier<String> modifier = new StructureModifier<String>(handle.getClass()).withTarget(handle).withType(String.class);
return new MinecraftKey(modifier.read(0), modifier.read(1));
}
/**
* Creates a MinecraftKey wrapper from an Enum constant. The resulting key
* is lower case, with underscores replaced by periods.
* @param value The value
* @return The resulting key
*/
public static MinecraftKey fromEnum(Enum<?> value) {
return new MinecraftKey(value.name().toLowerCase().replace("_", "."));
}
/**
* Gets the prefix of this MinecraftKey. It is minecraft by default.
* @return The prefix
*/
public String getPrefix() {
return prefix;
}
/**
* Gets the key of this MinecraftKey. It is generally the important part.
* @return The key
*/
public String getKey() {
return key;
}
/**
* Gets the full key of this MinecraftKey. It is in the format of
* {@code prefix:key}
* @return The full key
*/
public String getFullKey() {
return prefix + ":" + key;
}
/**
* Returns this key back into Enum format, upper case with periods replaced
* by underscores.
* @return The enum format
*/
public String getEnumFormat() {
return key.toUpperCase().replace(".", "_");
}
private static Constructor<?> constructor = null;
public static EquivalentConverter<MinecraftKey> getConverter() {
return new EquivalentConverter<MinecraftKey>() {
@Override
public MinecraftKey getSpecific(Object generic) {
return MinecraftKey.fromHandle(generic);
}
@Override
public Object getGeneric(Class<?> genericType, MinecraftKey specific) {
if (constructor == null) {
try {
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to obtain MinecraftKey constructor", e);
}
}
try {
return constructor.newInstance(specific.getPrefix(), specific.getKey());
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to create new MinecraftKey", e);
}
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
};
}
}
/**
* 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 java.lang.reflect.Constructor;
import java.util.Locale;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a MinecraftKey in 1.9.
* <p>
* Keys are in the format {@code prefix:key}
*
* @author dmulloy2
*/
public class MinecraftKey {
private final String prefix;
private final String key;
/**
* Constructs a new key with a given prefix and key.
*
* @param prefix The prefix, usually minecraft.
* @param key The key, the part we care about
*/
public MinecraftKey(String prefix, String key) {
this.prefix = prefix;
this.key = key;
}
/**
* Constructs a new key with minecraft prefix and a key.
* @param key The key
*/
public MinecraftKey(String key) {
this("minecraft", key);
}
/**
* Creates a MinecraftKey wrapper from a Minecraft handle.
* @param handle The handle
* @return The resulting key
*/
public static MinecraftKey fromHandle(Object handle) {
StructureModifier<String> modifier = new StructureModifier<String>(handle.getClass()).withTarget(handle).withType(String.class);
return new MinecraftKey(modifier.read(0), modifier.read(1));
}
/**
* Creates a MinecraftKey wrapper from an Enum constant. The resulting key
* is lower case, with underscores replaced by periods.
* @param value The value
* @return The resulting key
* @deprecated This isn't accurate in all cases
*/
@Deprecated
public static MinecraftKey fromEnum(Enum<?> value) {
return new MinecraftKey(value.name().toLowerCase(Locale.ENGLISH).replace("_", "."));
}
/**
* Gets the prefix of this MinecraftKey. It is minecraft by default.
* @return The prefix
*/
public String getPrefix() {
return prefix;
}
/**
* Gets the key of this MinecraftKey. It is generally the important part.
* @return The key
*/
public String getKey() {
return key;
}
/**
* Gets the full key of this MinecraftKey. It is in the format of
* {@code prefix:key}
* @return The full key
*/
public String getFullKey() {
return prefix + ":" + key;
}
/**
* Returns this key back into Enum format, upper case with periods replaced
* by underscores.
* @return The enum format
* @deprecated This isn't accurate in all cases
*/
@Deprecated
public String getEnumFormat() {
return key.toUpperCase(Locale.ENGLISH).replace(".", "_");
}
private static Constructor<?> constructor = null;
public static EquivalentConverter<MinecraftKey> getConverter() {
return new EquivalentConverter<MinecraftKey>() {
@Override
public MinecraftKey getSpecific(Object generic) {
return MinecraftKey.fromHandle(generic);
}
@Override
public Object getGeneric(MinecraftKey specific) {
if (constructor == null) {
try {
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to obtain MinecraftKey constructor", e);
}
}
try {
return constructor.newInstance(specific.getPrefix(), specific.getKey());
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to create new MinecraftKey", e);
}
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
};
}
}
@@ -1,268 +1,268 @@
/**
* 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.Array;
import java.lang.reflect.Constructor;
import org.bukkit.Location;
import org.bukkit.World;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a single block change.
*
* @author dmulloy2
*/
public class MultiBlockChangeInfo {
private static Constructor<?> constructor;
private static Class<?> nmsClass = MinecraftReflection.getMultiBlockChangeInfoClass();
private short location;
private WrappedBlockData data;
private ChunkCoordIntPair chunk;
public MultiBlockChangeInfo(short location, WrappedBlockData data, ChunkCoordIntPair chunk) {
this.location = location;
this.data = data;
this.chunk = chunk;
}
public MultiBlockChangeInfo(Location location, WrappedBlockData data) {
this.data = data;
this.chunk = new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4);
this.setLocation(location);
}
/**
* Returns this block change's absolute Location in a given World.
*
* @param world World for the location
* @return This block change's absolute Location
*/
public Location getLocation(World world) {
return new Location(world, getAbsoluteX(), getY(), getAbsoluteZ());
}
/**
* Sets this block change's absolute Location.
*
* @param location This block change's new location
*/
public void setLocation(Location location) {
setLocation(location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
/**
* Sets this block change's absolute coordinates.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
*/
public void setLocation(int x, int y, int z) {
x = x & 15;
z = z & 15;
this.location = (short) (x << 12 | z << 8 | y);
}
/**
* Gets this block change's relative x coordinate.
*
* @return Relative X coordinate
*/
public int getX() {
return location >> 12 & 15;
}
/**
* Gets this block change's absolute x coordinate.
*
* @return Absolute X coordinate
*/
public int getAbsoluteX() {
return (chunk.getChunkX() << 4) + getX();
}
/**
* Sets this block change's absolute x coordinate.
*
* @param x New x coordinate
*/
public void setX(int x) {
setLocation(x, getY(), getZ());
}
/**
* Gets this block change's y coordinate.
*
* @return Y coordinate
*/
public int getY() {
return location & 255;
}
/**
* Sets this block change's y coordinate
*
* @param y New y coordinate
*/
public void setY(int y) {
setLocation(getX(), y, getZ());
}
/**
* Gets this block change's relative z coordinate.
*
* @return Relative Z coordinate
*/
public int getZ() {
return location >> 8 & 15;
}
/**
* Gets this block change's absolute z coordinate.
*
* @return Absolute Z coordinate
*/
public int getAbsoluteZ() {
return (chunk.getChunkZ() << 4) + getZ();
}
/**
* Sets this block change's relative z coordinate.
*
* @param z New z coordinate
*/
public void setZ(int z) {
setLocation(getX(), getY(), z);
}
/**
* Gets this block change's block data.
*
* @return The block data
*/
public WrappedBlockData getData() {
return data;
}
/**
* Sets this block change's block data.
*
* @param data New block data
*/
public void setData(WrappedBlockData data) {
this.data = data;
}
/**
* Gets the chunk this block change occured in.
*
* @return The chunk
*/
public ChunkCoordIntPair getChunk() {
return chunk;
}
public static EquivalentConverter<MultiBlockChangeInfo> getConverter(final ChunkCoordIntPair chunk) {
return new EquivalentConverter<MultiBlockChangeInfo>() {
@Override
public MultiBlockChangeInfo getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false).withTarget(generic);
StructureModifier<Short> shorts = modifier.withType(short.class);
short location = shorts.read(0);
StructureModifier<WrappedBlockData> dataModifier = modifier.withType(MinecraftReflection.getIBlockDataClass(),
BukkitConverters.getWrappedBlockDataConverter());
WrappedBlockData data = dataModifier.read(0);
return new MultiBlockChangeInfo(location, data, chunk);
}
@Override
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo specific) {
try {
if (constructor == null) {
constructor = nmsClass.getConstructor(
PacketType.Play.Server.MULTI_BLOCK_CHANGE.getPacketClass(),
short.class,
MinecraftReflection.getIBlockDataClass()
);
}
return constructor.newInstance(
null,
specific.location,
BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), specific.data)
);
} catch (Throwable ex) {
throw new RuntimeException("Failed to construct MultiBlockChangeInfo instance.", ex);
}
}
@Override
public Class<MultiBlockChangeInfo> getSpecificType() {
return MultiBlockChangeInfo.class;
}
};
}
public static EquivalentConverter<MultiBlockChangeInfo[]> getArrayConverter(final ChunkCoordIntPair chunk) {
return new EquivalentConverter<MultiBlockChangeInfo[]>() {
private final EquivalentConverter<MultiBlockChangeInfo> converter = MultiBlockChangeInfo.getConverter(chunk);
@Override
public MultiBlockChangeInfo[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
MultiBlockChangeInfo[] result = new MultiBlockChangeInfo[array.length];
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getSpecific(array[i]);
}
return result;
}
@Override
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo[] specific) {
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
// Wrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getGeneric(nmsClass, specific[i]);
}
return result;
}
@Override
public Class<MultiBlockChangeInfo[]> getSpecificType() {
return MultiBlockChangeInfo[].class;
}
};
}
}
/**
* 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.Array;
import java.lang.reflect.Constructor;
import org.bukkit.Location;
import org.bukkit.World;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a single block change.
*
* @author dmulloy2
*/
public class MultiBlockChangeInfo {
private static Constructor<?> constructor;
private static Class<?> nmsClass = MinecraftReflection.getMultiBlockChangeInfoClass();
private short location;
private WrappedBlockData data;
private ChunkCoordIntPair chunk;
public MultiBlockChangeInfo(short location, WrappedBlockData data, ChunkCoordIntPair chunk) {
this.location = location;
this.data = data;
this.chunk = chunk;
}
public MultiBlockChangeInfo(Location location, WrappedBlockData data) {
this.data = data;
this.chunk = new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4);
this.setLocation(location);
}
/**
* Returns this block change's absolute Location in a given World.
*
* @param world World for the location
* @return This block change's absolute Location
*/
public Location getLocation(World world) {
return new Location(world, getAbsoluteX(), getY(), getAbsoluteZ());
}
/**
* Sets this block change's absolute Location.
*
* @param location This block change's new location
*/
public void setLocation(Location location) {
setLocation(location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
/**
* Sets this block change's absolute coordinates.
*
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
*/
public void setLocation(int x, int y, int z) {
x = x & 15;
z = z & 15;
this.location = (short) (x << 12 | z << 8 | y);
}
/**
* Gets this block change's relative x coordinate.
*
* @return Relative X coordinate
*/
public int getX() {
return location >> 12 & 15;
}
/**
* Gets this block change's absolute x coordinate.
*
* @return Absolute X coordinate
*/
public int getAbsoluteX() {
return (chunk.getChunkX() << 4) + getX();
}
/**
* Sets this block change's absolute x coordinate.
*
* @param x New x coordinate
*/
public void setX(int x) {
setLocation(x, getY(), getZ());
}
/**
* Gets this block change's y coordinate.
*
* @return Y coordinate
*/
public int getY() {
return location & 255;
}
/**
* Sets this block change's y coordinate
*
* @param y New y coordinate
*/
public void setY(int y) {
setLocation(getX(), y, getZ());
}
/**
* Gets this block change's relative z coordinate.
*
* @return Relative Z coordinate
*/
public int getZ() {
return location >> 8 & 15;
}
/**
* Gets this block change's absolute z coordinate.
*
* @return Absolute Z coordinate
*/
public int getAbsoluteZ() {
return (chunk.getChunkZ() << 4) + getZ();
}
/**
* Sets this block change's relative z coordinate.
*
* @param z New z coordinate
*/
public void setZ(int z) {
setLocation(getX(), getY(), z);
}
/**
* Gets this block change's block data.
*
* @return The block data
*/
public WrappedBlockData getData() {
return data;
}
/**
* Sets this block change's block data.
*
* @param data New block data
*/
public void setData(WrappedBlockData data) {
this.data = data;
}
/**
* Gets the chunk this block change occured in.
*
* @return The chunk
*/
public ChunkCoordIntPair getChunk() {
return chunk;
}
public static EquivalentConverter<MultiBlockChangeInfo> getConverter(final ChunkCoordIntPair chunk) {
return new EquivalentConverter<MultiBlockChangeInfo>() {
@Override
public MultiBlockChangeInfo getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false).withTarget(generic);
StructureModifier<Short> shorts = modifier.withType(short.class);
short location = shorts.read(0);
StructureModifier<WrappedBlockData> dataModifier = modifier.withType(MinecraftReflection.getIBlockDataClass(),
BukkitConverters.getWrappedBlockDataConverter());
WrappedBlockData data = dataModifier.read(0);
return new MultiBlockChangeInfo(location, data, chunk);
}
@Override
public Object getGeneric(MultiBlockChangeInfo specific) {
try {
if (constructor == null) {
constructor = nmsClass.getConstructor(
PacketType.Play.Server.MULTI_BLOCK_CHANGE.getPacketClass(),
short.class,
MinecraftReflection.getIBlockDataClass()
);
}
return constructor.newInstance(
null,
specific.location,
BukkitConverters.getWrappedBlockDataConverter().getGeneric(specific.data)
);
} catch (Throwable ex) {
throw new RuntimeException("Failed to construct MultiBlockChangeInfo instance.", ex);
}
}
@Override
public Class<MultiBlockChangeInfo> getSpecificType() {
return MultiBlockChangeInfo.class;
}
};
}
public static EquivalentConverter<MultiBlockChangeInfo[]> getArrayConverter(final ChunkCoordIntPair chunk) {
return new EquivalentConverter<MultiBlockChangeInfo[]>() {
private final EquivalentConverter<MultiBlockChangeInfo> converter = MultiBlockChangeInfo.getConverter(chunk);
@Override
public MultiBlockChangeInfo[] getSpecific(Object generic) {
Object[] array = (Object[]) generic;
MultiBlockChangeInfo[] result = new MultiBlockChangeInfo[array.length];
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getSpecific(array[i]);
}
return result;
}
@Override
public Object getGeneric(MultiBlockChangeInfo[] specific) {
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
// Wrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getGeneric(specific[i]);
}
return result;
}
@Override
public Class<MultiBlockChangeInfo[]> getSpecificType() {
return MultiBlockChangeInfo[].class;
}
};
}
}
@@ -1,189 +1,189 @@
/**
* 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);
}
}
/**
* 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(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(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<>(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);
}
}
@@ -0,0 +1,128 @@
/**
* (c) 2016 dmulloy2
*/
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.BukkitConverters.IgnoreNullConverter;
/**
* @author dmulloy2
*/
public class Vector3F {
protected float x;
protected float y;
protected float z;
public Vector3F() {
this(0, 0, 0);
}
public Vector3F(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public float getX() {
return x;
}
public Vector3F setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector3F setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector3F setZ(float z) {
this.z = z;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Float.floatToIntBits(x);
result = prime * result + Float.floatToIntBits(y);
result = prime * result + Float.floatToIntBits(z);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Vector3F) {
Vector3F that = (Vector3F) obj;
if (Float.floatToIntBits(x) != Float.floatToIntBits(that.x))
return false;
if (Float.floatToIntBits(y) != Float.floatToIntBits(that.y))
return false;
if (Float.floatToIntBits(z) != Float.floatToIntBits(that.z))
return false;
return true;
}
return false;
}
private static Constructor<?> constructor = null;
private static Class<?> clazz = MinecraftReflection.getMinecraftClass("Vector3f");
public static Class<?> getMinecraftClass() {
return clazz;
}
public static EquivalentConverter<Vector3F> getConverter() {
return Converters.ignoreNull(new EquivalentConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
public Object getGeneric(Vector3F specific) {
if (constructor == null) {
try {
constructor = clazz.getConstructor(float.class, float.class, float.class);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to create new instance of Vector3f", ex);
}
}
@Override
public Vector3F getSpecific(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
float y = modifier.read(1);
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
});
}
}
@@ -195,16 +195,16 @@ public class WrappedAttribute extends AbstractWrapper {
if (obj instanceof WrappedAttribute) {
WrappedAttribute other = (WrappedAttribute) obj;
return getBaseValue() == other.getBaseValue() &&
Objects.equal(getAttributeKey(), other.getAttributeKey()) &&
Sets.symmetricDifference(
getModifiers(),
other.getModifiers()
).isEmpty();
if (getBaseValue() == other.getBaseValue() &&
Objects.equal(getAttributeKey(), other.getAttributeKey())) {
return getModifiers().stream()
.filter((elem) -> !other.getModifiers().contains(elem))
.count() == 0;
}
}
return false;
}
@Override
public int hashCode() {
if (attributeModifiers == null)
@@ -251,12 +251,7 @@ public class WrappedAttribute extends AbstractWrapper {
@Override
public String toString() {
return Objects.toStringHelper("WrappedAttribute").
add("key", getAttributeKey()).
add("baseValue", getBaseValue()).
add("finalValue", getFinalValue()).
add("modifiers", getModifiers()).
toString();
return "WrappedAttribute[key=" + getAttributeKey() + ", base=" + getBaseValue() + ", final=" + getFinalValue() + ", modifiers=" + getModifiers() + "]";
}
/**
@@ -2,7 +2,7 @@ package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -91,7 +91,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Cached values
private final UUID uuid;
private final String name;
private final Supplier<String> name;
private final Operation operation;
private final double amount;
@@ -109,7 +109,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Use the supplied values instead of reading from the NMS instance
this.uuid = uuid;
this.name = name;
this.name = () -> name;
this.amount = amount;
this.operation = operation;
}
@@ -118,6 +118,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* Construct an attribute modifier wrapper around a given NMS instance.
* @param handle - the NMS instance.
*/
@SuppressWarnings("unchecked")
protected WrappedAttributeModifier(@Nonnull Object handle) {
// Update handle and modifier
super(MinecraftReflection.getAttributeModifierClass());
@@ -126,7 +127,15 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Load final values, caching them
this.uuid = (UUID) modifier.withType(UUID.class).read(0);
this.name = (String) modifier.withType(String.class).read(0);
StructureModifier<String> stringMod = modifier.withType(String.class);
if (stringMod.size() == 0) {
Supplier<String> supplier = (Supplier<String>) modifier.withType(Supplier.class).read(0);
this.name = supplier;
} else {
this.name = () -> stringMod.read(0);
}
this.amount = (Double) modifier.withType(double.class).read(0);
this.operation = Operation.fromId((Integer) modifier.withType(int.class).read(0));
}
@@ -213,7 +222,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @return The attribute key.
*/
public String getName() {
return name;
return name.get();
}
/**
@@ -1,166 +1,357 @@
/**
* 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;
}
}
/**
* 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 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;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
/**
* Represents a wrapper around IBlockData.
*
* @author dmulloy2
*/
public abstract class WrappedBlockData extends AbstractWrapper implements ClonableWrapper {
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 class NewBlockData extends WrappedBlockData {
private static MethodAccessor MATERIAL_FROM_BLOCK;
private static MethodAccessor TO_LEGACY_DATA;
private static MethodAccessor GET_BLOCK;
private static MethodAccessor BLOCK_FROM_MATERIAL;
private static MethodAccessor GET_BLOCK_DATA;
private static MethodAccessor FROM_LEGACY_DATA;
private static MethodAccessor GET_HANDLE;
static {
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Material.class)
.parameterExactArray(BLOCK)
.build();
MATERIAL_FROM_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(Material.class)
.returnTypeExact(BLOCK)
.build();
BLOCK_FROM_MATERIAL = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(byte.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(Material.class, byte.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeExact(BLOCK)
.parameterCount(0)
.build();
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(BLOCK);
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterCount(0)
.returnTypeExact(IBLOCK_DATA)
.build();
GET_BLOCK_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"));
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterCount(0)
.returnTypeExact(IBLOCK_DATA)
.build();
GET_HANDLE = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
}
private NewBlockData(Object handle) {
super(handle);
}
@Override
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return (Material) MATERIAL_FROM_BLOCK.invoke(null, block);
}
@Override
public int getData() {
return ((Number) TO_LEGACY_DATA.invoke(null, handle)).intValue();
}
@Override
public void setType(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
setHandle(GET_BLOCK_DATA.invoke(block));
}
@Override
public void setData(int data) {
setTypeAndData(getType(), data);
}
@Override
public void setTypeAndData(Material material, int data) {
setHandle(TO_LEGACY_DATA.invoke(null, material, (byte) data));
}
@Override
public WrappedBlockData deepClone() {
return NewBlockData.createNewData(getType(), getData());
}
private static WrappedBlockData createNewData(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
return new NewBlockData(GET_BLOCK_DATA.invoke(block));
}
private static WrappedBlockData createNewData(Material material, int data) {
return new NewBlockData(FROM_LEGACY_DATA.invoke(null, material, (byte) data));
}
private static WrappedBlockData createNewData(BlockData data) {
return new NewBlockData(GET_HANDLE.invoke(data));
}
}
private static class OldBlockData extends WrappedBlockData {
private static MethodAccessor FROM_LEGACY_DATA;
private static MethodAccessor TO_LEGACY_DATA;
private static MethodAccessor GET_NMS_BLOCK;
private static MethodAccessor GET_BLOCK;
static {
if (!MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
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]));
}
}
private OldBlockData(Object handle) {
super(handle);
}
@Override
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockConverter().getSpecific(block);
}
@Override
public int getData() {
Object block = GET_BLOCK.invoke(handle);
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
}
@Override
public void setType(Material type) {
setTypeAndData(type, 0);
}
@Override
public void setData(int data) {
setTypeAndData(getType(), data);
}
@Override
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);
}
@Override
public WrappedBlockData deepClone() {
return WrappedBlockData.createData(getType(), getData());
}
private static WrappedBlockData createOldData(Material type) {
Object blockData = GET_BLOCK.invoke(null, type);
return new OldBlockData(blockData);
}
private static WrappedBlockData createOldData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
return new OldBlockData(blockData);
}
}
public WrappedBlockData(Object handle) {
super(IBLOCK_DATA);
setHandle(handle);
}
/**
* Gets this BlockData's Bukkit material
* @return The Bukkit material
*/
public abstract Material getType();
/**
* Gets this BlockData's legacy data
* @return The legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
@Deprecated
public abstract int getData();
/**
* Sets this BlockData's type
* @param material Bukkit material
*/
public abstract void setType(Material material);
/**
* Sets this BlockData's legacy data
* @param data The new legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
@Deprecated
public abstract void setData(int data);
/**
* Sets this BlockData's type and legacy data
* @param material The new Bukkit material
* @param data The new legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
@Deprecated
public abstract void setTypeAndData(Material material, int data);
public abstract WrappedBlockData deepClone();
/**
* 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 MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type)
: OldBlockData.createOldData(type);
}
/**
* Creates a new BlockData instance with the given type and data.
* @param type Block type
* @param data Block data
* @return New BlockData
* @deprecated The flattening
*/
@Deprecated
public static WrappedBlockData createData(Material type, int data) {
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type, data)
: OldBlockData.createOldData(type, data);
}
public static WrappedBlockData fromHandle(Object handle) {
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? new NewBlockData(handle)
: new OldBlockData(handle);
}
/**
* Creates a new Wrapped Block Data instance from a given Spigot Block Data
* @param data Spigot block data
* @return The new Wrapped Block Data
*/
public static WrappedBlockData createData(BlockData data) {
return NewBlockData.createNewData(data);
}
@Override
public String toString() {
return "WrappedBlockData[handle=" + handle + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getType().hashCode();
result = prime * result + getData();
return result;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.handle.equals(that.handle)
|| (this.getType() == that.getType() && this.getData() == that.getData());
}
return false;
}
}
@@ -1,10 +1,10 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Method;
import java.util.List;
import java.io.StringReader;
import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
@@ -19,18 +19,35 @@ import com.google.common.base.Preconditions;
public class WrappedChatComponent extends AbstractWrapper {
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static Object GSON = null;
private static MethodAccessor DESERIALIZE = null;
private static MethodAccessor SERIALIZE_COMPONENT = null;
private static MethodAccessor DESERIALIZE_COMPONENT = null;
private static MethodAccessor CONSTRUCT_COMPONENT = null;
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
// Retrieve the correct methods
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
DESERIALIZE_COMPONENT = findDeserialize(fuzzy);
try {
GSON = FieldUtils.readStaticField(fuzzy.getFieldByType("gson", GSON_CLASS), true);
} catch (IllegalAccessException ex) {
throw new RuntimeException("Failed to obtain GSON field", ex);
}
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("ChatDeserializer"), true)
.getMethodByParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
@@ -39,23 +56,17 @@ public class WrappedChatComponent extends AbstractWrapper {
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
}
private static MethodAccessor findDeserialize(FuzzyReflection fuzzy) {
List<Method> methods = fuzzy.getMethodListByParameters(COMPONENT, new Class<?>[] { String.class });
if (methods.isEmpty()) {
throw new IllegalArgumentException("Unable to find deserialize method in " + fuzzy.getSource().getName());
private static Object deserialize(String json) {
// Should be non-null on 1.9 and up
if (DESERIALIZE != null) {
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
}
// Try to find b, we want leniency
for (Method method : methods) {
if (method.getName().equals("b")) {
return Accessors.getMethodAccessor(method);
}
}
// Oh well
return Accessors.getMethodAccessor(methods.get(0));
// Mock leniency behavior in 1.8
StringReader str = new StringReader(json);
return ComponentParser.deserialize(GSON, COMPONENT, str);
}
private transient String cache;
private WrappedChatComponent(Object handle, String cache) {
@@ -79,7 +90,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @return The chat component wrapper.
*/
public static WrappedChatComponent fromJson(String json) {
return new WrappedChatComponent(DESERIALIZE_COMPONENT.invoke(null, json), json);
return new WrappedChatComponent(deserialize(json), json);
}
/**
@@ -127,7 +138,7 @@ public class WrappedChatComponent extends AbstractWrapper {
* @param obj - the JSON that represents the new component.
*/
public void setJson(String obj) {
this.handle = DESERIALIZE_COMPONENT.invoke(null, obj);
this.handle = deserialize(obj);
this.cache = obj;
}
@@ -138,24 +149,9 @@ public class WrappedChatComponent extends AbstractWrapper {
public WrappedChatComponent deepClone() {
return fromJson(getJson());
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WrappedChatComponent) {
return ((WrappedChatComponent) obj).handle.equals(handle);
}
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
@Override
public String toString() {
return "WrappedChatComponent[json=" + getJson() + "]";
}
}
}
@@ -19,7 +19,6 @@ 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.
@@ -161,24 +160,6 @@ public class WrappedChunkCoordinate extends AbstractWrapper implements Comparabl
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,6 +1,6 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
* Copyright (C) 2018 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
@@ -16,19 +16,8 @@
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import java.lang.reflect.*;
import java.util.*;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException;
@@ -43,13 +32,17 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
/**
* Represents a DataWatcher in 1.9
*
* Represents a DataWatcher
* @author dmulloy2
*/
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
private static MethodAccessor GETTER = null;
@@ -61,7 +54,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
private static FieldAccessor MAP_FIELD = null;
private static ConstructorAccessor constructor = null;
private static ConstructorAccessor lightningConstructor = null;
private static ConstructorAccessor eggConstructor = null;
private static Object fakeEntity = null;
@@ -80,7 +73,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
/**
* Constructs a new DataWatcher using a fake lightning entity. The
* Constructs a new DataWatcher using a fake egg entity. The
* resulting DataWatcher will not have any keys or values and new ones will
* have to be added using watcher objects.
*/
@@ -100,7 +93,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
/**
* Constructs a new DataWatcher using a fake lightning entity and a given
* Constructs a new DataWatcher using a fake egg entity and a given
* list of watchable objects.
*
* @param objects The list of objects
@@ -108,8 +101,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
this();
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject object : objects) {
setObject(object.getWatcherObject(), object);
}
} else {
for (WrappedWatchableObject object : objects) {
setObject(object.getIndex(), object);
}
}
}
@@ -126,13 +125,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return fakeEntity;
}
// We can create a fake lightning strike without it affecting anything
if (lightningConstructor == null) {
lightningConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityLightning"),
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class, boolean.class);
// We can create a fake egg without it affecting anything
// Mojang added difficulty to lightning strikes, so this'll have to do
if (eggConstructor == null) {
eggConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityEgg"),
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class);
}
return fakeEntity = lightningConstructor.invoke(null, 0, 0, 0, true);
return fakeEntity = eggConstructor.invoke(null, 0, 0, 0);
}
// ---- Collection Methods
@@ -140,15 +140,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
@SuppressWarnings("unchecked")
private Map<Integer, Object> getMap() {
if (MAP_FIELD == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.typeDerivedOf(Map.class)
.build()));
}
if (MAP_FIELD == null) {
throw new FieldAccessException("Could not find index <-> Item map.");
try {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
.newBuilder()
.banModifier(Modifier.STATIC)
.typeDerivedOf(Map.class)
.build()));
} catch (IllegalArgumentException ex) {
throw new FieldAccessException("Failed to find watchable object map");
}
}
return (Map<Integer, Object>) MAP_FIELD.get(handle);
@@ -216,6 +217,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
/**
* @deprecated Renamed to {@link #remove(int)}
*/
@Deprecated
public WrappedWatchableObject removeObject(int index) {
return remove(index);
}
/**
* Removes the item at a given index.
*
@@ -234,7 +243,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return True if it does, false if not
*/
public boolean hasIndex(int index) {
return getMap().containsKey(index);
return getObject(index) != null;
}
/**
* Returns a set containing all the registered indexes
* @return The set
*/
public Set<Integer> indexSet() {
return getMap().keySet();
}
/**
@@ -321,59 +338,114 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* Retrieve a watchable object by index.
*
* @param index Index of the object to retrieve.
* @return The watched object.
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(int index) {
return getObject(new WrappedDataWatcherObject(index, null));
return getObject(WrappedDataWatcherObject.fromIndex(index));
}
/**
* Retrieve a watchable object by watcher object.
*
* @param object The watcher object
* @return The watched object
* @return The watched object or null if it doesn't exist.
*/
public Object getObject(WrappedDataWatcherObject object) {
Validate.notNull(object, "Watcher object cannot be null!");
if (GETTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType);
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
if (MinecraftReflection.watcherObjectExists()) {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(object.getHandleType())
.returnTypeExact(Object.class)
.build(), "get"));
} else {
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactType(int.class)
.returnTypeExact(MinecraftReflection.getDataWatcherItemClass())
.build()));
}
}
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
try {
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
} catch (Exception ex) {
// Nothing exists at this index
return null;
}
}
// ---- Object Setters
/**
* Sets the DataWatcher Item at a given index to a new value.
* Sets the DataWatcher Item at a given index to a new value. In 1.9 and up,
* you cannot register objects without a watcher object.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
* @see {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
* @throws IllegalArgumentException in 1.9 and up if there isn't already an
* object at this index
*/
public void setObject(int index, Object value, boolean update) {
if (MinecraftReflection.watcherObjectExists() && !hasIndex(index)) {
throw new IllegalArgumentException("You cannot register objects without a watcher object!");
}
setObject(WrappedDataWatcherObject.fromIndex(index), value, update);
}
/**
* Shortcut for {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, Object value) {
Validate.isTrue(hasIndex(index), "You cannot register objects without the watcher object!");
setObject(index, null, value);
setObject(index, value, false);
}
/**
* Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index of the object to set
* @param Serializer Serializer from {@link Serializer#get(Class)}
* @param serializer Serializer from {@link Registry#get(Class)}
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
*/
public void setObject(int index, Serializer serializer, Object value, boolean update) {
setObject(new WrappedDataWatcherObject(index, serializer), value, update);
}
/**
* Alias for {@link #setObject(int, Serializer, Object, boolean)}
*/
public void setObject(int index, Serializer serializer, Object value) {
setObject(new WrappedDataWatcherObject(index, serializer), value);
setObject(new WrappedDataWatcherObject(index, serializer), value, false);
}
/**
* Sets the DataWatcher Item at a given index to a new value.
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(int, Object, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value, boolean update) {
setObject(index, value.getRawValue(), update);
}
/**
* Alias for {@link #setObject(int, WrappedWatchableObject, boolean)}
*/
public void setObject(int index, WrappedWatchableObject value) {
setObject(index, value.getRawValue(), false);
}
/**
@@ -381,11 +453,19 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @param object Associated watcher object
* @param value Wrapped value
* @param update Whether or not to inform the client
*
* @see {@link #setObject(WrappedDataWatcherObject, Object)}
* @see #setObject(WrappedDataWatcherObject, Object)
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
setObject(object, value.getRawValue(), update);
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, WrappedWatchableObject, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value) {
setObject(object, value.getRawValue());
setObject(object, value.getRawValue(), false);
}
/**
@@ -399,27 +479,46 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @throws IllegalArgumentException If the watcher object is null or must
* have a serializer and does not have one.
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
Validate.notNull(object, "Watcher object cannot be null!");
if (SETTER == null || REGISTER == null) {
if (SETTER == null && REGISTER == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.parameterExactArray(object.getHandleType(), Object.class)
.build();
SETTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "set"));
REGISTER = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "register"));
List<Method> methods = fuzzy.getMethodList(contract);
for (Method method : methods) {
if (method.getName().equals("set") || method.getName().equals("watch")) {
SETTER = Accessors.getMethodAccessor(method);
} else {
REGISTER = Accessors.getMethodAccessor(method);
}
}
}
// Unwrap the object
value = WrappedWatchableObject.getUnwrapped(value);
if (hasIndex(object.getIndex())) {
SETTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
SETTER.invoke(handle, object.getHandle(), value);
} else {
Serializer serializer = object.getSerializer();
Validate.notNull(serializer, "You must specify a serializer to register an object!");
REGISTER.invoke(handle, object.getHandle(), WrappedWatchableObject.getUnwrapped(value));
object.checkSerializer();
REGISTER.invoke(handle, object.getHandle(), value);
}
if (update) {
getWatchableObject(object.getIndex()).setDirtyState(update);
}
}
/**
* Shortcut for {@link #setObject(WrappedDataWatcherObject, Object, boolean)}
*/
public void setObject(WrappedDataWatcherObject object, Object value) {
setObject(object, value, false);
}
// ---- Utility Methods
@@ -432,8 +531,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
public WrappedDataWatcher deepClone() {
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
if (MinecraftReflection.watcherObjectExists()) {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getWatcherObject(), wrapper);
}
} else {
for (WrappedWatchableObject wrapper : this) {
clone.setObject(wrapper.getIndex(), wrapper);
}
}
return clone;
@@ -479,26 +584,37 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
}
private static final ImmutableBiMap<Class<?>, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder<Class<?>, Integer>()
.put(Byte.class, 0)
.put(Short.class, 1)
.put(Integer.class, 2)
.put(Float.class, 3)
.put(String.class, 4)
.put(MinecraftReflection.getItemStackClass(), 5)
.put(MinecraftReflection.getBlockPositionClass(), 6)
.put(Vector3F.getMinecraftClass(), 7)
.build();
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the type ID associated with a given class. No longer supported
* in 1.9 and up due to the removal of type IDs.
*
* @param clazz
* @return Null
* @param clazz Class to find ID for
* @return The ID, or null if not found
*/
@Deprecated
public static Integer getTypeID(Class<?> clazz) {
return null;
return CLASS_TO_ID.get(clazz);
}
/**
* No longer supported in 1.9 due to the removal of a consistent type <-> ID map.
* Retrieves the class associated with a given type ID. No longer
* supported in 1.9 and up due to the removal of type IDs.
*
* @param typeID
* @return Null
* @param typeID ID to find Class for
* @return The Class, or null if not found
*/
@Deprecated
public static Class<?> getTypeClass(int typeID) {
return null;
return CLASS_TO_ID.inverse().get(typeID);
}
@Override
@@ -544,12 +660,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*
* @author dmulloy2
*/
public static class WrappedDataWatcherObject extends AbstractWrapper {
public static class WrappedDataWatcherObject {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherObjectClass();
private static ConstructorAccessor constructor = null;
private static MethodAccessor getSerializer = null;
private final StructureModifier<Object> modifier;
private StructureModifier<Object> modifier;
private Object handle;
protected WrappedDataWatcherObject() {
}
/**
* Creates a new watcher object from a NMS handle.
@@ -557,10 +677,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @param handle The handle
*/
public WrappedDataWatcherObject(Object handle) {
super(HANDLE_TYPE);
setHandle(handle);
this.modifier = new StructureModifier<Object>(HANDLE_TYPE).withTarget(handle);
this.handle = handle;
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
}
/**
@@ -573,12 +691,33 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
this(newHandle(index, serializer));
}
private static Object newHandle(int index, Serializer serializer) {
static WrappedDataWatcherObject fromIndex(int index) {
if (MinecraftReflection.watcherObjectExists()) {
return new WrappedDataWatcherObject(newHandle(index));
} else {
return new DummyWatcherObject(index);
}
}
private static Object newHandle(int index) {
Validate.isTrue(index >= 0, "index cannot be negative!");
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
Object handle = serializer != null ? serializer.getHandle() : null;
return constructor.invoke(index, null);
}
private static Object newHandle(int index, Serializer serializer) {
Validate.isTrue(index >= 0, "index cannot be negative!");
Validate.notNull(serializer, "serializer cannot be null!");
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
Object handle = serializer.getHandle();
return constructor.invoke(index, handle);
}
@@ -616,6 +755,18 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
public void checkSerializer() {
Validate.notNull(getSerializer(), "You must specify a serializer to register an object!");
}
public Object getHandle() {
return handle;
}
public Class<?> getHandleType() {
return HANDLE_TYPE;
}
@Override
public String toString() {
return "DataWatcherObject[index=" + getIndex() + ", serializer=" + getSerializer() + "]";
@@ -624,7 +775,6 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof WrappedDataWatcherObject) {
WrappedDataWatcherObject other = (WrappedDataWatcherObject) obj;
@@ -633,6 +783,57 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return false;
}
@Override
public int hashCode() {
return handle.hashCode();
}
}
private static class DummyWatcherObject extends WrappedDataWatcherObject {
private final int index;
public DummyWatcherObject(int index) {
this.index = index;
}
@Override
public int getIndex() {
return index;
}
@Override
public Serializer getSerializer() {
return null;
}
@Override
public Object getHandle() {
return getIndex();
}
@Override
public Class<?> getHandleType() {
return int.class;
}
@Override
public void checkSerializer() {
// Do nothing
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof DummyWatcherObject) {
DummyWatcherObject that = (DummyWatcherObject) obj;
return this.index == that.index;
}
return false;
}
}
/**
@@ -697,7 +898,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* <li>Float</li>
* <li>String</li>
* <li>IChatBaseComponent</li>
* <li>Optional&lt;ItemStack&gt;</li>
* <li>ItemStack</li>
* <li>Optional&lt;IBlockData&gt;</li>
* <li>Boolean</li>
* <li>Vector3f</li>
@@ -705,6 +906,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* <li>Optional&lt;BlockPosition&gt;</li>
* <li>EnumDirection</li>
* <li>Optional&lt;UUID&gt;</li>
* <li>NBTTagCompound</li>
* </ul>
*
* @author dmulloy2
@@ -726,7 +928,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz) {
Validate.notNull("Class cannot be null!");
Validate.notNull(clazz,"Class cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -735,7 +937,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
return null;
throw new IllegalArgumentException("No serializer found for " + clazz);
}
/**
@@ -752,6 +954,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
Validate.notNull(clazz, "Class cannot be null!");
initialize();
Validate.notEmpty(REGISTRY, "Registry has no elements!");
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)
&& serializer.isOptional() == optional) {
@@ -759,7 +963,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
return null;
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
}
/**
@@ -768,7 +972,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer, or null if none exists
*/
public static Serializer fromHandle(Object handle) {
Validate.notNull("Handle cannot be null!");
Validate.notNull(handle, "handle cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -796,7 +1000,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
Type[] args = type.getActualTypeArguments();
Type arg = args[0];
Class<?> innerClass = null;
Class<?> innerClass;
boolean optional = false;
if (arg instanceof Class<?>) {
@@ -816,9 +1020,92 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
throw new IllegalStateException("Failed to read field " + candidate);
}
if (serializer == null) {
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
}
REGISTRY.add(new Serializer(innerClass, serializer, optional));
}
}
}
// ---- Helper methods
/**
* Gets the serializer for IChatBaseComponents
* @return The serializer
*/
public static Serializer getChatComponentSerializer() {
return getChatComponentSerializer(false);
}
/**
* Gets the serializer for IChatBaseComponents
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getChatComponentSerializer(boolean optional) {
return get(MinecraftReflection.getIChatBaseComponentClass(), optional);
}
/**
* Gets the serializer for ItemStacks
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getItemStackSerializer(boolean optional) {
return get(MinecraftReflection.getItemStackClass(), optional);
}
/**
* Gets the serializer for BlockData
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockDataSerializer(boolean optional) {
return get(MinecraftReflection.getIBlockDataClass(), optional);
}
/**
* Gets the serializer for Vector3Fs
* @return The serializer
*/
public static Serializer getVectorSerializer() {
return get(Vector3F.getMinecraftClass());
}
/**
* Gets the serializer for BlockPositions
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getBlockPositionSerializer(boolean optional) {
return get(MinecraftReflection.getBlockPositionClass(), optional);
}
/**
* Gets the serializer for Directions
* @return The serializer
*/
public static Serializer getDirectionSerializer() {
return get(EnumWrappers.getDirectionClass());
}
/**
* Gets the serializer for UUIDs
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getUUIDSerializer(boolean optional) {
return get(UUID.class, optional);
}
/**
* Gets the serializer for NBT Compound tags
* @return The serializer
*/
public static Serializer getNBTCompoundSerializer() {
return get(MinecraftReflection.getNBTCompoundClass(), false);
}
}
}
@@ -333,4 +333,4 @@ public class WrappedGameProfile extends AbstractWrapper {
return false;
}
}
}
@@ -0,0 +1,153 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
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;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.inventory.ItemStack;
/**
* Represents an immutable wrapped ParticleParam in 1.13
*/
public class WrappedParticle<T> {
private static MethodAccessor toBukkit;
private static MethodAccessor toNMS;
private static MethodAccessor toCraftData;
private static void ensureMethods() {
if (toBukkit != null && toNMS != null) {
return;
}
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getMinecraftClass("ParticleParam"))
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getMinecraftClass("ParticleParam"))
.parameterCount(2)
.build();
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
Class<?> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
fuzzy = FuzzyReflection.fromClass(cbData);
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(cbData)
.parameterExactArray(MinecraftReflection.getIBlockDataClass())
.build();
toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
private final Particle particle;
private final T data;
private final Object handle;
private WrappedParticle(Object handle, Particle particle, T data) {
this.handle = handle;
this.particle = particle;
this.data = data;
}
/**
* @return This particle's Bukkit type
*/
public Particle getParticle() {
return particle;
}
/**
* Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
* {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
* for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
* be {@link Particle.DustOptions}
*
* @return The particle data
*/
public T getData() {
return data;
}
/**
* @return NMS handle
*/
public Object getHandle() {
return handle;
}
public static WrappedParticle fromHandle(Object handle) {
ensureMethods();
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
Object data = null;
switch (bukkit) {
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
data = getBlockData(handle);
break;
case ITEM_CRACK:
data = getItem(handle);
break;
case REDSTONE:
data = getRedstone(handle);
break;
default:
break;
}
return new WrappedParticle<>(handle, bukkit, data);
}
private static WrappedBlockData getBlockData(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
.read(0);
}
private static Object getItem(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
.read(0);
}
private static Object getRedstone(Object handle) {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
return new Particle.DustOptions(Color.fromRGB(
(int) (modifier.read(0) * 255),
(int) (modifier.read(1) * 255),
(int) (modifier.read(2) * 255)),
modifier.read(3));
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
Object handle = toNMS.invoke(null, particle, bukkitData);
return new WrappedParticle<>(handle, particle, data);
}
}
@@ -1,9 +1,5 @@
package com.comphenix.protocol.wrappers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
@@ -19,14 +15,13 @@ import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
@@ -34,25 +29,18 @@ import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
/**
* Represents a server ping packet data.
* @author Kristian
*/
public class WrappedServerPing extends AbstractWrapper {
/**
* Lookup of Minecraft versions and ping version numbers.
*/
private static ImmutableMap<MinecraftVersion, Integer> VERSION_NUMBERS = ImmutableMap.<MinecraftVersion, Integer>builder()
.put(MinecraftVersion.WORLD_UPDATE, 4)
.put(MinecraftVersion.SKIN_UPDATE, 5)
.put(MinecraftVersion.BOUNTIFUL_UPDATE, 47)
.build();
private static MinecraftVersion LAST_VERSION = MinecraftVersion.BOUNTIFUL_UPDATE;
public class WrappedServerPing extends AbstractWrapper implements ClonableWrapper {
private static Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
@@ -101,7 +89,7 @@ public class WrappedServerPing extends AbstractWrapper {
/**
* Construct a new server ping initialized with a zero player count, and zero maximum.
* <p>
* Note that the version string is set to 1.8.
* Note that the version string is set to 1.9.4.
*/
public WrappedServerPing() {
super(MinecraftReflection.getServerPingClass());
@@ -129,14 +117,8 @@ public class WrappedServerPing extends AbstractWrapper {
* Reset the version string to the default state.
*/
protected void resetVersion() {
ProtocolManager manager = ProtocolLibrary.getProtocolManager();
MinecraftVersion minecraftVersion = LAST_VERSION;
// Fetch the latest known version
if (manager != null) {
minecraftVersion = manager.getMinecraftVersion();
}
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), VERSION_NUMBERS.get(minecraftVersion));
MinecraftVersion minecraftVersion = MinecraftVersion.getCurrentVersion();
version = VERSION_CONSTRUCTOR.invoke(minecraftVersion.toString(), MinecraftProtocolVersion.getCurrentVersion());
VERSION.set(handle, version);
}
@@ -298,7 +280,7 @@ public class WrappedServerPing extends AbstractWrapper {
public void setPlayers(Iterable<? extends WrappedGameProfile> profile) {
if (players == null)
resetPlayers();
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(GAME_PROFILE_ARRAY, profile) : null);
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(profile) : null);
}
/**
@@ -132,24 +132,21 @@ public class WrappedSignedProperty extends AbstractWrapper {
}
@Override
public boolean equals(Object object){
public boolean equals(Object object) {
if (object == this) return true;
if (object instanceof WrappedSignedProperty) {
if (!super.equals(object))
return false;
WrappedSignedProperty that = (WrappedSignedProperty) object;
return Objects.equal(this.getName(), that.getName())
&& Objects.equal(this.getValue(), that.getValue())
&& Objects.equal(this.getSignature(), that.getSignature());
}
return false;
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("name", getName())
.add("value", getValue())
.add("signature", getSignature())
.toString();
return "WrappedSignedProperty[name=" + getName() + ", value=" + getValue() + ", signature=" + getSignature() + "]";
}
}
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff. Show more