Compare commits

..
302 Commits
Author SHA1 Message Date
retrooper 3400877958 Release of 2.4.0 2024-06-30 22:56:57 +02:00
retrooper b5a2c0ac29 Merge pull request #865 from cswhite2000/2.0
Switch EventManager listenerSet to CopyOnWriteArraySet
2024-06-30 09:40:48 +02:00
Christopher White c43dd6425a Switch EventManager listenerSet to CopyOnWriteArraySet 2024-06-29 23:21:34 -07:00
retrooper 8b643ae238 Small change of updater messages. 2024-06-30 08:07:55 +02:00
retrooper b07461340e Corrected latest chat type mappings to represent 1.20 2024-06-30 07:59:44 +02:00
retrooper 4cec8fe549 Correct the chat type mappings, temporarily assume that the 1.20.1 is referring to 1.20.2? 2024-06-30 07:56:23 +02:00
booky10 55ea059640 Merge pull request #864 from booky10/fix/sound-mappings-1.21
Fix missing/renamed sounds for 1.21
2024-06-30 05:09:09 +02:00
booky10 5429e80f66 Fix missing/renamed sounds for 1.21 2024-06-30 04:01:06 +02:00
retrooper 16c71fad14 Start working on WolfVariant in EntityMetadata packet. 2024-06-29 00:47:38 +02:00
retrooper d27a97c322 Bump version up to 2.4.0-SNAPSHOT! 2024-06-29 00:46:38 +02:00
retrooper 59ccfa89db Trying out PacketWrapper#writeVarInt optimization, written by Steinborn. 2024-06-29 00:26:55 +02:00
retrooper b6f25939be Correction in javadocs for SpigotConversionUtil 2024-06-29 00:10:23 +02:00
retrooper d19406edf4 Merge pull request #858 from Bram1903/chore/BlockDataMethodCleanup
Removed: Unnecessary 1.7.10 version check and improved JavaDocs
2024-06-29 00:04:29 +02:00
retrooper 0702ff3716 Merge pull request #852 from AbhigyaKrishna/2.0
feat: chat type mappings update
2024-06-29 00:00:22 +02:00
Bram 4577c5a948 Removed: Unnecessary 1.7.10 version check
Added: JavaDocs to SpigotConversionUtil#fromBukkitMaterialData and #toBukkitMaterialData
2024-06-28 16:32:11 +02:00
booky10 d073f716f9 Merge pull request #855 from dawon/bugfix/fix-new-potion-effects
Fix new potion effects
2024-06-27 23:56:06 +02:00
Jakub Zacek 5051c31bea Fix new potion effects 2024-06-27 23:48:47 +02:00
retrooper eb2d91745a Merge pull request #854 from booky10/fix/bungee-compat
Fix more bungee compatibility issues
2024-06-27 21:46:47 +02:00
booky10 9d40bc506e Disable debug mode in bungee plugin 2024-06-27 21:07:29 +02:00
booky10 0fa3be1ce2 Extract channel from player on login
Caused by login success being sent after the PostLoginEvent gets fired
2024-06-27 20:59:28 +02:00
booky10 acecdb57f4 Skip bungeecord query handler handler 2024-06-27 20:58:00 +02:00
retrooper 33d3a72d4d Fix bungee packetevents injector. 2024-06-27 20:18:10 +02:00
retrooper 2ce1fc1e79 Final change to this all 2024-06-27 19:08:56 +02:00
retrooper 63b9e27ebf More changes to the bungee pipeline injector? 2024-06-27 19:04:55 +02:00
retrooper 8093481f08 Last changes to the bungee pipeline injector? 2024-06-27 18:51:35 +02:00
AbhigyaKrishna 629871fcb5 fix: type naming 2024-06-27 21:15:13 +05:30
AbhigyaKrishna d2d9135c45 feat: chat type mappings update 2024-06-27 21:11:15 +05:30
retrooper bf5c3e90b3 Merge pull request #851 from booky10/fix/dimtype-reg-via-velocity
Fix errors on velocity when via is installed on backends
2024-06-27 16:58:01 +02:00
retrooper d100fd54de Delete .travis.yml
We no longer use Travis CI. The Jenkins builds are working again,
2024-06-27 16:54:46 +02:00
retrooper c8137956f1 Merge pull request #799 from booky10/fix/recipe-packet
Fix recipe packet writing/reading
2024-06-27 16:53:18 +02:00
booky10 e7739bac22 Remove MCProtocolLib credits from rewritten classes 2024-06-27 16:36:15 +02:00
booky10 5c9cfcb25d Merge branch '2.0' into fix/recipe-packet 2024-06-27 16:35:08 +02:00
booky10 4364d7a646 Check for dimension type registry name without vanilla namespace 2024-06-27 16:31:57 +02:00
booky10 0ba826f578 Work around missing dimension type registry causing join errors 2024-06-27 16:29:57 +02:00
retrooper 3e21b7d384 Merge pull request #836 from ManInMyVan/#693
Add #693
2024-06-27 16:10:20 +02:00
retrooper b934d7361d Merge pull request #844 from booky10/fix/item-hover-component
Support reading components in show_item hover events
2024-06-27 15:55:59 +02:00
retrooper 4047bafc6a Merge pull request #848 from booky10/feat/itemstack-builder-utils
Add utility methods to ItemStack builder
2024-06-24 17:13:36 +02:00
booky10 27de28476e Add utility methods to ItemStack builder
Allows easier modification of components and nbt data
2024-06-24 15:01:29 +02:00
retrooper e5dfb84a53 Update DefaultNBTSerializer 2024-06-24 00:15:50 +02:00
retrooper 2b3c77b6b8 Update DefaultNBTSerializer to have limit for LONG_ARRAY 2024-06-24 00:15:31 +02:00
retrooper 16294ccc63 Merge pull request #847 from Bram1903/fix/NettyVersionResolving
Fix: Netty Version not being properly converted to an PEVersion
2024-06-23 23:56:50 +02:00
Bram 9ae2cd0538 Fix: Netty Version not being properly converted to an PEVersion due to not using semantic versioning 2024-06-23 23:44:13 +02:00
retrooper 30492a7b1f chores: Format the multi-line comment appropriately and clarify references to the generated PEVersions class 2024-06-23 12:10:29 +02:00
AbhigyaKrishna ecce9d8c4e chores: Added default comment to the generated file 2024-06-23 15:32:23 +05:30
AbhigyaKrishna 448f7be2ae fix: include generateVersionFile task in task graph 2024-06-23 02:49:43 +05:30
Abhigya 4e3c45bc7e Merge pull request #819 from Bram1903/feat/GlobalVersioning
Feat: Global Versioning
2024-06-23 00:46:09 +05:30
AbhigyaKrishna ccd3fd4b7b chores: following some code design 2024-06-22 21:35:47 +05:30
Bram 5003d578c7 Fix: toArray being mend to be called asArray 2024-06-22 17:18:15 +02:00
AbhigyaKrishna 881a66740f feat: Removed version resolution from manifest 2024-06-22 15:39:14 +05:30
AbhigyaKrishna c9ae98814c Merge branch 'feat/GlobalVersioning' of github.com:Bram1903/packetevents into Bram1903-feat/GlobalVersioning 2024-06-22 15:36:43 +05:30
AbhigyaKrishna ee2c562c9e feat: PEVersions file generation 2024-06-22 15:33:36 +05:30
Bram cbd38ea581 Reverted: Previously changed comparison method 2024-06-22 11:08:08 +02:00
Bram 983b27d071 Added: Old toArray method and marked it as deprecated to prevent breaking changes 2024-06-22 10:39:03 +02:00
Bram b2a827e97b Merge branch 'refs/heads/2.0' into feat/GlobalVersioning 2024-06-22 10:31:59 +02:00
AbhigyaKrishna a709e5f746 fix: Velocity adventure issue 2024-06-22 08:13:58 +05:30
AbhigyaKrishna c1f482dd35 feat: MappingCompression task to depend on mapping files 2024-06-22 08:13:31 +05:30
booky10 269ca7ad43 Fix support for adventure <4.17.0 2024-06-22 04:17:14 +02:00
booky10 594f8cdc6e Fix show item hover support for <1.20.3 2024-06-22 04:14:20 +02:00
booky10 187041bd47 Support reading components in show_item hover events 2024-06-22 03:29:55 +02:00
booky10 68eb537b66 Merge pull request #843 from booky10/fix/light-data-1.17-newer
Fix light data throwing errors for 1.17+
2024-06-22 01:56:48 +02:00
AbhigyaKrishna b18884a5b3 fix: manifest implementation version should be in shadowJar 2024-06-22 05:19:26 +05:30
AbhigyaKrishna 061d1c25e8 fix: Version comparison 2024-06-22 04:57:11 +05:30
booky10 b1a881bfab Fix light data throwing errors for 1.17+ 2024-06-22 01:26:48 +02:00
AbhigyaKrishna e68dbe4155 chore: method design pattern 2024-06-22 04:56:39 +05:30
AbhigyaKrishna 60e33ef0a3 Merge branch 'feat/GlobalVersioning' of github.com:Bram1903/packetevents into Bram1903-feat/GlobalVersioning 2024-06-22 04:12:04 +05:30
AbhigyaKrishna eac72687cb Merge branch 'Bram1903-feat/NativeFoliaSupport' into 2.0-pe 2024-06-22 04:03:03 +05:30
AbhigyaKrishna d205d83f61 chore: Property access from a global final isFolia variable
Accessing a global final variable is better than having a copy in every class
2024-06-22 04:01:03 +05:30
AbhigyaKrishna 61969bf9df chore: Work directory set to rootDir
Work directory now set to rootDir for easier navigation
2024-06-22 03:45:46 +05:30
AbhigyaKrishna ed9ff0c5da Merge branch 'feat/NativeFoliaSupport' of https://github.com/Bram1903/packetevents into Bram1903-feat/NativeFoliaSupport 2024-06-22 03:34:20 +05:30
Abhigya 371ec62571 Merge pull request #842 from AbhigyaKrishna/2.0
fix: Fixes NoSuchMethodError for GsonComponentSerializer on version 1.16.5
2024-06-22 03:19:31 +05:30
AbhigyaKrishna 4eaf000dea fix: Fixes NoSuchMethodError for GsonComponentSerializer on version 1.16.5
Fixed NoSuchMethodError in GsonComponentSerializer due to older version of abstract classes in version 1.16.5

Ticket: #832
2024-06-22 03:16:19 +05:30
retrooper 97ca0007fc Merge remote-tracking branch 'origin/2.0' into 2.0 2024-06-21 22:44:45 +02:00
retrooper ece10d66c1 Undo something I removed, sorry! 2024-06-21 22:44:30 +02:00
booky10 420a658b0a Merge pull request #835 from libraryaddict/2.0
Divide Color's int rgb by 255 when using for particle colors
2024-06-21 22:21:53 +02:00
booky10 48fbb745e0 Merge branch '2.0' into fix/recipe-packet 2024-06-21 22:15:42 +02:00
retrooper 24d78ef500 Merge remote-tracking branch 'origin/2.0' into 2.0 2024-06-21 22:13:05 +02:00
retrooper a1123cdf67 Fix Paper entity lookup code. 2024-06-21 22:12:33 +02:00
booky10 cd3d3b17b1 Merge pull request #760 from ShadowOfHeaven-Me/WrapperPlayServerMapData-added
Added the missing WrapperPlayServerMapData
2024-06-21 21:44:37 +02:00
booky10 1f2de8a566 Merge pull request #715 from LeeGodSRC/feat/update-light
Added WrapperPlayServerUpdateLight and LightData
2024-06-21 21:32:25 +02:00
LeeGodSRC 74f4d65356 fix: Suggested changes for chunk packets by booky10 2024-06-22 03:16:11 +08:00
retrooper 2be8692661 Fix attempt for conversion between packetevents and bukkit itemstacks. 2024-06-21 13:32:43 +02:00
retrooper 5021eebd55 Final fix for bungee injector. 2024-06-21 13:26:37 +02:00
retrooper 124e59ba70 Another fix for bungee injector. 2024-06-21 13:12:42 +02:00
retrooper 301adfb28f Fix paper entity lookup 2024-06-21 13:06:38 +02:00
retrooper 8d80725cf7 Does this fix the Bungee injector 2024-06-21 12:56:16 +02:00
retrooper 7aa429988b Fixing the BungeeCord injector and the server version detection. (Part 2) 2024-06-21 12:40:43 +02:00
Bram 1cbd15e797 Merge branch 'refs/heads/2.0' into feat/NativeFoliaSupport 2024-06-21 09:11:09 +02:00
Bram 802c707902 Merge branch 'refs/heads/2.0' into feat/GlobalVersioning 2024-06-21 09:10:36 +02:00
retrooper 17d5e3f711 Merge remote-tracking branch 'origin/2.0' into 2.0 2024-06-21 08:05:39 +02:00
retrooper 7a342a8c21 Fixing the BungeeCord injector and the server version detection. 2024-06-21 08:04:53 +02:00
retrooper a4e9a2cd06 Merge pull request #800 from booky10/fix/various-packet-wrapper-issues
Fix various packet wrapper issues
2024-06-21 00:13:02 +02:00
retrooper 4abc4a7403 Update copy method argument in WrapperPlayServerWorldBorderWarningReach (compilation error) 2024-06-21 00:12:38 +02:00
retrooper 2a364895cf Correct constructor naming 2024-06-21 00:07:52 +02:00
retrooper ff56e81136 Correct packet wrapper naming 2024-06-21 00:06:08 +02:00
retrooper 371fcff23a Merge pull request #691 from ytnoos/reencode-fix
Fix re-encode for new bytebufs that were bigger than the received one
2024-06-21 00:03:05 +02:00
retrooper abe5b9f75a Merge pull request #827 from dawon/bugfix/fix-missing-body-slot
Fix missing Equipment body slot
2024-06-20 23:53:44 +02:00
retrooper 2c36b5ad9f Variable naming issue fixed 2024-06-20 23:48:42 +02:00
retrooper 0436b8cb6a Account for client version in EquipmentSlot enum. 2024-06-20 23:42:24 +02:00
retrooper 2fb967ec04 Merge pull request #732 from booky10/dev/1.21
1.21 update
2024-06-20 21:39:43 +02:00
retrooper aafc45d70d No more chat type handling 2024-06-20 21:32:12 +02:00
retrooper 3e25fa641c Minor documentation error fixed 2024-06-18 22:07:15 +02:00
retrooper 3f3c6ba2e1 * ChatMessage_v1_19_1.ChatTypeBoundNetwork relocated to ChatType.Bound (with backwards compatibility)
* ChatMessage_v1_19_1#getChatType -> getChatFormatting
* ChatMessage_v1_19_3#getChatType -> getChatFormatting
* WrapperPlayServerDisguisedChat#getChatType -> getChatFormatting
* ChatTypeDecoration#decorate implemented
2024-06-18 22:03:14 +02:00
retrooper 3175ef85c4 Merge pull request #690 from ytnoos/nbt-limiter
Added NBT, ByteBuf, MessageSignature read limits
2024-06-17 21:40:28 +02:00
retrooper e876b1c95f First effort to network-synchronized registries regarding chat, but please look at TODOs. 2024-06-17 07:09:52 +02:00
retrooper be2094a21f Merge pull request #828 from booky10/fix/block-entity-data-wrapper
Fix block entity data packet wrapper
2024-06-17 06:42:23 +02:00
ManInMyVan 95fdebfb08 Add #693 2024-06-16 12:29:56 -06:00
booky10 5662c7ea9c Fix name of attribute modifiers being unused 2024-06-16 19:48:12 +02:00
booky10 b8587a2487 Support reading int/byte/long arrays when parsing translation arguments
Fixes #801
2024-06-16 16:40:39 +02:00
booky10 1411fd09b9 Fix invalid WrappedBlockState mappings index 2024-06-16 16:28:08 +02:00
booky10 604a48e2dd Make mappings compression task depend on changes to mappings input files 2024-06-16 16:24:14 +02:00
booky10 f091ab69bc Support reading string translation arguments 2024-06-16 16:23:42 +02:00
libraryaddict e22db21f72 Fix particledata color, divide Color's rgb's int by 255f 2024-06-16 21:27:10 +12:00
retrooper 24000fd837 Merge branch '2.0' into dev/1.21 2024-06-16 08:38:32 +02:00
retrooper 87275458bb Merge pull request #833 from booky10/fix/velocity-version-detection
Fix velocity version failing to be detected
2024-06-16 06:58:18 +02:00
booky10 faac9cd83d Mirror changes by user in name/uuid attribute modifier on change 2024-06-16 05:22:03 +02:00
booky10 887a9fa959 Look up private constructor of ResourceLocation
Was made private in 1.21, should normally now be constructed using static methods
2024-06-16 05:07:07 +02:00
booky10 d716309e34 Fix initialization of PacketType 2024-06-16 05:05:05 +02:00
booky10 92603a382b Update attribute modifier encoding
Not perfect but idk how to solve this any better
2024-06-16 04:54:48 +02:00
booky10 88c788f654 Update item type mappings
Something was re-ordered in 24w19a
2024-06-16 04:46:24 +02:00
booky10 bdb429a0b4 Implement jukebox_playable component added with 24w21a 2024-06-16 04:45:21 +02:00
booky10 0c2e09bc7b Implement wrappers for new packets added in 24w21a
Adds wrappers for minecraft:custom_report_details and minecraft:server_links
2024-06-16 04:32:16 +02:00
booky10 131a66220f Add new packet types added with 24w21a 2024-06-16 04:19:16 +02:00
booky10 7e2c6726e0 Merge branch '2.0' into dev/1.21 2024-06-16 04:09:17 +02:00
LeeGodSRC 882e7b57f9 fix: Suggested changes for chunk packets 2024-06-15 21:00:54 +08:00
LeeGodSRC 990a19585f Merge remote-tracking branch 'upstream/2.0' into feat/update-light 2024-06-15 21:00:49 +08:00
booky10 c2f7c92ed4 Fix ClientVersion#getById 2024-06-15 02:32:30 +02:00
booky10 08b420ffb4 Reverse iteration over velocity ProtocolVersion
Newest get chosen first now
2024-06-15 02:32:17 +02:00
booky10 d9a821e44e Fix velocity version failing to detect 2024-06-14 14:17:16 +02:00
Bram 2e39d61d53 Bumped Travis to use Java 21 2024-06-12 09:37:15 +02:00
Bram bb9bd91970 Fixed: Merge Conflicts 2024-06-12 09:30:25 +02:00
Bram a789d9ec83 Merge branch 'refs/heads/2.0' into feat/NativeFoliaSupport
# Conflicts:
#	velocity/build.gradle.kts
2024-06-12 09:28:28 +02:00
Bram 524cf896c3 Fixed: Breaking change by removing an old constructor method.
The method has been marked as deprecated.
2024-06-12 09:25:34 +02:00
Bram 5190d71bc8 Merge branch 'refs/heads/2.0' into feat/GlobalVersioning 2024-06-12 09:20:31 +02:00
retrooper 29dd1cdc03 Merge pull request #794 from IssueDescription/2.0
Add mojang mapped jar support
2024-06-11 23:04:24 +02:00
retrooper cbe517e994 Add shadowJar block 2024-06-11 22:56:54 +02:00
retrooper 5175ed9733 Add mojang mapping attribute 2024-06-11 22:54:43 +02:00
retrooper 26ddcfde2a Merge branch '2.0' of https://github.com/retrooper/packetevents into fork/IssueDescription/2.0 2024-06-11 22:39:35 +02:00
retrooper e17eb7987f Undo BuildScript changes to fix conflict 2024-06-11 22:38:35 +02:00
retrooper 6cc2ac81df Fix package location of ServerPlayer on remapped paper 2024-06-11 19:20:32 +02:00
AbhigyaKrishna 12d16144c0 Merge remote-tracking branch 'pe/2.0' into 2.0-pe 2024-06-11 02:29:38 +05:30
AbhigyaKrishna f4fc034d51 Publication group and artifact naming changes 2024-06-11 02:27:16 +05:30
booky10 42c6df480c Fix block entity data packet wrapper 2024-06-10 20:38:20 +02:00
Jakub Zacek c591335dd4 Fix missing Equipment body slot 2024-06-10 20:34:58 +02:00
retrooper d93e01cca1 Update .travis.yml JDK to 17 2024-06-10 20:30:16 +02:00
retrooper b666a1a62c Update .travis.yml to JDK 17 2024-06-10 20:25:28 +02:00
retrooper fcdac76ba9 Rename travis.yml to .travis.yml 2024-06-10 18:19:58 +02:00
retrooper 3e33d38518 Create travis.yml 2024-06-10 18:17:52 +02:00
AbhigyaKrishna 46dd4be1d3 Publishing group and module group are now consistent
We are probably going to change the naming convention of module and group in future update but this should work for now
2024-06-10 20:14:16 +05:30
AbhigyaKrishna 809517f04b Maven publication pom now have better structure 2024-06-10 03:42:17 +05:30
AbhigyaKrishna f53e73ed9a Try fixing publication dependency resolution 2024-06-09 23:54:47 +05:30
AbhigyaKrishna a7e1852af4 compileOnly -> shadow configuration for pom generation 2024-06-09 01:54:20 +05:30
AbhigyaKrishna 95ac9d8633 Maven Publication for shadow configuration 2024-06-09 01:45:14 +05:30
Bram 8034de3ec3 Improved: JavaDocs 2024-06-08 10:36:28 +02:00
Bram ee6aac43d4 Added: Overload constructor which defaults snapshot to false
Removed: Cloneable interface, and simply returns a new object with the same values.
Added: Getters for the class field (Using Java record naming conventions)
2024-06-08 10:28:14 +02:00
Bram 2fef51f3d4 Rewrote: PEVersion, now implementing Comparable and Cloneable 2024-06-08 00:24:44 +02:00
Bram f27ead56c7 Fixed: Making Gradle not use the cache when just changing version number. 2024-06-07 19:26:35 +02:00
Bram 317853fd8e Merge branch 'refs/heads/2.0' into feat/GlobalVersioning 2024-06-07 19:14:15 +02:00
Bram db8a4184f8 Merge branch 'refs/heads/2.0' into feat/NativeFoliaSupport
# Conflicts:
#	spigot/build.gradle.kts
2024-06-07 19:12:43 +02:00
AbhigyaKrishna 95543c7033 Publication for patch module 2024-06-07 05:03:38 +05:30
AbhigyaKrishna 306f1598e0 Publish configuration 2024-06-07 04:30:02 +05:30
Abhigya 5a26451880 Merge pull request #823 from AbhigyaKrishna/2.0
Publication consistency
2024-06-07 02:50:21 +05:30
AbhigyaKrishna 9384187f06 Publication consistency for with older convention 2024-06-07 02:49:32 +05:30
AbhigyaKrishna 5e414bae58 Revert "Patch for group name"
This reverts commit 990dfc279f.
2024-06-07 02:48:55 +05:30
retrooper 990dfc279f Patch for group name 2024-06-06 23:06:13 +02:00
retrooper 27ec0876cb Publishing patch 2024-06-06 22:50:58 +02:00
retrooper c3853614c5 Merge pull request #814 from AbhigyaKrishna/library-patching
Library patching
2024-06-06 22:36:05 +02:00
_ShadowOfHeaven_ 67855eda8a Merge pull request #1 from booky10/fix/map-data-wrapper-version-compat
Fix map data wrapper version compat
2024-06-06 21:57:17 +02:00
retrooper e2ed78e81d Merge pull request #822 from RaynLegends/2.0
Fix: WrapperPlayServerChunkDataBulk not initialized correctly when instantiated multiple times
2024-06-06 18:26:15 +02:00
RaynLegends 80ae2de524 Fix: WrapperPlayServerChunkDataBulk not initialized correctly when instantiated multiple times
Fixes retrooper/packetevents#821
2024-06-06 11:46:52 +02:00
AbhigyaKrishna af3c440526 Backward compatibility to AdventureNBTSerializer 2024-06-06 10:23:42 +05:30
Bram 8bac60f2db Fixed: Documentation typo 2024-06-05 11:17:48 +02:00
Bram 6d0d1df82e Improved: Version parsing from the manifest implementation by moving it to its own method 2024-06-05 11:10:13 +02:00
Bram 44eba1fbdc Fixed: Unit Tests failing because of not being able to access the jar manifest. 2024-06-05 09:44:10 +02:00
Bram 7ca7eaa346 Improved: Global Versioning 2024-06-05 09:30:44 +02:00
ytnoos fb93dde220 upstream 2024-06-04 21:11:18 +02:00
Abhigya 4f9f58f76f Merge pull request #811 from joshuaprince/fix-map-decoration
Fix map ItemStack decoding for rotation key
2024-06-04 21:00:20 +05:30
Abhigya 4c2ee57f8d Merge pull request #817 from booky10/fix/joingame-respawn-screen
Fix enforcesSecureChat not being read on login packet
2024-06-04 20:52:46 +05:30
booky10 345db7d3f6 Add recipe serializer mappings compression to new mappings task 2024-06-04 14:25:12 +02:00
booky10 ab6df11274 Merge branch '2.0' into fix/recipe-packet 2024-06-04 14:21:45 +02:00
booky10 71ed30e2a9 Fix enforcesSecureChat not being read 2024-06-04 08:53:18 +02:00
AbhigyaKrishna cbfccafce7 Unregister debug listener 2024-06-03 22:57:43 +05:30
AbhigyaKrishna 2888afa999 Update to publish configuration which now includes shadow publication 2024-06-03 22:56:57 +05:30
AbhigyaKrishna 9c4d7cb933 Adventure version resolution fix 2024-06-03 22:54:25 +05:30
AbhigyaKrishna baec4bdd00 MIT Licence 2024-06-03 22:54:11 +05:30
AbhigyaKrishna a00ae8f52c Fix argument parsing 2024-06-03 21:31:36 +05:30
AbhigyaKrishna b973820c59 Shadow configuration for patch module and gson inclusion 2024-06-03 21:08:42 +05:30
AbhigyaKrishna 5fbf597d20 Removed gradle daemon as we are having issues 2024-06-03 21:08:04 +05:30
AbhigyaKrishna cc8bb89ee5 TranslatableArgument patch 2024-06-03 20:51:27 +05:30
AbhigyaKrishna 1f0792f854 Patch ShowItemSerializer and ShowEntitySerializer 2024-06-03 19:51:58 +05:30
AbhigyaKrishna 6b7ebc2162 adventure version bump 2024-06-03 19:47:47 +05:30
AbhigyaKrishna 596a961b05 Rollback constants 2024-06-03 19:30:52 +05:30
AbhigyaKrishna 9586d8da29 Changed adventure library configuration 2024-06-03 17:33:42 +05:30
AbhigyaKrishna 8391c11a7f Parent build function to not include patch module jar 2024-06-03 17:32:52 +05:30
AbhigyaKrishna 51de60ab4f Gradle script update 2024-06-03 16:46:48 +05:30
AbhigyaKrishna 5d14703492 Removed legacy fixes and updated Adventure serializer 2024-06-03 16:46:20 +05:30
AbhigyaKrishna d3968b15db Grouping change for adventure bundle 2024-06-03 16:45:48 +05:30
AbhigyaKrishna 990cc38b67 Configuration update to conventions 2024-06-03 16:45:12 +05:30
AbhigyaKrishna df0d135a72 GsonComponentSerializer patch 2024-06-03 15:57:14 +05:30
AbhigyaKrishna 18ac4937b6 Adventure gson serializer patches 2024-06-03 15:35:43 +05:30
AbhigyaKrishna 531cef241a Configuration changes 2024-06-03 15:35:18 +05:30
AbhigyaKrishna e5353800e7 Patch module 2024-06-03 15:34:52 +05:30
AbhigyaKrishna 21dfaceb22 Custom library patching toolchain 2024-06-03 15:29:31 +05:30
Bram c13454a255 Removed: Unnecessary null initializer 2024-06-02 19:21:50 +02:00
Bram 4f4f5f0578 Fixed: Build workflow by updating the Java version 2024-06-02 14:38:44 +02:00
Bram 02544cd0f5 Greatly improved the TaskWrapper readability 2024-06-02 13:38:27 +02:00
Bram 3ed09a3834 Updated the old FoliaCompatUtil to prevent reflections where possible 2024-06-02 13:08:56 +02:00
Bram 28749faa44 Renamed: serverInitEventClass -> regionizedServerInitEventClass
Added: Documentation on why we are still using reflections for the runTaskOnInit method
2024-06-02 12:59:17 +02:00
Bram 2dbbdcc5d4 Reworked: RegionScheduler to use the RegionScheduler class provided by Paper instead of using reflections 2024-06-02 12:56:44 +02:00
Bram 291266c4bd Reworked: GlobalRegionScheduler to use the GlobalRegionScheduler class provided by Paper instead of using reflections 2024-06-02 12:54:14 +02:00
Bram 411693611a Reworked: EntityScheduler to use the EntityScheduler class provided by Paper instead of using reflections
Fixed: Some AsyncScheduler methods still re-getting the bukkit scheduler
2024-06-02 12:51:17 +02:00
Bram 98c36ff39f Reworked: AsyncScheduler to use the AsyncScheduler class provided by Paper instead of using reflections 2024-06-02 12:42:04 +02:00
Bram 1e69b33d4f Reworked: TaskWrapper to use the ScheduledTask instead of using reflections to access the class 2024-06-02 12:38:58 +02:00
Bram ae2c6cdc4b Added: Proper run paper/velocity tasks 2024-06-02 12:08:14 +02:00
Bram 3104885fb5 Switched: spigot-api -> paper-api 2024-06-02 11:59:13 +02:00
Joshua Prince 081868571b Fix map ItemStack decoding for rotation key
Fixes https://github.com/retrooper/packetevents/issues/810
2024-06-01 16:50:53 -07:00
retrooper 6a3b18aff9 Merge pull request #807 from Bram1903/patch/bStatsSettingRemoval
Removed: bStats Configuration Option
2024-06-01 23:53:03 +02:00
Bram c4caba57bd Merge branch 'refs/heads/2.0' into patch/bStatsSettingRemoval 2024-06-01 23:35:22 +02:00
retrooper c9f3eb32aa Merge pull request #790 from AbhigyaKrishna/gradle-kotlin-dsl
Gradle build chain rework with kotlin DSL
2024-06-01 23:32:55 +02:00
Bram 1dad678f98 Fixed: Saying "removing" instead of "disabling" 2024-05-31 14:11:05 +02:00
Bram 4f579f59c3 Removed: bStats Setting
Deprecated: PacketEventsSettings#bStats
Deprecated: PacketEventsSettings#isbStatsEnabled
2024-05-31 11:56:53 +02:00
retrooper 3352f25ede Merge pull request #805 from booky10/fix/legacy-attributes
Add legacy names for armor and luck attribute
2024-05-30 19:51:06 +02:00
booky10 0378141ac4 Add legacy names for armor and luck attribute
Wasn't present previously because mojang doesn't include them in their attribute rename patch
2024-05-30 17:35:24 +02:00
booky10 9dbb877455 Fix sound effect packet wrapper for <1.9 2024-05-29 20:37:43 +02:00
booky10 ee382176c5 Add missing ResourceLocation#hashCode 2024-05-29 20:27:29 +02:00
booky10 b3d9c1f10f Refactor tags packet wrapper
Also fix support for versions <1.17
2024-05-29 20:25:00 +02:00
booky10 a51ce059e5 Fix serialization of command argument nodes for <1.19 2024-05-29 19:03:53 +02:00
booky10 126bf406ce Add legacy command argument type parsers 2024-05-29 19:02:54 +02:00
booky10 b476d51910 Fix missing seed in entity sound packet wrapper 2024-05-29 17:56:52 +02:00
booky10 02e9f7f901 Fix nbt_compound_tag command argument parser 2024-05-28 22:02:34 +02:00
booky10 0cf411c4de Fix recipe packet writing/reading 2024-05-28 21:17:21 +02:00
booky10 37e2317a5f Fix various packet wrapper constructors 2024-05-28 17:12:22 +02:00
retrooper 415550b354 Merge pull request #789 from MemencioPerez/2.0
Clear the buffer if the packet send event is canceled in Velocity and BungeeCord
2024-05-26 19:14:36 +02:00
retrooper 3b5099f940 NPC#changeSkin properly re-spawns the player on all versions 2024-05-26 14:58:12 +02:00
retrooper 34605f3d72 Fix NPC#despawnAll not emptying channels after usage. 2024-05-26 13:17:39 +02:00
retrooper 8e5eab8bd1 Addition to ByteBufAllocationOperator, new methods declared for further control 2024-05-25 18:19:15 +02:00
retrooper 8cde415712 Merge pull request #795 from booky10/fix/item-stack-enchantments
Fix more issues with ItemStack enchantment components
2024-05-21 20:19:20 +02:00
booky10 c5189d2788 Fix more issues with ItemStack enchantment components
Caused by every item having a default enchantments component set with an empty value
2024-05-21 20:18:01 +02:00
issueDescription e550924b38 Add mojang mapped jar support 2024-05-21 16:48:51 +01:00
AbhigyaKrishna 27d7495f65 Spigot version bump 2024-05-21 01:22:01 +05:30
AbhigyaKrishna 7477483f15 Merge branch 'refs/heads/2.0' into gradle-kotlin-dsl
# Conflicts:
#	spigot/build.gradle
2024-05-21 01:03:25 +05:30
Abhigya 7e064f453a Merge pull request #783 from booky10/fix/spigot-particle-conversions
Fix spigot particle conversion
2024-05-21 00:56:33 +05:30
AbhigyaKrishna f7e58de863 Revert "Update gradle publish github workflow"
This reverts commit 7a135fe0f5.
2024-05-21 00:15:25 +05:30
AbhigyaKrishna 7a135fe0f5 Update gradle publish github workflow
Removed unwanted github workflow tasks
2024-05-21 00:05:24 +05:30
AbhigyaKrishna 8336f1c0ab Misc changes 2024-05-21 00:01:23 +05:30
AbhigyaKrishna 2648f8538d Mapping compression task in kotlin
Rewritten mapping compression task in kotlin
2024-05-20 22:36:26 +05:30
AbhigyaKrishna 6893216ed2 Root project build cycle and javadoc
* Added global jar out task to root project build
* Configured javadoc task to not log warnings
2024-05-19 14:41:22 +05:30
AbhigyaKrishna b6f944ad57 Minor changes and patches to build cycle 2024-05-19 14:25:01 +05:30
Memencio 969f032607 Clear the buffer if the packet send event is canceled in BungeeCord 2024-05-18 19:26:12 -04:00
AbhigyaKrishna daed712bf7 Version catalog for buildSrc
Included version catalog for buildSrc
2024-05-19 04:21:38 +05:30
AbhigyaKrishna 835952a7d2 Gradle convention plugin
Using gradle convention plugin strategy for managing common gradle scripts
2024-05-19 04:08:45 +05:30
AbhigyaKrishna d272f8d19e Gradle kotlin dsl migration
Migrated gradle dsl from groovy to kotlin for easier maintainability
2024-05-19 03:35:45 +05:30
Memencio 8cb8ad7388 Clear the buffer if the packet send event is canceled in Velocity 2024-05-18 16:47:59 -04:00
AbhigyaKrishna 6793fcb928 Versions Catalog
Implementation of version catalog for managing dependencies
2024-05-19 00:18:14 +05:30
retrooper 86ea24d3ee Merge pull request #787 from booky10/fix/paper-1.8-cache-chunk-maps-unload-packet
Account for zero chunk data on unload chunk packet
2024-05-18 18:36:30 +02:00
booky10 69f9c4f500 Account for zero chunk data on unload chunk packet
Sent by Paper 1.8 if cache-chunk-maps is enabled in the paper config
2024-05-18 16:58:59 +02:00
retrooper 3d869f7dfc Merge pull request #786 from booky10/feat/spawn-entity-head-yaw
Add head-yaw methods to spawn entity packet wrapper
2024-05-18 15:35:39 +02:00
booky10 92d716d78e Add head-yaw methods to spawn entity packet wrapper 2024-05-18 15:22:21 +02:00
booky10 8a7f086fdf Fix spigot particle conversion
Fixes #775
2024-05-17 15:23:10 +02:00
booky10 58f6a77287 Implement direct chattype reading 2024-05-15 17:31:46 +02:00
booky10 04110da6f1 Implement direct painting variant reading 2024-05-15 16:54:09 +02:00
booky10 81e5a50b8a Add yaw and pitch to use item packet wrapper 2024-05-15 16:41:05 +02:00
retrooper dfb6787322 Merge pull request #774 from booky10/fix/various-invalid-registry-id-getters
Fix invalid registry ids in various registries
2024-05-12 19:25:55 +02:00
booky10 6b03502307 Fix invalid registry id getting in various registries 2024-05-12 19:22:40 +02:00
retrooper 499d2eea6c Fix CRAFT_PARTICLE_PARTICLES_FIELD being null exception 2024-05-12 17:02:50 +02:00
retrooper fdf1942553 Merge pull request #761 from MemencioPerez/2.0
Fix Encryption Response packet wrapper write method for server versions between 1.19 and 1.19.2
2024-05-11 18:33:03 +02:00
retrooper 9e9b9abb14 Merge pull request #769 from booky10/fix/decorated-pots
Fix pot decorations causing NPEs
2024-05-11 17:08:44 +02:00
booky10 92397b2e4a Fix pot decorations causing NPEs
Now wraps null values in optionals
2024-05-11 17:01:30 +02:00
retrooper 154b789b48 Merge pull request #767 from ItzZisker/2.0
fixed entity id assign bug
2024-05-11 12:01:31 +02:00
ItzZisker 0cee676a69 fixed entity id assign bug 2024-05-11 05:36:35 +03:30
retrooper 387f4661e8 Add ICHUNKPROVIDER class to spigot reflection util 2024-05-10 23:42:37 +02:00
retrooper c261e1d467 Addition of some methods and classes and constructors in SpigotReflectionUtil 2024-05-10 22:32:21 +02:00
booky10 b43a76e682 Initial logic for reading CustomData from non-compounds, WIP 2024-05-10 18:15:49 +02:00
booky10 6efb6f59b1 Update FoodProperties for 1.21 2024-05-10 18:10:26 +02:00
booky10 9f27a49358 Fix flag parsing for <1.13 2024-05-10 18:00:09 +02:00
booky10 5001b21e7a Fix version compatibility for map data packet wrapper 2024-05-10 18:00:07 +02:00
retrooper 24446c61e0 Merge pull request #765 from booky10/fix/particle-mappings-typo
Fix typo in particle mappings for 1.12.2
2024-05-10 17:35:52 +02:00
booky10 d03ed48409 Fix typo in particle mappings for 1.12.2 2024-05-10 17:28:01 +02:00
booky10 840153bc98 Update WrapperPlayServerProjectilePower for 1.21 2024-05-10 16:01:54 +02:00
booky10 f3ee112a4e Merge branch '2.0' into dev/1.21 2024-05-10 15:52:41 +02:00
retrooper 1634eed6e7 Merge pull request #763 from AbhigyaKrishna/2.0
StateValue Map caching
2024-05-10 00:05:35 +02:00
retrooper 6e7dbf13e3 Typo fix 2024-05-10 00:04:09 +02:00
AbhigyaKrishna d60c661d3d StateValue Map caching
Caching and reusing StateValue data map for saving memory
2024-05-10 02:55:30 +05:30
Memencio cd0f1bb241 Fix Encryption Response packet wrapper write method for server versions between 1.19 and 1.19.2 2024-05-09 16:36:04 -04:00
retrooper dd46140147 Implement immutable views on the values within registries 2024-05-09 19:26:58 +02:00
retrooper 337d35f1d8 ReflectionObject#readObjectArray implemented 2024-05-09 18:38:32 +02:00
retrooper 6ad2d61dd3 WrapperPlayServerSoundEffect fixed for 1.8-1.9.4 2024-05-09 18:36:36 +02:00
retrooper 1d6e737611 Bump version up to 2.3.0-SNAPSHOT 2024-05-09 00:41:13 +02:00
_ShadowOfHeaven_ 01e161d37e Added the missing WrapperPlayServerMapData 2024-05-07 23:47:00 +02:00
booky10 bbb3e47a50 Add new attributes for 1.21 2024-05-03 19:27:46 +02:00
booky10 09ab2ff82b Revert legacy itemtypes being changed 2024-05-03 16:24:55 +02:00
booky10 a59a8e116c Update all itemtypes for 1.21 2024-05-03 16:17:47 +02:00
booky10 9adfa831bf Update block and item tags for 1.21 2024-05-03 16:13:56 +02:00
booky10 3a4daec265 Add item type mappings for 1.21 2024-05-03 16:13:47 +02:00
booky10 de87ca203e Implement painting variant registry 2024-05-03 16:07:04 +02:00
booky10 9957cb3de9 Add 1.21 banner patterns 2024-05-03 15:57:17 +02:00
booky10 2ac60d7055 Add 1.21 version constants 2024-05-03 15:55:07 +02:00
leegod 87c2ca3c07 feat: Added getter and setter for LightData in WrapperPlayServerChunkData
chore: Added packet transformation for pre 1.18 (where light data and chunk data are sent separately)
2024-04-16 16:39:26 +08:00
leegod 1f1b1aabd1 feat: Added WrapperPlayServerUpdateLight
chore: Moved away LightData values from WrapperPlayServerChunkData
2024-04-16 16:27:53 +08:00
ytnoos e2865ce4be Fix re-encode for new bytebufs that were bigger than the received one 2024-03-02 10:47:52 +01:00
ytnoos f53f182090 MessageSignature limits 2024-03-02 10:34:35 +01:00
ytnoos 2ec7771a8a ByteBufInputStream limits 2024-03-02 10:32:17 +01:00
ytnoos 75320ef583 implemented NBT read limits following vanilla values 2024-03-02 10:29:02 +01:00
233 changed files with 11818 additions and 4105 deletions

