Compare commits

...
427 Commits
Author SHA1 Message Date
Myles 9385f03722 Bump version and tiny bit of formatting fix 2016-07-02 22:33:54 +01:00
Myles b48fc17ead Transform block 36 for 1.9.2 reading, and also use PartialType. 2016-07-02 22:32:40 +01:00
Myles b50d0075ca PacketWrapper allow returning of ChannelFuture with send, and fix blocked protocols disconnect.
Also remove a TODO.
2016-07-02 22:19:43 +01:00
Myles 84d006c71d Merge branch 'lenis-patch' 2016-07-02 22:06:39 +01:00
Myles 212da9c46c Fix resetReader being wrong, ty @Matsv for helping solve this 2016-07-02 22:05:08 +01:00
Myles 8edb7048a1 Code cleanup and implement index fix by @Matsv 2016-07-02 21:23:28 +01:00
Myles abe3c4b0c9 Identify unsigned byte properly 2016-07-02 21:07:41 +01:00
Myles c6d3c97c65 Fix clearing of wrapper 2016-07-02 21:07:22 +01:00
Matsv 13c6a9d612 Change getBlockId to get instead of indexOf 2016-07-02 20:45:34 +02:00
Myles 7d2ef743ac Fix stupidity, thanks @Pokechu22 <3 2016-07-02 19:09:38 +01:00
Myles 598d0f96d8 Not sure if i'm working forward or backwards on this, but pushing for progress 2016-07-01 20:17:25 +01:00
Matsv cbc22922d3 Set default force-sign-transformer to false 2016-07-01 18:51:42 +02:00
Myles 49783d36c2 Add link to docs. 2016-07-01 17:13:01 +01:00
Myles 70d91b4f87 Blocked versions now included when server sends supported versions, javadoc fixes & simple colour for blocked-msg. 2016-07-01 16:36:19 +01:00
Myles 17f3d13267 Fix #450, ProtocolSupport concurrency issues, also make listwrapper sync'd 2016-06-30 00:26:12 +01:00
Myles 559674bf58 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-06-29 16:43:42 +01:00
Myles fbc298bc30 Add some more detail to the config 2016-06-29 16:38:38 +01:00
Matsv e36902c441 Add possibility to block specific protocol versions 2016-06-29 12:26:03 +02:00
Matsv 805c78989a Port all the clients to prevent issues like ViaVersions BossBarAPI not working for clients on the same protocol. (If you use the api on a 1.9> server) 2016-06-29 11:35:15 +02:00
Mats 85e416171c Let the transformer handle the signs (#456) 2016-06-28 13:16:36 +02:00
Mats b55c0d0783 Possibility to force the json transformer (#455)
* Possibility to force the json transform

* Escape all the Strings. Who likes double quotes anyways.
2016-06-26 21:47:08 +01:00
Myles 33b4b3835f Update Contributors and version in README 2016-06-25 23:38:04 +01:00
Matsv 8ccd772105 Change the formatting a bit. merged #421 2016-06-25 13:22:18 +02:00
Matsv 8677c632df Merge remote-tracking branch 'jor/potion' into upmaster 2016-06-25 13:19:14 +02:00
Myles 560476c56f Should fix #452 2016-06-24 14:02:29 +01:00
lenis0012 20bbe7cec6 Support 1.9.2 servers
This code basically reads 1.9 data so it can then be transformed to add
in the tile entities.
it geta a little funky when it already loaded it as a 1.8 chunk tho, so
i had to add some methods to the chunk api.

in short: I hate minecraft
2016-06-23 04:00:59 +02:00
Myles 1a002486d0 Move back to snapshot, oops 2016-06-22 23:37:58 +01:00
Myles 26fcdfe3bc Allow ability to replace piston extension block to prevent crashing. 2016-06-22 23:34:32 +01:00
Mats 4af98b220c Fix piston tile entity crash (#443) 2016-06-22 21:37:53 +01:00
Myles f91fcb4ea8 Bump version 2016-06-22 21:33:25 +01:00
Myles b3060e0dc8 Start work on 1.9.2 chunk stuff, no where near done bit stuck on detecting skylight. 2016-06-22 21:32:26 +01:00
Myles 808a9a7d82 Small PPS change to allow %pps to be used as a placeholder. 2016-06-22 21:31:27 +01:00
Myles 32c43e6023 Fix chunks not being written and array type being weird. 2016-06-22 21:22:19 +01:00
Matsv 4c9c50f939 WIP: Move the TileEntities writer to the 1.9 transformer. 2016-06-22 22:00:24 +02:00
Matsv ac1b35379e Remove not required tags, and remove tested todo's 2016-06-22 20:38:40 +02:00
Myles b743589d77 This works but lags, only for 1.8 server. 2016-06-22 19:22:38 +01:00
Myles 9dad0a0d5e Fix the arrays 2016-06-22 18:20:42 +01:00
Matsv f5b49bc9ce Experimental implementation for block entities into chunks 2016-06-22 19:07:46 +02:00
Matsv b3612a68b0 Merge branch '1.10.1-chunkrefactor' of https://github.com/MylesIsCool/ViaVersion into 1.10.1 2016-06-22 18:36:42 +02:00
Myles 7385db02c2 Reorganise packages and push initial interface 2016-06-22 17:35:07 +01:00
Matsv 501d2ec3a2 Rewrite block tile entities. Not tested yet 2016-06-22 18:03:27 +02:00
Myles 1217a9b7e2 Make versions ordered by the index inserted. (Seems to work best over alphabetical) 2016-06-19 23:32:41 +01:00
Myles 6740c5c6dc Spring cleaning & make /viaver pps say when no players. 2016-06-19 22:36:15 +01:00
Myles 44e8056fb3 Add more versions & use ProtocolSupport API if not ported. 2016-06-19 22:35:49 +01:00
Myles d1da412cf7 Disable our system when an unknown handshake happens, fixes support for older mc versions. 2016-06-19 22:04:54 +01:00
Myles 6534a5414e Basic compatibility with ProtocolSupport, doesn't seem to work with 1.6.4 due to issues with our Login Handler. 2016-06-19 18:08:49 +01:00
Myles 91344a0b50 Cleanup some of the block placement code. 2016-06-19 18:07:00 +01:00
Myles cdd9ff4469 Back to snapshot 2016-06-15 19:47:10 +01:00
Myles d88b76db85 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-06-15 19:45:28 +01:00
Myles bc1d57853c Bump version 2016-06-15 19:44:25 +01:00
Troy Frew 27d6c12556 Added TacoSpigot to the block place fix. (#437)
TacoSpigot is a fork of PaperSpigot that people use.
2016-06-13 09:29:10 +02:00
Mats a76dd1ba49 Merge pull request #427 from Harry5573/master
Correct hologram Y offset
2016-06-10 15:38:52 +02:00
Mats 1a4388b3e7 Convert the Elytra sound properly they added in 1.9.2. Fix #430 (#432)
* Convert the Elytra sound properly they added in 1.9.2. Fix #430

* Change wrong version in comment

* Remove the passthrough
2016-06-09 16:03:01 +01:00
Mats f1b81f77d6 Use Myles new Magic transformer for the ArmorListener (#429)
* Use Myles new Magic transformer for the ArmorListener

* Use Myles new Magic transformer for the CommandBlockListener
2016-06-09 10:49:31 +01:00
Myles d6edec3a55 Allow packets to be transformed after sent by specifying the current protocol, woo! 2016-06-09 02:44:12 +01:00
Harry 094f4c6d58 Correct hologram Y offset
https://github.com/filoghost/HolographicDisplays/blob/master/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Offsets.java#L15 The offset is by default -1.25 for 1.8, however 1.9 clients require only -0.29. (-1.25 - -0.29) = -0.96 which should be the correct offset.
2016-06-08 21:43:15 +01:00
Matsv c57d0f60f9 Change to DEV version 2016-06-08 20:40:50 +02:00
Matsv dd5cd1ca48 Remove experimental message from readme 2016-06-08 20:38:39 +02:00
joserobjr ab4a565700 Update README.md (#426) 2016-06-08 20:36:29 +02:00
Myles f8ac54e237 Fix splash potion crash. 2016-06-08 19:02:11 +01:00
joserobjr 089e07fe49 Changes the random chest sounds to normal chest. (#424) 2016-06-08 19:09:19 +02:00
yzimroni 1d3b8f802c Update names from 'snapshot' to '1.10' (#422) 2016-06-08 18:00:21 +02:00
Myles 27283e48a2 Bump the version to avoid bad downloads. 2016-06-08 16:10:59 +01:00
Matsv d22584afbc Bye bungee we will not miss you, you eat our memory 2016-06-08 17:02:13 +02:00
Matsv 249ab01425 Rename package 2016-06-08 16:41:00 +02:00
Matsv 851097de44 Rename package 2016-06-08 16:38:59 +02:00
Matsv c095a51f7c 1.10 support and version bump 2016-06-08 16:35:06 +02:00
Matsv 5e62b7001e Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-08 15:35:55 +02:00
joserobjr a79533bf72 Fixed uncraftable potion conversions from 1.8 to 1.9 2016-06-08 10:31:03 -03:00
joserobjr e3510d01d6 Fixes potion data conversion from 1.8 to 1.9 2016-06-08 08:09:13 -03:00
Mats 8cc4ba168a Patch EchoPet wrong metadata value (#419) 2016-06-08 11:29:08 +01:00
Matsv 264a21d092 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-08 11:37:23 +02:00
Mats e2585db350 Make the main class cleaner by giving the config his own class (#418) 2016-06-07 19:18:48 +02:00
Matsv a2f7a52d31 Support for 1.10-pre2 2016-06-07 17:29:30 +02:00
Matsv d04121202d Modify the config comments to & 1.10 2016-06-07 09:11:33 +02:00
Matsv 95517ade2c Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-06-03 22:18:28 +02:00
Matsv c9441de7e4 Change from info to severe 2016-06-03 21:15:53 +02:00
Matsv dc946cb845 An amazing feature. 2016-06-03 20:49:06 +02:00
Myles 5fa30d9adb Merge branch 'master' into 1.10 2016-06-03 18:52:52 +01:00
Myles a7043424ec Support ProtocolLib + late-bind, also it now tells you the plugin to blame if it goes wrong. How nice! 2016-06-03 18:52:25 +01:00
Matsv e1b3d21732 Woops, the sounds were still broken. Now it should be fixed. no lies this time 2016-06-02 21:19:48 +02:00
Matsv 8ffc056a7e Cleanup and change class/package names from 1.9.3 2016-06-02 21:00:18 +02:00
Matsv 7c274fb03d Handle new sounds 2016-06-02 20:52:55 +02:00
Matsv 3b533b5138 Compatibility with 1.10-pre1 (number 2, git didn't push everything for some reason) 2016-06-02 18:37:05 +02:00
Matsv 5665510f7e Compatibility with 1.10-pre1 2016-06-02 18:34:04 +02:00
Matsv 47faa21ef4 Add latest build to readme & auto-build test 2016-05-27 19:44:21 +02:00
Myles 575f457830 0.8.5 SNAPSHOT 2016-05-27 18:37:34 +01:00
Matsv d5bf7909e8 Sync with master 2016-05-27 19:16:22 +02:00
Myles fdf90b7931 0.8.4 Release 2016-05-27 18:05:46 +01:00
Myles e3305e4eae Automatically buffer unknown entities, removes 2 config values and deprecate api 2016-05-27 01:00:01 +01:00
Matsv 15fe8d394e Support for 16w21b 2016-05-26 19:02:06 +02:00
Matsv 082db38c40 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion into 1.10 2016-05-26 17:03:40 +02:00
Myles be54dc41d1 Add SkinRestorer to plugin.yml unsure if this will fix anything 2016-05-26 14:27:27 +01:00
Myles 487e79c043 Fix javadocs 2016-05-26 13:54:22 +01:00
Matsv ef6390a0b8 Update version for the buildserver 2016-05-26 11:16:12 +02:00
Matsv 9d9fc65633 Support for 16w21a, how fun. A snapshot without packet changes 2016-05-25 16:44:20 +02:00
Matsv 6eac61c912 Sync with master 2016-05-24 21:17:30 +02:00
Matsv d89362a271 Sync with master 2016-05-24 21:13:28 +02:00
Matsv 0a407069fa Add Burger to sources :) 2016-05-24 21:09:04 +02:00
Myles 661fdb1127 Bump version 2016-05-22 11:42:53 +01:00
Myles 9e6c0f72a8 Update version to be 1.9.3/4 2016-05-22 11:41:59 +01:00
Myles 70ad2613f1 Update config to look a bit more nicer, doesn't have the extra space when generated by bukkit. 2016-05-22 11:41:27 +01:00
Myles 9052946c45 Merge branch 'master' into 1.10 2016-05-21 22:12:10 +01:00
Myles 3ddc43bf7b Implement isSpigot() in the API and make it so xray does not activate if they do not use spigot. 2016-05-21 22:07:20 +01:00
Matsv cdb757eae5 Implement Myles idea to just use templates instead of defining compatibility 2016-05-21 21:19:20 +02:00
Matsv 50a3e03649 Possibility to add 'isCompatible' with other types. Fixes crashes on a 1.8 server 2016-05-21 20:54:26 +02:00
Matsv 957284930e Fix sounds 2016-05-21 16:19:12 +02:00
Matsv 198a24371c Sync with master 2016-05-21 15:01:25 +02:00
Mats bcc994b0dd Add possibility to get the supported versions from the api and status response (#402)
* Possibility  to send the Supported versions inside the status response packet

* Make lowercase
2016-05-19 18:56:42 +01:00
Matsv f870bd5a32 Transform pitch correctly 2016-05-19 13:07:51 +02:00
Matsv a14abd6f4f Handle the new Metadata value for 16w20a 2016-05-19 12:18:04 +02:00
Matsv 2456c3cf17 Basic support for the new 1.10 snapshot 16w20a 2016-05-18 16:43:57 +02:00
Myles 9828e4ae1d Add maven default goal.
Requested by our lord and saviour @Aikar
2016-05-17 23:40:00 +01:00
Myles 030aa887c7 Version bump to 0.8.3-SNAPSHOT #hype 2016-05-17 22:56:52 +01:00
Myles ba624a2b48 1.9.4 backport (#399)
Allow 1.9.4 spigot to handle 1.9, 1.9.1, 1.9.2
2016-05-17 22:48:56 +01:00
Myles e7582cb1ec Change license to be more appropriate, this one is more open and allows much more creativity. 2016-05-11 14:19:30 +01:00
Mats 82bf11fcbc Fix anti-xray for 1.9 (#393)
* Fix anti-xray for 1.9

* Bye NMS.

* Perform

* Add config option
2016-05-10 19:53:37 +01:00
Myles ac6e8b1709 Bump version 2016-05-10 15:03:55 +01:00
Myles 00cc545795 Using javassist add compatibility for Spigot builds with protocol compatibility (eg. 1.9.1 & 1.9.2)
Also fix issue with protocol passthrough
*whew*
2016-05-10 14:37:31 +01:00
Myles c7ee816aba Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-05-10 10:27:32 +01:00
Myles e7f85e67f6 Change our data structures to use concurrent equivalents, netty is multi-threaded. 2016-05-10 10:26:49 +01:00
Mats d239d2f967 Stop depending the bossbar metadata rewriter on the bukkit api (#391)
* Don't depend the bossbar metadata rewriter on the bukkit api

* Rename field
2016-05-09 17:44:10 +01:00
Myles 63970c6d82 Version bump 2016-05-08 22:45:46 +01:00
Mats 4cffdd4f64 Don't call PlayerInteractEvent twice and fix the gamemode message (#390)
* Experimental feature to fix the 'Your gamemode has been updated to ' message

* Don't fire PlayerInteractEvent twice, fix #326
2016-05-08 19:49:13 +01:00
Mats cb3f0291b7 Well, this feature never worked. (#389) 2016-05-04 20:03:48 +01:00
Mats edaffad045 1.9.3 support 2016-05-04 17:32:38 +02:00
Myles 172f89940a Implement passthroughAll to make it easier for adding things to the end of a packet. 2016-05-03 21:09:48 +01:00
Myles fb4bc397c6 Add fallback to metaindex to living entity. 2016-04-30 20:40:09 +01:00
Myles e595344941 Implement rewriting for villager trade info. 2016-04-29 17:09:24 +01:00
Myles 05dbc53fd0 Add javassist to the libraries for future use. Apologies for the like extra 600kb :( 2016-04-27 21:57:13 +01:00
Mats 0374da573b Check if the player is ported, fix NullPointerException post 876 on spigotmc.org (#374) 2016-04-24 12:29:52 +01:00
Myles 8e64fb8c21 Add magical unknown protocol version, so that if it's not registered it doesn't NPE. 2016-04-23 20:39:16 +01:00
Myles 0f55402171 Version bump 2016-04-22 17:17:40 +01:00
Myles acb7372eb7 Ensure that the player is online. 2016-04-22 13:36:10 +01:00
Myles 057ab050ea Make it so base protocol actually registers its listeners Credit to @jollyolbrits for finding this silly one. 2016-04-22 13:28:54 +01:00
Myles a01997fe53 Typo 2016-04-21 23:12:40 +01:00
Myles e4adbf4a1a Add terorbility build server and remove filip's since it's dead for now. 2016-04-21 22:47:01 +01:00
Myles 261d68c58e Fix blocking shield not going away :P 2016-04-21 21:13:10 +01:00
Myles 38617d1f61 Version bump 2016-04-21 16:20:45 +01:00
Myles cc5bcb8a64 Allow NMS packet receiving 2016-04-21 16:19:29 +01:00
Mats b6794f5597 Cancel swap item, fix #364 (#365)
* Cancel swap item, fix #364

* Remove canceling drop item
2016-04-20 18:31:18 +01:00
Myles 18f77308f5 Bump version to .0.7.8-SNAPSHOT 2016-04-20 15:59:23 +01:00
Myles 6bb473ea8d Change PlayerMovementMapper to a handler and fix the mappings so it works. 2016-04-20 01:33:01 +01:00
Myles 7984f6400c Clean up not used code 2016-04-20 01:25:00 +01:00
Myles 3498d093b0 Thanks @jollyolbrits for debugging, fix the issue with ground not being sent properly for movement.
Fixes anti-cheat issues.
Placement issues.
Probably 100 billion other issues.
Fix me being an idiot and changing numbers.
2016-04-20 01:22:58 +01:00
Myles a3db32758c Add new is feature to PacketWrapper :) 2016-04-20 01:09:39 +01:00
Myles 45952d3759 Move Idle thread. 2016-04-20 00:50:44 +01:00
Myles c552b12696 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-04-19 22:53:48 +01:00
Myles 0ae9d9dbbf Fix bossbars so they send correctly, credits to @jollyolbrits for detailed reporting of where the issue may take place. 2016-04-19 22:53:27 +01:00
Mats 8852e3b3a3 Change item from main hand, not offhand. Fixes #329 (#363) 2016-04-19 16:32:03 +01:00
Myles a496b2d3a8 This branch would be better here as it won't be applicable if elsewhere. 2016-04-17 12:59:01 +01:00
Myles 6198dbfe70 Change to match the config 2016-04-17 12:55:34 +01:00
Myles a81d52a54f Implement a packet limiting system,
This means that if a client sends more than a maximum, it will be disconnected.
It also means if a client goes over a threshold multiple times over a period of time, it will be disconnected.

Improvements are welcome, it's mostly down to how the user configures it.
(Some values are higher so that we don't kill every server with a tiny bit of lag)
2016-04-17 12:55:18 +01:00
Myles cf20e8ac5f Bump version 2016-04-17 00:53:49 +01:00
Myles 4f8a248c6b Add item-cache as a less expensive way of getting items. (Will rewrite eventually to not depend on Bukkit) 2016-04-17 00:52:35 +01:00
Mats 57d58db35e Fix item showing twice, one in the new slot (#359) 2016-04-15 18:48:28 +01:00
Mats c214ca02e8 Make commandblocks compatible with all 1.8 spigot/craftbukkit versions, fix #355 (#356) 2016-04-14 12:59:23 +01:00
Myles 6b8b0e1771 Bump version to 0.7.6-SNAPSHOT 2016-04-13 19:42:53 +01:00
Myles e6671513f2 Improve an issue to do with block breaking ghosting back, note this is not a fully fix, but improves the issue quite a bit. 2016-04-13 16:54:18 +01:00
Myles 4c7b50d6fb Fix chunk sending order, Fixes #338, #292 (I believe.) 2016-04-13 15:05:50 +01:00
Mats 1c9229a2fc Don't unload the chunks twice (#350) 2016-04-13 12:25:41 +01:00
Mats d11c18c024 Remove MCProtocolLib (#349) 2016-04-12 12:40:12 +01:00
Mats dc4b5b4d35 Possibility to show the new 1.9 death messages (#348) 2016-04-12 12:01:37 +01:00
Mats 517299d673 Show skeletons' bow like 1.8, not down as 1.9 without target (#347) 2016-04-12 11:18:58 +01:00
Mats a8baa7af50 Transform Combat packet (#346) 2016-04-12 11:18:51 +01:00
Myles a1f847c905 Patch respawn packet to reset bulk / loaded cache. 2016-04-11 20:17:48 +01:00
Mats f3ea43b1eb Reformat / optimize imports (#344) 2016-04-10 18:43:08 +01:00
Mats 551b5d0654 Rewrite effect ids (#342)
* Rewrite effect ids

* Remove test line

* Remove Lombok unused thingy

* No need to check.
2016-04-10 14:29:25 +01:00
Mats 9f39662df6 Check if custom name is visible and if custom name is not empty. Fix …
* Check if custom name is visible and if custom name is not empty. Fix #327

* Fix typo
2016-04-06 13:50:09 +01:00
Myles 7ea2156f37 Bump version 2016-04-05 14:31:40 +01:00
Mats 5032e7d07e Only apply the 'Hologram patch' to small entities since it only happens to them.
Only apply the 'Hologram patch' to small entities (Doesn't fix issue but hey)
2016-04-04 15:55:48 +01:00
Myles c7c6ee9fbc Delay registration of protocols until the plugin has loaded to prevent it being done at the wrong time, #323 #316 Fixed 2016-04-03 23:30:10 +01:00
Mats 4431ca63c2 Fix viaversion list command 2016-04-03 11:26:27 +01:00
Myles 1fa7211cf0 Construct List properly, Fix #314 2016-04-03 01:51:38 +01:00
Myles de576795dc Blame @Matsv 2016-04-02 22:59:40 +01:00
Myles f6286bcc25 Bump version 2016-04-02 22:13:30 +01:00
Myles 8276b212e9 Merge pull request #313 from Matsv/master
Stop allowing everyone to change the instance
2016-04-02 21:32:46 +01:00
Matsv aabd9247a6 Stop allowing everyone to change the instance 2016-04-02 21:45:24 +02:00
Myles 997c2ad6a1 Fix Potions: ThrownPotion is now correct, and the particles are too.
Fixes #223
Also changes a tiny bit of javadoc :)
2016-04-02 14:46:30 +01:00
Myles 233a2ba5df Merge pull request #312 from Matsv/dev
Fix custom NBT for potions / spawneggs
2016-04-02 14:09:00 +01:00
Matsv ed0d5a7cfb Fix NBT for potions 2016-04-02 15:06:10 +02:00
Matsv 87f083460c Fix custom named spawneggs 2016-04-02 15:02:41 +02:00
Myles 2d6dedefb8 Add some packet plugins to loadbefore to ensure it works better with them. 2016-04-02 13:50:21 +01:00
Myles 8adbf36a3a Merge pull request #311 from Matsv/dev
Make command sender messages cleaner
2016-04-02 13:30:10 +01:00
Matsv 8c706b665c Don't format if no args are given 2016-04-02 11:58:15 +02:00
Matsv 2e3677a592 Make command sender messages cleaner 2016-04-02 11:51:17 +02:00
Myles 3602c5087b Don't send a meta data packet when list is empty. #288 2016-04-01 18:13:23 +01:00
Myles 5c84b3d8cc Merge pull request #309 from HugoDaBosss/master
Implement subcommand to reload the config
2016-04-01 16:41:03 +01:00
Hugo Kerstens 7defe7129f Implement subcommand to reload the config 2016-04-01 17:31:44 +02:00
Myles 995dc135b8 Merge pull request #308 from Matsv/boss
Cleanup BossBar class & add new bossbar api method, addPlayers()
2016-04-01 15:07:04 +01:00
Matsv 3f64fb7d5f Cleanup BossBar class & add new bossbar api method, addPlayers() 2016-04-01 16:03:56 +02:00
Myles ac8f7a00ae Merge pull request #305 from HugoDaBosss/master
Implement new protocol version registry
2016-03-31 22:45:39 +01:00
Myles 6893f34289 Support reloading to some extent, if you use protocol lib it will kick all your players... (So you probably should just get a PluginManager...)
One day i'll fully patch to work... (If I don't kick the players it ends up that ProtocolLib implodes and nobody can connect.)
2016-03-31 22:45:15 +01:00
Hugo Kerstens 0a69d62cbe Fixed typo (blame Mats) 2016-03-31 23:26:13 +02:00
Myles 5e0096aa2c Merge pull request #307 from Matsv/master
Woops use the same instance
2016-03-31 21:49:42 +01:00
Matsv 7326205495 Woops use the same instance 2016-03-31 22:48:20 +02:00
Myles a8212db7f2 Merge pull request #306 from Matsv/tabcomplete
Tabcomplete
2016-03-31 19:55:11 +01:00
Matsv 2905d77206 Reformatting moved color to the top ;( 2016-03-31 20:53:03 +02:00
Matsv cb8ec2ce20 Add tab-complete possibility and help subcommand 2016-03-31 20:50:22 +02:00
Hugo Kerstens 09f484f956 Implement new protocol version registry 2016-03-31 19:50:04 +02:00
Myles c48922eb3e Merge pull request #304 from Matsv/cmd
Implement new command API
2016-03-31 17:56:08 +01:00
Matsv b701a0ef67 Change debug mode subcmd description 2016-03-31 18:55:05 +02:00
Matsv eb50f4e907 Merge branch 'master' of https://github.com/Matsv/ViaVersion into cmd 2016-03-31 17:53:12 +02:00
Matsv 6480ea34fc Changed javadoc 2016-03-31 17:52:06 +02:00
Matsv 511941462f Add javadoc 2016-03-31 17:51:30 +02:00
Matsv 34cc783cac Fix missing description, add javadocs, make sure subcommand is valid, check permission 2016-03-31 17:48:15 +02:00
Matsv ecc79b4f49 Command API to keep it clean and make it possible for developers to add their own viaversion subcommands if they want to. 2016-03-31 17:22:42 +02:00
Myles 76f10c463f Merge pull request #301 from Matsv/master
Handle discontinued player item in hand, fixes #299
2016-03-31 15:49:24 +01:00
Matsv 41d5144b57 Handle discontinued player item in hand, fixes #299 2016-03-31 16:28:56 +02:00
Myles de196f2e4f Merge pull request #300 from HugoDaBosss/master
Small optimization in join packet
2016-03-31 14:10:52 +01:00
Hugo Kerstens 0acc2f95fb Remove unneeded code 2016-03-31 15:08:19 +02:00
Myles d16eb9b27e Version bump 2016-03-30 17:46:00 +01:00
Myles 8760b53a0a Fix Disconnect Issue (It's not really harmful but still) 2016-03-30 17:45:24 +01:00
Myles a40182186f Fix debug issue with 1.9.2 2016-03-30 17:34:13 +01:00
Myles ef2b173749 Merge pull request #295 from Matsv/master
Rename 1_9_1 PRE to just 1_9_1
2016-03-30 17:33:15 +01:00
Matsv 75c9284cda Rename 1_9_1 PRE to just 1_9_1 2016-03-30 18:23:52 +02:00
Myles 2abecba7bf Bump version 2016-03-30 16:54:49 +01:00
Myles fb6055cbfe 1.9.2 support (no protocol changes from what I've seen) 2016-03-30 16:51:55 +01:00
Myles 639c9ea8c1 Fix 1.8R0.1 not working, ensure flying packets are sent from right place in pipeline, should fix #273 2016-03-30 16:43:01 +01:00
Myles 1872845d2b Add additional information when packets aren't sent correctly. 2016-03-30 15:58:31 +01:00
Myles 93755c6732 Move patch to update health so it works better and is applied upon login. 2016-03-29 18:22:40 +01:00
Myles 68973fd136 Merge pull request #291 from Matsv/gitter
Add Gitter badge to README.md
2016-03-29 16:08:43 +01:00
Matsv 1a0bd37a01 Add gitter badge to ReadMe 2016-03-29 16:43:42 +02:00
Myles 3551779600 Merge pull request #290 from Matsv/master
Add JavaDocs link to README.md
2016-03-29 15:39:40 +01:00
Matsv b8deddcd2c Add JavaDocs link to README.md 2016-03-29 16:36:21 +02:00
Myles c49bf89ead Merge pull request #289 from Matsv/debug
Show all errors on debug
2016-03-29 15:03:36 +01:00
Myles 4ff4fe2ab2 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-03-29 15:02:04 +01:00
Myles 1b99e2cf57 Move EntityNBT to different file and fix the issue to do with respawn chunks unloading. #287 2016-03-29 15:01:38 +01:00
Matsv 55830b0dfb Show all errors on debug 2016-03-29 16:00:56 +02:00
Myles e61f56b2e0 Merge pull request #283 from Matsv/slpfix
Woops the new api broke ServerListPlus custom outdated message again …
2016-03-28 12:04:06 +01:00
Mats 95ea95be13 Woops the new api broke ServerListPlus custom outdated message again fixes #282 2016-03-28 11:52:29 +02:00
Myles d72b7fc4df Fix shields not being configurable 2016-03-27 23:52:57 +01:00
Myles 0992c74351 Improve block placing a bit 2016-03-27 23:40:32 +01:00
Myles 081c23ad8b Small Javadocs 2016-03-27 22:45:25 +01:00
Myles 258f0fc636 Change the README back ;) 2016-03-27 21:50:49 +01:00
Myles c4e3864fc0 Pom pom 2016-03-27 21:46:11 +01:00
Myles 405b6e8047 Merge pull request #280 from HugoDaBosss/apiv2
Make BossBar API easier to use
2016-03-27 21:37:14 +01:00
Hugo Kerstens 9847912128 Make BossBar API easier to use 2016-03-27 21:30:31 +02:00
Myles d89a34cca3 Allow packets to be written to the server using passthrouh ID, shouldn't break anything I also updated comment on why interaction is half broken 2016-03-27 19:13:19 +01:00
Myles 6197138380 Merge pull request #279 from HugoDaBosss/apiv2
Update BossBar API to use protocol version and add protocol version check by uuid API method
2016-03-27 18:31:17 +01:00
Hugo Kerstens f5f4876182 Add check for isPorted because server can be using 1.9 2016-03-27 18:05:04 +01:00
Hugo Kerstens 293c1229ff Update BossBar API to use protocol version and add protocol version by uuid API method 2016-03-27 17:58:31 +01:00
Myles ba60f10744 Match the source for SPIGOT-1.8 2016-03-27 16:37:42 +01:00
Myles c0493de4df Does not fix armour issue completely but improves right click armour items (it's still bugged, working on it) 2016-03-27 14:57:13 +01:00
Myles 46b2f69b01 Fix healing potion :) 2016-03-27 14:06:55 +01:00
Myles f926113c66 Improve paper patch to allow for latency in towering up. 2016-03-27 13:56:06 +01:00
Myles 0e4df51613 Better placement detection 2016-03-26 23:29:09 +00:00
Myles c5f72dc728 Wrong way oops 2016-03-26 22:22:19 +00:00
Myles cee5b1ee42 Improve the patch to check the pipe and to handle above head placement. 2016-03-26 15:55:13 +00:00
Myles f1b9cd2e56 Add PaperSpigot patch to fix block placement (Applies to all protocols for now, until someone finds an issue with this) 2016-03-26 15:28:42 +00:00
Myles 7807e6d9c4 Merge pull request #275 from HugoDaBosss/apiv2
Fix for block place sounds
2016-03-26 14:51:55 +00:00
Hugo Kerstens 4b354f806d Check if user is indeed using 1.9 to 1.8 protocol conversion 2016-03-26 15:38:15 +01:00
Hugo Kerstens 41707ca867 Not needed 2016-03-26 15:22:23 +01:00
Hugo Kerstens 5bca3a97ec Merge remote-tracking branch 'origin/apiv2' into apiv2 2016-03-26 15:18:39 +01:00
Hugo Kerstens 3679aa618f Fix for block place sounds 2016-03-26 15:17:00 +01:00
Myles e9d96f2daf Fix hologram offset. 2016-03-26 14:08:23 +00:00
Myles f371419396 Fix shields not being for the right entity >.> and also print out value for metadata 2016-03-26 13:07:06 +00:00
Myles f32d3211ed Fix issue with Bukkit materials which aren't found by ID 2016-03-26 12:33:22 +00:00
Hugo Kerstens 45f99d4c31 Small fixes and formatting 2016-03-25 23:34:02 +01:00
Myles 71a9c90fe9 Fix team packet 2016-03-25 22:02:25 +00:00
Myles 406500b0bd Fix type conversion not being instant, (causes issue when reading later on)
Introduce packet ID counting (so we can mark something for the next packet etc)
Move USE Item
Add patch to handle placement properly and not break items yay. (Requires a list of special items >.>)
(And lastplaceblock packet index)
2016-03-24 15:45:16 +00:00
Myles 9257298e17 Handle not found metadata properly, and register the player as a type.
Fixes #270
2016-03-23 17:56:02 +00:00
Myles 0376602894 Update TODO list, add pipeline cache 2016-03-23 14:00:48 +00:00
Myles 95ceabb495 Merge fix for duplicated events for placement 2016-03-23 13:41:11 +00:00
Myles 6c8ccb1b1e Don't send place block twice (1.9 will use use_item to handle items), fixes #244 (may fix more) 2016-03-23 13:37:45 +00:00
Myles f351dd03e7 Merge pull request #269 from Matsv/effectapiv2
Reimplementing the effect indicator
2016-03-23 12:43:20 +00:00
Myles c1e77a49b6 Merge branch 'master' into apiv2 2016-03-23 12:40:08 +00:00
Myles 9adf6040fa Optimise ViaIdleThread, use 1 object for all packets. (Quite a big speed up) 2016-03-23 12:35:38 +00:00
Mats 3e320618f9 Merge branch 'apiv2' of https://github.com/MylesIsCool/ViaVersion into apiv2 2016-03-23 13:13:33 +01:00
Myles 6c1de57d32 Merge pull request #268 from Matsv/apiv2
Fix small teampacket bug
2016-03-23 12:12:19 +00:00
Mats 7694d311d1 Reimplement effect indicator 2016-03-23 12:42:03 +01:00
Mats eee1fd941e Fix small teampacket bug 2016-03-23 11:34:13 +01:00
Hugo Kerstens 9c5c181389 Small fixes and formatting 2016-03-23 00:26:38 +01:00
Myles b4b85b1602 Merge pull request #266 from HugoDaBosss/apiv2
Implement new Config API at some more places
2016-03-22 23:23:50 +00:00
Hugo Kerstens f6bb1b48f5 Implement new Config API at some more places 2016-03-22 23:35:01 +01:00
Myles 4ce846a899 Make more errors informative :) 2016-03-22 19:37:15 +00:00
Myles ef08117f34 oops ;) better read the tin better 2016-03-22 19:28:37 +00:00
Myles 4b582a7279 Make PacketWrapper exceptions more informative :) 2016-03-22 19:27:27 +00:00
Myles ad53c1ed7f Merge pull request #265 from HugoDaBosss/apiv2
Optimise SoundEffect searching
2016-03-22 19:25:33 +00:00
Hugo Kerstens 6e46d018a4 Removed import? 2016-03-22 20:18:07 +01:00
Myles 6beacf9804 Merge pull request #264 from HugoDaBosss/apiv2
Removed some debug code
2016-03-22 19:17:28 +00:00
Hugo Kerstens e15c8d7257 Optimise sound searching 2016-03-22 20:16:39 +01:00
Hugo Kerstens 3518804cbf Remove some debug code 2016-03-22 20:11:24 +01:00
Myles 101f86c8cd Merge pull request #263 from HugoDaBosss/apiv2
Fixed spawn experience orb packet
2016-03-22 18:32:42 +00:00
Hugo Kerstens 22c2435656 Fixed spawn experience orb packet 2016-03-22 19:30:24 +01:00
Myles af53275ab9 JavaDoc for quite a lot of the API classes... yay 2016-03-22 18:02:46 +00:00
Myles eccbb567e8 Clean up some code in Base and MetaIndex 2016-03-22 16:56:36 +00:00
Myles bae3599388 Update DEBUG to work in APIv2, also fix a few usages to the config API (may need more) 2016-03-22 16:53:07 +00:00
Myles 88ab4e53a7 Merge pull request #261 from HugoDaBosss/apiv2
Add config option to suppress 'Unable to get entity for ID' errors (apiv2)
2016-03-22 16:48:13 +00:00
Hugo Kerstens 25fa2eabbb Small update for descriptions 2016-03-22 17:22:49 +01:00
Hugo Kerstens e12d8ec466 Add descriptions for the new Config API 2016-03-22 17:17:13 +01:00
Hugo Kerstens a7022bddf8 Small typo 2016-03-22 17:00:13 +01:00
Hugo Kerstens 523c27237d Small changes for the new Config API 2016-03-22 16:55:58 +01:00
Hugo Kerstens 147891f76b Implement config to the API 2016-03-22 16:53:53 +01:00
Myles a5ba52ce05 Rename ChunkHandler
Introduce Packet Filtering
2016-03-22 15:36:11 +00:00
Hugo Kerstens ccb2a4756f Add config option to suppress 'Unable to get entity for ID' errors 2016-03-22 16:27:39 +01:00
Myles f5a96b791d Move listeners to respectful classes to be registered. 2016-03-22 15:03:02 +00:00
Myles c628a8a23f Merge pull request #257 from HugoDaBosss/apiv2
Fix armorstands moving incorrectly when using relative movement packets (apiv2)
2016-03-22 14:38:33 +00:00
Myles 80f63d79ef Merge pull request #258 from HugoDaBosss/master
Fix armorstands moving incorrectly when using relative movement packets
2016-03-22 14:37:40 +00:00
Hugo Kerstens b225afe6ef Fix armorstands moving incorrectly when using relative movement packets 2016-03-22 15:28:00 +01:00
Hugo Kerstens 4e1625b017 Merge pull request #14 from MylesIsCool/master
Update from master
2016-03-22 15:27:12 +01:00
Hugo Kerstens 5c4867365e Fix armorstands moving incorrectly when using relative movement packets 2016-03-22 15:22:21 +01:00
Myles 3549a823cb Don't move when relative 2016-03-22 12:07:43 +00:00
Myles 23a00d2555 Match master 2016-03-21 22:30:05 +00:00
Myles 57931e7231 Patch inventory shield bug 2016-03-21 22:28:11 +00:00
Myles 9e19e0c8af Patch inventory sword bug 2016-03-21 22:24:03 +00:00
Myles 74461609c1 Change to use maps, much better for optimisation. 2016-03-21 19:42:52 +00:00
Myles 7d3e6bc7cb This is why people should read over code :)) 2016-03-21 19:13:55 +00:00
Myles 175606f320 Version bump 2016-03-20 16:40:03 +00:00
Myles b226e94981 Patch the other mem leak fix across 2016-03-19 23:49:26 +00:00
Myles 7d0687aa4a Fix chance of another leak happening... 2016-03-19 23:47:03 +00:00
Myles d152b8b9e1 Merge branch 'master' into apiv2 2016-03-19 22:05:57 +00:00
Myles 1fbd561aec Oops, fix 1.8 clients 2016-03-19 22:04:17 +00:00
Myles a8295c9de0 Merge pull request #254 from Matsv/ifremoveifremoveif
Remove unnecessary if statement, this is always true.
2016-03-19 21:37:04 +00:00
Mats e894a3c733 Remove unnecessary if statement, this is always true. 2016-03-19 21:53:26 +01:00
Myles a2f8f28010 Fix memory leak, add command. Merge from master 2016-03-19 20:51:23 +00:00
Myles 704c876772 One line of code can make holes in the world, one line can fix them. (Fixes packet mem leak, also adds useful secret displayleaks command :) ) 2016-03-19 20:45:07 +00:00
Myles 0233e2ab27 Merge pull request #250 from HugoDaBosss/apiv2
Change protocol constants to final fields
2016-03-19 20:25:32 +00:00
BuildTools 5ae8a35740 Change protocol constants to final fields 2016-03-19 01:11:16 +01:00
Myles ed766eb656 Merge pull request #247 from HugoDaBosss/apiv2
Small optimization for armor searching
2016-03-18 22:04:05 +00:00
Myles b1b35fe025 Bump version 2016-03-18 21:59:13 +00:00
HugoDaBosss 9b6a9fca3d Small optimization for armor searching 2016-03-18 22:57:16 +01:00
Myles 615128eba2 Bongo bongo bongo 2016-03-18 21:25:41 +00:00
Myles 8f3b938e39 Update readme so I don't forget 2016-03-18 21:24:16 +00:00
Myles 625a1d2191 Eventually we should have some sort of handler for this, but this is fine for now 2016-03-18 21:22:09 +00:00
Myles 8a35c0235e Move everything into the right package.
Clean up a lot of code.
Remove PacketUtil (evil laugh)
Add Pipeline Util
Organise listeners, and add protocol pipe checks
2016-03-18 21:18:48 +00:00
Myles 69e8ddcbf6 Update to match master and all current fixes 2016-03-18 20:22:38 +00:00
Myles ce8a504750 Merge from other branch to ensure we don't have to redo some of these patches 2016-03-18 18:25:58 +00:00
Myles 11f3844d63 Use GSON instead, should fix memory leak issue with JSON. Also make sure that we use a copy of players for the bossbar. 2016-03-18 18:14:58 +00:00
Myles 56d5f8eec6 Add protocol registry.
Fix some issues with PacketWrapper
Add 1.9.1 port
Add ProtocolVersion detection on boot
Add more type converters
Implement Pipeline properly
2016-03-17 21:24:25 +00:00
Myles 7a98cdd28f Remove more debug code
Correct casting detector in packet wrapper.
Re-order some packets
Implement Plugin Message Transformers
Implement Sound Transformer
Implement Block Entity Transformer
2016-03-17 13:58:49 +00:00
Mats d40c5851e9 Merge pull request #239 from Matsv/master
Add api method to get player's protocol id
2016-03-17 12:38:51 +01:00
Myles ccda9f11c8 Remove some debug info, make it so StoredObject protects user.
Implement rewriting of Slot in rewriter.
Implement Enchanting Patch
Implement Brewing Patch
Implement Auto-Team Patch
Implement Inventory Tracker
Remove some TODOs
Implement Item Throwing on elytra
(Should just be plugin channels and a few packets + sounds left)
2016-03-16 23:34:03 +00:00
Myles 7e133a0be8 Include UserConnection in StoredObject
Handle Blocking
Handle BossBars, Holograms
2016-03-16 21:52:00 +00:00
Mats d0e573613b Add api method to get player's protocol id, 2016-03-16 21:01:04 +01:00
Myles 2197b938f8 Implement ItemRewritter
Upgrade ViaIdleThread to new system
Add new method to ProtocolPipeline
Implement Use Item
2016-03-16 19:46:16 +00:00
Myles 0d3ba67233 Remove compress / decompress, rethrow errors if they occur from another handler, Fixes #233 2016-03-16 13:42:17 +00:00
Myles 9d57521b29 Bump Version 2016-03-15 21:57:38 +00:00
Myles d98d4ff466 Merge pull request #236 from Matsv/meta11
Discountinue player metadata 11.
2016-03-15 21:06:37 +00:00
Myles 71e27b4799 Merge pull request #232 from Matsv/dropcreative
Throw items when you try to put things in your second hand in creative.
2016-03-15 21:06:04 +00:00
Myles f1354edf20 Merge pull request #231 from Matsv/effect
Make it possible to use the new effect indicators in 1.9
2016-03-15 21:05:55 +00:00
Mats 4d82112286 Discountinue metadata 11. 2016-03-15 21:42:40 +01:00
Mats ba5a44d2d2 Remove debug message 2016-03-15 20:40:26 +01:00
Mats 597ddc7f82 Throw items when you try to put things in your second hand in creative. 2016-03-15 20:37:45 +01:00
Mats 6a7bd209a3 Make it possible to use the new effect indicators in 1.9 2016-03-15 20:20:39 +01:00
Myles 722f28f086 Merge pull request #228 from Matsv/slp
Make compatible with ServerListPlus custom outdated message. Fix #227
2016-03-15 17:51:56 +00:00
Mats daed15d61f Make compatible with ServerListPlus custom outdated message. Fix #227 2016-03-15 14:14:10 +01:00
Myles c300c47a14 Implement Optional UUIDs
Implement Meta Rewriting
Implement Type Converters

Implement System so it can be tested.

Most of it works excluding the missing packets, also fixes some stupid mistakes :)
2016-03-15 00:34:20 +00:00
Myles 89427cb2da PacketWrapper now has an ID attached and can be reset so that it can be read from stored values.
Removed some parameters from protocol, the ID will now be attached to the PacketWrapper (made more sense)
BaseProtocol doesn't handle ProtocolInfo anymore
Implement ProtocolPipeline (WIP)
2016-03-14 18:05:29 +00:00
Myles fce7f1740e Add packet cancelling, implement quite a few of the todo's 2016-03-14 16:51:56 +00:00
Myles 0357d8e6aa Implement Partial Types (Types which require constructing with info), Implement Items, Implement NBT, add exceptions so they go down the tree! 2016-03-14 16:09:50 +00:00
Myles 55335944e3 Update apiv2 to match master 2016-03-14 12:59:15 +00:00
Myles c0b2985343 Just a reminder :) 2016-03-14 12:55:17 +00:00
Myles 91ae5990b1 Almost forgot response handler woops, also allow PacketWrapper to be modifier :) 2016-03-14 12:48:05 +00:00
Myles 33f1a8975c Another quite big update:
Implement Unsigned Short
Implement Most of the Incoming Packets (Still need to implement a few, when I add some sort of packet sending / id changing and packet cancelling)
Implement a base protocol

Still need to do plugin messaging (I will probably forget)
2016-03-14 12:40:23 +00:00
Myles 7cfba0b602 Merge pull request #210 from Matsv/fixarmortjes
Fix #209 woops my fault
2016-03-13 17:02:07 +00:00
Myles 9b28129187 Using PacketType as a checklist, implement most of the base outgoing packets.
Add new REMAINING_BYTES type.
Add an outline of what ItemType will be like
Add a passthrough option to PacketWrapper to make it easy to passthrough and grab the type.
By default passthrough all the bytes :)
2016-03-13 17:01:36 +00:00
Mats 78ab7c840a Fix #209 woops my fault 2016-03-13 16:48:07 +01:00
Myles 669ec3f453 Merge pull request #208 from Matsv/drop
Drop SpaceHQ mcprotocol
2016-03-13 15:17:20 +00:00
Mats dd32b46055 Remove unused imports 2016-03-13 16:13:45 +01:00
Mats 7daa43146e Drop spacehq MC protocol since it's no longer used 2016-03-13 16:12:31 +01:00
Myles 89d5f2cc4f Merge pull request #207 from Matsv/armorattfix
Fix armor not showing on Respawn (With keepinventory true) & world change
2016-03-13 14:30:56 +00:00
Mats 53fb753284 Fix armor not showing on Respawn (With keepinventory true) & world change 2016-03-13 15:22:23 +01:00
Myles 81d89d3839 Rename methods in UserConnection, add new PacketHandler.
Change the protection of some methods to be appropriate.
Actually register packets
Save entity types
2016-03-13 13:40:53 +00:00
Myles 469d81df72 Merge pull request #206 from Matsv/cmdfix
Fix unable to open commandblock after respawn/world change
2016-03-13 13:18:22 +00:00
Mats 7d7f5a0bda Fix unable to open commandblock after respawn/world change 2016-03-13 14:02:10 +01:00
Myles 9954195a8f Merge pull request #205 from Matsv/master
Fix commandblocks not able to change command on update
2016-03-13 12:26:40 +00:00
Mats 62d210de68 Fix commandblocks not able to change command on every-tick commandblock update 2016-03-13 11:06:18 +01:00
Myles e19a2920b9 API: Work towards rewriting metadata, (This is nowhere near finished and I need to work on Item and NBT), Also change the names of a few classes to be more appropriate and implement transformation of the doubles.
(I probably messed up somewhere, nothing is tested as this is essentially API design)
2016-03-12 22:33:43 +00:00
Myles 8d2189cb8e Bump versions 2016-03-12 20:52:46 +00:00
Myles 44a044848f Fix ServerConnection #198 2016-03-12 20:48:04 +00:00
Myles 4f133366e1 Add limitation to NBT data, we'll stick with this for now. I don't see this easily being beaten. 2016-03-12 20:41:04 +00:00
Myles 7c66507960 Merge pull request #200 from lenis0012/chunks
Implement new chunk transformer
2016-03-12 20:27:44 +00:00
Lennart ten Wolde e436988303 transform chunk bulk packets 2016-03-12 15:47:35 +01:00
Lennart ten Wolde 8b65efc4bd Implement new chunk transformer 2016-03-12 13:58:10 +01:00
Myles 7cff20f218 Intitial APIv2, this does not work. Type conversion is not implemented, as well as quite a few things, most things will probably change. 2016-03-11 23:07:13 +00:00
Myles 131517105e Change README 2016-03-11 23:05:47 +00:00
Myles ae2592f3d1 Update to 1.9.1 PRE 2 :) 2016-03-11 16:20:15 +00:00
Myles c009827622 Merge pull request #192 from HugoDaBosss/optimize2
Optimize packet searching
2016-03-11 12:55:15 +00:00
HugoDaBosss afe18379e0 Optimize packet searching 2016-03-11 11:48:42 +01:00
Myles 5ac61fa71d Must of missed this ;) 0.6.3 in desc 2016-03-10 22:52:29 +00:00
HugoDaBosss 3511f9a7b2 Optimized packet searching 2016-03-10 23:13:23 +01:00
Myles e3f7493616 Merge pull request #191 from Matsv/master
Fix Bungeecord "Team viaversion already exists in this scoreboard" and might fix #172
2016-03-10 19:47:43 +00:00
Mats a769599448 Fix Bungeecord "Team viaversion already exists in this scoreboard" and might fix #86 2016-03-10 20:33:32 +01:00
Myles 5df8a1e8e7 Bump version 2016-03-10 16:47:52 +00:00
Myles a3d124b552 Merge pull request #190 from lenis0012/master
Add config wrapper to update config
2016-03-10 16:44:20 +00:00
Lennart ten Wolde 6d95af3963 Add config wrapper to update config
Configuration reads old config and moves it's values over to a new
config copied from the resources directory.

normally, when you save it would delete the comments, but they are
written back in to the new config by this wrapper

now people their config files will actually update
2016-03-10 17:40:40 +01:00
Myles c08a38ff21 Merge pull request #189 from lenis0012/master
fix metadata type mismatch
2016-03-10 16:21:02 +00:00
Lennart ten Wolde 4a804bae7c fix metadata type mismatch & add anti-flicker 2016-03-10 16:52:31 +01:00
Myles fd42c7eba6 Merge pull request #188 from lenis0012/master
fixed default boss title
2016-03-10 14:15:30 +00:00
Lennart ten Wolde 1af86d1354 fixed default boss title 2016-03-10 15:14:27 +01:00
Myles 1b77e1c89f Fix BarAPI, (adds new metaindex, and change colour to be right and remove debug) 2016-03-10 14:07:27 +00:00
Myles c42b9351b8 Remove sync-chunks 2016-03-10 13:25:49 +00:00
Myles ab8c8cd7c8 Merge pull request #187 from lenis0012/chunks
Add chunk handler to netty pipeline. Fixes #48
2016-03-10 13:22:48 +00:00
Myles 83518b92fe Make bossbar patch a configurable option 2016-03-10 13:22:32 +00:00
Myles 1bf1e8981c Merge pull request #186 from lenis0012/master
Add support for 1.8 boss bars. Fixes #41
2016-03-10 13:13:55 +00:00
Lennart ten Wolde b1f076936d Add chunk handler to netty pipeline. FIxes #48 2016-03-10 12:47:09 +01:00
Lennart ten Wolde 319e793cd6 Add support for 1.8 boss bars
Automatically converts legacy boss bars to 1.9 ones using ViaVersions's
boss API.

I suggest you add a config option
Note: also fixed crash on wither/dragon spawn
2016-03-10 03:14:30 +01:00
Myles c9e1360951 Version bump 2016-03-09 21:40:11 +00:00
Myles c11ad7930f Tiny cleanup allow it to be disabled via config and add lenis0012 to contributors <3 2016-03-09 21:30:43 +00:00
Myles 7b36a9de51 Merge pull request #185 from lenis0012/master
Manually perform idle updates. Fixed #160 #99
2016-03-09 21:21:59 +00:00
Lennart ten Wolde d5e9170109 Manually perform idle updates. Fixed #160 #99
Manually performs idle updates by keeping track of the player's
movement.
It compensates for the missing idle packets attempting to get a ratio of
20 movement related packets per second.
2016-03-09 22:17:17 +01:00
Myles 47b447f195 Add the option to use a hologram patch to fix invisible holograms being in wrong place, by default disabled. 2016-03-09 21:08:50 +00:00
Myles e2d27df1a8 Add another build server 2016-03-09 14:37:21 +00:00
Myles 9a781d940f Bump version 2016-03-09 14:29:30 +00:00
Myles fed36c9487 synchronize all the things! 2016-03-09 14:12:50 +00:00
Myles 463327c2a7 Bump version to 0.6.1 snapshot 2016-03-09 12:09:24 +00:00
Myles b0050d4a8d Add config option for blocking and fix crash due to synchronization 2016-03-09 12:08:21 +00:00
Myles a743895c3f Bump version to 0.6.0-SNAPSHOT 2016-03-09 01:13:24 +00:00
Myles f65666b7a6 Better support for ServerConnection detection, and also adding injectors. (Basic late-bind support is now available, but does not work with ProtocolLib due to it injecting first.) 2016-03-09 00:51:50 +00:00
Myles 3a217503fc Merge pull request #179 from HugoDaBosss/master
Stop error fetching hand item spamming console.
2016-03-08 21:24:47 +00:00
Myles c71b75e13b Oops 2016-03-08 21:23:57 +00:00
HugoDaBosss 0c890c3a30 Print errors while in debug mode 2016-03-08 22:23:36 +01:00
HugoDaBosss f38bb0ca22 Stop error fetching hand item spamming console. 2016-03-08 22:20:52 +01:00
Myles 2144fa1837 Handle any exception so it doesn't implode. (Still prints to the console.) 2016-03-08 21:16:34 +00:00
Myles fc4a617da7 Don't send chunk packets with no chunks :) 2016-03-08 21:13:15 +00:00
Myles 126ee6e03f Formatting 2016-03-08 20:54:03 +00:00
Myles 498867032f Merge pull request #178 from HugoDaBosss/master
Fix animation for bows
2016-03-08 20:51:02 +00:00
HugoDaBosss aef8eca790 Fix animation for bows 2016-03-08 21:48:21 +01:00
Myles d8bbb51721 Merge branch 'master' of https://github.com/MylesIsCool/ViaVersion 2016-03-08 19:27:24 +00:00
Myles 6e4fd5a63b Only show blocking if it knows that we're blocking #176 2016-03-08 19:27:04 +00:00
Myles e7bd6245c2 Merge pull request #175 from Matsv/master
Reimplement bossbar methods
2016-03-08 14:02:18 +00:00
Mats 6242cb0b47 Merge remote-tracking branch 'origin/master' 2016-03-08 13:48:20 +01:00
Mats bf5072e1cf Add removed bossbar methods 2016-03-08 13:48:03 +01:00
Mats c24b673550 Add removed bossbar methods 2016-03-08 13:46:30 +01:00
Myles dfc1e1cf51 Merge pull request #174 from Matsv/master
Add /viaversion debug to the commandlist
2016-03-08 12:42:16 +00:00
Mats c600ab6dd1 Add /viaversion debug to the commandlist 2016-03-08 13:30:53 +01:00
164 changed files with 12103 additions and 3613 deletions

No files matched your search

+22 -1
View File
@@ -1,3 +1,24 @@
License:
--------
You may modify this work, all rights are still owned by myself excluding any files in the spacebase package as they belong to the respected contributors. Please credit the original author (myself) when forking etc. (or if it helped you understand the new protocol or netty!)
MIT License
Copyright (c) 2016
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+22 -13
View File
@@ -1,34 +1,34 @@
# ViaVersion 0.5.8
# ViaVersion 0.9.5
[![Build Status](https://travis-ci.org/MylesIsCool/ViaVersion.svg?branch=master)](https://travis-ci.org/MylesIsCool/ViaVersion)
**Allows the connection of 1.9 clients to 1.8**
[![Gitter](https://badges.gitter.im/MylesIsCool/ViaVersion.svg)](https://gitter.im/MylesIsCool/ViaVersion)
This plugin modifies netty to allow connection of 1.9 clients to 1.8,
**Allows the connection of higher client versions to lower server versions**
###*Don't use late bind*
Allows 1.10, 1.9 on 1.8 server and 1.10 on a 1.9 server.
This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects.
**User Docs:** https://docs.viaversion.com/display/VIAVERSION/
**Contributor Chat:** https://gitter.im/MylesIsCool/ViaVersion
Sources:
--------
**wiki.vg** (Protocol info, though I did have to find a lot myself)
**MCProtocolLib** (used for chunk reading & some of writing.)
**Burger** We use this tool to get protocol and other useful information for new / undocumented Minecraft versions
**OpenNBT** (used for slot rewriting)
Contributors:
All Time Contributors:
--------
**Myself** (harhar)
**Matsv/StamBoom**
**Matsv**
**HugoDaBosss**
@@ -40,6 +40,12 @@ Contributors:
**fillefilip8**
**lenis0012**
**fuzzy_bot**
**joserobjr**
Releases / Dev Builds:
@@ -51,15 +57,18 @@ https://www.spigotmc.org/resources/viaversion.19254/history
----------
You can find unofficial dev builds here:
You can find official dev builds here:
**fillefilip8's Build Server:** http://build.fillecraft.com:8080/job/ViaVersion
**Jenkins:** https://ci.viaversion.com
**JavaDocs:** https://jd.viaversion.com
**Maven Repo:** https://repo.viaversion.com
License:
--------
You may modify this work, all rights are still owned by myself excluding any files in the spacebase package as they belong to the respected contributors. Please credit the original author (myself) when forking etc. (or if it helped you understand the new protocol or netty!)
MIT License, see LICENSE for more details.
Thanks!
+43 -1
View File
@@ -6,7 +6,7 @@
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>0.5.9-SNAPSHOT</version>
<version>0.9.5</version>
<packaging>jar</packaging>
<name>ViaVersion</name>
@@ -33,6 +33,7 @@
<build>
<finalName>${project.name}-${project.version}</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<defaultGoal>clean install</defaultGoal>
<!-- <testSourceDirectory>src/test/java</testSourceDirectory> -->
<resources>
@@ -101,6 +102,14 @@
<pattern>org.spacehq.opennbt</pattern>
<shadedPattern>us.myles.viaversion.libs.opennbt</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>us.myles.viaversion.libs.gson</shadedPattern>
</relocation>
<relocation>
<pattern>org.javassist</pattern>
<shadedPattern>us.myles.viaversion.libs.javassist</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
@@ -127,6 +136,12 @@
<id>spacehq-repo</id>
<url>https://repo.spacehq.org/content/repositories/releases/</url>
</repository>
<!-- Bungee repo -->
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
@@ -153,6 +168,15 @@
</exclusions>
</dependency>
<!-- Javassist (Bytecode Library) -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- NBT Edit Library -->
<dependency>
<groupId>org.spacehq</groupId>
@@ -162,6 +186,15 @@
<optional>true</optional>
</dependency>
<!-- GSON (JSON Library) -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- Netty (Network Library) -->
<dependency>
<groupId>io.netty</groupId>
@@ -170,6 +203,7 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
@@ -177,5 +211,13 @@
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
<!--Bungeecord-->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.9-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,126 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.util.PacketUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BlockStorage {
private int bitsPerEntry;
private List<Integer> states;
private FlexibleStorage storage;
public BlockStorage() {
this.bitsPerEntry = 4;
this.states = new ArrayList<>();
this.states.add(0);
this.storage = new FlexibleStorage(this.bitsPerEntry, 4096);
}
public BlockStorage(ByteBuf in) throws IOException {
this.bitsPerEntry = in.readUnsignedByte();
this.states = new ArrayList<>();
int stateCount = PacketUtil.readVarInt(in);
for (int i = 0; i < stateCount; i++) {
this.states.add(PacketUtil.readVarInt(in));
}
this.storage = new FlexibleStorage(this.bitsPerEntry, PacketUtil.readLongs(PacketUtil.readVarInt(in), in));
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
public void write(ByteBuf out) throws IOException {
out.writeByte(this.bitsPerEntry);
PacketUtil.writeVarInt(this.states.size(), out);
for (int state : this.states) {
PacketUtil.writeVarInt(state, out);
}
long[] data = this.storage.getData();
PacketUtil.writeVarInt(data.length, out);
PacketUtil.writeLongs(data, out);
}
public int getBitsPerEntry() {
return this.bitsPerEntry;
}
public List<Integer> getStates() {
return Collections.unmodifiableList(this.states);
}
public FlexibleStorage getStorage() {
return this.storage;
}
public int get(int x, int y, int z) {
int id = this.storage.get(index(x, y, z));
return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.states.size() ? this.states.get(id) : 0) : id;
}
public void set(int x, int y, int z, int state) {
set(index(x, y, z), state);
}
public void set(int ind, int state) {
int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
if (id == -1) {
this.states.add(state);
if (this.states.size() > 1 << this.bitsPerEntry) {
this.bitsPerEntry++;
List<Integer> oldStates = this.states;
if (this.bitsPerEntry > 8) {
oldStates = new ArrayList<>(this.states);
this.states.clear();
this.bitsPerEntry = 13;
}
FlexibleStorage oldStorage = this.storage;
this.storage = new FlexibleStorage(this.bitsPerEntry, this.storage.getSize());
for (int index = 0; index < this.storage.getSize(); index++) {
int value = oldStorage.get(index);
this.storage.set(index, this.bitsPerEntry <= 8 ? value : oldStates.get(value));
}
}
id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state;
}
this.storage.set(ind, id);
}
public boolean isEmpty() {
for (int index = 0; index < this.storage.getSize(); index++) {
if (this.storage.get(index) != 0) {
return false;
}
}
return true;
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof BlockStorage && this.bitsPerEntry == ((BlockStorage) o).bitsPerEntry && this.states.equals(((BlockStorage) o).states) && this.storage.equals(((BlockStorage) o).storage));
}
@Override
public int hashCode() {
int result = this.bitsPerEntry;
result = 31 * result + this.states.hashCode();
result = 31 * result + this.storage.hashCode();
return result;
}
}
@@ -1,46 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
public class Chunk {
private BlockStorage blocks;
private NibbleArray3d blocklight;
private NibbleArray3d skylight;
public Chunk(boolean skylight) {
this(new BlockStorage(), new NibbleArray3d(2048), skylight ? new NibbleArray3d(2048) : null);
}
public Chunk(BlockStorage blocks, NibbleArray3d blocklight, NibbleArray3d skylight) {
this.blocks = blocks;
this.blocklight = blocklight;
this.skylight = skylight;
}
public BlockStorage getBlocks() {
return this.blocks;
}
public NibbleArray3d getBlockLight() {
return this.blocklight;
}
public NibbleArray3d getSkyLight() {
return this.skylight;
}
public boolean isEmpty() {
return this.blocks.isEmpty();
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof Chunk && this.blocks.equals(((Chunk) o).blocks) && this.blocklight.equals(((Chunk) o).blocklight) && ((this.skylight == null && (((Chunk) o).skylight == null)) || (this.skylight != null && this.skylight.equals(((Chunk) o).skylight))));
}
@Override
public int hashCode() {
int result = this.blocks.hashCode();
result = 31 * result + this.blocklight.hashCode();
result = 31 * result + (this.skylight != null ? this.skylight.hashCode() : 0);
return result;
}
}
@@ -1,59 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Column {
private int x;
private int z;
private Chunk chunks[];
private byte biomeData[];
private boolean skylight;
public Column(int x, int z, Chunk chunks[]) {
this(x, z, chunks, null);
}
public Column(int x, int z, Chunk chunks[], byte biomeData[]) {
if(chunks.length != 16) {
throw new IllegalArgumentException("Chunk array length must be 16.");
}
if(biomeData != null && biomeData.length != 256) {
throw new IllegalArgumentException("Biome data array length must be 256.");
}
this.skylight = false;
boolean noSkylight = false;
for (Chunk chunk : chunks) {
if (chunk != null) {
if (chunk.getSkyLight() == null) {
noSkylight = true;
} else {
this.skylight = true;
}
}
}
if(noSkylight && this.skylight) {
throw new IllegalArgumentException("Either all chunks must have skylight values or none must have them.");
}
this.x = x;
this.z = z;
this.chunks = chunks;
this.biomeData = biomeData;
}
public boolean hasBiomeData() {
return this.biomeData != null;
}
public boolean hasSkylight() {
return this.skylight;
}
}
@@ -1,104 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import java.util.Arrays;
public class FlexibleStorage {
private final long[] data;
private final int bitsPerEntry;
private final int size;
private final long maxEntryValue;
public FlexibleStorage(int bitsPerEntry, int size) {
this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]);
}
public FlexibleStorage(int bitsPerEntry, long[] data) {
if(bitsPerEntry < 1 || bitsPerEntry > 32) {
throw new IllegalArgumentException("BitsPerEntry cannot be outside of accepted range.");
}
this.bitsPerEntry = bitsPerEntry;
this.data = data;
this.size = this.data.length * 64 / this.bitsPerEntry;
this.maxEntryValue = (1L << this.bitsPerEntry) - 1;
}
public long[] getData() {
return this.data;
}
public int getBitsPerEntry() {
return this.bitsPerEntry;
}
public int getSize() {
return this.size;
}
public int get(int index) {
if(index < 0 || index > this.size - 1) {
throw new IndexOutOfBoundsException();
}
int bitIndex = index * this.bitsPerEntry;
int startIndex = bitIndex / 64;
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
int startBitSubIndex = bitIndex % 64;
if(startIndex == endIndex) {
return (int) (this.data[startIndex] >>> startBitSubIndex & this.maxEntryValue);
} else {
int endBitSubIndex = 64 - startBitSubIndex;
return (int) ((this.data[startIndex] >>> startBitSubIndex | this.data[endIndex] << endBitSubIndex) & this.maxEntryValue);
}
}
public void set(int index, int value) {
if(index < 0 || index > this.size - 1) {
throw new IndexOutOfBoundsException();
}
if(value < 0 || value > this.maxEntryValue) {
throw new IllegalArgumentException("Value cannot be outside of accepted range.");
}
int bitIndex = index * this.bitsPerEntry;
int startIndex = bitIndex / 64;
int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64;
int startBitSubIndex = bitIndex % 64;
this.data[startIndex] = this.data[startIndex] & ~(this.maxEntryValue << startBitSubIndex) | ((long) value & this.maxEntryValue) << startBitSubIndex;
if(startIndex != endIndex) {
int endBitSubIndex = 64 - startBitSubIndex;
this.data[endIndex] = this.data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & this.maxEntryValue) >> endBitSubIndex;
}
}
private static int roundToNearest(int value, int roundTo) {
if(roundTo == 0) {
return 0;
} else if(value == 0) {
return roundTo;
} else {
if(value < 0) {
roundTo *= -1;
}
int remainder = value % roundTo;
return remainder != 0 ? value + roundTo - remainder : value;
}
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof FlexibleStorage && Arrays.equals(this.data, ((FlexibleStorage) o).data) && this.bitsPerEntry == ((FlexibleStorage) o).bitsPerEntry && this.size == ((FlexibleStorage) o).size && this.maxEntryValue == ((FlexibleStorage) o).maxEntryValue);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(this.data);
result = 31 * result + this.bitsPerEntry;
result = 31 * result + this.size;
result = 31 * result + (int) this.maxEntryValue;
return result;
}
}
@@ -1,71 +0,0 @@
package org.spacehq.mc.protocol.data.game.chunk;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.util.Arrays;
public class NibbleArray3d {
private byte[] data;
public NibbleArray3d(int size) {
this.data = new byte[size];
}
public NibbleArray3d(byte[] array) {
this.data = array;
}
public NibbleArray3d(ByteBuf in, int size) throws IOException {
this.data = new byte[size];
in.readBytes(this.data);
}
public void write(ByteBuf out) throws IOException {
out.writeBytes(this.data);
}
public byte[] getData() {
return this.data;
}
public int get(int x, int y, int z) {
int key = y << 8 | z << 4 | x;
int index = key >> 1;
int part = key & 1;
return part == 0 ? this.data[index] & 15 : this.data[index] >> 4 & 15;
}
public void set(int x, int y, int z, int val) {
int key = y << 8 | z << 4 | x;
int index = key >> 1;
int part = key & 1;
if(part == 0) {
this.data[index] = (byte) (this.data[index] & 240 | val & 15);
} else {
this.data[index] = (byte) (this.data[index] & 15 | (val & 15) << 4);
}
}
public void fill(int val) {
for(int index = 0; index < this.data.length << 1; index++) {
int ind = index >> 1;
int part = index & 1;
if(part == 0) {
this.data[ind] = (byte) (this.data[ind] & 240 | val & 15);
} else {
this.data[ind] = (byte) (this.data[ind] & 15 | (val & 15) << 4);
}
}
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof NibbleArray3d && Arrays.equals(this.data, ((NibbleArray3d) o).data));
}
@Override
public int hashCode() {
return Arrays.hashCode(this.data);
}
}
@@ -1,114 +0,0 @@
package org.spacehq.mc.protocol.util;
import io.netty.buffer.ByteBuf;
import org.spacehq.mc.protocol.data.game.chunk.Chunk;
import org.spacehq.mc.protocol.data.game.chunk.Column;
import org.spacehq.mc.protocol.data.game.chunk.NibbleArray3d;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
/* From https://github.com/Steveice10/MCProtocolLib/ */
/* No credit taken for writing this code, and used accordance to it's license
Original by Steveice10, modified to suit this plugin.
*/
public class NetUtil {
public static int writeNewColumn(ByteBuf out, Column column, boolean fullChunk, boolean hasSkylight) throws IOException {
int mask = 0;
Chunk chunks[] = column.getChunks();
for (int index = 0; index < chunks.length; index++) {
Chunk chunk = chunks[index];
if (chunk != null && (!fullChunk || !chunk.isEmpty())) {
mask |= 1 << index;
chunk.getBlocks().write(out);
chunk.getBlockLight().write(out);
if (hasSkylight || column.hasSkylight()) {
chunk.getSkyLight().write(out); // TODO: Make a PR to original lib to correct this
}
}
}
if (fullChunk && column.getBiomeData() != null) {
out.writeBytes(column.getBiomeData());
}
return mask;
}
public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) {
int pos = 0;
int expected = isFullChunk ? 256 : 0;
boolean sky = false;
ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
// 0 = Calculate expected length and determine if the packet has skylight.
// 1 = Create chunks from mask and get blocks.
// 2 = Get block light.
// 3 = Get sky light.
Chunk[] chunks = new Chunk[16];
for (int pass = 0; pass < 4; pass++) {
for (int ind = 0; ind < 16; ind++) {
if ((bitmask & 1 << ind) != 0) {
if (pass == 0) {
// Block length + Blocklight length
expected += (4096 * 2) + 2048;
}
if (pass == 1) {
chunks[ind] = new Chunk(sky || hasSkyLight);
buf.position(pos / 2);
int buffPos = buf.position();
// convert short array to new one
for (int index = 0; index < 4096; index++) {
short ss = buf.get(buffPos + index);
// s is 16 bits, 12 bits id and 4 bits data
int data = ss & 0xF;
int id = (ss >> 4) << 4 | data;
int newCombined = id; // test
chunks[ind].getBlocks().set(index, newCombined);
}
pos += 4096 * 2;
}
if (pass == 2) {
NibbleArray3d blocklight = chunks[ind].getBlockLight();
System.arraycopy(input, pos, blocklight.getData(), 0, blocklight.getData().length);
pos += blocklight.getData().length;
}
if (pass == 3) {
if (sky) {
NibbleArray3d skylight = chunks[ind].getSkyLight();
System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length);
pos += skylight.getData().length;
}
}
}
}
if (pass == 0) {
// If we have more data than blocks and blocklight combined, there must be skylight data as well.
if (input.length > expected) {
sky = checkForSky;
}
}
}
byte biomeData[] = null;
if (isFullChunk && (pos + 256 <= input.length)) {
biomeData = new byte[256];
System.arraycopy(input, pos, biomeData, 0, biomeData.length);
}
Column column = new Column(x, z, chunks, biomeData);
return column;
}
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion;
import net.md_5.bungee.api.plugin.Plugin;
import static net.md_5.bungee.api.ChatColor.RED;
public class Bungee extends Plugin {
@Override
public void onEnable() {
getLogger().severe(RED + "===============================================");
getLogger().severe(RED + "ViaVersion is NOT a Bungeecord plugin");
getLogger().severe(RED + "Install this plugin on all your spigot/bukkit");
getLogger().severe(RED + "servers and use the latest Bungeecord version");
getLogger().severe(RED + "to make ViaVersion work with BungeeCord.");
getLogger().severe(RED + "===============================================");
}
}
@@ -1,59 +0,0 @@
package us.myles.ViaVersion;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.packets.State;
@Getter
@Setter
public class ConnectionInfo {
private final SocketChannel channel;
private Object lastPacket;
private java.util.UUID UUID;
private State state = State.HANDSHAKE;
private String openWindow;
private int protocol = 0;
private int compression = 0;
private int entityID;
private boolean active = true;
private String username;
public ConnectionInfo(SocketChannel socketChannel) {
this.channel = socketChannel;
}
public Player getPlayer() {
return UUID == null ? null : Bukkit.getPlayer(UUID);
}
public void setActive(boolean active) {
this.active = active;
}
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
final ChannelHandler handler = channel.pipeline().get("encoder");
if (currentThread) {
channel.pipeline().context(handler).writeAndFlush(packet);
} else {
channel.eventLoop().submit(new Runnable() {
@Override
public void run() {
channel.pipeline().context(handler).writeAndFlush(packet);
}
});
}
}
public void sendRawPacket(final ByteBuf packet) {
sendRawPacket(packet, false);
}
public void closeWindow() {
this.openWindow = null;
}
}
@@ -0,0 +1,183 @@
package us.myles.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionConfig;
import us.myles.ViaVersion.util.Configuration;
import java.io.File;
import java.util.List;
public class ViaConfig implements ViaVersionConfig {
private final ViaVersionPlugin plugin;
public ViaConfig(ViaVersionPlugin plugin) {
this.plugin = plugin;
generateConfig();
}
public void generateConfig() {
File file = new File(plugin.getDataFolder(), "config.yml");
if (file.exists()) {
// Update conf options
Configuration oldConfig = new Configuration(file);
oldConfig.reload(false); // Load current options from conf
file.delete(); // Delete old conf
plugin.saveDefaultConfig(); // Generate new conf
Configuration newConfig = new Configuration(file);
newConfig.reload(true); // Load default options
for (String key : oldConfig.getKeys(false)) {
// Set option in new conf if exists
if (newConfig.contains(key)) {
newConfig.set(key, oldConfig.get(key));
}
}
newConfig.save();
} else {
plugin.saveDefaultConfig();
}
}
@Override
public boolean isCheckForUpdates() {
return plugin.getConfig().getBoolean("checkforupdates", true);
}
@Override
public boolean isPreventCollision() {
return plugin.getConfig().getBoolean("prevent-collision", true);
}
@Override
public boolean isNewEffectIndicator() {
return plugin.getConfig().getBoolean("use-new-effect-indicator", true);
}
@Override
public boolean isShowNewDeathMessages() {
return plugin.getConfig().getBoolean("use-new-deathmessages", false);
}
@Override
public boolean isSuppressMetadataErrors() {
return plugin.getConfig().getBoolean("suppress-metadata-errors", false);
}
@Override
public boolean isShieldBlocking() {
return plugin.getConfig().getBoolean("shield-blocking", true);
}
@Override
public boolean isHologramPatch() {
return plugin.getConfig().getBoolean("hologram-patch", false);
}
@Override
public boolean isBossbarPatch() {
return plugin.getConfig().getBoolean("bossbar-patch", true);
}
@Override
public boolean isBossbarAntiflicker() {
return plugin.getConfig().getBoolean("bossbar-anti-flicker", false);
}
@Override
public boolean isUnknownEntitiesSuppressed() {
return false;
}
@Override
public double getHologramYOffset() {
return plugin.getConfig().getDouble("hologram-y", -0.96D);
}
@Override
public boolean isBlockBreakPatch() {
return false;
}
@Override
public int getMaxPPS() {
return plugin.getConfig().getInt("max-pps", 140);
}
@Override
public String getMaxPPSKickMessage() {
return plugin.getConfig().getString("max-pps-kick-msg", "Sending packets too fast? lag?");
}
@Override
public int getTrackingPeriod() {
return plugin.getConfig().getInt("tracking-period", 6);
}
@Override
public int getWarningPPS() {
return plugin.getConfig().getInt("tracking-warning-pps", 120);
}
@Override
public int getMaxWarnings() {
return plugin.getConfig().getInt("tracking-max-warnings", 3);
}
@Override
public String getMaxWarningsKickMessage() {
return plugin.getConfig().getString("tracking-max-kick-msg", "You are sending too many packets, :(");
}
@Override
public boolean isAntiXRay() {
return plugin.getConfig().getBoolean("anti-xray-patch", true);
}
@Override
public boolean isSendSupportedVersions() {
return plugin.getConfig().getBoolean("send-supported-versions", false);
}
@Override
public boolean isStimulatePlayerTick() {
return plugin.getConfig().getBoolean("simulate-pt", true);
}
@Override
public boolean isItemCache() {
return plugin.getConfig().getBoolean("item-cache", true);
}
@Override
public boolean isNMSPlayerTicking() {
return plugin.getConfig().getBoolean("nms-player-ticking", true);
}
@Override
public boolean isReplacePistons() {
return plugin.getConfig().getBoolean("replace-pistons", false);
}
@Override
public int getPistonReplacementId() {
return plugin.getConfig().getInt("replacement-piston-id", 0);
}
public boolean isAutoTeam() {
// Collision has to be enabled first
return isPreventCollision() && plugin.getConfig().getBoolean("auto-team", true);
}
@Override
public boolean isForceJsonTransform() {
return plugin.getConfig().getBoolean("force-json-transform", false);
}
@Override
public List<Integer> getBlockedProtocols() {
return plugin.getConfig().getIntegerList("block-protocols");
}
@Override
public String getBlockedDisconnectMsg() {
return plugin.getConfig().getString("block-disconnect-msg", "You are using an unsupported Minecraft version!");
}
}
@@ -4,33 +4,37 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.ViaVersionAPI;
import us.myles.ViaVersion.api.boss.BossBar;
import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.armor.ArmorListener;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.boss.ViaBossBar;
import us.myles.ViaVersion.commands.ViaVersionCommand;
import us.myles.ViaVersion.classgenerator.ClassGenerator;
import us.myles.ViaVersion.commands.ViaCommandHandler;
import us.myles.ViaVersion.handlers.ViaVersionInitializer;
import us.myles.ViaVersion.listeners.CommandBlockListener;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.update.UpdateListener;
import us.myles.ViaVersion.update.UpdateUtil;
import us.myles.ViaVersion.util.ConcurrentList;
import us.myles.ViaVersion.util.ListWrapper;
import us.myles.ViaVersion.util.ProtocolSupportUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
@@ -38,96 +42,325 @@ import java.util.concurrent.TimeUnit;
public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
private final Map<UUID, ConnectionInfo> portedPlayers = new ConcurrentHashMap<>();
private final Map<UUID, UserConnection> portedPlayers = new ConcurrentHashMap<>();
private List<ChannelFuture> injectedFutures = new ArrayList<>();
private List<Pair<Field, Object>> injectedLists = new ArrayList<>();
private ViaCommandHandler commandHandler;
private boolean debug = false;
private boolean compatSpigotBuild = false;
private boolean spigot = true;
private boolean lateBind = false;
private boolean protocolSupport = false;
@Getter
private ViaConfig conf;
public static ItemStack getHandItem(final ConnectionInfo info) {
try {
return Bukkit.getScheduler().callSyncMethod(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Callable<ItemStack>() {
@Override
public ItemStack call() throws Exception {
if (info.getPlayer() != null) {
return info.getPlayer().getItemInHand();
}
return null;
}
}).get(10, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("Error fetching hand item ");
e.printStackTrace();
return null;
public ViaVersionPlugin() {
// Check if we're using protocol support too
protocolSupport = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
if (protocolSupport) {
getLogger().info("Patching to prevent concurrency issues...");
try {
patchLists();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onEnable() {
public void onLoad() {
// Config magic
conf = new ViaConfig(this);
ViaVersion.setInstance(this);
saveDefaultConfig();
// Handle reloads
if (System.getProperty("ViaVersion") != null) {
getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update.");
getLogger().severe("Some features may not work.");
return;
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
getLogger().severe("ViaVersion is already loaded, we're going to kick all the players... because otherwise we'll crash because of ProtocolLib.");
for (Player player : Bukkit.getOnlinePlayers()) {
player.kickPlayer("Server reload, please rejoin!");
}
} else {
getLogger().severe("ViaVersion is already loaded, this should work fine... Otherwise reboot the server!!!");
}
}
getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)");
// Spigot detector
try {
Class.forName("org.spigotmc.SpigotConfig");
} catch (ClassNotFoundException e) {
spigot = false;
}
// Check if it's a spigot build with a protocol mod
try {
compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null;
} catch (Exception e) {
compatSpigotBuild = false;
}
// Generate classes needed (only works if it's compat or ps)
ClassGenerator.generate();
lateBind = !isBinded();
getLogger().info("ViaVersion " + getDescription().getVersion() + (compatSpigotBuild ? "compat" : "") + " is now loaded" + (lateBind ? ", waiting for boot. (late-bind)" : ", injecting!"));
if (!lateBind)
injectPacketHandler();
}
@Override
public void onEnable() {
if (lateBind)
injectPacketHandler();
if (conf.isCheckForUpdates())
UpdateUtil.sendUpdateMessage(this);
// Gather version :)
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
@Override
public void run() {
gatherProtocolVersion();
// Check if there are any pipes to this version
if (ProtocolRegistry.SERVER_PROTOCOL != -1) {
getLogger().info("ViaVersion detected protocol version: " + ProtocolRegistry.SERVER_PROTOCOL);
if (!ProtocolRegistry.isWorkingPipe()) {
getLogger().warning("ViaVersion will not function on the current protocol.");
}
}
ProtocolRegistry.refreshVersions();
}
});
Bukkit.getPluginManager().registerEvents(new UpdateListener(this), this);
getCommand("viaversion").setExecutor(commandHandler = new ViaCommandHandler());
getCommand("viaversion").setTabCompleter(commandHandler);
// Register Protocol Listeners
ProtocolRegistry.registerListeners();
// Warn them if they have anti-xray on and they aren't using spigot
if (conf.isAntiXRay() && !spigot) {
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
}
}
@Override
public void onDisable() {
getLogger().info("ViaVersion is disabling, if this is a reload it may not work.");
uninject();
}
public void gatherProtocolVersion() {
try {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Class<?> pingClazz = ReflectionUtil.nms("ServerPing");
Object ping = null;
// Search for ping method
for (Field f : serverClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().equals("ServerPing")) {
f.setAccessible(true);
ping = f.get(server);
}
}
}
if (ping != null) {
Object serverData = null;
for (Field f : pingClazz.getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType().getSimpleName().endsWith("ServerData")) {
f.setAccessible(true);
serverData = f.get(ping);
}
}
}
if (serverData != null) {
int protocolVersion = -1;
for (Field f : serverData.getClass().getDeclaredFields()) {
if (f.getType() != null) {
if (f.getType() == int.class) {
f.setAccessible(true);
protocolVersion = (int) f.get(serverData);
}
}
}
if (protocolVersion != -1) {
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
return;
}
}
}
} catch (Exception e) {
e.printStackTrace();
// We couldn't work it out... We'll just use ping and hope for the best...
}
}
public Object getServerConnection() throws Exception {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Object connection = null;
for (Method m : serverClazz.getDeclaredMethods()) {
if (m.getReturnType() != null) {
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
if (m.getParameterTypes().length == 0) {
connection = m.invoke(server);
}
}
}
}
return connection;
}
public void injectPacketHandler() {
try {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
List wrapper = new ListWrapper((List) value) {
@Override
public synchronized void handleAdd(Object o) {
synchronized (this) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
}
}
}
};
injectedLists.add(new Pair<>(field, connection));
field.set(connection, wrapper);
// Iterate through current list
synchronized (wrapper) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
inject((ChannelFuture) o);
} else {
break; // not the right list.
}
}
}
}
}
System.setProperty("ViaVersion", getDescription().getVersion());
} catch (Exception e) {
getLogger().severe("Unable to inject handlers, are you on 1.8? ");
e.printStackTrace();
}
if (getConfig().getBoolean("checkforupdates"))
UpdateUtil.sendUpdateMessage(this);
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e) {
removePortedClient(e.getPlayer().getUniqueId());
}
}, this);
Bukkit.getPluginManager().registerEvents(new ArmorListener(this), this);
Bukkit.getPluginManager().registerEvents(new CommandBlockListener(this), this);
Bukkit.getPluginManager().registerEvents(new UpdateListener(this), this);
getCommand("viaversion").setExecutor(new ViaVersionCommand(this));
}
public void injectPacketHandler() throws Exception {
Class<?> serverClazz = ReflectionUtil.nms("MinecraftServer");
Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer");
Object connection = serverClazz.getDeclaredMethod("getServerConnection").invoke(server);
// loop through all fields checking if list
boolean injected = false;
public void patchLists() throws Exception {
Object connection = getServerConnection();
if (connection == null) {
getLogger().warning("We failed to find the ServerConnection? :( What server are you running?");
return;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(connection);
final Object value = field.get(connection);
if (value instanceof List) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
ChannelFuture future = (ChannelFuture) o;
ChannelPipeline pipeline = future.channel().pipeline();
ChannelHandler bootstrapAcceptor = pipeline.first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
} catch (NoSuchFieldException e) {
// field not found
throw new Exception("Unable to find childHandler, blame " + bootstrapAcceptor.getClass().getName());
}
injected = true;
} else {
break; // not the right list.
}
if (!(value instanceof ConcurrentList)) {
ConcurrentList list = new ConcurrentList();
list.addAll((List) value);
field.set(connection, list);
}
}
}
if (!injected) {
throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)");
}
public boolean isBinded() {
try {
Object connection = getServerConnection();
if (connection == null) {
return false;
}
for (Field field : connection.getClass().getDeclaredFields()) {
field.setAccessible(true);
final Object value = field.get(connection);
if (value instanceof List) {
// Inject the list
synchronized (value) {
for (Object o : (List) value) {
if (o instanceof ChannelFuture) {
return true;
} else {
break; // not the right list.
}
}
}
}
}
} catch (Exception e) {
}
return false;
}
private void inject(ChannelFuture future) {
try {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
ChannelInitializer newInit = new ViaVersionInitializer(oldInit);
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
injectedFutures.add(future);
} catch (NoSuchFieldException e) {
// let's find who to blame!
ClassLoader cl = bootstrapAcceptor.getClass().getClassLoader();
if (cl.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
PluginDescriptionFile yaml = ReflectionUtil.get(cl, "description", PluginDescriptionFile.class);
throw new Exception("Unable to inject, due to " + bootstrapAcceptor.getClass().getName() + ", try without the plugin " + yaml.getName() + "?");
} else {
throw new Exception("Unable to find childHandler, weird server version? " + bootstrapAcceptor.getClass().getName());
}
}
} catch (Exception e) {
getLogger().severe("Have you got late-bind enabled with something else?");
e.printStackTrace();
}
}
private void uninject() {
// TODO: Uninject from players currently online to prevent protocol lib issues.
for (ChannelFuture future : injectedFutures) {
ChannelHandler bootstrapAcceptor = future.channel().pipeline().first();
try {
ChannelInitializer<SocketChannel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
if (oldInit instanceof ViaVersionInitializer) {
ReflectionUtil.set(bootstrapAcceptor, "childHandler", ((ViaVersionInitializer) oldInit).getOriginal());
}
} catch (Exception e) {
System.out.println("Failed to remove injection... reload won't work with connections sorry");
}
}
injectedFutures.clear();
for (Pair<Field, Object> pair : injectedLists) {
try {
Object o = pair.getKey().get(pair.getValue());
if (o instanceof ListWrapper) {
pair.getKey().set(pair.getValue(), ((ListWrapper) o).getOriginalList());
}
} catch (IllegalAccessException e) {
System.out.println("Failed to remove injection... reload might not work with connections sorry");
}
}
injectedLists.clear();
}
@Override
@@ -135,6 +368,28 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return isPorted(player.getUniqueId());
}
@Override
public int getPlayerVersion(@NonNull Player player) {
if (!isPorted(player))
return getExternalVersion(player);
return portedPlayers.get(player.getUniqueId()).get(ProtocolInfo.class).getProtocolVersion();
}
@Override
public int getPlayerVersion(@NonNull UUID uuid) {
if (!isPorted(uuid))
return getExternalVersion(Bukkit.getPlayer(uuid));
return portedPlayers.get(uuid).get(ProtocolInfo.class).getProtocolVersion();
}
private int getExternalVersion(Player player) {
if (!isProtocolSupport()) {
return ProtocolRegistry.SERVER_PROTOCOL;
} else {
return ProtocolSupportUtil.getProtocolVersion(player);
}
}
@Override
public boolean isPorted(UUID playerUUID) {
return portedPlayers.containsKey(playerUUID);
@@ -145,14 +400,22 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
return getDescription().getVersion();
}
public UserConnection getConnection(UUID playerUUID) {
return portedPlayers.get(playerUUID);
}
public UserConnection getConnection(Player player) {
return portedPlayers.get(player.getUniqueId());
}
public void sendRawPacket(Player player, ByteBuf packet) throws IllegalArgumentException {
sendRawPacket(player.getUniqueId(), packet);
}
@Override
public void sendRawPacket(UUID uuid, ByteBuf packet) throws IllegalArgumentException {
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not on 1.9");
ConnectionInfo ci = portedPlayers.get(uuid);
if (!isPorted(uuid)) throw new IllegalArgumentException("This player is not controlled by ViaVersion!");
UserConnection ci = portedPlayers.get(uuid);
ci.sendRawPacket(packet);
}
@@ -176,26 +439,30 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
}
@Override
public boolean isSyncedChunks() {
return getConfig().getBoolean("sync-chunks", true);
public ViaVersionCommand getCommandHandler() {
return commandHandler;
}
public boolean isPreventCollision() {
return getConfig().getBoolean("prevent-collision", true);
@Override
public boolean isCompatSpigotBuild() {
return compatSpigotBuild;
}
public boolean isSuppressMetadataErrors() {
return getConfig().getBoolean("suppress-metadata-errors", false);
@Override
public SortedSet<Integer> getSupportedVersions() {
SortedSet<Integer> outputSet = new TreeSet<>(ProtocolRegistry.getSupportedVersions());
outputSet.removeAll(getConf().getBlockedProtocols());
return outputSet;
}
public boolean isAutoTeam() {
// Collision has to be enabled first
if (!isPreventCollision()) return false;
return getConfig().getBoolean("auto-team", true);
@Override
public boolean isSpigot() {
return this.spigot;
}
public void addPortedClient(ConnectionInfo info) {
portedPlayers.put(info.getUUID(), info);
public void addPortedClient(UserConnection info) {
portedPlayers.put(info.get(ProtocolInfo.class).getUuid(), info);
}
public void removePortedClient(UUID clientID) {
@@ -215,8 +482,47 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI {
f.get(10, TimeUnit.SECONDS);
}
} catch (Exception e) {
System.out.println("Failed to run task.");
e.printStackTrace();
System.out.println("Failed to run task: " + e.getClass().getName());
if (ViaVersion.getInstance().isDebug())
e.printStackTrace();
}
}
public boolean isProtocolSupport() {
return protocolSupport;
}
public Map<UUID, UserConnection> getPortedPlayers() {
return portedPlayers;
}
public boolean handlePPS(UserConnection info) {
// Max PPS Checker
if (conf.getMaxPPS() > 0) {
if (info.getPacketsPerSecond() >= conf.getMaxPPS()) {
info.disconnect(conf.getMaxPPSKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
return true; // don't send current packet
}
}
// Tracking PPS Checker
if (conf.getMaxWarnings() > 0 && conf.getTrackingPeriod() > 0) {
if (info.getSecondsObserved() > conf.getTrackingPeriod()) {
// Reset
info.setWarnings(0);
info.setSecondsObserved(1);
} else {
info.setSecondsObserved(info.getSecondsObserved() + 1);
if (info.getPacketsPerSecond() >= conf.getWarningPPS()) {
info.setWarnings(info.getWarnings() + 1);
}
if (info.getWarnings() >= conf.getMaxWarnings()) {
info.disconnect(conf.getMaxWarningsKickMessage().replace("%pps", ((Long) info.getPacketsPerSecond()).intValue() + ""));
return true; // don't send current packet
}
}
}
return false;
}
}
@@ -0,0 +1,463 @@
package us.myles.ViaVersion.api;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
import us.myles.ViaVersion.exception.InformativeException;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class PacketWrapper {
private final ByteBuf inputBuffer;
private final UserConnection userConnection;
private boolean send = true;
@Setter
@Getter
private int id = -1;
private LinkedList<Pair<Type, Object>> readableObjects = new LinkedList<>();
private List<Pair<Type, Object>> packetValues = new ArrayList<>();
public PacketWrapper(int packetID, ByteBuf inputBuffer, UserConnection userConnection) {
this.id = packetID;
this.inputBuffer = inputBuffer;
this.userConnection = userConnection;
}
/**
* Get a part from the output
*
* @param type The type of the part you wish to get.
* @param <T> The return type of the type you wish to get.
* @param index The index of the part (relative to the type)
* @return The requested type or throws ArrayIndexOutOfBounds
* @throws Exception If it fails to find it, an exception will be thrown.
*/
public <T> T get(Type<T> type, int index) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return (T) packetValue.getValue();
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId()).set("Data", packetValues);
}
/**
* Check if a type is at an index
*
* @param type The type of the part you wish to get.
* @param index The index of the part (relative to the type)
* @return True if the type is at the index
*/
public boolean is(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
/**
* Check if a type is at an index
*
* @param type The type of the part you wish to get.
* @param index The index of the part (relative to the type)
* @return True if the type is at the index
*/
public boolean isReadable(Type type, int index) {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : readableObjects) {
if (packetValue.getKey().getBaseClass() == type.getBaseClass()) { // Ref check
if (currentIndex == index) {
return true;
}
currentIndex++;
}
}
return false;
}
/**
* Set a currently existing part in the output
*
* @param type The type of the part you wish to set.
* @param <T> The return type of the type you wish to set.
* @param index The index of the part (relative to the type)
* @param value The value of the part you wish to set it to.
* @throws Exception If it fails to set it, an exception will be thrown.
*/
public <T> void set(Type<T> type, int index, T value) throws Exception {
int currentIndex = 0;
for (Pair<Type, Object> packetValue : packetValues) {
if (packetValue.getKey() == type) { // Ref check
if (currentIndex == index) {
packetValue.setValue(value);
return;
}
currentIndex++;
}
}
Exception e = new ArrayIndexOutOfBoundsException("Could not find type " + type.getTypeName() + " at " + index);
throw new InformativeException(e).set("Type", type.getTypeName()).set("Index", index).set("Packet ID", getId());
}
/**
* Read a type from the input.
*
* @param type The type you wish to read
* @param <T> The return type of the type you wish to read.
* @return The requested type
* @throws Exception If it fails to read
*/
public <T> T read(Type<T> type) throws Exception {
if (type == Type.NOTHING) return null;
if (readableObjects.isEmpty()) {
Preconditions.checkNotNull(inputBuffer, "This packet does not have an input buffer.");
// We could in the future log input read values, but honestly for things like bulk maps, mem waste D:
try {
return type.read(inputBuffer);
} catch (Exception e) {
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
} else {
Pair<Type, Object> read = readableObjects.poll();
if (read.getKey().equals(type) || (type.getBaseClass().equals(read.getKey().getBaseClass()) && type.getOutputClass().equals(read.getKey().getOutputClass()))) {
return (T) read.getValue();
} else {
if (type == Type.NOTHING) {
return read(type); // retry
} else {
Exception e = new IOException("Unable to read type " + type.getTypeName() + ", found " + read.getKey().getTypeName());
throw new InformativeException(e).set("Type", type.getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
}
}
}
/**
* Write a type to the output.
*
* @param type The type to write.
* @param <T> The return type of the type you wish to write.
* @param value The value of the type to write.
*/
public <T> void write(Type<T> type, T value) {
if (value != null) {
if (!type.getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (type instanceof TypeConverter) {
value = (T) ((TypeConverter) type).from(value);
} else {
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + type.getOutputClass());
}
}
}
packetValues.add(new Pair<Type, Object>(type, value));
}
/**
* Take a value from the input and write to the output.
*
* @param type The type to read and write.
* @param <T> The return type of the type you wish to pass through.
* @return The type which was read/written.
* @throws Exception If it failed to read or write
*/
public <T> T passthrough(Type<T> type) throws Exception {
T value = read(type);
write(type, value);
return value;
}
/**
* Take all the inputs and write them to the output.
*
* @throws Exception If it failed to read or write
*/
public void passthroughAll() throws Exception {
// Copy previous objects
packetValues.addAll(readableObjects);
readableObjects.clear();
// If the buffer has readable bytes, copy them.
if (inputBuffer.readableBytes() > 0) {
passthrough(Type.REMAINING_BYTES);
}
}
/**
* Write the current output to a buffer.
*
* @param buffer The buffer to write to.
* @throws Exception Throws an exception if it fails to write a value.
*/
public void writeToBuffer(ByteBuf buffer) throws Exception {
if (id != -1) {
Type.VAR_INT.write(buffer, id);
}
if (readableObjects.size() > 0) {
packetValues.addAll(readableObjects);
readableObjects.clear();
}
int index = 0;
for (Pair<Type, Object> packetValue : packetValues) {
try {
Object value = packetValue.getValue();
if (value != null) {
if (!packetValue.getKey().getOutputClass().isAssignableFrom(value.getClass())) {
// attempt conversion
if (packetValue.getKey() instanceof TypeConverter) {
value = ((TypeConverter) packetValue.getKey()).from(value);
} else {
System.out.println("Possible type mismatch: " + value.getClass().getName() + " -> " + packetValue.getKey().getOutputClass());
}
}
}
packetValue.getKey().write(buffer, value);
} catch (Exception e) {
throw new InformativeException(e).set("Index", index).set("Type", packetValue.getKey().getTypeName()).set("Packet ID", getId()).set("Data", packetValues);
}
index++;
}
writeRemaining(buffer);
}
/**
* Clear the input buffer / readable objects
*/
public void clearInputBuffer() {
if (inputBuffer != null)
inputBuffer.clear();
readableObjects.clear(); // :(
}
/**
* Clear the packet, used if you have to change the packet completely
*/
public void clearPacket() {
clearInputBuffer();
packetValues.clear();
}
private void writeRemaining(ByteBuf output) {
if (inputBuffer != null) {
output.writeBytes(inputBuffer, inputBuffer.readableBytes());
}
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
*
* @param packetProtocol - The protocol version of the packet.
* @param skipCurrentPipeline - Skip the current pipeline
* @throws Exception if it fails to write
*/
public ChannelFuture send(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline) throws Exception {
if (!isCancelled()) {
// Apply current pipeline
List<Protocol> protocols = new ArrayList<>(user().get(ProtocolInfo.class).getPipeline().pipes());
// Other way if outgoing
Collections.reverse(protocols);
int index = 0;
for (int i = 0; i < protocols.size(); i++) {
if (protocols.get(i).getClass().equals(packetProtocol)) {
index = skipCurrentPipeline ? (i + 1) : (i);
break;
}
}
// Reset reader before we start
resetReader();
// Apply other protocols
apply(Direction.OUTGOING, user().get(ProtocolInfo.class).getState(), index, protocols);
// Send
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
writeToBuffer(output);
return user().sendRawPacketFuture(output);
}
return user().getChannel().newFailedFuture(new Exception("Cancelled packet"));
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
*
* @param packetProtocol - The protocol version of the packet.
* @throws Exception if it fails to write
*/
public void send(Class<? extends Protocol> packetProtocol) throws Exception {
send(packetProtocol, true);
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
* Also returns the packets ChannelFuture
*
* @param packetProtocol - The protocol version of the packet.
* @return The packets ChannelFuture
* @throws Exception if it fails to write
*/
public ChannelFuture sendFuture(Class<? extends Protocol> packetProtocol) throws Exception {
return send(packetProtocol, true);
}
/**
* Send this packet to the associated user.
* Be careful not to send packets twice.
* (Sends it after current)
* <br />
* <b>This method is no longer used, it's favoured to use send(Protocol) as it will handle the pipeline properly.</b>
*
* @throws Exception if it fails to write
*/
@Deprecated
public void send() throws Exception {
if (!isCancelled()) {
// Send
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
writeToBuffer(output);
user().sendRawPacket(output);
}
}
/**
* Create a new packet for the target of this packet.
*
* @param packetID The ID of the new packet
* @return The newly created packet wrapper
*/
public PacketWrapper create(int packetID) {
return new PacketWrapper(packetID, null, user());
}
/**
* Create a new packet with values.
*
* @param packetID The ID of the new packet
* @param init A ValueCreator to write to the packet.
* @return The newly created packet wrapper
* @throws Exception If it failed to write the values from the ValueCreator.
*/
public PacketWrapper create(int packetID, ValueCreator init) throws Exception {
PacketWrapper wrapper = create(packetID);
init.write(wrapper);
return wrapper;
}
/**
* Applies a pipeline from an index to the wrapper
*
* @param direction The direction
* @param state The state
* @param index The index to start from
* @param pipeline The pipeline
* @return The current packetwrapper
* @throws Exception
*/
public PacketWrapper apply(Direction direction, State state, int index, List<Protocol> pipeline) throws Exception {
for (int i = index; i < pipeline.size(); i++) { // Copy to prevent from removal.
pipeline.get(i).transform(direction, state, this);
// Reset the reader for the packetWrapper (So it can be recycled across packets)
resetReader();
}
return this;
}
/**
* Cancel this packet from sending
*/
public void cancel() {
this.send = false;
}
/**
* Check if this packet is cancelled.
*
* @return True if the packet won't be sent.
*/
public boolean isCancelled() {
return !this.send;
}
/**
* Get the user associated with this Packet
*
* @return The user
*/
public UserConnection user() {
return this.userConnection;
}
/**
* Reset the reader, so that it can be read again.
*/
public void resetReader() {
// Move readable objects are packet values
this.packetValues.addAll(readableObjects);
this.readableObjects.clear();
// Move all packet values to the readable for next packet.
this.readableObjects.addAll(packetValues);
this.packetValues.clear();
}
/**
* Send the current packet to the server.
* (Ensure the ID is suitable for the server version)
*
* @throws Exception If it failed to write
*/
public void sendToServer() throws Exception {
if (!isCancelled()) {
ByteBuf output = inputBuffer == null ? Unpooled.buffer() : inputBuffer.alloc().buffer();
Type.VAR_INT.write(output, ViaDecodeHandler.PASSTHROUGH_ID); // Pass through
writeToBuffer(output);
PipelineUtil.getContextBefore("decompress", user().getChannel().pipeline()).fireChannelRead(output);
}
}
@Override
public String toString() {
return "PacketWrapper{" +
"packetValues=" + packetValues +
", readableObjects=" + readableObjects +
", id=" + id +
'}';
}
}
@@ -0,0 +1,23 @@
package us.myles.ViaVersion.api;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@EqualsAndHashCode
public class Pair<X, Y> {
private X key;
private Y value;
public Pair(X key, Y value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Pair{" + key + ", " + value + '}';
}
}
@@ -1,11 +1,19 @@
package us.myles.ViaVersion.api;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.Validate;
import us.myles.ViaVersion.ViaVersionPlugin;
public class ViaVersion {
@Getter
@Setter
private static ViaVersionAPI instance;
@Getter
private static ViaVersionConfig config;
public static void setInstance(ViaVersionPlugin plugin) {
Validate.isTrue(instance == null, "Instance is already set");
ViaVersion.instance = plugin;
ViaVersion.config = plugin.getConf();
}
}
@@ -5,22 +5,42 @@ import org.bukkit.entity.Player;
import us.myles.ViaVersion.api.boss.BossBar;
import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import java.util.SortedSet;
import java.util.UUID;
public interface ViaVersionAPI {
/**
* Is player using 1.9?
* Is the player connection modified by ViaVersion?
*
* @param player
* @return True if the client is on 1.9
* @param player Bukkit player object
* @return True if the client is modified (At the moment it also means version 1.9 and higher)
*/
boolean isPorted(Player player);
/**
* Get protocol number from a player
* Will also retrieve version from ProtocolSupport if it's being used.
*
* @param player Bukkit player object
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
*/
int getPlayerVersion(Player player);
/**
* Get protocol number from a player
*
* @param uuid UUID of a player
* @return Protocol ID, For example (47=1.8-1.8.8, 107=1.9, 108=1.9.1)
*/
int getPlayerVersion(UUID uuid);
/**
* Is player using 1.9?
*
* @param playerUUID
* @param playerUUID UUID of a player
* @return True if the client is on 1.9
*/
boolean isPorted(UUID playerUUID);
@@ -72,16 +92,49 @@ public interface ViaVersionAPI {
BossBar createBossBar(String title, float health, BossColor color, BossStyle style);
/**
* Obtain if global debug is enabled
* Get if global debug is enabled
*
* @return true if debug is enabled
*/
boolean isDebug();
/**
* Obtains if syncing chunks is on
* Get ViaVersions command handler
*
* @return true if it is
* @return command handler
*/
boolean isSyncedChunks();
ViaVersionCommand getCommandHandler();
/**
* Get if this version is a compatibility build for spigot.
* Eg. 1.9.1 / 1.9.2 allow certain versions to connect
*
* @return True if it is
*/
boolean isCompatSpigotBuild();
/**
* Get the supported protocol versions
* This method removes any blocked protocol versions.
* @see ProtocolRegistry#getSupportedVersions() for full list.
*
* @return a list of protocol versions
*/
SortedSet<Integer> getSupportedVersions();
/**
* Gets if the server uses spigot
* <p>
* Note: Will only work after ViaVersion load
*
* @return True if spigot
*/
boolean isSpigot();
/**
* Gets if protocol support is also being used.
*
* @return True if it is being used.
*/
boolean isProtocolSupport();
}
@@ -0,0 +1,212 @@
package us.myles.ViaVersion.api;
import java.util.List;
public interface ViaVersionConfig {
/**
* Get if the plugin should check for updates
*
* @return true if update checking is enabled
*/
boolean isCheckForUpdates();
/**
* Get if collision preventing for players is enabled
*
* @return true if collision preventing is enabled
*/
boolean isPreventCollision();
/**
* Get if 1.9 & 1.10 clients are shown the new effect indicator in the top-right corner
*
* @return true if the using of the new effect indicator is enabled
*/
boolean isNewEffectIndicator();
/**
* Get if 1.9 & 1.10 clients are shown the new death message on the death screen
*
* @return true if enabled
*/
boolean isShowNewDeathMessages();
/**
* Get if metadata errors will be suppressed
*
* @return true if metadata errors suppression is enabled
*/
boolean isSuppressMetadataErrors();
/**
* Get if blocking in 1.9 & 1.10 appears as a player holding a shield
*
* @return true if shield blocking is enabled
*/
boolean isShieldBlocking();
/**
* Get if armor stand positions are fixed so holograms show up at the correct height in 1.9 & 1.10
*
* @return true if hologram patching is enabled
*/
boolean isHologramPatch();
/**
* Get if boss bars are fixed for 1.9 & 1.10 clients
*
* @return true if boss bar patching is enabled
*/
boolean isBossbarPatch();
/**
* Get if the boss bars for 1.9 & 1.10 clients are being stopped from flickering
* This will keep all boss bars on 100% (not recommended)
*
* @return true if boss bar anti flickering is enabled
*/
boolean isBossbarAntiflicker();
/**
* Get if unknown entity errors will be suppressed
*
* @return true if boss bar patching is enabled
*/
@Deprecated
boolean isUnknownEntitiesSuppressed();
/**
* Get the vertical offset armor stands are being moved with when the hologram patch is enabled
*
* @return the vertical offset holograms will be moved with
*/
double getHologramYOffset();
/**
* Get if players will be automatically put in the same team when collision preventing is enabled
*
* @return true if automatic teaming is enabled
*/
boolean isAutoTeam();
/**
* Get if our block break patch is enabled to prevent weird ghost glitches.
*
* @return true if it is enabled.
*/
@Deprecated
boolean isBlockBreakPatch();
/**
* Get the maximum number of packets a client can send per second.
*
* @return The number of packets a client can send per second.
*/
int getMaxPPS();
/**
* Get the kick message sent if the user hits the max packets per second.
*
* @return Kick message, with colour codes using '&amp;'
*/
String getMaxPPSKickMessage();
/**
* The time in seconds that should be tracked for warnings
*
* @return Time in seconds that should be tracked for warnings
*/
int getTrackingPeriod();
/**
* The number of packets per second to count as a warning
*
* @return The number of packets per second to count as a warning.
*/
int getWarningPPS();
/**
* Get the maximum number of warnings the client can have in the interval
*
* @return The number of packets a client can send per second.
*/
int getMaxWarnings();
/**
* Get the kick message sent if the user goes over the warnings in the interval
*
* @return Kick message, with colour codes using '&amp;'
*/
String getMaxWarningsKickMessage();
/**
* Is anti-xray enabled?
*
* @return A boolean
*/
boolean isAntiXRay();
/**
* Send supported versions in the status response packet
*
* @return If true, enabled
*/
boolean isSendSupportedVersions();
/**
* Stimulate the player tick
*
* @return if true, enabled
*/
boolean isStimulatePlayerTick();
/**
* Use the item cache to prevent high resource usage
*
* @return if true, enabled
*/
boolean isItemCache();
/**
* Use the NMS player ticking
*
* @return if true, enabled
*/
boolean isNMSPlayerTicking();
/**
* Replace extended pistons on 1.10 chunk loading.
*
* @return True if to replace them
*/
boolean isReplacePistons();
/**
* Get the id for replacing extended pistons.
*
* @return The integer id
*/
int getPistonReplacementId();
/**
* Force json transform
*
* @return True if enabled
*/
boolean isForceJsonTransform();
/**
* Get the blocked protocols
*
* @return An Integer list
*/
List<Integer> getBlockedProtocols();
/**
* Get the custom disconnect message
*
* @return Disconnect message
*/
String getBlockedDisconnectMsg();
}
@@ -6,24 +6,40 @@ import java.util.Set;
import java.util.UUID;
public interface BossBar {
/**
* Get the current title
*
* @return the title
*/
String getTitle();
/**
* Change the title
*
* @param title Title can be in either JSON or just text
* @return The BossBar object
*/
void setTitle(String title);
BossBar setTitle(String title);
/**
* Get the health
*
* @return float between 0F - 1F
*/
float getHealth();
/**
* Change the health
*
* @param health this float has to be between 0F - 1F
* @return The BossBar object
*/
void setHealth(float health);
BossBar setHealth(float health);
/**
* Get the bossbar color
*
* @return
* @return The colour
*/
BossColor getColor();
@@ -31,47 +47,76 @@ public interface BossBar {
* Yay colors!
*
* @param color Whatever color you want!
* @return The BossBar object
*/
void setColor(BossColor color);
BossBar setColor(BossColor color);
/**
* Get the bosbar style
*
* @return BossStyle
*/
BossStyle getStyle();
/**
* Change the bosbar style
*
* @param style BossStyle
* @return The BossBar object
*/
void setStyle(BossStyle style);
BossBar setStyle(BossStyle style);
/**
* Show the bossbar to a player.
*
* @param player
* @param player The player
* @return The BossBar object
*/
void addPlayer(Player player);
BossBar addPlayer(Player player);
/**
* Show the bossbar to a player (uuid)
*
* @param player uuid of the player
* @return The BossBar object
*/
BossBar addPlayer(UUID player);
/**
* add multiple players
*
* @param players list of players
* @return The BossBar object
*/
BossBar addPlayers(Player... players);
/**
* Remove the bossbar from a player
*
* @param player
* @param player The player
* @return The BossBar object
*/
void removePlayer(Player player);
BossBar removePlayer(Player player);
/**
* Add flags
*
* @param flag
* @param flag The flag to add
* @return The BossBar object
*/
void addFlag(BossFlag flag);
BossBar addFlag(BossFlag flag);
/**
* Remove flags.
*
* @param flag
* @param flag The flag to remove
* @return The BossBar object
*/
void removeFlag(BossFlag flag);
BossBar removeFlag(BossFlag flag);
/**
* @param flag
* @return
* @param flag The flag to check against
* @return True if it has the flag
*/
boolean hasFlag(BossFlag flag);
@@ -84,13 +129,17 @@ public interface BossBar {
/**
* Show the bossbar to everyone (In the getPlayer set)
*
* @return The BossBar object
*/
void show();
BossBar show();
/**
* Hide the bossbar from everyone (In the getPlayer set)
*
* @return The BossBar object
*/
void hide();
BossBar hide();
/**
* Is it visible?
@@ -0,0 +1,77 @@
package us.myles.ViaVersion.api.command;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.commands.ViaCommandHandler;
import java.util.Collections;
import java.util.List;
public abstract class ViaSubCommand {
/**
* Subcommand name
*
* @return The command name
*/
public abstract String name();
/**
* subcommand description, this'll show in /viaversion list
*
* @return The command description
*/
public abstract String description();
/**
* Usage example:
* "playerversion [name]"
*
* @return The command usage
*/
public String usage() {
return name();
}
/**
* Permission, null for everyone
*
* @return The permission required to use the command
*/
public String permission() {
return "viaversion.admin";
}
/**
* Gets triggered on execution
*
* @param sender Command sender
* @param args Arguments
* @return command executed succesfully if false, show usage
*/
public abstract boolean execute(CommandSender sender, String[] args);
/**
* Yay, possibility to implement tab-completion
*
* @param sender Command sender
* @param args args
* @return tab complete possibilities
*/
public List<String> onTabComplete(CommandSender sender, String[] args) {
return Collections.emptyList();
}
public String color(String s) {
return ViaCommandHandler.color(s);
}
/**
* Send message formatted / colored
*
* @param sender command sender
* @param message string message
* @param args optional objects
*/
public void sendMessage(CommandSender sender, String message, Object... args) {
ViaCommandHandler.sendMessage(sender, message, args);
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.api.command;
public interface ViaVersionCommand {
/**
* Register your own subcommand inside ViaVersion
*
* @param command Your own SubCommand instance to handle it.
* @throws Exception throws an exception when the subcommand already exists or if it's not valid, example: spacee
*/
void registerSubCommand(ViaSubCommand command) throws Exception;
/**
* Check if a subcommand is registered.
*
* @param name Subcommand name
* @return true if it exists
*/
boolean hasSubCommand(String name);
/**
* Get subcommand instance by name
*
* @param name subcommand name
* @return ViaSubCommand instance
*/
ViaSubCommand getSubCommand(String name);
}
@@ -0,0 +1,10 @@
package us.myles.ViaVersion.api.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class StoredObject {
private UserConnection user;
}
@@ -0,0 +1,162 @@
package us.myles.ViaVersion.api.data;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.SocketChannel;
import lombok.Data;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Data
public class UserConnection {
private final SocketChannel channel;
Map<Class, StoredObject> storedObjects = new ConcurrentHashMap<>();
private boolean active = true;
private boolean pendingDisconnect = false;
private Object lastPacket;
private long sentPackets = 0L;
private long receivedPackets = 0L;
// Used for tracking pps
private long startTime = 0L;
private long intervalPackets = 0L;
private long packetsPerSecond = -1L;
// Used for handling warnings (over time)
private int secondsObserved = 0;
private int warnings = 0;
public UserConnection(SocketChannel socketChannel) {
this.channel = socketChannel;
}
/**
* Get an object from the storage
*
* @param objectClass The class of the object to get
* @param <T> The type of the class you want to get.
* @return The requested object
*/
public <T extends StoredObject> T get(Class<T> objectClass) {
return (T) storedObjects.get(objectClass);
}
/**
* Check if the storage has an object
*
* @param objectClass The object class to check
* @return True if the object is in the storage
*/
public boolean has(Class<? extends StoredObject> objectClass) {
return storedObjects.containsKey(objectClass);
}
/**
* Put an object into the stored objects based on class
*
* @param object The object to store.
*/
public void put(StoredObject object) {
storedObjects.put(object.getClass(), object);
}
/**
* Send a raw packet to the player
*
* @param packet The raw packet to send
* @param currentThread Should it run in the same thread
*/
public void sendRawPacket(final ByteBuf packet, boolean currentThread) {
final ChannelHandler handler = channel.pipeline().get("encoder");
if (currentThread) {
channel.pipeline().context(handler).writeAndFlush(packet);
} else {
channel.eventLoop().submit(new Runnable() {
@Override
public void run() {
channel.pipeline().context(handler).writeAndFlush(packet);
}
});
}
}
/**
* Send a raw packet to the player with returning the future
*
* @param packet The raw packet to send
* @return ChannelFuture of the packet being sent
*/
public ChannelFuture sendRawPacketFuture(final ByteBuf packet) {
final ChannelHandler handler = channel.pipeline().get("encoder");
return channel.pipeline().context(handler).writeAndFlush(packet);
}
/**
* Send a raw packet to the player (netty thread)
*
* @param packet The packet to send
*/
public void sendRawPacket(final ByteBuf packet) {
sendRawPacket(packet, false);
}
/**
* Used for incrementing the number of packets sent to the client
*/
public void incrementSent() {
this.sentPackets++;
}
/**
* Used for incrementing the number of packets received from the client
*
* @return True if the interval has reset
*/
public boolean incrementReceived() {
// handle stats
Long diff = System.currentTimeMillis() - startTime;
if (diff >= 1000) {
packetsPerSecond = intervalPackets;
startTime = System.currentTimeMillis();
intervalPackets = 1;
return true;
} else {
intervalPackets++;
}
// increase total
this.receivedPackets++;
return false;
}
/**
* Disconnect a connection
*
* @param reason The reason to use, not used if player is not active.
*/
public void disconnect(final String reason) {
if (!getChannel().isOpen()) return;
if (pendingDisconnect) return;
pendingDisconnect = true;
if (get(ProtocolInfo.class).getUuid() != null) {
final UUID uuid = get(ProtocolInfo.class).getUuid();
if (Bukkit.getPlayer(uuid) != null) {
Bukkit.getScheduler().runTask(Bukkit.getPluginManager().getPlugin("ViaVersion"), new Runnable() {
@Override
public void run() {
Player player = Bukkit.getPlayer(uuid);
if (player != null)
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
}
});
return;
}
}
getChannel().close(); // =)
}
}
@@ -0,0 +1,16 @@
package us.myles.ViaVersion.api.minecraft;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
@EqualsAndHashCode
public class Position {
private Long x;
private Long y;
private Long z;
}
@@ -0,0 +1,17 @@
package us.myles.ViaVersion.api.minecraft.chunks;
public interface Chunk {
int getX();
int getZ();
ChunkSection[] getSections();
boolean isGroundUp();
boolean isBiomeData();
byte[] getBiomeData();
int getBitmask();
}
@@ -0,0 +1,15 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import io.netty.buffer.ByteBuf;
public interface ChunkSection {
int getBlockId(int x, int y, int z);
void writeBlocks(ByteBuf output) throws Exception;
void writeBlockLight(ByteBuf output) throws Exception;
boolean hasSkyLight();
void writeSkyLight(ByteBuf output) throws Exception;
}
@@ -0,0 +1,127 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import java.util.Arrays;
public class NibbleArray {
private final byte[] handle;
public NibbleArray(int length) {
if (length == 0 || length % 2 != 0) {
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
}
this.handle = new byte[length / 2];
}
public NibbleArray(byte[] handle) {
if (handle.length == 0 || handle.length % 2 != 0) {
throw new IllegalArgumentException("Length of nibble array must be a positive number dividable by 2!");
}
this.handle = handle;
}
/**
* Get the value at a desired X, Y, Z
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @return The value at the given XYZ
*/
public byte get(int x, int y, int z) {
return get(y << 8 | z << 4 | x);
}
/**
* Get the value at an index
*
* @param index The index to lookup
* @return The value at that index.
*/
public byte get(int index) {
byte value = handle[index / 2];
if (index % 2 == 0) {
return (byte) (value & 0xF);
} else {
return (byte) ((value >> 4) & 0xF);
}
}
/**
* Set the value based on an x, y, z
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @param value Desired Value
*/
public void set(int x, int y, int z, int value) {
set(y << 8 | z << 4 | x, value);
}
/**
* Set a value at an index
*
* @param index The index to set the value at.
* @param value The desired value
*/
public void set(int index, int value) {
index /= 2;
if (index % 2 == 0) {
handle[index] = (byte) (handle[index] & 0xF0 | value & 0xF);
} else {
handle[index] = (byte) (handle[index] & 0xF | (value & 0xF) << 4);
}
}
/**
* The size of this nibble
*
* @return The size as an int of the nibble
*/
public int size() {
return handle.length * 2;
}
/**
* Get the actual number of bytes
*
* @return The number of bytes based on the handle.
*/
public int actualSize() {
return handle.length;
}
/**
* Fill the array with a value
*
* @param value Value to fill with
*/
public void fill(byte value) {
value &= 0xF; // Max nibble size (= 16)
Arrays.fill(handle, (byte) ((value << 4) | value));
}
/**
* Get the byte array behind this nibble
*
* @return The byte array
*/
public byte[] getHandle() {
return handle;
}
/**
* Copy a byte array into this nibble
*
* @param handle The byte array to copy in.
*/
public void setHandle(byte[] handle) {
if (handle.length != this.handle.length) {
throw new IllegalArgumentException("Length of handle must equal to size of nibble array!");
}
System.arraycopy(handle, 0, this.handle, 0, handle.length);
}
}
@@ -0,0 +1,28 @@
package us.myles.ViaVersion.api.minecraft.item;
import lombok.*;
import org.bukkit.inventory.ItemStack;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Item {
private short id;
private byte amount;
private short data;
private CompoundTag tag;
/**
* Create an item from a bukkit stack (doesn't save NBT)
*
* @param stack The stack to convert from
* @return The output stack
*/
public static Item getItem(ItemStack stack) {
if (stack == null) return null;
return new Item((short) stack.getTypeId(), (byte) stack.getAmount(), stack.getDurability(), null);
}
}
@@ -0,0 +1,26 @@
package us.myles.ViaVersion.api.minecraft.metadata;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.type.Type;
@AllArgsConstructor
@Getter
@Setter
public class Metadata {
private int id;
private int typeID;
private Type type;
private Object value;
@Override
public String toString() {
return "Metadata{" +
"id=" + id +
", typeID=" + typeID +
", type=" + type +
", value=" + value +
'}';
}
}
@@ -0,0 +1,156 @@
package us.myles.ViaVersion.api.protocol;
import lombok.AllArgsConstructor;
import lombok.Getter;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.exception.CancelException;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class Protocol {
private Map<Pair<State, Integer>, ProtocolPacket> incoming = new HashMap<>();
private Map<Pair<State, Integer>, ProtocolPacket> outgoing = new HashMap<>();
public Protocol() {
registerPackets();
}
/**
* Should this protocol filter an object packet from this class.
* Default: false
*
* @param packetClass The class of the current input
* @return True if it should handle the filtering
*/
public boolean isFiltered(Class packetClass) {
return false;
}
/**
* Filter a packet into the output
*
* @param info The current user connection
* @param packet The input packet as an object (NMS)
* @param output The list to put the object into.
* @throws Exception Throws exception if cancelled / error.
*/
protected void filterPacket(UserConnection info, Object packet, List output) throws Exception {
output.add(packet);
}
/**
* Register listeners for this protocol
*/
protected void registerListeners() {
}
/**
* Register the packets for this protocol
*/
protected abstract void registerPackets();
/**
* Initialise a user for this protocol setting up objects.
*
* @param userConnection The user to initialise
*/
public abstract void init(UserConnection userConnection);
/**
* Register an incoming packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
public void registerIncoming(State state, int oldPacketID, int newPacketID) {
registerIncoming(state, oldPacketID, newPacketID, null);
}
/**
* Register an incoming packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
public void registerIncoming(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
incoming.put(new Pair<>(state, newPacketID), protocolPacket);
}
/**
* Register an outgoing packet, with simple id transformation.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
*/
public void registerOutgoing(State state, int oldPacketID, int newPacketID) {
registerOutgoing(state, oldPacketID, newPacketID, null);
}
/**
* Register an outgoing packet, with id transformation and remapper.
*
* @param state The state which the packet is sent in.
* @param oldPacketID The old packet ID
* @param newPacketID The new packet ID
* @param packetRemapper The remapper to use for the packet
*/
public void registerOutgoing(State state, int oldPacketID, int newPacketID, PacketRemapper packetRemapper) {
ProtocolPacket protocolPacket = new ProtocolPacket(state, oldPacketID, newPacketID, packetRemapper);
outgoing.put(new Pair<>(state, oldPacketID), protocolPacket);
}
/**
* Transform a packet using this protocol
*
* @param direction The direction the packet is going in
* @param state The current protocol state
* @param packetWrapper The packet wrapper to transform
* @throws Exception Throws exception if it fails to transform
*/
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
Pair<State, Integer> statePacket = new Pair<>(state, packetWrapper.getId());
Map<Pair<State, Integer>, ProtocolPacket> packetMap = (direction == Direction.OUTGOING ? outgoing : incoming);
ProtocolPacket protocolPacket;
if (packetMap.containsKey(statePacket)) {
protocolPacket = packetMap.get(statePacket);
} else {
return;
}
// write packet id
int newID = direction == Direction.OUTGOING ? protocolPacket.getNewID() : protocolPacket.getOldID();
packetWrapper.setId(newID);
// remap
if (protocolPacket.getRemapper() != null) {
protocolPacket.getRemapper().remap(packetWrapper);
if (packetWrapper.isCancelled())
throw new CancelException();
}
}
@Override
public String toString() {
return "Protocol:" + getClass().getSimpleName();
}
@AllArgsConstructor
@Getter
class ProtocolPacket {
State state;
int oldID;
int newID;
PacketRemapper remapper;
}
}
@@ -0,0 +1,165 @@
package us.myles.ViaVersion.api.protocol;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
public class ProtocolPipeline extends Protocol {
List<Protocol> protocolList;
private UserConnection userConnection;
public ProtocolPipeline(UserConnection userConnection) {
super();
init(userConnection);
}
@Override
protected void registerPackets() {
protocolList = new CopyOnWriteArrayList<>();
// This is a pipeline so we register basic pipes
protocolList.add(ProtocolRegistry.BASE_PROTOCOL);
}
@Override
public void init(UserConnection userConnection) {
this.userConnection = userConnection;
ProtocolInfo protocolInfo = new ProtocolInfo(userConnection);
protocolInfo.setPipeline(this);
userConnection.put(protocolInfo);
/* Init through all our pipes */
for (Protocol protocol : protocolList) {
protocol.init(userConnection);
}
}
/**
* Add a protocol to the current pipeline
* This will call the .init method.
*
* @param protocol The protocol to add to the end
*/
public void add(Protocol protocol) {
if (protocolList != null) {
protocolList.add(protocol);
protocol.init(userConnection);
} else {
throw new NullPointerException("Tried to add protocol to early");
}
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
int originalID = packetWrapper.getId();
List<Protocol> protocols = new ArrayList<>(protocolList);
// Other way if outgoing
if (direction == Direction.OUTGOING)
Collections.reverse(protocols);
// Apply protocols
packetWrapper.apply(direction, state, 0, protocols);
super.transform(direction, state, packetWrapper);
if (ViaVersion.getInstance().isDebug()) {
// Debug packet
String packet = "UNKNOWN";
// For 1.8/1.9 server version, eventually we'll probably get an API for this...
if (ProtocolRegistry.SERVER_PROTOCOL >= ProtocolVersion.v1_8.getId() &&
ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_9_3.getId()) {
PacketType type;
if (ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_8.getId()) {
if (direction == Direction.INCOMING) {
type = PacketType.findNewPacket(state, direction, originalID);
} else {
type = PacketType.findOldPacket(state, direction, originalID);
}
} else {
type = PacketType.findNewPacket(state, direction, originalID);
}
if (type != null) {
// Filter :) This would be not hard coded too, sorry :(
if (type == PacketType.PLAY_CHUNK_DATA) return;
if (type == PacketType.PLAY_TIME_UPDATE) return;
if (type == PacketType.PLAY_KEEP_ALIVE) return;
if (type == PacketType.PLAY_KEEP_ALIVE_REQUEST) return;
if (type == PacketType.PLAY_ENTITY_LOOK_MOVE) return;
if (type == PacketType.PLAY_ENTITY_LOOK) return;
if (type == PacketType.PLAY_ENTITY_RELATIVE_MOVE) return;
if (type == PacketType.PLAY_PLAYER_POSITION_LOOK_REQUEST) return;
if (type == PacketType.PLAY_PLAYER_LOOK_REQUEST) return;
if (type == PacketType.PLAY_PLAYER_POSITION_REQUEST) return;
packet = type.name();
}
}
String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]";
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
String actualUsername = packetWrapper.user().get(ProtocolInfo.class).getUsername();
String username = actualUsername != null ? actualUsername + " " : "";
plugin.getLogger().log(Level.INFO, "{0}{1}: {2} {3} -> {4} [{5}] Value: {6}",
new Object[]{
username,
direction,
state,
originalID,
packetWrapper.getId(),
name,
packetWrapper
});
}
}
/**
* Check if the pipeline contains a protocol
*
* @param pipeClass The class to check
* @return True if the protocol class is in the pipeline
*/
public boolean contains(Class<? extends Protocol> pipeClass) {
for (Protocol protocol : protocolList) {
if (protocol.getClass().equals(pipeClass)) return true;
}
return false;
}
/**
* Use the pipeline to filter a NMS packet
*
* @param o The NMS packet object
* @param list The output list to write to
* @return If it should not write the input object to te list.
* @throws Exception If it failed to convert / packet cancelld.
*/
public boolean filter(Object o, List list) throws Exception {
for (Protocol protocol : protocolList) {
if (protocol.isFiltered(o.getClass())) {
protocol.filterPacket(userConnection, o, list);
return true;
}
}
return false;
}
public List<Protocol> pipes() {
return protocolList;
}
}
@@ -0,0 +1,183 @@
package us.myles.ViaVersion.api.protocol;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.bukkit.Bukkit;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.protocols.base.BaseProtocol;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2TO1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_1to1_9.Protocol1_9_1TO1_9;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3TO1_9_1_2;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_9_1.Protocol1_9TO1_9_1;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ProtocolRegistry {
public static final Protocol BASE_PROTOCOL = new BaseProtocol();
public static int SERVER_PROTOCOL = -1;
// Input Version -> Output Version & Protocol (Allows fast lookup)
private static Map<Integer, Map<Integer, Protocol>> registryMap = new ConcurrentHashMap<>();
private static Map<Pair<Integer, Integer>, List<Pair<Integer, Protocol>>> pathCache = new ConcurrentHashMap<>();
private static List<Protocol> registerList = Lists.newCopyOnWriteArrayList();
private static Set<Integer> supportedVersions = Sets.newConcurrentHashSet();
static {
// Base Protocol
registerProtocol(BASE_PROTOCOL, Arrays.<Integer>asList(), -1);
// Register built in protocols
registerProtocol(new Protocol1_9TO1_8(), Collections.singletonList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_8.getId());
registerProtocol(new Protocol1_9_1TO1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9.getId());
registerProtocol(new Protocol1_9_3TO1_9_1_2(), Arrays.asList(ProtocolVersion.v1_9_3.getId()), ProtocolVersion.v1_9_2.getId());
// Only supported for 1.9.4 server to 1.9 (nothing else)
registerProtocol(new Protocol1_9TO1_9_1(), Arrays.asList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_9_2.getId());
registerProtocol(new Protocol1_9_1_2TO1_9_3_4(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9_3.getId());
registerProtocol(new Protocol1_10To1_9_3_4(), Collections.singletonList(ProtocolVersion.v1_10.getId()), ProtocolVersion.v1_9_3.getId());
}
/**
* Register a protocol
*
* @param protocol The protocol to register.
* @param supported Supported client versions.
* @param output The output server version it converts to.
*/
public static void registerProtocol(Protocol protocol, List<Integer> supported, Integer output) {
// Clear cache as this may make new routes.
if (pathCache.size() > 0)
pathCache.clear();
for (Integer version : supported) {
if (!registryMap.containsKey(version)) {
registryMap.put(version, new HashMap<Integer, Protocol>());
}
registryMap.get(version).put(output, protocol);
}
if (Bukkit.getPluginManager().getPlugin("ViaVersion").isEnabled()) {
protocol.registerListeners();
refreshVersions();
} else {
registerList.add(protocol);
}
}
public static void refreshVersions() {
supportedVersions.clear();
supportedVersions.add(ProtocolRegistry.SERVER_PROTOCOL);
for (ProtocolVersion versions : ProtocolVersion.getProtocols()) {
List<Pair<Integer, Protocol>> paths = getProtocolPath(versions.getId(), ProtocolRegistry.SERVER_PROTOCOL);
if (paths == null) continue;
supportedVersions.add(versions.getId());
for (Pair<Integer, Protocol> path : paths)
supportedVersions.add(path.getKey());
}
}
/**
* Get the versions compatible with the server.
*
* @return Read-only set of the versions.
*/
public static SortedSet<Integer> getSupportedVersions() {
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
}
/**
* Check if this plugin is useful to the server.
*
* @return True if there is a useful pipe
*/
public static boolean isWorkingPipe() {
for (Map<Integer, Protocol> maps : registryMap.values()) {
if (maps.containsKey(SERVER_PROTOCOL)) return true;
}
return false; // No destination for protocol
}
/**
* Called when the server is enabled, to register any non registered listeners.
*/
public static void registerListeners() {
for (Protocol protocol : registerList) {
protocol.registerListeners();
}
registerList.clear();
}
/**
* Calculate a path to get from an input protocol to the servers protocol.
*
* @param current The current items in the path
* @param clientVersion The current input version
* @param serverVersion The desired output version
* @return The path which has been generated, null if failed.
*/
private static List<Pair<Integer, Protocol>> getProtocolPath(List<Pair<Integer, Protocol>> current, int clientVersion, int serverVersion) {
if (clientVersion == serverVersion) return null; // We're already there
if (current.size() > 50) return null; // Fail safe, protocol too complicated.
// First check if there is any protocols for this
if (!registryMap.containsKey(clientVersion)) {
return null; // Not supported
}
// Next check there isn't an obvious path
Map<Integer, Protocol> inputMap = registryMap.get(clientVersion);
if (inputMap.containsKey(serverVersion)) {
current.add(new Pair<>(serverVersion, inputMap.get(serverVersion)));
return current; // Easy solution
}
// There might be a more advanced solution... So we'll see if any of the others can get us there
List<Pair<Integer, Protocol>> shortest = null;
for (Map.Entry<Integer, Protocol> entry : inputMap.entrySet()) {
// Ensure it wasn't caught by the other loop
if (!entry.getKey().equals(serverVersion)) {
Pair<Integer, Protocol> pair = new Pair<>(entry.getKey(), entry.getValue());
// Ensure no recursion
if (!current.contains(pair)) {
// Create a copy
List<Pair<Integer, Protocol>> newCurrent = new ArrayList<>(current);
newCurrent.add(pair);
// Calculate the rest of the protocol using the current
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
if (newCurrent != null) {
// If it's shorter then choose it
if (shortest == null || shortest.size() > newCurrent.size()) {
shortest = newCurrent;
}
}
}
}
}
return shortest; // null if none found
}
/**
* Calculate a path from a client version to server version
*
* @param clientVersion The input client version
* @param serverVersion The desired output server version
* @return The path it generated, null if it failed.
*/
public static List<Pair<Integer, Protocol>> getProtocolPath(int clientVersion, int serverVersion) {
Pair<Integer, Integer> protocolKey = new Pair<>(clientVersion, serverVersion);
// Check cache
if (pathCache.containsKey(protocolKey)) {
return pathCache.get(protocolKey);
}
// Generate path
List<Pair<Integer, Protocol>> outputPath = getProtocolPath(new ArrayList<Pair<Integer, Protocol>>(), clientVersion, serverVersion);
// If it found a path, cache it.
if (outputPath != null) {
pathCache.put(protocolKey, outputPath);
}
return outputPath;
}
}
@@ -0,0 +1,94 @@
package us.myles.ViaVersion.api.protocol;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import java.util.*;
@AllArgsConstructor
@Getter
public class ProtocolVersion {
private static final Map<Integer, ProtocolVersion> versions = new HashMap<>();
private static final List<ProtocolVersion> versionList = new ArrayList<>();
public static final ProtocolVersion v1_4_6;
public static final ProtocolVersion v1_5_1;
public static final ProtocolVersion v1_5_2;
public static final ProtocolVersion v_1_6_1;
public static final ProtocolVersion v_1_6_2;
public static final ProtocolVersion v_1_6_3;
public static final ProtocolVersion v_1_6_4;
public static final ProtocolVersion v1_7_1;
public static final ProtocolVersion v1_7_6;
public static final ProtocolVersion v1_8;
public static final ProtocolVersion v1_9;
public static final ProtocolVersion v1_9_1;
public static final ProtocolVersion v1_9_2;
public static final ProtocolVersion v1_9_3;
public static final ProtocolVersion v1_10;
public static final ProtocolVersion unknown;
private final int id;
private final String name;
static {
// Before netty rewrite
register(v1_4_6 = new ProtocolVersion(51, "1.4.6"));
register(v1_5_1 = new ProtocolVersion(60, "1.5.1"));
register(v1_5_2 = new ProtocolVersion(61, "1.5.2"));
register(v_1_6_1 = new ProtocolVersion(73, "1.6.1"));
register(v_1_6_2 = new ProtocolVersion(74, "1.6.2"));
register(v_1_6_3 = new ProtocolVersion(77, "1.6.3"));
register(v_1_6_4 = new ProtocolVersion(78, "1.6.4"));
// After netty rewrite
register(v1_7_1 = new ProtocolVersion(4, "1.7-1.7.5"));
register(v1_7_6 = new ProtocolVersion(5, "1.7.6-1.7.10"));
register(v1_8 = new ProtocolVersion(47, "1.8.x"));
register(v1_9 = new ProtocolVersion(107, "1.9"));
register(v1_9_1 = new ProtocolVersion(108, "1.9.1"));
register(v1_9_2 = new ProtocolVersion(109, "1.9.2"));
register(v1_9_3 = new ProtocolVersion(110, "1.9.3/4"));
register(v1_10 = new ProtocolVersion(210, "1.10"));
register(unknown = new ProtocolVersion(-1, "UNKNOWN"));
}
public static void register(@NonNull ProtocolVersion protocol) {
versions.put(protocol.getId(), protocol);
versionList.add(protocol);
}
public static boolean isRegistered(int id) {
return versions.containsKey(id);
}
public static ProtocolVersion getProtocol(int id) {
if (versions.containsKey(id)) {
return versions.get(id);
} else {
return new ProtocolVersion(id, "Unknown (" + id + ")");
}
}
public static int getIndex(ProtocolVersion version) {
return versionList.indexOf(version);
}
public static List<ProtocolVersion> getProtocols() {
return Collections.unmodifiableList(new ArrayList<>(versions.values()));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProtocolVersion that = (ProtocolVersion) o;
return id == that.id;
}
@Override
public int hashCode() {
return id;
}
}
@@ -0,0 +1,24 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.exception.InformativeException;
public abstract class PacketHandler implements ValueWriter {
/**
* Handle a packet
*
* @param wrapper The associated wrapper
* @throws Exception Throws exception if it failed to handle the packet
*/
public abstract void handle(PacketWrapper wrapper) throws Exception;
@Override
public void write(PacketWrapper writer, Object inputValue) throws Exception {
try {
handle(writer);
} catch (InformativeException e) {
e.addSource(this.getClass());
throw e;
}
}
}
@@ -0,0 +1,104 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.exception.InformativeException;
import java.util.ArrayList;
import java.util.List;
public abstract class PacketRemapper {
private List<Pair<ValueReader, ValueWriter>> valueRemappers = new ArrayList<>();
public PacketRemapper() {
registerMap();
}
/**
* Map a type to the same type.
*
* @param type Type to map
*/
public void map(Type type) {
TypeRemapper remapper = new TypeRemapper(type);
map(remapper, remapper);
}
/**
* Map a type from an old type to a new type
*
* @param oldType The old type
* @param newType The new type
*/
public void map(Type oldType, Type newType) {
map(new TypeRemapper(oldType), new TypeRemapper(newType));
}
/**
* Map a type from an old type to a transformed new type.
*
* @param oldType The old type
* @param <T1> The old return type.
* @param transformer The transformer to use to produce the new type.
* @param <T2> The new return type.
*/
public <T1, T2> void map(Type<T1> oldType, ValueTransformer<T1, T2> transformer) {
map(new TypeRemapper(oldType), transformer);
}
/**
* Map a type using a basic ValueReader to a ValueWriter
*
* @param inputReader The reader to read with.
* @param outputWriter The writer to write with
* @param <T> The return type
*/
public <T> void map(ValueReader<T> inputReader, ValueWriter<T> outputWriter) {
valueRemappers.add(new Pair<ValueReader, ValueWriter>(inputReader, outputWriter));
}
/**
* Create a value
*
* @param creator The creator to used to make the value(s).
*/
public void create(ValueCreator creator) {
map(new TypeRemapper(Type.NOTHING), creator);
}
/**
* Create a handler
*
* @param handler The handler to use to handle the current packet.
*/
public void handler(PacketHandler handler) {
map(new TypeRemapper(Type.NOTHING), handler);
}
/**
* Register the mappings for this packet
*/
public abstract void registerMap();
/**
* Remap a packet wrapper
*
* @param packetWrapper The wrapper to remap
* @throws Exception Throws if it fails to write / read to the packet.
*/
public void remap(PacketWrapper packetWrapper) throws Exception {
try {
// Read all the current values
for (Pair<ValueReader, ValueWriter> valueRemapper : valueRemappers) {
Object object = valueRemapper.getKey().read(packetWrapper);
// Convert object to write type :O!!!
valueRemapper.getValue().write(packetWrapper, object);
}
// If we had handlers we'd put them here
} catch (InformativeException e) {
e.addSource(this.getClass());
throw e;
}
}
}
@@ -0,0 +1,22 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.type.Type;
public class TypeRemapper<T> implements ValueReader<T>, ValueWriter<T> {
private final Type<T> type;
public TypeRemapper(Type<T> type) {
this.type = type;
}
@Override
public T read(PacketWrapper wrapper) throws Exception {
return wrapper.read(type);
}
@Override
public void write(PacketWrapper output, T inputValue) {
output.write(type, inputValue);
}
}
@@ -0,0 +1,24 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.exception.InformativeException;
public abstract class ValueCreator implements ValueWriter {
/**
* Write new values to a Packet.
*
* @param wrapper The packet to write to
* @throws Exception Throws exception if it fails to write.
*/
public abstract void write(PacketWrapper wrapper) throws Exception;
@Override
public void write(PacketWrapper writer, Object inputValue) throws Exception {
try {
write(writer);
} catch (InformativeException e) {
e.addSource(this.getClass());
throw e;
}
}
}
@@ -0,0 +1,14 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
public interface ValueReader<T> {
/**
* Reads value from a PacketWrapper
*
* @param wrapper The wrapper to read from
* @return Returns the desired type
* @throws Exception Throws exception if it fails to read
*/
T read(PacketWrapper wrapper) throws Exception;
}
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.exception.InformativeException;
public abstract class ValueTransformer<T1, T2> implements ValueWriter<T1> {
private final Type<T2> outputType;
public ValueTransformer(Type<T2> outputType) {
this.outputType = outputType;
}
/**
* Transform a value from one type to another
*
* @param wrapper The current packet
* @param inputValue The input value
* @return The value to write to the wrapper
* @throws Exception Throws exception if it fails to transform a value
*/
public abstract T2 transform(PacketWrapper wrapper, T1 inputValue) throws Exception;
@Override
public void write(PacketWrapper writer, T1 inputValue) throws Exception {
try {
writer.write(outputType, transform(writer, inputValue));
} catch (InformativeException e) {
e.addSource(this.getClass());
throw e;
}
}
}
@@ -0,0 +1,14 @@
package us.myles.ViaVersion.api.remapper;
import us.myles.ViaVersion.api.PacketWrapper;
public interface ValueWriter<T> {
/**
* Write a value to a packet
*
* @param writer The packet wrapper to write to
* @param inputValue The value to write
* @throws Exception Throws exception if it fails to write
*/
void write(PacketWrapper writer, T inputValue) throws Exception;
}
@@ -0,0 +1,14 @@
package us.myles.ViaVersion.api.type;
import io.netty.buffer.ByteBuf;
public interface ByteBufReader<T> {
/**
* Read a value from a ByteBuf
*
* @param buffer The buffer to read from.
* @return The type based on the class type.
* @throws Exception Throws exception if it failed reading.
*/
T read(ByteBuf buffer) throws Exception;
}
@@ -0,0 +1,14 @@
package us.myles.ViaVersion.api.type;
import io.netty.buffer.ByteBuf;
public interface ByteBufWriter<T> {
/**
* Write an object to a type to a ByteBuf
*
* @param buffer The buffer to write to
* @param object The object to write
* @throws Exception Throws if it failed to write
*/
void write(ByteBuf buffer, T object) throws Exception;
}
@@ -0,0 +1,26 @@
package us.myles.ViaVersion.api.type;
import io.netty.buffer.ByteBuf;
public abstract class PartialType<T, X> extends Type<T> {
private final X param;
public PartialType(X param, Class<T> type) {
super(type);
this.param = param;
}
public abstract T read(ByteBuf buffer, X param) throws Exception;
public abstract void write(ByteBuf buffer, X param, T object) throws Exception;
@Override
public T read(ByteBuf buffer) throws Exception {
return read(buffer, this.param);
}
@Override
public void write(ByteBuf buffer, T object) throws Exception {
write(buffer, this.param, object);
}
}
@@ -0,0 +1,91 @@
package us.myles.ViaVersion.api.type;
import lombok.Getter;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.Vector;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.type.types.*;
import us.myles.ViaVersion.api.type.types.minecraft.*;
import java.util.UUID;
@Getter
public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T> {
/* Defined Types */
public static final Type<Byte> BYTE = new ByteType();
public static final Type<Byte[]> BYTE_ARRAY = new ArrayType<>(Type.BYTE);
public static final Type<byte[]> REMAINING_BYTES = new RemainingBytesType();
public static final Type<Short> UNSIGNED_BYTE = new UnsignedByteType();
public static final Type<Short[]> UNSIGNED_BYTE_ARRAY = new ArrayType<>(Type.UNSIGNED_BYTE);
public static final Type<Boolean> BOOLEAN = new BooleanType();
public static final Type<Boolean[]> BOOLEAN_ARRAY = new ArrayType<>(Type.BOOLEAN);
/* Number Types */
public static final Type<Integer> INT = new IntType();
public static final Type<Integer[]> INT_ARRAY = new ArrayType<>(Type.INT);
public static final Type<Double> DOUBLE = new DoubleType();
public static final Type<Double[]> DOUBLE_ARRAY = new ArrayType<>(Type.DOUBLE);
public static final Type<Long> LONG = new LongType();
public static final Type<Long[]> LONG_ARRAY = new ArrayType<>(Type.LONG);
public static final Type<Float> FLOAT = new FloatType();
public static final Type<Float[]> FLOAT_ARRAY = new ArrayType<>(Type.FLOAT);
public static final Type<Short> SHORT = new ShortType();
public static final Type<Short[]> SHORT_ARRAY = new ArrayType<>(Type.SHORT);
public static final Type<Integer> UNSIGNED_SHORT = new UnsignedShortType();
public static final Type<Integer[]> UNSIGNED_SHORT_ARRAY = new ArrayType<>(Type.UNSIGNED_SHORT);
/* Other Types */
public static final Type<String> STRING = new StringType();
public static final Type<String[]> STRING_ARRAY = new ArrayType<>(Type.STRING);
public static final Type<UUID> UUID = new UUIDType();
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Type.UUID);
/* Variable Types */
public static final Type<Integer> VAR_INT = new VarIntType();
public static final Type<Integer[]> VAR_INT_ARRAY = new ArrayType<>(Type.VAR_INT);
/* Special Types */
public static final Type<Void> NOTHING = new VoidType(); // This is purely used for remapping.
/* MC Types */
public static final Type<Position> POSITION = new PositionType();
public static final Type<EulerAngle> ROTATION = new EulerAngleType();
public static final Type<Vector> VECTOR = new VectorType();
public static final Type<CompoundTag> NBT = new NBTType();
public static final Type<CompoundTag[]> NBT_ARRAY = new ArrayType<>(Type.NBT);
public static final Type<UUID> OPTIONAL_UUID = new OptUUIDType();
public static final Type<Position> OPTIONAL_POSITION = new OptPositionType();
public static final Type<Item> ITEM = new ItemType();
public static final Type<Item[]> ITEM_ARRAY = new ItemArrayType();
/* Actual Class */
private final Class<? super T> outputClass;
private final String typeName;
public Type(Class<? super T> outputClass) {
this(outputClass.getSimpleName(), outputClass);
}
public Type(String typeName, Class<? super T> outputClass) {
this.outputClass = outputClass;
this.typeName = typeName;
}
public Class<? extends Type> getBaseClass() {
return this.getClass();
}
@Override
public String toString() {
return "Type|" + getTypeName();
}
}
@@ -0,0 +1,11 @@
package us.myles.ViaVersion.api.type;
public interface TypeConverter<T> {
/**
* Convert from a type to the current type
*
* @param o The input object
* @return The converted type as an object
*/
T from(Object o);
}
@@ -0,0 +1,39 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import java.lang.reflect.Array;
public class ArrayType<T> extends Type<T[]> {
private final Type<T> elementType;
public ArrayType(Type<T> type) {
super(type.getTypeName() + " Array", (Class<T[]>) getArrayClass(type.getOutputClass()));
this.elementType = type;
}
public static Class<?> getArrayClass(Class<?> componentType) {
// Should only happen once per class init.
return Array.newInstance(componentType, 0).getClass();
}
@Override
public T[] read(ByteBuf buffer) throws Exception {
int amount = Type.VAR_INT.read(buffer);
T[] array = (T[]) Array.newInstance(elementType.getOutputClass(), amount);
for (int i = 0; i < amount; i++) {
array[i] = elementType.read(buffer);
}
return array;
}
@Override
public void write(ByteBuf buffer, T[] object) throws Exception {
Type.VAR_INT.write(buffer, object.length);
for (T o : object) {
elementType.write(buffer, o);
}
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class BooleanType extends Type<Boolean> implements TypeConverter<Boolean> {
public BooleanType() {
super(Boolean.class);
}
@Override
public Boolean read(ByteBuf buffer) {
return buffer.readBoolean();
}
@Override
public void write(ByteBuf buffer, Boolean object) {
buffer.writeBoolean(object);
}
@Override
public Boolean from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue() == 1;
}
return (Boolean) o;
}
}
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class ByteType extends Type<Byte> implements TypeConverter<Byte> {
public ByteType() {
super(Byte.class);
}
@Override
public Byte read(ByteBuf buffer) {
return buffer.readByte();
}
@Override
public void write(ByteBuf buffer, Byte object) {
buffer.writeByte(object);
}
@Override
public Byte from(Object o) {
if (o instanceof Number) {
return ((Number) o).byteValue();
}
if (o instanceof Boolean) {
return (Boolean) o ? (byte) 1 : 0;
}
return (Byte) o;
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class DoubleType extends Type<Double> implements TypeConverter<Double> {
public DoubleType() {
super(Double.class);
}
@Override
public Double read(ByteBuf buffer) {
return buffer.readDouble();
}
@Override
public void write(ByteBuf buffer, Double object) {
buffer.writeDouble(object);
}
@Override
public Double from(Object o) {
if (o instanceof Number) {
return ((Number) o).doubleValue();
}
if (o instanceof Boolean) {
return (Boolean) o ? (byte) 1D : 0D;
}
return (Double) o;
}
}
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class FloatType extends Type<Float> implements TypeConverter<Float> {
public FloatType() {
super(Float.class);
}
@Override
public Float read(ByteBuf buffer) {
return buffer.readFloat();
}
@Override
public void write(ByteBuf buffer, Float object) {
buffer.writeFloat(object);
}
@Override
public Float from(Object o) {
if (o instanceof Number) {
return ((Number) o).floatValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? 1F : 0;
}
return (Float) o;
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class IntType extends Type<Integer> implements TypeConverter<Integer> {
public IntType() {
super(Integer.class);
}
@Override
public Integer read(ByteBuf buffer) {
return buffer.readInt();
}
@Override
public void write(ByteBuf buffer, Integer object) {
buffer.writeInt(object);
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? 1 : 0;
}
return (Integer) o;
}
}
@@ -0,0 +1,33 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class LongType extends Type<Long> implements TypeConverter<Long> {
public LongType() {
super(Long.class);
}
@Override
public Long read(ByteBuf buffer) {
return buffer.readLong();
}
@Override
public void write(ByteBuf buffer, Long object) {
buffer.writeLong(object);
}
@Override
public Long from(Object o) {
if (o instanceof Number) {
return ((Number) o).longValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? 1L : 0;
}
return (Long) o;
}
}
@@ -0,0 +1,22 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
public class RemainingBytesType extends Type<byte[]> {
public RemainingBytesType() {
super(byte[].class);
}
@Override
public byte[] read(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
buffer.readBytes(array);
return array;
}
@Override
public void write(ByteBuf buffer, byte[] object) {
buffer.writeBytes(object);
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class ShortType extends Type<Short> implements TypeConverter<Short> {
public ShortType() {
super(Short.class);
}
@Override
public Short read(ByteBuf buffer) {
return buffer.readShort();
}
@Override
public void write(ByteBuf buffer, Short object) {
buffer.writeShort(object);
}
@Override
public Short from(Object o) {
if (o instanceof Number) {
return ((Number) o).shortValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? (short) 1 : 0;
}
return (short) o;
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
public class StringType extends Type<String> {
public StringType() {
super(String.class);
}
@Override
public String read(ByteBuf buffer) throws Exception {
int len = Type.VAR_INT.read(buffer);
Preconditions.checkArgument(len <= Short.MAX_VALUE, "Cannot receive string longer than Short.MAX_VALUE (got %s characters)", len);
byte[] b = new byte[len];
buffer.readBytes(b);
return new String(b, Charsets.UTF_8);
}
@Override
public void write(ByteBuf buffer, String object) throws Exception {
Preconditions.checkArgument(object.length() <= Short.MAX_VALUE, "Cannot send string longer than Short.MAX_VALUE (got %s characters)", object.length());
byte[] b = object.getBytes(Charsets.UTF_8);
Type.VAR_INT.write(buffer, b.length);
buffer.writeBytes(b);
}
}
@@ -0,0 +1,23 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import java.util.UUID;
public class UUIDType extends Type<UUID> {
public UUIDType() {
super(UUID.class);
}
@Override
public UUID read(ByteBuf buffer) {
return new UUID(buffer.readLong(), buffer.readLong());
}
@Override
public void write(ByteBuf buffer, UUID object) {
buffer.writeLong(object.getMostSignificantBits());
buffer.writeLong(object.getLeastSignificantBits());
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class UnsignedByteType extends Type<Short> implements TypeConverter<Short> {
public UnsignedByteType() {
super("Unsigned Byte", Short.class);
}
@Override
public Short read(ByteBuf buffer) {
return buffer.readUnsignedByte();
}
@Override
public void write(ByteBuf buffer, Short object) {
buffer.writeByte(object);
}
@Override
public Short from(Object o) {
if (o instanceof Number) {
return ((Number) o).shortValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? (short) 1 : 0;
}
return (short) o;
}
}
@@ -0,0 +1,32 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class UnsignedShortType extends Type<Integer> implements TypeConverter<Integer> {
public UnsignedShortType() {
super(Integer.class);
}
@Override
public Integer read(ByteBuf buffer) {
return buffer.readUnsignedShort();
}
@Override
public void write(ByteBuf buffer, Integer object) {
buffer.writeShort(object);
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? 1 : 0;
}
return (Integer) o;
}
}
@@ -0,0 +1,65 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class VarIntType extends Type<Integer> implements TypeConverter<Integer> {
public VarIntType() {
super("VarInt", Integer.class);
}
@Override
public void write(ByteBuf buffer, Integer object) {
int part;
while (true) {
part = object & 0x7F;
object >>>= 7;
if (object != 0) {
part |= 0x80;
}
buffer.writeByte(part);
if (object == 0) {
break;
}
}
}
@Override
public Integer read(ByteBuf buffer) {
int out = 0;
int bytes = 0;
byte in;
while (true) {
in = buffer.readByte();
out |= (in & 0x7F) << (bytes++ * 7);
if (bytes > 5) { // 5 is maxBytes
throw new RuntimeException("VarInt too big");
}
if ((in & 0x80) != 0x80) {
break;
}
}
return out;
}
@Override
public Integer from(Object o) {
if (o instanceof Number) {
return ((Number) o).intValue();
}
if (o instanceof Boolean) {
return ((Boolean) o) ? 1 : 0;
}
return (Integer) o;
}
}
@@ -0,0 +1,26 @@
package us.myles.ViaVersion.api.type.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.api.type.TypeConverter;
public class VoidType extends Type<Void> implements TypeConverter<Void> {
public VoidType() {
super(Void.class);
}
@Override
public Void read(ByteBuf buffer) {
return null;
}
@Override
public void write(ByteBuf buffer, Void object) {
}
@Override
public Void from(Object o) {
return null;
}
}
@@ -0,0 +1,19 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.type.Type;
public abstract class BaseChunkType extends Type<Chunk> {
public BaseChunkType() {
super(Chunk.class);
}
public BaseChunkType(String typeName) {
super(typeName, Chunk.class);
}
@Override
public Class<? extends Type> getBaseClass() {
return BaseChunkType.class;
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import org.bukkit.util.EulerAngle;
import us.myles.ViaVersion.api.type.Type;
public class EulerAngleType extends Type<EulerAngle> {
public EulerAngleType() {
super(EulerAngle.class);
}
@Override
public EulerAngle read(ByteBuf buffer) throws Exception {
float x = Type.FLOAT.read(buffer);
float y = Type.FLOAT.read(buffer);
float z = Type.FLOAT.read(buffer);
return new EulerAngle(x, y, z);
}
@Override
public void write(ByteBuf buffer, EulerAngle object) throws Exception {
Type.FLOAT.write(buffer, (float) object.getX());
Type.FLOAT.write(buffer, (float) object.getY());
Type.FLOAT.write(buffer, (float) object.getZ());
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.type.Type;
public class ItemArrayType extends Type<Item[]> {
public ItemArrayType() {
super("Item Array", Item[].class);
}
@Override
public Item[] read(ByteBuf buffer) throws Exception {
int amount = Type.SHORT.read(buffer);
Item[] array = new Item[amount];
for (int i = 0; i < amount; i++) {
array[i] = Type.ITEM.read(buffer);
}
return array;
}
@Override
public void write(ByteBuf buffer, Item[] object) throws Exception {
Type.SHORT.write(buffer, (short) object.length);
for (Item o : object) {
Type.ITEM.write(buffer, o);
}
}
}
@@ -0,0 +1,38 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.type.Type;
public class ItemType extends Type<Item> {
public ItemType() {
super(Item.class);
}
@Override
public Item read(ByteBuf buffer) throws Exception {
short id = buffer.readShort();
if (id < 0) {
return null;
} else {
Item item = new Item();
item.setId(id);
item.setAmount(buffer.readByte());
item.setData(buffer.readShort());
item.setTag(Type.NBT.read(buffer));
return item;
}
}
@Override
public void write(ByteBuf buffer, Item object) throws Exception {
if (object == null) {
buffer.writeShort(-1);
} else {
buffer.writeShort(object.getId());
buffer.writeByte(object.getAmount());
buffer.writeShort(object.getData());
Type.NBT.write(buffer, object.getTag());
}
}
}
@@ -0,0 +1,17 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
import java.util.List;
public abstract class MetaListTypeTemplate extends Type<List<Metadata>> {
public MetaListTypeTemplate() {
super("MetaData List", List.class);
}
@Override
public Class<? extends Type> getBaseClass() {
return MetaListTypeTemplate.class;
}
}
@@ -0,0 +1,15 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.Type;
public abstract class MetaTypeTemplate extends Type<Metadata> {
public MetaTypeTemplate() {
super("Metadata type", Metadata.class);
}
@Override
public Class<? extends Type> getBaseClass() {
return MetaTypeTemplate.class;
}
}
@@ -0,0 +1,49 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.spacehq.opennbt.NBTIO;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import us.myles.ViaVersion.api.type.Type;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class NBTType extends Type<CompoundTag> {
public NBTType() {
super(CompoundTag.class);
}
@Override
public CompoundTag read(ByteBuf buffer) throws Exception {
Preconditions.checkArgument(buffer.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", buffer.readableBytes());
int readerIndex = buffer.readerIndex();
byte b = buffer.readByte();
if (b == 0) {
return null;
} else {
buffer.readerIndex(readerIndex);
ByteBufInputStream bytebufStream = new ByteBufInputStream(buffer);
try (DataInputStream dataInputStream = new DataInputStream(bytebufStream)) {
return (CompoundTag) NBTIO.readTag(dataInputStream);
}
}
}
@Override
public void write(ByteBuf buffer, CompoundTag object) throws Exception {
if (object == null) {
buffer.writeByte(0);
} else {
ByteBufOutputStream bytebufStream = new ByteBufOutputStream(buffer);
DataOutputStream dataOutputStream = new DataOutputStream(bytebufStream);
NBTIO.writeTag(dataOutputStream, object);
dataOutputStream.close();
}
}
}
@@ -0,0 +1,25 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.type.Type;
public class OptPositionType extends Type<Position> {
public OptPositionType() {
super(Position.class);
}
@Override
public Position read(ByteBuf buffer) throws Exception {
boolean present = buffer.readBoolean();
if (!present) return null;
return Type.POSITION.read(buffer);
}
@Override
public void write(ByteBuf buffer, Position object) throws Exception {
buffer.writeBoolean(object != null);
if (object != null)
Type.POSITION.write(buffer, object);
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.type.Type;
import java.util.UUID;
public class OptUUIDType extends Type<UUID> {
public OptUUIDType() {
super(UUID.class);
}
@Override
public UUID read(ByteBuf buffer) {
boolean present = buffer.readBoolean();
if (!present) return null;
return new UUID(buffer.readLong(), buffer.readLong());
}
@Override
public void write(ByteBuf buffer, UUID object) {
if (object == null) {
buffer.writeBoolean(false);
} else {
buffer.writeBoolean(true);
buffer.writeLong(object.getMostSignificantBits());
buffer.writeLong(object.getLeastSignificantBits());
}
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.type.Type;
public class PositionType extends Type<Position> {
public PositionType() {
super(Position.class);
}
@Override
public Position read(ByteBuf buffer) {
long val = buffer.readLong();
long x = (val >> 38); // signed
long y = (val >> 26) & 0xfff; // unsigned
// this shifting madness is used to preserve sign
long z = (val << 38) >> 38; // signed
return new Position(x, y, z);
}
@Override
public void write(ByteBuf buffer, Position object) {
buffer.writeLong(((object.getX() & 0x3ffffff) << 38) | ((object.getY() & 0xfff) << 26) | (object.getZ() & 0x3ffffff));
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.api.type.types.minecraft;
import io.netty.buffer.ByteBuf;
import org.bukkit.util.Vector;
import us.myles.ViaVersion.api.type.Type;
public class VectorType extends Type<Vector> {
public VectorType() {
super(Vector.class);
}
@Override
public Vector read(ByteBuf buffer) throws Exception {
int x = Type.INT.read(buffer);
int y = Type.INT.read(buffer);
int z = Type.INT.read(buffer);
return new Vector(x, y, z);
}
@Override
public void write(ByteBuf buffer, Vector object) throws Exception {
Type.INT.write(buffer, object.getBlockX());
Type.INT.write(buffer, object.getBlockY());
Type.INT.write(buffer, object.getBlockZ());
}
}
@@ -3,6 +3,7 @@ package us.myles.ViaVersion.boss;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
@@ -11,14 +12,11 @@ import us.myles.ViaVersion.api.boss.BossBar;
import us.myles.ViaVersion.api.boss.BossColor;
import us.myles.ViaVersion.api.boss.BossFlag;
import us.myles.ViaVersion.api.boss.BossStyle;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.*;
@Getter
public class ViaBossBar implements BossBar {
@@ -45,17 +43,18 @@ public class ViaBossBar implements BossBar {
}
@Override
public void setTitle(String title) {
Validate.notNull(title, "Title cannot be null");
public BossBar setTitle(@NonNull String title) {
this.title = title;
sendPacket(UpdateAction.UPDATE_TITLE);
return this;
}
@Override
public void setHealth(float health) {
public BossBar setHealth(float health) {
Validate.isTrue((health >= 0 && health <= 1), "Health must be between 0 and 1");
this.health = health;
sendPacket(UpdateAction.UPDATE_HEALTH);
return this;
}
@Override
@@ -64,52 +63,68 @@ public class ViaBossBar implements BossBar {
}
@Override
public void setColor(BossColor color) {
Validate.notNull(color, "Color cannot be null");
public BossBar setColor(@NonNull BossColor color) {
this.color = color;
sendPacket(UpdateAction.UPDATE_STYLE);
return this;
}
@Override
public void setStyle(BossStyle style) {
Validate.notNull(style, "Style cannot be null");
public BossBar setStyle(@NonNull BossStyle style) {
this.style = style;
sendPacket(UpdateAction.UPDATE_STYLE);
return this;
}
@Override
public void addPlayer(Player player) {
if (player != null && !players.contains(player.getUniqueId())) {
players.add(player.getUniqueId());
public BossBar addPlayer(@NonNull Player player) {
return addPlayer(player.getUniqueId());
}
@Override
public BossBar addPlayer(UUID player) {
if (!players.contains(player)) {
players.add(player);
if (visible)
sendPacket(player.getUniqueId(), getPacket(UpdateAction.ADD));
sendPacket(player, getPacket(UpdateAction.ADD));
}
return this;
}
@Override
public void removePlayer(Player player) {
if (player != null && players.contains(player.getUniqueId())) {
public BossBar removePlayer(@NonNull Player player) {
if (players.contains(player.getUniqueId())) {
players.remove(player.getUniqueId());
sendPacket(player.getUniqueId(), getPacket(UpdateAction.REMOVE));
}
return this;
}
@Override
public void addFlag(BossFlag flag) {
public BossBar addPlayers(@NonNull Player... players) {
for (Player p : players)
addPlayer(p);
return this;
}
@Override
public BossBar addFlag(@NonNull BossFlag flag) {
if (!hasFlag(flag))
flags.add(flag);
sendPacket(UpdateAction.UPDATE_FLAGS);
return this;
}
@Override
public void removeFlag(BossFlag flag) {
public BossBar removeFlag(@NonNull BossFlag flag) {
if (hasFlag(flag))
flags.remove(flag);
sendPacket(UpdateAction.UPDATE_FLAGS);
return this;
}
@Override
public boolean hasFlag(BossFlag flag) {
public boolean hasFlag(@NonNull BossFlag flag) {
return flags.contains(flag);
}
@@ -119,13 +134,15 @@ public class ViaBossBar implements BossBar {
}
@Override
public void show() {
public BossBar show() {
setVisible(true);
return this;
}
@Override
public void hide() {
public BossBar hide() {
setVisible(false);
return this;
}
@Override
@@ -141,50 +158,57 @@ public class ViaBossBar implements BossBar {
}
private void sendPacket(UpdateAction action) {
ByteBuf buf = getPacket(action);
for (UUID uuid : players)
for (UUID uuid : new ArrayList<>(players)) {
ByteBuf buf = getPacket(action);
sendPacket(uuid, buf);
}
}
private void sendPacket(UUID uuid, ByteBuf buf) {
if (!ViaVersion.getInstance().isPorted(uuid)) {
if (!ViaVersion.getInstance().isPorted(uuid) || !(ViaVersion.getInstance().getPlayerVersion(uuid) >= ProtocolVersion.v1_9.getId())) {
players.remove(uuid);
buf.release();
return;
}
ViaVersion.getInstance().sendRawPacket(uuid, buf);
}
private ByteBuf getPacket(UpdateAction action) {
ByteBuf buf = Unpooled.buffer();
PacketUtil.writeVarInt(PacketType.PLAY_BOSS_BAR.getNewPacketID(), buf);
PacketUtil.writeUUID(uuid, buf);
PacketUtil.writeVarInt(action.getId(), buf);
switch (action) {
case ADD:
PacketUtil.writeString(fixJson(title), buf);
buf.writeFloat(health);
PacketUtil.writeVarInt(color.getId(), buf);
PacketUtil.writeVarInt(style.getId(), buf);
buf.writeByte(flagToBytes());
break;
case REMOVE:
break;
case UPDATE_HEALTH:
buf.writeFloat(health);
break;
case UPDATE_TITLE:
PacketUtil.writeString(fixJson(title), buf);
break;
case UPDATE_STYLE:
PacketUtil.writeVarInt(color.getId(), buf);
PacketUtil.writeVarInt(style.getId(), buf);
break;
case UPDATE_FLAGS:
buf.writeByte(flagToBytes());
break;
}
try {
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.write(buf, 0x0C); // Boss bar packet
Type.UUID.write(buf, uuid);
Type.VAR_INT.write(buf, action.getId());
switch (action) {
case ADD:
Type.STRING.write(buf, fixJson(title));
buf.writeFloat(health);
Type.VAR_INT.write(buf, color.getId());
Type.VAR_INT.write(buf, style.getId());
buf.writeByte(flagToBytes());
break;
case REMOVE:
break;
case UPDATE_HEALTH:
buf.writeFloat(health);
break;
case UPDATE_TITLE:
Type.STRING.write(buf, fixJson(title));
break;
case UPDATE_STYLE:
Type.VAR_INT.write(buf, color.getId());
Type.VAR_INT.write(buf, style.getId());
break;
case UPDATE_FLAGS:
buf.writeByte(flagToBytes());
break;
}
return buf;
return buf;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private int flagToBytes() {
@@ -195,7 +219,7 @@ public class ViaBossBar implements BossBar {
}
private String fixJson(String text) {
return OutgoingTransformer.fixJson(text);
return Protocol1_9TO1_8.fixJson(text);
}
@RequiredArgsConstructor
@@ -0,0 +1,19 @@
package us.myles.ViaVersion.classgenerator;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
public class BasicHandlerConstructor implements HandlerConstructor {
@Override
public ViaEncodeHandler newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
return new ViaEncodeHandler(info, minecraftEncoder);
}
@Override
public ViaDecodeHandler newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
return new ViaDecodeHandler(info, minecraftDecoder);
}
}
@@ -0,0 +1,165 @@
package us.myles.ViaVersion.classgenerator;
import javassist.*;
import javassist.expr.ConstructorCall;
import javassist.expr.ExprEditor;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.handlers.ViaDecodeHandler;
import us.myles.ViaVersion.handlers.ViaEncodeHandler;
import us.myles.ViaVersion.util.ReflectionUtil;
public class ClassGenerator {
private static HandlerConstructor constructor = new BasicHandlerConstructor();
private static String psPackage = null;
public static HandlerConstructor getConstructor() {
return constructor;
}
public static void generate() {
if (ViaVersion.getInstance().isCompatSpigotBuild() || ViaVersion.getInstance().isProtocolSupport()) {
try {
ClassPool pool = ClassPool.getDefault();
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
pool.insertClassPath(new LoaderClassPath(p.getClass().getClassLoader()));
}
if (ViaVersion.getInstance().isCompatSpigotBuild()) {
Class decodeSuper = ReflectionUtil.nms("PacketDecoder");
Class encodeSuper = ReflectionUtil.nms("PacketEncoder");
// Generate the classes
addSpigotCompatibility(pool, ViaDecodeHandler.class, decodeSuper);
addSpigotCompatibility(pool, ViaEncodeHandler.class, encodeSuper);
} else {
Class decodeSuper = Class.forName(getPSPackage() + ".wrapped.WrappedDecoder");
Class encodeSuper = Class.forName(getPSPackage() + ".wrapped.WrappedEncoder");
// Generate the classes
addPSCompatibility(pool, ViaDecodeHandler.class, decodeSuper);
addPSCompatibility(pool, ViaEncodeHandler.class, encodeSuper);
}
// Implement Constructor
CtClass generated = pool.makeClass("us.myles.ViaVersion.classgenerator.generated.GeneratedConstructor");
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
generated.setInterfaces(new CtClass[]{handlerInterface});
// Import required classes
pool.importPackage("us.myles.ViaVersion.classgenerator.generated");
pool.importPackage("us.myles.ViaVersion.classgenerator");
pool.importPackage("us.myles.ViaVersion.api.data");
pool.importPackage("io.netty.handler.codec");
// Implement Methods
generated.addMethod(CtMethod.make("public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {\n" +
" return new ViaEncodeHandler(info, minecraftEncoder);\n" +
" }", generated));
generated.addMethod(CtMethod.make("public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {\n" +
" return new ViaDecodeHandler(info, minecraftDecoder);\n" +
" }", generated));
constructor = (HandlerConstructor) generated.toClass(HandlerConstructor.class.getClassLoader()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private static Class addSpigotCompatibility(ClassPool pool, Class input, Class superclass) {
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
try {
CtClass generated = pool.getAndRename(input.getName(), newName);
if (superclass != null) {
CtClass toExtend = pool.get(superclass.getName());
generated.setSuperclass(toExtend);
// If it's NMS satisfy constructor.
if (superclass.getName().startsWith("net.minecraft")) {
// Modify constructor to call super
if (generated.getConstructors().length != 0) {
generated.getConstructors()[0].instrument(new ExprEditor() {
@Override
public void edit(ConstructorCall c) throws CannotCompileException {
if (c.isSuper()) {
// Constructor for both has a stats thing.
c.replace("super(null);");
}
super.edit(c);
}
});
}
}
}
return generated.toClass(HandlerConstructor.class.getClassLoader());
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}
return null;
}
private static Class addPSCompatibility(ClassPool pool, Class input, Class superclass) {
String newName = "us.myles.ViaVersion.classgenerator.generated." + input.getSimpleName();
try {
CtClass generated = pool.getAndRename(input.getName(), newName);
if (superclass != null) {
CtClass toExtend = pool.get(superclass.getName());
generated.setSuperclass(toExtend);
// Override setRealEncoder / setRealDecoder
pool.importPackage(getPSPackage());
pool.importPackage(getPSPackage() + ".wrapped");
if (superclass.getName().endsWith("Decoder")) {
// Decoder
generated.addMethod(CtMethod.make("public void setRealDecoder(IPacketDecoder dec) {\n" +
" WrappedDecoder decoder = new WrappedDecoder();" +
" decoder.setRealDecoder(dec);\n" +
" this.minecraftDecoder = decoder;\n" +
" }", generated));
} else {
// Encoder
generated.addMethod(CtMethod.make("public void setRealEncoder(IPacketEncoder enc) {\n" +
" WrappedEncoder encoder = new WrappedEncoder();" +
" encoder.setRealEncoder(enc);\n" +
" this.minecraftEncoder = encoder;\n" +
" }", generated));
}
}
return generated.toClass(HandlerConstructor.class.getClassLoader());
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
}
return null;
}
public static String getPSPackage() {
if (psPackage == null) {
try {
Class.forName("protocolsupport.protocol.core.IPacketDecoder");
psPackage = "protocolsupport.protocol.core";
} catch (ClassNotFoundException e) {
try {
Class.forName("protocolsupport.protocol.pipeline.IPacketDecoder");
psPackage = "protocolsupport.protocol.pipeline";
} catch (ClassNotFoundException e1) {
psPackage = "unknown";
}
}
}
return psPackage;
}
}
@@ -0,0 +1,11 @@
package us.myles.ViaVersion.classgenerator;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.api.data.UserConnection;
public interface HandlerConstructor {
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
}
@@ -0,0 +1,153 @@
package us.myles.ViaVersion.commands;
import lombok.NonNull;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
import us.myles.ViaVersion.api.command.ViaVersionCommand;
import us.myles.ViaVersion.commands.defaultsubs.*;
import java.util.*;
public class ViaCommandHandler implements ViaVersionCommand, CommandExecutor, TabCompleter {
private Map<String, ViaSubCommand> commandMap;
public ViaCommandHandler() {
commandMap = new HashMap<>();
try {
registerDefaults();
} catch (Exception e) {
//ignore never throws exception because it doesn't exists
}
}
@Override
public void registerSubCommand(@NonNull ViaSubCommand command) throws Exception {
Validate.isTrue(command.name().matches("^[a-z0-9_-]{3,15}$"), command.name() + " is not a valid subcommand name");
if (hasSubCommand(command.name()))
throw new Exception("ViaSubCommand " + command.name() + " does already exists!"); //Maybe another exception later.
commandMap.put(command.name().toLowerCase(), command);
}
@Override
public boolean hasSubCommand(String name) {
return commandMap.containsKey(name.toLowerCase());
}
@Override
public ViaSubCommand getSubCommand(String name) {
return commandMap.get(name.toLowerCase());
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String arg, String[] args) {
if (args.length == 0) {
showHelp(sender);
return false;
}
if (!hasSubCommand(args[0])) {
sender.sendMessage(color("&cThis command is not found"));
showHelp(sender);
return false;
}
ViaSubCommand handler = getSubCommand(args[0]);
if (!hasPermission(sender, handler.permission())) {
sender.sendMessage(color("&cYou are not allowed to use this command!"));
return false;
}
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
boolean result = handler.execute(sender, subArgs);
if (!result)
sender.sendMessage("Usage: /viaversion " + handler.usage());
return result;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String arg, String[] args) {
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
List<String> output = new ArrayList<>();
//SubCommands tabcomplete
if (args.length == 1) {
if (!args[0].equals("")) {
for (ViaSubCommand sub : allowed)
if (sub.name().toLowerCase().startsWith(args[0].toLowerCase()))
output.add(sub.name());
} else {
for (ViaSubCommand sub : allowed)
output.add(sub.name());
}
}
//Let the SubCommand handle it
else if (args.length >= 2) {
if (getSubCommand(args[0]) != null) {
ViaSubCommand sub = getSubCommand(args[0]);
if (!allowed.contains(sub))
return output;
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
List<String> tab = sub.onTabComplete(sender, subArgs);
Collections.sort(tab);
return tab;
}
}
return output;
}
public void showHelp(CommandSender sender) {
Set<ViaSubCommand> allowed = calculateAllowedCommands(sender);
if (allowed.size() == 0) {
sender.sendMessage(color("&cYou are not allowed to use this command!"));
return;
}
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
sender.sendMessage(color("&6Commands:"));
for (ViaSubCommand cmd : allowed)
sender.sendMessage(color(String.format("&2/viaversion %s &7- &6%s", cmd.usage(), cmd.description())));
allowed.clear();
}
private Set<ViaSubCommand> calculateAllowedCommands(CommandSender sender) {
Set<ViaSubCommand> cmds = new HashSet<>();
for (ViaSubCommand sub : commandMap.values())
if (hasPermission(sender, sub.permission()))
cmds.add(sub);
return cmds;
}
private boolean hasPermission(CommandSender sender, String permission) {
return permission == null || sender.hasPermission(permission);
}
private void registerDefaults() throws Exception {
registerSubCommand(new ListSubCmd());
registerSubCommand(new PPSSubCmd());
registerSubCommand(new DebugSubCmd());
registerSubCommand(new DisplayLeaksSubCmd());
registerSubCommand(new DontBugMeSubCmd());
registerSubCommand(new AutoTeamSubCmd());
registerSubCommand(new HelpSubCmd());
registerSubCommand(new ReloadSubCmd());
}
public static String color(String string) {
try {
string = ChatColor.translateAlternateColorCodes('&', string); //Dont replace all & with $ like we did before.
} catch (Exception ignored) {
}
return string;
}
public static void sendMessage(@NonNull CommandSender sender, String message, Object... args) {
sender.sendMessage(color(args == null ? message : String.format(message, args)));
}
}
@@ -1,77 +0,0 @@
package us.myles.ViaVersion.commands;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import java.util.ArrayList;
import java.util.List;
/**
* Created by fillefilip8 on 2016-03-03.
*/
@RequiredArgsConstructor
public class ViaVersionCommand implements CommandExecutor {
private final ViaVersionPlugin plugin;
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (sender.hasPermission("viaversion.admin")) {
if (args.length == 0) {
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
sender.sendMessage(color("&6Commands:"));
sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients."));
sender.sendMessage(color("&2/viaversion autoteam &7- &6Toggle automatically teaming to prevent colliding."));
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
} else if (args.length == 1) {
if (args[0].equalsIgnoreCase("list")) {
List<String> portedPlayers = new ArrayList<>();
List<String> normalPlayers = new ArrayList<>();
for (Player p : Bukkit.getOnlinePlayers()) {
if (ViaVersion.getInstance().isPorted(p)) {
portedPlayers.add(p.getName());
} else {
normalPlayers.add(p.getName());
}
}
sender.sendMessage(color("&8[&61.9&8]: &b" + portedPlayers.toString()));
sender.sendMessage(color("&8[&61.8&8]: &b" + normalPlayers.toString()));
} else if (args[0].equalsIgnoreCase("debug")) {
plugin.setDebug(!plugin.isDebug());
sender.sendMessage(color("&6Debug mode is now " + (plugin.isDebug() ? "&aenabled" : "&cdisabled")));
} else if (args[0].equalsIgnoreCase("dontbugme")) {
boolean newValue = !plugin.getConfig().getBoolean("checkforupdates", true);
plugin.getConfig().set("checkforupdates", newValue);
plugin.saveConfig();
sender.sendMessage(color("&6We will " + (newValue ? "&anotify you about updates." : "&cnot tell you about updates.")));
} else if (args[0].equalsIgnoreCase("autoteam")) {
boolean newValue = !plugin.getConfig().getBoolean("auto-team", true);
plugin.getConfig().set("auto-team", newValue);
plugin.saveConfig();
sender.sendMessage(color("&6We will " + (newValue ? "&aautomatically team players" : "&cno longer auto team players")));
sender.sendMessage(color("&6All players will need to re-login for the change to take place."));
} else {
sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion()));
sender.sendMessage(color("&6Commands:"));
sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients."));
sender.sendMessage(color("&2/viaversion autoteam &7- &6Toggle automatically teaming to prevent colliding."));
sender.sendMessage(color("&2/viaversion dontbugme &7- &6Toggle checking for updates."));
}
}
}
return false;
}
public String color(String string) {
return string.replace("&", "§");
}
}
@@ -0,0 +1,31 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
public class AutoTeamSubCmd extends ViaSubCommand {
@Override
public String name() {
return "autoteam";
}
@Override
public String description() {
return "Toggle automatically teaming to prevent colliding.";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
boolean newValue = !ViaVersion.getConfig().isAutoTeam();
plugin.getConfig().set("auto-team", newValue);
plugin.saveConfig();
sendMessage(sender, "&6We will %s", (newValue ? "&aautomatically team players" : "&cno longer auto team players"));
sendMessage(sender, "&6All players will need to re-login for the change to take place.");
return true;
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
public class DebugSubCmd extends ViaSubCommand {
@Override
public String name() {
return "debug";
}
@Override
public String description() {
return "Toggle debug mode";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
plugin.setDebug(!plugin.isDebug());
sendMessage(sender, "&6Debug mode is now %s", (plugin.isDebug() ? "&aenabled" : "&cdisabled"));
return true;
}
}
@@ -0,0 +1,28 @@
package us.myles.ViaVersion.commands.defaultsubs;
import io.netty.util.ResourceLeakDetector;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.api.command.ViaSubCommand;
public class DisplayLeaksSubCmd extends ViaSubCommand {
@Override
public String name() {
return "displayleaks";
}
@Override
public String description() {
return "Try to hunt memory leaks!";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
if (ResourceLeakDetector.getLevel() != ResourceLeakDetector.Level.ADVANCED)
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
else
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
sendMessage(sender, "&6Leak detector is now %s", (ResourceLeakDetector.getLevel() == ResourceLeakDetector.Level.ADVANCED ? "&aenabled" : "&cdisabled"));
return true;
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
public class DontBugMeSubCmd extends ViaSubCommand {
@Override
public String name() {
return "dontbugme";
}
@Override
public String description() {
return "Toggle checking for updates";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
boolean newValue = !ViaVersion.getConfig().isCheckForUpdates();
plugin.getConfig().set("checkforupdates", newValue);
plugin.saveConfig();
sendMessage(sender, "&6We will %snotify you about updates.", (newValue ? "&a" : "&cnot "));
return true;
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
import us.myles.ViaVersion.commands.ViaCommandHandler;
public class HelpSubCmd extends ViaSubCommand {
@Override
public String name() {
return "help";
}
@Override
public String description() {
return "You are looking at it right now!";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
((ViaCommandHandler) plugin.getCommandHandler()).showHelp(sender);
return true;
}
}
@@ -0,0 +1,51 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.util.*;
public class ListSubCmd extends ViaSubCommand {
@Override
public String name() {
return "list";
}
@Override
public String description() {
return "Shows lists of the versions from logged in players";
}
@Override
public String usage() {
return "list";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
Map<ProtocolVersion, Set<String>> playerVersions = new TreeMap<>(new Comparator<ProtocolVersion>() {
@Override
public int compare(ProtocolVersion o1, ProtocolVersion o2) {
return ProtocolVersion.getIndex(o2) - ProtocolVersion.getIndex(o1);
}
});
for (Player p : Bukkit.getOnlinePlayers()) {
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
ProtocolVersion key = ProtocolVersion.getProtocol(playerVersion);
if (!playerVersions.containsKey(key))
playerVersions.put(key, new HashSet<String>());
playerVersions.get(key).add(p.getName());
}
for (Map.Entry<ProtocolVersion, Set<String>> entry : playerVersions.entrySet())
sendMessage(sender, "&8[&6%s&8]: &b%s", entry.getKey().getName(), entry.getValue());
playerVersions.clear();
return true;
}
}
@@ -0,0 +1,67 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import java.util.*;
public class PPSSubCmd extends ViaSubCommand {
@Override
public String name() {
return "pps";
}
@Override
public String description() {
return "Shows the packets per second of online players";
}
@Override
public String usage() {
return "pps";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
Map<Integer, Set<String>> playerVersions = new HashMap<>();
int totalPackets = 0;
int clients = 0;
long max = 0;
for (Player p : Bukkit.getOnlinePlayers()) {
if (!ViaVersion.getInstance().isPorted(p))
continue;
int playerVersion = ViaVersion.getInstance().getPlayerVersion(p);
if (!playerVersions.containsKey(playerVersion))
playerVersions.put(playerVersion, new HashSet<String>());
UserConnection uc = ((ViaVersionPlugin) ViaVersion.getInstance()).getConnection(p);
if (uc.getPacketsPerSecond() > -1) {
playerVersions.get(playerVersion).add(p.getName() + " (" + uc.getPacketsPerSecond() + " PPS)");
totalPackets += uc.getPacketsPerSecond();
if (uc.getPacketsPerSecond() > max) {
max = uc.getPacketsPerSecond();
}
clients++;
}
}
Map<Integer, Set<String>> sorted = new TreeMap<>(playerVersions);
sendMessage(sender, "&4Live Packets Per Second");
if (clients > 1) {
sendMessage(sender, "&cAverage: &f" + (totalPackets / clients));
sendMessage(sender, "&cHighest: &f" + max);
}
if (clients == 0) {
sendMessage(sender, "&cNo clients to display.");
}
for (Map.Entry<Integer, Set<String>> entry : sorted.entrySet())
sendMessage(sender, "&8[&6%s&8]: &b%s", ProtocolVersion.getProtocol(entry.getKey()).getName(), entry.getValue());
sorted.clear();
return true;
}
}
@@ -0,0 +1,27 @@
package us.myles.ViaVersion.commands.defaultsubs;
import org.bukkit.command.CommandSender;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.command.ViaSubCommand;
public class ReloadSubCmd extends ViaSubCommand {
@Override
public String name() {
return "reload";
}
@Override
public String description() {
return "Reload the config from the disk";
}
@Override
public boolean execute(CommandSender sender, String[] args) {
ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance();
plugin.getConf().generateConfig();
sendMessage(sender, "&6Configuration successfully reloaded! Some features may need a restart.");
return true;
}
}
@@ -1,4 +1,4 @@
package us.myles.ViaVersion;
package us.myles.ViaVersion.exception;
public class CancelException extends Exception {
}
@@ -0,0 +1,50 @@
package us.myles.ViaVersion.exception;
import java.util.HashMap;
import java.util.Map;
public class InformativeException extends Exception {
Map<String, Object> info = new HashMap<>();
int sources = 0;
public InformativeException(Throwable cause) {
super(cause);
}
public InformativeException set(String key, Object value) {
info.put(key, value);
return this;
}
public InformativeException addSource(Class<?> sourceClazz) {
return set("Source #" + sources++, getSource(sourceClazz));
}
private String getSource(Class<?> sourceClazz) {
if (sourceClazz.isAnonymousClass()) {
return sourceClazz.getName() + " (Anonymous)";
} else {
return sourceClazz.getName();
}
}
@Override
public String getMessage() {
StringBuilder builder = new StringBuilder();
builder.append("Please post this error to http://github.com/MylesIsCool/ViaVersion/issues\n{");
int i = 0;
for (Map.Entry<String, Object> entry : info.entrySet()) {
builder.append((i == 0 ? "" : ", ") + entry.getKey() + ": " + entry.getValue().toString());
i++;
}
builder.append("}\nActual Error: ");
return builder.toString();
}
@Override
public synchronized Throwable fillInStackTrace() {
// Don't record this stack
return this;
}
}
@@ -3,50 +3,90 @@ package us.myles.ViaVersion.handlers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.transformers.IncomingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.exception.CancelException;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class ViaDecodeHandler extends ByteToMessageDecoder {
private final IncomingTransformer incomingTransformer;
private final ByteToMessageDecoder minecraftDecoder;
private final ConnectionInfo info;
private final UserConnection info;
public static int PASSTHROUGH_ID = 1000;
public ViaDecodeHandler(ConnectionInfo info, ByteToMessageDecoder minecraftDecoder) {
public ViaDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
this.info = info;
this.minecraftDecoder = minecraftDecoder;
this.incomingTransformer = new IncomingTransformer(info);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
// use transformers
if (bytebuf.readableBytes() > 0) {
// Ignore if pending disconnect
if (info.isPendingDisconnect()) {
return;
}
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if (second) {
if (((ViaVersionPlugin) ViaVersion.getInstance()).handlePPS(info))
return;
}
if (info.isActive()) {
int id = PacketUtil.readVarInt(bytebuf);
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf newPacket = ctx.alloc().buffer();
try {
incomingTransformer.transform(id, bytebuf, newPacket);
if (id == ViaDecodeHandler.PASSTHROUGH_ID) {
newPacket.writeBytes(bytebuf);
} else {
PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info);
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(newPacket);
}
bytebuf.clear();
bytebuf = newPacket;
} catch (CancelException e) {
} catch (Exception e) {
// Clear Buffer
bytebuf.clear();
// Release Packet, be free!
newPacket.release();
throw e;
}
}
// call minecraft decoder
list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
try {
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
} finally {
if (info.isActive()) {
bytebuf.release();
}
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PacketUtil.containsCause(cause, CancelException.class)) return;
if (PipelineUtil.containsCause(cause, CancelException.class)) return;
super.exceptionCaught(ctx, cause);
}
}
@@ -3,77 +3,64 @@ package us.myles.ViaVersion.handlers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.CancelException;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.exception.CancelException;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ViaEncodeHandler extends MessageToByteEncoder {
private final ConnectionInfo info;
private final UserConnection info;
private final MessageToByteEncoder minecraftEncoder;
private final OutgoingTransformer outgoingTransformer;
public ViaEncodeHandler(ConnectionInfo info, MessageToByteEncoder minecraftEncoder) {
public ViaEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
this.info = info;
this.minecraftEncoder = minecraftEncoder;
this.outgoingTransformer = new OutgoingTransformer(info);
}
@Override
protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception {
if (ViaVersion.getInstance().isCompatSpigotBuild()) {
Field ver = minecraftEncoder.getClass().getDeclaredField("version");
ver.setAccessible(true);
ver.set(minecraftEncoder, ver.get(this));
}
// handle the packet type
if (!(o instanceof ByteBuf)) {
info.setLastPacket(o);
/* This transformer is more for fixing issues which we find hard at packet level :) */
if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk") && info.isActive()) {
final int[] locX = ReflectionUtil.get(o, "a", int[].class);
final int[] locZ = ReflectionUtil.get(o, "b", int[].class);
final Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World"));
Class<?> mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk");
final Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class);
Runnable chunks = new Runnable() {
@Override
public void run() {
for (int i = 0; i < locX.length; i++) {
int x = locX[i];
int z = locZ[i];
// world invoke function
try {
Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z);
Object packet = constructor.newInstance(chunk, true, 65535);
ctx.pipeline().writeAndFlush(packet);
} catch (InstantiationException | InvocationTargetException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
};
chunks.run();
bytebuf.clear();
throw new CancelException();
}
// call minecraft encoder
PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
try {
PipelineUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
}
}
if (bytebuf.readableBytes() == 0) {
throw new CancelException();
}
// Increment sent
info.incrementSent();
if (info.isActive()) {
int id = PacketUtil.readVarInt(bytebuf);
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf oldPacket = bytebuf.copy();
bytebuf.clear();
try {
outgoingTransformer.transform(id, oldPacket, bytebuf);
} catch (CancelException e) {
PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info);
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(bytebuf);
} catch (Exception e) {
bytebuf.clear();
throw e;
} finally {
@@ -84,7 +71,7 @@ public class ViaEncodeHandler extends MessageToByteEncoder {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PacketUtil.containsCause(cause, CancelException.class)) return;
if (PipelineUtil.containsCause(cause, CancelException.class)) return;
super.exceptionCaught(ctx, cause);
}
}
@@ -0,0 +1,35 @@
package us.myles.ViaVersion.handlers;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import java.util.List;
public class ViaPacketHandler extends MessageToMessageEncoder {
private final UserConnection info;
public ViaPacketHandler(UserConnection info) {
this.info = info;
}
@Override
protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception {
// Split chunks bulk packet up in to single chunks packets before it reached the encoder.
// This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice.
// It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data.
if (!(o instanceof ByteBuf)) {
info.setLastPacket(o);
/* This transformer is more for fixing issues which we find hard at packet level :) */
if (info.isActive()) {
if (info.get(ProtocolInfo.class).getPipeline().filter(o, list)) {
return;
}
}
}
list.add(o);
}
}
@@ -5,17 +5,20 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.ConnectionInfo;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
import us.myles.ViaVersion.classgenerator.ClassGenerator;
import us.myles.ViaVersion.classgenerator.HandlerConstructor;
import java.lang.reflect.Method;
public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
private final ChannelInitializer<SocketChannel> oldInit;
private final ChannelInitializer<SocketChannel> original;
private Method method;
public ViaVersionInitializer(ChannelInitializer<SocketChannel> oldInit) {
this.oldInit = oldInit;
this.original = oldInit;
try {
this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
this.method.setAccessible(true);
@@ -24,16 +27,26 @@ public class ViaVersionInitializer extends ChannelInitializer<SocketChannel> {
}
}
public ChannelInitializer<SocketChannel> getOriginal() {
return original;
}
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ConnectionInfo info = new ConnectionInfo(socketChannel);
UserConnection info = new UserConnection(socketChannel);
// init protocol
new ProtocolPipeline(info);
// Add originals
this.method.invoke(this.oldInit, socketChannel);
this.method.invoke(this.original, socketChannel);
HandlerConstructor constructor = ClassGenerator.getConstructor();
// Add our transformers
ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
MessageToByteEncoder encoder = constructor.newEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder"));
ByteToMessageDecoder decoder = constructor.newDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder"));
ViaPacketHandler chunkHandler = new ViaPacketHandler(info);
socketChannel.pipeline().replace("encoder", "encoder", encoder);
socketChannel.pipeline().replace("decoder", "decoder", decoder);
socketChannel.pipeline().addAfter("packet_handler", "viaversion_packet_handler", chunkHandler);
}
}
@@ -1,62 +0,0 @@
package us.myles.ViaVersion.listeners;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor;
import org.bukkit.block.Block;
import org.bukkit.block.CommandBlock;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.packets.PacketType;
import us.myles.ViaVersion.util.PacketUtil;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@RequiredArgsConstructor
public class CommandBlockListener implements Listener {
private final ViaVersionPlugin plugin;
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onJoin(final PlayerJoinEvent e) {
if (e.getPlayer().isOp() && plugin.isPorted(e.getPlayer())) {
ByteBuf buf = Unpooled.buffer();
PacketUtil.writeVarInt(PacketType.PLAY_ENTITY_STATUS.getNewPacketID(), buf);
buf.writeInt(e.getPlayer().getEntityId());
buf.writeByte(26);
plugin.sendRawPacket(e.getPlayer(), buf);
}
}
@EventHandler(ignoreCancelled = true)
public void onInteract(PlayerInteractEvent e) {
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && plugin.isPorted(e.getPlayer()) && e.getPlayer().isOp()) {
try {
sendCommandBlockPacket(e.getClickedBlock(), e.getPlayer());
} catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e1) {
e1.printStackTrace();
}
}
}
private void sendCommandBlockPacket(Block b, Player player) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
if (!(b.getState() instanceof CommandBlock))
return;
CommandBlock cmd = (CommandBlock) b.getState();
Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand"));
Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket");
Object nmsPlayer = ReflectionUtil.invoke(player, "getHandle");
Object playerConnection = ReflectionUtil.get(nmsPlayer, "playerConnection", ReflectionUtil.nms("PlayerConnection"));
Method sendPacket = playerConnection.getClass().getMethod("sendPacket", ReflectionUtil.nms("Packet"));
sendPacket.invoke(playerConnection, updatePacket); //Let the transformer do the work
}
}
@@ -1,206 +0,0 @@
package us.myles.ViaVersion.metadata;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.EntityType;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.Vector;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.slot.ItemSlotRewriter;
import us.myles.ViaVersion.slot.ItemSlotRewriter.ItemStack;
import us.myles.ViaVersion.transformers.OutgoingTransformer;
import us.myles.ViaVersion.util.PacketUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MetadataRewriter {
public static void writeMetadata1_9(EntityType type, List<Entry> list, ByteBuf output) {
short id = -1;
int data = -1;
for (Entry entry : list) {
MetaIndex metaIndex = entry.index;
try {
if (metaIndex.getNewType() != NewType.Discontinued) {
if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts
output.writeByte(metaIndex.getNewIndex());
output.writeByte(metaIndex.getNewType().getTypeID());
}
Object value = entry.value;
switch (metaIndex.getNewType()) {
case Byte:
// convert from int, byte
if (metaIndex.getOldType() == Type.Byte) {
output.writeByte((Byte) value);
}
if (metaIndex.getOldType() == Type.Int) {
output.writeByte(((Integer) value).byteValue());
}
break;
case OptUUID:
String owner = (String) value;
UUID toWrite = null;
if (owner.length() != 0) {
try {
toWrite = UUID.fromString(owner);
} catch (Exception ignored) {
}
}
output.writeBoolean(toWrite != null);
if (toWrite != null)
PacketUtil.writeUUID(toWrite, output);
break;
case BlockID:
// if we have both sources :))
if (metaIndex.getOldType() == Type.Byte) {
data = (Byte) value;
}
if (metaIndex.getOldType() == Type.Short) {
id = (Short) value;
}
if (id != -1 && data != -1) {
int combined = id << 4 | data;
data = -1;
id = -1;
PacketUtil.writeVarInt(combined, output);
}
break;
case VarInt:
// convert from int, short, byte
if (metaIndex.getOldType() == Type.Byte) {
PacketUtil.writeVarInt(((Byte) value).intValue(), output);
}
if (metaIndex.getOldType() == Type.Short) {
PacketUtil.writeVarInt(((Short) value).intValue(), output);
}
if (metaIndex.getOldType() == Type.Int) {
PacketUtil.writeVarInt((Integer) value, output);
}
break;
case Float:
output.writeFloat((Float) value);
break;
case String:
PacketUtil.writeString((String) value, output);
break;
case Boolean:
if (metaIndex == MetaIndex.AGEABLE_AGE)
output.writeBoolean((Byte) value < 0);
else
output.writeBoolean((Byte) value != 0);
break;
case Slot:
ItemStack item = (ItemStack) value;
ItemSlotRewriter.fixIdsFrom1_8To1_9(item);
ItemSlotRewriter.writeItemStack(item, output);
break;
case Position:
Vector vector = (Vector) value;
output.writeInt((int) vector.getX());
output.writeInt((int) vector.getY());
output.writeInt((int) vector.getZ());
break;
case Vector3F:
EulerAngle angle = (EulerAngle) value;
output.writeFloat((float) angle.getX());
output.writeFloat((float) angle.getY());
output.writeFloat((float) angle.getZ());
break;
case Chat:
PacketUtil.writeString(OutgoingTransformer.fixJson((String) value), output);
break;
default:
System.out.println("[Out] Unhandled MetaDataType: " + metaIndex.getNewType());
break;
}
}
} catch (Exception e) {
if (!((ViaVersionPlugin) ViaVersion.getInstance()).isSuppressMetadataErrors()) {
System.out.println("INCLUDE THIS IN YOUR ERROR LOG!");
if (type != null)
System.out.println("An error occurred with entity meta data for " + type + " OldID: " + entry.oldID);
else
System.out.println("An error occurred with entity meta data for UNKNOWN_ENTITY OldID: " + entry.oldID);
if (metaIndex != null) {
System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex());
System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType());
}
e.printStackTrace();
}
}
}
output.writeByte(255);
}
public static List<Entry> readMetadata1_8(EntityType entityType, ByteBuf buf) {
List<Entry> entries = new ArrayList<>();
byte item;
while ((item = buf.readByte()) != 127) {
Type type = Type.byId((item & 0xE0) >> 5);
int id = item & 0x1F;
MetaIndex index = MetaIndex.getIndex(entityType, id);
switch (type) {
case Byte:
entries.add(new Entry(index, buf.readByte(), id));
break;
case Short:
entries.add(new Entry(index, buf.readShort(), id));
break;
case Int:
entries.add(new Entry(index, buf.readInt(), id));
break;
case Float:
entries.add(new Entry(index, buf.readFloat(), id));
break;
case String:
entries.add(new Entry(index, PacketUtil.readString(buf), id));
break;
case Slot: {
try {
entries.add(new Entry(index, ItemSlotRewriter.readItemStack(buf), id));
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case Position: {
int x = buf.readInt();
int y = buf.readInt();
int z = buf.readInt();
entries.add(new Entry(index, new Vector(x, y, z), id));
break;
}
case Rotation: {
float x = buf.readFloat();
float y = buf.readFloat();
float z = buf.readFloat();
entries.add(new Entry(index, new EulerAngle(x, y, z), id));
break;
}
default:
System.out.println("[Out] Unhandled MetaDataType: " + type);
break;
}
}
return entries;
}
@Getter
@Setter
public static final class Entry {
private final int oldID;
private MetaIndex index;
private Object value;
private Entry(MetaIndex index, Object value, int id) {
this.index = index;
this.value = value;
this.oldID = id;
}
}
}
@@ -1,26 +0,0 @@
package us.myles.ViaVersion.metadata;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum NewType {
Byte(0),
VarInt(1),
Float(2),
String(3),
Chat(4),
Slot(5),
Boolean(6),
Vector3F(7),
Position(8),
OptPosition(9),
Direction(10),
OptUUID(11),
BlockID(12),
Discontinued(99);
private final int typeID;
}
@@ -1,200 +1,208 @@
package us.myles.ViaVersion.packets;
public enum PacketType {
/* Handshake serverbound */
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00),
/* Login serverbound */
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00),
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01),
/* Login clientbound */
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00),
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01),
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02),
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03),
/* Status serverbound */
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00),
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01),
/* Status clientbound */
STATUS_RESPONSE(State.STATUS, Direction.OUTGOING, 0x00),
STATUS_PONG(State.STATUS, Direction.OUTGOING, 0x01),
/* Play serverbound */
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00),
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01),
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02),
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03),
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04),
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05),
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06),
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07),
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08),
PLAY_PLUGIN_MESSAGE_REQUEST(State.PLAY, Direction.INCOMING, 0x17, 0x09),
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A),
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B),
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C),
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D),
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E),
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F),
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10),
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11),
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12),
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13),
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14),
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15),
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16),
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17),
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18),
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19),
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A),
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B),
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C),
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D),
/* Play clientbound */
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00),
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01),
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02),
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03),
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04),
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05),
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06),
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07),
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08),
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09),
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A),
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B),
PLAY_BOSS_BAR(State.PLAY, Direction.OUTGOING, -1, 0x0C),
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D),
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E),
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F),
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10),
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11),
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12),
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13),
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14),
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15),
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16),
PLAY_SET_COOLDOWN(State.PLAY, Direction.OUTGOING, -1, 0x17),
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18),
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19),
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A),
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B),
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C),
PLAY_UNLOAD_CHUNK(State.PLAY, Direction.OUTGOING, -1, 0x1D),
PLAY_CHANGE_GAME_STATE(State.PLAY, Direction.OUTGOING, 0x2B, 0x1E),
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F),
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20),
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21),
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22),
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23),
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24),
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25),
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26),
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27),
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28),
PLAY_VEHICLE_MOVE(State.PLAY, Direction.OUTGOING, -1, 0x29),
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A),
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B),
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C),
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D),
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E),
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F),
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30),
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31),
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32),
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33),
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34),
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35),
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36),
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37),
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38),
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39),
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A),
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B),
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C),
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D),
PLAY_UPDATE_HEALTH(State.PLAY, Direction.OUTGOING, 0x06, 0x3E),
PLAY_SCOREBOARD_OBJ(State.PLAY, Direction.OUTGOING, 0x3B, 0x3F),
PLAY_SET_PASSENGERS(State.PLAY, Direction.OUTGOING, -1, 0x40),
PLAY_TEAM(State.PLAY, Direction.OUTGOING, 0x3E, 0x41),
PLAY_UPDATE_SCORE(State.PLAY, Direction.OUTGOING, 0x3C, 0x42),
PLAY_SPAWN_POSITION(State.PLAY, Direction.OUTGOING, 0x05, 0x43),
PLAY_TIME_UPDATE(State.PLAY, Direction.OUTGOING, 0x03, 0x44),
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45),
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46),
PLAY_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, -1, 0x47),
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48),
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49),
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A),
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B),
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C),
PLAY_MAP_CHUNK_BULK(State.PLAY, Direction.OUTGOING, 0x26, -1),
PLAY_SET_COMPRESSION(State.PLAY, Direction.OUTGOING, 0x46, -1),
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1),;
private State state;
private Direction direction;
private int packetID;
private int newPacketID = -1;
PacketType(State state, Direction direction, int packetID) {
this.state = state;
this.direction = direction;
this.packetID = packetID;
this.newPacketID = packetID;
}
PacketType(State state, Direction direction, int packetID, int newPacketID) {
this.state = state;
this.direction = direction;
this.packetID = packetID;
this.newPacketID = newPacketID;
}
public State getState() {
return state;
}
public Direction getDirection() {
return direction;
}
public int getPacketID() {
return packetID;
}
public int getNewPacketID() {
return newPacketID;
}
public static PacketType findNewPacket(State state, Direction direction, int id) {
if(id == -1)
return null;
for (PacketType pt : values()) {
if (pt.getNewPacketID() == id
&& pt.getState() == state
&& pt.getDirection() == direction)
return pt;
}
return null;
}
public static PacketType findOldPacket(State state, Direction direction, int id) {
if(id == -1)
return null;
for (PacketType pt : values()) {
if (pt.getPacketID() == id
&& pt.getState() == state
&& pt.getDirection() == direction)
return pt;
}
return null;
}
public static PacketType getIncomingPacket(State state, int id) {
return findNewPacket(state, Direction.INCOMING, id);
}
public static PacketType getOutgoingPacket(State state, int id) {
return findOldPacket(state, Direction.OUTGOING, id);
}
}
package us.myles.ViaVersion.packets;
import java.util.HashMap;
@Deprecated
public enum PacketType {
/* Handshake serverbound */
HANDSHAKE(State.HANDSHAKE, Direction.INCOMING, 0x00), // Mapped
/* Login serverbound */
LOGIN_START(State.LOGIN, Direction.INCOMING, 0x00), // Mapped
LOGIN_ENCRYPTION_RESPONSE(State.LOGIN, Direction.INCOMING, 0x01), // Mapped
/* Login clientbound */
LOGIN_DISCONNECT(State.LOGIN, Direction.OUTGOING, 0x00), // Mapped
LOGIN_ENCRYPTION_REQUEST(State.LOGIN, Direction.OUTGOING, 0x01), // Mapped
LOGIN_SUCCESS(State.LOGIN, Direction.OUTGOING, 0x02), // Mapped
LOGIN_SETCOMPRESSION(State.LOGIN, Direction.OUTGOING, 0x03), // Mapped
/* Status serverbound */
STATUS_REQUEST(State.STATUS, Direction.INCOMING, 0x00), // Mapped
STATUS_PING(State.STATUS, Direction.INCOMING, 0x01), // Mapped
/* Status clientbound */
STATUS_RESPONSE(State.STATUS, Direction.OUTGOING, 0x00),
STATUS_PONG(State.STATUS, Direction.OUTGOING, 0x01),
/* Play serverbound */
PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00), // Mapped
PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01), // Mapped
PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02), // Mapped
PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03), // Mapped
PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04), // Mapped
PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05), // Mapped
PLAY_ENCHANT_ITEM(State.PLAY, Direction.INCOMING, 0x11, 0x06), // Mapped
PLAY_CLICK_WINDOW(State.PLAY, Direction.INCOMING, 0x0E, 0x07), // Mapped
PLAY_CLOSE_WINDOW_REQUEST(State.PLAY, Direction.INCOMING, 0x0D, 0x08), // Mapped
PLAY_PLUGIN_MESSAGE_REQUEST(State.PLAY, Direction.INCOMING, 0x17, 0x09),
PLAY_USE_ENTITY(State.PLAY, Direction.INCOMING, 0x02, 0x0A), // Mapped
PLAY_KEEP_ALIVE_REQUEST(State.PLAY, Direction.INCOMING, 0x00, 0x0B), // Mapped
PLAY_PLAYER_POSITION_REQUEST(State.PLAY, Direction.INCOMING, 0x04, 0x0C), // Mapped
PLAY_PLAYER_POSITION_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x06, 0x0D), // Mapped
PLAY_PLAYER_LOOK_REQUEST(State.PLAY, Direction.INCOMING, 0x05, 0x0E), // Mapped
PLAY_PLAYER(State.PLAY, Direction.INCOMING, 0x03, 0x0F), // Mapped
PLAY_VEHICLE_MOVE_REQUEST(State.PLAY, Direction.INCOMING, -1, 0x10), // Mapped
PLAY_STEER_BOAT(State.PLAY, Direction.INCOMING, -1, 0x11), // Mapped
PLAY_PLAYER_ABILITIES_REQUEST(State.PLAY, Direction.INCOMING, 0x13, 0x12), // Mapped
PLAY_PLAYER_DIGGING(State.PLAY, Direction.INCOMING, 0x07, 0x13), // Mapped
PLAY_ENTITY_ACTION(State.PLAY, Direction.INCOMING, 0x0B, 0x14), // Mapped
PLAY_STEER_VEHICLE(State.PLAY, Direction.INCOMING, 0x0C, 0x15), // Mapped
PLAY_RESOURCE_PACK_STATUS(State.PLAY, Direction.INCOMING, 0x19, 0x16), // Mapped
PLAY_HELD_ITEM_CHANGE_REQUEST(State.PLAY, Direction.INCOMING, 0x09, 0x17), // Mapped
PLAY_CREATIVE_INVENTORY_ACTION(State.PLAY, Direction.INCOMING, 0x10, 0x18), // Mapped
PLAY_UPDATE_SIGN_REQUEST(State.PLAY, Direction.INCOMING, 0x12, 0x19), // Mapped
PLAY_ANIMATION_REQUEST(State.PLAY, Direction.INCOMING, 0x0A, 0x1A), // Mapped
PLAY_SPECTATE(State.PLAY, Direction.INCOMING, 0x18, 0x1B), // Mapped
PLAY_PLAYER_BLOCK_PLACEMENT(State.PLAY, Direction.INCOMING, 0x08, 0x1C), // Mapped
PLAY_USE_ITEM(State.PLAY, Direction.INCOMING, -1, 0x1D), // Mapped
/* Play clientbound */
PLAY_SPAWN_OBJECT(State.PLAY, Direction.OUTGOING, 0x0E, 0x00), // Mapped
PLAY_SPAWN_XP_ORB(State.PLAY, Direction.OUTGOING, 0x11, 0x01), // Mapped
PLAY_SPAWN_GLOBAL_ENTITY(State.PLAY, Direction.OUTGOING, 0x2C, 0x02), // Mapped
PLAY_SPAWN_MOB(State.PLAY, Direction.OUTGOING, 0x0F, 0x03), // Mapped
PLAY_SPAWN_PAINTING(State.PLAY, Direction.OUTGOING, 0x10, 0x04), // Mapped
PLAY_SPAWN_PLAYER(State.PLAY, Direction.OUTGOING, 0x0C, 0x05), // Mapped
PLAY_ANIMATION(State.PLAY, Direction.OUTGOING, 0x0B, 0x06), // Mapped
PLAY_STATS(State.PLAY, Direction.OUTGOING, 0x37, 0x07), // Mapped
PLAY_BLOCK_BREAK_ANIMATION(State.PLAY, Direction.OUTGOING, 0x25, 0x08), // Mapped
PLAY_UPDATE_BLOCK_ENTITY(State.PLAY, Direction.OUTGOING, 0x35, 0x09), // Mapped
PLAY_BLOCK_ACTION(State.PLAY, Direction.OUTGOING, 0x24, 0x0A), // Mapped
PLAY_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x23, 0x0B), // Mapped
PLAY_BOSS_BAR(State.PLAY, Direction.OUTGOING, -1, 0x0C),
PLAY_SERVER_DIFFICULTY(State.PLAY, Direction.OUTGOING, 0x41, 0x0D), // Mapped
PLAY_TAB_COMPLETE(State.PLAY, Direction.OUTGOING, 0x3A, 0x0E), // Mapped
PLAY_CHAT_MESSAGE(State.PLAY, Direction.OUTGOING, 0x02, 0x0F), // Mapped
PLAY_MULTI_BLOCK_CHANGE(State.PLAY, Direction.OUTGOING, 0x22, 0x10), // Mapped
PLAY_CONFIRM_TRANSACTION(State.PLAY, Direction.OUTGOING, 0x32, 0x11), // Mapped
PLAY_CLOSE_WINDOW(State.PLAY, Direction.OUTGOING, 0x2E, 0x12), // Mapped
PLAY_OPEN_WINDOW(State.PLAY, Direction.OUTGOING, 0x2D, 0x13), // Mapped
PLAY_WINDOW_ITEMS(State.PLAY, Direction.OUTGOING, 0x30, 0x14), // Mapped
PLAY_WINDOW_PROPERTY(State.PLAY, Direction.OUTGOING, 0x31, 0x15), // Mapped
PLAY_SET_SLOT(State.PLAY, Direction.OUTGOING, 0x2F, 0x16), // Mapped
PLAY_SET_COOLDOWN(State.PLAY, Direction.OUTGOING, -1, 0x17),
PLAY_PLUGIN_MESSAGE(State.PLAY, Direction.OUTGOING, 0x3F, 0x18), // Mapped
PLAY_NAMED_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, 0x29, 0x19), // Mapped
PLAY_DISCONNECT(State.PLAY, Direction.OUTGOING, 0x40, 0x1A), // Mapped
PLAY_ENTITY_STATUS(State.PLAY, Direction.OUTGOING, 0x1A, 0x1B), // Mapped
PLAY_EXPLOSION(State.PLAY, Direction.OUTGOING, 0x27, 0x1C), // Mapped
PLAY_UNLOAD_CHUNK(State.PLAY, Direction.OUTGOING, -1, 0x1D),
PLAY_CHANGE_GAME_STATE(State.PLAY, Direction.OUTGOING, 0x2B, 0x1E),
PLAY_KEEP_ALIVE(State.PLAY, Direction.OUTGOING, 0x00, 0x1F), // Mapped
PLAY_CHUNK_DATA(State.PLAY, Direction.OUTGOING, 0x21, 0x20), // Mapped
PLAY_EFFECT(State.PLAY, Direction.OUTGOING, 0x28, 0x21), // Mapped
PLAY_PARTICLE(State.PLAY, Direction.OUTGOING, 0x2A, 0x22), // Mapped
PLAY_JOIN_GAME(State.PLAY, Direction.OUTGOING, 0x01, 0x23), // Mapped
PLAY_MAP(State.PLAY, Direction.OUTGOING, 0x34, 0x24), // Mapped
PLAY_ENTITY_RELATIVE_MOVE(State.PLAY, Direction.OUTGOING, 0x15, 0x25), // Mapped
PLAY_ENTITY_LOOK_MOVE(State.PLAY, Direction.OUTGOING, 0x17, 0x26), // Mapped
PLAY_ENTITY_LOOK(State.PLAY, Direction.OUTGOING, 0x16, 0x27), // Mapped
PLAY_ENTITY(State.PLAY, Direction.OUTGOING, 0x14, 0x28), // Mapped
PLAY_VEHICLE_MOVE(State.PLAY, Direction.OUTGOING, -1, 0x29),
PLAY_OPEN_SIGN_EDITOR(State.PLAY, Direction.OUTGOING, 0x36, 0x2A), // Mapped
PLAY_PLAYER_ABILITIES(State.PLAY, Direction.OUTGOING, 0x39, 0x2B), // Mapped
PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C), // Mapped
PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D), // Mapped
PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E), // Mapped
PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F), // Mapped
PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30), // Mapped
PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31), // Mapped
PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32), // Mapped
PLAY_RESPAWN(State.PLAY, Direction.OUTGOING, 0x07, 0x33), // Mapped
PLAY_ENTITY_HEAD_LOOK(State.PLAY, Direction.OUTGOING, 0x19, 0x34), // Mapped
PLAY_WORLD_BORDER(State.PLAY, Direction.OUTGOING, 0x44, 0x35), // Mapped
PLAY_CAMERA(State.PLAY, Direction.OUTGOING, 0x43, 0x36), // Mapped
PLAY_HELD_ITEM_CHANGE(State.PLAY, Direction.OUTGOING, 0x09, 0x37), // Mapped
PLAY_DISPLAY_SCOREBOARD(State.PLAY, Direction.OUTGOING, 0x3D, 0x38), // Mapped
PLAY_ENTITY_METADATA(State.PLAY, Direction.OUTGOING, 0x1C, 0x39), // Mapped
PLAY_ATTACH_ENTITY(State.PLAY, Direction.OUTGOING, 0x1B, 0x3A), // Mapped
PLAY_ENTITY_VELOCITY(State.PLAY, Direction.OUTGOING, 0x12, 0x3B), // Mapped
PLAY_ENTITY_EQUIPMENT(State.PLAY, Direction.OUTGOING, 0x04, 0x3C), // Mapped
PLAY_SET_XP(State.PLAY, Direction.OUTGOING, 0x1F, 0x3D), // Mapped
PLAY_UPDATE_HEALTH(State.PLAY, Direction.OUTGOING, 0x06, 0x3E), // Mapped
PLAY_SCOREBOARD_OBJ(State.PLAY, Direction.OUTGOING, 0x3B, 0x3F), // Mapped
PLAY_SET_PASSENGERS(State.PLAY, Direction.OUTGOING, -1, 0x40),
PLAY_TEAM(State.PLAY, Direction.OUTGOING, 0x3E, 0x41), // Mapped
PLAY_UPDATE_SCORE(State.PLAY, Direction.OUTGOING, 0x3C, 0x42), // Mapped
PLAY_SPAWN_POSITION(State.PLAY, Direction.OUTGOING, 0x05, 0x43), // Mapped
PLAY_TIME_UPDATE(State.PLAY, Direction.OUTGOING, 0x03, 0x44), // Mapped
PLAY_TITLE(State.PLAY, Direction.OUTGOING, 0x45, 0x45), // Mapped
PLAY_UPDATE_SIGN(State.PLAY, Direction.OUTGOING, 0x33, 0x46), // Mapped
PLAY_SOUND_EFFECT(State.PLAY, Direction.OUTGOING, -1, 0x47),
PLAY_PLAYER_LIST_HEADER_FOOTER(State.PLAY, Direction.OUTGOING, 0x47, 0x48), // Mapped
PLAY_COLLECT_ITEM(State.PLAY, Direction.OUTGOING, 0x0D, 0x49), // Mapped
PLAY_ENTITY_TELEPORT(State.PLAY, Direction.OUTGOING, 0x18, 0x4A), // Mapped
PLAY_ENTITY_PROPERTIES(State.PLAY, Direction.OUTGOING, 0x20, 0x4B), // Mapped
PLAY_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1D, 0x4C), // Mapped
PLAY_MAP_CHUNK_BULK(State.PLAY, Direction.OUTGOING, 0x26, -1),
PLAY_SET_COMPRESSION(State.PLAY, Direction.OUTGOING, 0x46, -1),
PLAY_UPDATE_ENTITY_NBT(State.PLAY, Direction.OUTGOING, 0x49, -1);
private static HashMap<Short, PacketType> oldids = new HashMap<>();
private static HashMap<Short, PacketType> newids = new HashMap<>();
static {
for (PacketType pt : PacketType.values()) {
oldids.put(toShort((short) pt.getPacketID(), (short) pt.getDirection().ordinal(), (short) pt.getState().ordinal()), pt);
newids.put(toShort((short) pt.getNewPacketID(), (short) pt.getDirection().ordinal(), (short) pt.getState().ordinal()), pt);
}
}
private State state;
private Direction direction;
private int packetID;
private int newPacketID = -1;
PacketType(State state, Direction direction, int packetID) {
this.state = state;
this.direction = direction;
this.packetID = packetID;
this.newPacketID = packetID;
}
PacketType(State state, Direction direction, int packetID, int newPacketID) {
this.state = state;
this.direction = direction;
this.packetID = packetID;
this.newPacketID = newPacketID;
}
public static PacketType findNewPacket(State state, Direction direction, int id) {
return newids.get(toShort((short) id, (short) direction.ordinal(), (short) state.ordinal()));
}
public static PacketType findOldPacket(State state, Direction direction, int id) {
return oldids.get(toShort((short) id, (short) direction.ordinal(), (short) state.ordinal()));
}
public static PacketType getIncomingPacket(State state, int id) {
return findNewPacket(state, Direction.INCOMING, id);
}
public static PacketType getOutgoingPacket(State state, int id) {
return findOldPacket(state, Direction.OUTGOING, id);
}
private static short toShort(short id, short direction, short state) {
return (short) ((id & 0x00FF) | (direction << 8) & 0x0F00 | (state << 12) & 0xF000);
}
public State getState() {
return state;
}
public Direction getDirection() {
return direction;
}
public int getPacketID() {
return packetID;
}
public int getNewPacketID() {
return newPacketID;
}
}
@@ -0,0 +1,224 @@
package us.myles.ViaVersion.protocols.base;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import us.myles.ViaVersion.ViaVersionPlugin;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.ViaVersion;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
public class BaseProtocol extends Protocol {
@Override
protected void registerPackets() {
/* Outgoing Packets */
// Status Response Packet
registerOutgoing(State.STATUS, 0x00, 0x00, new PacketRemapper() { // Status Response Packet
@Override
public void registerMap() {
map(Type.STRING);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ProtocolInfo info = wrapper.user().get(ProtocolInfo.class);
String originalStatus = wrapper.get(Type.STRING, 0);
try {
JSONObject json = (JSONObject) new JSONParser().parse(originalStatus);
JSONObject version = (JSONObject) json.get("version");
int protocolVersion = ((Long) version.get("protocol")).intValue();
if (ViaVersion.getConfig().isSendSupportedVersions()) //Send supported versions
version.put("supportedVersions", ViaVersion.getInstance().getSupportedVersions());
if (ProtocolRegistry.SERVER_PROTOCOL == -1) // Set the Server protocol if the detection on startup failed
ProtocolRegistry.SERVER_PROTOCOL = protocolVersion;
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), ProtocolRegistry.SERVER_PROTOCOL);
if (protocols != null) {
if (protocolVersion != 9999) //Fix ServerListPlus
version.put("protocol", info.getProtocolVersion());
} else {
// not compatible :(, *plays very sad violin*
wrapper.user().setActive(false);
}
if (ViaVersion.getConfig().getBlockedProtocols().contains(info.getProtocolVersion()))
version.put("protocol", -1); // Show blocked versions as outdated
wrapper.set(Type.STRING, 0, json.toJSONString()); // Update value
} catch (ParseException e) {
e.printStackTrace();
}
}
});
}
});
registerOutgoing(State.STATUS, 0x01, 0x01); // Status Pong Packet
registerOutgoing(State.LOGIN, 0x00, 0x00); // Login Disconnect Packet
registerOutgoing(State.LOGIN, 0x01, 0x01); // Encryption Request Packet
// Login Success Packet
registerOutgoing(State.LOGIN, 0x02, 0x02, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // 0 - UUID as String
map(Type.STRING); // 1 - Player Username
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ProtocolInfo info = wrapper.user().get(ProtocolInfo.class);
info.setState(State.PLAY);
// Save other info
UUID uuid = UUID.fromString(wrapper.get(Type.STRING, 0));
info.setUuid(uuid);
info.setUsername(wrapper.get(Type.STRING, 1));
// Add to ported clients
((ViaVersionPlugin) ViaVersion.getInstance()).addPortedClient(wrapper.user());
if (info.getPipeline().pipes().size() == 1 && info.getPipeline().pipes().get(0).getClass() == BaseProtocol.class) // Only base protocol
wrapper.user().setActive(false);
if (ViaVersion.getInstance().isDebug()) {
// Print out the route to console
((ViaVersionPlugin) ViaVersion.getInstance()).getLogger().log(Level.INFO, "{0} logged in with protocol {1}, Route: {2}",
new Object[]{
wrapper.get(Type.STRING, 1),
info.getProtocolVersion(),
StringUtils.join(info.getPipeline().pipes(), ", ")
});
}
}
});
}
});
registerOutgoing(State.LOGIN, 0x03, 0x03); // Login Set Compression Packet
/* Incoming Packets */
// Handshake Packet
registerIncoming(State.HANDSHAKE, 0x00, 0x00, new PacketRemapper() {
@Override
public void registerMap() {
// select right protocol
map(Type.VAR_INT); // 0 - Client Protocol Version
map(Type.STRING); // 1 - Server Address
map(Type.UNSIGNED_SHORT); // 2 - Server Port
map(Type.VAR_INT); // 3 - Next State
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int protVer = wrapper.get(Type.VAR_INT, 0);
int state = wrapper.get(Type.VAR_INT, 1);
ProtocolInfo info = wrapper.user().get(ProtocolInfo.class);
info.setProtocolVersion(protVer);
// Choose the pipe
List<Pair<Integer, Protocol>> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), ProtocolRegistry.SERVER_PROTOCOL);
ProtocolPipeline pipeline = wrapper.user().get(ProtocolInfo.class).getPipeline();
if (protocols != null) {
for (Pair<Integer, Protocol> prot : protocols) {
pipeline.add(prot.getValue());
}
wrapper.set(Type.VAR_INT, 0, ProtocolRegistry.SERVER_PROTOCOL);
}
// Change state
if (state == 1) {
info.setState(State.STATUS);
}
if (state == 2) {
info.setState(State.LOGIN);
}
}
});
}
});
registerIncoming(State.STATUS, 0x00, 0x00); // Status Request Packet
registerIncoming(State.STATUS, 0x01, 0x01); // Status Ping Packet
// Login Start Packet
registerIncoming(State.LOGIN, 0x00, 0x00, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(final PacketWrapper wrapper) throws Exception {
int protocol = wrapper.user().get(ProtocolInfo.class).getProtocolVersion();
if (ViaVersion.getConfig().getBlockedProtocols().contains(protocol)) {
if (!wrapper.user().getChannel().isOpen()) return;
PacketWrapper disconnectPacket = new PacketWrapper(0x00, null, wrapper.user()); // Disconnect Packet
Protocol1_9TO1_8.FIX_JSON.write(disconnectPacket, ChatColor.translateAlternateColorCodes('&', ViaVersion.getConfig().getBlockedDisconnectMsg()));
wrapper.cancel(); // cancel current
// Send and close
ChannelFuture future = disconnectPacket.sendFuture(BaseProtocol.class);
future.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
wrapper.user().getChannel().close();
}
});
}
}
});
}
}); // Login Start Packet
registerIncoming(State.LOGIN, 0x01, 0x01); // Encryption Response Packet
}
@Override
public void init(UserConnection userConnection) {
// Nothing gets added, ProtocolPipeline handles ProtocolInfo
}
@Override
protected void registerListeners() {
final ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.removePortedClient(e.getPlayer().getUniqueId());
}
}, plugin);
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws Exception {
super.transform(direction, state, packetWrapper);
if (direction == Direction.INCOMING && state == State.HANDSHAKE) {
// Disable if it isn't a handshake packet.
if (packetWrapper.getId() > 0) {
packetWrapper.user().setActive(false);
}
}
}
}
@@ -0,0 +1,24 @@
package us.myles.ViaVersion.protocols.base;
import lombok.Getter;
import lombok.Setter;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.protocol.ProtocolPipeline;
import us.myles.ViaVersion.packets.State;
import java.util.UUID;
@Getter
@Setter
public class ProtocolInfo extends StoredObject {
private State state = State.HANDSHAKE;
private int protocolVersion = -1;
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
public ProtocolInfo(UserConnection user) {
super(user);
}
}
@@ -0,0 +1,163 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.storage.ResourcePackTracker;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types.Meta1_10Type;
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types.MetaList1_10Type;
import java.util.List;
public class Protocol1_10To1_9_3_4 extends Protocol {
public static final Type<List<Metadata>> METADATA_LIST = new MetaList1_10Type();
public static final Type<Metadata> METADATA = new Meta1_10Type();
public static ValueTransformer<Short, Float> toNewPitch = new ValueTransformer<Short, Float>(Type.FLOAT) {
@Override
public Float transform(PacketWrapper wrapper, Short inputValue) throws Exception {
return inputValue / 63.5F;
}
};
@Override
protected void registerPackets() {
// Named sound effect
registerOutgoing(State.PLAY, 0x19, 0x19, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // 0 - Sound name
map(Type.VAR_INT); // 1 - Sound Category
map(Type.INT); // 2 - x
map(Type.INT); // 3 - y
map(Type.INT); // 4 - z
map(Type.FLOAT); // 5 - Volume
map(Type.UNSIGNED_BYTE, toNewPitch); // 6 - Pitch
}
});
// Sound effect
registerOutgoing(State.PLAY, 0x46, 0x46, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Sound name
map(Type.VAR_INT); // 1 - Sound Category
map(Type.INT); // 2 - x
map(Type.INT); // 3 - y
map(Type.INT); // 4 - z
map(Type.FLOAT); // 5 - Volume
map(Type.UNSIGNED_BYTE, toNewPitch); // 6 - Pitch
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int id = wrapper.get(Type.VAR_INT, 0);
wrapper.set(Type.VAR_INT, 0, getNewSoundId(id));
}
});
}
});
// Metadata packet
registerOutgoing(State.PLAY, 0x39, 0x39, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(METADATA_LIST); // 1 - Metadata list
}
});
// Spawn Mob
registerOutgoing(State.PLAY, 0x03, 0x03, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity id
map(Type.UUID); // 1 - UUID
map(Type.UNSIGNED_BYTE); // 2 - Entity Type
map(Type.DOUBLE); // 3 - X
map(Type.DOUBLE); // 4 - Y
map(Type.DOUBLE); // 5 - Z
map(Type.BYTE); // 6 - Yaw
map(Type.BYTE); // 7 - Pitch
map(Type.BYTE); // 8 - Head Pitch
map(Type.SHORT); // 9 - Velocity X
map(Type.SHORT); // 10 - Velocity Y
map(Type.SHORT); // 11 - Velocity Z
map(METADATA_LIST); // 12 - Metadata
}
});
// Spawn Player
registerOutgoing(State.PLAY, 0x05, 0x05, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT); // 0 - Entity ID
map(Type.UUID); // 1 - Player UUID
map(Type.DOUBLE); // 2 - X
map(Type.DOUBLE); // 3 - Y
map(Type.DOUBLE); // 4 - Z
map(Type.BYTE); // 5 - Yaw
map(Type.BYTE); // 6 - Pitch
map(METADATA_LIST); // 7 - Metadata list
}
});
// Packet Send ResourcePack
registerOutgoing(State.PLAY, 0x32, 0x32, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.STRING); // 0 - URL
map(Type.STRING); // 1 - Hash
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ResourcePackTracker tracker = wrapper.user().get(ResourcePackTracker.class);
tracker.setLastHash(wrapper.get(Type.STRING, 1)); // Store the hash for resourcepack status
}
});
}
});
// Packet ResourcePack status
registerIncoming(State.PLAY, 0x16, 0x16, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ResourcePackTracker tracker = wrapper.user().get(ResourcePackTracker.class);
wrapper.write(Type.STRING, tracker.getLastHash());
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT));
}
});
}
});
}
public int getNewSoundId(int id) { //TODO Make it better, suggestions are welcome. It's ugly and hardcoded now.
int newId = id;
if (id >= 24) //Blame the enchantment table sound
newId += 1;
if (id >= 248) //Blame the husk
newId += 4;
if (id >= 296) //Blame the polar bear
newId += 6;
if (id >= 354) //Blame the stray
newId += 4;
if (id >= 372) //Blame the wither skeleton
newId += 4;
return newId;
}
@Override
public void init(UserConnection userConnection) {
userConnection.put(new ResourcePackTracker(userConnection));
}
}
@@ -0,0 +1,18 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.storage;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
@Getter
@Setter
@ToString
public class ResourcePackTracker extends StoredObject {
private String lastHash = "";
public ResourcePackTracker(UserConnection user) {
super(user);
}
}
@@ -0,0 +1,30 @@
package us.myles.ViaVersion.protocols.protocol1_10to1_9_3.types;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
import us.myles.ViaVersion.api.type.types.minecraft.MetaTypeTemplate;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.metadata.NewType;
public class Meta1_10Type extends MetaTypeTemplate {
@Override
public Metadata read(ByteBuf buffer) throws Exception {
short index = buffer.readUnsignedByte();
if (index == 0xff) return null; //End of metadata
NewType type = NewType.byId(buffer.readByte());
return new Metadata(index, type.getTypeID(), type.getType(), type.getType().read(buffer));
}
@Override
public void write(ByteBuf buffer, Metadata object) throws Exception {
if (object == null) {
buffer.writeByte(255);
} else {
buffer.writeByte(object.getId());
buffer.writeByte(object.getTypeID());
object.getType().write(buffer, object.getValue());
}
}
}
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff. Show more