Compare commits

...
215 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
Dan Mulloy bea4356aca Update version to 4.0.0 for release 2016-05-15 17:43:20 -04:00
0x277F 32f51c612a Add contributing guidelines and issue template 2016-05-14 17:12:51 -04:00
Dan Mulloy 355d00d951 Complete 1.9.4 update
Fixes #204
2016-05-13 17:12:21 -04:00
Dan Mulloy bf56f3919f Merge pull request #205 from 0x277F/mc-1.9.4
Update to 1.9.4
2016-05-13 16:52:50 -04:00
0x277F d876580ddf Update to 1.9.4 2016-05-12 16:30:49 -06:00
Dan Mulloy 90f36198d2 Add plugin caller to setPacket warning
Fixes #201
2016-05-02 22:04:10 -04:00
Dan Mulloy e542f3c9d3 Add back a missing data watcher constructor
Also bolstered some of the unit tests
Fixes #191
2016-04-16 21:24:58 -04:00
Dan Mulloy 87c56d0248 We support 1.9.2 2016-04-10 21:28:52 -04:00
Dan Mulloy 8c928cb237 Allow the usage of user-created enums
Basically this will allow developers to create their own enums instead
of having to wait on me to make them. The only caveat is that enum
constants will have to match up exactly with their NMS counterparts
2016-04-06 22:55:14 -04:00
Dan Mulloy 6030992af4 Add modifiers for Sounds and Hands 2016-04-04 21:37:45 -04:00
Dan Mulloy 688da1a1bb Add protocol version for 1.9.2 2016-04-03 00:49:04 -04:00
Dan Mulloy ceecd51982 Update to 1.9.2 2016-04-02 23:01:19 -04:00
Dan Mulloy c77a700442 Add wrapper for ItemSlot
Closes #183
2016-04-02 14:54:31 -04:00
Dan Mulloy 10ded26d60 Documentation improvements 2016-04-02 14:47:54 -04:00
Dan Mulloy 74adaba8ac Update packet types and IDs for 1.9
A few packet names have changed, they're now deprecated
2016-03-20 17:46:23 -04:00
Dan Mulloy 19c57b0e5e Restore disabled tests, fix block data 2016-03-20 13:35:59 -04:00
Dan Mulloy f561057311 Fix an error with TinyProtocol reflection
Fixes aadnk#103
2016-03-19 22:36:25 -04:00
Dan Mulloy 0e6a7a39a0 Add modifiers for SoundCategory and MobEffectList
Fixes #156
2016-03-19 22:30:01 -04:00
Dan Mulloy 13b905e762 Fix a few issues with loading 2016-03-19 16:41:02 -04:00
Dan Mulloy b4c0cc5a27 Split API off into a separate module 2016-03-19 16:01:38 -04:00
Dan Mulloy a042c8bb3c Update the dig type enum for 1.9 2016-03-12 15:54:57 -05:00
Dan Mulloy 286a642739 Fix false update notifications with beta releases
Fixes #173
2016-03-12 15:51:12 -05:00
Dan Mulloy 513a83c4b0 Fix block positions in watchers and issues with components
Fixes #170
2016-03-12 15:35:34 -05:00
Dan Mulloy 9b2c8826b1 Fix a typo
That wouldn't be a terrible name for the class though
2016-03-11 14:15:14 -05:00
Dan Mulloy 413a3c27a4 Fix an issue with deep cloning data watchers
Fixes #172
2016-03-11 14:06:43 -05:00
Dan Mulloy c9328d98d7 Return to normal versioning 2016-03-09 22:02:35 -05:00
Dan Mulloy f6c0751ce3 Update version to 3.7.0-BETA for beta release 2016-03-07 19:08:16 -05:00
Dan Mulloy 16c7ebef73 A few minor tweaks 2016-03-07 19:05:58 -05:00
Dan Mulloy ec38ac7292 Revert changes to EntityUtilities
Citizens fixed the problem on their end
2016-03-06 18:32:39 -05:00
Dan Mulloy b8497a91a6 Disable updater tests
They're too prone to failure due to HTTP errors
2016-03-06 14:11:56 -05:00
Dan Mulloy 5115bcaf98 Add an optional cloner, fix sound cloning, clean up the data watcher 2016-03-06 14:02:45 -05:00
Dan Mulloy 7077037bb7 The serializer can be null
But this will cause issues when trying to register objects
2016-03-05 23:53:55 -05:00
Dan Mulloy 11ba322b5f Fix an issue with cloning metadata packets
Also improved some documentation and tests
2016-03-05 23:28:48 -05:00
Dan Mulloy a39a37ec51 Allow new entries to be created in the data watcher
Addresses #160
2016-03-05 20:36:54 -05:00
Dan Mulloy ec8fa0d1fe Add wrapper for MinecraftKey, more work on data watchers 2016-03-04 21:24:12 -05:00
Dan Mulloy 5efc4ffee3 Update player actions for 1.9
Fixes #161
2016-03-04 17:32:43 -05:00
Dan Mulloy c2d028df4f Fix a few issues with data watchers 2016-03-04 17:16:50 -05:00
Dan Mulloy 9b838df17d Fix a few errors with data watchers
This is why we have unit tests lol
2016-03-03 21:27:05 -05:00
Dan Mulloy a20ff4645d Add wrappers for data watcher objects, serializers, and the registry
Thanks @NavidK0 for help with the design
2016-03-03 21:18:51 -05:00
Dan Mulloy 850e3431da Restore a few more methods, add some documentation 2016-03-02 20:47:00 -05:00
Dan Mulloy 072a60516e Update particles for 1.9 2016-03-02 18:48:47 -05:00
Dan Mulloy 9b3feb995a Restore some methods to the data watcher wrappers 2016-03-02 18:27:12 -05:00
Dan Mulloy d359f7455f [Breaking] Update data watchers for 1.9
Basically I had to rewrite the classes since Mojang decided to muck the
whole thing up. If there are any methods necessary, I'll try to readd
them.
2016-03-02 18:08:01 -05:00
Dan Mulloy 4987cd188a Comment out data watcher exception
There's a chance that the fallbacks will actually work properly, it's
worth a try until I can find a permanent solution.
2016-03-01 18:34:35 -05:00
Dan Mulloy 019e45572d 1.9 update: Fix entity modifiers
Fixes #153
2016-03-01 16:07:59 -05:00
Dan Mulloy d87e877a27 Update internal versioning for 1.9
It looks like most things are stable at this point, just have to figure
out data watchers
2016-02-29 19:15:54 -05:00
Dan Mulloy 05c7da17b9 1.9 update: It compiles!
Note: This is an untested build, use with caution!
2016-02-29 18:55:29 -05:00
Dan Mulloy 8c21f75e6d Update readme links 2016-02-27 22:48:54 -05:00
Dan Mulloy 615a4e6e37 Use the correct Gson class in 1.7.10 2016-02-27 22:48:11 -05:00
Dan Mulloy 11ca1bf51e Merge pull request #150 from iNevet/master
Add the actual creation of the packet
2016-02-25 13:51:15 -05:00
iNevet 7fabfad65d Better code example 2016-02-23 15:28:14 +08:00
Dan Mulloy ce4779f49d Revert "Fix spelling mistake in PacketAdapter"
This reverts commit cc4d035b63.
2016-02-19 12:56:57 -05:00
Dan Mulloy a88b919e60 Make update checking more robust 2016-02-17 21:48:24 -05:00
Dan Mulloy cc4d035b63 Fix spelling mistake in PacketAdapter
Fixes #113

Thanks @Sheigutn and @NavidK0
2016-02-17 21:10:25 -05:00
Dan Mulloy 12b78eb1d7 Switch to Java 7
This shouldn't be an issue as Java 6 has been EOL'd for years now. Heck,
even Java 7 has been EOL'd

