Compare commits

...
276 Commits
Author SHA1 Message Date
retrooper ab8faf358c 2.5.0 Release 2024-09-20 22:07:20 +02:00
retrooper 243ce3c5dd Merge pull request #990 from Elikill58/2.0
Add wrapper for BossBar packet
2024-09-20 21:57:25 +02:00
retrooper d4762195f9 adhere to packetevents' convention of capitalizing UUID in setters & getters 2024-09-20 21:53:46 +02:00
retrooper 121e1615b6 remove awkward comment break in PEVersion#toStringWithoutSnapshot docs 2024-09-20 21:49:46 +02:00
retrooper 350d33ae6f Merge remote-tracking branch 'origin/2.0' into 2.0 2024-09-20 21:47:15 +02:00
retrooper d4f7bced86 bstats metrics do not collect snapshot & commit, solely version is captured. 2024-09-20 21:46:18 +02:00
retrooper 8037e659a8 Merge pull request #1000 from booky10/fix/particle-data-again
Revert some fixes for particle data decoding/encoding
2024-09-20 21:12:04 +02:00
retrooper 28c7849252 Merge pull request #999 from booky10/fix/bungee-injection
Rework bungee injector to allow for packet cancellation
2024-09-20 21:07:09 +02:00
retrooper cb277bf16a Merge pull request #995 from booky10/fix/packet-sending-race-condition
Synchronize packet encoding
2024-09-20 21:04:50 +02:00
booky10 0612e1e188 Revert some fixes for particle data decoding/encoding
Mojang's codecs are confusing
2024-09-20 20:30:08 +02:00
booky10 7a9f83129e Fix incorrect list recycling usage 2024-09-20 20:11:05 +02:00
booky10 a17e5f13a7 Rework bungee injector to allow for packet cancellation 2024-09-20 20:04:29 +02:00
retrooper 02a565bf39 Merge pull request #996 from booky10/fix/bungee-injection
Fix support for new bungee compression
2024-09-20 19:44:01 +02:00
booky10 f1d7d5ac81 Merge pull request #992 from galacticwarrior9/add-biomeeffects-getters 2024-09-18 08:33:30 +02:00
retrooper a1663f5a03 Merge pull request #994 from booky10/fix/pre-1.20.5-registry-data-limit
Remove nbt reading limit in registry-data packet for pre 1.20.5
2024-09-17 23:22:28 +02:00
booky10 909492cfae Fix support for new bungee compression 2024-09-17 16:39:46 +02:00
booky10 a3b4cc21f5 Synchronize packet encoding
Resolves concurrency issues which may happen when sending the same packet wrapper to different players, concurrently