No files matched your search

+2 -2
View File
@@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '17'
java-version: '21'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
-60
View File
@@ -1,60 +0,0 @@
import com.github.retrooper.MappingCompressionTask
// papermc repo + disableAutoTargetJvm needed for mockbukkit
repositories {
maven { url 'https://repo.papermc.io/repository/maven-public/' }
}
java {
disableAutoTargetJvm()
sourceSets {
main {
resources {
srcDirs += ["$buildDir.canonicalPath/mappings/generated"]
}
}
test {
resources {
srcDirs += ["$buildDir.canonicalPath/mappings/generated"]
}
}
}
}
dependencies {
compileOnly(adventureDependencies)
testImplementation(adventureDependencies)
testImplementation(project(":netty-common"))
testImplementation("com.github.seeseemelk:MockBukkit-v1.20:3.9.0")
testImplementation("org.slf4j:slf4j-simple:2.0.7")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.0")
}
test {
useJUnitPlatform()
}
task generateMapping(type: MappingCompressionTask) {
mappingsDir = rootDir.toPath().resolve("mappings")
outDir = project.buildDir.toPath().resolve("mappings/generated/assets/mappings")
outputs.upToDateWhen {
def gen_modified = project.buildDir.toPath().resolve("mappings/generated/assets/mappings").toFile().lastModified()
def list = []
project.rootProject.projectDir.toPath().resolve("mappings").toFile().eachFileRecurse(groovy.io.FileType.FILES) { file ->
list << file
}
for (File file : list) {
if (file.lastModified() > gen_modified) {
return false
}
}
return true
}
}
tasks.processResources.dependsOn(generateMapping)
+138
View File
@@ -0,0 +1,138 @@
import com.github.retrooper.compression.strategy.JsonArrayCompressionStrategy
import com.github.retrooper.compression.strategy.JsonObjectCompressionStrategy
import com.github.retrooper.compression.strategy.JsonToNbtStrategy
import com.github.retrooper.excludeAdventure
plugins {
packetevents.`shadow-conventions`
packetevents.`library-conventions`
`mapping-compression`
`pe-version`
}
// papermc repo + disableAutoTargetJvm needed for mockbukkit
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
java {
withJavadocJar()
}
dependencies {
compileOnlyApi(libs.bundles.adventure)
implementation(libs.adventure.api)
api(project(":patch:adventure-text-serializer-gson", "shadow")) {
excludeAdventure()
}
api(libs.adventure.text.serializer.legacy) {
excludeAdventure()
}
compileOnly(libs.gson)
testImplementation(libs.bundles.adventure)
testImplementation(project(":patch:adventure-text-serializer-gson"))
testImplementation(libs.adventure.text.serializer.legacy)
testImplementation(project(":netty-common"))
testImplementation(testlibs.mockbukkit)
testImplementation(testlibs.slf4j)
testImplementation(testlibs.bundles.junit)
}
mappingCompression {
mappingDirectory = rootDir.resolve("mappings")
outDirectory = project.layout.buildDirectory.dir("mappings/generated/assets/mappings")
with<JsonToNbtStrategy> {
compress("block/legacy_block_mappings.json")
compress("block/modern_block_mappings.json")
compress("stats/statistics.json")
}
with<JsonArrayCompressionStrategy> {
compress("attribute/attribute_mappings.json")
compress("block/block_entity_type_mappings.json")
compress("block/block_type_mappings.json")
compress("chat/chat_type_mappings.json")
compress("command/argument_parser_mappings.json")
compress("entity/entity_data_type_mappings.json")
compress("entity/painting_mappings.json")
compress("item/item_armor_material_mappings.json")
compress("item/item_banner_pattern_mappings.json")
compress("item/item_component_mappings.json")
compress("item/item_instrument_mappings.json")
compress("item/item_map_decoration_type_mappings.json")
compress("item/item_potion_mappings.json")
compress("item/item_trim_material_mappings.json")
compress("item/item_trim_pattern_mappings.json")
compress("item/recipe_serializer_mappings.json")
compress("particle/particle_type_mappings.json")
compress("sound/sound_mappings.json")
compress("world/world_position_source_mappings.json")
}
with<JsonObjectCompressionStrategy> {
compress("enchantment/enchantment_type_mappings.json")
compress("entity/entity_effect_mappings.json")
compress("entity/entity_type_mappings.json")
compress("entity/legacy_entity_type_mappings.json")
compress("item/item_type_mappings.json")
}
}
tasks {
javadoc {
setDestinationDir(file("${project.layout.buildDirectory.asFile.get()}/docs/javadoc"))
options {
(this as CoreJavadocOptions).addBooleanOption("Xdoclint:none", true)
}
mustRunAfter(generateVersionsFile)
}
sourcesJar {
mustRunAfter(generateVersionsFile)
}
withType<JavaCompile> {
dependsOn(generateVersionsFile)
}
processResources {
dependsOn(compressMappings)
from(project.layout.buildDirectory.dir("mappings/generated").get())
}
generateVersionsFile {
packageName = "com.github.retrooper.packetevents.util"
}
test {
useJUnitPlatform()
}
shadowJar {
exclude {
val path = it.path
path.startsWith("net/kyori") && !path.startsWith("net/kyori/adventure/text/serializer") && !path.startsWith("net/kyori/option")
}
}
}
publishing {
publications {
named<MavenPublication>("shadow") {
artifact(tasks["javadocJar"])
}
}
}
@@ -27,6 +27,8 @@ import com.github.retrooper.packetevents.netty.NettyManager;
import com.github.retrooper.packetevents.settings.PacketEventsSettings;
import com.github.retrooper.packetevents.util.LogManager;
import com.github.retrooper.packetevents.util.PEVersion;
//Note: The PEVersions class is generated. Refer to PEVersionTask.kt
import com.github.retrooper.packetevents.util.PEVersions;
import com.github.retrooper.packetevents.util.updatechecker.UpdateChecker;
import java.util.logging.Logger;
@@ -35,11 +37,9 @@ public abstract class PacketEventsAPI<T> {
private static final EventManager EVENT_MANAGER = new EventManager();
private static final PacketEventsSettings SETTINGS = new PacketEventsSettings();
private static final UpdateChecker UPDATE_CHECKER = new UpdateChecker();
private final Logger LOGGER = Logger.getLogger(PacketEventsAPI.class.getName());
private static final LogManager LOG_MANAGER = new LogManager();
//TODO UPDATE
private static final PEVersion VERSION = new PEVersion(2, 3, 0);
private static final Logger LOGGER = Logger.getLogger(PacketEventsAPI.class.getName());
public EventManager getEventManager() {
return EVENT_MANAGER;
}
@@ -53,7 +53,8 @@ public abstract class PacketEventsAPI<T> {
}
public PEVersion getVersion() {
return VERSION;
//Note: The PEVersions class is generated by the auto-version task. Refer to PEVersionTask.kt
return PEVersions.CURRENT;
}
public Logger getLogger() {
@@ -23,7 +23,7 @@ import com.github.retrooper.packetevents.exception.InvalidHandshakeException;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
public class EventManager {
@@ -82,7 +82,7 @@ public class EventManager {
public PacketListenerCommon registerListener(PacketListenerCommon listener) {
Set<PacketListenerCommon> listenerSet = listenersMap.get(listener.getPriority());
if (listenerSet == null) {
listenerSet = ConcurrentHashMap.newKeySet();
listenerSet = new CopyOnWriteArraySet<>();
}
listenerSet.add(listener);
listenersMap.put(listener.getPriority(), listenerSet);
@@ -100,9 +100,21 @@ public class InternalPacketListener extends PacketListenerAbstract {
NBTCompound registryDataTag = registryData.getRegistryData();
NBTList<NBTCompound> list = null;
if (registryDataTag != null) { // <1.20.5
list = registryDataTag
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.toString())
.getCompoundListTagOrNull("value");
//Handle dimension type
NBTCompound dimensionTypes = registryDataTag
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.toString());
if (dimensionTypes == null) {
dimensionTypes = registryDataTag
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.getKey());
}
if (dimensionTypes != null) {
list = dimensionTypes.getCompoundListTagOrNull("value");
}
if (list == null) {
list = new NBTList<>(NBTType.COMPOUND);
PacketEvents.getAPI().getLogger().warning("Can't find dimension type registry in registry data, "
+ "this may cause issues; available registries: " + registryDataTag.getTags().keySet());
}
} else if (DIMENSION_TYPE_REGISTRY_KEY.equals(registryData.getRegistryKey())) { // >=1.20.5
// remap to legacy format
list = new NBTList<>(NBTType.COMPOUND);
@@ -137,10 +149,22 @@ public class InternalPacketListener extends PacketListenerAbstract {
// Store world data
NBTCompound dimensionCodec = joinGame.getDimensionCodec();
if (dimensionCodec != null) {
NBTList<NBTCompound> list = dimensionCodec
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.toString())
.getCompoundListTagOrNull("value");
user.setWorldNBT(list);
NBTList<NBTCompound> types = null;
NBTCompound dimensionTypes = dimensionCodec
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.toString());
if (dimensionTypes == null) {
dimensionTypes = dimensionCodec
.getCompoundTagOrNull(DIMENSION_TYPE_REGISTRY_KEY.getKey());
}
if (dimensionTypes != null) { // account for dimension type registry somehow going missing
types = dimensionTypes.getCompoundListTagOrNull("value");
}
if (types == null) {
types = new NBTList<>(NBTType.COMPOUND);
PacketEvents.getAPI().getLogger().warning("Can't find dimension type registry in join packet, "
+ "this may cause issues; available registries: " + dimensionCodec.getTags().keySet());
}
user.setWorldNBT(types);
}
// Update world height
@@ -49,6 +49,7 @@ public enum ServerVersion {
V_1_19(759), V_1_19_1(760), V_1_19_2(760), V_1_19_3(761), V_1_19_4(762),
//1.20 and 1.20.1 have the same protocol version. 1.20.3 and 1.20.4 have the same protocol version. 1.20.5 and 1.20.6 have the same protocol version
V_1_20(763), V_1_20_1(763), V_1_20_2(764), V_1_20_3(765), V_1_20_4(765), V_1_20_5(766), V_1_20_6(766),
V_1_21(767),
//TODO UPDATE Add server version constant
ERROR(-1, true);
@@ -24,8 +24,11 @@ public interface ByteBufAllocationOperator {
Object copiedBuffer(byte[] bytes);
Object buffer();
Object buffer(int initialCapacity);
Object directBuffer();
Object directBuffer(int initialCapacity);
Object compositeBuffer();
Object compositeBuffer(int maxNumComponents);
}
@@ -103,6 +103,14 @@ public class ByteBufHelper {
PacketEvents.getAPI().getNettyManager().getByteBufOperator().writeShort(buffer, value);
}
public static int readMedium(Object buffer) {
return PacketEvents.getAPI().getNettyManager().getByteBufOperator().readMedium(buffer);
}
public static void writeMedium(Object buffer, int value) {
PacketEvents.getAPI().getNettyManager().getByteBufOperator().writeMedium(buffer, value);
}
public static int readInt(Object buffer) {
return PacketEvents.getAPI().getNettyManager().getByteBufOperator().readInt(buffer);
}
@@ -42,27 +42,27 @@ public class ByteBufInputStream extends InputStream implements DataInput {
this(buffer, ByteBufHelper.readableBytes(buffer), releaseOnClose);
}
public ByteBufInputStream(Object buffer, int length, boolean releaseOnClose) {
public ByteBufInputStream(Object buffer, int maxLength, boolean releaseOnClose) {
this.lineBuf = new StringBuilder();
if (buffer == null) {
throw new NullPointerException("buffer");
} else if (length < 0) {
} else if (maxLength < 0) {
if (releaseOnClose) {
ByteBufHelper.release(buffer);
}
throw new IllegalArgumentException("length: " + length);
} else if (length > ByteBufHelper.readableBytes(buffer)) {
throw new IllegalArgumentException("maxLength: " + maxLength);
} else if (ByteBufHelper.readableBytes(buffer) > maxLength) {
if (releaseOnClose) {
ByteBufHelper.release(buffer);
}
throw new IndexOutOfBoundsException("Too many bytes to be read - Needs " + length + ", maximum is " + ByteBufHelper.readableBytes(buffer));
throw new IndexOutOfBoundsException("Too many bytes to be read - Found " + ByteBufHelper.readableBytes(buffer) + ", maximum is " + maxLength);
} else {
this.releaseOnClose = releaseOnClose;
this.buffer = buffer;
this.startIndex = ByteBufHelper.readerIndex(buffer);
this.endIndex = this.startIndex + length;
this.endIndex = this.startIndex + ByteBufHelper.readableBytes(buffer);
ByteBufHelper.markReaderIndex(buffer);
}
}
@@ -35,12 +35,14 @@ public interface ByteBufOperator {
byte readByte(Object buffer);
short readShort(Object buffer);
int readMedium(Object buffer);
int readInt(Object buffer);
long readUnsignedInt(Object buffer);
long readLong(Object buffer);
void writeByte(Object buffer, int value);
void writeShort(Object buffer, int value);
void writeMedium(Object buffer, int value);
void writeInt(Object buffer, int value);
void writeLong(Object buffer, long value);
@@ -40,4 +40,16 @@ public class UnpooledByteBufAllocationHelper {
public static Object compositeBuffer() {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().compositeBuffer();
}
public static Object buffer(int initialCapacity) {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().buffer(initialCapacity);
}
public static Object directBuffer(int initialCapacity) {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().directBuffer(initialCapacity);
}
public static Object compositeBuffer(int maxNumComponents) {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().compositeBuffer(maxNumComponents);
}
}
@@ -89,7 +89,7 @@ public class Attributes {
// Added in 1.20.2
public static final Attribute GENERIC_MAX_ABSORPTION = define("generic.max_absorption");
// added in 1.20.5
// Added in 1.20.5
public static final Attribute PLAYER_BLOCK_BREAK_SPEED = define("player.block_break_speed");
public static final Attribute PLAYER_BLOCK_INTERACTION_RANGE = define("player.block_interaction_range");
public static final Attribute PLAYER_ENTITY_INTERACTION_RANGE = define("player.entity_interaction_range");
@@ -100,6 +100,17 @@ public class Attributes {
public static final Attribute GENERIC_SCALE = define("generic.scale");
public static final Attribute GENERIC_STEP_HEIGHT = define("generic.step_height");
// Added in 1.21
public static final Attribute GENERIC_BURNING_TIME = define("generic.burning_time");
public static final Attribute GENERIC_EXPLOSION_KNOCKBACK_RESISTANCE = define("generic.explosion_knockback_resistance");
public static final Attribute PLAYER_MINING_EFFICIENCY = define("player.mining_efficiency");
public static final Attribute GENERIC_MOVEMENT_EFFICIENCY = define("generic.movement_efficiency");
public static final Attribute GENERIC_OXYGEN_BONUS = define("generic.oxygen_bonus");
public static final Attribute PLAYER_SNEAKING_SPEED = define("player.sneaking_speed");
public static final Attribute PLAYER_SUBMERGED_MINING_SPEED = define("player.submerged_mining_speed");
public static final Attribute PLAYER_SWEEPING_DAMAGE_RATIO = define("player.sweeping_damage_ratio");
public static final Attribute GENERIC_WATER_MOVEMENT_EFFICIENCY = define("generic.water_movement_efficiency");
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -18,8 +18,32 @@
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.protocol.chat.message.ChatMessage_v1_19_1;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public interface ChatType extends MappedEntity {
ChatTypeDecoration getChatDecoration();
ChatTypeDecoration getNarrationDecoration();
static ChatType readDirect(PacketWrapper<?> wrapper) {
ChatTypeDecoration chatDecoration = ChatTypeDecoration.read(wrapper);
ChatTypeDecoration narrationDecoration = ChatTypeDecoration.read(wrapper);
return new StaticChatType(chatDecoration, narrationDecoration);
}
static void writeDirect(PacketWrapper<?> wrapper, ChatType chatType) {
ChatTypeDecoration.write(wrapper, chatType.getChatDecoration());
ChatTypeDecoration.write(wrapper, chatType.getNarrationDecoration());
}
class Bound extends ChatMessage_v1_19_1.ChatTypeBoundNetwork {
public Bound(ChatType type, Component name, @Nullable Component targetName) {
super(type, name, targetName);
}
}
}
@@ -0,0 +1,155 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.protocol.chat.message.ChatMessage_v1_19_1;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.format.Style;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.Parameter.*;
import static java.util.Arrays.asList;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.Style.empty;
import static net.kyori.adventure.text.format.Style.style;
import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
public class ChatTypeDecoration {
private final String translationKey;
private final List<Parameter> parameters;
private final Style style;
public ChatTypeDecoration(String translationKey, List<Parameter> parameters, Style style) {
this.translationKey = translationKey;
this.parameters = Collections.unmodifiableList(new ArrayList<>(parameters));
this.style = style;
}
public ChatTypeDecoration(NBTCompound nbt) {
//Read the translation key
this.translationKey = nbt.getStringTagValueOrDefault("translation_key", null);
//Read parameters
List<Parameter> temporaryList = new ArrayList<>();
NBTList<NBTString> parametersTag = nbt.getStringListTagOrNull("parameters");
if (parametersTag != null) {
for (NBTString p : parametersTag.getTags()) {
Parameter parameter = Parameter.valueByName(p.getValue().toUpperCase());
if (parameter != null) {
temporaryList.add(parameter);
}
}
}
this.parameters = Collections.unmodifiableList(temporaryList);
//Read style
NBTCompound styleTag = nbt.getCompoundTagOrNull("style");
if (styleTag != null) {
style = AdventureSerializer.getNBTSerializer().deserializeStyle(styleTag);
} else {
style = empty();
}
}
public static ChatTypeDecoration read(PacketWrapper<?> wrapper) {
String translationKey = wrapper.readString();
List<Parameter> parameters = wrapper.readList(ew -> ew.readEnum(Parameter.values()));
Style style = wrapper.readStyle();
return new ChatTypeDecoration(translationKey, parameters, style);
}
public static void write(PacketWrapper<?> wrapper, ChatTypeDecoration decoration) {
wrapper.writeString(decoration.translationKey);
wrapper.writeList(decoration.parameters, PacketWrapper::writeEnum);
wrapper.writeStyle(decoration.style);
}
public static ChatTypeDecoration withSender(String translationKey) {
return new ChatTypeDecoration(translationKey, asList(SENDER, CONTENT), empty());
}
public static ChatTypeDecoration incomingDirectMessage(String translationKey) {
return new ChatTypeDecoration(translationKey, asList(SENDER, CONTENT),
style(GRAY, ITALIC));
}
public static ChatTypeDecoration outgoingDirectMessage(String translationKey) {
return new ChatTypeDecoration(translationKey, asList(TARGET, CONTENT),
style(GRAY, ITALIC));
}
public static ChatTypeDecoration teamMessage(String translationKey) {
return new ChatTypeDecoration(translationKey, asList(TARGET, SENDER, CONTENT), empty());
}
public Component decorate(Component component, ChatType.Bound chatType) {
ComponentLike[] components = new ComponentLike[this.parameters.size()];
for (int i = 0; i < components.length; i++) {
Parameter parameter = this.parameters.get(i);
components[i] = parameter.selector.apply(component, chatType);
}
return Component.translatable(this.translationKey, null, this.style, components);
}
public String getTranslationKey() {
return this.translationKey;
}
public List<Parameter> getParameters() {
return this.parameters;
}
public Style getStyle() {
return this.style;
}
public enum Parameter {
SENDER((component, type) -> type.getName()),
TARGET((component, type) -> type.getTargetName() != null ? type.getTargetName() : Component.empty()),
CONTENT((component, type) -> component);
private final BiFunction<Component, ChatType.Bound, Component> selector;
Parameter(BiFunction<Component, ChatType.Bound, Component> selector) {
this.selector = selector;
}
@Nullable
public static Parameter valueByName(String name) {
try {
return valueOf(name);
} catch (IllegalArgumentException ex) {
return null;
}
}
}
}
@@ -24,18 +24,44 @@ import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.incomingDirectMessage;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.outgoingDirectMessage;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.teamMessage;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.withSender;
public class ChatTypes {
private static final Map<String, ChatType> CHAT_TYPE_MAP = new HashMap<>();
//Key - mappings version, value - map with chat type ids and chat types
private static final Map<Byte, Map<Integer, ChatType>> CHAT_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("chat/chat_type_mappings");
public static ChatType define(String key) {
return define(key, withSender("chat.type.text"));
}
public static ChatType define(String key, ChatTypeDecoration chatDeco) {
return define(key, chatDeco, withSender("chat.type.text.narrate"));
}
public static ChatType define(String key, ChatTypeDecoration chatDeco, ChatTypeDecoration narrationDeco) {
TypesBuilderData data = TYPES_BUILDER.define(key);
ChatType chatType = new ChatType() {
@Override
public ChatTypeDecoration getChatDecoration() {
return chatDeco;
}
@Override
public ChatTypeDecoration getNarrationDecoration() {
return narrationDeco;
}
@Override
public ResourceLocation getName() {
return data.getName();
@@ -61,12 +87,21 @@ public class ChatTypes {
}
public static final ChatType CHAT = define("chat");
public static final ChatType SAY_COMMAND = define("say_command");
public static final ChatType MSG_COMMAND_INCOMING = define("msg_command_incoming");
public static final ChatType MSG_COMMAND_OUTGOING = define("msg_command_outgoing");
public static final ChatType TEAM_MSG_COMMAND_INCOMING = define("team_msg_command_incoming");
public static final ChatType TEAM_MSG_COMMAND_OUTGOING = define("team_msg_command_outgoing");
public static final ChatType EMOTE_COMMAND = define("emote_command");
public static final ChatType SAY_COMMAND = define("say_command",
withSender("chat.type.announcement"));
public static final ChatType MSG_COMMAND_INCOMING = define("msg_command_incoming",
incomingDirectMessage("commands.message.display.incoming"));
public static final ChatType MSG_COMMAND_OUTGOING = define("msg_command_outgoing",
outgoingDirectMessage("commands.message.display.outgoing"));
public static final ChatType TEAM_MSG_COMMAND_INCOMING = define("team_msg_command_incoming",
teamMessage("chat.type.team.text"));
public static final ChatType TEAM_MSG_COMMAND_OUTGOING = define("team_msg_command_outgoing",
teamMessage("chat.type.team.sent"));
public static final ChatType EMOTE_COMMAND = define("emote_command",
withSender("chat.type.emote"),
withSender("chat.type.emote"));
// added by CraftBukkit for a few versions
public static final ChatType RAW = define("raw");
@Deprecated
@@ -78,6 +113,14 @@ public class ChatTypes {
@Deprecated
public static final ChatType TEAM_MSG_COMMAND = define("team_msg_command");
/**
* Returns an immutable view of the chat types.
* @return Chat Types
*/
public static Collection<ChatType> values() {
return Collections.unmodifiableCollection(CHAT_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -16,9 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.chat.Parsers.Parser;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
@@ -26,20 +27,33 @@ import java.util.List;
import java.util.Optional;
public class Node {
private byte flags;
private List<Integer> children;
private int redirectNodeIndex;
private Optional<String> name;
private Optional<Integer> parserID;
private Optional<Parser> parser;
private Optional<List<Object>> properties;
private Optional<ResourceLocation> suggestionsType;
public Node(byte flags, List<Integer> children, int redirectNodeIndex, @Nullable String name, @Nullable Integer parserID, @Nullable List<Object> properties, @Nullable ResourceLocation suggestionsType) {
public Node(
byte flags, List<Integer> children, int redirectNodeIndex, @Nullable String name,
@Nullable Integer parserID, @Nullable List<Object> properties, @Nullable ResourceLocation suggestionsType
) {
this(flags, children, redirectNodeIndex, name, parserID == null ? null : Parsers.getById(
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), parserID),
properties, suggestionsType);
}
public Node(
byte flags, List<Integer> children, int redirectNodeIndex, @Nullable String name, @Nullable Parser parser,
@Nullable List<Object> properties, @Nullable ResourceLocation suggestionsType
) {
this.flags = flags;
this.children = children;
this.redirectNodeIndex = redirectNodeIndex;
this.name = Optional.ofNullable(name);
this.parserID = Optional.ofNullable(parserID);
this.parser = Optional.ofNullable(parser);
this.properties = Optional.ofNullable(properties);
this.suggestionsType = Optional.ofNullable(suggestionsType);
}
@@ -76,12 +90,22 @@ public class Node {
this.name = name;
}
public Optional<Parser> getParser() {
return this.parser;
}
public void setParser(Optional<Parser> parser) {
this.parser = parser;
}
public Optional<Integer> getParserID() {
return parserID;
return this.parser.map(parser -> parser.getId(PacketEvents.getAPI()
.getServerManager().getVersion().toClientVersion()));
}
public void setParserID(Optional<Integer> parserID) {
this.parserID = parserID;
this.parser = parserID.map(id -> Parsers.getById(PacketEvents.getAPI()
.getServerManager().getVersion().toClientVersion(), id));
}
public Optional<List<Object>> getProperties() {
@@ -151,7 +151,9 @@ public class Parsers {
public static final Parser COMPONENT = define("component", null, null);
public static final Parser STYLE = define("style", null, null);
public static final Parser MESSAGE = define("message", null, null);
public static final Parser NBT = define("nbt", null, null);
public static final Parser NBT_COMPOUND_TAG = define("nbt_compound_tag", null, null);
@Deprecated
public static final Parser NBT = NBT_COMPOUND_TAG;
public static final Parser NBT_TAG = define("nbt_tag", null, null);
public static final Parser NBT_PATH = define("nbt_path", null, null);
public static final Parser OBJECTIVE = define("objective", null, null);
@@ -170,10 +172,13 @@ public class Parsers {
public static final Parser ITEM_SLOT = define("item_slot", null, null);
public static final Parser ITEM_SLOTS = define("item_slots", null, null);
public static final Parser RESOURCE_LOCATION = define("resource_location", null, null);
public static final Parser MOB_EFFECT = define("mob_effect", null, null);
public static final Parser FUNCTION = define("function", null, null);
public static final Parser ENTITY_ANCHOR = define("entity_anchor", null, null);
public static final Parser INT_RANGE = define("int_range", null, null);
public static final Parser FLOAT_RANGE = define("float_range", null, null);
public static final Parser ITEM_ENCHANTMENT = define("item_enchantment", null, null);
public static final Parser ENTITY_SUMMON = define("entity_summon", null, null);
public static final Parser DIMENSION = define("dimension", null, null);
public static final Parser GAMEMODE = define("gamemode", null, null);
public static final Parser TIME = define("time",
@@ -2,9 +2,9 @@ package com.github.retrooper.packetevents.protocol.chat;
public class SignedCommandArgument {
private String argument;
private byte[] signature;
private MessageSignature signature;
public SignedCommandArgument(String argument, byte[] signature) {
public SignedCommandArgument(String argument, MessageSignature signature) {
this.argument = argument;
this.signature = signature;
}
@@ -17,11 +17,11 @@ public class SignedCommandArgument {
this.argument = argument;
}
public byte[] getSignature() {
public MessageSignature getSignature() {
return signature;
}
public void setSignature(byte[] signature) {
public void setSignature(MessageSignature signature) {
this.signature = signature;
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
public class StaticChatType implements ChatType {
private final ChatTypeDecoration chatDecoration;
private final ChatTypeDecoration narrationDecoration;
public StaticChatType(ChatTypeDecoration chatDecoration, ChatTypeDecoration narrationDecoration) {
this.chatDecoration = chatDecoration;
this.narrationDecoration = narrationDecoration;
}
@Override
public ChatTypeDecoration getChatDecoration() {
return this.chatDecoration;
}
@Override
public ChatTypeDecoration getNarrationDecoration() {
return this.narrationDecoration;
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
}
}
@@ -30,7 +30,7 @@ import java.util.UUID;
public class ChatMessage_v1_19_1 extends ChatMessage_v1_16 {
private String plainContent;
private @Nullable Component unsignedChatContent;
private ChatTypeBoundNetwork chatType;
private ChatType.Bound chatFormatting;
private byte @Nullable [] previousSignature;
private byte[] signature;
private Instant timestamp;
@@ -40,15 +40,15 @@ public class ChatMessage_v1_19_1 extends ChatMessage_v1_16 {
public ChatMessage_v1_19_1(String plainContent, Component decoratedChatContent,
@Nullable Component unsignedChatContent,
UUID senderUUID, ChatTypeBoundNetwork chatType,
UUID senderUUID, ChatType.Bound chatFormatting,
byte @Nullable [] previousSignature, byte[] signature,
Instant timestamp, long salt,
LastSeenMessages lastSeenMessages,
FilterMask filterMask) {
super(decoratedChatContent, chatType.getType(), senderUUID);
super(decoratedChatContent, chatFormatting.getType(), senderUUID);
this.plainContent = plainContent;
this.unsignedChatContent = unsignedChatContent;
this.chatType = chatType;
this.chatFormatting = chatFormatting;
this.previousSignature = previousSignature;
this.signature = signature;
this.timestamp = timestamp;
@@ -79,20 +79,38 @@ public class ChatMessage_v1_19_1 extends ChatMessage_v1_16 {
@Override
public ChatType getType() {
return chatType.getType();
return chatFormatting.getType();
}
@Override
public void setType(ChatType type) {
chatType.setType(type);
chatFormatting.setType(type);
}
public ChatTypeBoundNetwork getChatType() {
return chatType;
public ChatType.Bound getChatFormatting() {
return chatFormatting;
}
public void setChatType(ChatTypeBoundNetwork type) {
this.chatType = type;
public void setChatFormatting(ChatType.Bound chatFormatting) {
this.chatFormatting = chatFormatting;
}
/**
* Retrieves the chat formatting. Please refer to {@link ChatMessage_v1_19_1#getChatFormatting}
* @return chat formatting which contains the actual chat type.
*/
@Deprecated
public ChatType.Bound getChatType() {
return chatFormatting;
}
/**
* Sets the chat formatting. Please refer to {@link ChatMessage_v1_19_1#setChatFormatting}
* @param chatFormatting formatting which contains the actual chat type.
*/
@Deprecated
public void setChatType(ChatType.Bound chatFormatting) {
this.chatFormatting = chatFormatting;
}
public byte @Nullable [] getPreviousSignature() {
@@ -143,6 +161,10 @@ public class ChatMessage_v1_19_1 extends ChatMessage_v1_16 {
this.filterMask = filterMask;
}
/**
* Please refer to {@link com.github.retrooper.packetevents.protocol.chat.ChatType.Bound}
*/
@Deprecated
public static class ChatTypeBoundNetwork {
private ChatType type;
private Component name;
@@ -1,5 +1,6 @@
package com.github.retrooper.packetevents.protocol.chat.message;
import com.github.retrooper.packetevents.protocol.chat.ChatType;
import com.github.retrooper.packetevents.protocol.chat.LastSeenMessages;
import com.github.retrooper.packetevents.protocol.chat.filter.FilterMask;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
@@ -19,10 +20,10 @@ public class ChatMessage_v1_19_3 extends ChatMessage_v1_16 {
LastSeenMessages.Packed lastSeenMessagesPacked;
@Nullable Component unsignedChatContent;
FilterMask filterMask;
ChatMessage_v1_19_1.ChatTypeBoundNetwork chatType;
ChatType.Bound chatFormatting;
public ChatMessage_v1_19_3(UUID senderUUID, int index, byte[] signature, String plainContent, Instant timestamp, long salt, LastSeenMessages.Packed lastSeenMessagesPacked, @Nullable Component unsignedChatContent, FilterMask filterMask, ChatMessage_v1_19_1.ChatTypeBoundNetwork chatType) {
super(Component.text(plainContent), chatType.getType(), senderUUID);
public ChatMessage_v1_19_3(UUID senderUUID, int index, byte[] signature, String plainContent, Instant timestamp, long salt, LastSeenMessages.Packed lastSeenMessagesPacked, @Nullable Component unsignedChatContent, FilterMask filterMask, ChatType.Bound chatFormatting) {
super(Component.text(plainContent), chatFormatting.getType(), senderUUID);
this.index = index;
this.signature = signature;
this.plainContent = plainContent;
@@ -31,7 +32,7 @@ public class ChatMessage_v1_19_3 extends ChatMessage_v1_16 {
this.lastSeenMessagesPacked = lastSeenMessagesPacked;
this.unsignedChatContent = unsignedChatContent;
this.filterMask = filterMask;
this.chatType = chatType;
this.chatFormatting = chatFormatting;
}
public int getIndex() {
@@ -109,11 +110,30 @@ public class ChatMessage_v1_19_3 extends ChatMessage_v1_16 {
this.filterMask = filterMask;
}
public ChatMessage_v1_19_1.ChatTypeBoundNetwork getChatType() {
return chatType;
public ChatType.Bound getChatFormatting() {
return chatFormatting;
}
public void setChatType(ChatMessage_v1_19_1.ChatTypeBoundNetwork chatType) {
this.chatType = chatType;
public void setChatFormatting(ChatType.Bound chatFormatting) {
this.chatFormatting = chatFormatting;
}
/**
* Retrieves the chat formatting. Please refer to {@link ChatMessage_v1_19_3#getChatFormatting}
* @return chat formatting which contains the actual chat type.
*/
@Deprecated
public ChatType.Bound getChatType() {
return chatFormatting;
}
/**
* Sets the chat formatting. Please refer to {@link ChatMessage_v1_19_3#setChatFormatting}
* @param chatFormatting formatting which contains the actual chat type.
*/
@Deprecated
public void setChatType(ChatType.Bound chatFormatting) {
this.chatFormatting = chatFormatting;
}
}
@@ -50,8 +50,8 @@ public class ChatMessageProcessor_v1_19_1 implements ChatMessageProcessor {
LastSeenMessages lastSeenMessages = wrapper.readLastSeenMessages();
Component unsignedChatContent = wrapper.readOptional(PacketWrapper::readComponent);
FilterMask filterMask = wrapper.readFilterMask();
ChatMessage_v1_19_1.ChatTypeBoundNetwork chatType = wrapper.readChatTypeBoundNetwork();
return new ChatMessage_v1_19_1(plainContent, chatContent, unsignedChatContent, senderUUID, chatType,
ChatType.Bound chatFormatting = wrapper.readChatTypeBoundNetwork();
return new ChatMessage_v1_19_1(plainContent, chatContent, unsignedChatContent, senderUUID, chatFormatting,
previousSignature, signature, timestamp, salt, lastSeenMessages, filterMask);
}
@@ -68,6 +68,6 @@ public class ChatMessageProcessor_v1_19_1 implements ChatMessageProcessor {
wrapper.writeLastSeenMessages(newData.getLastSeenMessages());
wrapper.writeOptional(newData.getUnsignedChatContent(), PacketWrapper::writeComponent);
wrapper.writeFilterMask(newData.getFilterMask());
wrapper.writeChatTypeBoundNetwork(newData.getChatType());
wrapper.writeChatTypeBoundNetwork(newData.getChatFormatting());
}
}
@@ -1,5 +1,6 @@
package com.github.retrooper.packetevents.protocol.chat.message.reader.impl;
import com.github.retrooper.packetevents.protocol.chat.ChatType;
import com.github.retrooper.packetevents.protocol.chat.LastSeenMessages;
import com.github.retrooper.packetevents.protocol.chat.filter.FilterMask;
import com.github.retrooper.packetevents.protocol.chat.message.ChatMessage;
@@ -25,7 +26,7 @@ public class ChatMessageProcessor_v1_19_3 implements ChatMessageProcessor {
LastSeenMessages.Packed lastSeenMessagesPacked = wrapper.readLastSeenMessagesPacked();
Component unsignedChatContent = wrapper.readOptional(PacketWrapper::readComponent);
FilterMask filterMask = wrapper.readFilterMask();
ChatMessage_v1_19_1.ChatTypeBoundNetwork chatType = wrapper.readChatTypeBoundNetwork();
ChatType.Bound chatType = wrapper.readChatTypeBoundNetwork();
return new ChatMessage_v1_19_3(senderUUID, index, signature, plainContent, timestamp, salt, lastSeenMessagesPacked, unsignedChatContent, filterMask, chatType);
}
@@ -42,6 +43,6 @@ public class ChatMessageProcessor_v1_19_3 implements ChatMessageProcessor {
wrapper.writeLastSeenMessagesPacked(newData.getLastSeenMessagesPacked());
wrapper.writeOptional(newData.getUnsignedChatContent().orElse(null), PacketWrapper::writeComponent);
wrapper.writeFilterMask(newData.getFilterMask());
wrapper.writeChatTypeBoundNetwork(newData.getChatType());
wrapper.writeChatTypeBoundNetwork(newData.getChatFormatting());
}
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.component.builtin.item.ArmorTr
import com.github.retrooper.packetevents.protocol.component.builtin.item.BannerLayers;
import com.github.retrooper.packetevents.protocol.component.builtin.item.BundleContents;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ChargedProjectiles;
import com.github.retrooper.packetevents.protocol.component.builtin.item.CustomData;
import com.github.retrooper.packetevents.protocol.component.builtin.item.DebugStickState;
import com.github.retrooper.packetevents.protocol.component.builtin.item.FireworkExplosion;
import com.github.retrooper.packetevents.protocol.component.builtin.item.FoodProperties;
@@ -35,6 +36,7 @@ import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemCon
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemDyeColor;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemEnchantments;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemFireworks;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemJukeboxPlayable;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemLock;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemLore;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemMapDecorations;
@@ -61,6 +63,8 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -132,7 +136,9 @@ public class ComponentTypes {
// item component types
public static final ComponentType<NBTCompound> CUSTOM_DATA = define("custom_data",
PacketWrapper::readNBT, PacketWrapper::writeNBT);
// mojang wraps their "persistent" codec as a stream codec just here,
// so packetevents has to handle nbt strings
CustomData::read, CustomData::write);
public static final ComponentType<Integer> MAX_STACK_SIZE = define("max_stack_size",
PacketWrapper::readVarInt, PacketWrapper::writeVarInt);
public static final ComponentType<Integer> MAX_DAMAGE = define("max_damage",
@@ -244,6 +250,19 @@ public class ComponentTypes {
public static final ComponentType<ItemContainerLoot> CONTAINER_LOOT = define("container_loot",
ItemContainerLoot::read, ItemContainerLoot::write);
// added in 1.21
public static final ComponentType<ItemJukeboxPlayable> JUKEBOX_PLAYABLE = define("jukebox_playable",
ItemJukeboxPlayable::read, ItemJukeboxPlayable::write);
/**
* Returns an immutable view of the component types.
*
* @return Component Types
*/
public static Collection<ComponentType<?>> values() {
return Collections.unmodifiableCollection(COMPONENT_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -87,4 +87,9 @@ public class ArmorTrim {
public int hashCode() {
return Objects.hash(this.material, this.pattern, this.showInTooltip);
}
@Override
public String toString() {
return "ArmorTrim{material=" + this.material + ", pattern=" + this.pattern + ", showInTooltip=" + this.showInTooltip + '}';
}
}
@@ -0,0 +1,45 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class CustomData {
private CustomData() {
}
public static NBTCompound read(PacketWrapper<?> wrapper) {
NBT nbt = wrapper.readNBTRaw();
if (nbt instanceof NBTCompound) {
return (NBTCompound) nbt;
}
if (nbt instanceof NBTString) {
// TODO: parse nbt string
}
throw new UnsupportedOperationException("Unsupported custom data nbt type: " + nbt.getType());
}
public static void write(PacketWrapper<?> wrapper, NBTCompound compound) {
wrapper.writeNBT(compound);
}
}
@@ -18,8 +18,11 @@
package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.potion.PotionEffect;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
@@ -31,13 +34,25 @@ public class FoodProperties {
private boolean canAlwaysEat;
private float eatSeconds;
private List<PossibleEffect> effects;
private @Nullable ItemStack usingConvertsTo;
public FoodProperties(int nutrition, float saturation, boolean canAlwaysEat, float eatSeconds, List<PossibleEffect> effects) {
public FoodProperties(
int nutrition, float saturation, boolean canAlwaysEat,
float eatSeconds, List<PossibleEffect> effects
) {
this(nutrition, saturation, canAlwaysEat, eatSeconds, effects, null);
}
public FoodProperties(
int nutrition, float saturation, boolean canAlwaysEat, float eatSeconds,
List<PossibleEffect> effects, @Nullable ItemStack usingConvertsTo
) {
this.nutrition = nutrition;
this.saturation = saturation;
this.canAlwaysEat = canAlwaysEat;
this.eatSeconds = eatSeconds;
this.effects = effects;
this.usingConvertsTo = usingConvertsTo;
}
public static FoodProperties read(PacketWrapper<?> wrapper) {
@@ -46,7 +61,9 @@ public class FoodProperties {
boolean canAlwaysEat = wrapper.readBoolean();
float eatSeconds = wrapper.readFloat();
List<PossibleEffect> effects = wrapper.readList(PossibleEffect::read);
return new FoodProperties(nutrition, saturation, canAlwaysEat, eatSeconds, effects);
ItemStack usingConvertsTo = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21)
? wrapper.readOptional(PacketWrapper::readItemStack) : null;
return new FoodProperties(nutrition, saturation, canAlwaysEat, eatSeconds, effects, usingConvertsTo);
}
public static void write(PacketWrapper<?> wrapper, FoodProperties props) {
@@ -55,6 +72,9 @@ public class FoodProperties {
wrapper.writeBoolean(props.canAlwaysEat);
wrapper.writeFloat(props.eatSeconds);
wrapper.writeList(props.effects, PossibleEffect::write);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21)) {
wrapper.writeOptional(props.usingConvertsTo, PacketWrapper::writeItemStack);
}
}
public int getNutrition() {
@@ -101,6 +121,14 @@ public class FoodProperties {
this.effects = effects;
}
public @Nullable ItemStack getUsingConvertsTo() {
return this.usingConvertsTo;
}
public void setUsingConvertsTo(@Nullable ItemStack usingConvertsTo) {
this.usingConvertsTo = usingConvertsTo;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
@@ -110,12 +138,13 @@ public class FoodProperties {
if (Float.compare(that.saturation, this.saturation) != 0) return false;
if (this.canAlwaysEat != that.canAlwaysEat) return false;
if (Float.compare(that.eatSeconds, this.eatSeconds) != 0) return false;
return this.effects.equals(that.effects);
if (!this.effects.equals(that.effects)) return false;
return Objects.equals(this.usingConvertsTo, that.usingConvertsTo);
}
@Override
public int hashCode() {
return Objects.hash(this.nutrition, this.saturation, this.canAlwaysEat, this.eatSeconds, this.effects);
return Objects.hash(this.nutrition, this.saturation, this.canAlwaysEat, this.eatSeconds, this.effects, this.usingConvertsTo);
}
public static class PossibleEffect {
@@ -0,0 +1,115 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.protocol.item.jukebox.JukeboxSong;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ItemJukeboxPlayable {
private @Nullable JukeboxSong song;
private @Nullable ResourceLocation songKey;
private boolean showInTooltip;
public ItemJukeboxPlayable(
@Nullable JukeboxSong song,
@Nullable ResourceLocation songKey,
boolean showInTooltip
) {
if ((song == null) == (songKey == null)) {
throw new IllegalStateException("Illegal state of both song and "
+ "song key combined: " + song + " / " + songKey);
}
this.song = song;
this.songKey = songKey;
this.showInTooltip = showInTooltip;
}
public static ItemJukeboxPlayable read(PacketWrapper<?> wrapper) {
JukeboxSong song;
ResourceLocation songKey;
if (wrapper.readBoolean()) {
song = JukeboxSong.read(wrapper);
songKey = null;
} else {
song = null;
songKey = wrapper.readIdentifier();
}
boolean showInTooltip = wrapper.readBoolean();
return new ItemJukeboxPlayable(song, songKey, showInTooltip);
}
public static void write(PacketWrapper<?> wrapper, ItemJukeboxPlayable jukeboxPlayable) {
if (jukeboxPlayable.song != null) {
wrapper.writeBoolean(true);
JukeboxSong.write(wrapper, jukeboxPlayable.song);
} else {
assert jukeboxPlayable.songKey != null;
wrapper.writeBoolean(false);
wrapper.writeIdentifier(jukeboxPlayable.songKey);
}
wrapper.writeBoolean(jukeboxPlayable.showInTooltip);
}
public @Nullable JukeboxSong getSong() {
return this.song;
}
public void setSong(JukeboxSong song) {
this.song = song;
this.songKey = null;
}
public @Nullable ResourceLocation getSongKey() {
return this.songKey;
}
public void setSongKey(ResourceLocation songKey) {
this.song = null;
this.songKey = songKey;
}
public boolean isShowInTooltip() {
return this.showInTooltip;
}
public void setShowInTooltip(boolean showInTooltip) {
this.showInTooltip = showInTooltip;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ItemJukeboxPlayable)) return false;
ItemJukeboxPlayable that = (ItemJukeboxPlayable) obj;
if (this.showInTooltip != that.showInTooltip) return false;
if (!Objects.equals(this.song, that.song)) return false;
return Objects.equals(this.songKey, that.songKey);
}
@Override
public int hashCode() {
return Objects.hash(this.song, this.songKey, this.showInTooltip);
}
}
@@ -122,7 +122,7 @@ public class ItemMapDecorations {
compound.setTag("type", new NBTString(decoration.type.getName().toString()));
compound.setTag("x", new NBTDouble(decoration.x));
compound.setTag("z", new NBTDouble(decoration.z));
compound.setTag("float", new NBTFloat(decoration.rotation));
compound.setTag("rotation", new NBTFloat(decoration.rotation));
}
public MapDecorationType getType() {
@@ -27,6 +27,7 @@ import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
public class PotDecorations {
@@ -36,8 +37,13 @@ public class PotDecorations {
private @Nullable ItemType right;
private @Nullable ItemType front;
private PotDecorations(Queue<ItemType> items) {
this(items.poll(), items.poll(), items.poll(), items.poll());
private PotDecorations(Queue<Optional<ItemType>> items) {
this(
items.isEmpty() ? null : items.remove().orElse(null),
items.isEmpty() ? null : items.remove().orElse(null),
items.isEmpty() ? null : items.remove().orElse(null),
items.isEmpty() ? null : items.remove().orElse(null)
);
}
public PotDecorations(
@@ -52,23 +58,28 @@ public class PotDecorations {
this.front = front;
}
private List<ItemType> asList() {
return Arrays.asList(this.back, this.left, this.right, this.front);
private List<Optional<ItemType>> asList() {
return Arrays.asList(
Optional.ofNullable(this.back),
Optional.ofNullable(this.left),
Optional.ofNullable(this.right),
Optional.ofNullable(this.front)
);
}
private static @Nullable ItemType readItem(PacketWrapper<?> wrapper) {
private static Optional<ItemType> readItem(PacketWrapper<?> wrapper) {
ItemType type = wrapper.readMappedEntity(ItemTypes::getById);
return type == ItemTypes.BRICK ? null : type;
return type == ItemTypes.BRICK ? Optional.empty() : Optional.of(type);
}
public static PotDecorations read(PacketWrapper<?> wrapper) {
Queue<ItemType> items = wrapper.<ItemType, Queue<ItemType>>readCollection(
ArrayDeque::new, PotDecorations::readItem);
Queue<Optional<ItemType>> items = wrapper.<Optional<ItemType>, Queue<Optional<ItemType>>>
readCollection(ArrayDeque::new, PotDecorations::readItem);
return new PotDecorations(items);
}
private static void writeItem(PacketWrapper<?> wrapper, @Nullable ItemType type) {
wrapper.writeMappedEntity(type == null ? ItemTypes.BRICK : type);
private static void writeItem(PacketWrapper<?> wrapper, Optional<ItemType> type) {
wrapper.writeMappedEntity(type.orElse(ItemTypes.BRICK));
}
public static void write(PacketWrapper<?> wrapper, PotDecorations decorations) {
@@ -39,11 +39,7 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
@@ -210,6 +206,14 @@ public class EntityDataTypes {
public static final EntityDataType<Integer> WOLF_VARIANT =
define("wolf_variant_type", readIntDeserializer(), writeIntSerializer());
/**
* Returns an immutable view of the entity-data types.
* @return Entity-Data Types
*/
public static Collection<EntityDataType<?>> values() {
return Collections.unmodifiableCollection(ENTITY_DATA_TYPE_MAP.values());
}
public static EntityDataType<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, EntityDataType<?>> typeIdMap = ENTITY_DATA_TYPE_ID_MAP.get((byte) index);
@@ -24,9 +24,7 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.*;
public class EntityTypes {
private static final Map<String, EntityType> ENTITY_TYPE_MAP = new HashMap<>();
@@ -297,6 +295,14 @@ public class EntityTypes {
public static final EntityType BREEZE_WIND_CHARGE = define("breeze_wind_charge", ABSTRACT_WIND_CHARGE);
public static final EntityType OMINOUS_ITEM_SPAWNER = define("ominous_item_spawner", ENTITY);
/**
* Returns an immutable view of the entity types.
* @return Entity Types
*/
public static Collection<EntityType> values() {
return Collections.unmodifiableCollection(ENTITY_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
LEGACY_TYPES_BUILDER.unloadFileMappings();
@@ -20,6 +20,8 @@ package com.github.retrooper.packetevents.protocol.entity.villager.type;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -62,4 +64,12 @@ public class VillagerTypes {
public static final VillagerType SNOW = define(4, "minecraft:snow");
public static final VillagerType SWAMP = define(5, "minecraft:swamp");
public static final VillagerType TAIGA = define(6, "minecraft:taiga");
/**
* Returns an immutable view of the villager types.
* @return Villager Types
*/
public static Collection<VillagerType> values() {
return Collections.unmodifiableCollection(VILLAGER_TYPE_MAP.values());
}
}
@@ -0,0 +1,14 @@
package com.github.retrooper.packetevents.protocol.entity.wolfvariant;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import java.util.Set;
//TODO Finish Wolf-Variant class
public class WolfVariant {
private ResourceLocation wildId, tameId, angryId;
public WolfVariant(ResourceLocation wildId, ResourceLocation tameId, ResourceLocation angryId) {
}
}
@@ -21,12 +21,14 @@ package com.github.retrooper.packetevents.protocol.item;
import com.github.retrooper.packetevents.protocol.component.ComponentType;
import com.github.retrooper.packetevents.protocol.component.ComponentTypes;
import com.github.retrooper.packetevents.protocol.component.PatchableComponentMap;
import com.github.retrooper.packetevents.protocol.component.StaticComponentMap;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemEnchantments;
import com.github.retrooper.packetevents.protocol.item.enchantment.Enchantment;
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentType;
import com.github.retrooper.packetevents.protocol.item.enchantment.type.EnchantmentTypes;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
@@ -273,17 +275,15 @@ public class ItemStack {
}
// check for components
Optional<ItemEnchantments> optEnchantments = this.getComponent(ENCHANTMENTS);
Optional<ItemEnchantments> optStoredEnchantments = this.getComponent(STORED_ENCHANTMENTS);
if (optEnchantments.isPresent() || optStoredEnchantments.isPresent()) {
ItemEnchantments enchantments = optEnchantments.orElse(ItemEnchantments.EMPTY);
ItemEnchantments storedEnchantments = optStoredEnchantments.orElse(ItemEnchantments.EMPTY);
ItemEnchantments enchantmentsComp = this.getComponentOr(ENCHANTMENTS, ItemEnchantments.EMPTY);
ItemEnchantments storedEnchantmentsComp = this.getComponentOr(STORED_ENCHANTMENTS, ItemEnchantments.EMPTY);
if (!enchantmentsComp.isEmpty() || !storedEnchantmentsComp.isEmpty()) {
List<Enchantment> enchantmentsList = new ArrayList<>(
enchantments.getEnchantmentCount() + storedEnchantments.getEnchantmentCount());
for (Map.Entry<EnchantmentType, Integer> enchantment : enchantments) {
enchantmentsComp.getEnchantmentCount() + storedEnchantmentsComp.getEnchantmentCount());
for (Map.Entry<EnchantmentType, Integer> enchantment : enchantmentsComp) {
enchantmentsList.add(new Enchantment(enchantment.getKey(), enchantment.getValue()));
}
for (Map.Entry<EnchantmentType, Integer> enchantment : storedEnchantments) {
for (Map.Entry<EnchantmentType, Integer> enchantment : storedEnchantmentsComp) {
enchantmentsList.add(new Enchantment(enchantment.getKey(), enchantment.getValue()));
}
return enchantmentsList;
@@ -320,16 +320,16 @@ public class ItemStack {
}
// check for components
Optional<ItemEnchantments> optEnchantments = this.getComponent(ENCHANTMENTS);
if (optEnchantments.isPresent()) {
int level = optEnchantments.get().getEnchantmentLevel(enchantment);
ItemEnchantments enchantmentsComp = this.getComponentOr(ENCHANTMENTS, ItemEnchantments.EMPTY);
if (!enchantmentsComp.isEmpty()) {
int level = enchantmentsComp.getEnchantmentLevel(enchantment);
if (level > 0) {
return level;
}
}
Optional<ItemEnchantments> optStoredEnchantments = this.getComponent(STORED_ENCHANTMENTS);
if (optStoredEnchantments.isPresent()) {
int level = optStoredEnchantments.get().getEnchantmentLevel(enchantment);
ItemEnchantments storedEnchantmentsComp = this.getComponentOr(STORED_ENCHANTMENTS, ItemEnchantments.EMPTY);
if (!storedEnchantmentsComp.isEmpty()) {
int level = storedEnchantmentsComp.getEnchantmentLevel(enchantment);
if (level > 0) {
return level;
}
@@ -496,11 +496,28 @@ public class ItemStack {
return this;
}
public Builder nbt(String key, NBT tag) {
if (this.nbt == null) {
this.nbt = new NBTCompound();
}
this.nbt.setTag(key, tag);
return this;
}
public Builder components(@Nullable PatchableComponentMap components) {
this.components = components;
return this;
}
public <T> Builder component(ComponentType<T> type, @Nullable T value) {
if (this.components == null) {
this.components = new PatchableComponentMap(this.type == null
? StaticComponentMap.SHARED_ITEM_COMPONENTS : this.type.getComponents());
}
this.components.set(type, value);
return this;
}
public Builder legacyData(int legacyData) {
this.legacyData = legacyData;
return this;
@@ -43,7 +43,7 @@ public class ArmorMaterials {
@Override
public int getId(ClientVersion version) {
return TYPES_BUILDER.getDataIndex(version);
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
@@ -24,6 +24,8 @@ import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -59,7 +61,7 @@ public class BannerPatterns {
@Override
public int getId(ClientVersion version) {
return TYPES_BUILDER.getDataIndex(version);
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
@@ -127,6 +129,18 @@ public class BannerPatterns {
public static final BannerPattern PIGLIN = define("piglin");
public static final BannerPattern DIAGONAL_LEFT = define("diagonal_left");
// Added with 1.21
public static final BannerPattern FLOW = define("flow");
public static final BannerPattern GUSTER = define("guster");
/**
* Returns an immutable view of the banner patterns.
* @return Banner Patterns
*/
public static Collection<BannerPattern> values() {
return Collections.unmodifiableCollection(PATTERN_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -0,0 +1,103 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.item.jukebox;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import java.util.Objects;
public class JukeboxSong {
private Sound sound;
private Component description;
private float lengthInSeconds;
private int comparatorOutput;
public JukeboxSong(Sound sound, Component description, float lengthInSeconds, int comparatorOutput) {
this.sound = sound;
this.description = description;
this.lengthInSeconds = lengthInSeconds;
this.comparatorOutput = comparatorOutput;
}
public static JukeboxSong read(PacketWrapper<?> wrapper) {
Sound sound = Sound.read(wrapper);
Component description = wrapper.readComponent();
float lengthInSeconds = wrapper.readFloat();
int comparatorOutput = wrapper.readVarInt();
return new JukeboxSong(sound, description, lengthInSeconds, comparatorOutput);
}
public static void write(PacketWrapper<?> wrapper, JukeboxSong song) {
Sound.write(wrapper, song.sound);
wrapper.writeComponent(song.description);
wrapper.writeFloat(song.lengthInSeconds);
wrapper.writeVarInt(song.comparatorOutput);
}
public Sound getSound() {
return this.sound;
}
public void setSound(Sound sound) {
this.sound = sound;
}
public Component getDescription() {
return this.description;
}
public void setDescription(Component description) {
this.description = description;
}
public float getLengthInSeconds() {
return this.lengthInSeconds;
}
public void setLengthInSeconds(float lengthInSeconds) {
this.lengthInSeconds = lengthInSeconds;
}
public int getComparatorOutput() {
return this.comparatorOutput;
}
public void setComparatorOutput(int comparatorOutput) {
this.comparatorOutput = comparatorOutput;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof JukeboxSong)) return false;
JukeboxSong that = (JukeboxSong) obj;
if (Float.compare(that.lengthInSeconds, this.lengthInSeconds) != 0) return false;
if (this.comparatorOutput != that.comparatorOutput) return false;
if (!this.sound.equals(that.sound)) return false;
return this.description.equals(that.description);
}
@Override
public int hashCode() {
return Objects.hash(this.sound, this.description, this.lengthInSeconds, this.comparatorOutput);
}
}
@@ -25,6 +25,8 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -117,6 +119,10 @@ public class MapDecorationTypes {
}
public static @Nullable MapDecorationType getById(int id, ClientVersion version) {
return getById(version, id);
}
public static @Nullable MapDecorationType getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, MapDecorationType> idMap = DECORATION_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
@@ -201,6 +207,14 @@ public class MapDecorationTypes {
public static final MapDecorationType TRIAL_CHAMBERS = define("trial_chambers",
true, COPPER_COLOR, true, false);
/**
* Returns an immutable view of the map decoration types.
* @return Map Decoration Types
*/
public static Collection<MapDecorationType> values() {
return Collections.unmodifiableCollection(DECORATION_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -29,6 +29,8 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -37,8 +39,8 @@ import static net.kyori.adventure.text.format.TextColor.color;
public class TrimMaterials {
private static final Map<String, TrimMaterial> PATTERN_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, TrimMaterial>> PATTERN_TYPE_ID_MAP = new HashMap<>();
private static final Map<String, TrimMaterial> TRIM_MATERIAL_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, TrimMaterial>> TRIM_MATERIAL_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_trim_material_mappings");
public static TrimMaterial define(String key, ItemType ingredient, float itemModelIndex, int color) {
@@ -59,7 +61,7 @@ public class TrimMaterials {
Map<ArmorMaterial, String> overrideArmorMaterials, Component description
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
TrimMaterial pattern = new TrimMaterial() {
TrimMaterial material = new TrimMaterial() {
@Override
public String getAssetName() {
return assetName;
@@ -92,7 +94,7 @@ public class TrimMaterials {
@Override
public int getId(ClientVersion version) {
return TYPES_BUILDER.getDataIndex(version);
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
@@ -103,18 +105,18 @@ public class TrimMaterials {
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PATTERN_TYPE_MAP, PATTERN_TYPE_ID_MAP, pattern);
return pattern;
MappingHelper.registerMapping(TYPES_BUILDER, TRIM_MATERIAL_MAP, TRIM_MATERIAL_ID_MAP, material);
return material;
}
// with key
public static TrimMaterial getByName(String name) {
return PATTERN_TYPE_MAP.get(name);
return TRIM_MATERIAL_MAP.get(name);
}
public static TrimMaterial getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, TrimMaterial> idMap = PATTERN_TYPE_ID_MAP.get((byte) index);
Map<Integer, TrimMaterial> idMap = TRIM_MATERIAL_ID_MAP.get((byte) index);
return idMap.get(id);
}
@@ -130,6 +132,14 @@ public class TrimMaterials {
public static final TrimMaterial QUARTZ = define("quartz", ItemTypes.QUARTZ, 0.1f, 0xE3D4C4);
public static final TrimMaterial REDSTONE = define("redstone", ItemTypes.REDSTONE, 0.4f, 0x971607);
/**
* Returns an immutable view of the trim materials.
* @return Trim Materials
*/
public static Collection<TrimMaterial> values() {
return Collections.unmodifiableCollection(TRIM_MATERIAL_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -77,7 +77,7 @@ public class TrimPatterns {
@Override
public int getId(ClientVersion version) {
return TYPES_BUILDER.getDataIndex(version);
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
@@ -1385,14 +1385,14 @@ public class ItemTypes {
public static final ItemType WAXED_EXPOSED_CHISELED_COPPER = builder("waxed_exposed_chiseled_copper").setMaxAmount(64).setPlacedType(StateTypes.WAXED_EXPOSED_CHISELED_COPPER).build();
public static final ItemType WAXED_WEATHERED_CHISELED_COPPER = builder("waxed_weathered_chiseled_copper").setMaxAmount(64).setPlacedType(StateTypes.WAXED_WEATHERED_CHISELED_COPPER).build();
public static final ItemType WAXED_OXIDIZED_CHISELED_COPPER = builder("waxed_oxidized_chiseled_copper").setMaxAmount(64).setPlacedType(StateTypes.WAXED_OXIDIZED_CHISELED_COPPER).build();
public static final ItemType COPPER_DOOR = builder("copper_door").setMaxAmount(64).setPlacedType(StateTypes.COPPER_DOOR).build();
public static final ItemType EXPOSED_COPPER_DOOR = builder("exposed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.EXPOSED_COPPER_DOOR).build();
public static final ItemType WEATHERED_COPPER_DOOR = builder("weathered_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WEATHERED_COPPER_DOOR).build();
public static final ItemType OXIDIZED_COPPER_DOOR = builder("oxidized_copper_door").setMaxAmount(64).setPlacedType(StateTypes.OXIDIZED_COPPER_DOOR).build();
public static final ItemType WAXED_COPPER_DOOR = builder("waxed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_COPPER_DOOR).build();
public static final ItemType WAXED_EXPOSED_COPPER_DOOR = builder("waxed_exposed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_EXPOSED_COPPER_DOOR).build();
public static final ItemType WAXED_WEATHERED_COPPER_DOOR = builder("waxed_weathered_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_WEATHERED_COPPER_DOOR).build();
public static final ItemType WAXED_OXIDIZED_COPPER_DOOR = builder("waxed_oxidized_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_OXIDIZED_COPPER_DOOR).build();
public static final ItemType COPPER_DOOR = builder("copper_door").setMaxAmount(64).setPlacedType(StateTypes.COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType EXPOSED_COPPER_DOOR = builder("exposed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.EXPOSED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType WEATHERED_COPPER_DOOR = builder("weathered_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WEATHERED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType OXIDIZED_COPPER_DOOR = builder("oxidized_copper_door").setMaxAmount(64).setPlacedType(StateTypes.OXIDIZED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType WAXED_COPPER_DOOR = builder("waxed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType WAXED_EXPOSED_COPPER_DOOR = builder("waxed_exposed_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_EXPOSED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType WAXED_WEATHERED_COPPER_DOOR = builder("waxed_weathered_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_WEATHERED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType WAXED_OXIDIZED_COPPER_DOOR = builder("waxed_oxidized_copper_door").setMaxAmount(64).setPlacedType(StateTypes.WAXED_OXIDIZED_COPPER_DOOR).setAttributes(ItemAttribute.FUEL).build();
public static final ItemType COPPER_TRAPDOOR = builder("copper_trapdoor").setMaxAmount(64).setPlacedType(StateTypes.COPPER_TRAPDOOR).build();
public static final ItemType EXPOSED_COPPER_TRAPDOOR = builder("exposed_copper_trapdoor").setMaxAmount(64).setPlacedType(StateTypes.EXPOSED_COPPER_TRAPDOOR).build();
public static final ItemType WEATHERED_COPPER_TRAPDOOR = builder("weathered_copper_trapdoor").setMaxAmount(64).setPlacedType(StateTypes.WEATHERED_COPPER_TRAPDOOR).build();
@@ -1429,7 +1429,7 @@ public class ItemTypes {
public static final ItemType HEAVY_CORE = builder("heavy_core").setMaxAmount(64).setPlacedType(StateTypes.HEAVY_CORE).build();
public static final ItemType BOGGED_SPAWN_EGG = builder("bogged_spawn_egg").setMaxAmount(64).build();
public static final ItemType WIND_CHARGE = builder("wind_charge").setMaxAmount(64).build();
public static final ItemType MACE = builder("mace").setMaxAmount(1).setMaxDurability(250).setComponent(DAMAGE, 0).setComponent(ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers(Arrays.asList(new ItemAttributeModifiers.ModifierEntry(Attributes.GENERIC_ATTACK_DAMAGE, new ItemAttributeModifiers.Modifier(TOOL_MODIFIER_ATTACK_DAMAGE_UUID, "Weapon modifier", 6.0d, AttributeOperation.ADDITION), ItemAttributeModifiers.EquipmentSlotGroup.MAINHAND), new ItemAttributeModifiers.ModifierEntry(Attributes.GENERIC_ATTACK_SPEED, new ItemAttributeModifiers.Modifier(TOOL_MODIFIER_ATTACK_SPEED_UUID, "Weapon modifier", -2.4d, AttributeOperation.ADDITION), ItemAttributeModifiers.EquipmentSlotGroup.MAINHAND)), true)).setComponent(TOOL, new ItemTool(Collections.emptyList(), 1.0f, 2)).build();
public static final ItemType MACE = builder("mace").setMaxAmount(1).setMaxDurability(500).setComponent(DAMAGE, 0).setComponent(ATTRIBUTE_MODIFIERS, new ItemAttributeModifiers(Arrays.asList(new ItemAttributeModifiers.ModifierEntry(Attributes.GENERIC_ATTACK_DAMAGE, new ItemAttributeModifiers.Modifier(TOOL_MODIFIER_ATTACK_DAMAGE_UUID, "Weapon modifier", 5.0d, AttributeOperation.ADDITION), ItemAttributeModifiers.EquipmentSlotGroup.MAINHAND), new ItemAttributeModifiers.ModifierEntry(Attributes.GENERIC_ATTACK_SPEED, new ItemAttributeModifiers.Modifier(TOOL_MODIFIER_ATTACK_SPEED_UUID, "Weapon modifier", -3.5d, AttributeOperation.ADDITION), ItemAttributeModifiers.EquipmentSlotGroup.MAINHAND)), true)).setComponent(TOOL, new ItemTool(Collections.emptyList(), 1.0f, 2)).build();
public static final ItemType FLOW_BANNER_PATTERN = builder("flow_banner_pattern").setMaxAmount(1).build();
public static final ItemType GUSTER_BANNER_PATTERN = builder("guster_banner_pattern").setMaxAmount(1).build();
public static final ItemType FLOW_ARMOR_TRIM_SMITHING_TEMPLATE = builder("flow_armor_trim_smithing_template").setMaxAmount(64).build();
@@ -1442,6 +1442,11 @@ public class ItemTypes {
public static final ItemType OMINOUS_BOTTLE = builder("ominous_bottle").setMaxAmount(64).setAttributes(ItemAttribute.EDIBLE).setComponent(FOOD, new FoodProperties(1, 0.2f, false, 1.6f, Collections.emptyList())).setComponent(OMINOUS_BOTTLE_AMPLIFIER, 0).build();
public static final ItemType BREEZE_ROD = builder("breeze_rod").setMaxAmount(64).build();
// 1.21 items
public static final ItemType MUSIC_DISC_CREATOR = builder("music_disc_creator").setMaxAmount(1).setAttributes(ItemAttribute.MUSIC_DISC).setComponent(RARITY, ItemRarity.RARE).build();
public static final ItemType MUSIC_DISC_CREATOR_MUSIC_BOX = builder("music_disc_creator_music_box").setMaxAmount(1).setAttributes(ItemAttribute.MUSIC_DISC).setComponent(RARITY, ItemRarity.RARE).build();
public static final ItemType MUSIC_DISC_PRECIPICE = builder("music_disc_precipice").setMaxAmount(1).setAttributes(ItemAttribute.MUSIC_DISC).setComponent(RARITY, ItemRarity.RARE).build();
/**
* @deprecated Burning furnace shows up as a missing texture, removed in 1.9
*/
@@ -0,0 +1,32 @@
package com.github.retrooper.packetevents.protocol.nbt;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class NBTLimiter {
private final int max;
@Nullable
private Object byteBuf;
private int bytes;
public NBTLimiter() {
this.max = Integer.MAX_VALUE;
}
public NBTLimiter(@NotNull Object byteBuf) {
this.byteBuf = byteBuf;
this.max = 2097152;
}
public void increment(int amount) {
bytes += amount;
if (bytes > max) throw new IllegalArgumentException("NBT size limit reached (" + bytes + "/" + max + ")");
}
public void checkReadability(int length) {
if(byteBuf != null && length > ByteBufHelper.readableBytes(byteBuf)) throw new IllegalArgumentException("Length is too large: " + length + ", readable: " + ByteBufHelper.readableBytes(byteBuf));
}
}
@@ -27,14 +27,21 @@ import java.util.Objects;
public class NBTList<T extends NBT> extends NBT {
protected final NBTType<T> type;
protected final List<T> tags = new ArrayList<>();
protected final List<T> tags;
public NBTList(NBTType<T> type) {
this.type = type;
this.tags = new ArrayList<>();
}
public NBTList(NBTType<T> type, int size) {
this.type = type;
this.tags = new ArrayList<>(size);
}
public NBTList(NBTType<T> type, List<T> tags) {
this.type = type;
this.tags = new ArrayList<>();
this.tags.addAll(tags);
}
@@ -37,6 +37,7 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class NBTCodec {
//PacketEvents start: JSON -> NBT conversion method
@Deprecated
public static NBT jsonToNBT(JsonElement element) {
@@ -149,11 +150,12 @@ public class NBTCodec {
//PacketEvents end
public static NBT readNBTFromBuffer(Object byteBuf, ServerVersion serverVersion) {
NBTLimiter limiter = new NBTLimiter(byteBuf);
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
try {
final boolean named = serverVersion.isOlderThan(ServerVersion.V_1_20_2);
return DefaultNBTSerializer.INSTANCE.deserializeTag(
new ByteBufInputStream(byteBuf), named);
limiter, new ByteBufInputStream(byteBuf), named);
} catch (IOException e) {
e.printStackTrace();
}
@@ -167,7 +169,7 @@ public class NBTCodec {
Object slicedBuffer = ByteBufHelper.readSlice(byteBuf, length);
try (DataInputStream stream = new DataInputStream(
new GZIPInputStream(new ByteBufInputStream(slicedBuffer)))) {
return DefaultNBTSerializer.INSTANCE.deserializeTag(stream);
return DefaultNBTSerializer.INSTANCE.deserializeTag(limiter, stream);
}
}
catch (IOException ex) {
@@ -31,20 +31,61 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
@SuppressWarnings("unchecked")
public DefaultNBTSerializer() {
super(DataInput::readByte, DataOutput::writeByte, DataInput::readUTF, DataOutput::writeUTF);
registerType(NBTType.END, 0, stream -> NBTEnd.INSTANCE, (stream, tag) -> {
super(
(limiter, dataInput) -> {
limiter.increment(1);
return dataInput.readByte();
},
DataOutput::writeByte,
(limiter, dataInput) -> {
String name = dataInput.readUTF();
limiter.increment(name.length() * 2 + 28);
return name;
},
DataOutput::writeUTF
);
registerType(NBTType.END, 0, (limiter, stream) -> {
limiter.increment(8);
return NBTEnd.INSTANCE;
}, (stream, tag) -> {
});
registerType(NBTType.BYTE, 1, stream -> new NBTByte(stream.readByte()), (stream, tag) -> stream.writeByte(tag.getAsByte()));
registerType(NBTType.SHORT, 2, stream -> new NBTShort(stream.readShort()), (stream, tag) -> stream.writeShort(tag.getAsShort()));
registerType(NBTType.INT, 3, stream -> new NBTInt(stream.readInt()), (stream, tag) -> stream.writeInt(tag.getAsInt()));
registerType(NBTType.LONG, 4, stream -> new NBTLong(stream.readLong()), (stream, tag) -> stream.writeLong(tag.getAsLong()));
registerType(NBTType.FLOAT, 5, stream -> new NBTFloat(stream.readFloat()), (stream, tag) -> stream.writeFloat(tag.getAsFloat()));
registerType(NBTType.DOUBLE, 6, stream -> new NBTDouble(stream.readDouble()), (stream, tag) -> stream.writeDouble(tag.getAsDouble()));
registerType(NBTType.STRING, 8, stream -> new NBTString(stream.readUTF()), (stream, tag) -> stream.writeUTF(tag.getValue()));
registerType(NBTType.BYTE, 1, (limiter, stream) -> {
limiter.increment(9);
return new NBTByte(stream.readByte());
}, (stream, tag) -> stream.writeByte(tag.getAsByte()));
registerType(NBTType.SHORT, 2, (limiter, stream) -> {
limiter.increment(10);
return new NBTShort(stream.readShort());
}, (stream, tag) -> stream.writeShort(tag.getAsShort()));
registerType(NBTType.INT, 3, (limiter, stream) -> {
limiter.increment(12);
return new NBTInt(stream.readInt());
}, (stream, tag) -> stream.writeInt(tag.getAsInt()));
registerType(NBTType.LONG, 4, (limiter, stream) -> {
limiter.increment(16);
return new NBTLong(stream.readLong());
}, (stream, tag) -> stream.writeLong(tag.getAsLong()));
registerType(NBTType.FLOAT, 5, (limiter, stream) -> {
limiter.increment(12);
return new NBTFloat(stream.readFloat());
}, (stream, tag) -> stream.writeFloat(tag.getAsFloat()));
registerType(NBTType.DOUBLE, 6, (limiter, stream) -> {
limiter.increment(16);
return new NBTDouble(stream.readDouble());
}, (stream, tag) -> stream.writeDouble(tag.getAsDouble()));
registerType(
NBTType.BYTE_ARRAY, 7,
stream -> {
byte[] array = new byte[stream.readInt()];
(limiter, stream) -> {
limiter.increment(24);
int length = stream.readInt();
if (length >= 1 << 24)
throw new IllegalArgumentException("Byte array length is too large: " + length);
limiter.checkReadability(length);
limiter.increment(length);
byte[] array = new byte[length];
stream.readFully(array);
return new NBTByteArray(array);
},
@@ -54,10 +95,76 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
stream.write(array);
}
);
registerType(NBTType.STRING, 8, (limiter, stream) -> {
limiter.increment(36);
String string = stream.readUTF();
limiter.increment(string.length() * 2);
return new NBTString(string);
}, (stream, tag) -> stream.writeUTF(tag.getValue()));
registerType(
NBTType.LIST, 9,
(limiter, stream) -> {
limiter.increment(37);
NBTType<? extends NBT> valueType = readTagType(limiter, stream);
int size = stream.readInt();
if ((valueType == NBTType.END) && (size > 0)) {
throw new IllegalStateException("Missing nbt list values tag type");
}
limiter.increment(4 * size);
NBTList<NBT> list = new NBTList<>((NBTType<NBT>) valueType, size);
for (int i = 0; i < size; i++) {
list.addTag(readTag(limiter, stream, valueType));
}
return list;
},
(stream, tag) -> {
writeTagType(stream, tag.getTagsType());
stream.writeInt(tag.size());
for (NBT value : ((List<NBT>) tag.getTags())) {
writeTag(stream, value);
}
}
);
registerType(
NBTType.COMPOUND, 10,
(limiter, stream) -> {
limiter.increment(48);
NBTCompound compound = new NBTCompound();
NBTType<?> valueType;
while ((valueType = readTagType(limiter, stream)) != NBTType.END) {
String name = readTagName(limiter, stream);
NBT nbt = readTag(limiter, stream, valueType);
if(!compound.getTags().containsKey(name)) limiter.increment(36);
compound.setTag(name, nbt);
}
return compound;
},
(stream, tag) -> {
for (Entry<String, NBT> entry : tag.getTags().entrySet()) {
NBT value = entry.getValue();
writeTagType(stream, value.getType());
writeTagName(stream, entry.getKey());
writeTag(stream, value);
}
writeTagType(stream, NBTType.END);
}
);
registerType(
NBTType.INT_ARRAY, 11,
stream -> {
int[] array = new int[stream.readInt()];
(limiter, stream) -> {
limiter.increment(24);
int length = stream.readInt();
if (length >= 1 << 24)
throw new IllegalArgumentException("Int array length is too large: " + length);
limiter.checkReadability(length * 4);
limiter.increment(length * 4);
int[] array = new int[length];
for (int i = 0; i < array.length; i++) {
array[i] = stream.readInt();
}
@@ -73,8 +180,17 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
);
registerType(
NBTType.LONG_ARRAY, 12,
stream -> {
long[] array = new long[stream.readInt()];
(limiter, stream) -> {
limiter.increment(24);
int length = stream.readInt();
if (length >= 1 << 24)
throw new IllegalArgumentException("Long array length is too large: " + length);
limiter.checkReadability(length * 8);
limiter.increment(length * 8);
long[] array = new long[length];
for (int i = 0; i < array.length; i++) {
array[i] = stream.readLong();
}
@@ -88,48 +204,6 @@ public class DefaultNBTSerializer extends NBTSerializer<DataInput, DataOutput> {
}
}
);
registerType(
NBTType.COMPOUND, 10,
stream -> {
NBTCompound compound = new NBTCompound();
NBTType<?> valueType = null;
while ((valueType = readTagType(stream)) != NBTType.END) {
compound.setTag(readTagName(stream), readTag(stream, valueType));
}
return compound;
},
(stream, tag) -> {
for (Entry<String, NBT> entry : tag.getTags().entrySet()) {
NBT value = entry.getValue();
writeTagType(stream, value.getType());
writeTagName(stream, entry.getKey());
writeTag(stream, value);
}
writeTagType(stream, NBTType.END);
}
);
registerType(
NBTType.LIST, 9,
stream -> {
NBTType<? extends NBT> valueType = readTagType(stream);
int size = stream.readInt();
if ((valueType == NBTType.END) && (size > 0)) {
throw new IllegalStateException("Missing nbt list values tag type");
}
NBTList<NBT> list = new NBTList<>((NBTType<NBT>) valueType);
for (int i = 0; i < size; i++) {
list.addTag(readTag(stream, valueType));
}
return list;
},
(stream, tag) -> {
writeTagType(stream, tag.getTagsType());
stream.writeInt(tag.size());
for (NBT value : ((List<NBT>) tag.getTags())) {
writeTag(stream, value);
}
}
);
}
}
@@ -19,6 +19,7 @@
package com.github.retrooper.packetevents.protocol.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import java.io.IOException;
@@ -46,19 +47,19 @@ public class NBTSerializer<IN, OUT> {
this.nameWriter = nameWriter;
}
public NBT deserializeTag(IN from) throws IOException {
return deserializeTag(from, true);
public NBT deserializeTag(NBTLimiter limiter, IN from) throws IOException {
return deserializeTag(limiter, from, true);
}
public NBT deserializeTag(IN from, boolean named) throws IOException {
NBTType<?> type = readTagType(from);
public NBT deserializeTag(NBTLimiter limiter, IN from, boolean named) throws IOException {
NBTType<?> type = readTagType(limiter, from);
if (type == NBTType.END) {
return null;
}
if (named) {
readTagName(from);
readTagName(limiter, from);
}
return readTag(from, type);
return readTag(limiter, from, type);
}
public void serializeTag(OUT to, NBT tag) throws IOException {
@@ -94,8 +95,8 @@ public class NBTSerializer<IN, OUT> {
typeWriters.put(type, typeWriter);
}
protected NBTType<?> readTagType(IN from) throws IOException {
int id = idReader.readId(from);
protected NBTType<?> readTagType(NBTLimiter limiter, IN from) throws IOException {
int id = idReader.readId(limiter, from);
NBTType<?> type = idToType.get(id);
if (type == null) {
throw new IOException(MessageFormat.format("Unknown nbt type id {0}", id));
@@ -103,16 +104,16 @@ public class NBTSerializer<IN, OUT> {
return type;
}
protected String readTagName(IN from) throws IOException {
return nameReader.readName(from);
protected String readTagName(NBTLimiter limiter, IN from) throws IOException {
return nameReader.readName(limiter, from);
}
protected NBT readTag(IN from, NBTType<?> type) throws IOException {
protected NBT readTag(NBTLimiter limiter, IN from, NBTType<?> type) throws IOException {
TagReader<IN, ? extends NBT> f = typeReaders.get(type);
if (f == null) {
throw new IOException(MessageFormat.format("No reader registered for nbt type {0}", type));
}
return f.readTag(from);
return f.readTag(limiter, from);
}
protected void writeTagType(OUT stream, NBTType<?> type) throws IOException {
@@ -138,7 +139,7 @@ public class NBTSerializer<IN, OUT> {
@FunctionalInterface
protected interface IdReader<T> {
int readId(T from) throws IOException;
int readId(NBTLimiter limiter, T from) throws IOException;
}
@FunctionalInterface
@@ -148,7 +149,7 @@ public class NBTSerializer<IN, OUT> {
@FunctionalInterface
protected interface NameReader<T> {
String readName(T from) throws IOException;
String readName(NBTLimiter limiter, T from) throws IOException;
}
@FunctionalInterface
@@ -158,7 +159,7 @@ public class NBTSerializer<IN, OUT> {
@FunctionalInterface
protected interface TagReader<IN, T extends NBT> {
T readTag(IN from) throws IOException;
T readTag(NBTLimiter limiter, IN from) throws IOException;
}
@FunctionalInterface
@@ -120,6 +120,7 @@ public class NPC {
WrapperPlayServerDestroyEntities destroyEntities = new WrapperPlayServerDestroyEntities(getId());
PacketEvents.getAPI().getProtocolManager().sendPacket(channel, destroyEntities);
}
channels.clear();
}
public void teleport(Location to) {
@@ -255,9 +256,16 @@ public class NPC {
}
PacketEvents.getAPI().getProtocolManager().sendPacket(channel, playerInfoAdd);
WrapperPlayServerSpawnPlayer spawnPlayer =
new WrapperPlayServerSpawnPlayer(getId(), getProfile().getUUID(), getLocation());
PacketEvents.getAPI().getProtocolManager().sendPacket(channel, spawnPlayer);
PacketWrapper<?> spawnPacket;
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_2)) {
spawnPacket = new WrapperPlayServerSpawnEntity(getId(), getProfile().getUUID(), EntityTypes.PLAYER, getLocation(), getLocation().getYaw(), 0, null);
}
else {
spawnPacket = new WrapperPlayServerSpawnPlayer(getId(),
getProfile().getUUID(),
getLocation());
}
PacketEvents.getAPI().getProtocolManager().sendPacket(channel, spawnPacket);
}
}
@@ -38,6 +38,7 @@ import com.github.retrooper.packetevents.protocol.packettype.clientbound.Clientb
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_20_2;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_20_3;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_20_5;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_21;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_7_10;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_8;
import com.github.retrooper.packetevents.protocol.packettype.clientbound.ClientboundPacketType_1_9;
@@ -45,6 +46,7 @@ import com.github.retrooper.packetevents.protocol.packettype.clientbound.Clientb
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_20_2;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_20_3;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_20_5;
import com.github.retrooper.packetevents.protocol.packettype.config.clientbound.ClientboundConfigPacketType_1_21;
import com.github.retrooper.packetevents.protocol.packettype.config.serverbound.ServerboundConfigPacketType_1_20_2;
import com.github.retrooper.packetevents.protocol.packettype.config.serverbound.ServerboundConfigPacketType_1_20_5;
import com.github.retrooper.packetevents.protocol.packettype.serverbound.ServerboundPacketType_1_12;
@@ -100,7 +102,8 @@ public final class PacketType {
ClientVersion.V_1_19_4,
ClientVersion.V_1_20_2,
ClientVersion.V_1_20_3,
ClientVersion.V_1_20_5);
ClientVersion.V_1_20_5,
ClientVersion.V_1_21);
//TODO UPDATE Update packet type mappings (serverbound pt. 1)
private static final VersionMapper SERVERBOUND_PLAY_VERSION_MAPPER = new VersionMapper(
@@ -127,7 +130,8 @@ public final class PacketType {
private static final VersionMapper CLIENTBOUND_CONFIG_VERSION_MAPPER = new VersionMapper(
ClientVersion.V_1_20_2,
ClientVersion.V_1_20_3,
ClientVersion.V_1_20_5);
ClientVersion.V_1_20_5,
ClientVersion.V_1_21);
// TODO UPDATE Update packet type mappings (config serverbound pt. 1)
private static final VersionMapper SERVERBOUND_CONFIG_VERSION_MAPPER = new VersionMapper(
ClientVersion.V_1_20_2,
@@ -512,7 +516,11 @@ public final class PacketType {
RESET_CHAT,
STORE_COOKIE,
TRANSFER,
SELECT_KNOWN_PACKS;
SELECT_KNOWN_PACKS,
// added in 1.21
CUSTOM_REPORT_DETAILS,
SERVER_LINKS;
private static int INDEX = 0;
private static final Map<Byte, Map<Integer, PacketTypeCommon>> PACKET_TYPE_ID_MAP = new HashMap<>();
@@ -528,6 +536,7 @@ public final class PacketType {
loadPacketIds(ClientboundConfigPacketType_1_20_2.values());
loadPacketIds(ClientboundConfigPacketType_1_20_3.values());
loadPacketIds(ClientboundConfigPacketType_1_20_5.values());
loadPacketIds(ClientboundConfigPacketType_1_21.values());
// TODO UPDATE Update packet type mappings (config clientbound pt. 2)
}
@@ -888,7 +897,11 @@ public final class PacketType {
DEBUG_SAMPLE,
STORE_COOKIE,
TRANSFER,
PROJECTILE_POWER;
PROJECTILE_POWER,
// added in 1.21
CUSTOM_REPORT_DETAILS,
SERVER_LINKS;
private static int INDEX = 0;
private static final Map<Byte, Map<Integer, PacketTypeCommon>> PACKET_TYPE_ID_MAP = new HashMap<>();
@@ -958,6 +971,7 @@ public final class PacketType {
loadPacketIds(ClientboundPacketType_1_20_2.values());
loadPacketIds(ClientboundPacketType_1_20_3.values());
loadPacketIds(ClientboundPacketType_1_20_5.values());
loadPacketIds(ClientboundPacketType_1_21.values());
//TODO UPDATE Update packet type mappings (clientbound pt. 2)
}
}
@@ -0,0 +1,147 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.packettype.clientbound;
public enum ClientboundPacketType_1_21 {
BUNDLE,
SPAWN_ENTITY,
SPAWN_EXPERIENCE_ORB,
ENTITY_ANIMATION,
STATISTICS,
ACKNOWLEDGE_BLOCK_CHANGES,
BLOCK_BREAK_ANIMATION,
BLOCK_ENTITY_DATA,
BLOCK_ACTION,
BLOCK_CHANGE,
BOSS_BAR,
SERVER_DIFFICULTY,
CHUNK_BATCH_END,
CHUNK_BATCH_BEGIN,
CHUNK_BIOMES,
CLEAR_TITLES,
TAB_COMPLETE,
DECLARE_COMMANDS,
CLOSE_WINDOW,
WINDOW_ITEMS,
WINDOW_PROPERTY,
SET_SLOT,
COOKIE_REQUEST,
SET_COOLDOWN,
CUSTOM_CHAT_COMPLETIONS,
PLUGIN_MESSAGE,
DAMAGE_EVENT,
DEBUG_SAMPLE,
DELETE_CHAT,
DISCONNECT,
DISGUISED_CHAT,
ENTITY_STATUS,
EXPLOSION,
UNLOAD_CHUNK,
CHANGE_GAME_STATE,
OPEN_HORSE_WINDOW,
HURT_ANIMATION,
INITIALIZE_WORLD_BORDER,
KEEP_ALIVE,
CHUNK_DATA,
EFFECT,
PARTICLE,
UPDATE_LIGHT,
JOIN_GAME,
MAP_DATA,
MERCHANT_OFFERS,
ENTITY_RELATIVE_MOVE,
ENTITY_RELATIVE_MOVE_AND_ROTATION,
ENTITY_ROTATION,
VEHICLE_MOVE,
OPEN_BOOK,
OPEN_WINDOW,
OPEN_SIGN_EDITOR,
PING,
DEBUG_PONG,
CRAFT_RECIPE_RESPONSE,
PLAYER_ABILITIES,
CHAT_MESSAGE,
END_COMBAT_EVENT,
ENTER_COMBAT_EVENT,
DEATH_COMBAT_EVENT,
PLAYER_INFO_REMOVE,
PLAYER_INFO_UPDATE,
FACE_PLAYER,
PLAYER_POSITION_AND_LOOK,
UNLOCK_RECIPES,
DESTROY_ENTITIES,
REMOVE_ENTITY_EFFECT,
RESET_SCORE,
RESOURCE_PACK_REMOVE,
RESOURCE_PACK_SEND,
RESPAWN,
ENTITY_HEAD_LOOK,
MULTI_BLOCK_CHANGE,
SELECT_ADVANCEMENTS_TAB,
SERVER_DATA,
ACTION_BAR,
WORLD_BORDER_CENTER,
WORLD_BORDER_LERP_SIZE,
WORLD_BORDER_SIZE,
WORLD_BORDER_WARNING_DELAY,
WORLD_BORDER_WARNING_REACH,
CAMERA,
HELD_ITEM_CHANGE,
UPDATE_VIEW_POSITION,
UPDATE_VIEW_DISTANCE,
SPAWN_POSITION,
DISPLAY_SCOREBOARD,
ENTITY_METADATA,
ATTACH_ENTITY,
ENTITY_VELOCITY,
ENTITY_EQUIPMENT,
SET_EXPERIENCE,
UPDATE_HEALTH,
SCOREBOARD_OBJECTIVE,
SET_PASSENGERS,
TEAMS,
UPDATE_SCORE,
UPDATE_SIMULATION_DISTANCE,
SET_TITLE_SUBTITLE,
TIME_UPDATE,
SET_TITLE_TEXT,
SET_TITLE_TIMES,
ENTITY_SOUND_EFFECT,
SOUND_EFFECT,
CONFIGURATION_START,
STOP_SOUND,
STORE_COOKIE,
SYSTEM_CHAT_MESSAGE,
PLAYER_LIST_HEADER_AND_FOOTER,
NBT_QUERY_RESPONSE,
COLLECT_ITEM,
ENTITY_TELEPORT,
TICKING_STATE,
TICKING_STEP,
TRANSFER,
UPDATE_ADVANCEMENTS,
UPDATE_ATTRIBUTES,
ENTITY_EFFECT,
DECLARE_RECIPES,
TAGS,
PROJECTILE_POWER,
CUSTOM_REPORT_DETAILS, // new packet
SERVER_LINKS, // new packet
}
@@ -0,0 +1,40 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.packettype.config.clientbound;
public enum ClientboundConfigPacketType_1_21 {
COOKIE_REQUEST,
PLUGIN_MESSAGE,
DISCONNECT,
CONFIGURATION_END,
KEEP_ALIVE,
PING,
RESET_CHAT,
REGISTRY_DATA,
RESOURCE_PACK_REMOVE,
RESOURCE_PACK_SEND,
STORE_COOKIE,
TRANSFER,
UPDATE_ENABLED_FEATURES,
UPDATE_TAGS,
SELECT_KNOWN_PACKS,
CUSTOM_REPORT_DETAILS, // new packet
SERVER_LINKS, // new packet
}
@@ -54,7 +54,7 @@ public class ParticleDustColorTransitionData extends ParticleData {
}
public ParticleDustColorTransitionData(float scale, Color start, Color end) {
this(scale, start.red(), start.green(), start.blue(), end.red(), end.green(), end.blue());
this(scale, start.red() / 255f, start.green() / 255f, start.blue() / 255f, end.red() / 255f, end.green() / 255f, end.blue() / 255f);
}
public float getScale() {
@@ -43,7 +43,7 @@ public class ParticleDustData extends ParticleData {
}
public ParticleDustData(float scale, Color color) {
this(scale, color.red(), color.green(), color.blue());
this(scale, color.red() / 255f, color.green() / 255f, color.blue() / 255f);
}
public float getRed() {
@@ -35,6 +35,8 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -227,6 +229,14 @@ public class ParticleTypes {
public static final ParticleType<ParticleData> RAID_OMEN = define("raid_omen");
public static final ParticleType<ParticleData> TRIAL_OMEN = define("trial_omen");
/**
* Returns an immutable view of the particle types.
* @return Particle Types
*/
public static Collection<ParticleType<?>> values() {
return Collections.unmodifiableCollection(PARTICLE_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -95,13 +95,15 @@ public enum ClientVersion {
* 1.20.5 and 1.20.6 have the same protocol version.
*/
V_1_20_5(766),
V_1_21(767),
//TODO UPDATE Add new protocol version field
@Deprecated
LOWER_THAN_SUPPORTED_VERSIONS(V_1_7_10.protocolVersion - 1, true),
//TODO UPDATE Update HIGHER_THAN_SUPPORTED_VERSIONS field
@Deprecated
HIGHER_THAN_SUPPORTED_VERSIONS(V_1_20_5.protocolVersion + 1, true),
HIGHER_THAN_SUPPORTED_VERSIONS(V_1_21.protocolVersion + 1, true),
UNKNOWN(-1, true);
@@ -172,9 +174,9 @@ public enum ClientVersion {
@NotNull
public static ClientVersion getById(int protocolVersion) {
if (protocolVersion < LOWEST_SUPPORTED_PROTOCOL_VERSION) {
return V_1_7_10;
return getOldest();
} else if (protocolVersion > HIGHEST_SUPPORTED_PROTOCOL_VERSION) {
return V_1_20_3;
return getLatest();
} else {
for (ClientVersion version : VALUES) {
if (version.protocolVersion > protocolVersion) {
@@ -27,33 +27,45 @@ public enum EquipmentSlot {
BOOTS(1),
LEGGINGS(2),
CHEST_PLATE(3),
HELMET(4);
HELMET(4),
BODY(0);
private static final EquipmentSlot[] VALUES = values();
private final byte legacyId;
EquipmentSlot(int legacyId) {
this.legacyId =(byte) legacyId;
this.legacyId = (byte) legacyId;
}
public int getId(ServerVersion version) {
if (version.isOlderThan(ServerVersion.V_1_9)) {
return getId(version.toClientVersion());
}
public int getId(ClientVersion version) {
if (version.isOlderThan(ClientVersion.V_1_9)) {
return legacyId;
}
else {
} else {
return ordinal();
}
}
@Nullable
public static EquipmentSlot getById(ServerVersion version, int id) {
// FIXME: try making this O(1)
for (EquipmentSlot slot : VALUES) {
if (slot.getId(version) == id) {
return slot;
public static EquipmentSlot getById(ClientVersion version, int id) {
if (version.isOlderThan(ClientVersion.V_1_9)) {
for (EquipmentSlot slot : VALUES) {
if (slot.getId(version) == id) {
return slot;
}
}
} else {
return VALUES[id];
}
return null;
}
@Nullable
public static EquipmentSlot getById(ServerVersion version, int id) {
return getById(version.toClientVersion(), id);
}
}
@@ -209,9 +209,9 @@ public class User {
}
public void sendTitle(Component title, Component subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) {
ServerVersion version = PacketEvents.getAPI().getInjector().isProxy() ? getClientVersion().toServerVersion() :
PacketEvents.getAPI().getServerManager().getVersion();
boolean modern = version.isNewerThanOrEquals(ServerVersion.V_1_17);
ClientVersion version = PacketEvents.getAPI().getInjector().isProxy() ? getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
boolean modern = version.isNewerThanOrEquals(ClientVersion.V_1_17);
PacketWrapper<?> animation;
PacketWrapper<?> setTitle = null;
PacketWrapper<?> setSubtitle = null;
@@ -27,6 +27,8 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -145,12 +147,20 @@ public class PotionTypes {
public static final PotionType DARKNESS = define("darkness");
// Added in 1.20.5
public static final PotionType TRIAL_OMEN = define("darkness");
public static final PotionType RAID_OMEN = define("darkness");
public static final PotionType WIND_CHARGED = define("darkness");
public static final PotionType WEAVING = define("darkness");
public static final PotionType OOZING = define("darkness");
public static final PotionType INFESTED = define("darkness");
public static final PotionType TRIAL_OMEN = define("trial_omen");
public static final PotionType RAID_OMEN = define("raid_omen");
public static final PotionType WIND_CHARGED = define("wind_charged");
public static final PotionType WEAVING = define("weaving");
public static final PotionType OOZING = define("oozing");
public static final PotionType INFESTED = define("infested");
/**
* Returns an immutable view of the potion types.
* @return Potion Types
*/
public static Collection<PotionType> values() {
return Collections.unmodifiableCollection(POTION_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
@@ -16,14 +16,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.strategy;
package com.github.retrooper.packetevents.protocol.recipe;
import java.nio.file.Path;
public interface CompressionStrategy {
byte COMPRESSION_VERSION = 1;
void compress(final Path from, final Path to);
public enum CookingCategory {
FOOD,
BLOCKS,
MISC,
}
@@ -0,0 +1,27 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe;
public enum CraftingCategory {
BUILDING,
REDSTONE,
EQUIPMENT,
MISC,
}
@@ -19,17 +19,26 @@
package com.github.retrooper.packetevents.protocol.recipe;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
// From MCProtocolLib
public class Ingredient {
private final @NotNull ItemStack[] options;
public Ingredient(@NotNull ItemStack... options) {
private final ItemStack[] options;
public Ingredient(ItemStack... options) {
this.options = options;
}
public @NotNull ItemStack[] getOptions() {
return options;
public static Ingredient read(PacketWrapper<?> wrapper) {
ItemStack[] options = wrapper.readArray(PacketWrapper::readItemStack, ItemStack.class);
return new Ingredient(options);
}
}
public static void write(PacketWrapper<?> wrapper, Ingredient ingredient) {
wrapper.writeArray(ingredient.options, PacketWrapper::writeItemStack);
}
public ItemStack[] getOptions() {
return this.options;
}
}
@@ -18,39 +18,89 @@
package com.github.retrooper.packetevents.protocol.recipe;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.recipe.data.RecipeData;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
// From MCProtocolLib
public class Recipe {
private final @NotNull RecipeType type;
private final @NotNull String identifier;
private final RecipeData data;
public class Recipe<T extends RecipeData> {
public Recipe(@NotNull RecipeType type, @NotNull String identifier, RecipeData data) {
this.type = type;
this.identifier = identifier;
private final ResourceLocation key;
private final RecipeSerializer<T> serializer;
private final T data;
@SuppressWarnings("unchecked") // backwards compat
@Deprecated
public Recipe(RecipeType serializer, String key, RecipeData data) {
this(
new ResourceLocation(key),
(RecipeSerializer<T>) serializer.getSerializer(),
(T) data
);
}
public Recipe(ResourceLocation key, RecipeSerializer<T> serializer, T data) {
this.key = key;
this.serializer = serializer;
this.data = data;
}
public @NotNull RecipeType getType() {
return type;
public static Recipe<?> read(PacketWrapper<?> wrapper) {
ResourceLocation key;
RecipeSerializer<?> serializer;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
key = wrapper.readIdentifier();
serializer = wrapper.readMappedEntity(RecipeSerializers::getById);
} else {
serializer = RecipeSerializers.getByName(wrapper.readIdentifier().toString());
key = wrapper.readIdentifier();
}
return read(wrapper, key, serializer);
}
public @NotNull String getIdentifier() {
return identifier;
private static <T extends RecipeData> Recipe<T> read(
PacketWrapper<?> wrapper,
ResourceLocation key,
RecipeSerializer<T> serializer
) {
T data = serializer.read(wrapper);
return new Recipe<>(key, serializer, data);
}
public @NotNull RecipeData getData() {
return data;
public static <T extends RecipeData> void write(PacketWrapper<?> wrapper, Recipe<T> recipe) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
wrapper.writeIdentifier(recipe.key);
wrapper.writeMappedEntity(recipe.serializer);
} else {
wrapper.writeIdentifier(recipe.serializer.getName());
wrapper.writeIdentifier(recipe.key);
}
recipe.serializer.write(wrapper, recipe.data);
}
@Deprecated
public RecipeType getType() {
return this.serializer.getLegacyType();
}
public String getIdentifier() {
return this.key.toString();
}
public ResourceLocation getKey() {
return this.key;
}
public RecipeSerializer<T> getSerializer() {
return this.serializer;
}
public T getData() {
return this.data;
}
@Override
public String toString() {
return "Recipe{" +
"type=" + type +
", identifier='" + identifier + '\'' +
", data=" + data +
'}';
return "Recipe{key=" + this.key + ", serializer=" + this.serializer + ", data=" + this.data + '}';
}
}
}
@@ -0,0 +1,33 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.recipe.data.RecipeData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public interface RecipeSerializer<T extends RecipeData> extends MappedEntity {
@Deprecated
RecipeType getLegacyType();
T read(PacketWrapper<?> wrapper);
void write(PacketWrapper<?> wrapper, T data);
}
@@ -0,0 +1,179 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.recipe.data.CookedRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.RecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.ShapedRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.ShapelessRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.SimpleRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.SmithingRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.SmithingTrimRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.StoneCuttingRecipeData;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class RecipeSerializers {
private static final Map<String, RecipeSerializer<?>> PATTERN_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, RecipeSerializer<?>>> PATTERN_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/recipe_serializer_mappings");
public static <T extends RecipeData> RecipeSerializer<T> define(
String key,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer
) {
return define(key, reader, writer, null);
}
public static <T extends RecipeData> RecipeSerializer<T> define(
String key,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer,
@Nullable RecipeType recipeType
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
RecipeSerializer<T> pattern = new RecipeSerializer<T>() {
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public RecipeType getLegacyType() {
if (recipeType == null) {
throw new UnsupportedOperationException("No legacy type found for " + this.getName());
}
return recipeType;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T data) {
writer.accept(wrapper, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof RecipeSerializer<?>) {
return this.getName().equals(((RecipeSerializer<?>) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PATTERN_TYPE_MAP, PATTERN_TYPE_ID_MAP, pattern);
return pattern;
}
// with key
public static RecipeSerializer<?> getByName(String name) {
return PATTERN_TYPE_MAP.get(name);
}
public static RecipeSerializer<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, RecipeSerializer<?>> idMap = PATTERN_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
}
public static final RecipeSerializer<ShapedRecipeData> CRAFTING_SHAPED = define("crafting_shaped",
ShapedRecipeData::read, ShapedRecipeData::write, RecipeType.CRAFTING_SHAPED);
public static final RecipeSerializer<ShapelessRecipeData> CRAFTING_SHAPELESS = define("crafting_shapeless",
ShapelessRecipeData::read, ShapelessRecipeData::write, RecipeType.CRAFTING_SHAPELESS);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_ARMORDYE = define("crafting_special_armordye",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_ARMORDYE);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_BOOKCLONING = define("crafting_special_bookcloning",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_BOOKCLONING);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_MAPCLONING = define("crafting_special_mapcloning",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_MAPCLONING);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_MAPEXTENDING = define("crafting_special_mapextending",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_MAPEXTENDING);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_FIREWORK_ROCKET = define("crafting_special_firework_rocket",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_FIREWORK_ROCKET);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_FIREWORK_STAR = define("crafting_special_firework_star",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_FIREWORK_STAR);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_FIREWORK_STAR_FADE = define("crafting_special_firework_star_fade",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_FIREWORK_STAR_FADE);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_TIPPEDARROW = define("crafting_special_tippedarrow",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_TIPPEDARROW);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_BANNERDUPLICATE = define("crafting_special_bannerduplicate",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_BANNERDUPLICATE);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_SHIELDDECORATION = define("crafting_special_shielddecoration",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_SHIELDDECORATION);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_SHULKERBOXCOLORING = define("crafting_special_shulkerboxcoloring",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_SHULKERBOXCOLORING);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_SUSPICIOUSSTEW = define("crafting_special_suspiciousstew",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_SUSPICIOUSSTEW);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_SPECIAL_REPAIRITEM = define("crafting_special_repairitem",
SimpleRecipeData::read, SimpleRecipeData::write, RecipeType.CRAFTING_SPECIAL_REPAIRITEM);
public static final RecipeSerializer<CookedRecipeData> SMELTING = define("smelting",
CookedRecipeData::read, CookedRecipeData::write, RecipeType.SMELTING);
public static final RecipeSerializer<CookedRecipeData> BLASTING = define("blasting",
CookedRecipeData::read, CookedRecipeData::write, RecipeType.BLASTING);
public static final RecipeSerializer<CookedRecipeData> SMOKING = define("smoking",
CookedRecipeData::read, CookedRecipeData::write, RecipeType.SMOKING);
public static final RecipeSerializer<CookedRecipeData> CAMPFIRE_COOKING = define("campfire_cooking",
CookedRecipeData::read, CookedRecipeData::write, RecipeType.CAMPFIRE_COOKING);
public static final RecipeSerializer<StoneCuttingRecipeData> STONECUTTING = define("stonecutting",
StoneCuttingRecipeData::read, StoneCuttingRecipeData::write, RecipeType.STONECUTTING);
@Deprecated
public static final RecipeSerializer<SmithingRecipeData> SMITHING = define("smithing_transform",
ew -> SmithingRecipeData.read(ew, true),
(ew, data) -> SmithingRecipeData.write(ew, data, true),
RecipeType.SMITHING);
public static final RecipeSerializer<SmithingRecipeData> SMITHING_TRANSFORM = define("smithing_transform",
SmithingRecipeData::read, SmithingRecipeData::write, RecipeType.SMITHING);
public static final RecipeSerializer<SmithingTrimRecipeData> SMITHING_TRIM = define("smithing_trim",
SmithingTrimRecipeData::read, SmithingTrimRecipeData::write);
public static final RecipeSerializer<SimpleRecipeData> CRAFTING_DECORATED_POT = define("crafting_decorated_pot",
SimpleRecipeData::read, SimpleRecipeData::write);
/**
* Returns an immutable view of the banner patterns.
*
* @return Banner Patterns
*/
public static Collection<RecipeSerializer<?>> values() {
return Collections.unmodifiableCollection(PATTERN_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
}
@@ -18,28 +18,45 @@
package com.github.retrooper.packetevents.protocol.recipe;
import org.jetbrains.annotations.Nullable;
// From MCProtocolLib
@Deprecated
public enum RecipeType {
CRAFTING_SHAPELESS,
CRAFTING_SHAPED,
CRAFTING_SPECIAL_ARMORDYE,
CRAFTING_SPECIAL_BOOKCLONING,
CRAFTING_SPECIAL_MAPCLONING,
CRAFTING_SPECIAL_MAPEXTENDING,
CRAFTING_SPECIAL_FIREWORK_ROCKET,
CRAFTING_SPECIAL_FIREWORK_STAR,
CRAFTING_SPECIAL_FIREWORK_STAR_FADE,
CRAFTING_SPECIAL_REPAIRITEM,
CRAFTING_SPECIAL_TIPPEDARROW,
CRAFTING_SPECIAL_BANNERDUPLICATE,
CRAFTING_SPECIAL_BANNERADDPATTERN,
CRAFTING_SPECIAL_SHIELDDECORATION,
CRAFTING_SPECIAL_SHULKERBOXCOLORING,
CRAFTING_SPECIAL_SUSPICIOUSSTEW,
SMELTING,
BLASTING,
SMOKING,
CAMPFIRE_COOKING,
STONECUTTING,
SMITHING
CRAFTING_SHAPELESS(RecipeSerializers.CRAFTING_SHAPELESS),
CRAFTING_SHAPED(RecipeSerializers.CRAFTING_SHAPED),
CRAFTING_SPECIAL_ARMORDYE(RecipeSerializers.CRAFTING_SPECIAL_ARMORDYE),
CRAFTING_SPECIAL_BOOKCLONING(RecipeSerializers.CRAFTING_SPECIAL_BOOKCLONING),
CRAFTING_SPECIAL_MAPCLONING(RecipeSerializers.CRAFTING_SPECIAL_MAPCLONING),
CRAFTING_SPECIAL_MAPEXTENDING(RecipeSerializers.CRAFTING_SPECIAL_MAPEXTENDING),
CRAFTING_SPECIAL_FIREWORK_ROCKET(RecipeSerializers.CRAFTING_SPECIAL_FIREWORK_ROCKET),
CRAFTING_SPECIAL_FIREWORK_STAR(RecipeSerializers.CRAFTING_SPECIAL_FIREWORK_STAR),
CRAFTING_SPECIAL_FIREWORK_STAR_FADE(RecipeSerializers.CRAFTING_SPECIAL_FIREWORK_STAR_FADE),
CRAFTING_SPECIAL_REPAIRITEM(RecipeSerializers.CRAFTING_SPECIAL_REPAIRITEM),
CRAFTING_SPECIAL_TIPPEDARROW(RecipeSerializers.CRAFTING_SPECIAL_TIPPEDARROW),
CRAFTING_SPECIAL_BANNERDUPLICATE(RecipeSerializers.CRAFTING_SPECIAL_BANNERDUPLICATE),
CRAFTING_SPECIAL_BANNERADDPATTERN(null), // I didn't find this anywhere
CRAFTING_SPECIAL_SHIELDDECORATION(RecipeSerializers.CRAFTING_SPECIAL_SHIELDDECORATION),
CRAFTING_SPECIAL_SHULKERBOXCOLORING(RecipeSerializers.CRAFTING_SPECIAL_SHULKERBOXCOLORING),
CRAFTING_SPECIAL_SUSPICIOUSSTEW(RecipeSerializers.CRAFTING_SPECIAL_SUSPICIOUSSTEW),
SMELTING(RecipeSerializers.SMELTING),
BLASTING(RecipeSerializers.BLASTING),
SMOKING(RecipeSerializers.SMOKING),
CAMPFIRE_COOKING(RecipeSerializers.CAMPFIRE_COOKING),
STONECUTTING(RecipeSerializers.STONECUTTING),
SMITHING(RecipeSerializers.SMITHING_TRANSFORM);
private final @Nullable RecipeSerializer<?> serializer;
RecipeType(@Nullable RecipeSerializer<?> serializer) {
this.serializer = serializer;
}
public RecipeSerializer<?> getSerializer() {
if (this.serializer == null) {
throw new UnsupportedOperationException("No serializer found for legacy recipe type " + this);
}
return this.serializer;
}
}
@@ -18,45 +18,81 @@
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.recipe.CookingCategory;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class CookedRecipeData implements RecipeData {
private final @NotNull String group;
private final @NotNull Ingredient ingredient;
private final String group;
private final CookingCategory category;
private final Ingredient ingredient;
private final ItemStack result;
private final float experience;
private final int cookingTime;
public CookedRecipeData(@NotNull String group, @NotNull Ingredient ingredient, @NotNull ItemStack result, float experience, int cookingTime) {
@Deprecated
public CookedRecipeData(String group, Ingredient ingredient, ItemStack result, float experience, int cookingTime) {
this(group, CookingCategory.MISC, ingredient, result, experience, cookingTime);
}
public CookedRecipeData(
String group, CookingCategory category, Ingredient ingredient,
ItemStack result, float experience, int cookingTime
) {
this.group = group;
this.category = category;
this.ingredient = ingredient;
this.result = result;
this.experience = experience;
this.cookingTime = cookingTime;
}
@NotNull
public static CookedRecipeData read(PacketWrapper<?> wrapper) {
String group = wrapper.readString();
CookingCategory category = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3) ?
wrapper.readEnum(CookingCategory.values()) : CookingCategory.MISC;
Ingredient ingredient = Ingredient.read(wrapper);
ItemStack result = wrapper.readItemStack();
float experience = wrapper.readFloat();
int cookingTime = wrapper.readVarInt();
return new CookedRecipeData(group, category, ingredient, result, experience, cookingTime);
}
public static void write(PacketWrapper<?> wrapper, CookedRecipeData data) {
wrapper.writeString(data.group);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
wrapper.writeEnum(data.category);
}
Ingredient.write(wrapper, data.ingredient);
wrapper.writeItemStack(data.result);
wrapper.writeFloat(data.experience);
wrapper.writeVarInt(data.cookingTime);
}
public String getGroup() {
return group;
return this.group;
}
public CookingCategory getCategory() {
return this.category;
}
@NotNull
public Ingredient getIngredient() {
return ingredient;
return this.ingredient;
}
@NotNull
public ItemStack getResult() {
return result;
return this.result;
}
public float getExperience() {
return experience;
return this.experience;
}
public int getCookingTime() {
return cookingTime;
return this.cookingTime;
}
}
}
@@ -18,23 +18,108 @@
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.recipe.CraftingCategory;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class ShapedRecipeData implements RecipeData {
private final String group;
private final CraftingCategory category;
private final ItemStack result;
private final boolean showNotification;
// pattern
private final int width;
private final int height;
private final @NotNull String group;
private final @NotNull Ingredient[] ingredients;
private final ItemStack result;
private final Ingredient[] ingredients;
public ShapedRecipeData(final int width, final int height, final @NotNull String group, final @NotNull Ingredient[] ingredients, final ItemStack result) {
@Deprecated
public ShapedRecipeData(int width, int height, String group, Ingredient[] ingredients, ItemStack result) {
this(group, CraftingCategory.MISC, result, true, width, height, ingredients);
}
public ShapedRecipeData(
String group, CraftingCategory category, ItemStack result, boolean showNotification,
int width, int height, Ingredient[] ingredients
) {
if (width * height != ingredients.length) {
throw new IllegalArgumentException("Illegal ingredients length, found " + ingredients.length
+ " but expected " + width + " * " + height);
}
this.group = group;
this.category = category;
this.result = result;
this.showNotification = showNotification;
this.width = width;
this.height = height;
this.group = group;
this.ingredients = ingredients;
this.result = result;
}
public static ShapedRecipeData read(PacketWrapper<?> wrapper) {
int width = 0, height = 0;
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_20_3)) {
width = wrapper.readVarInt();
height = wrapper.readVarInt();
}
String group = wrapper.readString();
CraftingCategory category = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)
? wrapper.readEnum(CraftingCategory.values()) : CraftingCategory.MISC;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
width = wrapper.readVarInt();
height = wrapper.readVarInt();
}
Ingredient[] ingredients = new Ingredient[width * height];
for (int i = 0; i < ingredients.length; i++) {
ingredients[i] = Ingredient.read(wrapper);
}
ItemStack result = wrapper.readItemStack();
boolean showNotification = true;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4)) {
showNotification = wrapper.readBoolean();
}
return new ShapedRecipeData(group, category, result, showNotification, width, height, ingredients);
}
public static void write(PacketWrapper<?> wrapper, ShapedRecipeData data) {
if (wrapper.getServerVersion().isOlderThan(ServerVersion.V_1_20_3)) {
wrapper.writeVarInt(data.width);
wrapper.writeVarInt(data.height);
}
wrapper.writeString(data.group);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
wrapper.writeEnum(data.category);
}
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
wrapper.writeVarInt(data.width);
wrapper.writeVarInt(data.height);
}
for (Ingredient ingredient : data.ingredients) {
Ingredient.write(wrapper, ingredient);
}
wrapper.writeItemStack(data.result);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4)) {
wrapper.writeBoolean(data.showNotification);
}
}
public String getGroup() {
return this.group;
}
public CraftingCategory getCategory() {
return this.category;
}
public ItemStack getResult() {
return this.result;
}
public boolean isShowNotification() {
return this.showNotification;
}
public int getWidth() {
@@ -45,15 +130,7 @@ public class ShapedRecipeData implements RecipeData {
return this.height;
}
public @NotNull String getGroup() {
return this.group;
}
public @NotNull Ingredient[] getIngredients() {
public Ingredient[] getIngredients() {
return this.ingredients;
}
public ItemStack getResult() {
return this.result;
}
}
}
@@ -18,30 +18,62 @@
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.recipe.CraftingCategory;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class ShapelessRecipeData implements RecipeData {
private final @NotNull String group;
private final @NotNull Ingredient[] ingredients;
private final String group;
private final CraftingCategory category;
private final Ingredient[] ingredients;
private final ItemStack result;
public ShapelessRecipeData(@NotNull String group, @NotNull Ingredient[] ingredients, @NotNull ItemStack result) {
@Deprecated
public ShapelessRecipeData(String group, Ingredient[] ingredients, ItemStack result) {
this(group, CraftingCategory.MISC, ingredients, result);
}
public ShapelessRecipeData(String group, CraftingCategory category, Ingredient[] ingredients, ItemStack result) {
this.group = group;
this.category = category;
this.ingredients = ingredients;
this.result = result;
}
public @NotNull String getGroup() {
return group;
public static ShapelessRecipeData read(PacketWrapper<?> wrapper) {
String group = wrapper.readString();
CraftingCategory category = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)
? wrapper.readEnum(CraftingCategory.values()) : CraftingCategory.MISC;
Ingredient[] ingredients = wrapper.readArray(Ingredient::read, Ingredient.class);
ItemStack result = wrapper.readItemStack();
return new ShapelessRecipeData(group, category, ingredients, result);
}
public @NotNull Ingredient[] getIngredients() {
return ingredients;
public static void write(PacketWrapper<?> wrapper, ShapelessRecipeData data) {
wrapper.writeString(data.group);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
wrapper.writeEnum(data.category);
}
wrapper.writeArray(data.ingredients, Ingredient::write);
wrapper.writeItemStack(data.result);
}
public @NotNull ItemStack getResult() {
return result;
public String getGroup() {
return this.group;
}
}
public CraftingCategory getCategory() {
return this.category;
}
public Ingredient[] getIngredients() {
return this.ingredients;
}
public ItemStack getResult() {
return this.result;
}
}
@@ -0,0 +1,48 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.recipe.CraftingCategory;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class SimpleRecipeData implements RecipeData {
private final CraftingCategory category;
public SimpleRecipeData(CraftingCategory category) {
this.category = category;
}
public static SimpleRecipeData read(PacketWrapper<?> wrapper) {
CraftingCategory category = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)
? wrapper.readEnum(CraftingCategory.values()) : CraftingCategory.MISC;
return new SimpleRecipeData(category);
}
public static void write(PacketWrapper<?> wrapper, SimpleRecipeData data) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
wrapper.writeEnum(data.getCategory());
}
}
public CraftingCategory getCategory() {
return this.category;
}
}
@@ -18,32 +18,75 @@
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.UnknownNullability;
public class SmithingRecipeData implements RecipeData {
private final @NotNull Ingredient base;
private final @NotNull Ingredient addition;
private final @UnknownNullability Ingredient template;
private final Ingredient base;
private final Ingredient addition;
private final ItemStack result;
public SmithingRecipeData(@NotNull Ingredient base, @NotNull Ingredient addition, ItemStack result) {
@Deprecated
public SmithingRecipeData(Ingredient base, Ingredient addition, ItemStack result) {
this(null, base, addition, result);
}
public SmithingRecipeData(
@UnknownNullability Ingredient template,
Ingredient base, Ingredient addition, ItemStack result
) {
this.template = template;
this.base = base;
this.addition = addition;
this.result = result;
}
@NotNull
public Ingredient getBase() {
return base;
public static SmithingRecipeData read(PacketWrapper<?> wrapper) {
return read(wrapper, false);
}
public static SmithingRecipeData read(PacketWrapper<?> wrapper, boolean legacy) {
Ingredient template = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20)
|| (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4) && !legacy)
? Ingredient.read(wrapper) : null;
Ingredient base = Ingredient.read(wrapper);
Ingredient addition = Ingredient.read(wrapper);
ItemStack result = wrapper.readItemStack();
return new SmithingRecipeData(template, base, addition, result);
}
public static void write(PacketWrapper<?> wrapper, SmithingRecipeData data) {
write(wrapper, data, false);
}
public static void write(PacketWrapper<?> wrapper, SmithingRecipeData data, boolean legacy) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_20)
|| (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4) && !legacy)) {
Ingredient.write(wrapper, data.template);
}
Ingredient.write(wrapper, data.base);
Ingredient.write(wrapper, data.addition);
wrapper.writeItemStack(data.result);
}
public @UnknownNullability Ingredient getTemplate() {
return this.template;
}
public Ingredient getBase() {
return this.base;
}
@NotNull
public Ingredient getAddition() {
return addition;
return this.addition;
}
public ItemStack getResult() {
return result;
return this.result;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class SmithingTrimRecipeData implements RecipeData {
private final Ingredient template;
private final Ingredient base;
private final Ingredient addition;
public SmithingTrimRecipeData(Ingredient template, Ingredient base, Ingredient addition) {
this.template = template;
this.base = base;
this.addition = addition;
}
public static SmithingTrimRecipeData read(PacketWrapper<?> wrapper) {
Ingredient template = Ingredient.read(wrapper);
Ingredient base = Ingredient.read(wrapper);
Ingredient addition = Ingredient.read(wrapper);
return new SmithingTrimRecipeData(template, base, addition);
}
public static void write(PacketWrapper<?> wrapper, SmithingTrimRecipeData data) {
Ingredient.write(wrapper, data.template);
Ingredient.write(wrapper, data.base);
Ingredient.write(wrapper, data.addition);
}
public Ingredient getTemplate() {
return this.template;
}
public Ingredient getBase() {
return this.base;
}
public Ingredient getAddition() {
return this.addition;
}
}
@@ -20,31 +20,42 @@ package com.github.retrooper.packetevents.protocol.recipe.data;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.recipe.Ingredient;
import org.jetbrains.annotations.NotNull;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class StoneCuttingRecipeData implements RecipeData {
private final @NotNull String group;
private final @NotNull Ingredient ingredient;
private final String group;
private final Ingredient ingredient;
private final ItemStack result;
public StoneCuttingRecipeData(@NotNull String group, @NotNull Ingredient ingredient, ItemStack result) {
public StoneCuttingRecipeData(String group, Ingredient ingredient, ItemStack result) {
this.group = group;
this.ingredient = ingredient;
this.result = result;
}
@NotNull
public static StoneCuttingRecipeData read(PacketWrapper<?> wrapper) {
String group = wrapper.readString();
Ingredient ingredient = Ingredient.read(wrapper);
ItemStack result = wrapper.readItemStack();
return new StoneCuttingRecipeData(group, ingredient, result);
}
public static void write(PacketWrapper<?> wrapper, StoneCuttingRecipeData data) {
wrapper.writeString(data.group);
Ingredient.write(wrapper, data.ingredient);
wrapper.writeItemStack(data.result);
}
public String getGroup() {
return group;
return this.group;
}
@NotNull
public Ingredient getIngredient() {
return ingredient;
return this.ingredient;
}
@NotNull
public ItemStack getResult() {
return result;
return this.result;
}
}
@@ -23,6 +23,8 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -43,6 +45,14 @@ public final class ScoreFormatTypes {
wrapper -> ScoreFormat.fixedScore(wrapper.readComponent()),
(wrapper, format) -> wrapper.writeComponent(format.getValue()));
/**
* Returns an immutable view of the score format types.
* @return Score Format Types
*/
public static Collection<ScoreFormatType> values() {
return Collections.unmodifiableCollection(SCORE_FORMAT_TYPE_MAP.values());
}
private ScoreFormatTypes() {
}
@@ -25,6 +25,8 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -41,7 +43,7 @@ public class Sounds {
public static Sound define(String key, ResourceLocation soundId, @Nullable Float range) {
TypesBuilderData data = TYPES_BUILDER.define(key);
Sound potionType = new Sound() {
Sound soundType = new Sound() {
@Override
public ResourceLocation getSoundId() {
return soundId;
@@ -70,8 +72,8 @@ public class Sounds {
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, SOUND_TYPE_MAP, SOUND_TYPE_ID_MAP, potionType);
return potionType;
MappingHelper.registerMapping(TYPES_BUILDER, SOUND_TYPE_MAP, SOUND_TYPE_ID_MAP, soundType);
return soundType;
}
public static @Nullable Sound getByName(String name) {
@@ -767,9 +769,19 @@ public class Sounds {
public static final Sound BLOCK_TRIAL_SPAWNER_SPAWN_ITEM = define("block.trial_spawner.spawn_item");
public static final Sound BLOCK_TRIAL_SPAWNER_SPAWN_ITEM_BEGIN = define("block.trial_spawner.spawn_item_begin");
public static final Sound BLOCK_TRIAL_SPAWNER_DETECT_PLAYER = define("block.trial_spawner.detect_player");
public static final Sound BLOCK_TRIAL_SPAWNER_CHARGE_ACTIVATE = define("block.trial_spawner.charge_activate");
public static final Sound BLOCK_TRIAL_SPAWNER_OMINOUS_ACTIVATE = define("block.trial_spawner.ominous_activate");
/**
* Removed in 1.21
*/
@Deprecated
public static final Sound BLOCK_TRIAL_SPAWNER_CHARGE_ACTIVATE = BLOCK_TRIAL_SPAWNER_OMINOUS_ACTIVATE;
public static final Sound BLOCK_TRIAL_SPAWNER_AMBIENT = define("block.trial_spawner.ambient");
public static final Sound BLOCK_TRIAL_SPAWNER_AMBIENT_CHARGED = define("block.trial_spawner.ambient_charged");
public static final Sound BLOCK_TRIAL_SPAWNER_AMBIENT_OMINOUS = define("block.trial_spawner.ambient_ominous");
/**
* Removed in 1.21
*/
@Deprecated
public static final Sound BLOCK_TRIAL_SPAWNER_AMBIENT_CHARGED = BLOCK_TRIAL_SPAWNER_AMBIENT_OMINOUS;
public static final Sound BLOCK_TRIAL_SPAWNER_OPEN_SHUTTER = define("block.trial_spawner.open_shutter");
public static final Sound BLOCK_TRIAL_SPAWNER_CLOSE_SHUTTER = define("block.trial_spawner.close_shutter");
public static final Sound BLOCK_TRIAL_SPAWNER_EJECT_ITEM = define("block.trial_spawner.eject_item");
@@ -1692,6 +1704,20 @@ public class Sounds {
public static final Sound EVENT_MOB_EFFECT_TRIAL_OMEN = define("event.mob_effect.trial_omen");
public static final Sound EVENT_MOB_EFFECT_RAID_OMEN = define("event.mob_effect.raid_omen");
// Added with 1.21
public static final Sound MUSIC_DISC_CREATOR = define("music_disc.creator");
public static final Sound MUSIC_DISC_CREATOR_MUSIC_BOX = define("music_disc.creator_music_box");
public static final Sound MUSIC_DISC_PRECIPICE = define("music_disc.precipice");
public static final Sound BLOCK_VAULT_REJECT_REWARDED_PLAYER = define("block.vault.reject_rewarded_player");
/**
* Returns an immutable view of the sounds.
* @return Sounds
*/
public static Collection<Sound> values() {
return Collections.unmodifiableCollection(SOUND_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -18,11 +18,13 @@
package com.github.retrooper.packetevents.protocol.world;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
import java.util.Map;
// From MCProtocolLib
//TODO Recode and make registry
@ApiStatus.Obsolete
public enum PaintingType {
KEBAB("Kebab", 0, 1, 1),
AZTEC("Aztec", 1, 1, 1),
@@ -105,4 +107,4 @@ public enum PaintingType {
}
private static final PaintingType[] VALUES = values();
}
}
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.world;
@Deprecated
public enum TileEntityType {
MOB_SPAWNER,
COMMAND_BLOCK,
@@ -23,6 +23,8 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -123,6 +125,14 @@ public class BlockEntityTypes {
public static final BlockEntityType TRIAL_SPAWNER = define("trial_spawner");
public static final BlockEntityType VAULT = define("vault");
/**
* Returns an immutable view of the block entity types.
* @return Block Entity Types
*/
public static Collection<BlockEntityType> values() {
return Collections.unmodifiableCollection(BLOCK_ENTITY_TYPE_MAP.values());
}
static {
TYPES_BUILDER.unloadFileMappings();
}
@@ -0,0 +1,53 @@
package com.github.retrooper.packetevents.protocol.world.chunk;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import java.util.BitSet;
@ApiStatus.Internal
public class ChunkBitMask {
private ChunkBitMask() {
}
public static long[] readBitSetLongs(PacketWrapper<?> packet) {
if (packet.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
//Read primary bit mask
return packet.readLongArray();
} else if (packet.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
//Read primary bit mask
return new long[]{packet.readVarInt()};
} else {
//Read primary bit mask
return new long[]{packet.readUnsignedShort()};
}
}
public static BitSet readChunkMask(PacketWrapper<?> packet) {
return BitSet.valueOf(readBitSetLongs(packet));
}
public static void writeChunkMask(PacketWrapper<?> packet, BitSet chunkMask) {
long[] longArray = chunkMask.toLongArray();
if (packet.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_17)) {
//Write primary bit mask
packet.writeLongArray(longArray);
} else if (packet.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
if (longArray.length > 0) {
packet.writeVarInt((int) longArray[0]);
} else {
packet.writeVarInt(0);
}
} else {
if (longArray.length > 0) {
packet.writeShort((int) longArray[0]);
} else {
packet.writeShort(0);
}
}
}
}
@@ -0,0 +1,183 @@
package com.github.retrooper.packetevents.protocol.world.chunk;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import java.util.BitSet;
public class LightData implements Cloneable {
private boolean trustEdges;
private BitSet blockLightMask;
private BitSet skyLightMask;
private BitSet emptyBlockLightMask;
private BitSet emptySkyLightMask;
private int skyLightCount;
private int blockLightCount;
private byte[][] skyLightArray;
private byte[][] blockLightArray;
public LightData() {
}
public LightData(boolean trustEdges, BitSet blockLightMask, BitSet skyLightMask, BitSet emptyBlockLightMask, BitSet emptySkyLightMask, int skyLightCount, int blockLightCount, byte[][] skyLightArray, byte[][] blockLightArray) {
this.trustEdges = trustEdges;
this.blockLightMask = blockLightMask;
this.skyLightMask = skyLightMask;
this.emptyBlockLightMask = emptyBlockLightMask;
this.emptySkyLightMask = emptySkyLightMask;
this.skyLightCount = skyLightCount;
this.blockLightCount = blockLightCount;
this.skyLightArray = skyLightArray;
this.blockLightArray = blockLightArray;
}
@Override
public LightData clone() {
try {
LightData clone = (LightData) super.clone();
clone.blockLightMask = (BitSet) blockLightMask.clone();
clone.skyLightMask = (BitSet) skyLightMask.clone();
clone.emptyBlockLightMask = (BitSet) emptyBlockLightMask.clone();
clone.emptySkyLightMask = (BitSet) emptySkyLightMask.clone();
clone.skyLightArray = skyLightArray.clone();
clone.blockLightArray = blockLightArray.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public boolean isTrustEdges() {
return trustEdges;
}
public void setTrustEdges(boolean trustEdges) {
this.trustEdges = trustEdges;
}
public BitSet getBlockLightMask() {
return blockLightMask;
}
public void setBlockLightMask(BitSet blockLightMask) {
this.blockLightMask = blockLightMask;
}
public BitSet getSkyLightMask() {
return skyLightMask;
}
public void setSkyLightMask(BitSet skyLightMask) {
this.skyLightMask = skyLightMask;
}
public BitSet getEmptyBlockLightMask() {
return emptyBlockLightMask;
}
public void setEmptyBlockLightMask(BitSet emptyBlockLightMask) {
this.emptyBlockLightMask = emptyBlockLightMask;
}
public BitSet getEmptySkyLightMask() {
return emptySkyLightMask;
}
public void setEmptySkyLightMask(BitSet emptySkyLightMask) {
this.emptySkyLightMask = emptySkyLightMask;
}
public int getSkyLightCount() {
return skyLightCount;
}
public void setSkyLightCount(int skyLightCount) {
this.skyLightCount = skyLightCount;
}
public int getBlockLightCount() {
return blockLightCount;
}
public void setBlockLightCount(int blockLightCount) {
this.blockLightCount = blockLightCount;
}
public byte[][] getSkyLightArray() {
return skyLightArray;
}
public void setSkyLightArray(byte[][] skyLightArray) {
this.skyLightArray = skyLightArray;
}
public byte[][] getBlockLightArray() {
return blockLightArray;
}
public void setBlockLightArray(byte[][] blockLightArray) {
this.blockLightArray = blockLightArray;
}
public static LightData read(PacketWrapper<?> packet) {
LightData lightData = new LightData();
ServerVersion serverVersion = packet.getServerVersion();
if (serverVersion.isOlderThanOrEquals(ServerVersion.V_1_19_4)) {
lightData.trustEdges = packet.readBoolean();
}
lightData.skyLightMask = ChunkBitMask.readChunkMask(packet);
lightData.blockLightMask = ChunkBitMask.readChunkMask(packet);
lightData.emptySkyLightMask = ChunkBitMask.readChunkMask(packet);
lightData.emptyBlockLightMask = ChunkBitMask.readChunkMask(packet);
boolean v17 = serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17);
lightData.skyLightCount = v17 ? packet.readVarInt() : 18;
lightData.skyLightArray = new byte[lightData.skyLightCount][];
for (int i = 0; i < lightData.skyLightCount; i++) {
if (v17 || lightData.skyLightMask.get(i)) {
lightData.skyLightArray[i] = packet.readByteArray();
}
}
lightData.blockLightCount = v17 ? packet.readVarInt() : 18;
lightData.blockLightArray = new byte[lightData.blockLightCount][];
for (int i = 0; i < lightData.blockLightCount; i++) {
if (v17 || lightData.blockLightMask.get(i)) {
lightData.blockLightArray[i] = packet.readByteArray();
}
}
return lightData;
}
public static void write(PacketWrapper<?> packet, LightData lightData) {
ServerVersion serverVersion = packet.getServerVersion();
if (serverVersion.isOlderThanOrEquals(ServerVersion.V_1_19_4)) {
packet.writeBoolean(lightData.trustEdges);
}
ChunkBitMask.writeChunkMask(packet, lightData.skyLightMask);
ChunkBitMask.writeChunkMask(packet, lightData.blockLightMask);
ChunkBitMask.writeChunkMask(packet, lightData.emptySkyLightMask);
ChunkBitMask.writeChunkMask(packet, lightData.emptyBlockLightMask);
boolean v17 = serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17);
if (v17) {
packet.writeVarInt(lightData.skyLightCount);
}
for (int i = 0; i < lightData.skyLightCount; i++) {
if (v17 || lightData.skyLightMask.get(i)) {
packet.writeByteArray(lightData.skyLightArray[i]);
}
}
if (v17) {
packet.writeVarInt(lightData.blockLightCount);
}
for (int i = 0; i < lightData.blockLightCount; i++) {
if (v17 || lightData.blockLightMask.get(i)) {
packet.writeByteArray(lightData.blockLightArray[i]);
}
}
}
}
@@ -0,0 +1,45 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.world.painting;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public interface PaintingVariant extends MappedEntity {
int getWidth();
int getHeight();
ResourceLocation getAssetId();
static PaintingVariant readDirect(PacketWrapper<?> wrapper) {
int width = wrapper.readVarInt();
int height = wrapper.readVarInt();
ResourceLocation assetId = wrapper.readIdentifier();
return new StaticPaintingVariant(width, height, assetId);
}
static void writeDirect(PacketWrapper<?> wrapper, PaintingVariant variant) {
wrapper.writeVarInt(variant.getWidth());
wrapper.writeVarInt(variant.getHeight());
wrapper.writeIdentifier(variant.getAssetId());
}
}
@@ -0,0 +1,146 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.world.painting;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import java.util.HashMap;
import java.util.Map;
public class PaintingVariants {
private static final Map<String, PaintingVariant> PAINTING_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, PaintingVariant>> PAINTING_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_banner_pattern_mappings");
public static PaintingVariant define(String key, int width, int height) {
ResourceLocation assetId = ResourceLocation.minecraft(key);
return define(key, width, height, assetId);
}
public static PaintingVariant define(String key, int width, int height, ResourceLocation assetId) {
TypesBuilderData data = TYPES_BUILDER.define(key);
PaintingVariant variant = new PaintingVariant() {
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public ResourceLocation getAssetId() {
return assetId;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return TYPES_BUILDER.getDataIndex(version);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PaintingVariant) {
return this.getName().equals(((PaintingVariant) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PAINTING_TYPE_MAP, PAINTING_TYPE_ID_MAP, variant);
return variant;
}
// with key
public static PaintingVariant getByName(String name) {
return PAINTING_TYPE_MAP.get(name);
}
public static PaintingVariant getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, PaintingVariant> idMap = PAINTING_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
}
public static final PaintingVariant POINTER = define("pointer", 4, 4);
public static final PaintingVariant CREEBET = define("creebet", 2, 1);
public static final PaintingVariant PRAIRIE_RIDE = define("prairie_ride", 1, 2);
public static final PaintingVariant POOL = define("pool", 2, 1);
public static final PaintingVariant EARTH = define("earth", 2, 2);
public static final PaintingVariant SKELETON = define("skeleton", 4, 3);
public static final PaintingVariant MATCH = define("match", 2, 2);
public static final PaintingVariant POND = define("pond", 3, 4);
public static final PaintingVariant HUMBLE = define("humble", 2, 2);
public static final PaintingVariant PIGSCENE = define("pigscene", 4, 4);
public static final PaintingVariant WATER = define("water", 2, 2);
public static final PaintingVariant ALBAN = define("alban", 1, 1);
public static final PaintingVariant FINDING = define("finding", 4, 2);
public static final PaintingVariant AZTEC2 = define("aztec2", 1, 1);
public static final PaintingVariant TIDES = define("tides", 3, 3);
public static final PaintingVariant FIGHTERS = define("fighters", 4, 2);
public static final PaintingVariant FIRE = define("fire", 2, 2);
public static final PaintingVariant CHANGING = define("changing", 4, 2);
public static final PaintingVariant BURNING_SKULL = define("burning_skull", 4, 4);
public static final PaintingVariant COTAN = define("cotan", 3, 3);
public static final PaintingVariant WANDERER = define("wanderer", 1, 2);
public static final PaintingVariant UNPACKED = define("unpacked", 4, 4);
public static final PaintingVariant SUNSET = define("sunset", 2, 1);
public static final PaintingVariant FERN = define("fern", 3, 3);
public static final PaintingVariant BUST = define("bust", 2, 2);
public static final PaintingVariant WIND = define("wind", 2, 2);
public static final PaintingVariant LOWMIST = define("lowmist", 4, 2);
public static final PaintingVariant PASSAGE = define("passage", 4, 2);
public static final PaintingVariant SUNFLOWERS = define("sunflowers", 3, 3);
public static final PaintingVariant GRAHAM = define("graham", 1, 2);
public static final PaintingVariant WASTELAND = define("wasteland", 1, 1);
public static final PaintingVariant SKULL_AND_ROSES = define("skull_and_roses", 2, 2);
public static final PaintingVariant BOUQUET = define("bouquet", 3, 3);
public static final PaintingVariant ORB = define("orb", 4, 4);
public static final PaintingVariant BOMB = define("bomb", 1, 1);
public static final PaintingVariant WITHER = define("wither", 2, 2);
public static final PaintingVariant BACKYARD = define("backyard", 3, 4);
public static final PaintingVariant ENDBOSS = define("endboss", 3, 3);
public static final PaintingVariant MEDITATIVE = define("meditative", 1, 1);
public static final PaintingVariant VOID = define("void", 2, 2);
public static final PaintingVariant KEBAB = define("kebab", 1, 1);
public static final PaintingVariant SEA = define("sea", 2, 1);
public static final PaintingVariant DONKEY_KONG = define("donkey_kong", 4, 3);
public static final PaintingVariant BAROQUE = define("baroque", 2, 2);
public static final PaintingVariant STAGE = define("stage", 2, 2);
public static final PaintingVariant AZTEC = define("aztec", 1, 1);
public static final PaintingVariant PLANT = define("plant", 1, 1);
public static final PaintingVariant CAVEBIRD = define("cavebird", 3, 3);
public static final PaintingVariant COURBET = define("courbet", 2, 1);
public static final PaintingVariant OWLEMONS = define("owlemons", 3, 3);
static {
TYPES_BUILDER.unloadFileMappings();
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.world.painting;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
public class StaticPaintingVariant implements PaintingVariant {
private final int width;
private final int height;
private final ResourceLocation assetId;
public StaticPaintingVariant(int width, int height, ResourceLocation assetId) {
this.width = width;
this.height = height;
this.assetId = assetId;
}
@Override
public int getWidth() {
return this.width;
}
@Override
public int getHeight() {
return this.height;
}
@Override
public ResourceLocation getAssetId() {
return this.assetId;
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
}
}
@@ -37,8 +37,23 @@ public class WrappedBlockState {
static {
STRING_UPDATER.put("grass_path", "dirt_path"); // 1.16 -> 1.17
loadLegacy();
loadModern();
// Try to reduce memory footprint by re-using hashmaps when they are equal
// We do this by setting the key to the NBTCompound of the data and the value to the data
// this.data = cache.computeIfAbsent(dataContent, (key) -> { // NBTCompound to data });
// This will get an equal value if present, otherwise it will compute the value
// Once this is done, we remove this cache to save memory
// A HashMap is used instead of another data type because a hashmap is o(1)
//
// 6160 total combinations, last updated with 1.20.5
// This brings total memory usage from 62 MB to 34 MB, a 28 MB reduction
// Using a HashMap reduces memory usage to less than a megabyte, I can't get precise numbers because it is hard to see on a heapdump
Map<NBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache = new HashMap<>(6160, 70);
loadLegacy(cache);
loadModern(cache);
cache.clear();
cache = null;
}
int globalID;
@@ -164,14 +179,12 @@ public class WrappedBlockState {
return 11;
} else if (version.isOlderThanOrEquals(ClientVersion.V_1_20_2)) {
return 12;
} else if (version.isOlderThanOrEquals(ClientVersion.V_1_20_5)) {
return 13;
}
// TODO UPDATE increment index (and add previous above)
return 14;
return 13;
}
private static void loadLegacy() {
private static void loadLegacy(Map<NBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache) {
Map<Integer, WrappedBlockState> stateByIdMap = new HashMap<>();
Map<WrappedBlockState, Integer> stateToIdMap = new HashMap<>();
Map<String, WrappedBlockState> stateByStringMap = new HashMap<>();
@@ -196,45 +209,49 @@ public class WrappedBlockState {
int data = Integer.parseInt(ids[1]);
int combinedID = (id << 4) | data;
StringBuilder dataStringBuilder = new StringBuilder();
NBTCompound dataContent = (NBTCompound) element.getValue();
Map<StateValue, Object> dataMap = new HashMap<>(dataContent.size());
Map.Entry<Map<StateValue, Object>, String> dataEntry = cache.computeIfAbsent(dataContent, (key) -> {
StringBuilder dataStringBuilder = new StringBuilder();
Map<StateValue, Object> dataMap = new HashMap<>(key.size());
for (Map.Entry<String, NBT> props : dataContent.getTags().entrySet()) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
for (Map.Entry<String, NBT> props : key.getTags().entrySet()) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
}
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT type in legacy mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT type in legacy mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
return new AbstractMap.SimpleEntry<>(dataMap, dataString);
});
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
String fullString = entry.getKey() + dataString;
WrappedBlockState state = new WrappedBlockState(type, dataMap, combinedID, (byte) 0);
String fullString = entry.getKey() + dataEntry.getValue();
WrappedBlockState state = new WrappedBlockState(type, dataEntry.getKey(), combinedID, (byte) 0);
stateByIdMap.put(combinedID, state);
stateToStringMap.put(state, fullString);
@@ -257,21 +274,21 @@ public class WrappedBlockState {
DEFAULT_STATES.put((byte) 0, stateTypeToBlockStateMap);
}
private static void loadModern() {
private static void loadModern(Map<NBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache) {
final NBTCompound compound = MappingHelper.decompress("mappings/block/modern_block_mappings");
for (Map.Entry<String, NBT> versionEntry : compound.getTags().entrySet()) {
if (versionEntry.getKey().equals("version")) continue;
ClientVersion version = ClientVersion.valueOf(versionEntry.getKey());
byte mappingIndex = getMappingsIndex(version);
NBTList<NBTCompound> list = (NBTList<NBTCompound>) versionEntry.getValue();
Map<Integer, WrappedBlockState> stateByIdMap = new HashMap<>();
Map<WrappedBlockState, Integer> stateToIdMap = new HashMap<>();
Map<String, WrappedBlockState> stateByStringMap = new HashMap<>();
Map<WrappedBlockState, String> stateToStringMap = new HashMap<>();
Map<StateType, WrappedBlockState> stateTypeToBlockStateMap = new HashMap<>();
if (versionEntry.getKey().equals("version")) continue;
ClientVersion version = ClientVersion.valueOf(versionEntry.getKey());
byte mappingIndex = getMappingsIndex(version);
NBTList<NBTCompound> list = (NBTList<NBTCompound>) versionEntry.getValue();
int id = 0;
for (NBTCompound element : list.getTags()) {
String typeString = element.getStringTagValueOrThrow("type");
@@ -299,45 +316,48 @@ public class WrappedBlockState {
int index = 0;
for (NBTCompound dataContent : element.getCompoundListTagOrThrow("entries").getTags()) {
StringBuilder dataStringBuilder = new StringBuilder();
Map.Entry<Map<StateValue, Object>, String> dataEntry = cache.computeIfAbsent(dataContent, (key) -> {
StringBuilder dataStringBuilder = new StringBuilder();
Map<StateValue, Object> dataMap = new HashMap<>(key.size());
Map<StateValue, Object> dataMap = new HashMap<>(dataContent.size());
for (Map.Entry<String, NBT> props : key.getTags().entrySet()) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
}
for (Map.Entry<String, NBT> props : dataContent.getTags().entrySet()) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT typeString in modern mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT typeString in modern mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
return new AbstractMap.SimpleEntry<>(dataMap, dataString);
});
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
String fullString = typeString + dataString;
WrappedBlockState state = new WrappedBlockState(type, dataMap, id, mappingIndex);
String fullString = typeString + dataEntry.getValue();
WrappedBlockState state = new WrappedBlockState(type, dataEntry.getKey(), id, mappingIndex);
if (defaultIdx == index) {
stateTypeToBlockStateMap.put(type, state);
@@ -363,7 +383,7 @@ public class WrappedBlockState {
@Override
public WrappedBlockState clone() {
return new WrappedBlockState(type, new HashMap<>(data), globalID, mappingsIndex);
return new WrappedBlockState(type, data, globalID, mappingsIndex);
}
@Override
@@ -426,7 +426,7 @@ public class BlockTags {
BlockTags.SCULK_REPLACEABLE_WORLD_GEN.addTag(BlockTags.SCULK_REPLACEABLE).add(StateTypes.DEEPSLATE_BRICKS, StateTypes.DEEPSLATE_TILES, StateTypes.COBBLED_DEEPSLATE, StateTypes.CRACKED_DEEPSLATE_BRICKS, StateTypes.CRACKED_DEEPSLATE_TILES, StateTypes.POLISHED_DEEPSLATE);
BlockTags.COMPLETES_FIND_TREE_TUTORIAL.addTag(BlockTags.LOGS).addTag(BlockTags.LEAVES).addTag(BlockTags.WART_BLOCKS);
BlockTags.MINEABLE_AXE.addTag(BlockTags.BANNERS).addTag(BlockTags.FENCE_GATES).addTag(BlockTags.LOGS).addTag(BlockTags.PLANKS).addTag(BlockTags.SAPLINGS).addTag(BlockTags.SIGNS).addTag(BlockTags.WOODEN_BUTTONS).addTag(BlockTags.WOODEN_DOORS).addTag(BlockTags.WOODEN_FENCES).addTag(BlockTags.WOODEN_PRESSURE_PLATES).addTag(BlockTags.WOODEN_SLABS).addTag(BlockTags.WOODEN_STAIRS).addTag(BlockTags.WOODEN_TRAPDOORS).addTag(BlockTags.ALL_HANGING_SIGNS).addTag(BlockTags.BAMBOO_BLOCKS).add(StateTypes.NOTE_BLOCK, StateTypes.ATTACHED_MELON_STEM, StateTypes.ATTACHED_PUMPKIN_STEM, StateTypes.AZALEA, StateTypes.BAMBOO, StateTypes.BARREL, StateTypes.BEE_NEST, StateTypes.BEEHIVE, StateTypes.BEETROOTS, StateTypes.BIG_DRIPLEAF_STEM, StateTypes.BIG_DRIPLEAF, StateTypes.BOOKSHELF, StateTypes.BROWN_MUSHROOM_BLOCK, StateTypes.BROWN_MUSHROOM, StateTypes.CAMPFIRE, StateTypes.CARROTS, StateTypes.CARTOGRAPHY_TABLE, StateTypes.CARVED_PUMPKIN, StateTypes.CAVE_VINES_PLANT, StateTypes.CAVE_VINES, StateTypes.CHEST, StateTypes.CHORUS_FLOWER, StateTypes.CHORUS_PLANT, StateTypes.COCOA, StateTypes.COMPOSTER, StateTypes.CRAFTING_TABLE, StateTypes.CRIMSON_FUNGUS, StateTypes.DAYLIGHT_DETECTOR, StateTypes.DEAD_BUSH, StateTypes.FERN, StateTypes.FLETCHING_TABLE, StateTypes.GLOW_LICHEN, StateTypes.SHORT_GRASS, StateTypes.HANGING_ROOTS, StateTypes.JACK_O_LANTERN, StateTypes.JUKEBOX, StateTypes.LADDER, StateTypes.LARGE_FERN, StateTypes.LECTERN, StateTypes.LILY_PAD, StateTypes.LOOM, StateTypes.MELON_STEM, StateTypes.MELON, StateTypes.MUSHROOM_STEM, StateTypes.NETHER_WART, StateTypes.POTATOES, StateTypes.PUMPKIN_STEM, StateTypes.PUMPKIN, StateTypes.RED_MUSHROOM_BLOCK, StateTypes.RED_MUSHROOM, StateTypes.SCAFFOLDING, StateTypes.SMALL_DRIPLEAF, StateTypes.SMITHING_TABLE, StateTypes.SOUL_CAMPFIRE, StateTypes.SPORE_BLOSSOM, StateTypes.SUGAR_CANE, StateTypes.SWEET_BERRY_BUSH, StateTypes.TALL_GRASS, StateTypes.TRAPPED_CHEST, StateTypes.TWISTING_VINES_PLANT, StateTypes.TWISTING_VINES, StateTypes.VINE, StateTypes.WARPED_FUNGUS, StateTypes.WEEPING_VINES_PLANT, StateTypes.WEEPING_VINES, StateTypes.WHEAT, StateTypes.MANGROVE_ROOTS, StateTypes.BAMBOO_MOSAIC, StateTypes.BAMBOO_MOSAIC_SLAB, StateTypes.BAMBOO_MOSAIC_STAIRS, StateTypes.CHISELED_BOOKSHELF);
BlockTags.LAVA_POOL_STONE_CANNOT_REPLACE.addTag(BlockTags.FEATURES_CANNOT_REPLACE).addTag(BlockTags.LEAVES).addTag(BlockTags.LOGS).addTag(BlockTags.FEATURES_CANNOT_REPLACE);
BlockTags.LAVA_POOL_STONE_CANNOT_REPLACE.addTag(BlockTags.FEATURES_CANNOT_REPLACE).addTag(BlockTags.LEAVES).addTag(BlockTags.LOGS);
BlockTags.PARROTS_SPAWNABLE_ON.addTag(BlockTags.LEAVES).addTag(BlockTags.LOGS).add(StateTypes.GRASS_BLOCK, StateTypes.AIR);
// Legacy/Unofficial, I don't know
BlockTags.AZALEA_GROWS_ON.addTag(BlockTags.DIRT).addTag(BlockTags.SAND).addTag(BlockTags.TERRACOTTA).add(StateTypes.SNOW_BLOCK, StateTypes.POWDER_SNOW);
@@ -193,7 +193,7 @@ public class ItemTags {
copy(BlockTags.WOODEN_BUTTONS, ItemTags.WOODEN_BUTTONS);
copy(BlockTags.STONE_BUTTONS, ItemTags.STONE_BUTTONS);
copy(BlockTags.WOOL_CARPETS, ItemTags.WOOL_CARPETS);
ItemTags.WOODEN_DOORS.add(ItemTypes.OAK_DOOR, ItemTypes.SPRUCE_DOOR, ItemTypes.BIRCH_DOOR, ItemTypes.JUNGLE_DOOR, ItemTypes.ACACIA_DOOR, ItemTypes.DARK_OAK_DOOR, ItemTypes.CRIMSON_DOOR, ItemTypes.WARPED_DOOR, ItemTypes.MANGROVE_DOOR, ItemTypes.BAMBOO_DOOR, ItemTypes.CHERRY_DOOR);
copy(BlockTags.WOODEN_DOORS, ItemTags.WOODEN_DOORS);
copy(BlockTags.WOODEN_STAIRS, ItemTags.WOODEN_STAIRS);
copy(BlockTags.WOODEN_SLABS, ItemTags.WOODEN_SLABS);
copy(BlockTags.WOODEN_FENCES, ItemTags.WOODEN_FENCES);
@@ -313,10 +313,10 @@ public class ItemTags {
ItemTags.WOLF_FOOD.addTag(ItemTags.MEAT);
ItemTags.STRIDER_TEMPT_ITEMS.addTag(ItemTags.STRIDER_FOOD).add(ItemTypes.WARPED_FUNGUS_ON_A_STICK);
ItemTags.BOATS.addTag(ItemTags.CHEST_BOATS).add(ItemTypes.OAK_BOAT, ItemTypes.SPRUCE_BOAT, ItemTypes.BIRCH_BOAT, ItemTypes.JUNGLE_BOAT, ItemTypes.ACACIA_BOAT, ItemTypes.DARK_OAK_BOAT, ItemTypes.MANGROVE_BOAT, ItemTypes.BAMBOO_RAFT, ItemTypes.CHERRY_BOAT);
ItemTags.MUSIC_DISCS.addTag(ItemTags.CREEPER_DROP_MUSIC_DISCS).add(ItemTypes.MUSIC_DISC_PIGSTEP, ItemTypes.MUSIC_DISC_OTHERSIDE, ItemTypes.MUSIC_DISC_5, ItemTypes.MUSIC_DISC_RELIC);
ItemTags.MUSIC_DISCS.addTag(ItemTags.CREEPER_DROP_MUSIC_DISCS).add(ItemTypes.MUSIC_DISC_PIGSTEP, ItemTypes.MUSIC_DISC_OTHERSIDE, ItemTypes.MUSIC_DISC_5, ItemTypes.MUSIC_DISC_RELIC, ItemTypes.MUSIC_DISC_CREATOR, ItemTypes.MUSIC_DISC_CREATOR_MUSIC_BOX, ItemTypes.MUSIC_DISC_PRECIPICE);
copy(BlockTags.DAMPENS_VIBRATIONS, ItemTags.DAMPENS_VIBRATIONS);
ItemTags.TRIMMABLE_ARMOR.addTag(ItemTags.FOOT_ARMOR).addTag(ItemTags.LEG_ARMOR).addTag(ItemTags.CHEST_ARMOR).addTag(ItemTags.HEAD_ARMOR);
ItemTags.DECORATED_POT_INGREDIENTS.addTag(ItemTags.DECORATED_POT_SHERDS).add(ItemTypes.BRICK, ItemTypes.FLOW_POTTERY_SHERD, ItemTypes.GUSTER_POTTERY_SHERD, ItemTypes.SCRAPE_POTTERY_SHERD);
ItemTags.DECORATED_POT_INGREDIENTS.addTag(ItemTags.DECORATED_POT_SHERDS).add(ItemTypes.BRICK);
ItemTags.BREAKS_DECORATED_POTS.addTag(ItemTags.SWORDS).addTag(ItemTags.AXES).addTag(ItemTags.PICKAXES).addTag(ItemTags.SHOVELS).addTag(ItemTags.HOES).add(ItemTypes.TRIDENT, ItemTypes.MACE);
ItemTags.ENCHANTABLE_FOOT_ARMOR.addTag(ItemTags.FOOT_ARMOR);
ItemTags.ENCHANTABLE_LEG_ARMOR.addTag(ItemTags.LEG_ARMOR);
@@ -333,7 +333,7 @@ public class ItemTags {
ItemTags.ENCHANTABLE_ARMOR.addTag(ItemTags.ENCHANTABLE_FOOT_ARMOR).addTag(ItemTags.ENCHANTABLE_LEG_ARMOR).addTag(ItemTags.ENCHANTABLE_CHEST_ARMOR).addTag(ItemTags.ENCHANTABLE_HEAD_ARMOR);
ItemTags.ENCHANTABLE_FIRE_ASPECT.addTag(ItemTags.ENCHANTABLE_SWORD).add(ItemTypes.MACE);
ItemTags.ENCHANTABLE_WEAPON.addTag(ItemTags.ENCHANTABLE_SHARP_WEAPON).add(ItemTypes.MACE);
ItemTags.ENCHANTABLE_VANISHING.addTag(ItemTags.ENCHANTABLE_DURABILITY).addTag(ItemTags.SKULLS).add(ItemTypes.COMPASS, ItemTypes.CARVED_PUMPKIN, ItemTypes.MACE);
ItemTags.ENCHANTABLE_VANISHING.addTag(ItemTags.ENCHANTABLE_DURABILITY).addTag(ItemTags.SKULLS).add(ItemTypes.COMPASS, ItemTypes.CARVED_PUMPKIN);
copy(BlockTags.COMPLETES_FIND_TREE_TUTORIAL, ItemTags.COMPLETES_FIND_TREE_TUTORIAL);
}
@@ -18,6 +18,8 @@
package com.github.retrooper.packetevents.resources;
import java.util.Objects;
public class ResourceLocation {
protected final String namespace;
protected final String key;
@@ -48,6 +50,11 @@ public class ResourceLocation {
return key;
}
@Override
public int hashCode() {
return Objects.hash(this.namespace, this.key);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ResourceLocation) {
@@ -35,7 +35,6 @@ public class PacketEventsSettings {
private boolean defaultReencode = true; // true for backwards compat and more idiot-proof
private boolean checkForUpdates = true;
private boolean downsampleColors = false;
private boolean bStatsEnabled = true;
private boolean debugEnabled = false;
private boolean fullStackTraceEnabled = false;
private boolean kickOnPacketExceptionEnabled = true;
@@ -98,9 +97,10 @@ public class PacketEventsSettings {
*
* @param bStatsEnabled Value
* @return Settings instance.
* @deprecated This method has been deprecated. To disable bStats use the config file found in the bStats folder.
*/
@Deprecated()
public PacketEventsSettings bStats(boolean bStatsEnabled) {
this.bStatsEnabled = bStatsEnabled;
return this;
}
@@ -179,10 +179,13 @@ public class PacketEventsSettings {
/**
* Should we collect server data anonymously and report to bStats?
*
* @return Getter for {@link #bStatsEnabled}
* @return Getter for bStatsEnabled
* @deprecated This method has been deprecated, because the setter has been removed.
* Disabling bStats is now done through the bStats config file.
*/
@Deprecated
public boolean isbStatsEnabled() {
return bStatsEnabled;
return true;
}
/**
@@ -1,6 +1,6 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
* Copyright (C) 2024 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,142 +18,223 @@
package com.github.retrooper.packetevents.util;
import java.util.Arrays;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* PacketEvents version.
* This class represents a PacketEvents version.
*
* @author retrooper
* @since 1.8
* Represents a PacketEvents version using Semantic Versioning.
* Supports version comparison, cloning, and provides a string representation.
* Snapshots will always resolve to a newer version than the non-snapshot version if major, minor, and patch are the same.
*/
public class PEVersion {
/**
* Array containing the digits in the version.
* For example, "1.8.9" will be stored as {1, 8, 9} in an array.
*/
private final int[] versionIntArray;
public class PEVersion implements Comparable<PEVersion> {
private final int major;
private final int minor;
private final int patch;
private final boolean snapshot;
/**
* Specify your version using an array.
* Constructs a {@link PEVersion} instance.
*
* @param version Array version.
* @param major the major version number.
* @param minor the minor version number.
* @param patch the patch version number.
* @param snapshot whether the version is a snapshot.
*/
public PEVersion(final int major, final int minor, final int patch, final boolean snapshot) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.snapshot = snapshot;
}
/**
* Constructs a {@link PEVersion} instance with snapshot defaulted to false.
*
* @param major the major version number.
* @param minor the minor version number.
* @param patch the patch version number.
*/
public PEVersion(final int major, final int minor, final int patch) {
this(major, minor, patch, false);
}
/**
* Constructs a {@link PEVersion} instance from an array of version numbers.
*
* @param version the version numbers (e.g., {1, 8, 9}).
* @deprecated use {@link #PEVersion(int, int, int)} or {@link #PEVersion(int, int, int, boolean)} instead.
*/
@Deprecated
public PEVersion(final int... version) {
this.versionIntArray = version;
this(version[0], version[1], version[2]);
}
/**
* Specify your version using a string, for example: "1.8.9".
* Constructs a {@link PEVersion} instance from a version string.
*
* @param version String version.
* @param version the version string (e.g., "1.8.9-SNAPSHOT").
* @throws IllegalArgumentException if the version string format is incorrect.
* @deprecated use {@link #fromString(String)} instead.
*/
public PEVersion(final String version) {
String[] versionIntegers = version.split("\\.");
int length = versionIntegers.length;
this.versionIntArray = new int[length];
for (int i = 0; i < length; i++) {
versionIntArray[i] = Integer.parseInt(versionIntegers[i]);
@Deprecated
public PEVersion(@NotNull final String version) {
String versionWithoutSnapshot = version.replace("-SNAPSHOT", "");
String[] parts = versionWithoutSnapshot.split("\\.");
if (parts.length < 2 || parts.length > 3) {
throw new IllegalArgumentException("Version string must be in the format 'major.minor[.patch][-SNAPSHOT]' found '" + version + "' instead.");
}
this.major = Integer.parseInt(parts[0]);
this.minor = Integer.parseInt(parts[1]);
this.patch = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
this.snapshot = version.contains("-SNAPSHOT");
}
/**
* Compare to another PEVersion.
* If we are newer than the compared version,
* this method will return 1.
* If we are older than the compared version,
* this method will return -1.
* If we are equal to the compared version,
* this method will return 0.
* Similar to {@link Integer#compareTo(Integer)}.
* Constructs a {@link PEVersion} instance from a version string.
*
* @param version Compared version
* @return Comparing to another Version.
* @param version the version string (e.g., "1.8.9-SNAPSHOT").
* @throws IllegalArgumentException if the version string format is incorrect.
*/
public int compareTo(PEVersion version) {
int localLength = versionIntArray.length;
int oppositeLength = version.versionIntArray.length;
int length = Math.max(localLength, oppositeLength);
for (int i = 0; i < length; i++) {
int localInteger = i < localLength ? versionIntArray[i] : 0;
int oppositeInteger = i < oppositeLength ? version.versionIntArray[i] : 0;
if (localInteger > oppositeInteger) {
return 1;
} else if (localInteger < oppositeInteger) {
return -1;
}
}
return 0;
public static PEVersion fromString(@NotNull final String version) {
return new PEVersion(version);
}
/**
* Does the {@link #compareTo(PEVersion)} return 1?
* Gets the major version number.
*
* @param version Compared version.
* @return Is this newer than the compared version.
* @return the major version number.
*/
public boolean isNewerThan(PEVersion version) {
return compareTo(version) == 1;
public int major() {
return major;
}
/**
* Does the {@link #compareTo(PEVersion)} return -1?
* Gets the minor version number.
*
* @param version Compared version.
* @return Is this older than the compared version.
* @return the minor version number.
*/
public boolean isOlderThan(PEVersion version) {
return compareTo(version) == -1;
public int minor() {
return minor;
}
/**
* Represented as an array.
* Gets the patch version number.
*
* @return Array version.
* @return the patch version number.
*/
public int[] asArray() {
return versionIntArray;
public int patch() {
return patch;
}
/**
* Is this version equal to the compared object.
* The object must be a PEVersion and the array values must be equal.
* Checks if the version is a snapshot.
*
* @param obj Compared object.
* @return Are they equal?
* @return true if snapshot, false otherwise.
*/
public boolean snapshot() {
return snapshot;
}
/**
* Compares this {@link PEVersion} with another {@link PEVersion}.
*
* @param other the other {@link PEVersion}.
* @return a negative integer, zero, or a positive integer as this version is less than,
* equal to, or greater than the specified version.
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof PEVersion) {
return Arrays.equals(versionIntArray, ((PEVersion) obj).versionIntArray);
}
return false;
public int compareTo(@NotNull final PEVersion other) {
int majorCompare = Integer.compare(this.major, other.major);
if (majorCompare != 0) return majorCompare;
int minorCompare = Integer.compare(this.minor, other.minor);
if (minorCompare != 0) return minorCompare;
int patchCompare = Integer.compare(this.patch, other.patch);
if (patchCompare != 0) return patchCompare;
return Boolean.compare(other.snapshot, this.snapshot);
}
/**
* Clone the PEVersion.
* Checks if the provided object is equal to this {@link PEVersion}.
*
* @return A clone.
* @param obj the object to compare.
* @return true if the provided object is equal to this {@link PEVersion}, false otherwise.
*/
@Override
public boolean equals(@NotNull final Object obj) {
if (this == obj) return true;
if (!(obj instanceof PEVersion)) return false;
PEVersion other = (PEVersion) obj;
return this.major == other.major &&
this.minor == other.minor &&
this.patch == other.patch &&
this.snapshot == other.snapshot;
}
/**
* Checks if this version is newer than the provided version.
*
* @param otherVersion the other {@link PEVersion}.
* @return true if this version is newer, false otherwise.
*/
public boolean isNewerThan(@NotNull final PEVersion otherVersion) {
return this.compareTo(otherVersion) > 0;
}
/**
* Checks if this version is older than the provided version.
*
* @param otherVersion the other {@link PEVersion}.
* @return true if this version is older, false otherwise.
*/
public boolean isOlderThan(@NotNull final PEVersion otherVersion) {
return this.compareTo(otherVersion) < 0;
}
/**
* Returns a hash code value for this {@link PEVersion}.
*
* @return a hash code value.
*/
@Override
public int hashCode() {
return Objects.hash(major, minor, patch, snapshot);
}
/**
* Creates and returns a copy of this {@link PEVersion}.
*
* @return a clone of this instance.
*/
@Override
public PEVersion clone() {
return new PEVersion(versionIntArray);
return new PEVersion(major, minor, patch, snapshot);
}
/**
* Represent the version as a string.
* Converts the {@link PEVersion} to a string representation.
*
* @return String representation.
* @return a string representation of the version.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(versionIntArray.length * 2 - 1).append(versionIntArray[0]);
for (int i = 1; i < versionIntArray.length; i++) {
sb.append(".").append(versionIntArray[i]);
}
return sb.toString();
return major + "." + minor + "." + patch + (snapshot ? "-SNAPSHOT" : "");
}
}
/**
* Converts the {@link PEVersion} to an array of version numbers.
*
* @return an array of version numbers.
*/
@Deprecated
public int[] asArray() {
return new int[]{major, minor, patch};
}
}
@@ -24,6 +24,7 @@ import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.event.UserDisconnectEvent;
import com.github.retrooper.packetevents.manager.protocol.ProtocolManager;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper;
import com.github.retrooper.packetevents.protocol.player.User;
import org.jetbrains.annotations.Nullable;
@@ -70,7 +71,7 @@ public class PacketEventsImplHelper {
return packetSendEvent;
}
public static PacketReceiveEvent handleServerBoundPacket(Object channel, User user,
public static Object handleServerBoundPacket(Object channel, User user,
Object player,
Object buffer,
boolean autoProtocolTranslation) throws Exception {
@@ -79,14 +80,17 @@ public class PacketEventsImplHelper {
int preProcessIndex = ByteBufHelper.readerIndex(buffer);
PacketReceiveEvent packetReceiveEvent = EventCreationUtil.createReceiveEvent(channel, user, player, buffer, autoProtocolTranslation);
int processIndex = ByteBufHelper.readerIndex(buffer);
Object finalBuffer = buffer;
PacketEvents.getAPI().getEventManager().callEvent(packetReceiveEvent, () -> {
ByteBufHelper.readerIndex(buffer, processIndex);
ByteBufHelper.readerIndex(finalBuffer, processIndex);
});
if (!packetReceiveEvent.isCancelled()) {
//Did they ever use a wrapper?
if (packetReceiveEvent.getLastUsedWrapper() != null) {
//Rewrite the buffer
ByteBufHelper.clear(buffer);
buffer = UnpooledByteBufAllocationHelper.buffer();
packetReceiveEvent.getLastUsedWrapper().setBuffer(buffer);
packetReceiveEvent.getLastUsedWrapper().writeVarInt(packetReceiveEvent.getPacketId());
packetReceiveEvent.getLastUsedWrapper().write();
} else {
@@ -103,7 +107,7 @@ public class PacketEventsImplHelper {
task.run();
}
}
return packetReceiveEvent;
return buffer;
}
public static void handleDisconnection(Object channel, @Nullable UUID uuid) {
@@ -21,9 +21,12 @@ package com.github.retrooper.packetevents.util;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.protocol.player.Equipment;
import com.github.retrooper.packetevents.protocol.world.chunk.LightData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateLight;
import java.util.Collections;
@@ -51,6 +54,23 @@ public class PacketTransformationUtil {
Equipment equipment = entityEquipment.getEquipment().get(i);
output[i] = new WrapperPlayServerEntityEquipment(entityEquipment.getEntityId(), Collections.singletonList(equipment));
}
return output;
}
} else if (wrapper instanceof WrapperPlayServerChunkData) {
WrapperPlayServerChunkData chunkData = (WrapperPlayServerChunkData) wrapper;
LightData lightData = chunkData.getLightData();
if (chunkData.getServerVersion().isOlderThan(ServerVersion.V_1_18) && lightData != null) {
//Transform into chunk data & light data packets
PacketWrapper<?>[] output = new PacketWrapper[2];
output[0] = new WrapperPlayServerUpdateLight(
chunkData.getColumn().getX(),
chunkData.getColumn().getZ(),
lightData
);
// Light packet must be sent first
output[1] = chunkData;
return output;
}
}
@@ -18,21 +18,55 @@
package com.github.retrooper.packetevents.util.adventure;
import com.github.retrooper.packetevents.protocol.nbt.*;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTByteArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTDouble;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTIntArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTLong;
import com.github.retrooper.packetevents.protocol.nbt.NBTLongArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTShort;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.*;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.EntityNBTComponent;
import net.kyori.adventure.text.KeybindComponent;
import net.kyori.adventure.text.NBTComponent;
import net.kyori.adventure.text.ScoreComponent;
import net.kyori.adventure.text.SelectorComponent;
import net.kyori.adventure.text.StorageNBTComponent;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.DataComponentValue;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.text.serializer.gson.BackwardCompatUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -79,7 +113,39 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
String translate = reader.readUTF("translate", Function.identity());
String translateFallback = reader.readUTF("fallback", Function.identity());
List<Component> translateWith = reader.readList("with", this::deserializeComponentList);
List<? extends ComponentLike> translateWith;
if (BackwardCompatUtil.IS_4_15_0_OR_NEWER) {
NBTType<?> type = reader.type("with");
if (type == NBTType.INT_ARRAY) {
translateWith = reader.readIntArray("with", params -> {
List<TranslationArgument> args = new ArrayList<>(params.length);
for (int param : params) {
args.add(TranslationArgument.numeric(param));
}
return args;
});
} else if (type == NBTType.BYTE_ARRAY) {
translateWith = reader.readByteArray("with", params -> {
List<TranslationArgument> args = new ArrayList<>(params.length);
for (byte param : params) {
args.add(TranslationArgument.bool(param != (byte) 0));
}
return args;
});
} else if (type == NBTType.LONG_ARRAY) {
translateWith = reader.readLongArray("with", params -> {
List<TranslationArgument> args = new ArrayList<>(params.length);
for (long param : params) {
args.add(TranslationArgument.numeric(param));
}
return args;
});
} else {
translateWith = reader.readList("with", this::deserializeTranslationArgumentList);
}
} else {
translateWith = reader.readList("with", this::deserializeComponentList);
}
NBTReader score = reader.child("score");
String selector = reader.readUTF("selector", Function.identity());
String keybind = reader.readUTF("keybind", Function.identity());
@@ -98,7 +164,11 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
builder = Component.text().content(text);
} else if (translate != null) {
if (translateWith != null) {
builder = Component.translatable().key(translate).fallback(translateFallback).arguments(translateWith);
if (BackwardCompatUtil.IS_4_15_0_OR_NEWER) {
builder = Component.translatable().key(translate).fallback(translateFallback).arguments(translateWith);
} else {
builder = Component.translatable().key(translate).fallback(translateFallback).args(translateWith);
}
} else {
builder = Component.translatable().key(translate).fallback(translateFallback);
}
@@ -168,7 +238,11 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
// translation arguments
List<Component> args = ((TranslatableComponent) component).args();
if (!args.isEmpty()) {
writer.writeList("with", NBTType.COMPOUND, serializeComponentList(args));
if (BackwardCompatUtil.IS_4_15_0_OR_NEWER) {
writer.writeList("with", NBTType.COMPOUND, serializeTranslationArgumentList(((TranslatableComponent) component).arguments()));
} else {
writer.writeList("with", NBTType.COMPOUND, serializeComponentList(args));
}
}
} else if (component instanceof ScoreComponent) {
// nested compound
@@ -271,11 +345,30 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
style.hoverEvent(HoverEvent.showItem(hoverEvent.readUTF("contents", Key::key), 1));
} else {
NBTReader child = hoverEvent.child("contents");
style.hoverEvent(HoverEvent.showItem(
child.readUTF("id", Key::key),
child.readInt("count", Function.identity()),
child.readUTF("tag", BinaryTagHolder::binaryTagHolder)
));
Key itemId = child.readUTF("id", Key::key);
Integer count = child.readInt("count", Function.identity());
int nonNullCount = count == null ? 1 : count;
BinaryTagHolder tag = child.readUTF("tag", BinaryTagHolder::binaryTagHolder);
if (tag != null || !BackwardCompatUtil.IS_4_17_0_OR_NEWER) {
style.hoverEvent(HoverEvent.showItem(itemId, nonNullCount, tag));
} else {
Map<Key, DataComponentValue> components = child.readCompound("components", nbt -> {
Map<Key, DataComponentValue> map = new HashMap<>(nbt.size());
for (Map.Entry<String, NBT> entry : nbt.getTags().entrySet()) {
if (entry.getKey().startsWith("!")) { // removed component
Key key = Key.key(entry.getKey().substring(1));
map.put(key, DataComponentValue.removed());
continue;
}
Key key = Key.key(entry.getKey());
map.put(key, new NbtComponentValue(entry.getValue()));
}
return map;
});
style.hoverEvent(HoverEvent.showItem(itemId, nonNullCount,
components == null ? Collections.emptyMap() : components));
}
}
} else if (action.equals(HoverEvent.Action.SHOW_ENTITY)) {
NBTReader child = hoverEvent.child("contents");
@@ -332,14 +425,32 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
Key itemId = item.item();
int count = item.count();
BinaryTagHolder nbt = item.nbt();
boolean emptyComps = !BackwardCompatUtil.IS_4_17_0_OR_NEWER || item.dataComponents().isEmpty();
if (count == 1 && nbt == null) {
if (count == 1 && nbt == null && emptyComps) {
child.writeUTF("contents", itemId.asString());
} else {
NBTWriter itemNBT = child.child("contents");
itemNBT.writeUTF("id", itemId.asString());
itemNBT.writeInt("count", count);
if (nbt != null) itemNBT.writeUTF("tag", nbt.string());
if (nbt != null) {
itemNBT.writeUTF("tag", nbt.string());
}
if (!emptyComps) {
NBTWriter compsNbt = itemNBT.child("components");
for (Map.Entry<Key, DataComponentValue> entry : item.dataComponents().entrySet()) {
if (entry.getValue() == DataComponentValue.removed()) {
// I don't think value matters here
compsNbt.writeCompound("!" + entry.getKey(), new NBTCompound());
continue;
}
if (entry.getValue() instanceof NbtComponentValue) {
NBT compNbt = ((NbtComponentValue) entry.getValue()).nbt;
compsNbt.write(entry.getKey().toString(), compNbt);
}
// unsupported entry component value, skip for now
}
}
}
break;
}
@@ -427,6 +538,35 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
}
// -------------------------------------------------
// ------------ TranslationArgument List ------------
private @NotNull List<TranslationArgument> deserializeTranslationArgumentList(List<?> value) {
if (value.isEmpty()) return Collections.emptyList();
List<TranslationArgument> arguments = new ArrayList<>(value.size());
for (Object nbt : value) {
if (nbt instanceof NBTByte) {
arguments.add(TranslationArgument.bool(((NBTByte) nbt).getAsByte() != (byte) 0));
} else if (nbt instanceof NBTNumber) {
arguments.add(TranslationArgument.numeric(((NBTNumber) nbt).getAsInt()));
} else if (nbt instanceof NBTString) {
arguments.add(TranslationArgument.component(Component.text(((NBTString) nbt).getValue())));
} else {
arguments.add(TranslationArgument.component(deserialize(requireType((NBT) nbt, NBTType.COMPOUND))));
}
}
return arguments;
}
private List<NBTCompound> serializeTranslationArgumentList(List<TranslationArgument> value) {
List<NBTCompound> arguments = new ArrayList<>(value.size());
for (TranslationArgument argument : value) {
arguments.add(serializeComponent(argument.asComponent()));
}
return arguments;
}
// -------------------------------------------------
static class NBTReader {
private final NBTCompound compound;
@@ -644,4 +784,13 @@ public class AdventureNBTSerializer implements ComponentSerializer<Component, Co
}
return (T) nbt;
}
private static final class NbtComponentValue implements DataComponentValue {
private final NBT nbt;
public NbtComponentValue(NBT nbt) {
this.nbt = nbt;
}
}
}
@@ -1,168 +0,0 @@
package com.github.retrooper.packetevents.util.adventure;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.util.reflection.Reflection;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.json.JSONOptions;
import net.kyori.adventure.util.Index;
import net.kyori.option.OptionState;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.function.Function;
public class AdventureReflectionUtil {
static boolean IS_LEGACY_ADVENTURE;
static boolean IS_4_15_0_OR_NEWER = false;
static TypeAdapter<Key> KEY_SERIALIZER_INSTANCE;
static Function<Gson, Object> COMPONENT_SERIALIZER_CREATE;
static TypeAdapter<ClickEvent.Action> CLICK_EVENT_ACTION_SERIALIZER_INSTANCE;
static TypeAdapter<HoverEvent.Action<?>> HOVER_EVENT_ACTION_SERIALIZER_INSTANCE;
static Function<Gson, Object> SHOW_ITEM_SERIALIZER_CREATE;
static Function<Gson, Object> SHOW_ENTITY_SERIALIZER_CREATE;
static TypeAdapter<TextColor> TEXT_COLOR_SERIALIZER_INSTANCE;
static TypeAdapter<TextColor> TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE;
static TypeAdapter<TextDecoration> TEXT_DECORATION_SERIALIZER_INSTANCE;
static TypeAdapter<BlockNBTComponent.Pos> BLOCK_NBT_POS_SERIALIZER_INSTANCE;
static TypeAdapter<UUID> UUID_SERIALIZER_INSTANCE;
static Function<Gson, Object> TRANSLATION_ARGUMENT_SERIALIZER_CREATE;
static {
IS_LEGACY_ADVENTURE = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.SerializerFactory") == null;
Class<?> KEY_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.KeySerializer");
Field KEY_SERIALIZER_INSTANCE_FIELD = Reflection.getField(KEY_SERIALIZER, "INSTANCE");
KEY_SERIALIZER_INSTANCE = (TypeAdapter<Key>) getSafe(KEY_SERIALIZER_INSTANCE_FIELD);
Class<?> COMPONENT_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.ComponentSerializerImpl");
Class<?> SHOW_ITEM_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.ShowItemSerializer");
Class<?> SHOW_ENTITY_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.ShowEntitySerializer");
if (IS_LEGACY_ADVENTURE) {
Constructor<?> COMPONENT_SERIALIZER_CONSTRUCTOR = Reflection.getConstructor(COMPONENT_SERIALIZER, 0);
COMPONENT_SERIALIZER_CREATE = gson -> invokeSafe(COMPONENT_SERIALIZER_CONSTRUCTOR);
Class<?> INDEXED_SERIALIZER_CLASS = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.IndexedSerializer");
Method INDEXED_SERIALIZER_CREATE_METHOD = Reflection.getMethod(INDEXED_SERIALIZER_CLASS, "of", String.class, Index.class);
CLICK_EVENT_ACTION_SERIALIZER_INSTANCE = (TypeAdapter<ClickEvent.Action>) invokeSafe(INDEXED_SERIALIZER_CREATE_METHOD, "click action", ClickEvent.Action.NAMES);
HOVER_EVENT_ACTION_SERIALIZER_INSTANCE = (TypeAdapter<HoverEvent.Action<?>>) invokeSafe(INDEXED_SERIALIZER_CREATE_METHOD, "hover action", HoverEvent.Action.NAMES);
TEXT_DECORATION_SERIALIZER_INSTANCE = (TypeAdapter<TextDecoration>) invokeSafe(INDEXED_SERIALIZER_CREATE_METHOD, "text decoration", TextDecoration.NAMES);
Constructor<?> SHOW_ITEM_SERIALIZER_CONSTRUCTOR = Reflection.getConstructor(SHOW_ITEM_SERIALIZER, 0);
SHOW_ITEM_SERIALIZER_CREATE = gson -> invokeSafe(SHOW_ITEM_SERIALIZER_CONSTRUCTOR);
Constructor<?> SHOW_ENTITY_SERIALIZER_CONSTRUCTOR = Reflection.getConstructor(SHOW_ENTITY_SERIALIZER, 0);
SHOW_ENTITY_SERIALIZER_CREATE = gson -> invokeSafe(SHOW_ENTITY_SERIALIZER_CONSTRUCTOR);
} else {
IS_4_15_0_OR_NEWER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.TranslationArgumentSerializer") != null;
final Object optionState;
if (IS_4_15_0_OR_NEWER) {
// adventure >= v4.15; add option state for serializer construction
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_16)) {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
optionState = JSONOptions.byDataVersion();
} else {
optionState = JSONOptions.byDataVersion().at(2526);
}
} else {
optionState = JSONOptions.byDataVersion().at(2525);
}
Method COMPONENT_SERIALIZER_CREATE_METHOD = Reflection.getMethod(COMPONENT_SERIALIZER, "create", OptionState.class, Gson.class);
COMPONENT_SERIALIZER_CREATE = gson -> invokeSafe(COMPONENT_SERIALIZER_CREATE_METHOD, optionState, gson);
} else {
// adventure < 4.15; no option states available
optionState = null;
Method COMPONENT_SERIALIZER_CREATE_METHOD = Reflection.getMethod(COMPONENT_SERIALIZER, "create", Gson.class);
COMPONENT_SERIALIZER_CREATE = gson -> invokeSafe(COMPONENT_SERIALIZER_CREATE_METHOD, gson);
}
Class<?> CLICK_EVENT_ACTION_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.ClickEventActionSerializer");
Field CLICK_EVENT_ACTION_SERIALIZER_INSTANCE_FIELD = Reflection.getField(CLICK_EVENT_ACTION_SERIALIZER, "INSTANCE");
CLICK_EVENT_ACTION_SERIALIZER_INSTANCE = (TypeAdapter<ClickEvent.Action>) getSafe(CLICK_EVENT_ACTION_SERIALIZER_INSTANCE_FIELD);
Class<?> HOVER_EVENT_ACTION_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.HoverEventActionSerializer");
Field HOVER_EVENT_ACTION_SERIALIZER_INSTANCE_FIELD = Reflection.getField(HOVER_EVENT_ACTION_SERIALIZER, "INSTANCE");
HOVER_EVENT_ACTION_SERIALIZER_INSTANCE = (TypeAdapter<HoverEvent.Action<?>>) getSafe(HOVER_EVENT_ACTION_SERIALIZER_INSTANCE_FIELD);
Class<?> TEXT_DECORATION_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.TextDecorationSerializer");
Field TEXT_DECORATION_SERIALIZER_INSTANCE_FIELD = Reflection.getField(TEXT_DECORATION_SERIALIZER, "INSTANCE");
TEXT_DECORATION_SERIALIZER_INSTANCE = (TypeAdapter<TextDecoration>) getSafe(TEXT_DECORATION_SERIALIZER_INSTANCE_FIELD);
Method SHOW_ITEM_SERIALIZER_CREATE_METHOD = Reflection.getMethod(SHOW_ITEM_SERIALIZER, "create", Gson.class);
SHOW_ITEM_SERIALIZER_CREATE = gson -> invokeSafe(SHOW_ITEM_SERIALIZER_CREATE_METHOD, gson);
Method SHOW_ENTITY_SERIALIZER_CREATE_METHOD = Reflection.getMethod(SHOW_ENTITY_SERIALIZER, "create", Gson.class);
SHOW_ENTITY_SERIALIZER_CREATE = gson -> invokeSafe(SHOW_ENTITY_SERIALIZER_CREATE_METHOD, gson);
if (IS_4_15_0_OR_NEWER) {
Class<?> UUID_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.UUIDSerializer");
Method UUID_SERIALIZER_CREATE = Reflection.getMethod(UUID_SERIALIZER, "uuidSerializer", OptionState.class);
UUID_SERIALIZER_INSTANCE = (TypeAdapter<UUID>) invokeSafe(UUID_SERIALIZER_CREATE, optionState);
Class<?> TRANSLATION_ARGUMENT_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.TranslationArgumentSerializer");
Method TRANSLATION_ARGUMENT_SERIALIZER_CREATE_METHOD = Reflection.getMethod(TRANSLATION_ARGUMENT_SERIALIZER, "create", Gson.class);
TRANSLATION_ARGUMENT_SERIALIZER_CREATE = gson -> invokeSafe(TRANSLATION_ARGUMENT_SERIALIZER_CREATE_METHOD, gson);
}
}
Class<?> TEXT_COLOR_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.TextColorSerializer");
Field TEXT_COLOR_SERIALIZER_INSTANCE_FIELD = Reflection.getField(TEXT_COLOR_SERIALIZER, "INSTANCE");
TEXT_COLOR_SERIALIZER_INSTANCE = (TypeAdapter<TextColor>) getSafe(TEXT_COLOR_SERIALIZER_INSTANCE_FIELD);
Field TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE_FIELD = Reflection.getField(TEXT_COLOR_SERIALIZER, "DOWNSAMPLE_COLOR");
TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE = (TypeAdapter<TextColor>) getSafe(TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE_FIELD);
Class<?> BLOCK_NBT_COMPONENT_POS_SERIALIZER = Reflection.getClassByNameWithoutException("net.kyori.adventure.text.serializer.gson.BlockNBTComponentPosSerializer");
Field BLOCK_NBT_COMPONENT_POS_SERIALIZER_INSTANCE_FIELD = Reflection.getField(BLOCK_NBT_COMPONENT_POS_SERIALIZER, "INSTANCE");
BLOCK_NBT_POS_SERIALIZER_INSTANCE = (TypeAdapter<BlockNBTComponent.Pos>) getSafe(BLOCK_NBT_COMPONENT_POS_SERIALIZER_INSTANCE_FIELD);
}
static Object getSafe(Field field) {
return getSafe(field, null);
}
static Object getSafe(Field field, Object instance) {
if (field == null) return null;
try {
return field.get(instance);
} catch (Exception e) {
return null;
}
}
static Object invokeSafe(Method method, Object... params) {
return invokeSafe(null, method, params);
}
static Object invokeSafe(Object instance, Method method, Object... params) {
if (method == null) return null;
try {
return method.invoke(instance, params);
} catch (Exception e) {
return null;
}
}
static Object invokeSafe(Constructor<?> constructor, Object... params) {
if (constructor == null) return null;
try {
return constructor.newInstance(params);
} catch (Exception e) {
return null;
}
}
}
@@ -21,9 +21,12 @@ package com.github.retrooper.packetevents.util.adventure;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.stats.Statistics;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.json.JSONOptions;
import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public class AdventureSerializer {
@@ -35,10 +38,17 @@ public class AdventureSerializer {
public static GsonComponentSerializer getGsonSerializer() {
if (GSON == null) {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
GSON = new GsonComponentSerializerExtended(
version.isOlderThan(ServerVersion.V_1_16) || PacketEvents.getAPI().getSettings().shouldDownsampleColors(),
version.isOlderThanOrEquals(ServerVersion.V_1_12_2)
);
GSON = GsonComponentSerializer.builder().editOptions(builder -> {
boolean is_1_20_3_or_new = version.isNewerThanOrEquals(ServerVersion.V_1_20_3);
builder.value(JSONOptions.EMIT_RGB, version.isNewerThanOrEquals(ServerVersion.V_1_16) && !PacketEvents.getAPI().getSettings().shouldDownsampleColors());
builder.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.BOTH);
builder.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, is_1_20_3_or_new);
builder.value(JSONOptions.VALIDATE_STRICT_EVENTS, is_1_20_3_or_new);
builder.value(JSONOptions.SHOW_ITEM_HOVER_DATA_MODE, JSONOptions.ShowItemHoverDataMode.EMIT_EITHER);
})
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
.showAchievementToComponent(input -> Statistics.getById(input).display())
.build();
}
return GSON;
}
@@ -1,119 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.util.adventure;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.UnaryOperator;
/**
* Implementing adventures {@link GsonComponentSerializer} to fix jsons for legacy versions.
*/
public class GsonComponentSerializerExtended implements GsonComponentSerializer {
private final Gson serializer;
private final UnaryOperator<GsonBuilder> populator;
public GsonComponentSerializerExtended(final boolean downsampleColor, final boolean emitLegacyHover) {
if (AdventureReflectionUtil.IS_LEGACY_ADVENTURE) {
this.populator = builder -> {
builder.registerTypeHierarchyAdapter(Key.class, AdventureReflectionUtil.KEY_SERIALIZER_INSTANCE);
builder.registerTypeHierarchyAdapter(Component.class, AdventureReflectionUtil.COMPONENT_SERIALIZER_CREATE.apply(null));
builder.registerTypeHierarchyAdapter(Style.class, new Legacy_StyleSerializerExtended(emitLegacyHover));
builder.registerTypeAdapter(ClickEvent.Action.class, AdventureReflectionUtil.CLICK_EVENT_ACTION_SERIALIZER_INSTANCE);
builder.registerTypeAdapter(HoverEvent.Action.class, AdventureReflectionUtil.HOVER_EVENT_ACTION_SERIALIZER_INSTANCE);
builder.registerTypeAdapter(HoverEvent.ShowItem.class, AdventureReflectionUtil.SHOW_ITEM_SERIALIZER_CREATE.apply(null));
builder.registerTypeAdapter(HoverEvent.ShowEntity.class, AdventureReflectionUtil.SHOW_ENTITY_SERIALIZER_CREATE.apply(null));
builder.registerTypeAdapter(TextColorWrapper.class, TextColorWrapper.Serializer.INSTANCE);
builder.registerTypeHierarchyAdapter(TextColor.class, downsampleColor ? AdventureReflectionUtil.TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE : AdventureReflectionUtil.TEXT_COLOR_SERIALIZER_INSTANCE);
builder.registerTypeAdapter(TextDecoration.class, AdventureReflectionUtil.TEXT_DECORATION_SERIALIZER_INSTANCE);
builder.registerTypeHierarchyAdapter(BlockNBTComponent.Pos.class, AdventureReflectionUtil.BLOCK_NBT_POS_SERIALIZER_INSTANCE);
return builder;
};
} else {
this.populator = builder -> {
builder.registerTypeAdapterFactory(new SerializerFactory(downsampleColor, emitLegacyHover));
return builder;
};
}
this.serializer = this.populator.apply(new GsonBuilder().disableHtmlEscaping()).create();
}
@Override
public @NotNull Gson serializer() {
return this.serializer;
}
@Override
public @NotNull UnaryOperator<GsonBuilder> populator() {
return this.populator;
}
@Override
public @NotNull Component deserialize(final @NotNull String string) {
final Component component = this.serializer().fromJson(string, Component.class);
if (component == null) throw new JsonParseException("Don't know how to turn " + string + " into a Component");
return component;
}
@Override
public @Nullable Component deserializeOr(final @Nullable String input, final @Nullable Component fallback) {
if (input == null) return fallback;
final Component component = this.serializer().fromJson(input, Component.class);
if (component == null) return fallback;
return component;
}
@Override
public @NotNull String serialize(final @NotNull Component component) {
return this.serializer().toJson(component);
}
@Override
public @NotNull Component deserializeFromTree(final @NotNull JsonElement input) {
final Component component = this.serializer().fromJson(input, Component.class);
if (component == null) throw new JsonParseException("Don't know how to turn " + input + " into a Component");
return component;
}
@Override
public @NotNull JsonElement serializeToTree(final @NotNull Component component) {
return this.serializer().toJsonTree(component);
}
@Override
public @NotNull Builder toBuilder() {
throw new UnsupportedOperationException("Builder pattern is not supported."); // We don't need to support this
}
}
@@ -1,173 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.util.adventure;
import com.github.retrooper.packetevents.protocol.stats.Statistics;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.util.Codec;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.io.IOException;
import java.util.Locale;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HoverSerializer {
private static final TagStringIO SNBT_IO = TagStringIO.get();
private static final Codec<CompoundBinaryTag, String, IOException, IOException> SNBT_CODEC = createCodec(SNBT_IO::asCompound, SNBT_IO::asString);
static final String ITEM_TYPE = "id";
static final String ITEM_COUNT = "Count";
static final String ITEM_TAG = "tag";
static final String ENTITY_NAME = "name";
static final String ENTITY_TYPE = "type";
static final String ENTITY_ID = "id";
static final Pattern LEGACY_NAME_PATTERN = Pattern.compile("([A-Z][a-z]+)([A-Z][a-z]+)?");
public HoverEvent.ShowItem deserializeShowItem(final GsonLike gson, final JsonElement input, boolean legacy) throws IOException {
if (legacy) {
final Component component = gson.fromJson(input, Component.class);
assertTextComponent(component);
final CompoundBinaryTag contents = SNBT_CODEC.decode(((TextComponent) component).content());
final CompoundBinaryTag tag = contents.getCompound(ITEM_TAG);
return createShowItem(
Key.key(contents.getString(ITEM_TYPE)),
contents.getByte(ITEM_COUNT, (byte) 1),
tag == CompoundBinaryTag.empty() ? null : BinaryTagHolder.encode(tag, SNBT_CODEC)
);
} else {
return gson.fromJson(input, HoverEvent.ShowItem.class);
}
}
public HoverEvent.ShowEntity deserializeShowEntity(final GsonLike gson, final JsonElement input, final Codec.Decoder<Component, String, ? extends RuntimeException> componentCodec, boolean legacy) throws IOException {
if (legacy) {
final Component component = gson.fromJson(input, Component.class);
assertTextComponent(component);
final CompoundBinaryTag contents = SNBT_CODEC.decode(((TextComponent) component).content());
String type = contents.getString(ENTITY_TYPE);
final Matcher matcher = LEGACY_NAME_PATTERN.matcher(type);
if (matcher.matches()) {
final StringJoiner joiner = new StringJoiner("_");
joiner.add(matcher.group(1));
if (matcher.group(2) != null) {
joiner.add(matcher.group(2));
}
type = joiner.toString().toLowerCase(Locale.ROOT);
}
return createShowEntity(
Key.key(type),
UUID.fromString(contents.getString(ENTITY_ID)),
componentCodec.decode(contents.getString(ENTITY_NAME))
);
} else {
return gson.fromJson(input, HoverEvent.ShowEntity.class);
}
}
public Component deserializeShowAchievement(final JsonElement input) {
assertStringValue(input);
return Statistics.getById(input.getAsString()).display();
}
private static void assertStringValue(final JsonElement element) {
if (!(element.isJsonPrimitive() && ((JsonPrimitive) element).isString())) {
throw new IllegalArgumentException("Legacy events must be single Component instances");
}
}
private static void assertTextComponent(final Component component) {
if (!(component instanceof TextComponent) || !component.children().isEmpty()) {
throw new IllegalArgumentException("Legacy events must be single Component instances");
}
}
public @NotNull Component serializeShowItem(final HoverEvent.@NotNull ShowItem input) throws IOException {
final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putString(ITEM_TYPE, input.item().asString())
.putByte(ITEM_COUNT, (byte) input.count());
final BinaryTagHolder nbt = input.nbt();
if (nbt != null) {
builder.put(ITEM_TAG, nbt.get(SNBT_CODEC));
}
return Component.text(SNBT_CODEC.encode(builder.build()));
}
public @NotNull Component serializeShowEntity(final HoverEvent.@NotNull ShowEntity input, final Codec.Encoder<Component, String, ? extends RuntimeException> componentCodec) throws IOException {
final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putString(ENTITY_ID, input.id().toString())
.putString(ENTITY_TYPE, input.type().asString());
final Component name = input.name();
if (name != null) {
builder.putString(ENTITY_NAME, componentCodec.encode(name));
}
return Component.text(SNBT_CODEC.encode(builder.build()));
}
public interface GsonLike {
static GsonLike fromGson(final Gson gson) {
return gson::fromJson;
}
<T> T fromJson(final @Nullable JsonElement json, final Class<T> classOfT);
}
// Backward Compatibility
private static HoverEvent.ShowItem createShowItem(final @NotNull Key item, final @Range(from = 0, to = Integer.MAX_VALUE) int count, final @Nullable BinaryTagHolder nbt) {
try {
return HoverEvent.ShowItem.showItem(item, count, nbt);
} catch (final NoSuchMethodError ignored) {
return HoverEvent.ShowItem.of(item, count, nbt);
}
}
private static HoverEvent.ShowEntity createShowEntity(final @NotNull Key type, final @NotNull UUID id, final @Nullable Component name) {
try {
return HoverEvent.ShowEntity.showEntity(type, id, name);
} catch (final NoSuchMethodError ignored) {
return HoverEvent.ShowEntity.of(type, id, name);
}
}
private static <D, E, DX extends Throwable, EX extends Throwable> @NotNull Codec<D, E, DX, EX> createCodec(final @NotNull Codec.Decoder<D, E, DX> decoder, final @NotNull Codec.Encoder<D, E, EX> encoder) {
try {
return Codec.codec(decoder, encoder);
} catch (final NoSuchMethodError ignored) {
return Codec.of(decoder, encoder);
}
}
}
@@ -1,260 +0,0 @@
package com.github.retrooper.packetevents.util.adventure;
import com.google.gson.*;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.util.Codec;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Type;
import java.util.EnumSet;
import java.util.Set;
public class Legacy_StyleSerializerExtended implements JsonDeserializer<Style>, JsonSerializer<Style> {
private static final TextDecoration[] DECORATIONS = {
TextDecoration.BOLD,
TextDecoration.ITALIC,
TextDecoration.UNDERLINED,
TextDecoration.STRIKETHROUGH,
TextDecoration.OBFUSCATED
};
static {
// Ensure coverage of decorations
final Set<TextDecoration> knownDecorations = EnumSet.allOf(TextDecoration.class);
for (final TextDecoration decoration : DECORATIONS) {
knownDecorations.remove(decoration);
}
if (!knownDecorations.isEmpty()) {
throw new IllegalStateException("Gson serializer is missing some text decorations: " + knownDecorations);
}
}
static final String FONT = "font";
static final String COLOR = "color";
static final String INSERTION = "insertion";
static final String CLICK_EVENT = "clickEvent";
static final String CLICK_EVENT_ACTION = "action";
static final String CLICK_EVENT_VALUE = "value";
static final String HOVER_EVENT = "hoverEvent";
static final String HOVER_EVENT_ACTION = "action";
static final String HOVER_EVENT_CONTENTS = "contents";
static final @Deprecated String HOVER_EVENT_VALUE = "value";
private final boolean emitLegacyHover;
private final HoverSerializer hoverSerializer;
Legacy_StyleSerializerExtended(final boolean emitLegacyHover) {
this.emitLegacyHover = emitLegacyHover;
this.hoverSerializer = new HoverSerializer();
}
public Style deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
return this.deserialize(object, context);
}
private Style deserialize(final JsonObject json, final JsonDeserializationContext context) throws JsonParseException {
final Style.Builder style = Style.style();
if (json.has(FONT)) {
style.font(context.deserialize(json.get(FONT), Key.class));
}
if (json.has(COLOR)) {
final TextColorWrapper color = context.deserialize(json.get(COLOR), TextColorWrapper.class);
if (color.color != null) {
style.color(color.color);
} else if (color.decoration != null) {
style.decoration(color.decoration, true);
}
}
for (final TextDecoration decoration : DECORATIONS) {
final String value = TextDecoration.NAMES.key(decoration);
if (json.has(value)) {
style.decoration(decoration, json.get(value).getAsBoolean());
}
}
if (json.has(INSERTION)) {
style.insertion(json.get(INSERTION).getAsString());
}
if (json.has(CLICK_EVENT)) {
final JsonObject clickEvent = json.getAsJsonObject(CLICK_EVENT);
if (clickEvent != null) {
final ClickEvent.Action action = optionallyDeserialize(clickEvent.getAsJsonPrimitive(CLICK_EVENT_ACTION), context, ClickEvent.Action.class);
if (action != null && action.readable()) {
final JsonPrimitive rawValue = clickEvent.getAsJsonPrimitive(CLICK_EVENT_VALUE);
final String value = rawValue == null ? null : rawValue.getAsString();
if (value != null) {
style.clickEvent(ClickEvent.clickEvent(action, value));
}
}
}
}
if (json.has(HOVER_EVENT)) {
final JsonObject hoverEventObject = json.getAsJsonObject(HOVER_EVENT);
if (hoverEventObject != null) {
// Patch for hover event deserialization
final JsonPrimitive serializedAction = hoverEventObject.getAsJsonPrimitive(HOVER_EVENT_ACTION);
if (serializedAction != null) {
final JsonElement rawValue;
final boolean legacy;
if (hoverEventObject.has(HOVER_EVENT_CONTENTS)) {
legacy = false;
rawValue = hoverEventObject.get(HOVER_EVENT_CONTENTS);
} else if (hoverEventObject.has(HOVER_EVENT_VALUE)) {
rawValue = hoverEventObject.get(HOVER_EVENT_VALUE);
legacy = true;
} else {
legacy = false;
rawValue = null;
}
if (rawValue != null) {
HoverEvent.Action action = null;
Object value = null;
switch (serializedAction.getAsString()) {
case "show_text":
action = HoverEvent.Action.SHOW_TEXT;
value = context.deserialize(rawValue, Component.class);;
break;
case "show_item":
action = HoverEvent.Action.SHOW_ITEM;
value = tryIgnoring(() -> this.hoverSerializer.deserializeShowItem(context::deserialize, rawValue, legacy));
break;
case "show_entity":
action = HoverEvent.Action.SHOW_ENTITY;
value = tryIgnoring(() -> this.hoverSerializer.deserializeShowEntity(context::deserialize, rawValue, this.decoder(context), legacy));
break;
case "show_achievement":
action = HoverEvent.Action.SHOW_TEXT;
value = tryIgnoring(() -> this.hoverSerializer.deserializeShowAchievement(rawValue));
break;
}
if (value != null) {
style.hoverEvent(HoverEvent.hoverEvent(action, value));
}
}
}
}
}
if (json.has(FONT)) {
style.font(context.deserialize(json.get(FONT), Key.class));
}
return style.build();
}
private <T> T tryIgnoring(final ExceptionalFunction<T> function) {
try {
return function.invoke();
} catch (IOException e) {
return null;
}
}
@FunctionalInterface
interface ExceptionalFunction<T> {
T invoke() throws IOException;
}
private static <T> T optionallyDeserialize(final JsonElement json, final JsonDeserializationContext context, final Class<T> type) {
return json == null ? null : context.deserialize(json, type);
}
private Codec.Decoder<Component, String, JsonParseException> decoder(final JsonDeserializationContext ctx) {
return string -> {
final JsonReader reader = new JsonReader(new StringReader(string));
return (Component) ctx.deserialize(Streams.parse(reader), Component.class);
};
}
public JsonElement serialize(final Style src, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject json = new JsonObject();
for (final TextDecoration decoration : DECORATIONS) {
final TextDecoration.State state = src.decoration(decoration);
if (state != TextDecoration.State.NOT_SET) {
final String name = TextDecoration.NAMES.key(decoration);
assert name != null; // should never be null
json.addProperty(name, state == TextDecoration.State.TRUE);
}
}
final TextColor color = src.color();
if (color != null) {
json.add(COLOR, context.serialize(color));
}
final String insertion = src.insertion();
if (insertion != null) {
json.addProperty(INSERTION, insertion);
}
final ClickEvent clickEvent = src.clickEvent();
if (clickEvent != null) {
JsonObject eventJson = new JsonObject();
eventJson.add(CLICK_EVENT_ACTION, context.serialize(clickEvent.action()));
eventJson.addProperty(CLICK_EVENT_VALUE, clickEvent.value());
json.add(CLICK_EVENT, eventJson);
}
final HoverEvent<?> hoverEvent = src.hoverEvent();
if (hoverEvent != null) {
final JsonObject eventJson = new JsonObject();
eventJson.add(HOVER_EVENT_ACTION, context.serialize(hoverEvent.action()));
final JsonElement modernContents = context.serialize(hoverEvent.value());
eventJson.add(HOVER_EVENT_CONTENTS, modernContents);
if (this.emitLegacyHover) {
eventJson.add(HOVER_EVENT_VALUE, this.serializeLegacyHoverEvent(hoverEvent, modernContents, context));
}
json.add(HOVER_EVENT, eventJson);
}
final Key font = src.font();
if (font != null) {
json.add(FONT, context.serialize(font));
}
return json;
}
private JsonElement serializeLegacyHoverEvent(final HoverEvent<?> hoverEvent, final JsonElement modernContents, final JsonSerializationContext context) {
if (hoverEvent.action() == HoverEvent.Action.SHOW_TEXT) { // serialization is the same
return modernContents;
}
// Patch for hover event serialization
Component serialized = null;
try {
if (hoverEvent.action() == HoverEvent.Action.SHOW_ENTITY) {
serialized = this.hoverSerializer.serializeShowEntity((HoverEvent.ShowEntity) hoverEvent.value(), this.encoder(context));
} else if (hoverEvent.action() == HoverEvent.Action.SHOW_ITEM) {
serialized = this.hoverSerializer.serializeShowItem((HoverEvent.ShowItem) hoverEvent.value());
}
} catch (IOException var6) {
throw new JsonSyntaxException(var6);
}
return serialized == null ? JsonNull.INSTANCE : context.serialize(serialized);
}
private Codec.Encoder<Component, String, RuntimeException> encoder(final JsonSerializationContext ctx) {
return component -> ctx.serialize(component).toString();
}
}
@@ -1,84 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.util.adventure;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import java.util.UUID;
public class SerializerFactory implements TypeAdapterFactory {
private final boolean downsampleColors;
private final boolean emitLegacyHover;
SerializerFactory(final boolean downsampleColors, final boolean emitLegacyHover) {
this.downsampleColors = downsampleColors;
this.emitLegacyHover = emitLegacyHover;
}
@Override
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final Class<? super T> rawType = type.getRawType();
if (Component.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.COMPONENT_SERIALIZER_CREATE.apply(gson);
} else if (Key.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.KEY_SERIALIZER_INSTANCE;
} else if (Style.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) StyleSerializerExtended.create(this.emitLegacyHover, gson);
} else if (ClickEvent.Action.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.CLICK_EVENT_ACTION_SERIALIZER_INSTANCE;
} else if (HoverEvent.Action.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.HOVER_EVENT_ACTION_SERIALIZER_INSTANCE;
} else if (HoverEvent.ShowItem.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.SHOW_ITEM_SERIALIZER_CREATE.apply(gson);
} else if (HoverEvent.ShowEntity.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.SHOW_ENTITY_SERIALIZER_CREATE.apply(gson);
} else if (TextColorWrapper.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) TextColorWrapper.Serializer.INSTANCE;
} else if (TextColor.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) (this.downsampleColors ? AdventureReflectionUtil.TEXT_COLOR_SERIALIZER_DOWNSAMPLE_COLOR_INSTANCE : AdventureReflectionUtil.TEXT_COLOR_SERIALIZER_INSTANCE);
} else if (TextDecoration.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.TEXT_DECORATION_SERIALIZER_INSTANCE;
} else if (BlockNBTComponent.Pos.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.BLOCK_NBT_POS_SERIALIZER_INSTANCE;
} else if (AdventureReflectionUtil.IS_4_15_0_OR_NEWER) {
if (UUID.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.UUID_SERIALIZER_INSTANCE;
} else if (TranslationArgument.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) AdventureReflectionUtil.TRANSLATION_ARGUMENT_SERIALIZER_CREATE.apply(gson);
}
}
return null;
}
}
@@ -1,297 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.util.adventure;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.util.Codec;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;
public class StyleSerializerExtended extends TypeAdapter<Style> {
private static final TextDecoration[] DECORATIONS = {
TextDecoration.BOLD,
TextDecoration.ITALIC,
TextDecoration.UNDERLINED,
TextDecoration.STRIKETHROUGH,
TextDecoration.OBFUSCATED
};
static {
// Ensure coverage of decorations
final Set<TextDecoration> knownDecorations = EnumSet.allOf(TextDecoration.class);
for (final TextDecoration decoration : DECORATIONS) {
knownDecorations.remove(decoration);
}
if (!knownDecorations.isEmpty()) {
throw new IllegalStateException("Gson serializer is missing some text decorations: " + knownDecorations);
}
}
static final String FONT = "font";
static final String COLOR = "color";
static final String INSERTION = "insertion";
static final String CLICK_EVENT = "clickEvent";
static final String CLICK_EVENT_ACTION = "action";
static final String CLICK_EVENT_VALUE = "value";
static final String HOVER_EVENT = "hoverEvent";
static final String HOVER_EVENT_ACTION = "action";
static final String HOVER_EVENT_CONTENTS = "contents";
static final @Deprecated String HOVER_EVENT_VALUE = "value";
static TypeAdapter<Style> create(final boolean emitLegacyHover, final Gson gson) {
return new StyleSerializerExtended(emitLegacyHover, gson).nullSafe();
}
private final boolean emitLegacyHover;
private final Gson gson;
private final HoverSerializer hoverSerializer;
private StyleSerializerExtended(final boolean emitLegacyHover, final Gson gson) {
this.emitLegacyHover = emitLegacyHover;
this.gson = gson;
this.hoverSerializer = new HoverSerializer();
}
@Override
public Style read(final JsonReader in) throws IOException {
in.beginObject();
final Style.Builder style = Style.style();
while (in.hasNext()) {
final String fieldName = in.nextName();
if (fieldName.equals(FONT)) {
style.font(this.gson.fromJson(in, Key.class));
} else if (fieldName.equals(COLOR)) {
final TextColorWrapper color = this.gson.fromJson(in, TextColorWrapper.class);
if (color.color != null) {
style.color(color.color);
} else if (color.decoration != null) {
style.decoration(color.decoration, TextDecoration.State.TRUE);
}
} else if (TextDecoration.NAMES.keys().contains(fieldName)) {
style.decoration(TextDecoration.NAMES.value(fieldName), this.readBoolean(in));
} else if (fieldName.equals(INSERTION)) {
style.insertion(in.nextString());
} else if (fieldName.equals(CLICK_EVENT)) {
in.beginObject();
ClickEvent.Action action = null;
String value = null;
while (in.hasNext()) {
final String clickEventField = in.nextName();
if (clickEventField.equals(CLICK_EVENT_ACTION)) {
action = this.gson.fromJson(in, ClickEvent.Action.class);
} else if (clickEventField.equals(CLICK_EVENT_VALUE)) {
value = in.peek() == JsonToken.NULL ? null : in.nextString();
} else {
in.skipValue();
}
}
if (action != null && action.readable() && value != null) {
style.clickEvent(ClickEvent.clickEvent(action, value));
}
in.endObject();
} else if (fieldName.equals(HOVER_EVENT)) {
final JsonObject hoverEventObject = this.gson.fromJson(in, JsonObject.class);
if (hoverEventObject != null) {
final JsonPrimitive serializedAction = hoverEventObject.getAsJsonPrimitive(HOVER_EVENT_ACTION);
if (serializedAction == null) {
continue;
}
// Patch for hover event deserialization
final JsonElement rawValue;
boolean legacy = false;
if (hoverEventObject.has(HOVER_EVENT_CONTENTS)) {
rawValue = hoverEventObject.get(HOVER_EVENT_CONTENTS);
} else if (hoverEventObject.has(HOVER_EVENT_VALUE)) {
rawValue = hoverEventObject.get(HOVER_EVENT_VALUE);
legacy = true;
} else {
rawValue = null;
}
if (rawValue != null) {
HoverEvent.Action action = null;
Object value = null;
switch (serializedAction.getAsString()) {
case "show_text":
action = HoverEvent.Action.SHOW_TEXT;
value = this.gson.fromJson(rawValue, Component.class);;
break;
case "show_item":
action = HoverEvent.Action.SHOW_ITEM;
value = this.hoverSerializer.deserializeShowItem(HoverSerializer.GsonLike.fromGson(gson), rawValue, legacy);
break;
case "show_entity":
action = HoverEvent.Action.SHOW_ENTITY;
value = this.hoverSerializer.deserializeShowEntity(HoverSerializer.GsonLike.fromGson(gson), rawValue, this.decoder(), legacy);
break;
case "show_achievement":
action = HoverEvent.Action.SHOW_TEXT;
value = this.hoverSerializer.deserializeShowAchievement(rawValue);
break;
}
if (value != null) {
style.hoverEvent(HoverEvent.hoverEvent(action, value));
}
}
}
} else {
in.skipValue();
}
}
in.endObject();
return style.build();
}
private boolean readBoolean(final JsonReader in) throws IOException {
final JsonToken peek = in.peek();
if (peek == JsonToken.BOOLEAN) {
return in.nextBoolean();
} else if (peek == JsonToken.STRING || peek == JsonToken.NUMBER) {
return Boolean.parseBoolean(in.nextString());
} else {
throw new JsonParseException("Token of type " + peek + " cannot be interpreted as a boolean");
}
}
private Codec.Decoder<Component, String, JsonParseException> decoder() {
return string -> this.gson.fromJson(string, Component.class);
}
private Codec.Encoder<Component, String, JsonParseException> encoder() {
return component -> this.gson.toJson(component, Component.class);
}
@Override
public void write(final JsonWriter out, final Style value) throws IOException {
out.beginObject();
for (final TextDecoration decoration : DECORATIONS) {
final TextDecoration.State state = value.decoration(decoration);
if (state != TextDecoration.State.NOT_SET) {
final String name = TextDecoration.NAMES.key(decoration);
assert name != null; // should never be null
out.name(name);
out.value(state == TextDecoration.State.TRUE);
}
}
final @Nullable TextColor color = value.color();
if (color != null) {
out.name(COLOR);
this.gson.toJson(color, TextColor.class, out);
}
final @Nullable String insertion = value.insertion();
if (insertion != null) {
out.name(INSERTION);
out.value(insertion);
}
final @Nullable ClickEvent clickEvent = value.clickEvent();
if (clickEvent != null) {
out.name(CLICK_EVENT);
out.beginObject();
out.name(CLICK_EVENT_ACTION);
this.gson.toJson(clickEvent.action(), ClickEvent.Action.class, out);
out.name(CLICK_EVENT_VALUE);
out.value(clickEvent.value());
out.endObject();
}
final @Nullable HoverEvent<?> hoverEvent = value.hoverEvent();
if (hoverEvent != null) {
out.name(HOVER_EVENT);
out.beginObject();
out.name(HOVER_EVENT_ACTION);
final HoverEvent.Action<?> action = hoverEvent.action();
this.gson.toJson(action, HoverEvent.Action.class, out);
if (this.emitLegacyHover) {
out.name(HOVER_EVENT_VALUE);
this.serializeLegacyHoverEvent(hoverEvent, out);
} else if (!action.toString().equals("show_achievement")) { // Using string because the variable for SHOW_ACHIEVEMENT is not available in version before 4.14.0
out.name(HOVER_EVENT_CONTENTS);
if (action == HoverEvent.Action.SHOW_ITEM) {
this.gson.toJson(hoverEvent.value(), HoverEvent.ShowItem.class, out);
} else if (action == HoverEvent.Action.SHOW_ENTITY) {
this.gson.toJson(hoverEvent.value(), HoverEvent.ShowEntity.class, out);
} else if (action == HoverEvent.Action.SHOW_TEXT) {
this.gson.toJson(hoverEvent.value(), Component.class, out);
} else {
throw new JsonParseException("Don't know how to serialize " + hoverEvent.value());
}
}
out.endObject();
}
final @Nullable Key font = value.font();
if (font != null) {
out.name(FONT);
this.gson.toJson(font, Key.class, out);
}
out.endObject();
}
private void serializeLegacyHoverEvent(final HoverEvent<?> hoverEvent, final JsonWriter out) throws IOException {
if (hoverEvent.action() == HoverEvent.Action.SHOW_TEXT) { // serialization is the same
this.gson.toJson(hoverEvent.value(), Component.class, out);
return;
} else if (hoverEvent.action().toString().equals("show_achievement")) {
this.gson.toJson(hoverEvent.value(), String.class, out);
return;
}
// Patch for hover event serialization
Component serialized = null;
try {
if (hoverEvent.action() == HoverEvent.Action.SHOW_ENTITY) {
serialized = this.hoverSerializer.serializeShowEntity((HoverEvent.ShowEntity) hoverEvent.value(), this.encoder());
} else if (hoverEvent.action() == HoverEvent.Action.SHOW_ITEM) {
serialized = this.hoverSerializer.serializeShowItem((HoverEvent.ShowItem) hoverEvent.value());
}
} catch (final IOException ex) {
throw new JsonSyntaxException(ex);
}
if (serialized != null) {
this.gson.toJson(serialized, Component.class, out);
} else {
out.nullValue();
}
}
}
@@ -1,77 +0,0 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2022 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.util.adventure;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
public class TextColorWrapper {
final @Nullable TextColor color;
final @Nullable TextDecoration decoration;
final boolean reset;
TextColorWrapper(final @Nullable TextColor color, final @Nullable TextDecoration decoration, final boolean reset) {
this.color = color;
this.decoration = decoration;
this.reset = reset;
}
static final class Serializer extends TypeAdapter<TextColorWrapper> {
static final TextColorWrapper.Serializer INSTANCE = new TextColorWrapper.Serializer();
private Serializer() {
}
@Override
public void write(final JsonWriter out, final TextColorWrapper value) {
throw new JsonSyntaxException("Cannot write TextColorWrapper instances");
}
@Override
public TextColorWrapper read(final JsonReader in) throws IOException {
final String input = in.nextString();
final TextColor color = this.colorFromString(input);
final TextDecoration decoration = TextDecoration.NAMES.value(input);
final boolean reset = decoration == null && input.equals("reset");
if (color == null && decoration == null && !reset) {
throw new JsonParseException("Don't know how to parse " + input + " at " + in.getPath());
}
return new TextColorWrapper(color, decoration, reset);
}
TextColor colorFromString(final String value) {
if (value.startsWith("#")) {
return TextColor.fromHexString(value);
} else {
return NamedTextColor.NAMES.value(value);
}
}
}
}
Loaded 100 of 233 files, more files were not shown because too many files have changed in this diff. Show more