Also updated maven plugin versions
2016-02-17 21:03:26 -05:00
Dan Mulloy 8a5267ab76 Update version to 3.7-SNAPSHOT 2016-02-17 20:52:11 -05:00
Dan Mulloy a189947617 Fix issue with entity tracking on PaperSpigot 2016-02-16 15:43:23 -05:00
Dan Mulloy f01b3999ff Update version to 3.6.6-SNAPSHOT 2016-02-16 15:41:55 -05:00
373 changed files with 14559 additions and 16876 deletions

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
Contributing to ProtocolLib
===========================
This page will detail specific things that must be done if you intend to contribute to this project.
## Updating ProtocolLib to a New Protocol Version
#### Before we get started:
1. **When do we need to update the protocol version?**
_A Minecraft protocol version is a number for each specification of the Minecraft protocol.
This means it is incremented when there is a change to the [Protocol Specification](http://wiki.vg/Protocol),
but not necessarily for each Minecraft version update. If the latest Minecraft version contains a protocol change,
that means ProtocolLib needs to be updated._
2. **When do we need to update other parts of ProtocolLib?**
_When the package version guard changes. The package version guard is the version string inserted
into the package identifier for CraftBukkit and net.minecraft.server (e.g. v1_9_R2). It is incremented
by Spigot if there is a major breaking change in the internal server code. This is to encourage plugins
that interact directly with CraftBukkit/nms code to update more specifically. However, since most of
ProtocolLib uses reflection, and the structure of packet classes is generally static, one will have
to go through the code and change imports from nms/CraftBukkit._
#### Ready? Let's get started!
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [freenode.net](http://freenode.net)
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
2. Search for usages of the now-defunct NMS package guard and change them.
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
(or had their ID changed), make sure to update this list. If a packet was removed
in favor of usage of another packet, instead of removing it, move it to the bottom of the list
in its section, add a deprecation warning to it, and redirect it to the packet that replaced it.
4. `mvn` in the root directory to build the project.
5. If tests fail in the maven build, go through the tests to make sure you removed references to any removed packets
and changed the NMS version guards.
6. Increment the package version in `com.comphenix.protocol.utility.Constants`.
7. `com.comphenix.protocol.ProtocolLibrary` contains several constants that must be updated, including the Minecraft version
and the release date.
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
If the protocol version has been incremented, add a new line to the map.
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
the build fails, debug!
+11
View File
@@ -0,0 +1,11 @@
_Follow this template except for feature requests. Use pastebin when providing /protocol dump and any relevant errors._
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:
+4
View File
@@ -7,6 +7,10 @@
.classpath
.settings/
bin/
.idea/
*.iml
.ipr
.iws
*.pydevproject
.metadata
+2 -3
View File
@@ -1,9 +1,8 @@
language: java
jdk:
- oraclejdk8
- oraclejdk7
script: mvn clean test -U
before_install: cd ProtocolLib
script: mvn clean test
#before_install: cd ProtocolLib
install: true
notifications:
email:
-167
View File
@@ -1,167 +0,0 @@
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
target/
.gitignore
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
*.dotCover
## TODO: If you have NuGet Package Restore enabled, uncomment this
#packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store
/target
/target
@@ -1,58 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol;
import org.bukkit.Bukkit;
/**
* Ignore this class.
*
* @author Kristian
*/
public class Application {
private static Thread mainThread;
private static boolean primaryMethod = true;
public static void main(String[] args) {
// For now, though we might consider making a proper application
System.out.println("This is a Bukkit library. Place it in the plugin-folder and restart the server!");
}
/**
* Determine if we are running on the main thread.
* @return TRUE if we are, FALSE otherwise.
*/
public static boolean isPrimaryThread() {
if (primaryMethod) {
try {
return Bukkit.isPrimaryThread();
} catch (LinkageError e) {
primaryMethod = false;
}
}
// Fallback method
return Thread.currentThread().equals(mainThread);
}
/**
* Register the calling thread as the primary thread.
*/
static void registerPrimaryThread() {
mainThread = Thread.currentThread();
}
}
File diff suppressed because it is too large. Load diff
@@ -1,67 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.guava;
import java.io.DataInputStream;
import java.util.Set;
import java.util.logging.Level;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.google.common.collect.Range;
/**
* @author dmulloy2
*/
public class Guava {
private static GuavaCompat compat;
static {
try {
Range.closed(1, 2);
compat = new Guava17();
} catch (Throwable ex) {
try {
Class<?> clazz = Class.forName("com.comphenix.protocol.compat.guava.Guava10");
compat = (GuavaCompat) clazz.newInstance();
} catch (Throwable ex1) {
ProtocolLibrary.log(Level.SEVERE, "Failed to create Guava 10 compat:", ex1);
}
}
}
private static GuavaCompat getCompat() {
return compat;
}
public static <C extends Comparable<C>> Range<C> closedRange(C lower, C upper) {
return getCompat().closedRange(lower, upper);
}
public static <C extends Comparable<C>> Range<C> singleton(C singleton) {
return getCompat().singletonRange(singleton);
}
public static Set<Integer> toSet(Range<Integer> range) {
return getCompat().toSet(range);
}
public static DataInputStream addHeader(DataInputStream input, PacketType type) {
return getCompat().addHeader(input, type);
}
}
@@ -1,76 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.guava;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Range;
import com.google.common.io.ByteSource;
/**
* @author dmulloy2
*/
public class Guava17 implements GuavaCompat {
@Override
public <C extends Comparable<C>> Range<C> closedRange(C lower, C upper) {
return Range.closed(lower, upper);
}
@Override
public <C extends Comparable<C>> Range<C> singletonRange(C singleton) {
return Range.singleton(singleton);
}
@Override
public Set<Integer> toSet(Range<Integer> range) {
return ContiguousSet.create(range, DiscreteDomain.integers());
}
@Override
public DataInputStream addHeader(final DataInputStream input, final PacketType type) {
ByteSource header = new ByteSource() {
@Override
public InputStream openStream() throws IOException {
byte[] data = new byte[] { (byte) type.getLegacyId() };
return new ByteArrayInputStream(data);
}
};
ByteSource data = new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return input;
}
};
// Combine them into a single stream
try {
return new DataInputStream(ByteSource.concat(header, data).openStream());
} catch (IOException e) {
throw new RuntimeException("Cannot add header.", e);
}
}
}
@@ -1,38 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.guava;
import java.io.DataInputStream;
import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.google.common.collect.Range;
/**
* @author dmulloy2
*/
public interface GuavaCompat {
<C extends Comparable<C>> Range<C> closedRange(C lower, C upper);
<C extends Comparable<C>> Range<C> singletonRange(C singleton);
Set<Integer> toSet(Range<Integer> range);
DataInputStream addHeader(DataInputStream input, PacketType type);
}
@@ -1,28 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import com.comphenix.protocol.injector.netty.Injector;
/**
* @author dmulloy2
*/
public interface ChannelInjector extends Injector {
WrappedChannel getChannel();
}
@@ -1,103 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import java.util.List;
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.Iterables;
import com.google.common.collect.Lists;
/**
* @author dmulloy2
*/
public class LegacyProtocolRegistry extends ProtocolRegistry {
public LegacyProtocolRegistry() {
super();
}
@Override
protected void initialize() {
final Object[] protocols = enumProtocol.getEnumConstants();
List<Map<Integer, Class<?>>> serverMaps = Lists.newArrayList();
List<Map<Integer, Class<?>>> clientMaps = Lists.newArrayList();
StructureModifier<Object> modifier = null;
// Result
Register result = new Register();
for (Object protocol : protocols) {
if (modifier == null)
modifier = new StructureModifier<Object>(protocol.getClass().getSuperclass(), false);
StructureModifier<Map<Integer, Class<?>>> maps = modifier.withTarget(protocol).withType(Map.class);
serverMaps.add(maps.read(0));
clientMaps.add(maps.read(1));
}
// Maps we have to occationally check have changed
for (Map<Integer, Class<?>> map : Iterables.concat(serverMaps, clientMaps)) {
result.containers.add(new MapContainer(map));
}
// Heuristic - there are more server packets than client packets
if (sum(clientMaps) > sum(serverMaps)) {
// Swap if this is violated
List<Map<Integer, Class<?>>> temp = serverMaps;
serverMaps = clientMaps;
clientMaps = temp;
}
for (int i = 0; i < protocols.length; i++) {
Enum<?> enumProtocol = (Enum<?>) protocols[i];
Protocol equivalent = Protocol.fromVanilla(enumProtocol);
// Associate known types
associatePackets(result, serverMaps.get(i), equivalent, Sender.SERVER);
associatePackets(result, clientMaps.get(i), 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.fromID(protocol, sender, entry.getKey(), entry.getValue());
try {
register.typeToClass.put(type, entry.getValue());
if (sender == Sender.SERVER)
register.serverPackets.add(type);
if (sender == Sender.CLIENT)
register.clientPackets.add(type);
} catch (IllegalArgumentException ex) {
// Sometimes this happens with fake packets, just ignore it
}
}
}
}
@@ -1,99 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.logging.Level;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.compat.netty.independent.IndependentNetty;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
/**
* @author dmulloy2
*/
public class Netty {
private static NettyCompat compat;
static {
try {
Class.forName("io.netty.buffer.ByteBuf");
compat = new IndependentNetty();
} catch (ClassNotFoundException ex) {
try {
Class<?> clazz = Class.forName("com.comphenix.protocol.compat.netty.shaded.ShadedNetty");
compat = (NettyCompat) clazz.newInstance();
} catch (Exception ex1) {
ProtocolLibrary.log(Level.SEVERE, "Failed to create legacy netty compat:", ex1);
}
}
}
private static NettyCompat getCompat() {
return compat;
}
public static boolean isIndependent() {
return compat instanceof IndependentNetty;
}
public static WrappedByteBuf createPacketBuffer() {
return getCompat().createPacketBuffer();
}
public static Class<?> getGenericFutureListenerArray() {
return getCompat().getGenericFutureListenerArray();
}
public static Class<?> getChannelHandlerContext() {
return getCompat().getChannelHandlerContext();
}
public static String toEncodedText(CompressedImage image) {
return getCompat().toEncodedText(image);
}
public static WrappedByteBuf decode(byte[] encoded) {
return getCompat().decode(encoded);
}
public static ProtocolInjector getProtocolInjector(Plugin library, PacketFilterManager packetFilterManager, ErrorReporter reporter) {
return getCompat().getProtocolInjector(library, packetFilterManager, reporter);
}
public static WrappedByteBuf packetReader(DataInputStream input) {
return getCompat().packetReader(input);
}
public static WrappedByteBuf packetWriter(DataOutputStream output) {
return getCompat().packetWriter(output);
}
public static WrappedByteBuf copiedBuffer(byte[] array) {
return getCompat().copiedBuffer(array);
}
public static WrappedByteBuf buffer() {
return getCompat().buffer();
}
}
@@ -1,55 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
/**
* @author dmulloy2
*/
// TODO: Sort out packet readers/writers
public interface NettyCompat {
WrappedByteBuf createPacketBuffer();
WrappedByteBuf copiedBuffer(byte[] array);
WrappedByteBuf buffer();
Class<?> getGenericFutureListenerArray();
Class<?> getChannelHandlerContext();
String toEncodedText(CompressedImage image);
WrappedByteBuf decode(byte[] encoded);
ProtocolInjector getProtocolInjector(Plugin plugin, ListenerInvoker invoker, ErrorReporter reporter);
WrappedByteBuf packetReader(DataInputStream input);
WrappedByteBuf packetWriter(DataOutputStream output);
}
@@ -1,38 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import com.comphenix.protocol.injector.netty.ChannelListener;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
/**
* @author dmulloy2
*/
public interface ProtocolInjector extends ChannelListener {
PlayerInjectionHandler getPlayerInjector();
PacketInjector getPacketInjector();
void setDebug(boolean debug);
void inject();
void close();
}
@@ -1,46 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @author dmulloy2
*/
public interface WrappedByteBuf {
void writeBytes(ObjectInputStream input, int id) throws IOException;
Object getHandle();
int readableBytes();
void readBytes(ObjectOutputStream output, int readableBytes) throws IOException;
void readBytes(byte[] data);
void writeByte(byte b);
void writeByte(int i);
void writeBytes(byte[] bytes);
byte[] array();
}
@@ -1,26 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty;
/**
* @author dmulloy2
*/
public interface WrappedChannel {
void writeAndFlush(Object packet);
}
@@ -1,105 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty.independent;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.base64.Base64;
import io.netty.util.concurrent.GenericFutureListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.compat.netty.NettyCompat;
import com.comphenix.protocol.compat.netty.ProtocolInjector;
import com.comphenix.protocol.compat.netty.WrappedByteBuf;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedServerPing.CompressedImage;
import com.google.common.base.Charsets;
/**
* @author dmulloy2
*/
public class IndependentNetty implements NettyCompat {
@Override
public WrappedByteBuf createPacketBuffer() {
ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.buffer();
Class<?> packetSerializer = MinecraftReflection.getPacketDataSerializerClass();
try {
return new NettyByteBuf((ByteBuf) packetSerializer.getConstructor(ByteBuf.class).newInstance(buffer));
} catch (Exception e) {
throw new RuntimeException("Cannot construct packet serializer.", e);
}
}
@Override
public Class<?> getGenericFutureListenerArray() {
return GenericFutureListener[].class;
}
@Override
public Class<?> getChannelHandlerContext() {
return ChannelHandlerContext.class;
}
@Override
public String toEncodedText(CompressedImage image) {
final ByteBuf buffer = Unpooled.wrappedBuffer(image.getDataCopy());
String computed = "data:" + image.getMime() + ";base64," +
Base64.encode(buffer).toString(Charsets.UTF_8);
return computed;
}
@Override
public WrappedByteBuf decode(byte[] encoded) {
return new NettyByteBuf(Base64.decode(Unpooled.wrappedBuffer(encoded)));
}
@Override
public ProtocolInjector getProtocolInjector(Plugin plugin, ListenerInvoker invoker, ErrorReporter reporter) {
return new NettyProtocolInjector(plugin, invoker, reporter);
}
@Override
public WrappedByteBuf packetReader(DataInputStream input) {
return new NettyByteBuf(NettyByteBufAdapter.packetReader(input));
}
@Override
public WrappedByteBuf packetWriter(DataOutputStream output) {
return new NettyByteBuf(NettyByteBufAdapter.packetWriter(output));
}
@Override
public WrappedByteBuf copiedBuffer(byte[] array) {
return new NettyByteBuf(Unpooled.copiedBuffer(array));
}
@Override
public WrappedByteBuf buffer() {
return new NettyByteBuf(Unpooled.buffer());
}
}
@@ -1,83 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty.independent;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import com.comphenix.protocol.compat.netty.WrappedByteBuf;
/**
* @author dmulloy2
*/
public class NettyByteBuf implements WrappedByteBuf {
private WeakReference<ByteBuf> handle;
public NettyByteBuf(ByteBuf handle) {
this.handle = new WeakReference<ByteBuf>(handle);
}
@Override
public void writeBytes(ObjectInputStream input, int id) throws IOException {
handle.get().writeBytes(input, id);
}
@Override
public Object getHandle() {
return handle.get();
}
@Override
public int readableBytes() {
return handle.get().readableBytes();
}
@Override
public void readBytes(ObjectOutputStream output, int readableBytes) throws IOException {
handle.get().readBytes(output, readableBytes);
}
@Override
public void readBytes(byte[] data) {
handle.get().readBytes(data);
}
@Override
public void writeByte(byte b) {
handle.get().writeByte(b);
}
@Override
public void writeByte(int i) {
handle.get().writeByte(i);
}
@Override
public void writeBytes(byte[] bytes) {
handle.get().writeBytes(bytes);
}
@Override
public byte[] array() {
return handle.get().array();
}
}
@@ -1,55 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import com.comphenix.protocol.compat.netty.WrappedByteBuf;
/**
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
public int getId() {
return id;
}
public byte[] getBytes() {
return bytes;
}
public void writeId(WrappedByteBuf output) {
int i = id;
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
}
output.writeByte(i);
}
public void writeBytes(WrappedByteBuf output) {
output.writeBytes(bytes);
}
}
@@ -1,753 +0,0 @@
/*
* Copyright 2011-2013 Tyler Blair. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and contributors and should not be interpreted as representing official policies,
* either expressed or implied, of anybody else.
*/
package com.comphenix.protocol.metrics;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.utility.WrappedScheduler;
public class Metrics {
/**
* The current revision number
*/
private final static int REVISION = 7;
/**
* The base url of the metrics domain
*/
private static final String BASE_URL = "http://report.mcstats.org";
/**
* The url used to report a server's status
*/
private static final String REPORT_URL = "/plugin/%s";
/**
* Interval of time to ping (in minutes)
*/
private static final int PING_INTERVAL = 15;
/**
* The plugin this metrics submits for
*/
private final Plugin plugin;
/**
* All of the custom graphs to submit to metrics
*/
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
/**
* The plugin configuration file
*/
private final YamlConfiguration configuration;
/**
* The plugin configuration file
*/
private final File configurationFile;
/**
* Unique server id
*/
private final String guid;
/**
* Debug mode
*/
private final boolean debug;
/**
* Lock for synchronization
*/
private final Object optOutLock = new Object();
/**
* The scheduled task
*/
private volatile WrappedScheduler.TaskWrapper task = null;
public Metrics(final Plugin plugin) throws IOException {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null");
}
this.plugin = plugin;
// load the config
configurationFile = getConfigFile();
configuration = YamlConfiguration.loadConfiguration(configurationFile);
// add some defaults
configuration.addDefault("opt-out", false);
configuration.addDefault("guid", UUID.randomUUID().toString());
configuration.addDefault("debug", false);
// Do we need to create the file?
if (configuration.get("guid", null) == null) {
configuration.options().header("http://mcstats.org").copyDefaults(true);
configuration.save(configurationFile);
}
// Load the guid then
guid = configuration.getString("guid");
debug = configuration.getBoolean("debug", false);
}
/**
* Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
* website. Plotters can be added to the graph object returned.
*
* @param name The name of the graph
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
*/
public Graph createGraph(final String name) {
if (name == null) {
throw new IllegalArgumentException("Graph name cannot be null");
}
// Construct the graph object
final Graph graph = new Graph(name);
// Now we can add our graph
graphs.add(graph);
// and return back
return graph;
}
/**
* Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
*
* @param graph The name of the graph
*/
public void addGraph(final Graph graph) {
if (graph == null) {
throw new IllegalArgumentException("Graph cannot be null");
}
graphs.add(graph);
}
/**
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
* initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
* ticks.
*
* @return True if statistics measuring is running, otherwise false.
*/
public boolean start() {
synchronized (optOutLock) {
// Did we opt out?
if (isOptOut()) {
return false;
}
// Is metrics already running?
if (task != null) {
return true;
}
// Begin hitting the server with glorious data
task = WrappedScheduler.runAsynchronouslyRepeat(plugin, new Runnable() {
private boolean firstPost = true;
@Override
public void run() {
try {
// This has to be synchronized or it can collide with the disable method.
synchronized (optOutLock) {
// Disable Task, if it is running and the server owner decided to opt-out
if (isOptOut() && task != null) {
task.cancel();
task = null;
// Tell all plotters to stop gathering information.
for (Graph graph : graphs) {
graph.onOptOut();
}
}
}
// We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING!
postPlugin(!firstPost);
// After the first post we set firstPost to false
// Each post thereafter will be a ping
firstPost = false;
} catch (IOException e) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
}
}
}
}, 0, PING_INTERVAL * 1200);
return true;
}
}
/**
* Has the server owner denied plugin metrics?
*
* @return true if metrics should be opted out of it
*/
public boolean isOptOut() {
synchronized (optOutLock) {
try {
// Reload the metrics file
configuration.load(getConfigFile());
} catch (IOException ex) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
} catch (InvalidConfigurationException ex) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
}
return configuration.getBoolean("opt-out", false);
}
}
/**
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
*
* @throws IOException If configuration saving fails
*/
public void enable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized (optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if (isOptOut()) {
configuration.set("opt-out", false);
configuration.save(configurationFile);
}
// Enable Task, if it is not running
if (task == null) {
start();
}
}
}
/**
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
*
* @throws IOException If configuration saving fails
*/
public void disable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized (optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if (!isOptOut()) {
configuration.set("opt-out", true);
configuration.save(configurationFile);
}
// Disable Task, if it is running
if (task != null) {
task.cancel();
task = null;
}
}
}
/**
* Gets the File object of the config file that should be used to store data such as the GUID and opt-out status
*
* @return the File object for the config file
*/
public File getConfigFile() {
// I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use
// is to abuse the plugin object we already have
// plugin.getDataFolder() => base/plugins/PluginA/
// pluginsFolder => base/plugins/
// The base is not necessarily relative to the startup directory.
File pluginsFolder = plugin.getDataFolder().getParentFile();
// return => base/plugins/PluginMetrics/config.yml
return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml");
}
/**
* Generic method that posts a plugin to the metrics website
*
* @throws IOException If posting fails
*/
private void postPlugin(final boolean isPing) throws IOException {
// Server software specific section
PluginDescriptionFile description = plugin.getDescription();
String pluginName = description.getName();
boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled
String pluginVersion = trimVersion(description.getVersion()); // ProtocolLib
String serverVersion = Bukkit.getVersion();
int playersOnline = Util.getOnlinePlayers().size(); // ProtocolLib
// END server software specific section -- all code below does not use any code outside of this class / Java
// Construct the post data
StringBuilder json = new StringBuilder(1024);
json.append('{');
// The plugin's description file containg all of the plugin data such as name, version, author, etc
appendJSONPair(json, "guid", guid);
appendJSONPair(json, "plugin_version", pluginVersion);
appendJSONPair(json, "server_version", serverVersion);
appendJSONPair(json, "players_online", Integer.toString(playersOnline));
// New data as of R6
String osname = System.getProperty("os.name");
String osarch = System.getProperty("os.arch");
String osversion = System.getProperty("os.version");
String java_version = System.getProperty("java.version");
int coreCount = Runtime.getRuntime().availableProcessors();
// normalize os arch .. amd64 -> x86_64
if (osarch.equals("amd64")) {
osarch = "x86_64";
}
appendJSONPair(json, "osname", osname);
appendJSONPair(json, "osarch", osarch);
appendJSONPair(json, "osversion", osversion);
appendJSONPair(json, "cores", Integer.toString(coreCount));
appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0");
appendJSONPair(json, "java_version", java_version);
// If we're pinging, append it
if (isPing) {
appendJSONPair(json, "ping", "1");
}
if (graphs.size() > 0) {
synchronized (graphs) {
json.append(',');
json.append('"');
json.append("graphs");
json.append('"');
json.append(':');
json.append('{');
boolean firstGraph = true;
final Iterator<Graph> iter = graphs.iterator();
while (iter.hasNext()) {
Graph graph = iter.next();
StringBuilder graphJson = new StringBuilder();
graphJson.append('{');
for (Plotter plotter : graph.getPlotters()) {
appendJSONPair(graphJson, plotter.getColumnName(), Integer.toString(plotter.getValue()));
}
graphJson.append('}');
if (!firstGraph) {
json.append(',');
}
json.append(escapeJSON(graph.getName()));
json.append(':');
json.append(graphJson);
firstGraph = false;
}
json.append('}');
}
}
// close json
json.append('}');
// Create the url
URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName)));
// Connect to the website
URLConnection connection;
// Mineshafter creates a socks proxy, so we can safely bypass it
// It does not reroute POST requests so we need to go around it
if (isMineshafterPresent()) {
connection = url.openConnection(Proxy.NO_PROXY);
} else {
connection = url.openConnection();
}
byte[] uncompressed = json.toString().getBytes();
byte[] compressed = gzip(json.toString());
// Headers
connection.addRequestProperty("User-Agent", "MCStats/" + REVISION);
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Content-Encoding", "gzip");
connection.addRequestProperty("Content-Length", Integer.toString(compressed.length));
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.setDoOutput(true);
if (debug) {
System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length);
}
// Write the data
OutputStream os = connection.getOutputStream();
os.write(compressed);
os.flush();
// Now read the response
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String response = reader.readLine();
// close resources
os.close();
reader.close();
if (response == null || response.startsWith("ERR") || response.startsWith("7")) {
if (response == null) {
response = "null";
} else if (response.startsWith("7")) {
response = response.substring(response.startsWith("7,") ? 2 : 1);
}
throw new IOException(response);
} else {
// Is this the first update this hour?
if (response.equals("1") || response.contains("This is your first update this hour")) {
synchronized (graphs) {
final Iterator<Graph> iter = graphs.iterator();
while (iter.hasNext()) {
final Graph graph = iter.next();
for (Plotter plotter : graph.getPlotters()) {
plotter.reset();
}
}
}
}
}
}
// Trim the version string to not include the build number
// Not doing this has an interesting effect on the version graph
private String trimVersion(String version) {
return version.contains("-b") ? version.substring(0, version.indexOf("-b")) : version;
}
/**
* GZip compress a string of bytes
*
* @param input Input to compress
* @return Compressed string
*/
public static byte[] gzip(String input) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = null;
try {
gzos = new GZIPOutputStream(baos);
gzos.write(input.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (gzos != null) try {
gzos.close();
} catch (IOException ignore) {
}
}
return baos.toByteArray();
}
/**
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
*
* @return true if mineshafter is installed on the server
*/
private boolean isMineshafterPresent() {
try {
Class.forName("mineshafter.MineServer");
return true;
} catch (Exception e) {
return false;
}
}
// Appends a json encoded key/value pair to the given string builder.
private static void appendJSONPair(StringBuilder json, String key, String value) {
boolean isValueNumeric = false;
try {
if (value.equals("0") || !value.endsWith("0")) {
Double.parseDouble(value);
isValueNumeric = true;
}
} catch (NumberFormatException e) {
isValueNumeric = false;
}
if (json.charAt(json.length() - 1) != '{') {
json.append(',');
}
json.append(escapeJSON(key));
json.append(':');
if (isValueNumeric) {
json.append(value);
} else {
json.append(escapeJSON(value));
}
}
// Escape a string to create a valid JSON string
private static String escapeJSON(String text) {
StringBuilder builder = new StringBuilder();
builder.append('"');
for (int index = 0; index < text.length(); index++) {
char chr = text.charAt(index);
switch (chr) {
case '"':
case '\\':
builder.append('\\');
builder.append(chr);
break;
case '\b':
builder.append("\\b");
break;
case '\t':
builder.append("\\t");
break;
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
default:
if (chr < ' ') {
String t = "000" + Integer.toHexString(chr);
builder.append("\\u" + t.substring(t.length() - 4));
} else {
builder.append(chr);
}
break;
}
}
builder.append('"');
return builder.toString();
}
// Encode text as UTF-8
private static String urlEncode(final String text) throws UnsupportedEncodingException {
return URLEncoder.encode(text, "UTF-8");
}
/**
* Represents a custom graph on the website
*/
public static class Graph {
/**
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
* rejected
*/
private final String name;
/**
* The set of plotters that are contained within this graph
*/
private final Set<Plotter> plotters = new LinkedHashSet<Plotter>();
private Graph(final String name) {
this.name = name;
}
/**
* Gets the graph's name
*
* @return the Graph's name
*/
public String getName() {
return name;
}
/**
* Add a plotter to the graph, which will be used to plot entries
*
* @param plotter the plotter to add to the graph
*/
public void addPlotter(final Plotter plotter) {
plotters.add(plotter);
}
/**
* Remove a plotter from the graph
*
* @param plotter the plotter to remove from the graph
*/
public void removePlotter(final Plotter plotter) {
plotters.remove(plotter);
}
/**
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
*
* @return an unmodifiable {@link java.util.Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(plotters);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Graph)) {
return false;
}
final Graph graph = (Graph) object;
return graph.name.equals(name);
}
/**
* Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
*/
protected void onOptOut() {
}
}
/**
* Interface used to collect custom data for a plugin
*/
public static abstract class Plotter {
/**
* The plot's name
*/
private final String name;
/**
* Construct a plotter with the default plot name
*/
public Plotter() {
this("Default");
}
/**
* Construct a plotter with a specific plot name
*
* @param name the name of the plotter to use, which will show up on the website
*/
public Plotter(final String name) {
this.name = name;
}
/**
* Get the current value for the plotted point. Since this function defers to an external function it may or may
* not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
* from any thread so care should be taken when accessing resources that need to be synchronized.
*
* @return the current value for the point to be plotted.
*/
public abstract int getValue();
/**
* Get the column name for the plotted point
*
* @return the plotted point's column name
*/
public String getColumnName() {
return name;
}
/**
* Called after the website graphs have been updated
*/
public void reset() {
}
@Override
public int hashCode() {
return getColumnName().hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Plotter)) {
return false;
}
final Plotter plotter = (Plotter) object;
return plotter.name.equals(name) && plotter.getValue() == getValue();
}
}
}
@@ -1,109 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Map;
import java.util.Map.Entry;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.google.common.collect.Maps;
/**
* Represents an object that can clone a specific list of Bukkit- and Minecraft-related objects.
*
* @author Kristian
*/
public class BukkitCloner implements Cloner {
// List of classes we support
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
public BukkitCloner() {
addClass(0, MinecraftReflection.getItemStackClass());
addClass(1, MinecraftReflection.getDataWatcherClass());
// Try to add position classes
try {
addClass(2, MinecraftReflection.getBlockPositionClass());
} catch (Throwable ex) {
}
try {
addClass(3, MinecraftReflection.getChunkPositionClass());
} catch (Throwable ex) {
}
if (MinecraftReflection.isUsingNetty()) {
addClass(4, MinecraftReflection.getServerPingClass());
}
}
private void addClass(int id, Class<?> clazz) {
if (clazz != null)
clonableClasses.put(id, clazz);
}
private int findMatchingClass(Class<?> type) {
// See if is a subclass of any of our supported superclasses
for (Entry<Integer, Class<?>> entry : clonableClasses.entrySet()) {
if (entry.getValue().isAssignableFrom(type)) {
return entry.getKey();
}
}
return -1;
}
@Override
public boolean canClone(Object source) {
if (source == null)
return false;
return findMatchingClass(source.getClass()) >= 0;
}
@Override
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
// Convert to a wrapper
switch (findMatchingClass(source.getClass())) {
case 0:
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
case 1:
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
return dataConverter.getGeneric(clonableClasses.get(1), dataConverter.getSpecific(source).deepClone());
case 2:
EquivalentConverter<BlockPosition> blockConverter = BlockPosition.getConverter();
return blockConverter.getGeneric(clonableClasses.get(2), blockConverter.getSpecific(source));
case 3:
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
return chunkConverter.getGeneric(clonableClasses.get(3), chunkConverter.getSpecific(source));
case 4:
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
}
}
}
@@ -1,57 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.ClassVisitor;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.MethodVisitor;
public abstract class EmptyClassVisitor implements ClassVisitor {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// NOP
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// NOP
return null;
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// NOP
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// NOP
return null;
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
// NOP
}
@Override
public void visitSource(String source, String debug) {
// NOP
}
}
@@ -1,132 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
public class EmptyMethodVisitor implements MethodVisitor {
@Override
public AnnotationVisitor visitAnnotationDefault() {
// NOP
return null;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitCode() {
// NOP
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
// NOP
}
@Override
public void visitInsn(int opcode) {
// NOP
}
@Override
public void visitIntInsn(int opcode, int operand) {
// NOP
}
@Override
public void visitVarInsn(int opcode, int var) {
// NOP
}
@Override
public void visitTypeInsn(int opcode, String type) {
// NOP
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitJumpInsn(int opcode, Label label) {
// NOP
}
@Override
public void visitLabel(Label label) {
// NOP
}
@Override
public void visitLdcInsn(Object cst) {
// NOP
}
@Override
public void visitIincInsn(int var, int increment) {
// NOP
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
// NOP
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
// NOP
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
// NOP
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
// NOP
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
// NOP
}
@Override
public void visitLineNumber(int line, Label start) {
// NOP
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
}
@@ -1,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();
}
};
}
}
@@ -1,166 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import org.bukkit.Material;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a wrapper around IBlockData.
*
* @author dmulloy2
*/
public class WrappedBlockData extends AbstractWrapper {
private static final Class<?> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
private static final Class<?> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
private static final Class<?> BLOCK = MinecraftReflection.getBlockClass();
private static MethodAccessor FROM_LEGACY_DATA = null;
private static MethodAccessor TO_LEGACY_DATA = null;
private static MethodAccessor GET_NMS_BLOCK = null;
private static MethodAccessor GET_BLOCK = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(int.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(int.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[] { Material.class }));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[0]));
}
public WrappedBlockData(Object handle) {
super(IBLOCK_DATA);
setHandle(handle);
}
/**
* Retrieves the type of this BlockData.
* @return The type of this BlockData.
*/
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockConverter().getSpecific(block);
}
/**
* Retrieves the type id of this BlockData.
* @return The type id of this BlockData.
* @deprecated ID's are deprecated
*/
@Deprecated
public int getTypeId() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockIDConverter().getSpecific(block);
}
/**
* Retrieves the data of this BlockData.
* @return The data of this BlockData.
*/
public int getData() {
Object block = GET_BLOCK.invoke(handle);
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
}
/**
* Sets the type of this BlockData.
* @param type New type
*/
public void setType(Material type) {
setTypeAndData(type, 0);
}
/**
* Sets the data of this BlockData.
* @param data New data
*/
public void setData(int data) {
setTypeAndData(getType(), data);
}
/**
* Sets the type and data of this BlockData.
* @param type New type
* @param data New data
*/
public void setTypeAndData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
setHandle(blockData);
}
/**
* Creates a new BlockData instance with the given type and no data.
* @param type Block type
* @return New BlockData
*/
public static WrappedBlockData createData(Material type) {
return createData(type, 0);
}
/**
* Creates a new BlockData instance with the given type and data.
* @param type Block type
* @param data Block data
* @return New BlockData
*/
public static WrappedBlockData createData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
return new WrappedBlockData(blockData);
}
@Override
public String toString() {
return "WrappedBlockData[handle=" + handle + "]";
}
@Override
public boolean equals(Object o) {
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.getType() == that.getType();
}
return false;
}
}
@@ -1,924 +0,0 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nullable;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FieldAccessException;
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;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.ReadOnlyFieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
/**
* Wraps a DataWatcher that is used to transmit arbitrary key-value pairs with a given entity.
*
* @author Kristian
*/
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
/**
* Every custom watchable type in the Spigot protocol hack.
* @author Kristian
*/
public enum CustomType {
BYTE_SHORT("org.spigotmc.ProtocolData$ByteShort", 0, short.class),
DUAL_BYTE("org.spigotmc.ProtocolData$DualByte", 0, byte.class, byte.class),
HIDDEN_BYTE("org.spigotmc.ProtocolData$HiddenByte", 0, byte.class),
INT_BYTE("org.spigotmc.ProtocolData$IntByte", 2, int.class, byte.class),
DUAL_INT("org.spigotmc.ProtocolData$DualInt", 2, int.class, int.class);
private Class<?> spigotClass;
private ConstructorAccessor constructor;
private FieldAccessor secondaryValue;
private int typeId;
private CustomType(String className, int typeId, Class<?>... parameters) {
try {
this.spigotClass = Class.forName(className);
this.constructor = Accessors.getConstructorAccessor(spigotClass, parameters);
this.secondaryValue = parameters.length > 1 ? Accessors.getFieldAccessor(spigotClass, "value2", true) : null;
} catch (ClassNotFoundException e) {
// ProtocolLibrary.log(Level.WARNING, "Unable to find " + className);
this.spigotClass = null;
}
this.typeId = typeId;
}
/**
* Construct a new instance of this Spigot type.
* @param value - the value. Cannot be NULL.
* @return The instance to construct.
*/
Object newInstance(Object value) {
return newInstance(value, null);
}
/**
* Construct a new instance of this Spigot type.
* <p>
* The secondary value may be NULL if this custom type does not contain a secondary value.
* @param value - the value.
* @param secondary - optional secondary value.
* @return
*/
Object newInstance(Object value, Object secondary) {
Preconditions.checkNotNull(value, "value cannot be NULL.");
if (hasSecondary()) {
return constructor.invoke(value, secondary);
} else {
if (secondary != null) {
throw new IllegalArgumentException("Cannot construct " + this + " with a secondary value");
}
return constructor.invoke(value);
}
}
/**
* Set the secondary value of a given type.
* @param instance - the instance.
* @param secondary - the secondary value.
*/
void setSecondary(Object instance, Object secondary) {
if (!hasSecondary()) {
throw new IllegalArgumentException(this + " does not have a secondary value.");
}
secondaryValue.set(instance, secondary);
}
/**
* Get the secondary value of a type.
* @param instance - the instance.
* @return The secondary value.
*/
Object getSecondary(Object instance) {
if (!hasSecondary()) {
throw new IllegalArgumentException(this + " does not have a secondary value.");
}
return secondaryValue.get(instance);
}
/**
* Determine if this type has a secondary value.
* @return TRUE if it does, FALSE otherwise.
*/
public boolean hasSecondary() {
return secondaryValue != null;
}
/**
* Underlying Spigot class.
* @return The class.
*/
public Class<?> getSpigotClass() {
return spigotClass;
}
/**
* The equivalent type ID.
* @return The equivalent ID.
*/
public int getTypeId() {
return typeId;
}
/**
* Retrieve the custom Spigot type of a value.
* @param value - the value.
* @return The Spigot type, or NULL if not found.
*/
public static CustomType fromValue(Object value) {
for (CustomType type : CustomType.values()) {
if (type.getSpigotClass().isInstance(value)) {
return type;
}
}
return null;
}
}
/**
* Used to assign integer IDs to given types.
*/
private static Map<Class<?>, Integer> TYPE_MAP;
/**
* Custom types in the bountiful update.
*/
private static Map<Class<?>, Integer> CUSTOM_MAP;
// Accessors
private static FieldAccessor TYPE_MAP_ACCESSOR;
private static FieldAccessor VALUE_MAP_ACCESSOR;
// Fields
private static Field READ_WRITE_LOCK_FIELD;
private static Field ENTITY_FIELD;
// Methods
private static Method CREATE_KEY_VALUE_METHOD;
private static Method UPDATE_KEY_VALUE_METHOD;
private static Method GET_KEY_VALUE_METHOD;
// Constructors
private static Constructor<?> CREATE_DATA_WATCHER_CONSTRUCTOR;
// Entity methods
private volatile static Field ENTITY_DATA_FIELD;
/**
* Whether or not this class has already been initialized.
*/
private static boolean HAS_INITIALIZED;
// Lock
private ReadWriteLock readWriteLock;
// Map of watchable objects
private Map<Integer, Object> watchableObjects;
// A map view of all the watchable objects
private Map<Integer, WrappedWatchableObject> mapView;
/**
* Initialize a new data watcher.
* @throws FieldAccessException If we're unable to wrap a DataWatcher.
*/
public WrappedDataWatcher() {
super(MinecraftReflection.getDataWatcherClass());
// Just create a new watcher
try {
if (MinecraftReflection.isUsingNetty()) {
setHandle(newEntityHandle(null));
} else {
setHandle(getHandleType().newInstance());
}
initialize();
} catch (Exception e) {
throw new RuntimeException("Unable to construct DataWatcher.", e);
}
}
/**
* Create a wrapper for a given data watcher.
* @param handle - the data watcher to wrap.
* @throws FieldAccessException If we're unable to wrap a DataWatcher.
*/
public WrappedDataWatcher(Object handle) {
super(MinecraftReflection.getDataWatcherClass());
setHandle(handle);
initialize();
}
/**
* Construct a new data watcher with the given entity.
* <p>
* In 1.6.4 and ealier, this will fall back to using {@link #WrappedDataWatcher()}.
* @param entity - the entity.
* @return The wrapped data watcher.
*/
public static WrappedDataWatcher newWithEntity(Entity entity) {
// Use the old constructor
if (!MinecraftReflection.isUsingNetty())
return new WrappedDataWatcher();
return new WrappedDataWatcher(newEntityHandle(entity));
}
/**
* Construct a new native DataWatcher with the given entity.
* <p>
* Warning: This is only supported in 1.7.2 and above.
* @param entity - the entity, or NULL.
* @return The data watcher.
*/
private static Object newEntityHandle(Entity entity) {
Class<?> dataWatcher = MinecraftReflection.getDataWatcherClass();
try {
if (CREATE_DATA_WATCHER_CONSTRUCTOR == null)
CREATE_DATA_WATCHER_CONSTRUCTOR = dataWatcher.getConstructor(MinecraftReflection.getEntityClass());
return CREATE_DATA_WATCHER_CONSTRUCTOR.newInstance(
BukkitUnwrapper.getInstance().unwrapItem(entity)
);
} catch (Exception e) {
throw new RuntimeException("Cannot construct data watcher.", e);
}
}
/**
* Create a new data watcher for a list of watchable objects.
* <p>
* Note that the watchable objects are not cloned, and will be modified in place. Use "deepClone" if
* that is not desirable.
* <p>
* The {@link #removeObject(int)} method will not modify the given list, however.
*
* @param watchableObjects - list of watchable objects that will be copied.
* @throws FieldAccessException Unable to read watchable objects.
*/
public WrappedDataWatcher(List<WrappedWatchableObject> watchableObjects) throws FieldAccessException {
this();
Lock writeLock = getReadWriteLock().writeLock();
Map<Integer, Object> map = getWatchableObjectMap();
writeLock.lock();
try {
// Add the watchable objects by reference
for (WrappedWatchableObject watched : watchableObjects) {
map.put(watched.getIndex(), watched.handle);
}
} finally {
writeLock.unlock();
}
}
/**
* Retrieve the ID of a given type, if it's allowed to be watched.
* @param clazz - Class to check for.
* @return The ID, or NULL if it cannot be watched.
* @throws FieldAccessException If we cannot initialize the reflection machinery.
*/
public static Integer getTypeID(Class<?> clazz) throws FieldAccessException {
initialize();
Integer result = TYPE_MAP.get(WrappedWatchableObject.getUnwrappedType(clazz));
if (result == null) {
result = CUSTOM_MAP.get(clazz);
}
return result;
}
/**
* Retrieve the type of a given ID, if it's allowed to be watched.
* @param id - id to get type for
* @return The type using a given ID, or NULL if it cannot be watched.
* @throws FieldAccessException If we cannot initialize the reflection machinery.
*/
public static Class<?> getTypeClass(int id) throws FieldAccessException {
initialize();
for (Map.Entry<Class<?>, Integer> entry : TYPE_MAP.entrySet()) {
if (Objects.equal(entry.getValue(), id)) {
return entry.getKey();
}
}
// Unknown class type
return null;
}
/**
* Get a watched byte.
* @param index - index of the watched byte.
* @return The watched byte, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public Byte getByte(int index) throws FieldAccessException {
return (Byte) getObject(index);
}
/**
* Get a watched short.
* @param index - index of the watched short.
* @return The watched short, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public Short getShort(int index) throws FieldAccessException {
return (Short) getObject(index);
}
/**
* Get a watched integer.
* @param index - index of the watched integer.
* @return The watched integer, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public Integer getInteger(int index) throws FieldAccessException {
return (Integer) getObject(index);
}
/**
* Get a watched float.
* @param index - index of the watched float.
* @return The watched float, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public Float getFloat(int index) throws FieldAccessException {
return (Float) getObject(index);
}
/**
* Get a watched string.
* @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public String getString(int index) throws FieldAccessException {
return (String) getObject(index);
}
/**
* Get a watched string.
* @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public ItemStack getItemStack(int index) throws FieldAccessException {
return (ItemStack) getObject(index);
}
/**
* Get a watched string.
* @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist.
* @throws FieldAccessException Cannot read underlying field.
*/
public WrappedChunkCoordinate getChunkCoordinate(int index) throws FieldAccessException {
return (WrappedChunkCoordinate) getObject(index);
}
/**
* Retrieve a watchable object by index.
* @param index - index of the object to retrieve.
* @return The watched object.
* @throws FieldAccessException Cannot read underlying field.
*/
public Object getObject(int index) throws FieldAccessException {
Object watchable = getWatchedObject(index);
if (watchable != null) {
return new WrappedWatchableObject(watchable).getValue();
} else {
return null;
}
}
/**
* Retrieve every watchable object in this watcher.
* @return Every watchable object.
* @throws FieldAccessException If reflection failed.
*/
public List<WrappedWatchableObject> getWatchableObjects() throws FieldAccessException {
Lock readLock = getReadWriteLock().readLock();
readLock.lock();
try {
List<WrappedWatchableObject> result = new ArrayList<WrappedWatchableObject>();
// Add each watchable object to the list
for (Object watchable : getWatchableObjectMap().values()) {
if (watchable != null) {
result.add(new WrappedWatchableObject(watchable));
} else {
result.add(null);
}
}
return result;
} finally {
readLock.unlock();
}
}
@Override
public boolean equals(Object obj) {
// Quick checks
if (obj == this)
return true;
if (obj == null)
return false;
if (obj instanceof WrappedDataWatcher) {
WrappedDataWatcher other = (WrappedDataWatcher) obj;
Iterator<WrappedWatchableObject> first = iterator(), second = other.iterator();
// Make sure they're the same size
if (size() != other.size())
return false;
for (; first.hasNext() && second.hasNext(); ) {
// See if the two elements are equal
if (!first.next().equals(second.next()))
return false;
}
return true;
}
return false;
}
@Override
public int hashCode() {
return getWatchableObjects().hashCode();
}
/**
* Retrieve a copy of every index associated with a watched object.
* @return Every watched object index.
* @throws FieldAccessException If we're unable to read the underlying object.
*/
public Set<Integer> indexSet() throws FieldAccessException {
Lock readLock = getReadWriteLock().readLock();
readLock.lock();
try {
return new HashSet<Integer>(getWatchableObjectMap().keySet());
} finally {
readLock.unlock();
}
}
/**
* Clone the content of the current DataWatcher.
* @return A cloned data watcher.
*/
public WrappedDataWatcher deepClone() {
WrappedDataWatcher clone = new WrappedDataWatcher();
// Make a new copy instead
for (WrappedWatchableObject watchable : this) {
clone.setObject(watchable.getIndex(), watchable.getClonedValue());
}
return clone;
}
/**
* Retrieve the number of watched objects.
* @return Watched object count.
* @throws FieldAccessException If we're unable to read the underlying object.
*/
public int size() throws FieldAccessException {
Lock readLock = getReadWriteLock().readLock();
readLock.lock();
try {
return getWatchableObjectMap().size();
} finally {
readLock.unlock();
}
}
/**
* Remove a given object from the underlying DataWatcher.
* @param index - index of the object to remove.
* @return The watchable object that was removed, or NULL If none could be found.
*/
public WrappedWatchableObject removeObject(int index) {
Lock writeLock = getReadWriteLock().writeLock();
writeLock.lock();
try {
Object removed = getWatchableObjectMap().remove(index);
return removed != null ? new WrappedWatchableObject(removed) : null;
} finally {
writeLock.unlock();
}
}
/**
* Set a watched byte.
* @param index - index of the watched byte.
* @param newValue - the new watched value.
* @throws FieldAccessException Cannot read underlying field.
*/
public void setObject(int index, Object newValue) throws FieldAccessException {
setObject(index, newValue, true);
}
/**
* Set a watched byte.
* @param index - index of the watched byte.
* @param newValue - the new watched value.
* @param update - whether or not to refresh every listening client.
* @throws FieldAccessException Cannot read underlying field.
*/
public void setObject(int index, Object newValue, boolean update) throws FieldAccessException {
// Aquire write lock
Lock writeLock = getReadWriteLock().writeLock();
writeLock.lock();
try {
Object watchable = getWatchedObject(index);
if (watchable != null) {
new WrappedWatchableObject(watchable).setValue(newValue, update);
} else {
CREATE_KEY_VALUE_METHOD.invoke(handle, index, WrappedWatchableObject.getUnwrapped(newValue));
}
// Handle invoking the method
} catch (IllegalArgumentException e) {
throw new FieldAccessException("Cannot convert arguments.", e);
} catch (IllegalAccessException e) {
throw new FieldAccessException("Illegal access.", e);
} catch (InvocationTargetException e) {
throw new FieldAccessException("Checked exception in Minecraft.", e);
} finally {
writeLock.unlock();
}
}
/**
* Set a watched byte with an optional secondary value.
* @param index - index of the watched byte.
* @param newValue - the new watched value.
* @param secondary - optional secondary value.
* @param update - whether or not to refresh every listening client.
* @param type - custom type.
* @throws FieldAccessException If we cannot read the underlying field.
*/
public void setObject(int index, Object newValue, Object secondary, boolean update, CustomType type) throws FieldAccessException {
Object created = type.newInstance(newValue, secondary);
// Now update the watcher
setObject(index, created, update);
}
private Object getWatchedObject(int index) throws FieldAccessException {
// We use the get-method first and foremost
if (GET_KEY_VALUE_METHOD != null) {
try {
return GET_KEY_VALUE_METHOD.invoke(handle, index);
} catch (Exception e) {
throw new FieldAccessException("Cannot invoke get key method for index " + index, e);
}
} else {
try {
getReadWriteLock().readLock().lock();
return getWatchableObjectMap().get(index);
} finally {
getReadWriteLock().readLock().unlock();
}
}
}
/**
* Retrieve the current read write lock.
* @return Current read write lock.
* @throws FieldAccessException If we're unable to read the underlying field.
*/
protected ReadWriteLock getReadWriteLock() throws FieldAccessException {
try {
// Cache the read write lock
if (readWriteLock != null)
return readWriteLock;
else if (READ_WRITE_LOCK_FIELD != null)
return readWriteLock = (ReadWriteLock) FieldUtils.readField(READ_WRITE_LOCK_FIELD, handle, true);
else
return readWriteLock = new ReentrantReadWriteLock();
} catch (IllegalAccessException e) {
throw new FieldAccessException("Unable to read lock field.", e);
}
}
/**
* Retrieve the underlying map of key values that stores watchable objects.
* @return A map of watchable objects.
* @throws FieldAccessException If we don't have permission to perform reflection.
*/
@SuppressWarnings("unchecked")
protected Map<Integer, Object> getWatchableObjectMap() throws FieldAccessException {
if (watchableObjects == null)
watchableObjects = (Map<Integer, Object>) VALUE_MAP_ACCESSOR.get(handle);
return watchableObjects;
}
/**
* Retrieve the data watcher associated with an entity.
* @param entity - the entity to read from.
* @return Associated data watcher.
* @throws FieldAccessException Reflection failed.
*/
public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException {
if (ENTITY_DATA_FIELD == null)
ENTITY_DATA_FIELD = FuzzyReflection.fromClass(MinecraftReflection.getEntityClass(), true).
getFieldByType("datawatcher", MinecraftReflection.getDataWatcherClass());
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
try {
Object nsmWatcher = FieldUtils.readField(ENTITY_DATA_FIELD, unwrapper.unwrapItem(entity), true);
if (nsmWatcher != null)
return new WrappedDataWatcher(nsmWatcher);
else
return null;
} catch (IllegalAccessException e) {
throw new FieldAccessException("Cannot access DataWatcher field.", e);
}
}
/**
* Invoked when a data watcher is first used.
*/
@SuppressWarnings("unchecked")
private static void initialize() throws FieldAccessException {
// This method should only be run once, even if an exception is thrown
if (!HAS_INITIALIZED)
HAS_INITIALIZED = true;
else
return;
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherClass(), true);
for (Field lookup : fuzzy.getFieldListByType(Map.class)) {
if (Modifier.isStatic(lookup.getModifiers())) {
// This must be the type map
TYPE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true);
} else {
// If not, then we're probably dealing with the value map
VALUE_MAP_ACCESSOR = Accessors.getFieldAccessor(lookup, true);
}
}
// Spigot workaround (not necessary
initializeSpigot(fuzzy);
// Any custom types
CUSTOM_MAP = initializeCustom();
// Initialize static type type
TYPE_MAP = (Map<Class<?>, Integer>) TYPE_MAP_ACCESSOR.get(null);
try {
READ_WRITE_LOCK_FIELD = fuzzy.getFieldByType("readWriteLock", ReadWriteLock.class);
} catch (IllegalArgumentException e) {
// It's not a big deal
}
// Check for the entity field as well
if (MinecraftReflection.isUsingNetty()) {
ENTITY_FIELD = fuzzy.getFieldByType("entity", MinecraftReflection.getEntityClass());
ENTITY_FIELD.setAccessible(true);
}
initializeMethods(fuzzy);
}
// For Spigot's bountiful update patch
private static Map<Class<?>, Integer> initializeCustom() {
Map<Class<?>, Integer> map = Maps.newHashMap();
for (CustomType type : CustomType.values()) {
if (type.getSpigotClass() != null) {
map.put(type.getSpigotClass(), type.getTypeId());
}
}
return map;
}
// TODO: Remove, as this was fixed in build #1189 of Spigot
private static void initializeSpigot(FuzzyReflection fuzzy) {
// See if the workaround is needed
if (TYPE_MAP_ACCESSOR != null && VALUE_MAP_ACCESSOR != null)
return;
for (Field lookup : fuzzy.getFields()) {
final Class<?> type = lookup.getType();
if (TroveWrapper.isTroveClass(type)) {
// Create a wrapper accessor
final ReadOnlyFieldAccessor accessor = TroveWrapper.wrapMapField(
Accessors.getFieldAccessor(lookup, true), new Function<Integer, Integer>() {
@Override
public Integer apply(@Nullable Integer value) {
// Do not use zero for no entry value
if (value == 0)
return -1;
return value;
}
});
if (Modifier.isStatic(lookup.getModifiers())) {
TYPE_MAP_ACCESSOR = accessor;
} else {
VALUE_MAP_ACCESSOR = accessor;
}
}
}
if (TYPE_MAP_ACCESSOR == null)
throw new IllegalArgumentException("Unable to find static type map.");
if (VALUE_MAP_ACCESSOR == null)
throw new IllegalArgumentException("Unable to find static value map.");
}
private static void initializeMethods(FuzzyReflection fuzzy) {
List<Method> candidates = fuzzy.getMethodListByParameters(Void.TYPE,
new Class<?>[] { int.class, Object.class});
// Load the get-method
try {
GET_KEY_VALUE_METHOD = fuzzy.getMethodByParameters(
"getWatchableObject", MinecraftReflection.getWatchableObjectClass(), new Class[] { int.class });
GET_KEY_VALUE_METHOD.setAccessible(true);
} catch (IllegalArgumentException e) {
// Use the fallback method
}
for (Method method : candidates) {
if (!method.getName().startsWith("watch")) {
CREATE_KEY_VALUE_METHOD = method;
} else {
UPDATE_KEY_VALUE_METHOD = method;
}
}
// Did we succeed?
if (UPDATE_KEY_VALUE_METHOD == null || CREATE_KEY_VALUE_METHOD == null) {
// Go by index instead
if (candidates.size() > 1) {
CREATE_KEY_VALUE_METHOD = candidates.get(0);
UPDATE_KEY_VALUE_METHOD = candidates.get(1);
} else {
throw new IllegalStateException("Unable to find create and update watchable object. Update ProtocolLib.");
}
// Be a little scientist - see if this in fact IS the right way around
try {
WrappedDataWatcher watcher = new WrappedDataWatcher();
watcher.setObject(0, 0);
watcher.setObject(0, 1);
if (watcher.getInteger(0) != 1) {
throw new IllegalStateException("This cannot be!");
}
} catch (Exception e) {
// Nope
UPDATE_KEY_VALUE_METHOD = candidates.get(0);
CREATE_KEY_VALUE_METHOD = candidates.get(1);
}
}
}
@Override
public Iterator<WrappedWatchableObject> iterator() {
// We'll wrap the iterator instead of creating a new list every time
return Iterators.transform(getWatchableObjectMap().values().iterator(),
new Function<Object, WrappedWatchableObject>() {
@Override
public WrappedWatchableObject apply(@Nullable Object item) {
if (item != null)
return new WrappedWatchableObject(item);
else
return null;
}
});
}
/**
* Retrieve a view of this DataWatcher as a map.
* <p>
* Any changes to the map will be reflected in this DataWatcher, and vice versa.
* @return A view of the data watcher as a map.
*/
public Map<Integer, WrappedWatchableObject> asMap() {
// Construct corresponding map
if (mapView == null) {
mapView = new ConvertedMap<Integer, Object, WrappedWatchableObject>(getWatchableObjectMap()) {
@Override
protected Object toInner(WrappedWatchableObject outer) {
if (outer == null)
return null;
return outer.getHandle();
}
@Override
protected WrappedWatchableObject toOuter(Object inner) {
if (inner == null)
return null;
return new WrappedWatchableObject(inner);
}
};
}
return mapView;
}
@Override
public String toString() {
return asMap().toString();
}
/**
* Retrieve the entity associated with this data watcher.
* <p>
* <b>Warning:</b> This is only supported on 1.7.2 and above.
* @return The entity, or NULL.
*/
public Entity getEntity() {
if (!MinecraftReflection.isUsingNetty())
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
try {
return (Entity) MinecraftReflection.getBukkitEntity(ENTITY_FIELD.get(handle));
} catch (Exception e) {
throw new RuntimeException("Unable to retrieve entity.", e);
}
}
/**
* Set the entity associated with this data watcher.
* <p>
* <b>Warning:</b> This is only supported on 1.7.2 and above.
* @param entity - the new entity.
*/
public void setEntity(Entity entity) {
if (!MinecraftReflection.isUsingNetty())
throw new IllegalStateException("This method is only supported on 1.7.2 and above.");
try {
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
} catch (Exception e) {
throw new RuntimeException("Unable to set entity.", e);
}
}
}
@@ -1,467 +0,0 @@
/*
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Objects;
/**
* Represents a watchable object.
*
* @author Kristian
*/
public class WrappedWatchableObject extends AbstractWrapper {
// Whether or not the reflection machinery has been initialized
private static boolean hasInitialized;
// The field containing the value itself
private static StructureModifier<Object> baseModifier;
// Used to create new watchable objects
private static Constructor<?> watchableConstructor;
// The watchable object class type
private static Class<?> watchableObjectClass;
protected StructureModifier<Object> modifier;
// Type of the stored value
private Class<?> typeClass;
/**
* Wrap a given raw Minecraft watchable object.
* @param handle - the raw watchable object to wrap.
*/
public WrappedWatchableObject(Object handle) {
super(MinecraftReflection.getWatchableObjectClass());
load(handle);
}
/**
* Construct a watchable object from an index and a given value.
* @param index - the index.
* @param value - non-null value of specific types.
*/
public WrappedWatchableObject(int index, Object value) {
super(MinecraftReflection.getWatchableObjectClass());
if (value == null)
throw new IllegalArgumentException("Value cannot be NULL.");
// Get the correct type ID
Integer typeID = WrappedDataWatcher.getTypeID(value.getClass());
if (typeID != null) {
if (watchableConstructor == null) {
try {
watchableConstructor = MinecraftReflection.getWatchableObjectClass().
getConstructor(int.class, int.class, Object.class);
} catch (Exception e) {
throw new RuntimeException("Cannot get the WatchableObject(int, int, Object) constructor.", e);
}
}
// Create the object
try {
load(watchableConstructor.newInstance(typeID, index, getUnwrapped(value)));
} catch (Exception e) {
throw new RuntimeException("Cannot construct underlying WatchableObject.", e);
}
} else {
throw new IllegalArgumentException("Cannot watch the type " + value.getClass());
}
}
// /**
// * Construct a custom watchable object from an index, value and custom type.
// * @param index - the index.
// * @param primary - non-null value of specific types.
// * @param secondary - optional secondary value, if the type can store it.
// * @param type - the custom Spigot type.
// */
// public WrappedWatchableObject(int index, Object value, Object secondary, CustomType type) {
// this(index, type.newInstance(value, secondary));
// }
// Wrap a NMS object
private void load(Object handle) {
initialize();
this.handle = handle;
this.modifier = baseModifier.withTarget(handle);
// Make sure the type is correct
if (!watchableObjectClass.isAssignableFrom(handle.getClass())) {
throw new ClassCastException("Cannot cast the class " + handle.getClass().getName() +
" to " + watchableObjectClass.getName());
}
}
/**
* Initialize reflection machinery.
*/
private static void initialize() {
if (!hasInitialized) {
hasInitialized = true;
watchableObjectClass = MinecraftReflection.getWatchableObjectClass();
baseModifier = new StructureModifier<Object>(watchableObjectClass, null, false);
}
}
// /**
// * Retrieve the custom type of this object.
// * @return The custom type, or NULL if not applicable.
// */
// public CustomType getCustomType() {
// return CustomType.fromValue(getValue());
// }
/**
* Retrieve the correct super type of the current value.
* @return Super type.
* @throws FieldAccessException Unable to read values.
*/
public Class<?> getType() throws FieldAccessException {
return getWrappedType(getTypeRaw());
}
/**
* Retrieve the correct super type of the current value, given the raw NMS object.
* @return Super type.
* @throws FieldAccessException Unable to read values.
*/
private Class<?> getTypeRaw() throws FieldAccessException {
if (typeClass == null) {
typeClass = WrappedDataWatcher.getTypeClass(getTypeID());
if (typeClass == null) {
throw new IllegalStateException("Unrecognized data type: " + getTypeID());
}
}
return typeClass;
}
/**
* Retrieve the index of this watchable object. This is used to identify a value.
* @return Object index.
* @throws FieldAccessException Reflection failed.
*/
public int getIndex() throws FieldAccessException {
return modifier.<Integer>withType(int.class).read(1);
}
/**
* Set the the index of this watchable object.
* @param index - the new object index.
* @throws FieldAccessException Reflection failed.
*/
public void setIndex(int index) throws FieldAccessException {
modifier.<Integer>withType(int.class).write(1, index);
}
/**
* Retrieve the type ID of a watchable object.
* <table border=1>
* <caption>Type to Value</caption>
* <tbody>
* <tr>
* <th>Type ID</th>
* <th>Data Type</th>
* </tr>
* <tr>
* <td>0</td>
* <td>Byte</td>
* </tr>
* <tr>
* <td>1</td>
* <td>Short</td>
* </tr>
* <tr>
* <td>2</td>
* <td>Int</td>
* </tr>
* <tr>
* <td>3</td>
* <td>Float</td>
* </tr>
* <tr>
* <td>4</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>5</td>
* <td>{@link org.bukkit.inventory.ItemStack ItemStack}</td>
* </tr>
* <tr>
* <td>6<sup>*</sup></td>
* <td>{@link com.comphenix.protocol.wrappers.ChunkPosition ChunkPosition}</td>
* </tr>
* </tbody>
* </table>
* @return Type ID that identifies the type of the value.
* @throws FieldAccessException Reflection failed.
*/
public int getTypeID() throws FieldAccessException {
return modifier.<Integer>withType(int.class).read(0);
}
/**
* Set the type ID of a watchable object.
* @param id - the new ID.
* @throws FieldAccessException Reflection failed.
* @see #getTypeID()
*/
public void setTypeID(int id) throws FieldAccessException {
modifier.<Integer>withType(int.class).write(0, id);
}
/**
* Update the value field.
* @param newValue - new value.
* @throws FieldAccessException Unable to use reflection.
*/
public void setValue(Object newValue) throws FieldAccessException {
setValue(newValue, true);
}
/**
* Update the value field.
* @param newValue - new value.
* @param updateClient - whether or not to update listening clients.
* @throws FieldAccessException Unable to use reflection.
*/
public void setValue(Object newValue, boolean updateClient) throws FieldAccessException {
// Verify a few quick things
if (newValue == null)
throw new IllegalArgumentException("Cannot watch a NULL value.");
if (!getType().isAssignableFrom(newValue.getClass()))
throw new IllegalArgumentException("Object " + newValue + " must be of type " + getType().getName());
// See if we should update the client to
if (updateClient)
setDirtyState(true);
// Use the modifier to set the value
modifier.withType(Object.class).write(0, getUnwrapped(newValue));
}
/**
* Read the underlying value field.
* @return The underlying value.
*/
private Object getNMSValue() {
return modifier.withType(Object.class).read(0);
}
/**
* Read the value field.
* @return The watched value.
* @throws FieldAccessException Unable to use reflection.
*/
public Object getValue() throws FieldAccessException {
return getWrapped(modifier.withType(Object.class).read(0));
}
// /**
// * Retrieve the secondary value associated with this watchable object.
// * <p>
// * This is only applicable for certain {@link CustomType}.
// * @return The secondary value, or NULL if not found.
// */
// public Object getSecondaryValue() {
// CustomType type = getCustomType();
// return type != null ? type.getSecondary(getValue()) : null;
// }
//
// /**
// * Set the secondary value.
// * @param secondary - the secondary value.
// * @throws IllegalStateException If this watchable object does not have a secondary value.
// */
// public void setSecondaryValue(Object secondary) {
// CustomType type = getCustomType();
//
// if (type == null) {
// throw new IllegalStateException(this + " does not have a custom type.");
// }
// type.setSecondary(getValue(), secondary);
// }
/**
* Set whether or not the value must be synchronized with the client.
* @param dirty - TRUE if the value should be synchronized, FALSE otherwise.
* @throws FieldAccessException Unable to use reflection.
*/
public void setDirtyState(boolean dirty) throws FieldAccessException {
modifier.<Boolean>withType(boolean.class).write(0, dirty);
}
/**
* Retrieve whether or not the value must be synchronized with the client.
* @return TRUE if the value should be synchronized, FALSE otherwise.
* @throws FieldAccessException Unable to use reflection.
*/
public boolean getDirtyState() throws FieldAccessException {
return modifier.<Boolean>withType(boolean.class).read(0);
}
/**
* Retrieve the wrapped object value, if needed.
* @param value - the raw NMS object to wrap.
* @return The wrapped object.
*/
@SuppressWarnings("rawtypes")
static Object getWrapped(Object value) {
// Handle the special cases
if (MinecraftReflection.isItemStack(value)) {
return BukkitConverters.getItemStackConverter().getSpecific(value);
} else if (MinecraftReflection.isChunkCoordinates(value)) {
return new WrappedChunkCoordinate((Comparable) value);
} else {
return value;
}
}
/**
* Retrieve the wrapped type, if needed.
* @param wrapped - the wrapped class type.
* @return The wrapped class type.
*/
static Class<?> getWrappedType(Class<?> unwrapped) {
if (unwrapped.equals(MinecraftReflection.getChunkPositionClass()))
return ChunkPosition.class;
else if (unwrapped.equals(MinecraftReflection.getBlockPositionClass()))
return BlockPosition.class;
else if (unwrapped.equals(MinecraftReflection.getChunkCoordinatesClass()))
return WrappedChunkCoordinate.class;
else if (unwrapped.equals(MinecraftReflection.getItemStackClass()))
return ItemStack.class;
else
return unwrapped;
}
/**
* Retrieve the raw NMS value.
* @param wrapped - the wrapped position.
* @return The raw NMS object.
*/
static Object getUnwrapped(Object wrapped) {
// Convert special cases
if (wrapped instanceof ChunkPosition)
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
else if (wrapped instanceof BlockPosition)
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
else if (wrapped instanceof WrappedChunkCoordinate)
return ((WrappedChunkCoordinate) wrapped).getHandle();
else if (wrapped instanceof ItemStack)
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
else
return wrapped;
}
/**
* Retrieve the unwrapped type, if needed.
* @param wrapped - the unwrapped class type.
* @return The unwrapped class type.
*/
static Class<?> getUnwrappedType(Class<?> wrapped) {
if (wrapped.equals(ChunkPosition.class))
return MinecraftReflection.getChunkPositionClass();
else if (wrapped.equals(BlockPosition.class))
return MinecraftReflection.getBlockPositionClass();
else if (wrapped.equals(WrappedChunkCoordinate.class))
return MinecraftReflection.getChunkCoordinatesClass();
else if (ItemStack.class.isAssignableFrom(wrapped))
return MinecraftReflection.getItemStackClass();
else
return wrapped;
}
/**
* Clone the current wrapped watchable object, along with any contained objects.
* @return A deep clone of the current watchable object.
* @throws FieldAccessException If we're unable to use reflection.
*/
public WrappedWatchableObject deepClone() throws FieldAccessException {
WrappedWatchableObject clone = new WrappedWatchableObject(
DefaultInstances.DEFAULT.getDefault(MinecraftReflection.getWatchableObjectClass()));
clone.setDirtyState(getDirtyState());
clone.setIndex(getIndex());
clone.setTypeID(getTypeID());
clone.setValue(getClonedValue(), false);
return clone;
}
// Helper
Object getClonedValue() throws FieldAccessException {
Object value = getNMSValue();
// Only a limited set of references types are supported
if (MinecraftReflection.isBlockPosition(value)) {
EquivalentConverter<BlockPosition> converter = BlockPosition.getConverter();
return converter.getGeneric(MinecraftReflection.getBlockPositionClass(), converter.getSpecific(value));
} else if (MinecraftReflection.isChunkPosition(value)) {
EquivalentConverter<ChunkPosition> converter = ChunkPosition.getConverter();
return converter.getGeneric(MinecraftReflection.getChunkPositionClass(), converter.getSpecific(value));
} else if (MinecraftReflection.isItemStack(value)) {
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(value).clone());
} else {
// A string or primitive wrapper, which are all immutable.
return value;
}
}
@Override
public boolean equals(Object obj) {
// Quick checks
if (obj == this)
return true;
if (obj == null)
return false;
if (obj instanceof WrappedWatchableObject) {
WrappedWatchableObject other = (WrappedWatchableObject) obj;
return Objects.equal(getIndex(), other.getIndex()) &&
Objects.equal(getTypeID(), other.getTypeID()) &&
Objects.equal(getValue(), other.getValue());
}
// No, this is not equivalent
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(getIndex(), getTypeID(), getValue());
}
@Override
public String toString() {
return String.format("[%s: %s (%s)]", getIndex(), getValue(), getType().getSimpleName());
}
}
@@ -1,65 +0,0 @@
package com.comphenix.protocol;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.logging.Logger;
import net.minecraft.server.v1_8_R3.DispenserRegistry;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_8_R3.CraftServer;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_8_R3.util.Versioning;
import com.comphenix.protocol.utility.Constants;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
/**
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
*
* @author Kristian
*/
public class BukkitInitialization {
private static boolean initialized;
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing.
* @throws IllegalAccessException If we are unable to initialize Bukkit.
*/
public static void initializeItemMeta() throws IllegalAccessException {
if (!initialized) {
// Denote that we're done
initialized = true;
DispenserRegistry.c(); // Basically registers everything
// Mock the server object
Server mockedServer = mock(Server.class);
when(mockedServer.getLogger()).thenReturn(Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
when(mockedServer.getVersion()).thenReturn(CraftServer.class.getPackage().getImplementationVersion());
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.isPrimaryThread()).thenReturn(true);
// Inject this fake server
Bukkit.setServer(mockedServer);
initializePackage();
}
}
/**
* Ensure that package names are correctly set up.
*/
public static void initializePackage() {
// Initialize reflection
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.BOUNTIFUL_UPDATE);
}
}
@@ -1,40 +0,0 @@
package com.comphenix.protocol;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.compat.netty.independent.NettyProtocolRegistry;
import com.comphenix.protocol.injector.netty.ProtocolRegistry;
public class PacketTypeTest {
@BeforeClass
public static void initializeReflection() {
BukkitInitialization.initializePackage();
}
@Test
public void testFindCurrent() {
assertEquals(PacketType.Play.Client.STEER_VEHICLE, PacketType.findCurrent(Protocol.PLAY, Sender.CLIENT, "SteerVehicle"));
}
@Test
public void ensureAllExist() {
ProtocolRegistry registry = new NettyProtocolRegistry();
Map<PacketType, Class<?>> lookup = registry.getPacketTypeLookup();
for (Entry<PacketType, Class<?>> entry : lookup.entrySet()) {
PacketType type = entry.getKey();
Class<?> clazz = entry.getValue();
assertFalse("Packet " + clazz + " does not have a corresponding PacketType!", type.isDynamic());
}
}
}
@@ -1,25 +0,0 @@
package com.comphenix.protocol.reflect.cloning;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
public class AggregateClonerTest {
@BeforeClass
public static void initializeBukkit() {
BukkitInitialization.initializePackage();
}
@Test
public void testArrays() {
List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
}
}
@@ -1,31 +0,0 @@
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
//@PrepareForTest(CraftItemFactory.class)
public class WrappedWatchableObjectTest {
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializeItemMeta();
}
@Test
public void testItemStack() {
final ItemStack stack = new ItemStack(Material.GOLD_AXE);
final WrappedWatchableObject test = new WrappedWatchableObject(0, stack);
ItemStack value = (ItemStack) test.getValue();
assertEquals(value.getType(), stack.getType());
}
}
+19 -12
View File
@@ -15,8 +15,8 @@ Currently maintained by dmulloy2 on behalf of [Spigot](http://www.spigotmc.org/)
### Resources
* [Resource Page](http://www.spigotmc.org/resources/protocollib.1997/)
* [Downloads](http://ci.shadowvolt.com/job/ProtocolLib)
* [JavaDoc](http://ci.shadowvolt.com/job/ProtocolLib/javadoc)
* [Dev Builds](http://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](http://ci.dmulloy2.net/job/ProtocolLib/javadoc)
### Compilation
@@ -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.4</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
@@ -131,6 +143,7 @@ you don't have to create an ArrayList with this version:
````java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles().
write(0, player.getLocation().getX()).
write(1, player.getLocation().getY()).
@@ -154,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
+34 -141
View File
@@ -1,9 +1,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>3.6.5</version>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<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>v3-SNAPSHOT</version>
<relativePath>../</relativePath>
<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>
@@ -36,58 +36,25 @@
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.sf</pattern>
<shadedPattern>com.comphenix.net.sf</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.spigotmc:spigot</exclude>
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>junit:junit</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<version>3.7.0</version>
<configuration>
<source>1.6</source>
<target>1.6</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.5</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>
@@ -95,51 +62,22 @@
</configuration>
</plugin>
<!--<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<phase>package</phase>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
<goal>javadoc</goal>
</goals>
</execution>
</executions>
</plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<!--<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.5.2</version>
<configuration>
<threshold>High</threshold>
<effort>Default</effort>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<configuration>
<failOnError>false</failOnError>
<links>
<link>http://download.oracle.com/javase/6/docs/api/</link>
</links>
</configuration>
</plugin>
</plugins>
</reporting>-->
<profiles>
<profile>
<id>jenkins</id>
@@ -150,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>
@@ -161,12 +100,15 @@
<value>true</value>
</property>
</activation>
<properties>
<project.fullVersion>${project.version}</project.fullVersion>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -180,7 +122,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.1</version>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -191,10 +133,11 @@
</executions>
</plugin>
<!-- TODO: Look into signing releases
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -205,6 +148,7 @@
</execution>
</executions>
</plugin>
-->
</plugins>
</build>
@@ -229,7 +173,7 @@
<developer>
<id>dmulloy2</id>
<name>Dan Mulloy</name>
<url>http://shadowvolt.com/</url>
<url>http://dmulloy2.net/</url>
<roles>
<role>developer</role>
<role>maintainer</role>
@@ -246,55 +190,4 @@
<timezone>1</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-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();
}
}
File diff suppressed because it is too large. Load diff
@@ -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();
}
}
@@ -17,10 +17,6 @@
package com.comphenix.protocol;
import java.util.Set;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.IntEnum;
/**
@@ -165,30 +161,7 @@ public final class Packets {
public static Server getRegistry() {
return INSTANCE;
}
/**
* Determine if the given packet is a valid server packet in the current version of Minecraft.
* <p>
* Use {@link PacketType#isSupported()} instead.
* @param packetID - the packet to test.
* @return TRUE if this packet is supported, FALSE otherwise.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
@Deprecated
public static boolean isSupported(int packetID) throws FieldAccessException {
return PacketFilterManager.getServerPackets().contains(packetID);
}
/**
* Retrieve every client packet the current version of Minecraft is aware of.
* @return Every supported server packet.
* @throws FieldAccessException If we're unable to retrieve the server packet data from Minecraft.
*/
@Deprecated
public static Set<Integer> getSupported() throws FieldAccessException {
return PacketFilterManager.getServerPackets();
}
// We only allow a single instance of this class
private Server() {
super();
@@ -262,26 +235,7 @@ public final class Packets {
public static Client getRegistry() {
return INSTANCE;
}
/**
* Determine if the given packet is a valid client packet in the current version of Minecraft.
* @param packetID - the packet to test.
* @return TRUE if this packet is supported, FALSE otherwise.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static boolean isSupported(int packetID) throws FieldAccessException {
return PacketFilterManager.getClientPackets().contains(packetID);
}
/**
* Retrieve every client packet the current version of Minecraft is aware of.
* @return Every supported client packet.
* @throws FieldAccessException If we're unable to retrieve the client packet data from Minecraft.
*/
public static Set<Integer> getSupported() throws FieldAccessException {
return PacketFilterManager.getClientPackets();
}
// Like above
private Client() {
super();
@@ -20,12 +20,13 @@ 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;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
import com.comphenix.protocol.injector.PlayerInjectHooks;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -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;
}
}
}
@@ -0,0 +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.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;
}
}
@@ -0,0 +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 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);
}
}
}
@@ -29,6 +29,7 @@ import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PacketConstructor;
@@ -279,4 +280,6 @@ public interface ProtocolManager extends PacketStream {
* @return Asynchronous packet manager.
*/
public AsynchronousManager getAsynchronousManager();
}
public void verifyWhitelist(PacketListener listener, ListeningWhitelist whitelist);
}
@@ -35,7 +35,6 @@ import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.PrioritizedListener;
import com.comphenix.protocol.injector.SortedPacketListenerList;
import com.comphenix.protocol.injector.packet.PacketRegistry;
@@ -181,11 +180,11 @@ public class AsyncFilterManager implements AsynchronousManager {
// Add listener to either or both processing queue
if (hasValidWhitelist(sendingWhitelist)) {
PacketFilterManager.verifyWhitelist(listener, sendingWhitelist);
manager.verifyWhitelist(listener, sendingWhitelist);
serverProcessingQueue.addListener(handler, sendingWhitelist);
}
if (hasValidWhitelist(receivingWhitelist)) {
PacketFilterManager.verifyWhitelist(listener, receivingWhitelist);
manager.verifyWhitelist(listener, receivingWhitelist);
clientProcessingQueue.addListener(handler, receivingWhitelist);
}
@@ -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);
}
/**
@@ -28,7 +28,7 @@ import java.util.logging.Level;
import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.PrioritizedListener;
@@ -429,15 +429,17 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
// Incoming chat packets are async only if they aren't commands
return ! content.startsWith("/");
} else {
ProtocolLibrary.log(Level.WARNING, "Failed to determine contents of incoming chat packet!");
ProtocolLogger.log(Level.WARNING, "Failed to determine contents of incoming chat packet!");
alwaysSync = true;
}
} else if (event.getPacketType() == PacketType.Status.Server.SERVER_INFO) {
return true;
} else {
// TODO: Find more cases of async packets
return false;
}
} else {
ProtocolLibrary.log(Level.INFO, "Could not determine asynchronous state of packets (this can probably be ignored)");
ProtocolLogger.log(Level.INFO, "Could not determine asynchronous state of packets (this can probably be ignored)");
alwaysSync = true;
}
}
@@ -243,4 +243,4 @@ public class ExpireHashMap<K, V> {
return valueView.toString();
}
}
@@ -23,7 +23,6 @@ import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import com.comphenix.protocol.compat.guava.Guava;
import com.google.common.base.Objects;
import com.google.common.collect.Range;
@@ -68,7 +67,7 @@ public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue
@Override
public Range<TKey> getKey() {
return Guava.closedRange(left.key, right.key);
return Range.closed(left.key, right.key);
}
@Override
@@ -35,7 +35,7 @@ import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.collections.ExpireHashMap;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
@@ -464,13 +464,15 @@ public class DetailedErrorReporter implements ErrorReporter {
} else {
try {
if (!apacheCommonsMissing)
return (ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null));
return ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null);
} catch (LinkageError ex) {
// Apache is probably missing
apacheCommonsMissing = true;
} catch (Exception e) {
} catch (ThreadDeath | OutOfMemoryError e) {
throw e;
} catch (Throwable ex) {
// Don't use the error logger to log errors in error logging (that could lead to infinite loops)
ProtocolLibrary.log(Level.WARNING, "Cannot convert to a String with Apache: " + e.getMessage());
ProtocolLogger.log(Level.WARNING, "Cannot convert to a String with Apache: " + ex.getMessage());
}
// Use our custom object printer instead
@@ -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);
}
}
@@ -2,6 +2,7 @@ package com.comphenix.protocol.events;
import com.comphenix.protocol.injector.GamePhase;
/**
* Represents additional options a listener may require.
*
@@ -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 + "]";
}
/**
@@ -23,15 +23,17 @@ import java.io.ObjectOutputStream;
import java.lang.ref.WeakReference;
import java.util.EventObject;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import com.comphenix.protocol.Application;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
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;
@@ -202,7 +204,7 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return TRUE if we are, FALSE otherwise.
*/
public boolean isAsync() {
return !Application.isPrimaryThread();
return !Bukkit.isPrimaryThread();
}
/**
@@ -231,7 +233,7 @@ public class PacketEvent extends EventObject implements Cancellable {
if (CHANGE_WARNINGS.put(oldType, newType)) {
ProtocolLibrary.getErrorReporter().reportWarning(this,
Report.newBuilder(REPORT_CHANGING_PACKET_TYPE_IS_CONFUSING).
messageParam(oldType, newType).
messageParam(PluginContext.getPluginCaller(new Exception()), oldType, newType).
build());
}
}
@@ -320,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;
}
/**
@@ -452,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.
@@ -0,0 +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;
}
@@ -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;
}
}
}
@@ -14,7 +14,7 @@
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.compat.netty.independent;
package com.comphenix.protocol.injector.netty;
import io.netty.buffer.AbstractByteBuf;
import io.netty.buffer.ByteBuf;
@@ -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.compat.netty.independent;
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;
}
}
}
@@ -0,0 +1,263 @@
/**
* 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 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) {
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;
}
output.writeByte(i);
}
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;
}
}
Loaded 100 of 373 files, more files were not shown because too many files have changed in this diff. Show more