This is not a perfect solution, but the alternative would be refactoring the packetwrapper to be based on buffers passed during encoding as parameters, which would be too much effort and would require breaking changes
2024-09-17 16:29:30 +02:00
booky10 402ebd729e Remove nbt reading limit in registry-data packet for pre 1.20.5 2024-09-17 16:21:49 +02:00
galacticwarrior9 349b5dc110 Add missing getters to BiomeEffects 2024-09-17 13:43:07 +01:00
Elikill58 c778cc3368 Convert list to EnumSet 2024-09-16 23:03:43 +02:00
Elikill58 26e5aab84e Rename divison to overlay 2024-09-16 19:54:01 +02:00
Elikill58 79da4e1bc4 Convert flags 2024-09-16 19:50:29 +02:00
Elikill58 51b550ca5e Use adventure's enum 2024-09-16 11:29:37 +02:00
Elikill58 2877b74c42 Remove own config... 2024-09-15 19:54:08 +02:00
Elikill58 c67d1a86c5 Fix missing import 2024-09-15 19:41:28 +02:00
Elikill58 5cf9b34fd2 Add wrapper for BossBar packet 2024-09-15 19:24:59 +02:00
retrooper 99acbe4960 Merge pull request #980 from booky10/fix/javadoc-links
Update javadoc overview links
2024-09-02 22:27:53 +02:00
booky10 2bf64b6107 Update javadoc overview links 2024-09-02 22:26:07 +02:00
retrooper 4e86af8754 Add Javadocs URL 2024-09-02 22:22:44 +02:00
retrooper 6bf639f18a Update links to documentation. 2024-09-02 22:11:34 +02:00
retrooper 9a7bb17459 Merge pull request #978 from AoElite/2.0
fixed runAtFixedRate not using ticks when running on folia
2024-09-01 22:38:57 +02:00
AoElite 6b84c683ee fixed runAtFixedRate not using ticks when running on folia 2024-09-01 16:33:12 -04:00
booky10 dc015dc457 Merge pull request #935 from rafi67000/gradleUpdate
Gradle, and shadow update
2024-08-29 22:34:52 +02:00
booky10 fd7bc84cd5 Merge pull request #972 from booky10/fix/workaround-spigot-item-conversion-issues
Use global synchronized registries as fallback
2024-08-29 17:46:19 +02:00
booky10 9fd5b3b912 Use global synchronized registries as fallback for spigot and fabric
Works around issues where plugins convert items to bukkit items, which doesn't preserve registry holder context, e.g. https://github.com/nulli0n/ExcellentEnchants-spigot/blob/8623159ca61b60e1d3cae22c77a99e82b15b9d5f/Core/src/main/java/su/nightexpress/excellentenchants/hook/impl/PacketEventsHook.java
2024-08-29 12:49:12 +02:00
rafi67000 a2d259af58 Update to Gradle 8.10 2024-08-27 20:29:28 +02:00
rafi67000 6cc21a6d98 Merge branch 'retrooper:2.0' into gradleUpdate 2024-08-27 20:13:20 +02:00
retrooper e105770dde Merge pull request #967 from KermanIsPretty/fix/join-game-data-codec-1.7
Fix 1.8< clients not having Reduced Debug Info.
2024-08-27 12:28:49 +02:00
booky10 68b7ee51dc Merge pull request #968 from ManInMyVan/tuff-walls
tuff walls exceed cube
2024-08-26 19:50:22 +02:00
ManInMyVan a4a375f411 tuff walls exceed cube 2024-08-26 11:19:35 -06:00
KermanIsPretty ae6b3d18a4 Fix 1.8< clients not having reducedDebugInfo. 2024-08-25 15:50:02 -05:00
booky10 8763b0e77f Merge pull request #966 from ManInMyVan/fix-inconsistency
Fix inconsistency in StateTypes
2024-08-25 10:55:44 +02:00
ManInMyVan 2440b64981 Fix inconsistency in StateTypes 2024-08-25 01:02:36 -06:00
retrooper ebe73587fe Merge pull request #959 from AoElite/2.0
API for checking if packetevents is terminated
2024-08-22 17:34:43 +02:00
AoElite 5b87ac4f61 unnecessary fix 2024-08-21 08:56:58 -04:00
AoElite d9a3f33199 implement for test 2024-08-21 08:29:16 -04:00
AoElite 9f44834514 don't forget netty one 2024-08-20 21:04:03 -04:00
AoElite 314d4a2813 option to kick on join if terminated & added API for checking if terminated 2024-08-20 21:02:39 -04:00
retrooper 97fee0c898 Merge pull request #956 from booky10/fix/particle-data-decoding-1.20.4
Fix particle data decoding and encoding for <1.20.5
2024-08-21 00:19:56 +02:00
booky10 bdc96a88cd Merge branch '2.0' into fix/particle-data-decoding-1.20.4 2024-08-20 19:33:29 +02:00
retrooper af1691e6e5 Merge pull request #957 from booky10/fix/bungeecord-resolving
Fix bungeecord artifact not resolving
2024-08-20 19:03:40 +02:00
booky10 e4058f7f15 Fix bungeecord artifact not resolving 2024-08-20 18:06:55 +02:00
booky10 7f0c922e10 Fix particle data decoding and encoding for <1.20.5 2024-08-20 17:57:54 +02:00
retrooper 453793eb60 Merge pull request #955 from booky10/fix/item-adventure-predicate-component-reading
Read item adventure predicate nbt and property matchers as optionals
2024-08-20 17:37:45 +02:00
booky10 015e7a4647 Read item adventure predicate nbt and property matchers as optionals 2024-08-20 16:59:17 +02:00
retrooper 45c0c580f7 Merge pull request #954 from booky10/fix/1.19-issues
Fix some 1.19 issues related to registries
2024-08-20 15:39:11 +02:00
booky10 1c12a868c8 Fix chattype decoding and encoding for 1.19 2024-08-20 15:20:54 +02:00
booky10 a3f22320d5 Fix error when handling empty legacy registries 2024-08-20 15:10:57 +02:00
retrooper f4bb1dd8fc kick players even if user is null & they throw an exception, kick user for exceptions also in encoder. 2024-08-18 07:11:01 +02:00
retrooper 5203b3a376 Merge pull request #943 from ManInMyVan/static-getByName
Make BlockTags::getByName static
2024-08-17 20:22:28 +02:00
retrooper fe66d743a9 Merge pull request #949 from booky10/fix/1.16.5-errors
Fix some errors related to old minecraft versions
2024-08-17 20:01:13 +02:00
retrooper 1d1cf84c60 Merge pull request #950 from booky10/fix/dynamic-registry-elements-equality
Don't do deep equals checks for dynamic registry entries
2024-08-17 19:57:02 +02:00
booky10 1c81aab994 Don't do deep equals checks for dynamic registry entries 2024-08-17 19:12:52 +02:00
booky10 52559f6fc8 Fix binary tag holder for adventure <4.10.0 2024-08-17 17:55:29 +02:00
booky10 06cc25bf23 Add workaround to make DimensionType#getId work for 1.16.2 to 1.18.2 2024-08-17 17:49:32 +02:00
booky10 86cf0d51ed Replace usages of translation fallback in ComponentSerializerImpl 2024-08-17 17:36:25 +02:00
booky10 5fbb052d6f Fix wrong recipe serializer definition key 2024-08-17 17:36:09 +02:00
booky10 b7021fcd1f Fix support for adventure <4.10
Replaces Index#valueToKey usages with alternative
Fixes error on join in 1.16.5
2024-08-17 17:22:22 +02:00
booky10 64db7f13c6 Fix support for adventure <4.11
Replaces Index#valueOrThrow usages with copied implementation
Fixes error on join in 1.16.5
2024-08-17 17:05:41 +02:00
retrooper 20c9fdf634 Merge pull request #947 from booky10/fix/dimension-type-npes
Handle null values correctly in StaticDimensionType#equals
2024-08-17 17:03:29 +02:00
booky10 a71035a157 Handle null values correctly in StaticDimensionType#equals 2024-08-17 16:19:56 +02:00
ManInMyVan b0002102e3 Make BlockTags::getByName static 2024-08-15 19:29:07 -06:00
retrooper a3e65cfcbe Merge pull request #939 from rexlManu/2.0
(Velocity) Avoid user if channel cannot be found
2024-08-14 09:35:48 +02:00
Emmanuel Lampe edd9c5ce3d Merge branch 'retrooper:2.0' into 2.0 2024-08-14 05:22:04 +02:00
retrooper e918e6b10a Merge remote-tracking branch 'origin/2.0' into 2.0 2024-08-13 21:04:18 +02:00
retrooper f6c309fd18 add copyright notice to Metrics 2024-08-13 21:03:20 +02:00
retrooper 322a99dbc4 final change in README 2024-08-13 17:50:54 +02:00
retrooper 6793dee0bc More fixes to README 2024-08-13 17:46:39 +02:00
retrooper d33751dbb0 fix BUILD STATUS in README 2024-08-13 17:41:26 +02:00
retrooper fa67de16a1 fixed issues in README 2024-08-13 17:40:10 +02:00
retrooper 57fc21f411 patch readme 2024-08-13 17:37:27 +02:00
retrooper 9999ee3b4a updated README for packetevents 2024-08-13 17:32:03 +02:00
retrooper 4a90cadeb1 Merge pull request #933 from booky10/fix/bungee-server-nullability
Account for null servers in bungee
2024-08-12 18:49:59 +02:00
retrooper 048903f33f Merge pull request #934 from booky10/fix/translation-fallback-adventure
Fix compatibility with adventure versions older than 4.13.0
2024-08-12 18:48:09 +02:00
retrooper 3fa5eaf2a3 Merge pull request #932 from booky10/fix/dimension-type-mappings
Fix dimension type mappings
2024-08-12 18:47:01 +02:00
rafi67000 93bd6d5ac9 Update wrapper 2024-08-12 17:24:04 +02:00
rafi67000 aa5535615c A little tweak... 2024-08-12 17:22:15 +02:00
booky10 9ccd85178d Fix compatibility with adventure versions older than 4.13.0
Translation fallback methods didn't exist before
2024-08-12 17:11:16 +02:00
rafi67000 14fd279c06 Update and switch to maintained shadow fork 2024-08-12 17:07:20 +02:00
rafi67000 6df845324b Update gradle to 8.9 2024-08-12 17:06:59 +02:00
booky10 c3f233ca61 Account for null servers in bungee 2024-08-12 16:47:52 +02:00
booky10 ed4f018b34 Fix dimension type mappings 2024-08-12 16:44:09 +02:00
retrooper ef4bc1c02e github sponsors added as funding option 2024-08-12 09:32:44 +02:00
retrooper ca725b68a5 fix: NPE error in DimensionType#decode 2024-08-12 09:09:02 +02:00
booky10 40f7463e93 Merge pull request #927 from AoElite/2.0
Fix sound exception on 1.21
2024-08-12 00:57:46 +02:00
AoElite 7778768c30 tweak 2024-08-11 18:55:09 -04:00
AoElite 6a6be44ab2 fix sound exception on 1.21 2024-08-11 15:57:12 -04:00
Abhigya fe67c23ad4 Merge pull request #926 from AoElite/2.0
Release item buffer regardless of exception
2024-08-11 23:45:40 +05:30
AoElite c09348d4f7 release item buffer regardless of exception 2024-08-11 13:55:17 -04:00
retrooper e04cd2108f Merge pull request #919 from libraryaddict/patch-2
Fix #916 via removing explicit type call of nbt get
2024-08-10 17:18:35 +02:00
retrooper 39f0dee9f1 Merge pull request #912 from booky10/feat/1.21.1-server-version
Add 1.21.1 version support
2024-08-10 17:16:20 +02:00
libraryaddict 9b9ae8d36c Fix #916 2024-08-10 22:47:03 +12:00
retrooper fc934195de Merge pull request #866 from booky10/feat/network-synced-registries
Network synced registries
2024-08-09 07:49:14 +02:00
retrooper 6c4db8029b chore: bump up version to 2.5.0-SNAPSHOT 2024-08-09 07:46:25 +02:00
retrooper c4ce7b7a87 packetevents version represented as string only provides snapshotCommit if it is a snapshot, not for stable releases. 2024-08-09 07:44:31 +02:00
booky10 b751c63625 Add 1.21.1 version support 2024-08-08 16:44:16 +02:00
retrooper 31ac149b42 Merge pull request #909 from booky10/feat/peversion-build-timestamp
Add build timestamp to PEVersions class
2024-08-06 19:08:17 +02:00
booky10 2f45e9e469 Add equality methods for all dynamic registry value classes 2024-08-06 17:55:27 +02:00
booky10 7b152ff264 Don't store exact copies of vanilla registry elements 2024-08-06 17:25:55 +02:00
booky10 e9eb441a1c Implement caching on bungee platform 2024-08-06 17:19:23 +02:00
booky10 55c1093783 Fix biome and enchantment type definitions on bungee 2024-08-06 17:17:05 +02:00
booky10 963ec4e978 Revert some debug changes which weren't meant to be pushed 2024-08-06 17:09:53 +02:00
booky10 26c30f46e7 Implement key-based registry caching and velocity proxy per-server caching
Bungee isn't finished yet
2024-08-06 17:06:55 +02:00
booky10 c2cdafb17f Add system property to force disable caching 2024-08-06 16:09:38 +02:00
booky10 22060a9889 Actually make caching work by not decoding registries when not needed 2024-08-06 16:08:55 +02:00
booky10 e1a54099a4 Add build timestamp to PEVersions class 2024-08-06 15:24:28 +02:00
retrooper 3fa6a0f69a fix: wrong tag name used in ParticleDustData 2024-08-05 22:48:26 +02:00
retrooper 676cb6c3ce feature: cache legacy registries 2024-08-05 22:34:54 +02:00
retrooper a3063f0bcb feature: cache registries globally on non-proxy servers
Each kind of registry has a VersionedRegistry. This VersionedRegistry now contains a SimpleRegistry as a cache. If the cache is not null, it will be used globally. This makes the usage of registries convenient for PacketEvents users as they no longer need to access them within each user instance. If the plugin is on proxy, we shall store all registries for each user. If the plugin is not on proxy, we shall only store the dimension type registry for each user and globally cache the rest.
2024-08-05 22:17:53 +02:00
retrooper c1520401b6 deletion: remove hard-coded 1.21 enchantment type mappings (as temporary fix) 2024-08-05 22:07:57 +02:00
booky10 d988d0c684 Fix backwards compatibility for dimension type codec 2024-08-04 23:00:46 +02:00
booky10 e2283d6f4e Fix backwards compatibility for encoding trim patterns 2024-08-04 22:39:25 +02:00
booky10 ea56cfde04 Add backwards compatibility support for chat types 2024-08-04 22:34:44 +02:00
booky10 16733e587e Add full legacy data support for biomes 2024-08-04 21:58:28 +02:00
booky10 b247807f0f Fix biome mapping test 2024-08-04 20:30:26 +02:00
booky10 679aea8181 Add even more biome mappings 2024-08-04 18:47:30 +02:00
booky10 f0d499a008 Add more biome mappings 2024-08-04 18:41:22 +02:00
booky10 b5320f2ae7 Fix data reading for biomes and enchantment types 2024-08-04 18:24:52 +02:00
retrooper 0fd0821a93 Merge pull request #907 from booky10/fix/fire-legacy-mappings
Map legacy block ids for fire block
2024-08-04 18:17:59 +02:00
booky10 c7c1b9da17 Map legacy block ids for fire block 2024-08-04 18:15:03 +02:00
retrooper d67d307788 Merge pull request #906 from booky10/fix/component-equality
Fix some component equality checking issues
2024-08-04 18:10:45 +02:00
booky10 c057f18914 Merge branch '2.0' into feat/network-synced-registries 2024-08-04 18:05:46 +02:00
booky10 7a5b425dbb Merge pull request #1 from libraryaddict/feat/network-synced-registries
Feat/network synced registries
2024-08-04 18:01:20 +02:00
booky10 9b22739977 Remove setters from jukebox song interface 2024-08-04 17:56:03 +02:00
booky10 12e0869e0a Fix JukeboxSongs#values javadoc 2024-08-04 17:55:22 +02:00
booky10 0039ab8704 Resolve small backwards compatibility issues 2024-08-04 17:55:12 +02:00
retrooper 7da389db14 feature: 1.21 enchantment mappings (ordered alphabetically) 2024-08-04 16:14:17 +02:00
libraryaddict 1f85797768 Merge pull request #1 from packetevents/2.0
Feature: Create Damage Event wrapper using new Damage Types
2024-08-04 23:49:55 +12:00
retrooper 3c55703718 feature: damage event wrapper using new damage types 2024-08-04 10:50:28 +02:00
booky10 e3734c2d94 Fix some component equality checking issues 2024-08-01 22:41:22 +02:00
Emmanuel Lampe afc571e331 fix(velocity): check for fake connections 2024-08-01 09:59:47 +02:00
libraryaddict 4549d07a6c IJukeboxSong 2024-07-31 17:36:11 +12:00
libraryaddict acc220729b Rename JukeboxSong and StaticJukeboxSong 2024-07-31 01:45:15 +12:00
libraryaddict b27f64c3b1 Missing license disclaimer thingy 2024-07-30 21:17:32 +12:00
libraryaddict 1f7a09a561 Current WIP, adds damage types and jukebox 2024-07-30 21:14:15 +12:00
retrooper f0b15c3f94 Merge pull request #900 from booky10/fix/number-issues-adventure-nbt-serializer
Allow item hover event count to be specified as any number
2024-07-27 11:32:31 +02:00
booky10 cc03d3029d Allow item hover event count to be specified as any number
Fixes compatibility with vanilla codec, as vanilla allows any number to be decoded within DFU
2024-07-23 17:57:46 +02:00
retrooper ef2d24c5ff Merge pull request #894 from booky10/fix/chunk-codec-issues
Fix chunk codec issues
2024-07-23 13:58:58 +02:00
retrooper 6a69d1bcb4 Generics usage in ProtocolPacketEvent for platform-dependent player 2024-07-22 18:50:35 +02:00
retrooper ea1e11d690 Merge pull request #874 from Bram1903/fix/LatestFabricSupport
Fabric 1.21 Support
2024-07-22 11:32:47 +02:00
retrooper 65b51fa6f5 Update javadocs link. 2024-07-19 17:27:25 +02:00
retrooper dae31cedf6 Fix fabric injection, packet listeners now work. 2024-07-19 17:21:32 +02:00
retrooper 6179f997c9 Update fabric version 2024-07-19 16:42:50 +02:00
retrooper 8cb083c29a Correction in PacketListenerPriority documentation, MONITOR should be avoided when modifying. 2024-07-19 13:21:57 +02:00
retrooper 7dd2434ea9 Modification of PacketEvents settings is now marked as internal API behavior. Generally, you are not advised to change the settings.
Unnecessary static in PacketEventsAPI fixed.
2024-07-19 13:17:34 +02:00
booky10 66ca952a1d Add getter/setter for light data in Chunk_v1_9 packet
Would otherwise be impossible to send new chunk layers for 1.9 to 1.13
2024-07-18 19:57:10 +02:00
booky10 d6fef1180f Don't read sky data in overworld dimension for 1.14 to 1.15.2
Sky data in overworld was only present before 1.14
2024-07-18 18:48:13 +02:00
booky10 32451165ba Don't allow usage of singleton palette before 1.18 2024-07-18 18:47:28 +02:00
Bram 2c806b2d4e Removed: foojay-resolver in favor of the foojay-resolver-convention 2024-07-14 11:17:40 +02:00
Bram a8dadfdfff Merge branch 'refs/heads/2.0' into fix/LatestFabricSupport
# Conflicts:
#	settings.gradle.kts
2024-07-14 11:16:32 +02:00
booky10 4524b4289c Merge pull request #883 from booky10/fix/1.21-item-attribute-modifiers-component
Fix reading/writing of item attribute modifier component in 1.21
2024-07-12 23:18:36 +02:00
retrooper 67010e8f11 Merge pull request #885 from booky10/feat/javadoc-publishing
Implement automatic javadoc publishing
2024-07-12 16:39:42 +02:00
retrooper 245ee7699a Merge pull request #890 from booky10/fix/null-material-types
Fix missing material types for vault and heavy core
2024-07-12 16:38:11 +02:00
booky10 25dfb3c5b9 Fix missing material types for vault and heavy core 2024-07-12 16:36:57 +02:00
booky10 52709f279b Update javadoc links 2024-07-11 17:43:23 +02:00
booky10 13d6437638 Remove javadoc pages deployment
Will be handled in different repo, see https://github.com/packetevents/packetevents.github.io
2024-07-11 16:51:46 +02:00
booky10 7a3505070e Restrict javadoc deployment for tag pushes 2024-07-11 01:27:31 +02:00
booky10 7d79603d08 Move api javadoc to pages root 2024-07-11 00:57:37 +02:00
booky10 7890a8ae97 Add javadoc overview 2024-07-11 00:55:54 +02:00
booky10 c73e149f43 Fix spigot javadoc jar generation 2024-07-11 00:24:13 +02:00
booky10 2f72766aac Fix wrong action name 2024-07-10 23:50:33 +02:00
booky10 43d915d09c Add id-token permission to javadoc extraction job 2024-07-10 23:49:47 +02:00
booky10 3444b07f6a Try if github pages upload action works 2024-07-10 23:46:23 +02:00
booky10 3cfb1c5b3e Add initial javadoc upload workflow
Designed to not work yet, just testing if it outputs correct stuff as act is broken for me
2024-07-10 23:15:39 +02:00
booky10 150736b170 Update github build workflow actions 2024-07-10 23:11:20 +02:00
booky10 1f325a36d6 Enable javadoc jar generation 2024-07-10 23:10:56 +02:00
booky10 bdc8f6ef82 Use jvm 21 for kotlin jvm toolchain in buildscript 2024-07-10 23:10:44 +02:00
booky10 d365e4b949 Add foojay resolver convention plugin
Allows automatic download of specified java toolchain jvm version
2024-07-10 23:10:25 +02:00
booky10 4f4a3ba87b Update gradle wrapper to v8.8 2024-07-10 23:06:44 +02:00
retrooper 7ceeaa5f3f Merge pull request #872 from ShadowOfHeaven-Me/2.0
Optimized the EventManager
2024-07-10 21:07:52 +02:00
retrooper 6f968275c1 Merge pull request #882 from KoutaChan/fix-pallet-shift
Fix: Palette index calculation error
2024-07-10 18:29:41 +02:00
booky10 245b7a0d2a Fix reading/writing of item attribute modifier component in 1.21 2024-07-10 16:29:36 +02:00
KoutaChan 13b1007c1c Fix: pallet bit 2024-07-10 23:26:15 +09:00
KoutaChan c164908aac Fix: bit shift error 2024-07-10 23:22:40 +09:00
KoutaChan 1dd28f124a Fix: Palette index calculation error 2024-07-10 23:02:53 +09:00
retrooper 149483ef21 Merge pull request #881 from booky10/fix/version-hash-publishing
Don't specify version hash in published maven pom
2024-07-10 15:49:32 +02:00
booky10 0a689564ce Don't specify version hash in published maven pom 2024-07-10 15:44:33 +02:00
retrooper facc6bc074 Merge pull request #877 from booky10/feat/versioning-commit-hash
Add current commit hash to project version
2024-07-09 22:57:14 +02:00
booky10 41224c9faf Fix snapshot commit for release versions 2024-07-09 22:54:15 +02:00
retrooper 14548f5679 Even more clarity, now LGTM 2024-07-09 22:47:50 +02:00
retrooper d31d0b2487 Correction and clarity to the PEVersion#snapshotCommit method documentation 2024-07-09 22:43:38 +02:00
retrooper 1366872acb Merge pull request #870 from AbhigyaKrishna/mappings-reading-optimization
Mappings reading optimization
2024-07-09 11:41:11 +02:00
retrooper 40fcd23d87 More clarity on the deprecation reason in PEVersion#asArray 2024-07-09 11:31:48 +02:00
retrooper ba7d09ee91 Correction in the documentation of PEVersion: Accuracy in the content and grammar whilst ensuring clarity. 2024-07-09 11:19:08 +02:00
booky10 b0fa4eb3b7 Don't include snapshot commit hash in file name 2024-07-09 01:54:40 +02:00
Bram a8bf163355 Fix: Left behind old shadow plugin convention 2024-07-08 19:00:31 +02:00
booky10 f81730c689 Add snapshot commit api to PEVersion 2024-07-08 15:18:07 +02:00
booky10 a830fe8b63 Adjust PEVersionTask regex to allow any commit hash length 2024-07-08 04:18:25 +02:00
booky10 05c116271e Add current commit hash to project version
Only added when version is a snapshot version
Disables itself if no git directory is found
2024-07-08 04:09:31 +02:00
booky10 a08f89813e Allow chat type decoration parameters to be defined directly 2024-07-08 03:47:39 +02:00
booky10 87fa35dea0 Fix enchantment registry not being able to be replaced
Caused by directly referencing static registry
2024-07-08 03:45:32 +02:00
booky10 4ed644313a Allow enchantment definition slots to be defined directly 2024-07-08 03:43:34 +02:00
booky10 9fdd1615bd Merge branch '2.0' into feat/network-synced-registries 2024-07-08 03:25:15 +02:00
booky10 4ab220f189 Make TrimMaterial override armor materials optional 2024-07-08 03:25:00 +02:00
booky10 adef187685 Fix MappedEntitySet codec 2024-07-08 03:24:21 +02:00
booky10 0e5a443e9b Fix various issues encountered on login 2024-07-08 03:01:45 +02:00
booky10 4961ad6e8d Add biome registry handling to SynchronizedRegistriesHandler
Seems like I forgot to add it there
2024-07-08 02:44:32 +02:00
booky10 e056f1ca0a Implement synchronized painting variant registries 2024-07-08 02:43:58 +02:00
retrooper 9d43fd7a35 Stop re-encoding by default on BungeeCord 2024-07-07 23:56:29 +02:00
AbhigyaKrishna 45bafb37fe feat: HashedByteArray -> BinaryNBTCompound 2024-07-05 22:52:16 +05:30
AbhigyaKrishna 3554f3c6ac feat: Read compound tags in binary format to save memory 2024-07-05 22:42:32 +05:30
AbhigyaKrishna 10d4cf4a9d feat: fabric adventure dependency fix 2024-07-05 20:17:04 +05:30
retrooper 7a1621aa37 Final correction for bungee injector. 2024-07-05 14:11:57 +02:00
retrooper 22a5e67c69 Another correction, but probably obsolete 2024-07-05 13:56:33 +02:00
retrooper 8737ba218a Correct decompress decode method on Bungee. Reflection error. 2024-07-05 13:51:47 +02:00
Bram 98e0a35f0c Removed: Unused Mod Properties in the Fabric module gradle.properties 2024-07-05 10:01:21 +02:00
Bram 21500f3cab Fixed: Another issue in the PacketEventsInjectorMixin 2024-07-04 23:58:13 +02:00
Bram 750d87937a Switched: johnrengelman shadow to googler's shadowjar for Java 21 support (See https://github.com/johnrengelman/shadow/issues/908) 2024-07-04 23:55:25 +02:00
Bram f78ffc9a5c Fixed: PacketEventsInjectorMixin 2024-07-04 23:33:10 +02:00
Bram 48b789d268 Added: foojay-resolver (fixes issues with the latest Fabric Loom dependency
Re-added: Parchment (mixed something up)
Fixed: Compile issue with removed class import
2024-07-04 23:29:29 +02:00
ShadowOfHeaven a765347cba Added a class description 2024-07-04 23:02:54 +02:00
ShadowOfHeaven 21fe4c920d Just a little sneak of my name there 2024-07-04 22:55:05 +02:00
Bram d35505b83a Updated: Fabric Loom 0.11-SNAPSHOT -> 1.7.2
Updated: Gradle Wrapper 8.5 -> 8.7 (Required by Fabric Loom)
Removed: parchmentmc (Fabric Loom provides native support for Parchment mappings as of version 0.9)
Changed: Fabric Dependency id from fabric -> fabric-api (see: https://fabricmc.net/2022/11/24/1193.html)
2024-07-04 21:36:50 +02:00
ShadowOfHeaven 930d1370ce Removed the User value association system. Another PR will be made 2024-07-04 09:45:25 +02:00
ShadowOfHeaven 4f01b9b2d6 Added a User value association system 2024-07-04 00:35:46 +02:00
ShadowOfHeaven 79ae548463 Optimized the EventManager v2 2024-07-03 19:59:41 +02:00
booky10 08120db4b4 Add small biome mapping test 2024-07-03 18:18:38 +02:00
booky10 11d2d979cc Fix biome mapping not being compressed and shadowed 2024-07-03 18:18:26 +02:00
booky10 3822609afe Implement most particle data codecs
Theoretically required for full support of biome data serialization
2024-07-03 18:15:25 +02:00
booky10 dd057ca20f Finish biome effects implementation 2024-07-03 15:16:32 +02:00
ShadowOfHeaven 42403c9f51 Optimized the EventManager 2024-07-03 00:37:35 +02:00
AbhigyaKrishna 9eb6b11fe0 fix: Statistics not being cached 2024-07-03 02:27:32 +05:30
booky10 8c02988330 Save dimension type as reference while decoding
Reference gets resolved when calling the getter, this allows legacy version registries to be swapped before resolving the new dimension type
2024-07-02 18:58:12 +02:00
AbhigyaKrishna fd7223a259 fix: Statistics reading 2024-07-02 15:37:16 +05:30
AbhigyaKrishna b22560852f chores: Update licence 2024-07-02 15:30:06 +05:30
AbhigyaKrishna 3498720e8c feat: Change SequentialNBTReader class design to resemble reader 2024-07-02 15:24:46 +05:30
AbhigyaKrishna 457aed221b fix: NBTEnd not being read when there is no data 2024-07-02 14:39:24 +05:30
AbhigyaKrishna d6fb68d8e7 chores: Update licence 2024-07-02 03:32:50 +05:30
AbhigyaKrishna 0e6c15eb8b feat: Diff load order 2024-07-02 03:30:00 +05:30
AbhigyaKrishna 485124bfef feat: change diff tags order
'removals' tags now come before 'additions' tag
Order: removals, additions, changes
2024-07-02 03:27:53 +05:30
AbhigyaKrishna 3517d1fd00 feat: Refactor SequentialNBTReader 2024-07-02 03:07:38 +05:30
AbhigyaKrishna 7d80f08e1c fix: nbt reading 2024-07-02 03:04:31 +05:30
AbhigyaKrishna ff0af88299 chores: Removed debug statements 2024-07-02 02:16:25 +05:30
booky10 ba08959d39 Add wolf_variant registry and start with biome registry
Biome registry is required for wolf variant registry to fully work
2024-07-01 22:41:13 +02:00
AbhigyaKrishna 050aab96f7 fix: SequentialNBTReader parsing 2024-07-02 02:11:00 +05:30
booky10 afcdfae5a1 Fix version being ignored in User#getMinWorldHeight 2024-07-01 21:13:46 +02:00
booky10 8908cce24f Implement enchantment effect component types 2024-07-01 21:08:49 +02:00
booky10 31447cb6cf Fix test errors 2024-07-01 20:21:02 +02:00
booky10 884ab800f5 Start working on enchantment registry 2024-07-01 19:11:32 +02:00
booky10 6e87103114 Move everything related to registries from InternalPacketListener to new class 2024-07-01 16:24:55 +02:00
booky10 c3b8c213d3 Implement trim_material as a synchronized registry 2024-07-01 16:22:14 +02:00
booky10 33f50a0eb9 Implement trim_pattern as a synchronized registry 2024-07-01 16:12:32 +02:00
booky10 3f309bc92d Migrate usages of chat types to new registry 2024-07-01 16:04:33 +02:00
booky10 8315b8d335 Implement chat_type as a synchronized registry 2024-07-01 15:59:15 +02:00
booky10 902c99aca2 Revert some now useless changes to MappedEntity#getId 2024-07-01 15:41:52 +02:00
booky10 f4fc20a1c8 Revert formatting changes in ChunkReader_v1_7 2024-07-01 15:36:50 +02:00
booky10 0690c45558 Add quality methods to StaticDimensionType 2024-07-01 15:35:11 +02:00
booky10 699fd9ac02 Finish migrating Dimension to DimensionType 2024-07-01 15:32:08 +02:00
booky10 b01db5ca2a Work on creating registries of sent registries 2024-07-01 12:39:13 +02:00
booky10 4fdc9c67ad Finish encode of DimensionType 2024-07-01 12:38:56 +02:00
AbhigyaKrishna 395ed0d58e fix: version mapper initialization 2024-07-01 10:03:17 +05:30
AbhigyaKrishna bcad08260c feat: Implement sequential reading for all mappings 2024-07-01 09:55:34 +05:30
AbhigyaKrishna 06be6d52b2 feat: Implement closeable on sequential reader 2024-07-01 09:55:05 +05:30
AbhigyaKrishna 7ec896b8b0 feat: Updated block mappings for easier access to v2 mapping deserializer 2024-07-01 09:37:39 +05:30
AbhigyaKrishna 2717a94db5 feat: Implement sequential reader in type builder 2024-07-01 09:08:19 +05:30
AbhigyaKrishna 2ea20e3d19 feat: Bump Compression Task version 2024-07-01 09:07:43 +05:30
AbhigyaKrishna 94d939ba67 feat: Changes to mappings format 2024-07-01 08:13:25 +05:30
AbhigyaKrishna 620af0aec0 feat: SequentialNBTReader 2024-07-01 07:15:07 +05:30
AbhigyaKrishna 2299d82b92 feat: Improved NBTSerializer class 2024-07-01 05:56:54 +05:30
AbhigyaKrishna 25dbe61f64 feat: NBTLimiter class rewrite
Rewritten NBTLimiter class with better approach for better extendability
2024-07-01 05:53:41 +05:30
retrooper ecd183156a * Bump version up to 2.4.1-SNAPSHOT
* Marked SpigotReflectionUtil.getEntityById as internal
2024-07-01 00:20:35 +02:00
booky10 3f0cb7e799 Start working on dimension type registry 2024-06-30 23:33:40 +02:00
booky10 7b209c9733 Revert changes to Dimension 2024-06-30 22:37:10 +02:00
booky10 dfd490afc8 Merge branch '2.0' into feat/network-synced-registries 2024-06-30 22:34:34 +02:00
booky10 92564a3a09 More WIP changes to networked registries 2024-06-27 16:10:20 +02:00
booky10 172cfceed7 WIP starting network synchronized registries 2024-06-25 20:47:01 +02:00
212 changed files with 37235 additions and 25707 deletions

No files matched your search

+1 -3
View File
@@ -1,5 +1,3 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: retrooper
custom: ['https://paypal.me/retrooperdev']
github: [retrooper]
+13 -10
View File
@@ -8,16 +8,19 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Validate gradle wrapper
uses: gradle/actions/wrapper-validation@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'adopt'
cache: 'gradle'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
@@ -25,37 +28,37 @@ jobs:
run: chmod +x gradlew && ./gradlew build
- name: Upload build artifacts api
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: api-build
path: api/build/libs
- name: Upload build artifacts spigot
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: spigot-build
path: spigot/build/libs
- name: Upload build artifacts netty-common
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: netty-common-build
path: netty-common/build/libs
- name: Upload build artifacts bungeecord
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: bungeecord-build
path: bungeecord/build/libs
- name: Upload build artifacts velocity
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: velocity-build
path: velocity/build/libs
- name: Upload build artifacts fabric
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: fabric-build
path: fabric/build/libs
path: fabric/build/libs
+32 -22
View File
@@ -1,33 +1,43 @@
# packetevents
<!--suppress HtmlDeprecatedAttribute -->
<div align="center">
<h2><i>packetevents</i></h2>
<a href="https://github.com/retrooper/packetevents/actions"><img src="https://img.shields.io/github/actions/workflow/status/retrooper/packetevents/gradle-publish.yml?style=for-the-badge&logo=github"></a>
<a href="https://discord.gg/DVHxPPxHZc"><img src="https://img.shields.io/discord/721686193061888071?color=5562e9&logo=discord&logoColor=white&style=for-the-badge"></a>
<img src="https://img.shields.io/github/license/retrooper/packetevents?style=for-the-badge&logo=github">
<a href="https://bstats.org/plugin/bukkit/packetevents/11327"><img src="https://img.shields.io/bstats/servers/11327?style=for-the-badge"></a>
<a href="https://github.com/retrooper/packetevents/releases"><img src="https://img.shields.io/github/downloads/retrooper/packetevents/total.svg?style=for-the-badge&logo=github"></a>
</div>
PacketEvents is emerging as a strong contender in the realm of Minecraft packet processing libraries. Designed to simplify packet manipulation while delivering high performance, this powerful tool aims to demystify the complexities of projects utilizing packets. Imbued with versatility, PacketEvents supports various platforms such as Spigot, BungeeCord, Velocity, and Fabric.
[![](https://img.shields.io/badge/License-GPLv3-yellow.svg)](https://github.com/retrooper/packetevents/blob/2.0/LICENSE) [![](https://github.com/retrooper/packetevents/actions/workflows/build.yml/badge.svg)](https://github.com/retrooper/packetevents/actions) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/prqzNg9tRU)
<h3>Resources</h3>
PacketEvents is a fast and efficient multi-platform packet processing library. Our packet wrappers are easy to use with multi-version support. With our API you can write code that runs on Spigot, BungeeCord, Velocity, and Fabric! Switch to PacketEvents today by joining \
our [Discord Server](https://discord.me/packetevents) for help.
- [Read documentation](https://docs.packetevents.com/)
- [Getting Started](https://docs.packetevents.com/getting-started)
- [JavaDocs](javadocs.packetevents.com)
- [Releases](https://github.com/retrooper/packetevents/releases/)
- [GitHub](https://github.com/retrooper/packetevents/releases/)
- [Modrinth](https://modrinth.com/plugin/packetevents)
- [SpigotMC](https://www.spigotmc.org/resources/packetevents-api.80279/)
- [Development Builds](https://ci.codemc.io/job/retrooper/job/packetevents)
- [Statistics](https://bstats.org/plugin/bukkit/packetevents/11327)
### Links
* [Find release builds on Modrinth](https://modrinth.com/plugin/packetevents)
* [Find development builds on Jenkins](https://ci.codemc.io/job/retrooper/job/packetevents)
* [View documentation on our JavaDocs Page](https://packetevents.github.io/javadocs)
<h3>Sponsors</h3>
### Setup
[How to Setup PacketEvents](https://github.com/retrooper/packetevents/wiki/Depending-on-pre%E2%80%90built-PacketEvents)
<i>Thanks to the following sponsors for supporting this project:</i>
### Examples / How to use PacketEvents
[Example repository](https://github.com/retrooper/packetevents-example)
<a href="https://pebblehost.com"><img src="https://pebblehost.com/src/img/logos/main-old.png" width=150><br>
### Sponsorships
[![](https://www.ej-technologies.com/images/product_banners/jprofiler_small.png)](https://www.ej-technologies.com/products/jprofiler/overview.html)
<a href="https://www.ej-technologies.com"><img src="https://www.ej-technologies.com/images/product_banners/jprofiler_small.png"><br>
### Statistics
PacketEvents optionally collects statistics using [bStats](https://bstats.org/)\
Check out our [bStats page](https://bstats.org/plugin/bukkit/packetevents/11327)
<a href="https://github.com/radstevee"><img src="https://github.com/radstevee.png" width="50px" alt="radstevee"/></a><br>
### Credit to these projects
[wiki.vg/Protocol](https://wiki.vg/Protocol)\
[netty](https://github.com/netty/netty)\
[gson](https://github.com/google/gson)\
[adventure](https://github.com/KyoriPowered/adventure)\
<h3>Credits</h3>
<i>Here are some projects that we are heavily inspired by, thus we have integrated</i>\
<i>small portions of their code into our work.</i>
[Protocol Documentation for the Minecraft Java Edition](https://wiki.vg/Protocol)\
[ViaVersion](https://github.com/ViaVersion/ViaVersion)\
[ProtocolSupport](https://github.com/ProtocolSupport/ProtocolSupport)\
[adventure](https://github.com/KyoriPowered/adventure)\
[MCProtocolLib](https://github.com/GeyserMC/MCProtocolLib/)
+14
View File
@@ -21,6 +21,7 @@ java {
dependencies {
compileOnlyApi(libs.bundles.adventure)
compileOnlyApi(libs.bundles.adventure.serializers)
implementation(libs.adventure.api)
api(project(":patch:adventure-text-serializer-gson", "shadow")) {
excludeAdventure()
@@ -47,7 +48,11 @@ mappingCompression {
compress("block/legacy_block_mappings.json")
compress("block/modern_block_mappings.json")
compress("enchantment/enchantment_type_data.json")
compress("stats/statistics.json")
compress("world/biome_data.json")
}
with<JsonArrayCompressionStrategy> {
@@ -60,13 +65,19 @@ mappingCompression {
compress("command/argument_parser_mappings.json")
compress("damage/damagetype_mappings.json")
compress("enchantment/effect_component_type.json")
compress("entity/entity_data_type_mappings.json")
compress("entity/painting_mappings.json")
compress("entity/wolf_variant_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_jukebox_song_mappings.json")
compress("item/item_map_decoration_type_mappings.json")
compress("item/item_potion_mappings.json")
compress("item/item_trim_material_mappings.json")
@@ -77,6 +88,7 @@ mappingCompression {
compress("sound/sound_mappings.json")
compress("world/biome_mappings.json")
compress("world/world_position_source_mappings.json")
}
@@ -88,6 +100,8 @@ mappingCompression {
compress("entity/legacy_entity_type_mappings.json")
compress("item/item_type_mappings.json")
compress("world/dimension_type_mappings.json")
}
}
@@ -34,22 +34,22 @@ import com.github.retrooper.packetevents.util.updatechecker.UpdateChecker;
import java.util.logging.Logger;
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 static final LogManager LOG_MANAGER = new LogManager();
private final EventManager eventManager = new EventManager();
private final PacketEventsSettings settings = new PacketEventsSettings();
private final UpdateChecker updateChecker = new UpdateChecker();
private final LogManager logManager = new LogManager();
private static final Logger LOGGER = Logger.getLogger(PacketEventsAPI.class.getName());
public EventManager getEventManager() {
return EVENT_MANAGER;
return eventManager;
}
public PacketEventsSettings getSettings() {
return SETTINGS;
return settings;
}
public UpdateChecker getUpdateChecker() {
return UPDATE_CHECKER;
return updateChecker;
}
public PEVersion getVersion() {
@@ -62,7 +62,7 @@ public abstract class PacketEventsAPI<T> {
}
public LogManager getLogManager() {
return LOG_MANAGER;
return logManager;
}
public abstract void load();
@@ -75,6 +75,8 @@ public abstract class PacketEventsAPI<T> {
public abstract void terminate();
public abstract boolean isTerminated();
public abstract T getPlugin();
public abstract ServerManager getServerManager();
@@ -23,12 +23,36 @@ 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;
/**
* Class for event managing. Implements both, internal and API methods.
*
* @apiNote It is highly recommended to register the event listeners at server start-up,
* without frequent modifications during runtime, since this class needs to recalculate all of
* its event listeners during any modifications. In other words, it assumes that reads (events fired)
* greatly outnumber writes (listeners modifications). If your case requires frequent listener modifications,
* open a pull request on GitHub and describe your case.
*
* @author retrooper
* @author ShadowOfHeaven (optimization)
*
*/
public class EventManager {
private final Map<PacketListenerPriority, Set<PacketListenerCommon>> listenersMap =
Collections.synchronizedMap(new EnumMap<>(PacketListenerPriority.class));
//Using a ConcurrentHashMap is faster and more secure here, compared to Collections.synchronizedMap(new EnumMap<>(PacketListenerPriority.class))
//This is mainly due to:
//1. On each modification Collections.synchronizedMap synchronizes the whole Map object, while ConcurrentHashMap only it's internal, currently modified Node
//2. ConcurrentHashMap won't fail in a multi-thread environment, while Collections.synchronizedMap is said to have a lot of potential problems,
//being a generalized method for synchronization
private final Map<PacketListenerPriority, Set<PacketListenerCommon>> listenersMap = new ConcurrentHashMap<>();
//Since reads greatly outnumber writes, create an array for the best possible iteration time
//Updated as a whole on writes, no index modifications are allowed
private volatile PacketListenerCommon[] listeners = new PacketListenerCommon[0];
/**
* Call the PacketEvent.
@@ -43,32 +67,43 @@ public class EventManager {
callEvent(event, null);
}
/**
* Call the PacketEvent.
* This method processes the event on all the registered dynamic packet event listeners.
* The {@link PacketListenerPriority#LOWEST} prioritized listeners will be processing first,
* the {@link PacketListenerPriority#MONITOR} will be processing last and can
* be the final decider whether the event has been cancelled or not.
*
* @param event {@link PacketEvent}
* @param postCallListenerAction The action to be ran after all the listeners have finished processing
*/
public void callEvent(PacketEvent event, @Nullable Runnable postCallListenerAction) {
for (PacketListenerPriority priority : PacketListenerPriority.values()) {
Set<PacketListenerCommon> listeners = listenersMap.get(priority);
if (listeners != null) {
for (PacketListenerCommon listener : listeners) {
try {
event.call(listener);
} catch (Exception t) {
// ignore handshake exceptions
if (t.getClass() != InvalidHandshakeException.class) {
PacketEvents.getAPI().getLogger().log(Level.WARNING, "PacketEvents caught an unhandled exception while calling your listener.", t);
}
}
if (postCallListenerAction != null) {
postCallListenerAction.run();
}
for (PacketListenerCommon listener : listeners) {
try {
event.call(listener);
} catch (Exception t) {
// ignore handshake exceptions
if (t.getClass() != InvalidHandshakeException.class) {
PacketEvents.getAPI().getLogger().log(Level.WARNING, "PacketEvents caught an unhandled exception while calling your listener.", t);
}
}
if (postCallListenerAction != null) {
postCallListenerAction.run();
}
}
// For performance reasons, we don't want to re-encode the packet if it's not needed.
if (event instanceof ProtocolPacketEvent && !((ProtocolPacketEvent<?>) event).needsReEncode()) {
((ProtocolPacketEvent<?>) event).setLastUsedWrapper(null);
if (event instanceof ProtocolPacketEvent && !((ProtocolPacketEvent) event).needsReEncode()) {
((ProtocolPacketEvent) event).setLastUsedWrapper(null);
}
}
/**
* Register the dynamic packet event listener.
*
* @param listener {@link PacketListenerCommon}
* @param priority {@link PacketListenerPriority}
*/
public PacketListenerCommon registerListener(PacketListener listener, PacketListenerPriority priority) {
PacketListenerCommon packetListenerAbstract = listener.asAbstract(priority);
return registerListener(packetListenerAbstract);
@@ -80,12 +115,8 @@ public class EventManager {
* @param listener {@link PacketListenerCommon}
*/
public PacketListenerCommon registerListener(PacketListenerCommon listener) {
Set<PacketListenerCommon> listenerSet = listenersMap.get(listener.getPriority());
if (listenerSet == null) {
listenerSet = new CopyOnWriteArraySet<>();
}
listenerSet.add(listener);
listenersMap.put(listener.getPriority(), listenerSet);
this.registerListenerNoRecalculation(listener);
this.recalculateListeners();
return listener;
}
@@ -96,21 +127,22 @@ public class EventManager {
*/
public PacketListenerCommon[] registerListeners(PacketListenerCommon... listeners) {
for (PacketListenerCommon listener : listeners) {
registerListener(listener);
this.registerListenerNoRecalculation(listener);
}
this.recalculateListeners();
return listeners;
}
public void unregisterListener(PacketListenerCommon listener) {
Set<PacketListenerCommon> listenerSet = listenersMap.get(listener.getPriority());
if (listenerSet == null) return;
listenerSet.remove(listener);
if (this.unregisterListenerNoRecalculation(listener)) this.recalculateListeners();
}
public void unregisterListeners(PacketListenerCommon... listeners) {
boolean modified = false;
for (PacketListenerCommon listener : listeners) {
unregisterListener(listener);
modified |= this.unregisterListenerNoRecalculation(listener);//OR - at least one of these needs to be true to return true, meaning the boolean will permanently stay true if once set so
}
if (modified) this.recalculateListeners();
}
@@ -118,6 +150,36 @@ public class EventManager {
* Unregister all dynamic packet event listeners.
*/
public void unregisterAllListeners() {
listenersMap.clear();
this.listenersMap.clear();
synchronized (this) {//like booky10 said, the synchronization is necessary here
this.listeners = new PacketListenerCommon[0];
}
}
//Needs to be synchronized in order to avoid race conditions (for example where the 'listeners' variable
//is overridden by its non-up-to-date value, simply because it finished a bit later than the most recent update)
private void recalculateListeners() {
synchronized (this) {
List<PacketListenerCommon> list = new ArrayList<>();
//adds from LOWEST to MONITOR, so in the correct order
for (PacketListenerPriority priority : PacketListenerPriority.values()) {
Set<PacketListenerCommon> set = this.listenersMap.get(priority);
if (set != null) list.addAll(set);
}
this.listeners = list.toArray(new PacketListenerCommon[0]);
}
}
//Internal registration methods, specifically separated for lesser overhead when registering an array of Listeners
private void registerListenerNoRecalculation(PacketListenerCommon listener) {
Set<PacketListenerCommon> listenerSet = this.listenersMap.computeIfAbsent(listener.getPriority(), p -> new CopyOnWriteArraySet<>());
listenerSet.add(listener);
}
//Returns true if the listener was removed, so a modification occurred
private boolean unregisterListenerNoRecalculation(PacketListenerCommon listener) {
Set<PacketListenerCommon> listenerSet = this.listenersMap.get(listener.getPriority());
return listenerSet != null && listenerSet.remove(listener);
}
}
@@ -30,8 +30,7 @@ package com.github.retrooper.packetevents.event;
*/
public enum PacketListenerPriority {
/**
* Listener is of very low importance.
* This listener will be ran first.
* This listener will be run first and has little say in the outcome of events.
*/
LOWEST,
@@ -41,8 +40,11 @@ public enum PacketListenerPriority {
LOW,
/**
* Default listener priority.
* Listener is neither important nor unimportant and may run normally.
* The normal listener priority.
* If possible, always pick this.
* It allows other projects to easily overturn your decisions.
* Moreover, it is pretty bold to assume that your project should
* always have the final say.
*/
NORMAL,
@@ -52,13 +54,13 @@ public enum PacketListenerPriority {
HIGH,
/**
* Listener is of critical importance and wants to decide the cancellation of an event.
* Listener is of critical importance. Use this to decide the final state of packets.
*/
HIGHEST,
/**
* Listener is purely trying to decide the cancellation of an event.
* This listener should be ran last.
* Only use this priority if you want to perform logic based on the outcome of an event.
* Please do not modify packets in this stage.
*/
MONITOR;
@@ -18,7 +18,6 @@
package com.github.retrooper.packetevents.event;
import com.github.retrooper.packetevents.event.simple.*;
import com.github.retrooper.packetevents.exception.PacketProcessException;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
@@ -26,7 +25,7 @@ import com.github.retrooper.packetevents.protocol.PacketSide;
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.protocol.player.User;
public class PacketReceiveEvent extends ProtocolPacketEvent<Object> {
public class PacketReceiveEvent extends ProtocolPacketEvent {
protected PacketReceiveEvent(Object channel, User user, Object player, Object rawByteBuf,
boolean autoProtocolTranslation) throws PacketProcessException {
super(PacketSide.CLIENT, channel, user, player, rawByteBuf, autoProtocolTranslation);
@@ -29,7 +29,7 @@ import com.github.retrooper.packetevents.protocol.player.User;
import java.util.ArrayList;
import java.util.List;
public class PacketSendEvent extends ProtocolPacketEvent<Object> {
public class PacketSendEvent extends ProtocolPacketEvent {
private List<Runnable> tasksAfterSend = null;
protected PacketSendEvent(Object channel, User user, Object player, Object rawByteBuf,
@@ -24,11 +24,11 @@ package com.github.retrooper.packetevents.event;
* @author retrooper
* @since 1.6.9
*/
public interface PlayerEvent<T> {
public interface PlayerEvent {
/**
* Associated player.
*
* @return Player.
*/
T getPlayer();
<T> T getPlayer();
}
@@ -39,11 +39,11 @@ import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
public abstract class ProtocolPacketEvent<T> extends PacketEvent implements PlayerEvent<T>, CancellableEvent, UserEvent {
public abstract class ProtocolPacketEvent extends PacketEvent implements PlayerEvent, CancellableEvent, UserEvent {
private final Object channel;
private final ConnectionState connectionState;
private final User user;
private final T player;
private final Object player;
private Object byteBuf;
private final int packetID;
private final PacketTypeCommon packetType;
@@ -55,7 +55,7 @@ public abstract class ProtocolPacketEvent<T> extends PacketEvent implements Play
private boolean needsReEncode = PacketEvents.getAPI().getSettings().reEncodeByDefault();
public ProtocolPacketEvent(PacketSide packetSide, Object channel,
User user, T player, Object byteBuf,
User user, Object player, Object byteBuf,
boolean autoProtocolTranslation) throws PacketProcessException {
this.channel = channel;
this.user = user;
@@ -92,7 +92,7 @@ public abstract class ProtocolPacketEvent<T> extends PacketEvent implements Play
}
public ProtocolPacketEvent(int packetID, PacketTypeCommon packetType, ServerVersion serverVersion, Object channel,
User user, T player, Object byteBuf) {
User user, Object player, Object byteBuf) {
this.channel = channel;
this.user = user;
this.player = player;
@@ -133,8 +133,8 @@ public abstract class ProtocolPacketEvent<T> extends PacketEvent implements Play
}
@Override
public T getPlayer() {
return player;
public <T> T getPlayer() {
return (T) player;
}
public ConnectionState getConnectionState() {
@@ -213,7 +213,7 @@ public abstract class ProtocolPacketEvent<T> extends PacketEvent implements Play
}
@Override
public ProtocolPacketEvent<?> clone() {
public ProtocolPacketEvent clone() {
return this instanceof PacketReceiveEvent ? ((PacketReceiveEvent) this).clone()
: ((PacketSendEvent) this).clone();
}
@@ -20,7 +20,7 @@ package com.github.retrooper.packetevents.event;
import com.github.retrooper.packetevents.protocol.player.User;
public class UserLoginEvent extends PacketEvent implements CallableEvent, UserEvent, PlayerEvent<Object> {
public class UserLoginEvent extends PacketEvent implements CallableEvent, UserEvent, PlayerEvent {
private final User user;
private final Object player;
@@ -35,8 +35,8 @@ public class UserLoginEvent extends PacketEvent implements CallableEvent, UserEv
}
@Override
public Object getPlayer() {
return player;
public <T> T getPlayer() {
return (T) player;
}
@Override
@@ -26,31 +26,21 @@ import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.manager.protocol.ProtocolManager;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.SynchronizedRegistriesHandler;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerRegistryData;
import com.github.retrooper.packetevents.wrapper.configuration.server.WrapperConfigServerRegistryData.RegistryElement;
import com.github.retrooper.packetevents.wrapper.handshaking.client.WrapperHandshakingClientHandshake;
import com.github.retrooper.packetevents.wrapper.login.server.WrapperLoginServerLoginSuccess;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerJoinGame;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import java.net.InetSocketAddress;
import java.util.List;
public class InternalPacketListener extends PacketListenerAbstract {
private static final ResourceLocation DIMENSION_TYPE_REGISTRY_KEY =
ResourceLocation.minecraft("dimension_type");
public InternalPacketListener() {
this(PacketListenerPriority.LOWEST);
}
@@ -92,94 +82,37 @@ public class InternalPacketListener extends PacketListenerAbstract {
}
}
// The server sends dimension information in configuration phase >= 1.20.2
// The server sends dimension information in configuration phase, since 1.20.2
else if (event.getPacketType() == PacketType.Configuration.Server.REGISTRY_DATA) {
WrapperConfigServerRegistryData registryData = new WrapperConfigServerRegistryData(event);
WrapperConfigServerRegistryData packet = new WrapperConfigServerRegistryData(event);
// Store world data
NBTCompound registryDataTag = registryData.getRegistryData();
NBTList<NBTCompound> list = null;
if (registryDataTag != null) { // <1.20.5
//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);
List<RegistryElement> elements = registryData.getElements();
if (elements != null) {
int i = 0;
for (RegistryElement element : elements) {
NBTCompound tag = new NBTCompound();
tag.setTag("name", new NBTString(element.getId().toString()));
tag.setTag("id", new NBTInt(i++));
if (element.getData() != null) { // may be null because of known packs not being sent
tag.setTag("element", element.getData());
}
list.addTag(tag);
}
}
if (packet.getElements() != null) { // 1.20.2 to 1.20.5
SynchronizedRegistriesHandler.handleRegistry(user, packet.getServerVersion().toClientVersion(),
packet.getRegistryKey(), packet.getElements());
}
if (list != null) {
user.setWorldNBT(list);
if (packet.getRegistryData() != null) { // since 1.20.5
SynchronizedRegistriesHandler.handleLegacyRegistries(user, packet.getServerVersion()
.toClientVersion(), packet.getRegistryData());
}
}
// The server sends dimension information in login packet for >= 1.17 and < 1.20.2
// The server sends registry info in login packet for 1.16 to 1.20.1
else if (event.getPacketType() == PacketType.Play.Server.JOIN_GAME) {
WrapperPlayServerJoinGame joinGame = new WrapperPlayServerJoinGame(event);
user.setEntityId(joinGame.getEntityId());
user.setDimension(joinGame.getDimension());
if (event.getServerVersion().isOlderThanOrEquals(ServerVersion.V_1_16_5)) {
return; // Fixed world height, no tags are sent to the client
if (joinGame.getDimensionCodec() != null) { // 1.16 to 1.20.1
SynchronizedRegistriesHandler.handleLegacyRegistries(user, joinGame.getServerVersion().toClientVersion(),
joinGame.getDimensionCodec());
}
// Store world data
NBTCompound dimensionCodec = joinGame.getDimensionCodec();
if (dimensionCodec != null) {
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
user.switchDimensionType(joinGame.getServerVersion(), joinGame.getDimension());
user.setDimensionType(joinGame.getDimensionType());
}
// Respawn is used to switch dimensions
else if (event.getPacketType() == PacketType.Play.Server.RESPAWN) {
WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn(event);
user.setDimension(respawn.getDimension());
if (event.getServerVersion().isOlderThanOrEquals(ServerVersion.V_1_16_5)) {
return; // Fixed world height, no tags are sent to the client
}
user.switchDimensionType(respawn.getServerVersion(), respawn.getDimension());
WrapperPlayServerRespawn packet = new WrapperPlayServerRespawn(event);
user.setDimensionType(packet.getDimensionType());
} else if (event.getPacketType() == PacketType.Play.Server.CONFIGURATION_START) {
user.setEncoderState(ConnectionState.CONFIGURATION);
} else if (event.getPacketType() == PacketType.Configuration.Server.CONFIGURATION_END) {
@@ -104,10 +104,13 @@ public interface ProtocolManager {
PacketWrapper<?>[] wrappers = PacketTransformationUtil.transform(wrapper);
Object[] buffers = new Object[wrappers.length];
for (int i = 0; i < wrappers.length; i++) {
wrappers[i].prepareForSend(channel, outgoing);
buffers[i] = wrappers[i].buffer;
// Fix race condition when sending packets to multiple people (due to when the buffer is freed)
wrappers[i].buffer = null;
PacketWrapper<?> wrappper = wrappers[i];
synchronized (wrappper.bufferLock) {
wrappper.prepareForSend(channel, outgoing);
buffers[i] = wrappper.buffer;
// Fix race condition when sending packets to multiple people (due to when the buffer is freed)
wrappper.buffer = null;
}
}
return buffers;
}
@@ -18,6 +18,10 @@
package com.github.retrooper.packetevents.manager.server;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import org.jetbrains.annotations.Nullable;
public interface ServerManager {
/**
* Get the server version.
@@ -34,4 +38,20 @@ public interface ServerManager {
default SystemOS getOS() {
return SystemOS.getOS();
}
/**
* Gets a platform-specific network-synchronized-registries cache key.
* <p>
* This tells packetevents, if a registry should be cached or read again. On
* backend servers with global registries, this may be a constant value. On
* proxy servers with per-server registries, this may be a value which depends
* on the current server the {@link User} is on.
*
* @param user the {@link User} for which the registry gets read
* @param version the version the packet is for
* @return some value or null for no caching at all
*/
default @Nullable Object getRegistryCacheKey(User user, ClientVersion version) {
return null; // no caching
}
}
@@ -49,7 +49,8 @@ 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),
//1.21 and 1.21.1 have the same protocol version
V_1_21(767), V_1_21_1(767),
//TODO UPDATE Add server version constant
ERROR(-1, true);
@@ -19,17 +19,38 @@
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.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
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.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.Index;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
public interface ChatType extends MappedEntity {
import java.util.Objects;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
public interface ChatType extends MappedEntity, CopyableEntity<ChatType> {
@UnknownNullability("only nullable for 1.19")
ChatTypeDecoration getChatDecoration();
@ApiStatus.Obsolete(since = "1.19.1")
@Nullable ChatTypeDecoration getOverlayDecoration();
@UnknownNullability("only nullable for 1.19")
ChatTypeDecoration getNarrationDecoration();
@ApiStatus.Obsolete(since = "1.19.1")
@Nullable NarrationPriority getNarrationPriority();
static ChatType readDirect(PacketWrapper<?> wrapper) {
ChatTypeDecoration chatDecoration = ChatTypeDecoration.read(wrapper);
ChatTypeDecoration narrationDecoration = ChatTypeDecoration.read(wrapper);
@@ -41,9 +62,105 @@ public interface ChatType extends MappedEntity {
ChatTypeDecoration.write(wrapper, chatType.getNarrationDecoration());
}
static ChatType decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
// everything is nullable in 1.19, even the chat format!
NBTCompound compound = (NBTCompound) nbt;
NBTCompound chatTag = compound.getCompoundTagOrNull("chat");
NBTCompound narrationTag = compound.getCompoundTagOrNull("narration");
ChatTypeDecoration overlay = null;
NarrationPriority narrationPriority = null;
if (version.isOlderThan(ClientVersion.V_1_19_1)) {
NBTCompound overlayTag = compound.getCompoundTagOrNull("overlay");
if (overlayTag != null) {
// why is this nullable? why is this wrapped? There is no reason to wrap this...
overlayTag = overlayTag.getCompoundTagOrNull("description");
if (overlayTag != null) {
overlay = ChatTypeDecoration.decode(overlayTag, version);
}
}
if (chatTag != null) {
chatTag = chatTag.getCompoundTagOrNull("description");
}
if (narrationTag != null) {
// the priority is the ONLY value which isn't nullable!
narrationPriority = indexValueOrThrow(NarrationPriority.ID_INDEX,
narrationTag.getStringTagValueOrThrow("priority"));
narrationTag = narrationTag.getCompoundTagOrNull("description");
}
} else {
// ensure this isn't null in everything but 1.19
Objects.requireNonNull(chatTag, "NBT chat does not exist");
Objects.requireNonNull(narrationTag, "NBT narration does not exist");
}
ChatTypeDecoration chat = chatTag == null ? null : ChatTypeDecoration.decode(chatTag, version);
ChatTypeDecoration narration = narrationTag == null ? null : ChatTypeDecoration.decode(narrationTag, version);
return new StaticChatType(data, chat, overlay, narration, narrationPriority);
}
static NBT encode(ChatType chatType, ClientVersion version) {
NBTCompound compound = new NBTCompound();
NBT chatTag = chatType.getChatDecoration() == null ? null :
ChatTypeDecoration.encode(chatType.getChatDecoration(), version);
NBT narrationTag = chatType.getNarrationDecoration() == null ? null :
ChatTypeDecoration.encode(chatType.getNarrationDecoration(), version);
if (version.isOlderThan(ClientVersion.V_1_19_1)) {
ChatTypeDecoration overlayDeco = chatType.getOverlayDecoration();
if (overlayDeco != null) {
NBTCompound overlayCompound = new NBTCompound();
overlayCompound.setTag("description",
ChatTypeDecoration.encode(overlayDeco, version));
compound.setTag("overlay", overlayCompound);
}
if (narrationTag != null) {
NBTCompound narrationCompound = new NBTCompound();
narrationCompound.setTag("description", narrationTag);
if (chatType.getNarrationPriority() != null) {
narrationCompound.setTag("priority", new NBTString(chatType.getNarrationPriority().getId()));
}
narrationTag = narrationCompound;
}
if (chatTag != null) {
NBTCompound chatCompound = new NBTCompound();
chatCompound.setTag("description", chatTag);
chatTag = chatCompound;
}
}
if (chatTag != null) {
compound.setTag("chat", chatTag);
}
if (narrationTag != null) {
compound.setTag("narration", narrationTag);
}
return compound;
}
class Bound extends ChatMessage_v1_19_1.ChatTypeBoundNetwork {
public Bound(ChatType type, Component name, @Nullable Component targetName) {
super(type, name, targetName);
}
}
@ApiStatus.Obsolete(since = "1.19.1")
enum NarrationPriority {
CHAT("chat"),
SYSTEM("system");
public static final Index<String, NarrationPriority> ID_INDEX = Index.create(
NarrationPriority.class, NarrationPriority::getId);
private final String id;
NarrationPriority(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
}
@@ -18,23 +18,29 @@
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.NBT;
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.protocol.player.ClientVersion;
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 net.kyori.adventure.util.Index;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.Parameter.*;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.Parameter.CONTENT;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.Parameter.SENDER;
import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration.Parameter.TARGET;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
import static java.util.Arrays.asList;
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
import static net.kyori.adventure.text.format.Style.empty;
@@ -53,32 +59,6 @@ public class ChatTypeDecoration {
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()));
@@ -92,6 +72,44 @@ public class ChatTypeDecoration {
wrapper.writeStyle(decoration.style);
}
public static ChatTypeDecoration decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
String translationKey = compound.getStringTagValueOrThrow("translation_key");
NBT paramsTag = compound.getTagOrThrow("parameters");
List<Parameter> params;
if (paramsTag instanceof NBTList<?>) {
params = new ArrayList<>();
NBTList<?> paramsTagList = (NBTList<?>) paramsTag;
for (NBT paramTag : paramsTagList.getTags()) {
String paramId = ((NBTString) paramTag).getValue();
params.add(indexValueOrThrow(Parameter.ID_INDEX, paramId));
}
} else {
String paramId = ((NBTString) paramsTag).getValue();
params = Collections.singletonList(indexValueOrThrow(Parameter.ID_INDEX, paramId));
}
NBTCompound styleTag = compound.getCompoundTagOrNull("style");
Style style = styleTag == null ? empty() :
AdventureSerializer.getNBTSerializer().deserializeStyle(styleTag);
return new ChatTypeDecoration(translationKey, params, style);
}
public static NBT encode(ChatTypeDecoration decoration, ClientVersion version) {
NBTList<NBTString> paramsTag = NBTList.createStringList();
for (Parameter param : decoration.parameters) {
paramsTag.addTag(new NBTString(param.getId()));
}
NBTCompound compound = new NBTCompound();
compound.setTag("translation_key", new NBTString(decoration.translationKey));
compound.setTag("parameters", paramsTag);
if (!decoration.style.isEmpty()) {
compound.setTag("style", AdventureSerializer.getNBTSerializer()
.serializeStyle(decoration.style));
}
return compound;
}
public static ChatTypeDecoration withSender(String translationKey) {
return new ChatTypeDecoration(translationKey, asList(SENDER, CONTENT), empty());
}
@@ -131,24 +149,46 @@ public class ChatTypeDecoration {
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);
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ChatTypeDecoration)) return false;
ChatTypeDecoration that = (ChatTypeDecoration) obj;
if (!this.translationKey.equals(that.translationKey)) return false;
if (!this.parameters.equals(that.parameters)) return false;
return this.style.equals(that.style);
}
@Override
public int hashCode() {
return Objects.hash(this.translationKey, this.parameters, this.style);
}
public enum Parameter {
SENDER("sender", (component, type) -> type.getName()),
TARGET("target", (component, type) -> type.getTargetName() != null
? type.getTargetName() : Component.empty()),
CONTENT("content", (component, type) -> component);
public static final Index<String, Parameter> ID_INDEX = Index.create(
Parameter.class, Parameter::getId);
private final String id;
private final BiFunction<Component, ChatType.Bound, Component> selector;
Parameter(BiFunction<Component, ChatType.Bound, Component> selector) {
Parameter(String id, BiFunction<Component, ChatType.Bound, Component> selector) {
this.id = id;
this.selector = selector;
}
@Nullable
public static Parameter valueByName(String name) {
try {
return valueOf(name);
} catch (IllegalArgumentException ex) {
return null;
}
public String getId() {
return this.id;
}
@Deprecated
public static @Nullable Parameter valueByName(String id) {
return ID_INDEX.value(id);
}
}
}
@@ -19,71 +19,50 @@
package com.github.retrooper.packetevents.protocol.chat;
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 com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
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 {
public final 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");
private static final VersionedRegistry<ChatType> REGISTRY = new VersionedRegistry<>(
"chat_type", "chat/chat_type_mappings");
private ChatTypes() {
}
@ApiStatus.Internal
public static ChatType define(String key) {
return define(key, withSender("chat.type.text"));
}
@ApiStatus.Internal
public static ChatType define(String key, ChatTypeDecoration chatDeco) {
return define(key, chatDeco, withSender("chat.type.text.narrate"));
}
@ApiStatus.Internal
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();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
};
MappingHelper.registerMapping(TYPES_BUILDER, CHAT_TYPE_MAP, CHAT_TYPE_ID_MAP, chatType);
return chatType;
return REGISTRY.define(key, data ->
new StaticChatType(data, chatDeco, narrationDeco));
}
public static VersionedRegistry<ChatType> getRegistry() {
return REGISTRY;
}
//with minecraft:key
public static ChatType getByName(String name) {
return CHAT_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static ChatType getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
return CHAT_TYPE_ID_MAP.get((byte) index).get(id);
return REGISTRY.getById(version, id);
}
public static final ChatType CHAT = define("chat");
@@ -115,13 +94,14 @@ public class ChatTypes {
/**
* Returns an immutable view of the chat types.
*
* @return Chat Types
*/
public static Collection<ChatType> values() {
return Collections.unmodifiableCollection(CHAT_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -18,41 +18,101 @@
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
public class StaticChatType implements ChatType {
import java.util.Objects;
private final ChatTypeDecoration chatDecoration;
private final ChatTypeDecoration narrationDecoration;
public class StaticChatType extends AbstractMappedEntity implements ChatType {
public StaticChatType(ChatTypeDecoration chatDecoration, ChatTypeDecoration narrationDecoration) {
private final @UnknownNullability("only nullable for 1.19") ChatTypeDecoration chatDecoration;
private final @Nullable ChatTypeDecoration overlayDecoration;
private final @UnknownNullability("only nullable for 1.19") ChatTypeDecoration narrationDecoration;
private final @Nullable NarrationPriority narrationPriority;
public StaticChatType(
ChatTypeDecoration chatDecoration,
ChatTypeDecoration narrationDecoration
) {
this(null, chatDecoration, null, narrationDecoration, null);
}
public StaticChatType(
@Nullable TypesBuilderData data,
ChatTypeDecoration chatDecoration,
ChatTypeDecoration narrationDecoration
) {
this(data, chatDecoration, null, narrationDecoration, null);
}
public StaticChatType(
@UnknownNullability("only nullable for 1.19") ChatTypeDecoration chatDecoration,
@Nullable ChatTypeDecoration overlayDecoration,
@UnknownNullability("only nullable for 1.19") ChatTypeDecoration narrationDecoration,
@Nullable NarrationPriority narrationPriority
) {
this(null, chatDecoration, overlayDecoration, narrationDecoration, narrationPriority);
}
public StaticChatType(
@Nullable TypesBuilderData data,
@UnknownNullability("only nullable for 1.19") ChatTypeDecoration chatDecoration,
@Nullable ChatTypeDecoration overlayDecoration,
@UnknownNullability("only nullable for 1.19") ChatTypeDecoration narrationDecoration,
@Nullable NarrationPriority narrationPriority
) {
super(data);
this.chatDecoration = chatDecoration;
this.overlayDecoration = overlayDecoration;
this.narrationDecoration = narrationDecoration;
this.narrationPriority = narrationPriority;
}
@Override
public ChatTypeDecoration getChatDecoration() {
public ChatType copy(@Nullable TypesBuilderData newData) {
return new StaticChatType(newData, this.chatDecoration,
this.overlayDecoration, this.narrationDecoration, this.narrationPriority);
}
@Override
public @UnknownNullability("only nullable for 1.19") ChatTypeDecoration getChatDecoration() {
return this.chatDecoration;
}
@Override
public ChatTypeDecoration getNarrationDecoration() {
public @Nullable ChatTypeDecoration getOverlayDecoration() {
return this.overlayDecoration;
}
@Override
public @UnknownNullability("only nullable for 1.19") ChatTypeDecoration getNarrationDecoration() {
return this.narrationDecoration;
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
public @Nullable NarrationPriority getNarrationPriority() {
return this.narrationPriority;
}
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticChatType)) return false;
if (!super.equals(obj)) return false;
StaticChatType that = (StaticChatType) obj;
if (!Objects.equals(this.chatDecoration, that.chatDecoration)) return false;
if (!Objects.equals(this.overlayDecoration, that.overlayDecoration)) return false;
if (!Objects.equals(this.narrationDecoration, that.narrationDecoration)) return false;
return this.narrationPriority == that.narrationPriority;
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.chatDecoration, this.overlayDecoration, this.narrationDecoration, this.narrationPriority);
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
public String toString() {
return "StaticChatType{chatDecoration=" + this.chatDecoration + ", overlayDecoration=" + this.overlayDecoration + ", narrationDecoration=" + this.narrationDecoration + ", narrationPriority=" + this.narrationPriority + '}';
}
}
@@ -31,14 +31,13 @@ public class ChatMessageProcessorLegacy implements ChatMessageProcessor {
@Override
public ChatMessage readChatMessage(@NotNull PacketWrapper<?> wrapper) {
Component chatContent = wrapper.readComponent();
int id = wrapper.readByte();
ChatType type = ChatTypes.getById(wrapper.getServerVersion().toClientVersion(), id);
ChatType type = wrapper.readMappedEntity(ChatTypes.getRegistry());
return new ChatMessageLegacy(chatContent, type);
}
@Override
public void writeChatMessage(@NotNull PacketWrapper<?> wrapper, @NotNull ChatMessage data) {
wrapper.writeComponent(data.getChatContent());
wrapper.writeByte(data.getType().getId(wrapper.getServerVersion().toClientVersion()));
wrapper.writeMappedEntity(data.getType());
}
}
@@ -33,8 +33,7 @@ public class ChatMessageProcessor_v1_16 implements ChatMessageProcessor {
@Override
public ChatMessage readChatMessage(@NotNull PacketWrapper<?> wrapper) {
Component chatContent = wrapper.readComponent();
int id = wrapper.readByte();
ChatType type = ChatTypes.getById(wrapper.getServerVersion().toClientVersion(), id);
ChatType type = wrapper.readMappedEntity(ChatTypes.getRegistry());
UUID senderUUID = wrapper.readUUID();
return new ChatMessage_v1_16(chatContent, type, senderUUID);
}
@@ -42,7 +41,7 @@ public class ChatMessageProcessor_v1_16 implements ChatMessageProcessor {
@Override
public void writeChatMessage(@NotNull PacketWrapper<?> wrapper, @NotNull ChatMessage data) {
wrapper.writeComponent(data.getChatContent());
wrapper.writeByte(data.getType().getId(wrapper.getServerVersion().toClientVersion()));
wrapper.writeMappedEntity(data.getType());
wrapper.writeUUID(((ChatMessage_v1_16) data).getSenderUUID());
}
}
@@ -36,8 +36,7 @@ public class ChatMessageProcessor_v1_19 implements ChatMessageProcessor {
public ChatMessage readChatMessage(@NotNull PacketWrapper<?> wrapper) {
Component chatContent = wrapper.readComponent();
Component unsignedChatContent = wrapper.readOptional(PacketWrapper::readComponent);
int id = wrapper.readVarInt();
ChatType type = ChatTypes.getById(wrapper.getServerVersion().toClientVersion(), id);
ChatType type = wrapper.readMappedEntity(ChatTypes.getRegistry());
UUID senderUUID = wrapper.readUUID();
Component senderDisplayName = wrapper.readComponent();
@Nullable Component teamName = wrapper.readOptional(PacketWrapper::readComponent);
@@ -53,7 +52,7 @@ public class ChatMessageProcessor_v1_19 implements ChatMessageProcessor {
ChatMessage_v1_19 newData = (ChatMessage_v1_19) data;
wrapper.writeComponent(newData.getChatContent());
wrapper.writeOptional(newData.getUnsignedChatContent(), PacketWrapper::writeComponent);
wrapper.writeVarInt(newData.getType().getId(wrapper.getServerVersion().toClientVersion()));
wrapper.writeMappedEntity(newData.getType());
wrapper.writeUUID(newData.getSenderUUID());
wrapper.writeComponent(newData.getSenderDisplayName());
wrapper.writeOptional(newData.getTeamName(), PacketWrapper::writeComponent);
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
@@ -88,4 +89,22 @@ public class ComponentPredicate implements Predicate<IComponentMap> {
public void setRequiredComponents(List<ComponentValue<?>> requiredComponents) {
this.requiredComponents = requiredComponents;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof ComponentPredicate)) return false;
ComponentPredicate that = (ComponentPredicate) obj;
return this.requiredComponents.equals(that.requiredComponents);
}
@Override
public int hashCode() {
return Objects.hashCode(this.requiredComponents);
}
@Override
public String toString() {
return "ComponentPredicate{requiredComponents=" + this.requiredComponents + '}';
}
}
@@ -19,6 +19,8 @@
package com.github.retrooper.packetevents.protocol.component;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public interface ComponentType<T> extends MappedEntity {
@@ -26,4 +28,18 @@ public interface ComponentType<T> extends MappedEntity {
T read(PacketWrapper<?> wrapper);
void write(PacketWrapper<?> wrapper, T content);
T decode(NBT nbt, ClientVersion version);
NBT encode(T value, ClientVersion version);
interface Decoder<T> {
T decode(NBT nbt, ClientVersion version);
}
interface Encoder<T> {
NBT encode(T value, ClientVersion version);
}
}
@@ -56,85 +56,51 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Dummy;
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.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
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;
import java.util.function.Function;
public class ComponentTypes {
/**
* Contains all item data component types.
*
* @see EnchantEffectComponentTypes
*/
public final class ComponentTypes {
private static final Map<String, ComponentType<?>> COMPONENT_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, ComponentType<?>>> COMPONENT_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_component_mappings");
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>(
"data_component_type", "item/item_component_mappings");
private ComponentTypes() {
}
@ApiStatus.Internal
public static <T> ComponentType<T> define(String key) {
return define(key, null, null);
}
@ApiStatus.Internal
public static <T> ComponentType<T> define(String key, @Nullable Reader<T> reader, @Nullable Writer<T> writer) {
TypesBuilderData data = TYPES_BUILDER.define(key);
ComponentType<T> type = new ComponentType<T>() {
@Override
public T read(PacketWrapper<?> wrapper) {
return reader == null ? null : reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T content) {
if (writer != null) {
writer.accept(wrapper, content);
}
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ComponentType<?>) {
return this.getName().equals(((ComponentType<?>) obj).getName());
}
return false;
}
@Override
public String toString() {
return "Component[" + this.getName() + "]";
}
};
MappingHelper.registerMapping(TYPES_BUILDER, COMPONENT_TYPE_MAP, COMPONENT_TYPE_ID_MAP, type);
return type;
return REGISTRY.define(key, data -> new StaticComponentType<>(data, reader, writer));
}
public static VersionedRegistry<ComponentType<?>> getRegistry() {
return REGISTRY;
}
// with key
public static ComponentType<?> getByName(String name) {
return COMPONENT_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static ComponentType<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, ComponentType<?>> idMap = COMPONENT_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
// item component types
public static final ComponentType<NBTCompound> CUSTOM_DATA = define("custom_data",
// mojang wraps their "persistent" codec as a stream codec just here,
// so packetevents has to handle nbt strings
@@ -260,16 +226,10 @@ public class ComponentTypes {
* @return Component Types
*/
public static Collection<ComponentType<?>> values() {
return Collections.unmodifiableCollection(COMPONENT_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
@FunctionalInterface
public interface Reader<T> extends Function<PacketWrapper<?>, T> {}
@FunctionalInterface
public interface Writer<T> extends BiConsumer<PacketWrapper<?>, T> {}
}
@@ -0,0 +1,120 @@
/*
* 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;
import com.github.retrooper.packetevents.protocol.component.ComponentType.Decoder;
import com.github.retrooper.packetevents.protocol.component.ComponentType.Encoder;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
/**
* Contains all enchantment effect component types.
*
* @see ComponentTypes
*/
public final class EnchantEffectComponentTypes {
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>(
"enchantment_effect_component_type", "enchantment/effect_component_type");
private EnchantEffectComponentTypes() {
}
@ApiStatus.Internal
public static <T> ComponentType<T> define(String key) {
return define(key, null, null);
}
@ApiStatus.Internal
public static <T> ComponentType<T> define(String key, @Nullable Decoder<T> reader, @Nullable Encoder<T> writer) {
return REGISTRY.define(key, data -> new StaticComponentType<>(data, reader, writer));
}
public static VersionedRegistry<ComponentType<?>> getRegistry() {
return REGISTRY;
}
// just pass everything through for now
public static ComponentType<NBT> DAMAGE_PROTECTION = define("damage_protection",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> DAMAGE_IMMUNITY = define("damage_immunity",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> DAMAGE = define("damage",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> SMASH_DAMAGE_PER_FALLEN_BLOCK = define("smash_damage_per_fallen_block",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> KNOCKBACK = define("knockback",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> ARMOR_EFFECTIVENESS = define("armor_effectiveness",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> POST_ATTACK = define("post_attack",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> HIT_BLOCK = define("hit_block",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> ITEM_DAMAGE = define("item_damage",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> ATTRIBUTES = define("attributes",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> EQUIPMENT_DROPS = define("equipment_drops",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> LOCATION_CHANGED = define("location_changed",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> TICK = define("tick",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> AMMO_USE = define("ammo_use",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PROJECTILE_PIERCING = define("projectile_piercing",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PROJECTILE_SPAWNED = define("projectile_spawned",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PROJECTILE_SPREAD = define("projectile_spread",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PROJECTILE_COUNT = define("projectile_count",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> TRIDENT_RETURN_ACCELERATION = define("trident_return_acceleration",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> FISHING_TIME_REDUCTION = define("fishing_time_reduction",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> FISHING_LUCK_BONUS = define("fishing_luck_bonus",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> BLOCK_EXPERIENCE = define("block_experience",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> MOB_EXPERIENCE = define("mob_experience",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> REPAIR_WITH_XP = define("repair_with_xp",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> CROSSBOW_CHARGE_TIME = define("crossbow_charge_time",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> CROSSBOW_CHARGING_SOUNDS = define("crossbow_charging_sounds",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> TRIDENT_SOUND = define("trident_sound",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PREVENT_EQUIPMENT_DROP = define("prevent_equipment_drop",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> PREVENT_ARMOR_CHANGE = define("prevent_armor_change",
(nbt, version) -> nbt, (val, version) -> val);
public static ComponentType<NBT> TRIDENT_SPIN_ATTACK_STRENGTH = define("trident_spin_attack_strength",
(nbt, version) -> nbt, (val, version) -> val);
static {
REGISTRY.unloadMappings();
}
}
@@ -18,13 +18,44 @@
package com.github.retrooper.packetevents.protocol.component;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
public interface IComponentMap {
@SuppressWarnings("unchecked") // safe in this case
static StaticComponentMap decode(NBT nbt, ClientVersion version, IRegistry<? extends ComponentType<?>> registry) {
NBTCompound compound = (NBTCompound) nbt;
StaticComponentMap.Builder components = StaticComponentMap.builder();
for (Map.Entry<String, NBT> entry : compound.getTags().entrySet()) {
ComponentType<?> type = registry.getByName(entry.getKey());
if (type == null) {
throw new IllegalStateException("Unknown component type named " + entry.getKey() + " encountered");
}
Object value = type.decode(entry.getValue(), version);
components.set((ComponentType<? super Object>) type, value);
}
return components.build();
}
@SuppressWarnings("unchecked") // safe in this case
static NBT encode(StaticComponentMap components, ClientVersion version) {
NBTCompound compound = new NBTCompound();
for (Map.Entry<ComponentType<?>, ?> entry : components.getDelegate().entrySet()) {
String key = entry.getKey().getName().toString();
NBT value = ((ComponentType<? super Object>) entry.getKey()).encode(entry.getValue(), version);
compound.setTag(key, value);
}
return compound;
}
default <T> Optional<T> getOptional(ComponentType<T> type) {
return Optional.ofNullable(this.get(type));
}
@@ -96,6 +96,20 @@ public class PatchableComponentMap implements IComponentMap {
return this.patches;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof PatchableComponentMap)) return false;
PatchableComponentMap that = (PatchableComponentMap) obj;
if (!this.base.equals(that.base)) return false;
return this.patches.equals(that.patches);
}
@Override
public int hashCode() {
return Objects.hash(this.base, this.patches);
}
@Override
public String toString() {
return "PatchableComponentMap{base=" + this.base + ", patches=" + this.patches + '}';
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class StaticComponentMap implements IComponentMap {
@@ -82,6 +83,24 @@ public class StaticComponentMap implements IComponentMap {
return this.empty;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticComponentMap)) return false;
StaticComponentMap that = (StaticComponentMap) obj;
return this.delegate.equals(that.delegate);
}
@Override
public int hashCode() {
return Objects.hashCode(this.delegate);
}
@Override
public String toString() {
return "StaticComponentMap{empty=" + this.empty + ", delegate=" + this.delegate + '}';
}
public static class Builder {
private final Map<ComponentType<?>, Object> map = new HashMap<>();
@@ -0,0 +1,94 @@
/*
* 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;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.Nullable;
public class StaticComponentType<T> extends AbstractMappedEntity implements ComponentType<T> {
private final @Nullable Reader<T> reader;
private final @Nullable Writer<T> writer;
private final @Nullable Decoder<T> decoder;
private final @Nullable Encoder<T> encoder;
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Reader<T> reader,
@Nullable Writer<T> writer
) {
this(data, reader, writer, null, null);
}
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Decoder<T> decoder,
@Nullable Encoder<T> encoder
) {
this(data, null, null, decoder, encoder);
}
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Reader<T> reader,
@Nullable Writer<T> writer,
@Nullable Decoder<T> decoder,
@Nullable Encoder<T> encoder
) {
super(data);
this.reader = reader;
this.writer = writer;
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return this.reader != null ? this.reader.apply(wrapper) : null;
}
@Override
public void write(PacketWrapper<?> wrapper, T content) {
if (this.writer != null) {
this.writer.accept(wrapper, content);
}
}
@Override
public T decode(NBT nbt, ClientVersion version) {
if (this.decoder != null) {
return this.decoder.decode(nbt, version);
}
throw new UnsupportedOperationException();
}
@Override
public NBT encode(T value, ClientVersion version) {
if (this.encoder != null) {
return this.encoder.encode(value, version);
}
throw new UnsupportedOperationException();
}
}
@@ -80,7 +80,7 @@ public class BannerLayers {
public static Layer read(PacketWrapper<?> wrapper) {
BannerPattern pattern = wrapper.readMappedEntityOrDirect(
BannerPatterns::getById, BannerPattern::readDirect);
BannerPatterns.getRegistry(), BannerPattern::readDirect);
DyeColor color = wrapper.readEnum(DyeColor.values());
return new Layer(pattern, color);
}
@@ -104,8 +104,9 @@ public class ItemAdventurePredicate {
public static BlockPredicate read(PacketWrapper<?> wrapper) {
MappedEntitySet<StateType.Mapped> blocks = wrapper.readOptional(
ew -> MappedEntitySet.read(ew, StateTypes::getMappedById));
List<PropertyMatcher> properties = wrapper.readList(PropertyMatcher::read);
NBTCompound nbt = wrapper.readNBT();
List<PropertyMatcher> properties = wrapper.readOptional(
ew -> wrapper.readList(PropertyMatcher::read));
NBTCompound nbt = wrapper.readOptional(PacketWrapper::readNBT);
return new BlockPredicate(blocks, properties, nbt);
}
@@ -18,10 +18,15 @@
package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attribute;
import com.github.retrooper.packetevents.protocol.attribute.AttributeOperation;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes.PropertyModifier;
import net.kyori.adventure.util.Index;
import org.jetbrains.annotations.ApiStatus;
import java.util.Collections;
import java.util.List;
@@ -168,11 +173,16 @@ public class ItemAttributeModifiers {
public static class Modifier {
private UUID id;
private String name;
private UUID id; // removed in 1.21
private String name; // ResourceLocation since 1.21
private double value;
private AttributeOperation operation;
public Modifier(ResourceLocation name, double value, AttributeOperation operation) {
this(PropertyModifier.generateSemiUniqueId(name), name.toString(), value, operation);
}
@ApiStatus.Obsolete
public Modifier(UUID id, String name, double value, AttributeOperation operation) {
this.id = id;
this.name = name;
@@ -181,32 +191,56 @@ public class ItemAttributeModifiers {
}
public static Modifier read(PacketWrapper<?> wrapper) {
UUID id = wrapper.readUUID();
String name = wrapper.readString();
UUID id;
String name;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21)) {
ResourceLocation nameKey = wrapper.readIdentifier();
name = nameKey.toString();
id = PropertyModifier.generateSemiUniqueId(nameKey);
} else {
id = wrapper.readUUID();
name = wrapper.readString();
}
double value = wrapper.readDouble();
AttributeOperation operation = wrapper.readEnum(AttributeOperation.values());
return new Modifier(id, name, value, operation);
}
public static void write(PacketWrapper<?> wrapper, Modifier modifier) {
wrapper.writeUUID(modifier.id);
wrapper.writeString(modifier.name);
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_21)) {
wrapper.writeIdentifier(new ResourceLocation(modifier.name));
} else {
wrapper.writeUUID(modifier.id);
wrapper.writeString(modifier.name);
}
wrapper.writeDouble(modifier.value);
wrapper.writeEnum(modifier.operation);
}
public ResourceLocation getNameKey() {
return new ResourceLocation(this.name);
}
public void setNameKey(ResourceLocation nameKey) {
this.name = nameKey.toString();
}
@ApiStatus.Obsolete
public UUID getId() {
return this.id;
}
@ApiStatus.Obsolete
public void setId(UUID id) {
this.id = id;
}
@ApiStatus.Obsolete
public String getName() {
return this.name;
}
@ApiStatus.Obsolete
public void setName(String name) {
this.name = name;
}
@@ -250,15 +284,29 @@ public class ItemAttributeModifiers {
}
public enum EquipmentSlotGroup {
ANY,
MAINHAND,
OFFHAND,
HAND,
FEET,
LEGS,
CHEST,
HEAD,
ARMOR,
BODY,
ANY("any"),
MAINHAND("mainhand"),
OFFHAND("offhand"),
HAND("hand"),
FEET("feet"),
LEGS("legs"),
CHEST("chest"),
HEAD("head"),
ARMOR("armor"),
BODY("body");
public static final Index<String, EquipmentSlotGroup> ID_INDEX = Index.create(
EquipmentSlotGroup.class, EquipmentSlotGroup::getId);
private final String id;
EquipmentSlotGroup(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
}
@@ -47,9 +47,8 @@ public class ItemEnchantments implements Iterable<Map.Entry<EnchantmentType, Int
}
public static ItemEnchantments read(PacketWrapper<?> wrapper) {
ClientVersion version = wrapper.getServerVersion().toClientVersion();
Map<EnchantmentType, Integer> enchantments = wrapper.readMap(
ew -> EnchantmentTypes.getById(version, ew.readVarInt()),
ew -> wrapper.readMappedEntity(EnchantmentTypes.getRegistry()),
PacketWrapper::readVarInt
);
boolean showInTooltip = wrapper.readBoolean();
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.component.builtin.item;
import com.github.retrooper.packetevents.protocol.item.jukebox.IJukeboxSong;
import com.github.retrooper.packetevents.protocol.item.jukebox.JukeboxSong;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@@ -27,7 +28,7 @@ import java.util.Objects;
public class ItemJukeboxPlayable {
private @Nullable JukeboxSong song;
private @Nullable IJukeboxSong song;
private @Nullable ResourceLocation songKey;
private boolean showInTooltip;
@@ -35,6 +36,14 @@ public class ItemJukeboxPlayable {
@Nullable JukeboxSong song,
@Nullable ResourceLocation songKey,
boolean showInTooltip
) {
this((IJukeboxSong) song, songKey, showInTooltip);
}
public ItemJukeboxPlayable(
@Nullable IJukeboxSong song,
@Nullable ResourceLocation songKey,
boolean showInTooltip
) {
if ((song == null) == (songKey == null)) {
throw new IllegalStateException("Illegal state of both song and "
@@ -47,10 +56,10 @@ public class ItemJukeboxPlayable {
}
public static ItemJukeboxPlayable read(PacketWrapper<?> wrapper) {
JukeboxSong song;
IJukeboxSong song;
ResourceLocation songKey;
if (wrapper.readBoolean()) {
song = JukeboxSong.read(wrapper);
song = IJukeboxSong.read(wrapper);
songKey = null;
} else {
song = null;
@@ -63,7 +72,7 @@ public class ItemJukeboxPlayable {
public static void write(PacketWrapper<?> wrapper, ItemJukeboxPlayable jukeboxPlayable) {
if (jukeboxPlayable.song != null) {
wrapper.writeBoolean(true);
JukeboxSong.write(wrapper, jukeboxPlayable.song);
IJukeboxSong.write(wrapper, jukeboxPlayable.song);
} else {
assert jukeboxPlayable.songKey != null;
wrapper.writeBoolean(false);
@@ -72,10 +81,26 @@ public class ItemJukeboxPlayable {
wrapper.writeBoolean(jukeboxPlayable.showInTooltip);
}
public @Nullable JukeboxSong getSong() {
public @Nullable IJukeboxSong getJukeboxSong() {
return this.song;
}
public void setJukeboxSong(@Nullable JukeboxSong song) {
this.song = song;
this.songKey = null;
}
@Deprecated
public @Nullable JukeboxSong getSong() {
if (this.song == null) {
return null;
} else if (this.song instanceof JukeboxSong) {
return (JukeboxSong) this.song;
}
return (JukeboxSong) this.song.copy(null);
}
@Deprecated
public void setSong(JukeboxSong song) {
this.song = song;
this.songKey = null;
@@ -0,0 +1,104 @@
/*
* 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.entity.wolfvariant;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.world.biome.Biome;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticWolfVariant extends AbstractMappedEntity implements WolfVariant {
private final ResourceLocation wildTexture;
private final ResourceLocation tameTexture;
private final ResourceLocation angryTexture;
private final MappedEntitySet<Biome> biomes;
public StaticWolfVariant(
ResourceLocation wildTexture,
ResourceLocation tameTexture,
ResourceLocation angryTexture,
MappedEntitySet<Biome> biomes
) {
this(null, wildTexture, tameTexture, angryTexture, biomes);
}
public StaticWolfVariant(
@Nullable TypesBuilderData data,
ResourceLocation wildTexture,
ResourceLocation tameTexture,
ResourceLocation angryTexture,
MappedEntitySet<Biome> biomes
) {
super(data);
this.wildTexture = wildTexture;
this.tameTexture = tameTexture;
this.angryTexture = angryTexture;
this.biomes = biomes;
}
@Override
public WolfVariant copy(@Nullable TypesBuilderData newData) {
return new StaticWolfVariant(newData, this.wildTexture, this.tameTexture, this.angryTexture, this.biomes);
}
@Override
public ResourceLocation getWildTexture() {
return this.wildTexture;
}
@Override
public ResourceLocation getTameTexture() {
return this.tameTexture;
}
@Override
public ResourceLocation getAngryTexture() {
return this.angryTexture;
}
@Override
public MappedEntitySet<Biome> getBiomes() {
return this.biomes;
}
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticWolfVariant)) return false;
if (!super.equals(obj)) return false;
StaticWolfVariant that = (StaticWolfVariant) obj;
if (!this.wildTexture.equals(that.wildTexture)) return false;
if (!this.tameTexture.equals(that.tameTexture)) return false;
if (!this.angryTexture.equals(that.angryTexture)) return false;
return this.biomes.equals(that.biomes);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.wildTexture, this.tameTexture, this.angryTexture, this.biomes);
}
@Override
public String toString() {
return "StaticWolfVariant{wildTexture=" + this.wildTexture + ", tameTexture=" + this.tameTexture + ", angryTexture=" + this.angryTexture + ", biomes=" + this.biomes + '}';
}
}
@@ -1,14 +1,62 @@
/*
* 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.entity.wolfvariant;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
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.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.biome.Biome;
import com.github.retrooper.packetevents.protocol.world.biome.Biomes;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public interface WolfVariant extends MappedEntity, CopyableEntity<WolfVariant> {
//TODO Finish Wolf-Variant class
public class WolfVariant {
private ResourceLocation wildId, tameId, angryId;
ResourceLocation getWildTexture();
public WolfVariant(ResourceLocation wildId, ResourceLocation tameId, ResourceLocation angryId) {
ResourceLocation getTameTexture();
ResourceLocation getAngryTexture();
MappedEntitySet<Biome> getBiomes();
static WolfVariant decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
ResourceLocation wildTexture = new ResourceLocation(compound.getStringTagValueOrThrow("wild_texture"));
ResourceLocation tameTexture = new ResourceLocation(compound.getStringTagValueOrThrow("tame_texture"));
ResourceLocation angryTexture = new ResourceLocation(compound.getStringTagValueOrThrow("angry_texture"));
MappedEntitySet<Biome> biomes = MappedEntitySet.decode(
compound.getTagOrThrow("biomes"), version, Biomes.getRegistry());
return new StaticWolfVariant(data, wildTexture, tameTexture, angryTexture, biomes);
}
static NBT encode(WolfVariant variant, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("wild_texture", new NBTString(variant.getWildTexture().toString()));
compound.setTag("tame_texture", new NBTString(variant.getTameTexture().toString()));
compound.setTag("angry_texture", new NBTString(variant.getAngryTexture().toString()));
compound.setTag("biomes", MappedEntitySet.encode(variant.getBiomes(), version));
return compound;
}
}
@@ -0,0 +1,88 @@
/*
* 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.entity.wolfvariant;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.world.biome.Biome;
import com.github.retrooper.packetevents.protocol.world.biome.Biomes;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.util.Arrays;
public final class WolfVariants {
private static final VersionedRegistry<WolfVariant> REGISTRY = new VersionedRegistry<>(
"wolf_variant", "entity/wolf_variant_mappings");
private WolfVariants() {
}
@ApiStatus.Internal
public static WolfVariant define(String key, MappedEntitySet<Biome> biomes) {
return define(key, "wolf_" + key, biomes);
}
@ApiStatus.Internal
public static WolfVariant define(String key, String assetId, MappedEntitySet<Biome> biomes) {
return define(key, ResourceLocation.minecraft("entity/wolf/" + assetId),
ResourceLocation.minecraft("entity/wolf/" + assetId + "_tame"),
ResourceLocation.minecraft("entity/wolf/" + assetId + "_angry"), biomes);
}
@ApiStatus.Internal
public static WolfVariant define(
String key,
ResourceLocation wildTexture,
ResourceLocation tameTexture,
ResourceLocation angryTexture,
MappedEntitySet<Biome> biomes
) {
return REGISTRY.define(key, data -> new StaticWolfVariant(
data, wildTexture, tameTexture, angryTexture, biomes));
}
public static VersionedRegistry<WolfVariant> getRegistry() {
return REGISTRY;
}
public static final WolfVariant PALE = define("pale", "wolf",
new MappedEntitySet<>(Arrays.asList(Biomes.TAIGA)));
public static final WolfVariant SPOTTED = define("spotted",
new MappedEntitySet<>(ResourceLocation.minecraft("is_savanna")));
public static final WolfVariant SNOWY = define("snowy",
new MappedEntitySet<>(Arrays.asList(Biomes.GROVE)));
public static final WolfVariant BLACK = define("black",
new MappedEntitySet<>(Arrays.asList(Biomes.OLD_GROWTH_PINE_TAIGA)));
public static final WolfVariant ASHEN = define("ashen",
new MappedEntitySet<>(Arrays.asList(Biomes.SNOWY_TAIGA)));
public static final WolfVariant RUSTY = define("rusty",
new MappedEntitySet<>(ResourceLocation.minecraft("is_jungle")));
public static final WolfVariant WOODS = define("woods",
new MappedEntitySet<>(Arrays.asList(Biomes.FOREST)));
public static final WolfVariant CHESTNUT = define("chestnut",
new MappedEntitySet<>(Arrays.asList(Biomes.OLD_GROWTH_SPRUCE_TAIGA)));
public static final WolfVariant STRIPED = define("striped",
new MappedEntitySet<>(ResourceLocation.minecraft("is_badlands")));
static {
REGISTRY.unloadMappings();
}
}
@@ -32,10 +32,12 @@ 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;
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 com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
@@ -82,6 +84,54 @@ public class ItemStack {
updateCachedEmptyStatus();
}
public static ItemStack decode(NBT nbt, ClientVersion version) {
if (nbt instanceof NBTString) {
ResourceLocation itemName = new ResourceLocation(((NBTString) nbt).getValue());
return ItemStack.builder().type(ItemTypes.getByName(itemName.toString())).build();
}
NBTCompound compound = (NBTCompound) nbt;
ItemStack.Builder builder = ItemStack.builder();
ResourceLocation itemName = Optional.ofNullable(compound.getStringTagValueOrNull("id")).map(Optional::of)
.orElseGet(() -> Optional.ofNullable(compound.getStringTagValueOrNull("item")))
.map(ResourceLocation::new).orElseThrow(() -> new IllegalArgumentException(
"No item type specified: " + compound.getTags().keySet()));
builder.type(ItemTypes.getByName(itemName.toString()));
builder.nbt(compound.getCompoundTagOrNull("tag"));
Optional.ofNullable(compound.getNumberTagOrNull("Count")).map(Optional::of)
.orElseGet(() -> Optional.ofNullable(compound.getNumberTagOrNull("count")))
.map(NBTNumber::getAsInt).ifPresent(builder::amount);
// TODO components
return builder.build();
}
public static NBT encodeForParticle(ItemStack itemStack, ClientVersion version) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_5)) {
boolean simple = itemStack.isEmpty()
|| itemStack.components == null
|| itemStack.components.getPatches().isEmpty();
if (simple) {
return new NBTString(itemStack.type.getName().toString());
}
}
NBTCompound compound = new NBTCompound();
compound.setTag("id", new NBTString(itemStack.type.getName().toString()));
if (version.isOlderThan(ClientVersion.V_1_20_5)) {
compound.setTag("Count", new NBTInt(itemStack.amount));
if (itemStack.nbt != null) {
compound.setTag("tag", itemStack.nbt);
}
}
// TODO components
return compound;
}
public int getMaxStackSize() {
return this.getComponentOr(MAX_STACK_SIZE,
// fallback to legacy specified max stack size
@@ -18,11 +18,18 @@
package com.github.retrooper.packetevents.protocol.item.banner;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
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.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
public interface BannerPattern extends MappedEntity {
public interface BannerPattern extends MappedEntity, CopyableEntity<BannerPattern> {
ResourceLocation getAssetId();
@@ -38,4 +45,18 @@ public interface BannerPattern extends MappedEntity {
wrapper.writeIdentifier(pattern.getAssetId());
wrapper.writeString(pattern.getTranslationKey());
}
static BannerPattern decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
ResourceLocation assetId = new ResourceLocation(compound.getStringTagValueOrThrow("asset_id"));
String translationKey = compound.getStringTagValueOrThrow("translation_key");
return new StaticBannerPattern(data, assetId, translationKey);
}
static NBT encode(BannerPattern bannerPattern, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("asset_id", new NBTString(bannerPattern.getAssetId().toString()));
compound.setTag("translation_key", new NBTString(bannerPattern.getTranslationKey()));
return compound;
}
}
@@ -20,71 +20,42 @@ package com.github.retrooper.packetevents.protocol.item.banner;
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 com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class BannerPatterns {
public final class BannerPatterns {
private static final Map<String, BannerPattern> PATTERN_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, BannerPattern>> PATTERN_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_banner_pattern_mappings");
private static final VersionedRegistry<BannerPattern> REGISTRY = new VersionedRegistry<>(
"banner_pattern", "item/item_banner_pattern_mappings");
private BannerPatterns() {
}
@ApiStatus.Internal
public static BannerPattern define(String key) {
ResourceLocation assetId = ResourceLocation.minecraft(key);
String translationKey = "block.minecraft.banner." + key;
return define(key, assetId, translationKey);
}
@ApiStatus.Internal
public static BannerPattern define(String key, ResourceLocation assetId, String translationKey) {
TypesBuilderData data = TYPES_BUILDER.define(key);
BannerPattern pattern = new BannerPattern() {
@Override
public ResourceLocation getAssetId() {
return assetId;
}
@Override
public String getTranslationKey() {
return translationKey;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BannerPattern) {
return getName().equals(((BannerPattern) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PATTERN_TYPE_MAP, PATTERN_TYPE_ID_MAP, pattern);
return pattern;
return REGISTRY.define(key, data ->
new StaticBannerPattern(data, assetId, translationKey));
}
public static VersionedRegistry<BannerPattern> getRegistry() {
return REGISTRY;
}
// with key
public static BannerPattern getByName(String name) {
return PATTERN_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static BannerPattern getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, BannerPattern> idMap = PATTERN_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
public static final BannerPattern SQUARE_BOTTOM_LEFT = define("square_bottom_left");
@@ -135,13 +106,14 @@ public class BannerPatterns {
/**
* Returns an immutable view of the banner patterns.
*
* @return Banner Patterns
*/
public static Collection<BannerPattern> values() {
return Collections.unmodifiableCollection(PATTERN_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -18,21 +18,33 @@
package com.github.retrooper.packetevents.protocol.item.banner;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticBannerPattern implements BannerPattern {
public class StaticBannerPattern extends AbstractMappedEntity implements BannerPattern {
private final ResourceLocation assetId;
private final String translationKey;
public StaticBannerPattern(ResourceLocation assetId, String translationKey) {
this(null, assetId, translationKey);
}
public StaticBannerPattern(@Nullable TypesBuilderData data, ResourceLocation assetId, String translationKey) {
super(data);
this.assetId = assetId;
this.translationKey = translationKey;
}
@Override
public BannerPattern copy(@Nullable TypesBuilderData newData) {
return new StaticBannerPattern(newData, this.assetId, this.translationKey);
}
@Override
public ResourceLocation getAssetId() {
return this.assetId;
@@ -43,32 +55,21 @@ public class StaticBannerPattern implements BannerPattern {
return this.translationKey;
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
}
@Override
public boolean equals(Object obj) {
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticBannerPattern)) return false;
if (!super.equals(obj)) return false;
StaticBannerPattern that = (StaticBannerPattern) obj;
if (!this.assetId.equals(that.assetId)) return false;
return this.translationKey.equals(that.translationKey);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.assetId, this.translationKey);
}
@Override
public int hashCode() {
return Objects.hash(this.assetId, this.translationKey);
public String toString() {
return "StaticBannerPattern{assetId=" + this.assetId + ", translationKey='" + this.translationKey + '\'' + '}';
}
}
@@ -0,0 +1,78 @@
/*
* 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.enchantment;
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.player.ClientVersion;
import java.util.Objects;
public final class EnchantmentCost {
private final int base;
private final int perLevelAboveFirst;
public EnchantmentCost(int base, int perLevelAboveFirst) {
this.base = base;
this.perLevelAboveFirst = perLevelAboveFirst;
}
public static EnchantmentCost decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
int base = compound.getNumberTagOrThrow("base").getAsInt();
int perLevelAboveFirst = compound.getNumberTagOrThrow("per_level_above_first").getAsInt();
return new EnchantmentCost(base, perLevelAboveFirst);
}
public static NBT encode(EnchantmentCost cost, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("base", new NBTInt(cost.base));
compound.setTag("per_level_above_first", new NBTInt(cost.perLevelAboveFirst));
return compound;
}
public int getBase() {
return this.base;
}
public int getPerLevelAboveFirst() {
return this.perLevelAboveFirst;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof EnchantmentCost)) return false;
EnchantmentCost that = (EnchantmentCost) obj;
if (this.base != that.base) return false;
return this.perLevelAboveFirst == that.perLevelAboveFirst;
}
@Override
public int hashCode() {
return Objects.hash(this.base, this.perLevelAboveFirst);
}
@Override
public String toString() {
return "EnchantmentCost{base=" + this.base + ", perLevelAboveFirst=" + this.perLevelAboveFirst + '}';
}
}
@@ -0,0 +1,175 @@
/*
* 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.enchantment;
import com.github.retrooper.packetevents.protocol.component.builtin.item.ItemAttributeModifiers.EquipmentSlotGroup;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
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;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
public final class EnchantmentDefinition {
private final MappedEntitySet<ItemType> supportedItems;
private final Optional<MappedEntitySet<ItemType>> primaryItems;
private final int weight;
private final int maxLevel;
private final EnchantmentCost minCost;
private final EnchantmentCost maxCost;
private final int anvilCost;
private final List<EquipmentSlotGroup> slots;
public EnchantmentDefinition(
MappedEntitySet<ItemType> supportedItems,
Optional<MappedEntitySet<ItemType>> primaryItems,
int weight, int maxLevel,
EnchantmentCost minCost, EnchantmentCost maxCost,
int anvilCost, List<EquipmentSlotGroup> slots
) {
this.supportedItems = supportedItems;
this.primaryItems = primaryItems;
this.weight = weight;
this.maxLevel = maxLevel;
this.minCost = minCost;
this.maxCost = maxCost;
this.anvilCost = anvilCost;
this.slots = slots;
}
public static EnchantmentDefinition decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
MappedEntitySet<ItemType> supportedItems = MappedEntitySet.decode(
compound.getTagOrThrow("supported_items"), version, ItemTypes.getRegistry());
Optional<MappedEntitySet<ItemType>> primaryItems = Optional.ofNullable(compound.getTagOrNull("primary_items"))
.map(items -> MappedEntitySet.decode(items, version, ItemTypes.getRegistry()));
int weight = compound.getNumberTagOrThrow("weight").getAsInt();
int maxLevel = compound.getNumberTagOrThrow("max_level").getAsInt();
EnchantmentCost minCost = EnchantmentCost.decode(compound.getTagOrThrow("min_cost"), version);
EnchantmentCost maxCost = EnchantmentCost.decode(compound.getTagOrThrow("max_cost"), version);
int anvilCost = compound.getNumberTagOrThrow("anvil_cost").getAsInt();
NBT slotsTag = compound.getTagOrThrow("slots");
List<EquipmentSlotGroup> slots;
if (slotsTag instanceof NBTList<?>) {
NBTList<?> slotsTagList = (NBTList<?>) slotsTag;
slots = new ArrayList<>(slotsTagList.size());
for (NBT tag : slotsTagList.getTags()) {
String slotGroupId = ((NBTString) tag).getValue();
slots.add(indexValueOrThrow(EquipmentSlotGroup.ID_INDEX, slotGroupId));
}
} else {
String slotGroupId = ((NBTString) slotsTag).getValue();
EquipmentSlotGroup slotGroup = indexValueOrThrow(EquipmentSlotGroup.ID_INDEX, slotGroupId);
slots = Collections.singletonList(slotGroup);
}
return new EnchantmentDefinition(supportedItems, primaryItems, weight,
maxLevel, minCost, maxCost, anvilCost, slots);
}
public static NBT encode(EnchantmentDefinition definition, ClientVersion version) {
NBTList<NBTString> slotsTag = NBTList.createStringList();
for (EquipmentSlotGroup slot : definition.slots) {
slotsTag.addTag(new NBTString(slot.getId()));
}
NBTCompound compound = new NBTCompound();
compound.setTag("supported_items", MappedEntitySet.encode(
definition.supportedItems, version));
definition.primaryItems.ifPresent(set -> compound.setTag(
"primary_items", MappedEntitySet.encode(set, version)));
compound.setTag("weight", new NBTInt(definition.weight));
compound.setTag("max_level", new NBTInt(definition.maxLevel));
compound.setTag("min_cost", EnchantmentCost.encode(definition.minCost, version));
compound.setTag("max_cost", EnchantmentCost.encode(definition.maxCost, version));
compound.setTag("anvil_cost", new NBTInt(definition.anvilCost));
compound.setTag("slots", slotsTag);
return compound;
}
public MappedEntitySet<ItemType> getSupportedItems() {
return this.supportedItems;
}
public Optional<MappedEntitySet<ItemType>> getPrimaryItems() {
return this.primaryItems;
}
public int getWeight() {
return this.weight;
}
public int getMaxLevel() {
return this.maxLevel;
}
public EnchantmentCost getMinCost() {
return this.minCost;
}
public EnchantmentCost getMaxCost() {
return this.maxCost;
}
public int getAnvilCost() {
return this.anvilCost;
}
public List<EquipmentSlotGroup> getSlots() {
return this.slots;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof EnchantmentDefinition)) return false;
EnchantmentDefinition that = (EnchantmentDefinition) obj;
if (this.weight != that.weight) return false;
if (this.maxLevel != that.maxLevel) return false;
if (this.anvilCost != that.anvilCost) return false;
if (!this.supportedItems.equals(that.supportedItems)) return false;
if (!this.primaryItems.equals(that.primaryItems)) return false;
if (!this.minCost.equals(that.minCost)) return false;
if (!this.maxCost.equals(that.maxCost)) return false;
return this.slots.equals(that.slots);
}
@Override
public int hashCode() {
return Objects.hash(this.supportedItems, this.primaryItems, this.weight, this.maxLevel, this.minCost, this.maxCost, this.anvilCost, this.slots);
}
@Override
public String toString() {
return "EnchantmentDefinition{supportedItems=" + this.supportedItems + ", primaryItems=" + this.primaryItems + ", weight=" + this.weight + ", maxLevel=" + this.maxLevel + ", minCost=" + this.minCost + ", maxCost=" + this.maxCost + ", anvilCost=" + this.anvilCost + ", slots=" + this.slots + '}';
}
}
@@ -18,7 +18,56 @@
package com.github.retrooper.packetevents.protocol.item.enchantment.type;
import com.github.retrooper.packetevents.protocol.component.EnchantEffectComponentTypes;
import com.github.retrooper.packetevents.protocol.component.IComponentMap;
import com.github.retrooper.packetevents.protocol.component.StaticComponentMap;
import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDefinition;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public interface EnchantmentType extends MappedEntity {
import java.util.Optional;
public interface EnchantmentType extends MappedEntity, CopyableEntity<EnchantmentType> {
Component getDescription();
EnchantmentDefinition getDefinition();
MappedEntitySet<EnchantmentType> getExclusiveSet();
StaticComponentMap getEffects();
static EnchantmentType decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
Component description = AdventureSerializer.fromNbt(compound.getTagOrThrow("description"));
EnchantmentDefinition definition = EnchantmentDefinition.decode(compound, version);
MappedEntitySet<EnchantmentType> exclusiveSet = Optional.ofNullable(compound.getTagOrNull("exclusive_set"))
.map(tag -> MappedEntitySet.decode(tag, version, EnchantmentTypes.getRegistry()))
.orElseGet(MappedEntitySet::createEmpty);
StaticComponentMap effects = Optional.ofNullable(compound.getTagOrNull("effects"))
.map(tag -> IComponentMap.decode(tag, version,
EnchantEffectComponentTypes.getRegistry()))
.orElse(StaticComponentMap.EMPTY);
return new StaticEnchantmentType(data, description, definition, exclusiveSet, effects);
}
static NBT encode(EnchantmentType type, ClientVersion version) {
NBTCompound compound = (NBTCompound) EnchantmentDefinition.encode(type.getDefinition(), version);
compound.setTag("description", AdventureSerializer.toNbt(type.getDescription()));
if (!type.getExclusiveSet().isEmpty()) {
compound.setTag("exclusive_set", MappedEntitySet.encode(type.getExclusiveSet(), version));
}
if (!type.getEffects().isEmpty()) {
compound.setTag("effects", IComponentMap.encode(type.getEffects(), version));
}
return compound;
}
}
@@ -18,17 +18,21 @@
package com.github.retrooper.packetevents.protocol.item.enchantment.type;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
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 com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class EnchantmentTypes {
public final class EnchantmentTypes {
private static final Map<String, String> STRING_UPDATER = new HashMap<>();
@@ -37,47 +41,50 @@ public class EnchantmentTypes {
STRING_UPDATER.put("minecraft:sweeping", "minecraft:sweeping_edge");
}
private static final Map<String, EnchantmentType> ENCHANTMENT_TYPE_MAPPINGS = new HashMap<>();
private static final Map<Byte, Map<Integer, EnchantmentType>> ENCHANTMENT_TYPE_ID_MAPPINGS = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("enchantment/enchantment_type_mappings");
// load data from file, enchantment types are too complex to define in code here
private static final Map<ResourceLocation, NBTCompound> ENCHANTMENT_DATA;
public static EnchantmentType define(String key) {
TypesBuilderData data = TYPES_BUILDER.define(key);
EnchantmentType enchantmentType = new EnchantmentType() {
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof EnchantmentType) {
return getName() == ((EnchantmentType) obj).getName();
static {
ENCHANTMENT_DATA = new HashMap<>();
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/enchantment/enchantment_type_data")) {
while (dataTag.hasNext()) {
Map.Entry<String, NBT> entry = dataTag.next();
if (entry.getKey().equals("version")) {
continue; // skip version field
}
return false;
ResourceLocation enchantKey = new ResourceLocation(entry.getKey());
ENCHANTMENT_DATA.put(enchantKey, ((SequentialNBTReader.Compound) entry.getValue()).readFully());
}
};
MappingHelper.registerMapping(TYPES_BUILDER, ENCHANTMENT_TYPE_MAPPINGS, ENCHANTMENT_TYPE_ID_MAPPINGS, enchantmentType);
return enchantmentType;
} catch (IOException exception) {
throw new RuntimeException("Error while reading enchantment type data", exception);
}
}
@Nullable
public static EnchantmentType getByName(String name) {
private static final VersionedRegistry<EnchantmentType> REGISTRY = new VersionedRegistry<>(
"enchantment", "enchantment/enchantment_type_mappings");
@ApiStatus.Internal
public static EnchantmentType define(String key) {
return REGISTRY.define(key, data -> {
NBTCompound dataTag = ENCHANTMENT_DATA.get(data.getName());
if (dataTag == null) {
throw new IllegalArgumentException("Can't define enchantment " + data.getName() + ", no data found");
}
return EnchantmentType.decode(dataTag, ClientVersion.getLatest(), data);
});
}
public static VersionedRegistry<EnchantmentType> getRegistry() {
return REGISTRY;
}
public static @Nullable EnchantmentType getByName(String name) {
String fixedName = STRING_UPDATER.getOrDefault(name, name);
return ENCHANTMENT_TYPE_MAPPINGS.get(fixedName);
return REGISTRY.getByName(fixedName);
}
@Nullable
public static EnchantmentType getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, EnchantmentType> typeIdMap = ENCHANTMENT_TYPE_ID_MAPPINGS.get((byte) index);
return typeIdMap.get(id);
public static @Nullable EnchantmentType getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static final EnchantmentType ALL_DAMAGE_PROTECTION = define("protection");
@@ -126,6 +133,7 @@ public class EnchantmentTypes {
public static final EnchantmentType WIND_BURST = define("wind_burst");
static {
TYPES_BUILDER.unloadFileMappings();
ENCHANTMENT_DATA.clear();
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,106 @@
/*
* 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.enchantment.type;
import com.github.retrooper.packetevents.protocol.component.StaticComponentMap;
import com.github.retrooper.packetevents.protocol.item.enchantment.EnchantmentDefinition;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticEnchantmentType extends AbstractMappedEntity implements EnchantmentType {
private final Component description;
private final EnchantmentDefinition definition;
private final MappedEntitySet<EnchantmentType> exclusiveSet;
private final StaticComponentMap effects;
public StaticEnchantmentType(
Component description,
EnchantmentDefinition definition,
MappedEntitySet<EnchantmentType> exclusiveSet,
StaticComponentMap effects
) {
this(null, description, definition, exclusiveSet, effects);
}
public StaticEnchantmentType(
@Nullable TypesBuilderData data,
Component description,
EnchantmentDefinition definition,
MappedEntitySet<EnchantmentType> exclusiveSet,
StaticComponentMap effects
) {
super(data);
this.description = description;
this.definition = definition;
this.exclusiveSet = exclusiveSet;
this.effects = effects;
}
@Override
public EnchantmentType copy(@Nullable TypesBuilderData newData) {
return new StaticEnchantmentType(newData, this.description,
this.definition, this.exclusiveSet, this.effects);
}
@Override
public Component getDescription() {
return this.description;
}
@Override
public EnchantmentDefinition getDefinition() {
return this.definition;
}
@Override
public MappedEntitySet<EnchantmentType> getExclusiveSet() {
return this.exclusiveSet;
}
@Override
public StaticComponentMap getEffects() {
return this.effects;
}
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticEnchantmentType)) return false;
if (!super.equals(obj)) return false;
StaticEnchantmentType that = (StaticEnchantmentType) obj;
if (!this.description.equals(that.description)) return false;
if (!this.definition.equals(that.definition)) return false;
if (!this.exclusiveSet.equals(that.exclusiveSet)) return false;
return this.effects.equals(that.effects);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.description, this.definition, this.exclusiveSet, this.effects);
}
@Override
public String toString() {
return "StaticEnchantmentType{description=" + this.description + ", definition=" + this.definition + ", exclusiveSet=" + this.exclusiveSet + ", effects=" + this.effects + "}";
}
}
@@ -0,0 +1,79 @@
/*
* 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.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public interface IJukeboxSong extends MappedEntity, CopyableEntity<IJukeboxSong> {
Sound getSound();
Component getDescription();
float getLengthInSeconds();
int getComparatorOutput();
static IJukeboxSong decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound_event"), version);
Component description = AdventureSerializer.fromNbt(compound.getTagOrThrow("description"));
float length = compound.getNumberTagOrThrow("length_in_seconds").getAsFloat();
int comparator_output = compound.getNumberTagOrThrow("comparator_output").getAsInt();
return new JukeboxSong(data, sound, description, length, comparator_output);
}
static NBT encode(IJukeboxSong jukeboxSong, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound_event", Sound.encode(jukeboxSong.getSound(), version));
compound.setTag("description", AdventureSerializer.toNbt(jukeboxSong.getDescription()));
compound.setTag("length_in_seconds", new NBTFloat(jukeboxSong.getLengthInSeconds()));
compound.setTag("comparator_output", new NBTInt(jukeboxSong.getComparatorOutput()));
return compound;
}
static IJukeboxSong read(PacketWrapper<?> wrapper) {
Sound sound = Sound.read(wrapper);
Component description = wrapper.readComponent();
float lengthInSeconds = wrapper.readFloat();
int comparatorOutput = wrapper.readVarInt();
return new JukeboxSong(null, sound, description, lengthInSeconds, comparatorOutput);
}
static void write(PacketWrapper<?> wrapper, IJukeboxSong song) {
Sound.write(wrapper, song.getSound());
wrapper.writeComponent(song.getDescription());
wrapper.writeFloat(song.getLengthInSeconds());
wrapper.writeVarInt(song.getComparatorOutput());
}
}
@@ -18,77 +18,93 @@
package com.github.retrooper.packetevents.protocol.item.jukebox;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class JukeboxSong {
public class JukeboxSong extends AbstractMappedEntity implements IJukeboxSong {
private Sound sound;
private Component description;
private float lengthInSeconds;
private int comparatorOutput;
public JukeboxSong(Sound sound, Component description, float lengthInSeconds, int comparatorOutput) {
this(null, sound, description, lengthInSeconds, comparatorOutput);
}
public JukeboxSong(@Nullable TypesBuilderData data, Sound sound, Component description, float lengthInSeconds,
int comparatorOutput) {
super(data);
this.sound = sound;
this.description = description;
this.lengthInSeconds = lengthInSeconds;
this.comparatorOutput = comparatorOutput;
}
@Deprecated
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);
return (JukeboxSong) IJukeboxSong.read(wrapper);
}
@Deprecated
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);
IJukeboxSong.write(wrapper, song);
}
@Override
public IJukeboxSong copy(@Nullable TypesBuilderData newData) {
return new JukeboxSong(newData, sound, description, lengthInSeconds, comparatorOutput);
}
@Override
public Sound getSound() {
return this.sound;
}
@Deprecated
public void setSound(Sound sound) {
this.sound = sound;
}
@Override
public Component getDescription() {
return this.description;
}
@Deprecated
public void setDescription(Component description) {
this.description = description;
}
@Override
public float getLengthInSeconds() {
return this.lengthInSeconds;
}
@Deprecated
public void setLengthInSeconds(float lengthInSeconds) {
this.lengthInSeconds = lengthInSeconds;
}
@Override
public int getComparatorOutput() {
return this.comparatorOutput;
}
@Deprecated
public void setComparatorOutput(int comparatorOutput) {
this.comparatorOutput = comparatorOutput;
}
@Override
public boolean equals(Object obj) {
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof JukeboxSong)) return false;
if (!super.equals(obj)) return false;
JukeboxSong that = (JukeboxSong) obj;
if (Float.compare(that.lengthInSeconds, this.lengthInSeconds) != 0) return false;
if (this.comparatorOutput != that.comparatorOutput) return false;
@@ -96,8 +112,12 @@ public class JukeboxSong {
return this.description.equals(that.description);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.sound, this.description, this.lengthInSeconds, this.comparatorOutput);
}
@Override
public int hashCode() {
return Objects.hash(this.sound, this.description, this.lengthInSeconds, this.comparatorOutput);
public String toString() {
return "JukeboxSong{sound=" + this.sound + ", description=" + this.description + ", lengthInSeconds=" + this.lengthInSeconds + ", comparatorOutput=" + this.comparatorOutput + '}';
}
}
@@ -0,0 +1,96 @@
/*
* 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.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.protocol.sound.Sounds;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class JukeboxSongs {
private static final VersionedRegistry<IJukeboxSong> REGISTRY = new VersionedRegistry<>("jukebox_song",
"item/item_jukebox_song_mappings");
private JukeboxSongs() {
}
private static String makeDescriptionId(String var0, @Nullable ResourceLocation var1) {
return var1 == null ? var0 + ".unregistered_sadface" :
var0 + "." + var1.getNamespace() + "." + var1.getKey().replace('/', '.');
}
@ApiStatus.Internal
public static IJukeboxSong define(String key, Sound sound, float lengthInSeconds, int comparatorOutput) {
return REGISTRY.define(key, data -> new JukeboxSong(data, sound,
Component.translatable(makeDescriptionId("jukebox_song", data.getName())), lengthInSeconds,
comparatorOutput));
}
public static VersionedRegistry<IJukeboxSong> getRegistry() {
return REGISTRY;
}
public static IJukeboxSong getByName(String name) {
return REGISTRY.getByName(name);
}
public static IJukeboxSong getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static final IJukeboxSong THIRTEEN = define("13", Sounds.MUSIC_DISC_13, 178, 1);
public static final IJukeboxSong CAT = define("cat", Sounds.MUSIC_DISC_CAT, 185, 2);
public static final IJukeboxSong BLOCKS = define("blocks", Sounds.MUSIC_DISC_BLOCKS, 345, 3);
public static final IJukeboxSong CHIRP = define("chirp", Sounds.MUSIC_DISC_CHIRP, 185, 4);
public static final IJukeboxSong FAR = define("far", Sounds.MUSIC_DISC_FAR, 174, 5);
public static final IJukeboxSong MALL = define("mall", Sounds.MUSIC_DISC_MALL, 197, 6);
public static final IJukeboxSong MELLOHI = define("mellohi", Sounds.MUSIC_DISC_MELLOHI, 96, 7);
public static final IJukeboxSong STAL = define("stal", Sounds.MUSIC_DISC_STAL, 150, 8);
public static final IJukeboxSong STRAD = define("strad", Sounds.MUSIC_DISC_STRAD, 188, 9);
public static final IJukeboxSong WARD = define("ward", Sounds.MUSIC_DISC_WARD, 251, 10);
public static final IJukeboxSong ELEVEN = define("11", Sounds.MUSIC_DISC_11, 71, 11);
public static final IJukeboxSong WAIT = define("wait", Sounds.MUSIC_DISC_WAIT, 238, 12);
public static final IJukeboxSong PIGSTEP = define("pigstep", Sounds.MUSIC_DISC_PIGSTEP, 149, 13);
public static final IJukeboxSong OTHERSIDE = define("otherside", Sounds.MUSIC_DISC_OTHERSIDE, 195, 14);
public static final IJukeboxSong FIVE = define("5", Sounds.MUSIC_DISC_5, 178, 15);
public static final IJukeboxSong RELIC = define("relic", Sounds.MUSIC_DISC_RELIC, 218, 14);
public static final IJukeboxSong PRECIPICE = define("precipice", Sounds.MUSIC_DISC_PRECIPICE, 299, 13);
public static final IJukeboxSong CREATOR = define("creator", Sounds.MUSIC_DISC_CREATOR, 176, 12);
public static final IJukeboxSong CREATOR_MUSIC_BOX = define("creator_music_box", Sounds.MUSIC_DISC_CREATOR_MUSIC_BOX,
73, 11);
/**
* Returns an immutable view of the jukebox songs.
*
* @return Jukebox Songs
*/
public static Collection<IJukeboxSong> values() {
return REGISTRY.getEntries();
}
static {
REGISTRY.unloadMappings();
}
}
@@ -20,15 +20,15 @@ package com.github.retrooper.packetevents.protocol.item.trimmaterial;
import com.github.retrooper.packetevents.protocol.item.armormaterial.ArmorMaterial;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Objects;
public class StaticTrimMaterial implements TrimMaterial {
public class StaticTrimMaterial extends AbstractMappedEntity implements TrimMaterial {
private final String assetName;
private final ItemType ingredient;
@@ -40,6 +40,15 @@ public class StaticTrimMaterial implements TrimMaterial {
String assetName, ItemType ingredient, float itemModelIndex,
Map<ArmorMaterial, String> overrideArmorMaterials, Component description
) {
this(null, assetName, ingredient, itemModelIndex, overrideArmorMaterials, description);
}
public StaticTrimMaterial(
@Nullable TypesBuilderData data,
String assetName, ItemType ingredient, float itemModelIndex,
Map<ArmorMaterial, String> overrideArmorMaterials, Component description
) {
super(data);
this.assetName = assetName;
this.ingredient = ingredient;
this.itemModelIndex = itemModelIndex;
@@ -48,18 +57,9 @@ public class StaticTrimMaterial implements TrimMaterial {
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
public TrimMaterial copy(@Nullable TypesBuilderData newData) {
return new StaticTrimMaterial(newData, this.assetName, this.ingredient, this.itemModelIndex,
this.overrideArmorMaterials, this.description);
}
@Override
@@ -87,20 +87,24 @@ public class StaticTrimMaterial implements TrimMaterial {
return this.description;
}
@Override
public boolean equals(Object obj) {
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticTrimMaterial)) return false;
StaticTrimMaterial material = (StaticTrimMaterial) obj;
if (Float.compare(material.itemModelIndex, this.itemModelIndex) != 0) return false;
if (!this.assetName.equals(material.assetName)) return false;
if (!this.ingredient.equals(material.ingredient)) return false;
if (!this.overrideArmorMaterials.equals(material.overrideArmorMaterials)) return false;
return this.description.equals(material.description);
if (!super.equals(obj)) return false;
StaticTrimMaterial that = (StaticTrimMaterial) obj;
if (Float.compare(that.itemModelIndex, this.itemModelIndex) != 0) return false;
if (!this.assetName.equals(that.assetName)) return false;
if (!this.ingredient.equals(that.ingredient)) return false;
if (!this.overrideArmorMaterials.equals(that.overrideArmorMaterials)) return false;
return this.description.equals(that.description);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.assetName, this.ingredient, this.itemModelIndex, this.overrideArmorMaterials, this.description);
}
@Override
public int hashCode() {
return Objects.hash(this.assetName, this.ingredient, this.itemModelIndex, this.overrideArmorMaterials, this.description);
public String toString() {
return "StaticTrimMaterial{assetName='" + this.assetName + '\'' + ", ingredient=" + this.ingredient + ", itemModelIndex=" + this.itemModelIndex + ", overrideArmorMaterials=" + this.overrideArmorMaterials + ", description=" + this.description + '}';
}
}
@@ -22,14 +22,24 @@ import com.github.retrooper.packetevents.protocol.item.armormaterial.ArmorMateri
import com.github.retrooper.packetevents.protocol.item.armormaterial.ArmorMaterials;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public interface TrimMaterial extends MappedEntity {
public interface TrimMaterial extends MappedEntity, CopyableEntity<TrimMaterial> {
String getAssetName();
@@ -46,7 +56,7 @@ public interface TrimMaterial extends MappedEntity {
Component getDescription();
static TrimMaterial read(PacketWrapper<?> wrapper) {
return wrapper.readMappedEntityOrDirect(TrimMaterials::getById, TrimMaterial::readDirect);
return wrapper.readMappedEntityOrDirect(TrimMaterials.getRegistry(), TrimMaterial::readDirect);
}
static TrimMaterial readDirect(PacketWrapper<?> wrapper) {
@@ -72,4 +82,49 @@ public interface TrimMaterial extends MappedEntity {
PacketWrapper::writeMappedEntity, PacketWrapper::writeString);
wrapper.writeComponent(material.getDescription());
}
static TrimMaterial decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
String assetName = compound.getStringTagValueOrThrow("asset_name");
ItemType ingredient = ItemTypes.getByName(compound.getStringTagValueOrThrow("ingredient"));
float itemModelIndex = compound.getNumberTagOrThrow("item_model_index").getAsFloat();
NBTCompound overrideArmorMaterialsTag = compound.getCompoundTagOrNull("override_armor_materials");
Map<ArmorMaterial, String> overrideArmorMaterials;
if (overrideArmorMaterialsTag != null) {
overrideArmorMaterials = new HashMap<>();
for (Map.Entry<String, NBT> entry : overrideArmorMaterialsTag.getTags().entrySet()) {
ArmorMaterial material = ArmorMaterials.getByName(entry.getKey());
String override = ((NBTString) entry.getValue()).getValue();
overrideArmorMaterials.put(material, override);
}
} else {
overrideArmorMaterials = Collections.emptyMap();
}
Component description = AdventureSerializer.fromNbt(((NBTCompound) nbt).getTagOrThrow("description"));
return new StaticTrimMaterial(data, assetName, ingredient, itemModelIndex, overrideArmorMaterials, description);
}
static NBT encode(TrimMaterial material, ClientVersion version) {
NBTCompound overrideArmorMaterialsTag;
if (!material.getOverrideArmorMaterials().isEmpty()) {
overrideArmorMaterialsTag = new NBTCompound();
for (Map.Entry<ArmorMaterial, String> entry : material.getOverrideArmorMaterials().entrySet()) {
String materialName = entry.getKey().getName().toString();
NBTString overrideTag = new NBTString(entry.getValue());
overrideArmorMaterialsTag.setTag(materialName, overrideTag);
}
} else {
overrideArmorMaterialsTag = null;
}
NBTCompound compound = new NBTCompound();
compound.setTag("asset_name", new NBTString(material.getAssetName()));
compound.setTag("ingredient", new NBTString(material.getIngredient().getName().toString()));
compound.setTag("item_model_index", new NBTFloat(material.getItemModelIndex()));
if (overrideArmorMaterialsTag != null) {
compound.setTag("override_armor_materials", overrideArmorMaterialsTag);
}
compound.setTag("description", AdventureSerializer.toNbt(material.getDescription()));
return compound;
}
}
@@ -24,25 +24,26 @@ import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
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 com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static net.kyori.adventure.text.Component.translatable;
import static net.kyori.adventure.text.format.TextColor.color;
public class TrimMaterials {
public final class TrimMaterials {
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");
private static final VersionedRegistry<TrimMaterial> REGISTRY = new VersionedRegistry<>(
"trim_material", "item/item_trim_material_mappings");
private TrimMaterials() {
}
@ApiStatus.Internal
public static TrimMaterial define(String key, ItemType ingredient, float itemModelIndex, int color) {
// darken own armor material - if present
Map<ArmorMaterial, String> overrideArmorMaterials = new HashMap<>(2);
@@ -56,68 +57,25 @@ public class TrimMaterials {
return define(key, key, ingredient, itemModelIndex, overrideArmorMaterials, description);
}
@ApiStatus.Internal
public static TrimMaterial define(
String key, String assetName, ItemType ingredient, float itemModelIndex,
Map<ArmorMaterial, String> overrideArmorMaterials, Component description
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
TrimMaterial material = new TrimMaterial() {
@Override
public String getAssetName() {
return assetName;
}
@Override
public ItemType getIngredient() {
return ingredient;
}
@Override
public float getItemModelIndex() {
return itemModelIndex;
}
@Override
public Map<ArmorMaterial, String> getOverrideArmorMaterials() {
return overrideArmorMaterials;
}
@Override
public Component getDescription() {
return description;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TrimMaterial) {
return this.getName().equals(((TrimMaterial) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, TRIM_MATERIAL_MAP, TRIM_MATERIAL_ID_MAP, material);
return material;
return REGISTRY.define(key, data ->
new StaticTrimMaterial(data, assetName, ingredient, itemModelIndex, overrideArmorMaterials, description));
}
public static VersionedRegistry<TrimMaterial> getRegistry() {
return REGISTRY;
}
// with key
public static TrimMaterial getByName(String name) {
return TRIM_MATERIAL_MAP.get(name);
return REGISTRY.getByName(name);
}
public static TrimMaterial getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, TrimMaterial> idMap = TRIM_MATERIAL_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
// Added in 1.19.4
@@ -134,13 +92,14 @@ public class TrimMaterials {
/**
* Returns an immutable view of the trim materials.
*
* @return Trim Materials
*/
public static Collection<TrimMaterial> values() {
return Collections.unmodifiableCollection(TRIM_MATERIAL_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -19,13 +19,15 @@
package com.github.retrooper.packetevents.protocol.item.trimpattern;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticTrimPattern implements TrimPattern {
public class StaticTrimPattern extends AbstractMappedEntity implements TrimPattern {
private final ResourceLocation assetId;
private final ItemType templateItem;
@@ -36,6 +38,15 @@ public class StaticTrimPattern implements TrimPattern {
ResourceLocation assetId, ItemType templateItem,
Component description, boolean decal
) {
this(null, assetId, templateItem, description, decal);
}
public StaticTrimPattern(
@Nullable TypesBuilderData data,
ResourceLocation assetId, ItemType templateItem,
Component description, boolean decal
) {
super(data);
this.assetId = assetId;
this.templateItem = templateItem;
this.description = description;
@@ -43,18 +54,9 @@ public class StaticTrimPattern implements TrimPattern {
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
public TrimPattern copy(@Nullable TypesBuilderData newData) {
return new StaticTrimPattern(newData, this.assetId,
this.templateItem, this.description, this.decal);
}
@Override
@@ -77,19 +79,23 @@ public class StaticTrimPattern implements TrimPattern {
return this.decal;
}
@Override
public boolean equals(Object obj) {
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticTrimPattern)) return false;
StaticTrimPattern pattern = (StaticTrimPattern) obj;
if (this.decal != pattern.decal) return false;
if (!this.assetId.equals(pattern.assetId)) return false;
if (!this.templateItem.equals(pattern.templateItem)) return false;
return this.description.equals(pattern.description);
if (!super.equals(obj)) return false;
StaticTrimPattern that = (StaticTrimPattern) obj;
if (this.decal != that.decal) return false;
if (!this.assetId.equals(that.assetId)) return false;
if (!this.templateItem.equals(that.templateItem)) return false;
return this.description.equals(that.description);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.assetId, this.templateItem, this.description, this.decal);
}
@Override
public int hashCode() {
return Objects.hash(this.assetId, this.templateItem, this.description, this.decal);
public String toString() {
return "StaticTrimPattern{assetId=" + this.assetId + ", templateItem=" + this.templateItem + ", description=" + this.description + ", decal=" + this.decal + '}';
}
}
@@ -20,12 +20,21 @@ package com.github.retrooper.packetevents.protocol.item.trimpattern;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
public interface TrimPattern extends MappedEntity {
public interface TrimPattern extends MappedEntity, CopyableEntity<TrimPattern> {
ResourceLocation getAssetId();
@@ -36,7 +45,7 @@ public interface TrimPattern extends MappedEntity {
boolean isDecal();
static TrimPattern read(PacketWrapper<?> wrapper) {
return wrapper.readMappedEntityOrDirect(TrimPatterns::getById, TrimPattern::readDirect);
return wrapper.readMappedEntityOrDirect(TrimPatterns.getRegistry(), TrimPattern::readDirect);
}
static TrimPattern readDirect(PacketWrapper<?> wrapper) {
@@ -57,4 +66,24 @@ public interface TrimPattern extends MappedEntity {
wrapper.writeComponent(pattern.getDescription());
wrapper.writeBoolean(pattern.isDecal());
}
static TrimPattern decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
ResourceLocation assetId = new ResourceLocation(compound.getStringTagValueOrThrow("asset_id"));
ItemType templateItem = ItemTypes.getByName(compound.getStringTagValueOrThrow("template_item"));
Component description = AdventureSerializer.fromNbt(compound.getTagOrThrow("description"));
boolean decal = version.isNewerThanOrEquals(ClientVersion.V_1_20_2) && compound.getBoolean("decal");
return new StaticTrimPattern(data, assetId, templateItem, description, decal);
}
static NBT encode(TrimPattern pattern, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("asset_id", new NBTString(pattern.getAssetId().toString()));
compound.setTag("template_item", new NBTString(pattern.getTemplateItem().getName().toString()));
compound.setTag("description", AdventureSerializer.toNbt(pattern.getDescription()));
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_2)) {
compound.setTag("decal", new NBTByte(pattern.isDecal()));
}
return compound;
}
}
@@ -22,20 +22,19 @@ import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
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 com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
import java.util.Map;
public final class TrimPatterns {
public class TrimPatterns {
private static final VersionedRegistry<TrimPattern> REGISTRY = new VersionedRegistry<>(
"trim_pattern", "item/item_trim_pattern_mappings");
private static final Map<String, TrimPattern> PATTERN_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, TrimPattern>> PATTERN_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_trim_pattern_mappings");
private TrimPatterns() {
}
@ApiStatus.Internal
public static TrimPattern define(String key) {
ResourceLocation assetId = ResourceLocation.minecraft(key);
ItemType templateItem = ItemTypes.getByName(assetId + "_armor_trim_smithing_template");
@@ -44,63 +43,25 @@ public class TrimPatterns {
return define(key, assetId, templateItem, description, decal);
}
@ApiStatus.Internal
public static TrimPattern define(
String key, ResourceLocation assetId, ItemType templateItem,
Component description, boolean decal
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
TrimPattern pattern = new TrimPattern() {
@Override
public ResourceLocation getAssetId() {
return assetId;
}
@Override
public ItemType getTemplateItem() {
return templateItem;
}
@Override
public Component getDescription() {
return description;
}
@Override
public boolean isDecal() {
return decal;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TrimPattern) {
return this.getName().equals(((TrimPattern) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PATTERN_TYPE_MAP, PATTERN_TYPE_ID_MAP, pattern);
return pattern;
return REGISTRY.define(key, data ->
new StaticTrimPattern(data, assetId, templateItem, description, decal));
}
public static VersionedRegistry<TrimPattern> getRegistry() {
return REGISTRY;
}
// with key
public static TrimPattern getByName(String name) {
return PATTERN_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static TrimPattern getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, TrimPattern> idMap = PATTERN_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
// Added in 1.19.4
@@ -128,6 +89,6 @@ public class TrimPatterns {
public static final TrimPattern FLOW = define("flow");
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -45,9 +45,7 @@ import com.github.retrooper.packetevents.protocol.potion.PotionTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
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.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -87,14 +85,15 @@ import static com.github.retrooper.packetevents.protocol.component.ComponentType
import static com.github.retrooper.packetevents.protocol.component.ComponentTypes.WRITABLE_BOOK_CONTENT;
public class ItemTypes {
private static final Map<String, ItemType> ITEM_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, ItemType>> ITEM_TYPE_ID_MAP = new HashMap<>();
private static final VersionedRegistry<ItemType> REGISTRY = new VersionedRegistry<>(
"item", "item/item_type_mappings");
private static final Map<StateType, ItemType> HELD_TO_PLACED_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_type_mappings");
private static final UUID TOOL_MODIFIER_ATTACK_DAMAGE_UUID = UUID.fromString("cb3f55d3-645c-4f38-a497-9c13a33db5cf");
private static final UUID TOOL_MODIFIER_ATTACK_SPEED_UUID = UUID.fromString("fa233e1c-4180-4865-b01b-bcce9785aca3");
// <editor-fold desc="item type definitions" defaultstate="collapsed">
public static final ItemType GILDED_BLACKSTONE = builder("gilded_blackstone").setMaxAmount(64).setPlacedType(StateTypes.GILDED_BLACKSTONE).build();
public static final ItemType NETHER_BRICK_SLAB = builder("nether_brick_slab").setMaxAmount(64).setPlacedType(StateTypes.NETHER_BRICK_SLAB).build();
public static final ItemType ANDESITE_SLAB = builder("andesite_slab").setMaxAmount(64).setPlacedType(StateTypes.ANDESITE_SLAB).build();
@@ -1488,8 +1487,15 @@ public class ItemTypes {
@Deprecated
public static final ItemType POTTERY_SHARD_SKULL = builder("POTTERY_SHARD_SKULL").setMaxAmount(64).build();
// </editor-fold>
public static VersionedRegistry<ItemType> getRegistry() {
return REGISTRY;
}
public static Collection<ItemType> values() {
return ITEM_TYPE_MAP.values();
return REGISTRY.getEntries();
}
public static Builder builder(String key) {
@@ -1516,10 +1522,7 @@ public class ItemTypes {
}
StaticComponentMap components = mapBuilder.build();
TypesBuilderData data = TYPES_BUILDER.define(key);
ItemType type = new ItemType() {
private final int[] ids = data.getData();
return REGISTRY.define(key, data -> new ItemType() {
@Override
public int getMaxAmount() {
return maxAmount;
@@ -1537,8 +1540,7 @@ public class ItemTypes {
@Override
public int getId(ClientVersion version) {
int index = TYPES_BUILDER.getDataIndex(version);
return ids[index];
return data.getId(version);
}
@Override
@@ -1579,21 +1581,15 @@ public class ItemTypes {
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, ITEM_TYPE_MAP, ITEM_TYPE_ID_MAP, type);
return type;
});
}
@Nullable
public static ItemType getByName(String name) {
return ITEM_TYPE_MAP.get(name);
public static @Nullable ItemType getByName(String name) {
return REGISTRY.getByName(name);
}
@NotNull
public static ItemType getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, ItemType> typeIdMap = ITEM_TYPE_ID_MAP.get((byte) index);
return typeIdMap.getOrDefault(id, ItemTypes.AIR);
public static @Nullable ItemType getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static ItemType getTypePlacingState(StateType type) {
@@ -1662,6 +1658,6 @@ public class ItemTypes {
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,79 @@
/*
* 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.mapper;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public abstract class AbstractMappedEntity implements MappedEntity {
protected final @Nullable TypesBuilderData data;
protected AbstractMappedEntity(@Nullable TypesBuilderData data) {
this.data = data;
}
public @Nullable TypesBuilderData getRegistryData() {
return this.data;
}
@Override
public ResourceLocation getName() {
if (this.data != null) {
return this.data.getName();
}
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
if (this.data != null) {
return this.data.getId(version);
}
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return this.data != null;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof AbstractMappedEntity)) return false;
AbstractMappedEntity that = (AbstractMappedEntity) obj;
if (this.data != null && that.data != null) {
return this.data.getName().equals(that.data.getName());
}
return false;
}
@Override
public int hashCode() {
if (this.data != null) {
return Objects.hashCode(this.data.getName());
}
return super.hashCode();
}
}
@@ -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.mapper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.Nullable;
public interface CopyableEntity<T extends MappedEntity> {
T copy(@Nullable TypesBuilderData newData);
}
@@ -18,13 +18,18 @@
package com.github.retrooper.packetevents.protocol.mapper;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
/**
@@ -54,6 +59,10 @@ public class MappedEntitySet<T> {
this.entities = entities;
}
public static <Z extends MappedEntity> MappedEntitySet<Z> createEmpty() {
return new MappedEntitySet<>(new ArrayList<>(0));
}
public static <Z extends MappedEntity> MappedEntitySet<Z> read(
PacketWrapper<?> wrapper, BiFunction<ClientVersion, Integer, Z> getter) {
int count = wrapper.readVarInt() - 1;
@@ -80,4 +89,64 @@ public class MappedEntitySet<T> {
wrapper.writeMappedEntity(entity);
}
}
public static <Z extends MappedEntity> MappedEntitySet<Z> decode(
NBT nbt, ClientVersion version, IRegistry<Z> registry) {
if (nbt instanceof NBTString) {
String tagName = ((NBTString) nbt).getValue();
ResourceLocation tagKey = new ResourceLocation(tagName);
return new MappedEntitySet<>(tagKey);
}
NBTList<?> listTag = (NBTList<?>) nbt;
List<Z> list = new ArrayList<>(listTag.size());
for (NBT tag : listTag.getTags()) {
ResourceLocation tagKey = new ResourceLocation(((NBTString) tag).getValue());
list.add(registry.getByName(tagKey));
}
return new MappedEntitySet<>(list);
}
public static <Z extends MappedEntity> NBT encode(MappedEntitySet<Z> set, ClientVersion version) {
if (set.tagKey != null) {
return new NBTString(set.tagKey.toString());
}
assert set.entities != null; // can't be null, verified in ctor
NBTList<NBTString> listTag = NBTList.createStringList();
for (Z entity : set.entities) {
listTag.addTag(new NBTString(entity.getName().toString()));
}
return listTag;
}
public boolean isEmpty() {
return this.entities != null && this.entities.isEmpty();
}
public @Nullable ResourceLocation getTagKey() {
return this.tagKey;
}
public @Nullable List<T> getEntities() {
return this.entities;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof MappedEntitySet)) return false;
MappedEntitySet<?> that = (MappedEntitySet<?>) obj;
if (!Objects.equals(this.tagKey, that.tagKey)) return false;
return Objects.equals(this.entities, that.entities);
}
@Override
public int hashCode() {
return Objects.hash(this.tagKey, this.entities);
}
@Override
public String toString() {
return "MappedEntitySet{tagKey=" + this.tagKey + ", entities=" + this.entities + '}';
}
}
@@ -30,6 +30,10 @@ public class NBTByte extends NBTNumber {
this((byte) (value ? 1 : 0));
}
public boolean getAsBool() {
return this.value != 0;
}
@Override
public NBTType<NBTByte> getType() {
return NBTType.BYTE;
@@ -1,32 +1,67 @@
/*
* 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.nbt;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class NBTLimiter {
public interface NBTLimiter {
private final int max;
@Nullable
private Object byteBuf;
private int bytes;
int DEFAULT_MAX_SIZE = 2097152;
public NBTLimiter() {
this.max = Integer.MAX_VALUE;
static NBTLimiter noop() {
return new NBTLimiter() {
@Override
public void increment(int amount) {
// no-op
}
@Override
public void checkReadability(int length) {
// no-op
}
};
}
public NBTLimiter(@NotNull Object byteBuf) {
this.byteBuf = byteBuf;
this.max = 2097152;
static NBTLimiter forBuffer(final @NotNull Object byteBuf) {
return forBuffer(byteBuf, DEFAULT_MAX_SIZE);
}
public void increment(int amount) {
bytes += amount;
static NBTLimiter forBuffer(final @NotNull Object byteBuf, final int max) {
return new NBTLimiter() {
private int bytes;
if (bytes > max) throw new IllegalArgumentException("NBT size limit reached (" + bytes + "/" + max + ")");
@Override
public void increment(int amount) {
bytes += amount;
if (bytes > max) throw new IllegalArgumentException("NBT size limit reached (" + bytes + "/" + max + ")");
}
@Override
public void checkReadability(int length) {
if (length > ByteBufHelper.readableBytes(byteBuf)) throw new IllegalArgumentException("Length is too large: " + length + ", readable: " + ByteBufHelper.readableBytes(byteBuf));
}
};
}
public void checkReadability(int length) {
if(byteBuf != null && length > ByteBufHelper.readableBytes(byteBuf)) throw new IllegalArgumentException("Length is too large: " + length + ", readable: " + ByteBufHelper.readableBytes(byteBuf));
}
void increment(int amount);
void checkReadability(int length);
}
@@ -150,7 +150,11 @@ public class NBTCodec {
//PacketEvents end
public static NBT readNBTFromBuffer(Object byteBuf, ServerVersion serverVersion) {
NBTLimiter limiter = new NBTLimiter(byteBuf);
NBTLimiter limiter = NBTLimiter.forBuffer(byteBuf);
return readNBTFromBuffer(byteBuf, serverVersion, limiter);
}
public static NBT readNBTFromBuffer(Object byteBuf, ServerVersion serverVersion, NBTLimiter limiter) {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
try {
final boolean named = serverVersion.isOlderThan(ServerVersion.V_1_20_2);
@@ -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.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import java.io.IOException;
public interface NBTReader<T extends NBT, IN> {
default T deserializeTag(NBTLimiter limiter, IN from) throws IOException {
return deserializeTag(limiter, from, true);
}
T deserializeTag(NBTLimiter limiter, IN from, boolean named) throws IOException;
}
@@ -27,7 +27,7 @@ import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
public class NBTSerializer<IN, OUT> {
public class NBTSerializer<IN, OUT> implements NBTReader<NBT, IN>, NBTWriter<NBT, OUT> {
protected final IdReader<IN> idReader;
protected final IdWriter<OUT> idWriter;
@@ -47,10 +47,7 @@ public class NBTSerializer<IN, OUT> {
this.nameWriter = nameWriter;
}
public NBT deserializeTag(NBTLimiter limiter, IN from) throws IOException {
return deserializeTag(limiter, from, true);
}
@Override
public NBT deserializeTag(NBTLimiter limiter, IN from, boolean named) throws IOException {
NBTType<?> type = readTagType(limiter, from);
if (type == NBTType.END) {
@@ -62,10 +59,7 @@ public class NBTSerializer<IN, OUT> {
return readTag(limiter, from, type);
}
public void serializeTag(OUT to, NBT tag) throws IOException {
serializeTag(to, tag, true);
}
@Override
public void serializeTag(OUT to, NBT tag, boolean named) throws IOException {
NBTType<?> type = tag.getType();
writeTagType(to, type);
@@ -95,7 +89,7 @@ public class NBTSerializer<IN, OUT> {
typeWriters.put(type, typeWriter);
}
protected NBTType<?> readTagType(NBTLimiter limiter, IN from) throws IOException {
NBTType<?> readTagType(NBTLimiter limiter, IN from) throws IOException {
int id = idReader.readId(limiter, from);
NBTType<?> type = idToType.get(id);
if (type == null) {
@@ -104,11 +98,11 @@ public class NBTSerializer<IN, OUT> {
return type;
}
protected String readTagName(NBTLimiter limiter, IN from) throws IOException {
String readTagName(NBTLimiter limiter, IN from) throws IOException {
return nameReader.readName(limiter, from);
}
protected NBT readTag(NBTLimiter limiter, IN from, NBTType<?> type) throws IOException {
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));
@@ -116,7 +110,7 @@ public class NBTSerializer<IN, OUT> {
return f.readTag(limiter, from);
}
protected void writeTagType(OUT stream, NBTType<?> type) throws IOException {
void writeTagType(OUT stream, NBTType<?> type) throws IOException {
int id = typeToId.getOrDefault(type, -1);
if (id == -1) {
throw new IOException(MessageFormat.format("Unknown nbt type {0}", type));
@@ -124,12 +118,12 @@ public class NBTSerializer<IN, OUT> {
idWriter.writeId(stream, id);
}
protected void writeTagName(OUT stream, String name) throws IOException {
void writeTagName(OUT stream, String name) throws IOException {
nameWriter.writeName(stream, name);
}
@SuppressWarnings("unchecked")
protected void writeTag(OUT stream, NBT tag) throws IOException {
void writeTag(OUT stream, NBT tag) throws IOException {
TagWriter<OUT, NBT> f = (TagWriter<OUT, NBT>) typeWriters.get(tag.getType());
if (f == null) {
throw new IOException(MessageFormat.format("No writer registered for nbt type {0}", tag.getType()));
@@ -0,0 +1,32 @@
/*
* 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.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import java.io.IOException;
public interface NBTWriter<T extends NBT, OUT> {
default void serializeTag(OUT to, T tag) throws IOException {
serializeTag(to, tag, true);
}
void serializeTag(OUT to, NBT tag, boolean named) throws IOException;
}
@@ -0,0 +1,698 @@
/*
* 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.nbt.serializer;
import com.github.retrooper.packetevents.protocol.nbt.*;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.util.*;
public final class SequentialNBTReader implements NBTReader<NBT, DataInputStream> {
public static final SequentialNBTReader INSTANCE = new SequentialNBTReader();
private static final Map<NBTType<?>, TagSkip> TAG_SKIPS = new HashMap<>(16);
private static final Map<NBTType<?>, TagBinaryReader> TAG_BINARY_READERS = new HashMap<>(16);
@Override
public NBT deserializeTag(NBTLimiter limiter, DataInputStream from, boolean named) throws IOException {
NBTType<?> type = DefaultNBTSerializer.INSTANCE.readTagType(limiter, from);
if (named) {
// skip name
int len = from.readUnsignedShort();
from.skipBytes(len);
}
NBT nbt;
if (type == NBTType.COMPOUND) {
nbt = new Compound(from, limiter, () -> {});
} else if (type == NBTType.LIST) {
nbt = new List(from, limiter, () -> {});
} else {
nbt = DefaultNBTSerializer.INSTANCE.readTag(limiter, from, type);
}
return nbt;
}
private static void checkReadable(NBT lastRead) {
if (lastRead == null) return;
if (lastRead instanceof Iterator && ((Iterator<NBT>) lastRead).hasNext()) {
throw new IllegalStateException("Previous nbt has not been read completely");
}
}
public static class Compound extends NBT implements Iterator<Map.Entry<String, NBT>>, Iterable<Map.Entry<String, NBT>>, Skippable, Closeable {
private final DataInputStream stream;
private final NBTLimiter limiter;
private final Runnable onComplete;
private NBTType<?> nextType;
private NBT lastRead;
private boolean hasReadType;
private Compound(DataInputStream stream, NBTLimiter limiter, Runnable onComplete) {
this.stream = stream;
this.limiter = limiter;
this.onComplete = onComplete;
limiter.increment(48);
runCompleted();
}
@Override
public NBTType<?> getType() {
return NBTType.COMPOUND;
}
@Override
public boolean equals(Object other) {
return this == other;
}
@Override
public int hashCode() {
return 0;
}
@Override
public NBT copy() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
checkReadable(lastRead);
if (!hasReadType) {
try {
nextType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, stream);
hasReadType = true;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return nextType != NBTType.END;
}
@Override
public Map.Entry<String, NBT> next() {
if (!hasNext()) {
throw new IllegalStateException("No more elements in compound");
}
try {
hasReadType = false;
String name = DefaultNBTSerializer.INSTANCE.readTagName(limiter, stream);
if (nextType == NBTType.COMPOUND) {
lastRead = new Compound(stream, limiter, this::runCompleted);
} else if (nextType == NBTType.LIST) {
lastRead = new List(stream, limiter, this::runCompleted);
} else {
lastRead = DefaultNBTSerializer.INSTANCE.readTag(limiter, stream, nextType);
runCompleted();
}
limiter.increment(36);
return new AbstractMap.SimpleEntry<>(name, lastRead);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void runCompleted() {
if (!hasNext()) {
onComplete.run();
}
}
@NotNull
@Override
public Iterator<Map.Entry<String, NBT>> iterator() {
return this;
}
@Override
public void skip() {
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return;
try {
int len = stream.readUnsignedShort();
stream.skipBytes(len);
TAG_SKIPS.get(nextType).skip(limiter, stream);
limiter.increment(36);
TAG_SKIPS.get(NBTType.COMPOUND).skip(limiter, stream);
hasReadType = true;
nextType = NBTType.END;
} catch (IOException e) {
throw new RuntimeException(e);
}
runCompleted();
}
@Override
public void skipOne() {
checkReadable(lastRead);
if (!hasNext()) return;
try {
int len = stream.readUnsignedShort();
stream.skipBytes(len);
TAG_SKIPS.get(nextType).skip(limiter, stream);
limiter.increment(36);
hasReadType = false;
} catch (IOException e) {
throw new RuntimeException(e);
}
runCompleted();
}
public NBTCompound readFully() {
try {
// skip last read
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return new NBTCompound();
NBTCompound compound = new NBTCompound();
// we have read a tag type, so we can start reading the tag
do {
String name = DefaultNBTSerializer.INSTANCE.readTagName(limiter, stream);
NBT nbt = DefaultNBTSerializer.INSTANCE.readTag(limiter, stream, nextType);
limiter.increment(36);
compound.setTag(name, nbt);
} while ((nextType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, stream)) != NBTType.END);
hasReadType = true;
runCompleted();
return compound;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public byte[] readFullyAsBytes() {
try {
// skip last read
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return new byte[] { 10, 0 }; // empty compound
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bytes)) {
out.write(10); // compound type
do {
out.write(DefaultNBTSerializer.INSTANCE.typeToId.get(nextType));
// name
byte[] name = TAG_BINARY_READERS.get(NBTType.STRING).read(NBTLimiter.noop(), stream);
limiter.increment(name.length * 2 + 28);
out.write(name);
// nbt
byte[] nbt = TAG_BINARY_READERS.get(nextType).read(limiter, stream);
limiter.increment(36);
out.write(nbt);
} while ((nextType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, stream)) != NBTType.END);
out.write(0);
hasReadType = true;
runCompleted();
return bytes.toByteArray();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() throws IOException {
stream.close();
}
}
public static class List extends NBT implements Iterator<NBT>, Iterable<NBT>, Skippable, Closeable {
private final DataInputStream stream;
private final NBTLimiter limiter;
private final Runnable onComplete;
private final NBTType<?> listType;
private NBT lastRead;
public int remaining;
private List(DataInputStream stream, NBTLimiter limiter, Runnable onComplete) {
this.stream = stream;
this.limiter = limiter;
this.onComplete = onComplete;
limiter.increment(37);
try {
this.listType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, stream);
this.remaining = stream.readInt();
limiter.increment(remaining * 4);
} catch (IOException e) {
throw new RuntimeException(e);
}
runCompleted();
}
@Override
public NBTType<?> getType() {
return NBTType.LIST;
}
@Override
public boolean equals(Object other) {
return this == other;
}
@Override
public int hashCode() {
return 1;
}
@Override
public NBT copy() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
return remaining > 0;
}
@Override
public NBT next() {
checkReadable(lastRead);
if (!hasNext()) {
throw new IllegalStateException("No more elements in list");
}
try {
remaining--;
if (listType == NBTType.COMPOUND) {
lastRead = new Compound(stream, limiter, this::runCompleted);
} else if (listType == NBTType.LIST) {
lastRead = new List(stream, limiter, this::runCompleted);
} else {
lastRead = DefaultNBTSerializer.INSTANCE.readTag(limiter, stream, listType);
runCompleted();
}
return lastRead;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void runCompleted() {
if (!hasNext()) {
onComplete.run();
}
}
@NotNull
@Override
public Iterator<NBT> iterator() {
return this;
}
@Override
public void skip() {
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return;
try {
TagSkip typeSkip = TAG_SKIPS.get(listType);
for (int i = 0; i < remaining; i++) {
typeSkip.skip(limiter, stream);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
remaining = 0;
runCompleted();
}
@Override
public void skipOne() {
checkReadable(lastRead);
if (!hasNext()) return;
try {
TAG_SKIPS.get(listType).skip(limiter, stream);
} catch (IOException e) {
throw new RuntimeException(e);
}
remaining--;
runCompleted();
}
public NBTList<NBT> readFully() {
try {
// skip last read
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return new NBTList<>((NBTType<NBT>) listType, 0);
NBTList<NBT> list = new NBTList<>((NBTType<NBT>) listType, remaining);
for (int i = 0; i < remaining; i++) {
list.addTag(DefaultNBTSerializer.INSTANCE.readTag(limiter, stream, listType));
}
remaining = 0;
runCompleted();
return list;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public byte[] readFullyAsBinary() {
try {
// skip last read
if (lastRead instanceof Skippable) {
((Skippable) lastRead).skip();
}
if (!hasNext()) return new byte[] { 9 };
byte[] array = null;
for (int i = 0; i < remaining; i++) {
byte[] element = TAG_BINARY_READERS.get(listType).read(limiter, stream);
if (array == null) {
array = new byte[2 + Integer.BYTES + remaining * element.length]; // all the remaining child tags will be of same size
array[0] = 9; // list tag
array[1] = DefaultNBTSerializer.INSTANCE.typeToId.get(listType).byteValue();
// write length
array[2] = (byte) (remaining >>> 24);
array[3] = (byte) (remaining >>> 16);
array[4] = (byte) (remaining >>> 8);
array[5] = (byte) remaining;
}
System.arraycopy(element, 0, array, 1 + Integer.BYTES + i * element.length, element.length);
}
remaining = 0;
runCompleted();
return array;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() throws IOException {
stream.close();
}
}
interface Skippable {
void skip();
void skipOne();
}
@FunctionalInterface
private interface TagSkip {
void skip(NBTLimiter limiter, DataInput in) throws IOException;
}
@FunctionalInterface
private interface TagBinaryReader {
byte[] read(NBTLimiter limiter, DataInput in) throws IOException;
}
static {
TAG_SKIPS.put(NBTType.BYTE, (limiter, in) -> {
limiter.increment(9);
in.skipBytes(Byte.BYTES);
});
TAG_SKIPS.put(NBTType.SHORT, (limiter, in) -> {
limiter.increment(10);
in.skipBytes(Short.BYTES);
});
TAG_SKIPS.put(NBTType.INT, (limiter, in) -> {
limiter.increment(12);
in.skipBytes(Integer.BYTES);
});
TAG_SKIPS.put(NBTType.LONG, (limiter, in) -> {
limiter.increment(16);
in.skipBytes(Long.BYTES);
});
TAG_SKIPS.put(NBTType.FLOAT, (limiter, in) -> {
limiter.increment(12);
in.skipBytes(Float.BYTES);
});
TAG_SKIPS.put(NBTType.DOUBLE, (limiter, in) -> {
limiter.increment(16);
in.skipBytes(Double.BYTES);
});
TAG_SKIPS.put(NBTType.BYTE_ARRAY, (limiter, in) -> {
limiter.increment(24);
int length = in.readInt();
limiter.checkReadability(length);
limiter.increment(length);
in.skipBytes(length);
});
TAG_SKIPS.put(NBTType.STRING, (limiter, in) -> {
limiter.increment(36);
int length = in.readUnsignedShort();
limiter.increment(length * 2);
in.skipBytes(length);
});
TAG_SKIPS.put(NBTType.LIST, (limiter, in) -> {
limiter.increment(37);
NBTType<?> listType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in);
int length = in.readInt();
limiter.increment(length * 4);
for (int i = 0; i < length; i++) {
TAG_SKIPS.get(listType).skip(limiter, in);
}
});
TAG_SKIPS.put(NBTType.COMPOUND, (limiter, in) -> {
limiter.increment(48);
NBTType<?> valueType;
while ((valueType = DefaultNBTSerializer.INSTANCE.readTagType(limiter, in)) != NBTType.END) {
int utfLen = in.readUnsignedShort();
in.skipBytes(utfLen);
limiter.increment(36);
TAG_SKIPS.get(valueType).skip(limiter, in);
}
});
TAG_SKIPS.put(NBTType.INT_ARRAY, (limiter, in) -> {
limiter.increment(24);
int length = in.readInt();
limiter.checkReadability(length * 4);
limiter.increment(length * 4);
in.skipBytes(length * Integer.BYTES);
});
TAG_SKIPS.put(NBTType.LONG_ARRAY, (limiter, in) -> {
limiter.increment(24);
int length = in.readInt();
limiter.checkReadability(length * 8);
limiter.increment(length * 8);
in.skipBytes(length * Long.BYTES);
});
TAG_BINARY_READERS.put(NBTType.BYTE, (limiter, in) -> {
limiter.increment(9);
return new byte[] { in.readByte() };
});
TAG_BINARY_READERS.put(NBTType.SHORT, (limiter, in) -> {
limiter.increment(10);
return new byte[] { in.readByte(), in.readByte() };
});
TAG_BINARY_READERS.put(NBTType.INT, (limiter, in) -> {
limiter.increment(12);
byte[] bytes = new byte[Integer.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.LONG, (limiter, in) -> {
limiter.increment(16);
byte[] bytes = new byte[Long.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.FLOAT, (limiter, in) -> {
limiter.increment(12);
byte[] bytes = new byte[Float.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.DOUBLE, (limiter, in) -> {
limiter.increment(16);
byte[] bytes = new byte[Double.BYTES];
in.readFully(bytes);
return bytes;
});
TAG_BINARY_READERS.put(NBTType.BYTE_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
if (len >= 1 << 24)
throw new IllegalArgumentException("Byte array length is too large: " + len);
limiter.checkReadability(len);
limiter.increment(len);
byte[] array = new byte[Integer.BYTES + len];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
in.readFully(array, Integer.BYTES, len);
return array;
});
TAG_BINARY_READERS.put(NBTType.STRING, (limiter, in) -> {
limiter.increment(36);
byte[] length = new byte[Short.BYTES];
in.readFully(length);
// read len
short signedVal = (short) ((length[0] & 0xFF) << 8 | (length[1] & 0xFF));
int len = signedVal >= 0 ? signedVal : 0x10000 + signedVal;
limiter.increment(len * 2);
byte[] array = new byte[Short.BYTES + len];
System.arraycopy(length, 0, array, 0, Short.BYTES);
in.readFully(array, Short.BYTES, len);
return array;
});
TAG_BINARY_READERS.put(NBTType.LIST, (limiter, in) -> {
limiter.increment(37);
byte type = in.readByte();
NBTType<? extends NBT> nbtType = DefaultNBTSerializer.INSTANCE.idToType.get((int) type);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
limiter.increment(len * 4);
byte[] array = null;
for (int i = 0; i < len; i++) {
byte[] element = TAG_BINARY_READERS.get(nbtType).read(limiter, in);
if (array == null) {
array = new byte[1 + Integer.BYTES + len * element.length]; // all the remaining child tags will be of same size
array[0] = type;
System.arraycopy(length, 0, array, 1, Integer.BYTES);
}
System.arraycopy(element, 0, array, 1 + Integer.BYTES + i * element.length, element.length);
}
return array;
});
TAG_BINARY_READERS.put(NBTType.COMPOUND, (limiter, in) -> {
limiter.increment(48);
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bytes)) {
byte type;
while ((type = in.readByte()) != 0) {
out.writeByte(type);
// name
byte[] name = TAG_BINARY_READERS.get(NBTType.STRING).read(NBTLimiter.noop(), in);
limiter.increment(name.length * 2 + 28);
out.write(name);
// nbt
byte[] nbt = TAG_BINARY_READERS.get(DefaultNBTSerializer.INSTANCE.idToType.get((int) type)).read(limiter, in);
limiter.increment(36);
out.write(nbt);
}
out.write(0); // end
return bytes.toByteArray();
}
});
TAG_BINARY_READERS.put(NBTType.INT_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
limiter.checkReadability(len * 4);
limiter.increment(len * 4);
byte[] array = new byte[Integer.BYTES + len * Integer.BYTES];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
in.readFully(array, Integer.BYTES, len * Integer.BYTES);
return array;
});
TAG_BINARY_READERS.put(NBTType.LONG_ARRAY, (limiter, in) -> {
limiter.increment(24);
byte[] length = new byte[Integer.BYTES];
in.readFully(length);
int len = bytesToInt(length);
limiter.checkReadability(len * 8);
limiter.increment(len * 8);
byte[] array = new byte[Integer.BYTES + len * Long.BYTES];
System.arraycopy(length, 0, array, 0, Integer.BYTES);
in.readFully(array, Integer.BYTES, len * Long.BYTES);
return array;
});
}
private static int bytesToInt(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) |
((bytes[1] & 0xFF) << 16) |
((bytes[2] & 0xFF) << 8 ) |
((bytes[3] & 0xFF));
}
}
@@ -18,9 +18,14 @@
package com.github.retrooper.packetevents.protocol.particle;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleData;
import com.github.retrooper.packetevents.protocol.particle.type.ParticleType;
import com.github.retrooper.packetevents.protocol.particle.type.ParticleTypes;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class Particle<T extends ParticleData> {
@@ -48,6 +53,24 @@ public class Particle<T extends ParticleData> {
particle.getType().writeData(wrapper, particle.data);
}
@SuppressWarnings("unchecked")
public static Particle<?> decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
NBT typeTag = compound.getTagOrThrow("type");
ParticleType<?> type = typeTag instanceof NBTNumber
? ParticleTypes.getById(version, ((NBTNumber) typeTag).getAsInt())
: ParticleTypes.getByName(((NBTString) typeTag).getValue());
ParticleData data = type.decodeData(compound, version);
return new Particle<>((ParticleType<? super ParticleData>) type, data);
}
public static <T extends ParticleData> NBT encode(Particle<T> particle, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("type", new NBTString(particle.type.getName().toString()));
particle.type.encodeData(particle.getData(), version, compound);
return compound;
}
public ParticleType<T> getType() {
return this.type;
}
@@ -19,11 +19,13 @@
package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class ParticleBlockStateData extends ParticleData implements LegacyConvertible {
private WrappedBlockState blockState;
public ParticleBlockStateData(WrappedBlockState blockState) {
@@ -42,11 +44,9 @@ public class ParticleBlockStateData extends ParticleData implements LegacyConver
int blockID;
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
blockID = wrapper.readVarInt();
}
else if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
} else if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
blockID = wrapper.readInt();
}
else {
} else {
blockID = wrapper.readVarInt();
}
return new ParticleBlockStateData(WrappedBlockState.getByGlobalId(wrapper.getServerVersion()
@@ -56,12 +56,22 @@ public class ParticleBlockStateData extends ParticleData implements LegacyConver
public static void write(PacketWrapper<?> wrapper, ParticleBlockStateData data) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
wrapper.writeVarInt(data.getBlockState().getGlobalId());
}
else {
} else {
wrapper.writeInt(data.getBlockState().getGlobalId());
}
}
public static ParticleBlockStateData decode(NBTCompound compound, ClientVersion version) {
String key = version.isNewerThanOrEquals(ClientVersion.V_1_20_5) ? "block_state" : "value";
WrappedBlockState state = WrappedBlockState.decode(compound.getTagOrThrow(key), version);
return new ParticleBlockStateData(state);
}
public static void encode(ParticleBlockStateData data, ClientVersion version, NBTCompound compound) {
String key = version.isNewerThanOrEquals(ClientVersion.V_1_20_5) ? "block_state" : "value";
compound.setTag(key, WrappedBlockState.encode(data.blockState, version));
}
@Override
public boolean isEmpty() {
return false;
@@ -19,8 +19,16 @@
package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
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.NBTNumber;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import static com.github.retrooper.packetevents.protocol.particle.data.ParticleDustData.decodeColor;
import static com.github.retrooper.packetevents.util.MathUtil.floor;
public class ParticleColorData extends ParticleData {
private int color;
@@ -40,6 +48,33 @@ public class ParticleColorData extends ParticleData {
}
}
public static ParticleColorData decode(NBTCompound compound, ClientVersion version) {
int argb;
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_5)) {
NBT colorTag = compound.getTagOrThrow("color");
if (colorTag instanceof NBTNumber) {
argb = ((NBTNumber) colorTag).getAsInt();
} else {
float[] color = decodeColor(colorTag);
assert color.length == 4; // required by vanilla protocol
argb = (floor(color[0] * 255f) << 24)
| (floor(color[1] * 255f) << 16)
| (floor(color[2] * 255f) << 8)
| floor(color[3] * 255f);
}
} else {
// no data to decode for <1.20.5
argb = 0xFFFFFFFF;
}
return new ParticleColorData(argb);
}
public static void encode(ParticleColorData data, ClientVersion version, NBTCompound compound) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_20_5)) {
compound.setTag("color", new NBTInt(data.color));
}
}
public int getColor() {
return this.color;
}
@@ -20,9 +20,15 @@ package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.color.Color;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import static com.github.retrooper.packetevents.protocol.particle.data.ParticleDustData.decodeColor;
import static com.github.retrooper.packetevents.protocol.particle.data.ParticleDustData.encodeColor;
public class ParticleDustColorTransitionData extends ParticleData {
//0.01 - 4
private float scale;
@@ -49,6 +55,10 @@ public class ParticleDustColorTransitionData extends ParticleData {
this.endBlue = endBlue;
}
public ParticleDustColorTransitionData(float scale, float[] startRGB, float[] endRGB) {
this(scale, startRGB[0], startRGB[1], startRGB[2], endRGB[0], endRGB[1], endRGB[2]);
}
public ParticleDustColorTransitionData(float scale, Vector3f startRGB, Vector3f endRGB) {
this(scale, startRGB.getX(), startRGB.getY(), startRGB.getZ(), endRGB.getX(), endRGB.getY(), endRGB.getZ());
}
@@ -145,6 +155,31 @@ public class ParticleDustColorTransitionData extends ParticleData {
}
}
public static ParticleDustColorTransitionData decode(NBTCompound compound, ClientVersion version) {
String fromColorKey = "from_color";
String toColorKey = "to_color";
if (version.isOlderThan(ClientVersion.V_1_20_5)) {
fromColorKey = "fromColor";
toColorKey = "toColor";
}
float[] fromColor = decodeColor(compound.getTagOrThrow(fromColorKey));
float[] toColor = decodeColor(compound.getTagOrThrow(toColorKey));
float scale = compound.getNumberTagOrThrow("scale").getAsFloat();
return new ParticleDustColorTransitionData(scale, fromColor, toColor);
}
public static void encode(ParticleDustColorTransitionData data, ClientVersion version, NBTCompound compound) {
String fromColorKey = "from_color";
String toColorKey = "to_color";
if (version.isOlderThan(ClientVersion.V_1_20_5)) {
fromColorKey = "fromColor";
toColorKey = "toColor";
}
compound.setTag(fromColorKey, encodeColor(null, data.startRed, data.startGreen, data.startBlue));
compound.setTag(toColorKey, encodeColor(null, data.endRed, data.endGreen, data.endBlue));
compound.setTag("scale", new NBTFloat(data.scale));
}
@Override
public boolean isEmpty() {
return false;
@@ -19,8 +19,20 @@
package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.protocol.color.Color;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByteArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTIntArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.nbt.NBTLongArray;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class ParticleDustData extends ParticleData {
//0.01 - 4
@@ -38,6 +50,10 @@ public class ParticleDustData extends ParticleData {
this.blue = blue;
}
public ParticleDustData(float scale, float[] rgb) {
this(scale, rgb[0], rgb[1], rgb[2]);
}
public ParticleDustData(float scale, Vector3f rgb) {
this(scale, rgb.getX(), rgb.getY(), rgb.getZ());
}
@@ -93,6 +109,63 @@ public class ParticleDustData extends ParticleData {
wrapper.writeFloat(data.getScale());
}
@ApiStatus.Internal
public static float[] decodeColor(NBT tag) {
// I hate how nbt works
if (tag instanceof NBTList<?>) {
NBTList<?> colorTagList = (NBTList<?>) tag;
float first = ((NBTNumber) colorTagList.getTag(0)).getAsFloat();
float second = ((NBTNumber) colorTagList.getTag(1)).getAsFloat();
float third = ((NBTNumber) colorTagList.getTag(2)).getAsFloat();
if (colorTagList.size() > 3) {
float fourth = ((NBTNumber) colorTagList.getTag(3)).getAsFloat();
return new float[]{first, second, third, fourth}; // ARGB
}
return new float[]{first, second, third}; // RGB
} else if (tag instanceof NBTByteArray) {
byte[] colors = ((NBTByteArray) tag).getValue();
return colors.length > 3
? new float[]{colors[0], colors[1], colors[2], colors[3]}
: new float[]{colors[0], colors[1], colors[2]};
} else if (tag instanceof NBTIntArray) {
int[] colors = ((NBTIntArray) tag).getValue();
return colors.length > 3
? new float[]{colors[0], colors[1], colors[2], colors[3]}
: new float[]{colors[0], colors[1], colors[2]};
} else if (tag instanceof NBTLongArray) {
long[] colors = ((NBTLongArray) tag).getValue();
return colors.length > 3
? new float[]{colors[0], colors[1], colors[2], colors[3]}
: new float[]{colors[0], colors[1], colors[2]};
} else {
throw new UnsupportedOperationException("Unsupported color nbt tag: " + tag);
}
}
@ApiStatus.Internal
public static NBT encodeColor(@Nullable Float alpha, float red, float green, float blue) {
NBTList<NBTFloat> nbtList = new NBTList<>(NBTType.FLOAT,
alpha == null ? 3 : 4);
if (alpha != null) {
nbtList.addTag(new NBTFloat(alpha));
}
nbtList.addTag(new NBTFloat(red));
nbtList.addTag(new NBTFloat(green));
nbtList.addTag(new NBTFloat(blue));
return nbtList;
}
public static ParticleDustData decode(NBTCompound compound, ClientVersion version) {
NBT colorNBT = compound.getTagOrNull("color");
float[] color = decodeColor(colorNBT);
float scale = compound.getNumberTagOrThrow("scale").getAsFloat();
return new ParticleDustData(scale, color);
}
public static void encode(ParticleDustData data, ClientVersion version, NBTCompound compound) {
compound.setTag("color", encodeColor(null, data.red, data.green, data.blue));
compound.setTag("scale", new NBTFloat(data.scale));
}
@Override
public boolean isEmpty() {
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@@ -42,8 +43,7 @@ public class ParticleItemStackData extends ParticleData implements LegacyConvert
public static ParticleItemStackData read(PacketWrapper<?> wrapper) {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13)) {
return new ParticleItemStackData(wrapper.readItemStack());
}
else {
} else {
return new ParticleItemStackData(ItemStack.builder()
.type(ItemTypes.getById(wrapper.getClientVersion(), wrapper.readVarInt()))
.build());
@@ -54,6 +54,17 @@ public class ParticleItemStackData extends ParticleData implements LegacyConvert
wrapper.writeItemStack(data.getItemStack());
}
public static ParticleItemStackData decode(NBTCompound compound, ClientVersion version) {
String key = version.isNewerThanOrEquals(ClientVersion.V_1_20_5) ? "item" : "value";
ItemStack stack = ItemStack.decode(compound.getTagOrThrow(key), version);
return new ParticleItemStackData(stack);
}
public static void encode(ParticleItemStackData data, ClientVersion version, NBTCompound compound) {
String key = version.isNewerThanOrEquals(ClientVersion.V_1_20_5) ? "item" : "value";
compound.setTag(key, ItemStack.encodeForParticle(data.itemStack, version));
}
@Override
public boolean isEmpty() {
return false;
@@ -1,5 +1,8 @@
package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class ParticleSculkChargeData extends ParticleData {
@@ -26,6 +29,15 @@ public class ParticleSculkChargeData extends ParticleData {
wrapper.writeFloat(data.getRoll());
}
public static ParticleSculkChargeData decode(NBTCompound compound, ClientVersion version) {
float roll = compound.getNumberTagOrThrow("roll").getAsFloat();
return new ParticleSculkChargeData(roll);
}
public static void encode(ParticleSculkChargeData data, ClientVersion version, NBTCompound compound) {
compound.setTag("roll", new NBTFloat(data.roll));
}
@Override
public boolean isEmpty() {
return false;
@@ -1,5 +1,8 @@
package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public class ParticleShriekData extends ParticleData {
@@ -26,6 +29,15 @@ public class ParticleShriekData extends ParticleData {
wrapper.writeVarInt(data.getDelay());
}
public static ParticleShriekData decode(NBTCompound compound, ClientVersion version) {
int delay = compound.getNumberTagOrThrow("delay").getAsInt();
return new ParticleShriekData(delay);
}
public static void encode(ParticleShriekData data, ClientVersion version, NBTCompound compound) {
compound.setTag("delay", new NBTInt(data.delay));
}
@Override
public boolean isEmpty() {
return false;
@@ -21,6 +21,9 @@ package com.github.retrooper.packetevents.protocol.particle.data;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTIntArray;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.positionsource.PositionSource;
import com.github.retrooper.packetevents.protocol.world.positionsource.PositionSourceType;
@@ -152,6 +155,26 @@ public class ParticleVibrationData extends ParticleData {
wrapper.writeVarInt(data.getTicks());
}
public static ParticleVibrationData decode(NBTCompound compound, ClientVersion version) {
Vector3i origin = version.isNewerThanOrEquals(ClientVersion.V_1_19) ? null :
new Vector3i(compound.getTagOfTypeOrThrow("origin", NBTIntArray.class).getValue());
PositionSource destination = PositionSource.decode(compound.getCompoundTagOrThrow("destination"), version);
int arrivalInTicks = compound.getNumberTagOrThrow("arrival_in_ticks").getAsInt();
return new ParticleVibrationData(origin, destination, arrivalInTicks);
}
public static void encode(ParticleVibrationData data, ClientVersion version, NBTCompound compound) {
if (version.isOlderThan(ClientVersion.V_1_19)) {
Vector3i startPos = data.getStartingPosition();
if (startPos != null) {
compound.setTag("origin", new NBTIntArray(
new int[]{startPos.x, startPos.y, startPos.z}));
}
}
compound.setTag("destination", PositionSource.encode(data.source, version));
compound.setTag("arrival_in_ticks", new NBTInt(data.ticks));
}
@ApiStatus.Obsolete
public Vector3i getStartingPosition() {
return this.startingPosition;
@@ -19,7 +19,9 @@
package com.github.retrooper.packetevents.protocol.particle.type;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleData;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import java.util.function.BiConsumer;
@@ -31,6 +33,10 @@ public interface ParticleType<T extends ParticleData> extends MappedEntity {
void writeData(PacketWrapper<?> wrapper, T data);
T decodeData(NBTCompound compound, ClientVersion version);
void encodeData(T data, ClientVersion version, NBTCompound compound);
@Deprecated
default Function<PacketWrapper<?>, ParticleData> readDataFunction() {
return this::readData;
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.particle.type;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleBlockStateData;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleColorData;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleData;
@@ -33,6 +34,9 @@ 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 com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
@@ -47,13 +51,17 @@ public class ParticleTypes {
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("particle/particle_type_mappings");
public static ParticleType<ParticleData> define(String key) {
return define(key, wrapper -> ParticleData.emptyData(), null);
return define(
key,
wrapper -> ParticleData.emptyData(), null,
(nbt, version) -> ParticleData.emptyData(), null
);
}
public static <T extends ParticleData> ParticleType<T> define(
String key,
PacketWrapper.Reader<T> reader,
@Nullable PacketWrapper.Writer<T> writer
Reader<T> reader, @Nullable Writer<T> writer,
Decoder<T> decoder, @Nullable Encoder<T> encoder
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
ParticleType<T> particleType = new ParticleType<T>() {
@@ -67,7 +75,21 @@ public class ParticleTypes {
if (writer != null) {
writer.accept(wrapper, data);
} else if (!data.isEmpty()) {
throw new UnsupportedOperationException("Trying to serialize non-empty data for " + this.getName());
throw new UnsupportedOperationException("Trying to write non-empty data for " + this.getName());
}
}
@Override
public T decodeData(NBTCompound compound, ClientVersion version) {
return decoder.decode(compound, version);
}
@Override
public void encodeData(T data, ClientVersion version, NBTCompound compound) {
if (encoder != null) {
encoder.encode(data, version, compound);
} else if (!data.isEmpty()) {
throw new UnsupportedOperationException("Trying to encode non-empty data for " + this.getName());
}
}
@@ -107,8 +129,12 @@ public class ParticleTypes {
@Deprecated // Removed in 1.20.5
public static final ParticleType<ParticleData> AMBIENT_ENTITY_EFFECT = define("ambient_entity_effect");
public static final ParticleType<ParticleData> ANGRY_VILLAGER = define("angry_villager");
public static final ParticleType<ParticleBlockStateData> BLOCK = define("block", ParticleBlockStateData::read, ParticleBlockStateData::write);
public static final ParticleType<ParticleBlockStateData> BLOCK_MARKER = define("block_marker", ParticleBlockStateData::read, ParticleBlockStateData::write);
public static final ParticleType<ParticleBlockStateData> BLOCK = define("block",
ParticleBlockStateData::read, ParticleBlockStateData::write,
ParticleBlockStateData::decode, ParticleBlockStateData::encode);
public static final ParticleType<ParticleBlockStateData> BLOCK_MARKER = define("block_marker",
ParticleBlockStateData::read, ParticleBlockStateData::write,
ParticleBlockStateData::decode, ParticleBlockStateData::encode);
public static final ParticleType<ParticleData> BUBBLE = define("bubble");
public static final ParticleType<ParticleData> CLOUD = define("cloud");
public static final ParticleType<ParticleData> CRIT = define("crit");
@@ -119,23 +145,33 @@ public class ParticleTypes {
public static final ParticleType<ParticleData> LANDING_LAVA = define("landing_lava");
public static final ParticleType<ParticleData> DRIPPING_WATER = define("dripping_water");
public static final ParticleType<ParticleData> FALLING_WATER = define("falling_water");
public static final ParticleType<ParticleDustData> DUST = define("dust", ParticleDustData::read, ParticleDustData::write);
public static final ParticleType<ParticleDustColorTransitionData> DUST_COLOR_TRANSITION = define("dust_color_transition", ParticleDustColorTransitionData::read, ParticleDustColorTransitionData::write);
public static final ParticleType<ParticleDustData> DUST = define("dust",
ParticleDustData::read, ParticleDustData::write,
ParticleDustData::decode, ParticleDustData::encode);
public static final ParticleType<ParticleDustColorTransitionData> DUST_COLOR_TRANSITION = define("dust_color_transition",
ParticleDustColorTransitionData::read, ParticleDustColorTransitionData::write,
ParticleDustColorTransitionData::decode, ParticleDustColorTransitionData::encode);
public static final ParticleType<ParticleData> EFFECT = define("effect");
public static final ParticleType<ParticleData> ELDER_GUARDIAN = define("elder_guardian");
public static final ParticleType<ParticleData> ENCHANTED_HIT = define("enchanted_hit");
public static final ParticleType<ParticleData> ENCHANT = define("enchant");
public static final ParticleType<ParticleData> END_ROD = define("end_rod");
public static final ParticleType<ParticleColorData> ENTITY_EFFECT = define("entity_effect", ParticleColorData::read, ParticleColorData::write);
public static final ParticleType<ParticleColorData> ENTITY_EFFECT = define("entity_effect",
ParticleColorData::read, ParticleColorData::write,
ParticleColorData::decode, ParticleColorData::encode);
public static final ParticleType<ParticleData> EXPLOSION_EMITTER = define("explosion_emitter");
public static final ParticleType<ParticleData> EXPLOSION = define("explosion");
public static final ParticleType<ParticleData> SONIC_BOOM = define("sonic_boom");
public static final ParticleType<ParticleBlockStateData> FALLING_DUST = define("falling_dust", ParticleBlockStateData::read, ParticleBlockStateData::write);
public static final ParticleType<ParticleBlockStateData> FALLING_DUST = define("falling_dust",
ParticleBlockStateData::read, ParticleBlockStateData::write,
ParticleBlockStateData::decode, ParticleBlockStateData::encode);
public static final ParticleType<ParticleData> FIREWORK = define("firework");
public static final ParticleType<ParticleData> FISHING = define("fishing");
public static final ParticleType<ParticleData> FLAME = define("flame");
public static final ParticleType<ParticleData> SCULK_SOUL = define("sculk_soul");
public static final ParticleType<ParticleSculkChargeData> SCULK_CHARGE = define("sculk_charge", ParticleSculkChargeData::read, ParticleSculkChargeData::write);
public static final ParticleType<ParticleSculkChargeData> SCULK_CHARGE = define("sculk_charge",
ParticleSculkChargeData::read, ParticleSculkChargeData::write,
ParticleSculkChargeData::decode, ParticleSculkChargeData::encode);
public static final ParticleType<ParticleData> SCULK_CHARGE_POP = define("sculk_charge_pop");
public static final ParticleType<ParticleData> SOUL_FIRE_FLAME = define("soul_fire_flame");
public static final ParticleType<ParticleData> SOUL = define("soul");
@@ -144,8 +180,12 @@ public class ParticleTypes {
public static final ParticleType<ParticleData> COMPOSTER = define("composter");
public static final ParticleType<ParticleData> HEART = define("heart");
public static final ParticleType<ParticleData> INSTANT_EFFECT = define("instant_effect");
public static final ParticleType<ParticleItemStackData> ITEM = define("item", ParticleItemStackData::read, ParticleItemStackData::write);
public static final ParticleType<ParticleVibrationData> VIBRATION = define("vibration", ParticleVibrationData::read, ParticleVibrationData::write);
public static final ParticleType<ParticleItemStackData> ITEM = define("item",
ParticleItemStackData::read, ParticleItemStackData::write,
ParticleItemStackData::decode, ParticleItemStackData::encode);
public static final ParticleType<ParticleVibrationData> VIBRATION = define("vibration",
ParticleVibrationData::read, ParticleVibrationData::write,
ParticleVibrationData::decode, ParticleVibrationData::encode);
public static final ParticleType<ParticleData> ITEM_SLIME = define("item_slime");
public static final ParticleType<ParticleData> ITEM_SNOWBALL = define("item_snowball");
public static final ParticleType<ParticleData> LARGE_SMOKE = define("large_smoke");
@@ -197,7 +237,9 @@ public class ParticleTypes {
public static final ParticleType<ParticleData> WAX_OFF = define("wax_off");
public static final ParticleType<ParticleData> ELECTRIC_SPARK = define("electric_spark");
public static final ParticleType<ParticleData> SCRAPE = define("scrape");
public static final ParticleType<ParticleShriekData> SHRIEK = define("shriek", ParticleShriekData::read, ParticleShriekData::write);
public static final ParticleType<ParticleShriekData> SHRIEK = define("shriek",
ParticleShriekData::read, ParticleShriekData::write,
ParticleShriekData::decode, ParticleShriekData::encode);
//Added in 1.19.3, BUT REMOVED in 1.20, replaced with CHERRY_LEAVES
public static final ParticleType<ParticleData> DRIPPING_CHERRY_LEAVES = define("dripping_cherry_leaves");
public static final ParticleType<ParticleData> FALLING_CHERRY_LEAVES = define("falling_cherry_leaves");
@@ -224,13 +266,16 @@ public class ParticleTypes {
public static final ParticleType<ParticleData> ITEM_COBWEB = define("item_cobweb");
public static final ParticleType<ParticleData> TRIAL_SPAWNER_DETECTION_OMINOUS = define("trial_spawner_detection_ominous");
public static final ParticleType<ParticleData> VAULT_CONNECTION = define("vault_connection");
public static final ParticleType<ParticleBlockStateData> DUST_PILLAR = define("dust_pillar", ParticleBlockStateData::read, ParticleBlockStateData::write);
public static final ParticleType<ParticleBlockStateData> DUST_PILLAR = define("dust_pillar",
ParticleBlockStateData::read, ParticleBlockStateData::write,
ParticleBlockStateData::decode, ParticleBlockStateData::encode);
public static final ParticleType<ParticleData> OMINOUS_SPAWNING = define("ominous_spawning");
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() {
@@ -240,4 +285,16 @@ public class ParticleTypes {
static {
TYPES_BUILDER.unloadFileMappings();
}
@ApiStatus.Internal
@FunctionalInterface
public interface Decoder<T> {
T decode(NBTCompound compound, ClientVersion version);
}
@ApiStatus.Internal
@FunctionalInterface
public interface Encoder<T> {
void encode(T value, ClientVersion version, NBTCompound compound);
}
}
@@ -95,7 +95,10 @@ public enum ClientVersion {
* 1.20.5 and 1.20.6 have the same protocol version.
*/
V_1_20_5(766),
/**
* 1.21 and 1.21.1 have the same protocol version.
*/
V_1_21(767),
//TODO UPDATE Add new protocol version field
@@ -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
@@ -30,7 +30,12 @@ import com.github.retrooper.packetevents.protocol.chat.message.ChatMessage_v1_16
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTList;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionTypes;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChatMessage;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerCloseWindow;
@@ -40,23 +45,25 @@ import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSe
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSystemChatMessage;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTitle;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class User {
public class User implements IRegistryHolder {
private final Object channel;
private ConnectionState decoderState;
private ConnectionState encoderState;
private ClientVersion clientVersion;
private final UserProfile profile;
private int entityId = -1;
private int minWorldHeight = 0;
private int totalWorldHeight = 256;
private List<NBTCompound> worldNBT;
private Dimension dimension = new Dimension(0);
private DimensionType dimensionType = DimensionTypes.OVERWORLD;
private final Map<ResourceLocation, IRegistry<?>> registries = new HashMap<>();
public User(Object channel,
ConnectionState connectionState, ClientVersion clientVersion,
@@ -68,6 +75,17 @@ public class User {
this.profile = profile;
}
@ApiStatus.Internal
@Override
public @Nullable IRegistry<?> getRegistry(ResourceLocation registryKey, ClientVersion version) {
return this.registries.get(registryKey);
}
@ApiStatus.Internal
public void putRegistry(IRegistry<?> registry) {
this.registries.put(registry.getRegistryKey(), registry);
}
public Object getChannel() {
return channel;
}
@@ -249,116 +267,105 @@ public class User {
//TODO sendTitle that is cross-version
// dimension type related methods
public int getMinWorldHeight() {
return minWorldHeight;
return this.getMinWorldHeight(null);
}
public void setMinWorldHeight(int minWorldHeight) {
this.minWorldHeight = minWorldHeight;
public int getMinWorldHeight(@Nullable ClientVersion version) {
if (version == null) {
version = PacketEvents.getAPI().getInjector().isProxy() ? this.getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
}
return this.dimensionType.getMinY(version);
}
public int getTotalWorldHeight() {
return totalWorldHeight;
return this.getTotalWorldHeight(null);
}
public int getTotalWorldHeight(@Nullable ClientVersion version) {
if (version == null) {
version = PacketEvents.getAPI().getInjector().isProxy() ? this.getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
}
return this.dimensionType.getHeight(version);
}
public DimensionType getDimensionType() {
return this.dimensionType;
}
@ApiStatus.Internal
public void setDimensionType(DimensionType dimensionType) {
this.dimensionType = dimensionType;
}
// legacy dimension type related methods
@Deprecated
public void setMinWorldHeight(int minWorldHeight) {
throw new UnsupportedOperationException();
}
@Deprecated
public void setTotalWorldHeight(int totalWorldHeight) {
this.totalWorldHeight = totalWorldHeight;
throw new UnsupportedOperationException();
}
@Deprecated
public void switchDimensionType(ServerVersion version, Dimension dimension) {
NBTCompound dimensionData = this.getWorldNBT(dimension);
if (dimensionData != null) {
NBTCompound worldNBT = dimensionData.getCompoundTagOrNull("element");
if (worldNBT != null) {
this.setMinWorldHeight(worldNBT.getNumberTagOrNull("min_y").getAsInt());
this.setTotalWorldHeight(worldNBT.getNumberTagOrNull("height").getAsInt());
return;
} else {
this.setDefaultWorldHeights(version, dimension);
}
}
if (version.isOlderThan(ServerVersion.V_1_20_5)
|| this.clientVersion.isOlderThan(ClientVersion.V_1_20_5)) {
// hide this warning on 1.20.5, as the server does no longer send
// vanilla datapack dimension type contents
//
// this 1.20.5 feature can be fully worked around with by clearing the
// known packs sent by the client to the server durings config phase
PacketEvents.getAPI().getLogger().warning(
"No data was sent for dimension " + dimensionData + " to " + this.getName());
}
throw new UnsupportedOperationException();
}
@Deprecated
public void setDefaultWorldHeights(ServerVersion version, Dimension dimension) {
boolean extended = version.isNewerThanOrEquals(ServerVersion.V_1_18)
&& "minecraft:overworld".equals(this.getWorldName(dimension));
this.setDefaultWorldHeights(extended);
throw new UnsupportedOperationException();
}
@Deprecated
public void setDefaultWorldHeights(boolean extended) {
this.minWorldHeight = extended ? -64 : 0;
this.totalWorldHeight = extended ? 256 + 128 : 256;
throw new UnsupportedOperationException();
}
@Deprecated
public void setWorldNBT(NBTList<NBTCompound> worldNBT) {
this.worldNBT = worldNBT.getTags();
throw new UnsupportedOperationException();
}
@Deprecated
public Dimension getDimension() {
return dimension;
return Dimension.fromDimensionType(this.dimensionType, this, null);
}
@Deprecated
public void setDimension(Dimension dimension) {
this.dimension = dimension;
this.dimensionType = dimension.asDimensionType(this, null);
}
@Nullable
public NBTCompound getWorldNBT(String worldName) {
if (worldNBT == null) {
return null;
}
for (NBTCompound compound : worldNBT) {
if (compound.getStringTagOrNull("name").getValue().equals(worldName)) {
return compound;
}
}
return null;
@Deprecated
public @Nullable NBTCompound getWorldNBT(String worldName) {
throw new UnsupportedOperationException();
}
@Deprecated
public @Nullable NBTCompound getWorldNBT(int worldId) {
if (this.worldNBT == null) {
return null;
}
for (NBTCompound element : this.worldNBT) {
if (element.getNumberTagOrNull("id").getAsInt() == worldId) {
return element;
}
}
return null;
throw new UnsupportedOperationException();
}
@Deprecated
public @Nullable NBTCompound getWorldNBT(Dimension dimension) {
String dimensionName = dimension.getDimensionName();
if (!dimensionName.isEmpty()) {
return this.getWorldNBT(dimensionName);
}
return this.getWorldNBT(dimension.getId());
throw new UnsupportedOperationException();
}
@Deprecated
public @Nullable String getWorldName(int worldId) {
if (this.worldNBT == null) {
return null;
}
for (NBTCompound element : this.worldNBT) {
if (element.getNumberTagOrNull("id").getAsInt() == worldId) {
return element.getStringTagValueOrNull("name");
}
}
return null;
throw new UnsupportedOperationException();
}
public @Nullable String getWorldName(Dimension dimension) {
String dimensionName = dimension.getDimensionName();
return dimensionName.isEmpty() ? this.getWorldName(dimension.getId()) : dimensionName;
@Deprecated
public String getWorldName(Dimension dimension) {
throw new UnsupportedOperationException();
}
}
@@ -32,6 +32,7 @@ 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.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
@@ -152,8 +153,8 @@ public class RecipeSerializers {
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",
@ApiStatus.Obsolete
public static final RecipeSerializer<SmithingRecipeData> SMITHING = define("smithing",
ew -> SmithingRecipeData.read(ew, true),
(ew, data) -> SmithingRecipeData.write(ew, data, true),
RecipeType.SMITHING);
@@ -19,10 +19,18 @@
package com.github.retrooper.packetevents.protocol.sound;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public interface Sound extends MappedEntity {
ResourceLocation getSoundId();
@@ -48,4 +56,27 @@ public interface Sound extends MappedEntity {
wrapper.writeIdentifier(sound.getSoundId());
wrapper.writeOptional(sound.getRange(), PacketWrapper::writeFloat);
}
static Sound decode(NBT nbt, ClientVersion version) {
if (nbt instanceof NBTString) {
return Sounds.getByNameOrCreate(((NBTString) nbt).getValue());
}
NBTCompound compound = (NBTCompound) nbt;
ResourceLocation soundId = new ResourceLocation(((NBTCompound) nbt).getStringTagValueOrThrow("sound_id"));
Float range = Optional.ofNullable(compound.getNumberTagOrNull("range"))
.map(NBTNumber::getAsFloat).orElse(null);
return new StaticSound(soundId, range);
}
static NBT encode(Sound sound, ClientVersion version) {
if (sound.isRegistered()) {
return new NBTString(sound.getName().toString());
}
NBTCompound compound = new NBTCompound();
compound.setTag("sound_id", new NBTString(sound.getSoundId().toString()));
if (sound.getRange() != null) {
compound.setTag("range", new NBTFloat(sound.getRange()));
}
return compound;
}
}
@@ -76,6 +76,14 @@ public class Sounds {
return soundType;
}
public static Sound getByNameOrCreate(String name) {
Sound builtinSound = getByName(name);
if (builtinSound == null) {
return new StaticSound(new ResourceLocation(name), null);
}
return builtinSound;
}
public static @Nullable Sound getByName(String name) {
return SOUND_TYPE_MAP.get(name);
}
@@ -21,12 +21,13 @@ package com.github.retrooper.packetevents.protocol.stats;
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.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
import com.github.retrooper.packetevents.util.adventure.AdventureSerializer;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import net.kyori.adventure.text.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -43,38 +44,46 @@ public class Statistics {
if (version.isOlderThan(ServerVersion.V_1_12_2)) {
NBTCompound mapping = MappingHelper.decompress("mappings/stats/statistics");
try (final SequentialNBTReader.Compound mapping = MappingHelper.decompress("mappings/stats/statistics")) {
mapping.skipOne(); //Skip version
if (version.isOlderThanOrEquals(ServerVersion.V_1_8_3)) {
mapping = mapping.getCompoundTagOrThrow("V_1_8");
} else {
mapping = mapping.getCompoundTagOrThrow("V_1_12");
}
if (version.isOlderThanOrEquals(ServerVersion.V_1_8_3)) {
mapping.skipOne(); // Skip to version 1.8
}
for (Map.Entry<String, NBT> entry : mapping.getTags().entrySet()) {
Component value = AdventureSerializer.parseComponent(((NBTString) entry.getValue()).getValue());
SequentialNBTReader.Compound toLoad = (SequentialNBTReader.Compound) mapping.next();
Statistic statistic = new Statistic() {
@Override
public String getId() {
return entry.getKey();
}
for (Map.Entry<String, NBT> entry : toLoad) {
String value = ((NBTString) entry.getValue()).getValue();
Statistic statistic = new Statistic() {
private Component cachedDisplay;
@Override
public Component display() {
return value;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Statistic) {
return ((Statistic) obj).getId().equals(this.getId());
@Override
public String getId() {
return entry.getKey();
}
return false;
}
};
STATISTIC_MAP.put(entry.getKey(), statistic);
@Override
public Component display() {
if (cachedDisplay == null) {
cachedDisplay = AdventureSerializer.parseComponent(value);
}
return cachedDisplay;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Statistic) {
return ((Statistic) obj).getId().equals(this.getId());
}
return false;
}
};
STATISTIC_MAP.put(entry.getKey(), statistic);
}
} catch (IOException e) {
throw new RuntimeException("Cannot load statistics mappings", e);
}
}
}
@@ -18,9 +18,21 @@
package com.github.retrooper.packetevents.protocol.world;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionTypeRef;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionTypes;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import org.jetbrains.annotations.Nullable;
/**
* @deprecated use {@link com.github.retrooper.packetevents.protocol.world.dimension.DimensionType instead}
*/
@Deprecated
public class Dimension {
private int id;
private NBTCompound attributes;
@@ -40,6 +52,68 @@ public class Dimension {
this.attributes = attributes;
}
@Deprecated
public Dimension(int id, NBTCompound attributes) {
this.id = id;
this.attributes = attributes;
}
public static Dimension fromDimensionTypeRef(DimensionTypeRef ref) {
if (ref instanceof DimensionTypeRef.NameRef) {
Dimension dimension = new Dimension(0);
dimension.setDimensionName(ref.getName().toString());
return dimension;
} else if (ref instanceof DimensionTypeRef.IdRef) {
return new Dimension(ref.getId());
} else if (ref instanceof DimensionTypeRef.DataRef) {
return new Dimension((NBTCompound) ref.getData());
} else if (ref instanceof DimensionTypeRef.DirectRef) {
return fromDimensionType(((DimensionTypeRef.DirectRef) ref).getDimensionType(),
null, ((DimensionTypeRef.DirectRef) ref).getVersion());
} else {
throw new UnsupportedOperationException("Unsupported DimensionTypeRef implementation: " + ref);
}
}
public static Dimension fromDimensionType(
com.github.retrooper.packetevents.protocol.world.dimension.DimensionType dimensionType,
@Nullable User user, @Nullable ClientVersion version
) {
if (version == null) {
version = user != null && PacketEvents.getAPI().getInjector().isProxy() ? user.getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
}
NBTCompound encodedType = (NBTCompound) com.github.retrooper.packetevents.protocol.world.dimension.DimensionType.encode(dimensionType, version);
return new Dimension(dimensionType.getId(version), encodedType);
}
public DimensionTypeRef asDimensionTypeRef() {
if (this.attributes == null) {
return new DimensionTypeRef.IdRef(this.id);
} else if (this.attributes.size() > 1) {
// seems to contain more than just "effects"
return new DimensionTypeRef.DataRef(this.attributes);
} else {
ResourceLocation dimensionName = new ResourceLocation(this.getDimensionName());
return new DimensionTypeRef.NameRef(dimensionName);
}
}
public com.github.retrooper.packetevents.protocol.world.dimension.DimensionType asDimensionType(
@Nullable User user, @Nullable ClientVersion version) {
IRegistry<com.github.retrooper.packetevents.protocol.world.dimension.DimensionType> registry = user != null
? user.getRegistryOr(DimensionTypes.getRegistry()) : DimensionTypes.getRegistry();
String dimName = this.getDimensionName();
if (!dimName.isEmpty()) {
return registry.getByName(new ResourceLocation(dimName));
}
if (version == null) {
version = user != null && PacketEvents.getAPI().getInjector().isProxy() ? user.getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
}
return registry.getById(version, this.id);
}
public String getDimensionName() {
return getAttributes().getStringTagValueOrDefault("effects", "");
}
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.world;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Vector3i;
import org.jetbrains.annotations.NotNull;
@@ -36,9 +37,13 @@ public class WorldBlockPosition {
this.blockPosition = new Vector3i(x, y, z);
}
@Deprecated
public WorldBlockPosition(@NotNull Dimension dimension, @NotNull Vector3i blockPosition) {
this.world = new ResourceLocation(dimension.getDimensionName());
this.blockPosition = blockPosition;
this(new ResourceLocation(dimension.getDimensionName()), blockPosition);
}
public WorldBlockPosition(@NotNull DimensionType dimensionType, @NotNull Vector3i blockPosition) {
this(dimensionType.getName(), blockPosition);
}
public ResourceLocation getWorld() {
@@ -0,0 +1,188 @@
/*
* 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.biome;
import com.github.retrooper.packetevents.protocol.mapper.CopyableEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTFloat;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.util.Index;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
public interface Biome extends MappedEntity, CopyableEntity<Biome> {
boolean hasPrecipitation();
@ApiStatus.Obsolete(since = "1.19.3")
Precipitation getPrecipitation();
float getTemperature();
TemperatureModifier getTemperatureModifier();
float getDownfall();
@ApiStatus.Obsolete(since = "1.19")
@Nullable Category getCategory();
@ApiStatus.Obsolete(since = "1.18")
@Nullable Float getDepth();
@ApiStatus.Obsolete(since = "1.18")
@Nullable Float getScale();
BiomeEffects getEffects();
static Biome decode(NBT nbt, ClientVersion version, @Nullable TypesBuilderData data) {
NBTCompound compound = (NBTCompound) nbt;
float temperature = compound.getNumberTagOrThrow("temperature").getAsFloat();
TemperatureModifier temperatureModifier =
Optional.ofNullable(compound.getStringTagValueOrNull("temperature_modifier"))
.map(id -> indexValueOrThrow(TemperatureModifier.ID_INDEX, id))
.orElse(TemperatureModifier.NONE);
float downfall = compound.getNumberTagOrThrow("downfall").getAsFloat();
boolean precipitation = version.isNewerThan(ClientVersion.V_1_19_3) ? compound.getBoolean("has_precipitation") :
indexValueOrThrow(Precipitation.ID_INDEX, compound.getStringTagValueOrThrow("precipitation")) != Precipitation.NONE;
BiomeEffects effects = BiomeEffects.decode(compound.getTagOrThrow("effects"), version);
// removed with 1.19
Category category = version.isNewerThanOrEquals(ClientVersion.V_1_19) ? null :
indexValueOrThrow(Category.ID_INDEX, compound.getStringTagValueOrThrow("category"));
Float depth = version.isNewerThanOrEquals(ClientVersion.V_1_18) ? null :
compound.getNumberTagOrThrow("depth").getAsFloat();
Float scale = version.isNewerThanOrEquals(ClientVersion.V_1_18) ? null :
compound.getNumberTagOrThrow("scale").getAsFloat();
return new StaticBiome(data, precipitation, temperature, temperatureModifier, downfall,
category, depth, scale, effects);
}
static NBT encode(Biome biome, ClientVersion version) {
NBTCompound compound = new NBTCompound();
if (version.isNewerThan(ClientVersion.V_1_19_3)) {
compound.setTag("has_precipitation", new NBTByte(biome.hasPrecipitation()));
} else {
compound.setTag("precipitation", new NBTString(biome.getPrecipitation().getId()));
}
compound.setTag("temperature", new NBTFloat(biome.getTemperature()));
if (biome.getTemperatureModifier() != TemperatureModifier.NONE) {
compound.setTag("temperature_modifier", new NBTString(biome.getTemperatureModifier().getId()));
}
compound.setTag("downfall", new NBTFloat(biome.getDownfall()));
if (version.isOlderThan(ClientVersion.V_1_19)) {
if (biome.getCategory() != null) {
compound.setTag("category", new NBTString(biome.getCategory().getId()));
}
if (version.isOlderThan(ClientVersion.V_1_18)) {
if (biome.getDepth() != null) {
compound.setTag("depth", new NBTFloat(biome.getDepth()));
}
if (biome.getScale() != null) {
compound.setTag("scale", new NBTFloat(biome.getScale()));
}
}
}
compound.setTag("effects", BiomeEffects.encode(biome.getEffects(), version));
return compound;
}
@ApiStatus.Obsolete(since = "1.19")
enum Category {
NONE("none"),
TAIGA("taiga"),
EXTREME_HILLS("extreme_hills"),
JUNGLE("jungle"),
MESA("mesa"),
PLAINS("plains"),
SAVANNA("savanna"),
ICY("icy"),
THE_END("the_end"),
BEACH("beach"),
FOREST("forest"),
OCEAN("ocean"),
DESERT("desert"),
RIVER("river"),
SWAMP("swamp"),
MUSHROOM("mushroom"),
NETHER("nether"),
UNDERGROUND("underground"),
MOUNTAIN("mountain");
public static final Index<String, Category> ID_INDEX = Index.create(Category.class,
Category::getId);
private final String id;
Category(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
@ApiStatus.Obsolete(since = "1.19.3")
enum Precipitation {
NONE("none"),
RAIN("rain"),
SNOW("snow");
public static final Index<String, Precipitation> ID_INDEX = Index.create(Precipitation.class,
Precipitation::getId);
private final String id;
Precipitation(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
enum TemperatureModifier {
NONE("none"),
FROZEN("frozen");
public static final Index<String, TemperatureModifier> ID_INDEX = Index.create(
TemperatureModifier.class, TemperatureModifier::getId);
private final String id;
TemperatureModifier(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
}
@@ -0,0 +1,432 @@
/*
* 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.biome;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
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.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.particle.Particle;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import net.kyori.adventure.util.Index;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
public class BiomeEffects {
private final int fogColor;
private final int waterColor;
private final int waterFogColor;
private final int skyColor;
private final OptionalInt foliageColor;
private final OptionalInt grassColor;
private final GrassColorModifier grassColorModifier;
private final Optional<ParticleSettings> particle;
private final Optional<Sound> ambientSound;
private final Optional<MoodSettings> moodSound;
private final Optional<AdditionsSettings> additionsSound;
private final Optional<MusicSettings> music;
public BiomeEffects(
int fogColor, int waterColor, int waterFogColor, int skyColor,
OptionalInt foliageColor, OptionalInt grassColor,
GrassColorModifier grassColorModifier,
Optional<ParticleSettings> particle,
Optional<Sound> ambientSound,
Optional<MoodSettings> moodSound,
Optional<AdditionsSettings> additionsSound,
Optional<MusicSettings> music
) {
this.fogColor = fogColor;
this.waterColor = waterColor;
this.waterFogColor = waterFogColor;
this.skyColor = skyColor;
this.foliageColor = foliageColor;
this.grassColor = grassColor;
this.grassColorModifier = grassColorModifier;
this.particle = particle;
this.ambientSound = ambientSound;
this.moodSound = moodSound;
this.additionsSound = additionsSound;
this.music = music;
}
public static BiomeEffects decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
int fogColor = compound.getNumberTagOrThrow("fog_color").getAsInt();
int waterColor = compound.getNumberTagOrThrow("water_color").getAsInt();
int waterFogColor = compound.getNumberTagOrThrow("water_fog_color").getAsInt();
int skyColor = compound.getNumberTagOrThrow("sky_color").getAsInt();
OptionalInt foliageColor = Optional.ofNullable(compound.getNumberTagOrNull("foliage_color"))
.map(NBTNumber::getAsInt).map(OptionalInt::of).orElseGet(OptionalInt::empty);
OptionalInt grassColor = Optional.ofNullable(compound.getNumberTagOrNull("grass_color"))
.map(NBTNumber::getAsInt).map(OptionalInt::of).orElseGet(OptionalInt::empty);
GrassColorModifier grassColorModifier = Optional.ofNullable(compound.getStringTagValueOrNull("grass_color_modifier"))
.map(id -> indexValueOrThrow(GrassColorModifier.ID_INDEX, id)).orElse(GrassColorModifier.NONE);
Optional<ParticleSettings> particle = Optional.ofNullable(compound.getTagOrNull("particle"))
.map(tag -> ParticleSettings.decode(tag, version));
Optional<Sound> ambientSound = Optional.ofNullable(compound.getTagOrNull("ambient_sound"))
.map(tag -> Sound.decode(tag, version));
Optional<MoodSettings> moodSound = Optional.ofNullable(compound.getTagOrNull("mood_sound"))
.map(tag -> MoodSettings.decode(tag, version));
Optional<AdditionsSettings> additionsSound = Optional.ofNullable(compound.getTagOrNull("additions_sound"))
.map(tag -> AdditionsSettings.decode(tag, version));
Optional<MusicSettings> music = Optional.ofNullable(compound.getTagOrNull("music"))
.map(tag -> MusicSettings.decode(tag, version));
return new BiomeEffects(fogColor, waterColor, waterFogColor, skyColor, foliageColor, grassColor,
grassColorModifier, particle, ambientSound, moodSound, additionsSound, music);
}
public static NBT encode(BiomeEffects effects, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("fog_color", new NBTInt(effects.fogColor));
compound.setTag("water_color", new NBTInt(effects.waterColor));
compound.setTag("water_fog_color", new NBTInt(effects.waterFogColor));
compound.setTag("sky_color", new NBTInt(effects.skyColor));
effects.foliageColor.ifPresent(color ->
compound.setTag("foliage_color", new NBTInt(color)));
effects.grassColor.ifPresent(color ->
compound.setTag("grass_color", new NBTInt(color)));
if (effects.grassColorModifier != GrassColorModifier.NONE) {
compound.setTag("grass_color_modifier", new NBTString(effects.grassColorModifier.getId()));
}
effects.particle.ifPresent(settings -> compound.setTag(
"particle", ParticleSettings.encode(settings, version)));
effects.ambientSound.ifPresent(sound -> compound.setTag(
"ambient_sound", Sound.encode(sound, version)));
effects.moodSound.ifPresent(sound -> compound.setTag(
"mood_sound", MoodSettings.encode(sound, version)));
effects.additionsSound.ifPresent(sound -> compound.setTag(
"additions_sound", AdditionsSettings.encode(sound, version)));
effects.music.ifPresent(music -> compound.setTag(
"music", MusicSettings.encode(music, version)));
return compound;
}
public int getFogColor() {
return this.fogColor;
}
public int getWaterColor() {
return this.waterColor;
}
public int getWaterFogColor() {
return this.waterFogColor;
}
public int getSkyColor() {
return this.skyColor;
}
public OptionalInt getFoliageColor() {
return this.foliageColor;
}
public OptionalInt getGrassColor() {
return this.grassColor;
}
public GrassColorModifier getGrassColorModifier() {
return this.grassColorModifier;
}
public Optional<ParticleSettings> getParticle() {
return this.particle;
}
public Optional<Sound> getAmbientSound() {
return this.ambientSound;
}
public Optional<MoodSettings> getMoodSound() {
return this.moodSound;
}
public Optional<MusicSettings> getMusic() {
return this.music;
}
public Optional<AdditionsSettings> getAdditionsSound() {
return this.additionsSound;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof BiomeEffects)) return false;
BiomeEffects that = (BiomeEffects) obj;
if (this.fogColor != that.fogColor) return false;
if (this.waterColor != that.waterColor) return false;
if (this.waterFogColor != that.waterFogColor) return false;
if (this.skyColor != that.skyColor) return false;
if (!this.foliageColor.equals(that.foliageColor)) return false;
if (!this.grassColor.equals(that.grassColor)) return false;
if (this.grassColorModifier != that.grassColorModifier) return false;
if (!this.particle.equals(that.particle)) return false;
if (!this.ambientSound.equals(that.ambientSound)) return false;
if (!this.moodSound.equals(that.moodSound)) return false;
if (!this.additionsSound.equals(that.additionsSound)) return false;
return this.music.equals(that.music);
}
@Override
public int hashCode() {
return Objects.hash(this.fogColor, this.waterColor, this.waterFogColor, this.skyColor, this.foliageColor, this.grassColor, this.grassColorModifier, this.particle, this.ambientSound, this.moodSound, this.additionsSound, this.music);
}
public enum GrassColorModifier {
NONE("none"),
DARK_FOREST("dark_forest"),
SWAMP("swamp");
public static final Index<String, GrassColorModifier> ID_INDEX = Index.create(
GrassColorModifier.class, GrassColorModifier::getId);
private final String id;
GrassColorModifier(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
public static final class ParticleSettings {
private final Particle<?> particle;
private final float probability;
public ParticleSettings(Particle<?> particle, float probability) {
this.particle = particle;
this.probability = probability;
}
public static ParticleSettings decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
Particle<?> particle = Particle.decode(compound.getTagOrNull("options"), version);
float probability = compound.getNumberTagOrThrow("probability").getAsFloat();
return new ParticleSettings(particle, probability);
}
public static NBT encode(ParticleSettings settings, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("options", Particle.encode(settings.particle, version));
compound.setTag("probability", new NBTFloat(settings.probability));
return compound;
}
public Particle<?> getParticle() {
return this.particle;
}
public float getProbability() {
return this.probability;
}
}
public static final class MoodSettings {
private final Sound sound;
private final int tickDelay;
private final int blockSearchExtent;
private final double soundOffset;
public MoodSettings(Sound sound, int tickDelay, int blockSearchExtent, double soundOffset) {
this.sound = sound;
this.tickDelay = tickDelay;
this.blockSearchExtent = blockSearchExtent;
this.soundOffset = soundOffset;
}
public static MoodSettings decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound"), version);
int tickDelay = compound.getNumberTagOrThrow("tick_delay").getAsInt();
int blockSearchExtent = compound.getNumberTagOrThrow("block_search_extent").getAsInt();
double soundOffset = compound.getNumberTagOrThrow("offset").getAsDouble();
return new MoodSettings(sound, tickDelay, blockSearchExtent, soundOffset);
}
public static NBT encode(MoodSettings settings, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound", Sound.encode(settings.sound, version));
compound.setTag("tick_delay", new NBTInt(settings.tickDelay));
compound.setTag("block_search_extent", new NBTInt(settings.blockSearchExtent));
compound.setTag("offset", new NBTDouble(settings.soundOffset));
return compound;
}
public Sound getSound() {
return this.sound;
}
public int getTickDelay() {
return this.tickDelay;
}
public int getBlockSearchExtent() {
return this.blockSearchExtent;
}
public double getSoundOffset() {
return this.soundOffset;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof MoodSettings)) return false;
MoodSettings that = (MoodSettings) obj;
if (this.tickDelay != that.tickDelay) return false;
if (this.blockSearchExtent != that.blockSearchExtent) return false;
if (Double.compare(that.soundOffset, this.soundOffset) != 0) return false;
return this.sound.equals(that.sound);
}
@Override
public int hashCode() {
return Objects.hash(this.sound, this.tickDelay, this.blockSearchExtent, this.soundOffset);
}
}
public static final class AdditionsSettings {
private final Sound sound;
private final double tickChance;
public AdditionsSettings(Sound sound, double tickChance) {
this.sound = sound;
this.tickChance = tickChance;
}
public static AdditionsSettings decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound"), version);
double tickChance = compound.getNumberTagOrThrow("tick_chance").getAsDouble();
return new AdditionsSettings(sound, tickChance);
}
public static NBT encode(AdditionsSettings settings, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound", Sound.encode(settings.sound, version));
compound.setTag("tick_chance", new NBTDouble(settings.tickChance));
return compound;
}
public Sound getSound() {
return this.sound;
}
public double getTickChance() {
return this.tickChance;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof AdditionsSettings)) return false;
AdditionsSettings that = (AdditionsSettings) obj;
if (Double.compare(that.tickChance, this.tickChance) != 0) return false;
return this.sound.equals(that.sound);
}
@Override
public int hashCode() {
return Objects.hash(this.sound, this.tickChance);
}
}
public static final class MusicSettings {
private final Sound sound;
private final int minDelay;
private final int maxDelay;
private final boolean replaceMusic;
public MusicSettings(Sound sound, int minDelay, int maxDelay, boolean replaceMusic) {
this.sound = sound;
this.minDelay = minDelay;
this.maxDelay = maxDelay;
this.replaceMusic = replaceMusic;
}
public static MusicSettings decode(NBT nbt, ClientVersion version) {
NBTCompound compound = (NBTCompound) nbt;
Sound sound = Sound.decode(compound.getTagOrThrow("sound"), version);
int minDelay = compound.getNumberTagOrThrow("min_delay").getAsInt();
int maxDelay = compound.getNumberTagOrThrow("max_delay").getAsInt();
boolean replaceMusic = compound.getBoolean("replace_current_music");
return new MusicSettings(sound, minDelay, maxDelay, replaceMusic);
}
public static NBT encode(MusicSettings settings, ClientVersion version) {
NBTCompound compound = new NBTCompound();
compound.setTag("sound", Sound.encode(settings.sound, version));
compound.setTag("min_delay", new NBTInt(settings.minDelay));
compound.setTag("max_delay", new NBTInt(settings.maxDelay));
compound.setTag("replace_current_music", new NBTByte(settings.replaceMusic));
return compound;
}
public Sound getSound() {
return this.sound;
}
public int getMinDelay() {
return this.minDelay;
}
public int getMaxDelay() {
return this.maxDelay;
}
public boolean isReplaceMusic() {
return this.replaceMusic;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof MusicSettings)) return false;
MusicSettings that = (MusicSettings) obj;
if (this.minDelay != that.minDelay) return false;
if (this.maxDelay != that.maxDelay) return false;
if (this.replaceMusic != that.replaceMusic) return false;
return this.sound.equals(that.sound);
}
@Override
public int hashCode() {
return Objects.hash(this.sound, this.minDelay, this.maxDelay, this.replaceMusic);
}
}
}
@@ -0,0 +1,145 @@
/*
* 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.biome;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
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.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public final class Biomes {
private static final VersionedRegistry<Biome> REGISTRY = new VersionedRegistry<>(
"worldgen/biome", "world/biome_mappings");
// load data from file, biomes are too complex to define in code here
private static final Map<ResourceLocation, NBTCompound> BIOME_DATA;
static {
BIOME_DATA = new HashMap<>();
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/world/biome_data")) {
while (dataTag.hasNext()) {
Map.Entry<String, NBT> entry = dataTag.next();
if (entry.getKey().equals("version")) {
continue; // skip version field
}
ResourceLocation biomeKey = new ResourceLocation(entry.getKey());
BIOME_DATA.put(biomeKey, ((SequentialNBTReader.Compound) entry.getValue()).readFully());
}
} catch (IOException exception) {
throw new RuntimeException("Error while reading biome data", exception);
}
}
private Biomes() {
}
@ApiStatus.Internal
public static Biome define(String key) {
return REGISTRY.define(key, data -> {
NBTCompound dataTag = BIOME_DATA.get(data.getName());
if (dataTag == null) {
throw new IllegalArgumentException("Can't define biome " + data.getName() + ", no data found");
}
return Biome.decode(dataTag, ClientVersion.getLatest(), data);
});
}
public static VersionedRegistry<Biome> getRegistry() {
return REGISTRY;
}
public static final Biome BADLANDS = define("badlands");
public static final Biome BAMBOO_JUNGLE = define("bamboo_jungle");
public static final Biome BASALT_DELTAS = define("basalt_deltas");
public static final Biome BEACH = define("beach");
public static final Biome BIRCH_FOREST = define("birch_forest");
public static final Biome CHERRY_GROVE = define("cherry_grove");
public static final Biome COLD_OCEAN = define("cold_ocean");
public static final Biome CRIMSON_FOREST = define("crimson_forest");
public static final Biome DARK_FOREST = define("dark_forest");
public static final Biome DEEP_COLD_OCEAN = define("deep_cold_ocean");
public static final Biome DEEP_DARK = define("deep_dark");
public static final Biome DEEP_FROZEN_OCEAN = define("deep_frozen_ocean");
public static final Biome DEEP_LUKEWARM_OCEAN = define("deep_lukewarm_ocean");
public static final Biome DEEP_OCEAN = define("deep_ocean");
public static final Biome DESERT = define("desert");
public static final Biome DRIPSTONE_CAVES = define("dripstone_caves");
public static final Biome END_BARRENS = define("end_barrens");
public static final Biome END_HIGHLANDS = define("end_highlands");
public static final Biome END_MIDLANDS = define("end_midlands");
public static final Biome ERODED_BADLANDS = define("eroded_badlands");
public static final Biome FLOWER_FOREST = define("flower_forest");
public static final Biome FOREST = define("forest");
public static final Biome FROZEN_OCEAN = define("frozen_ocean");
public static final Biome FROZEN_PEAKS = define("frozen_peaks");
public static final Biome FROZEN_RIVER = define("frozen_river");
public static final Biome GROVE = define("grove");
public static final Biome ICE_SPIKES = define("ice_spikes");
public static final Biome JAGGED_PEAKS = define("jagged_peaks");
public static final Biome JUNGLE = define("jungle");
public static final Biome LUKEWARM_OCEAN = define("lukewarm_ocean");
public static final Biome LUSH_CAVES = define("lush_caves");
public static final Biome MANGROVE_SWAMP = define("mangrove_swamp");
public static final Biome MEADOW = define("meadow");
public static final Biome MUSHROOM_FIELDS = define("mushroom_fields");
public static final Biome NETHER_WASTES = define("nether_wastes");
public static final Biome OCEAN = define("ocean");
public static final Biome OLD_GROWTH_BIRCH_FOREST = define("old_growth_birch_forest");
public static final Biome OLD_GROWTH_PINE_TAIGA = define("old_growth_pine_taiga");
public static final Biome OLD_GROWTH_SPRUCE_TAIGA = define("old_growth_spruce_taiga");
public static final Biome PLAINS = define("plains");
public static final Biome RIVER = define("river");
public static final Biome SAVANNA = define("savanna");
public static final Biome SAVANNA_PLATEAU = define("savanna_plateau");
public static final Biome SMALL_END_ISLANDS = define("small_end_islands");
public static final Biome SNOWY_BEACH = define("snowy_beach");
public static final Biome SNOWY_PLAINS = define("snowy_plains");
public static final Biome SNOWY_SLOPES = define("snowy_slopes");
public static final Biome SNOWY_TAIGA = define("snowy_taiga");
public static final Biome SOUL_SAND_VALLEY = define("soul_sand_valley");
public static final Biome SPARSE_JUNGLE = define("sparse_jungle");
public static final Biome STONY_PEAKS = define("stony_peaks");
public static final Biome STONY_SHORE = define("stony_shore");
public static final Biome SUNFLOWER_PLAINS = define("sunflower_plains");
public static final Biome SWAMP = define("swamp");
public static final Biome TAIGA = define("taiga");
public static final Biome THE_END = define("the_end");
public static final Biome THE_VOID = define("the_void");
public static final Biome WARM_OCEAN = define("warm_ocean");
public static final Biome WARPED_FOREST = define("warped_forest");
public static final Biome WINDSWEPT_FOREST = define("windswept_forest");
public static final Biome WINDSWEPT_GRAVELLY_HILLS = define("windswept_gravelly_hills");
public static final Biome WINDSWEPT_HILLS = define("windswept_hills");
public static final Biome WINDSWEPT_SAVANNA = define("windswept_savanna");
public static final Biome WOODED_BADLANDS = define("wooded_badlands");
static {
BIOME_DATA.clear();
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,180 @@
/*
* 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.biome;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticBiome extends AbstractMappedEntity implements Biome {
private final boolean precipitation;
private final float temperature;
private final TemperatureModifier temperatureModifier;
private final float downfall;
private final @Nullable Category category;
private final @Nullable Float depth;
private final @Nullable Float scale;
private final BiomeEffects effects;
public StaticBiome(
boolean precipitation, float temperature, TemperatureModifier temperatureModifier,
float downfall, BiomeEffects effects
) {
this(null, precipitation, temperature, temperatureModifier, downfall, effects);
}
@ApiStatus.Obsolete
public StaticBiome(@Nullable TypesBuilderData data, Precipitation precipitation, float temperature,
TemperatureModifier temperatureModifier, float downfall, BiomeEffects effects) {
this(data, precipitation != Precipitation.NONE, temperature, temperatureModifier, downfall, effects);
}
public StaticBiome(
@Nullable TypesBuilderData data, boolean precipitation,
float temperature, TemperatureModifier temperatureModifier,
float downfall, BiomeEffects effects
) {
this(data, precipitation, temperature, temperatureModifier,
downfall, null, null, null, effects);
}
public StaticBiome(
boolean precipitation, float temperature, TemperatureModifier temperatureModifier,
float downfall, @Nullable Category category, @Nullable Float depth,
@Nullable Float scale, BiomeEffects effects
) {
this(null, precipitation, temperature, temperatureModifier, downfall,
category, depth, scale, effects);
}
@ApiStatus.Obsolete
public StaticBiome(@Nullable TypesBuilderData data, Precipitation precipitation, float temperature,
TemperatureModifier temperatureModifier, float downfall, @Nullable Category category,
@Nullable Float depth, @Nullable Float scale, BiomeEffects effects) {
this(data, precipitation != Precipitation.NONE, temperature, temperatureModifier,
downfall, category, depth, scale, effects);
}
public StaticBiome(
@Nullable TypesBuilderData data, boolean precipitation,
float temperature, TemperatureModifier temperatureModifier,
float downfall, @Nullable Category category, @Nullable Float depth,
@Nullable Float scale, BiomeEffects effects
) {
super(data);
this.precipitation = precipitation;
this.temperature = temperature;
this.temperatureModifier = temperatureModifier;
this.downfall = downfall;
this.category = category;
this.depth = depth;
this.scale = scale;
this.effects = effects;
}
@Override
public Biome copy(@Nullable TypesBuilderData newData) {
return new StaticBiome(newData, this.precipitation, this.temperature, this.temperatureModifier,
this.downfall, this.category, this.depth, this.scale, this.effects);
}
@Override
public boolean hasPrecipitation() {
return this.precipitation;
}
@Override
public Precipitation getPrecipitation() {
if (!this.hasPrecipitation()) {
return Precipitation.NONE;
}
switch (this.getTemperatureModifier()) {
case NONE:
return Precipitation.RAIN;
case FROZEN:
return Precipitation.SNOW;
default:
throw new AssertionError();
}
}
@Override
public float getTemperature() {
return this.temperature;
}
@Override
public TemperatureModifier getTemperatureModifier() {
return this.temperatureModifier;
}
@Override
public float getDownfall() {
return this.downfall;
}
@Override
public @Nullable Category getCategory() {
return this.category;
}
@Override
public @Nullable Float getDepth() {
return this.depth;
}
@Override
public @Nullable Float getScale() {
return this.scale;
}
@Override
public BiomeEffects getEffects() {
return this.effects;
}
public boolean deepEquals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticBiome)) return false;
if (!super.equals(obj)) return false;
StaticBiome that = (StaticBiome) obj;
if (this.precipitation != that.precipitation) return false;
if (Float.compare(that.temperature, this.temperature) != 0) return false;
if (Float.compare(that.downfall, this.downfall) != 0) return false;
if (this.temperatureModifier != that.temperatureModifier) return false;
if (this.category != that.category) return false;
if (!Objects.equals(this.depth, that.depth)) return false;
if (!Objects.equals(this.scale, that.scale)) return false;
return this.effects.equals(that.effects);
}
public int deepHashCode() {
return Objects.hash(super.hashCode(), this.precipitation, this.temperature,
this.temperatureModifier, this.downfall, this.category, this.depth, this.scale, this.effects);
}
@Override
public String toString() {
return "StaticBiome{precipitation=" + this.precipitation + ", temperature=" + this.temperature + ", temperatureModifier=" + this.temperatureModifier + ", downfall=" + this.downfall + ", category=" + this.category + ", depth=" + this.depth + ", scale=" + this.scale + ", effects=" + this.effects + '}';
}
}
@@ -20,14 +20,12 @@ package com.github.retrooper.packetevents.protocol.world.chunk.impl.v1_16;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.stream.NetStreamOutput;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.NibbleArray3d;
import com.github.retrooper.packetevents.protocol.world.chunk.palette.DataPalette;
import com.github.retrooper.packetevents.protocol.world.chunk.palette.PaletteType;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
public class Chunk_v1_9 implements BaseChunk {
private static final int AIR = 0;
@@ -56,7 +54,8 @@ public class Chunk_v1_9 implements BaseChunk {
}
if (isSixteen) {
dataPalette = DataPalette.read(in, PaletteType.CHUNK);
// singleton palette got added with 1.18 which isn't supported by this chunk section implementation
dataPalette = DataPalette.read(in, PaletteType.CHUNK, false);
} else {
dataPalette = DataPalette.readLegacy(in);
}
@@ -117,4 +116,20 @@ public class Chunk_v1_9 implements BaseChunk {
// 1.14+, we can rely on the value
return this.blockCount == 0;
}
}
public NibbleArray3d getSkyLight() {
return this.skyLight;
}
public void setSkyLight(NibbleArray3d skyLight) {
this.skyLight = skyLight;
}
public NibbleArray3d getBlockLight() {
return this.blockLight;
}
public void setBlockLight(NibbleArray3d blockLight) {
this.blockLight = blockLight;
}
}
@@ -55,8 +55,12 @@ public class DataPalette {
}
public static DataPalette read(NetStreamInput in, PaletteType paletteType) {
return read(in, paletteType, true);
}
public static DataPalette read(NetStreamInput in, PaletteType paletteType, boolean allowSingletonPalette) {
int bitsPerEntry = in.readByte();
Palette palette = readPalette(paletteType, bitsPerEntry, in);
Palette palette = readPalette(paletteType, bitsPerEntry, in, allowSingletonPalette);
BitStorage storage;
if (!(palette instanceof SingletonPalette)) {
int length = in.readVarInt();
@@ -94,7 +98,7 @@ public class DataPalette {
public static DataPalette readLegacy(NetStreamInput in) {
int bitsPerEntry = in.readByte() & 0xff;
Palette palette = readPalette(PaletteType.CHUNK, bitsPerEntry, in);
Palette palette = readPalette(PaletteType.CHUNK, bitsPerEntry, in, false);
BaseStorage storage;
if (!(palette instanceof SingletonPalette)) {
int length = in.readVarInt();
@@ -108,7 +112,7 @@ public class DataPalette {
public int get(int x, int y, int z) {
if (storage != null) {
int id = this.storage.get(index(x, y, z));
int id = this.storage.get(index(this.paletteType, x, y, z));
return this.palette.idToState(id);
} else {
return this.palette.idToState(0);
@@ -126,7 +130,7 @@ public class DataPalette {
}
if (this.storage != null) {
int index = index(x, y, z);
int index = index(this.paletteType, x, y, z);
int curr = this.storage.get(index);
this.storage.set(index, id);
@@ -137,11 +141,16 @@ public class DataPalette {
}
}
private static Palette readPalette(PaletteType paletteType, int bitsPerEntry, NetStreamInput in) {
private static Palette readPalette(
PaletteType paletteType,
int bitsPerEntry,
NetStreamInput in,
boolean allowSingletonPalette
) {
if (bitsPerEntry > paletteType.getMaxBitsPerEntry()) {
return new GlobalPalette();
}
if (bitsPerEntry == 0) {
if (bitsPerEntry == 0 && allowSingletonPalette) {
return new SingletonPalette(in);
}
if (bitsPerEntry <= paletteType.getMinBitsPerEntry()) {
@@ -186,7 +195,7 @@ public class DataPalette {
}
}
private static int index(int x, int y, int z) {
return y << 8 | z << 4 | x;
private static int index(PaletteType paletteType, int x, int y, int z) {
return (y << paletteType.getBitShift() | z) << paletteType.getBitShift() | x;
}
}
@@ -19,16 +19,18 @@
package com.github.retrooper.packetevents.protocol.world.chunk.palette;
public enum PaletteType {
BIOME(1, 3, 64),
CHUNK(4, 8, 4096);
BIOME(1, 3, 2, 64),
CHUNK(4, 8, 4, 4096);
private final int minBitsPerEntry;
private final int maxBitsPerEntry;
private final int bitShift;
private final int storageSize;
PaletteType(int minBitsPerEntry, int maxBitsPerEntry, int storageSize) {
PaletteType(int minBitsPerEntry, int maxBitsPerEntry, int bitShift, int storageSize) {
this.minBitsPerEntry = minBitsPerEntry;
this.maxBitsPerEntry = maxBitsPerEntry;
this.bitShift = bitShift;
this.storageSize = storageSize;
}
@@ -40,6 +42,10 @@ public enum PaletteType {
return minBitsPerEntry;
}
public int getBitShift() {
return bitShift;
}
public int getStorageSize() {
return storageSize;
}
@@ -21,9 +21,27 @@ package com.github.retrooper.packetevents.protocol.world.chunk.reader;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.util.BitSet;
public interface ChunkReader {
BaseChunk[] read(Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn);
@Deprecated
default BaseChunk[] read(
Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight,
boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn
) {
DimensionType dimensionType = dimension.asDimensionType(null, null);
return this.read(dimensionType, set, sevenExtendedMask, fullChunk,
hasSkyLight, checkForSky, chunkSize, data, dataIn);
}
default BaseChunk[] read(
DimensionType dimensionType, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight,
boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn
) {
Dimension dimension = Dimension.fromDimensionType(dimensionType, null, null);
return this.read(dimension, set, sevenExtendedMask, fullChunk, hasSkyLight, checkForSky, chunkSize, data, dataIn);
}
}
@@ -19,16 +19,16 @@
package com.github.retrooper.packetevents.protocol.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v1_16.Chunk_v1_9;
import com.github.retrooper.packetevents.protocol.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.util.BitSet;
public class ChunkReader_v1_16 implements ChunkReader {
@Override
public BaseChunk[] read(Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
public BaseChunk[] read(DimensionType dimensionType, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
BaseChunk[] chunks = new BaseChunk[chunkSize];
for (int index = 0; index < chunks.length; ++index) {
@@ -19,16 +19,16 @@
package com.github.retrooper.packetevents.protocol.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v_1_18.Chunk_v1_18;
import com.github.retrooper.packetevents.protocol.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.util.BitSet;
public class ChunkReader_v1_18 implements ChunkReader {
@Override
public BaseChunk[] read(Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
public BaseChunk[] read(DimensionType dimensionType, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
BaseChunk[] chunks = new BaseChunk[chunkSize];
for (int index = 0; index < chunkSize; ++index) {
@@ -19,19 +19,19 @@
package com.github.retrooper.packetevents.protocol.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.ByteArray3d;
import com.github.retrooper.packetevents.protocol.world.chunk.NetworkChunkData;
import com.github.retrooper.packetevents.protocol.world.chunk.NibbleArray3d;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v1_7.Chunk_v1_7;
import com.github.retrooper.packetevents.protocol.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.util.BitSet;
public class ChunkReader_v1_7 implements ChunkReader {
@Override
public BaseChunk[] read(Dimension dimension, BitSet primarySet, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
public BaseChunk[] read(DimensionType dimensionType, BitSet primarySet, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
Chunk_v1_7[] chunks = new Chunk_v1_7[16];
int pos = 0;
int expected = 0;
@@ -19,13 +19,13 @@
package com.github.retrooper.packetevents.protocol.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.NetworkChunkData;
import com.github.retrooper.packetevents.protocol.world.chunk.NibbleArray3d;
import com.github.retrooper.packetevents.protocol.world.chunk.ShortArray3d;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v1_8.Chunk_v1_8;
import com.github.retrooper.packetevents.protocol.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -35,7 +35,7 @@ import java.util.BitSet;
public class ChunkReader_v1_8 implements ChunkReader {
@Override
public BaseChunk[] read(Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
public BaseChunk[] read(DimensionType dimensionType, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasSkyLight, boolean checkForSky, int chunkSize, byte[] data, NetStreamInput dataIn) {
Chunk_v1_8[] chunks = new Chunk_v1_8[16];
int pos = 0;
int expected = fullChunk ? 256 : 0; // 256 if full chunk for the biome data, always sent if full chunk
@@ -19,17 +19,17 @@
package com.github.retrooper.packetevents.protocol.world.chunk.reader.impl;
import com.github.retrooper.packetevents.protocol.stream.NetStreamInput;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v1_16.Chunk_v1_9;
import com.github.retrooper.packetevents.protocol.world.chunk.reader.ChunkReader;
import com.github.retrooper.packetevents.protocol.world.dimension.DimensionType;
import java.util.BitSet;
public class ChunkReader_v1_9 implements ChunkReader {
@Override
public BaseChunk[] read(Dimension dimension, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasBlockLight, boolean hasSkyLight, int chunkSize, byte[] data, NetStreamInput dataIn) {
public BaseChunk[] read(DimensionType dimensionType, BitSet set, BitSet sevenExtendedMask, boolean fullChunk, boolean hasBlockLight, boolean hasSkyLight, int chunkSize, byte[] data, NetStreamInput dataIn) {
BaseChunk[] chunks = new BaseChunk[chunkSize];
for (int index = 0; index < chunks.length; ++index) {
Loaded 100 of 212 files, more files were not shown because too many files have changed in this diff. Show more