Compare commits

..
204 Commits
Author SHA1 Message Date
Nassim Jahnke 4d5d5e3e7b Avoid full pipeline iteration in raw packet sends
Closes #5136
2026-11-01 11:21:56 +01:00
RK_01 d8415e6f59 Copy registry data before sending configuration packets (#5134) 2026-10-29 11:31:09 +01:00
Nassim Jahnke eb00c5e0b1 Bump version to 5.12.3-SNAPSHOT 2026-10-29 11:30:37 +01:00
Nassim Jahnke ad24236c6b 5.12.2 Release 2026-10-27 15:59:29 +01:00
Nassim Jahnke ab503f497a Update dependencies 2026-10-27 15:56:10 +01:00
Nassim Jahnke 3afc3ee1e1 Add warning for servers running 1.20.5-1.21.4
Effectively unfixable Vanilla protocol exploits make supporting these versions effectively impossible.
2026-10-27 15:56:10 +01:00
Nassim Jahnke cb55edf220 Don't stringify complex packet values in errors
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-OHZWL7WM.
2026-10-27 15:54:07 +01:00
Nassim Jahnke 9f4803b832 Limit serverbound data nesting depth
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-1V7UVEKT.
2026-10-27 15:54:06 +01:00
Nassim Jahnke 88419cdd71 Use arraycopy for primitive array copies
Discovered by Anthropic's OSS Scanner, as ANT-2026-NH253ZCQ.
2026-10-27 15:33:35 +01:00
Nassim Jahnke 2579ac2153 Apply vanilla limits to more serverbound counts
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-GVS9G86J.
2026-10-27 15:33:35 +01:00
Nassim Jahnke 9d2f9f10e7 Don't cache missing paths for unknown versions
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-JJN86714.
2026-10-27 15:33:35 +01:00
Nassim Jahnke 218fc88313 Limit profile key lengths
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-8Z8EOA8N.
2026-10-27 15:33:35 +01:00
Nassim Jahnke 357cb229bc Mask and randomize 1.21.5 item hash keys
Discovered by Anthropic's OSS Scanner, as vulnerabilities ANT-2026-UGG51ZRR and ANT-2026-Z4170OQA.
2026-10-27 15:33:34 +01:00
Nassim Jahnke d89ea4c7be Release packets sent after the channel closed
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-X0V9ES7R.
2026-10-27 15:33:34 +01:00
Nassim Jahnke be08ce39f6 Don't back up nested items in 1.20.3->1.20.5
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-01EM4SCC.
2026-10-27 15:33:34 +01:00
Nassim Jahnke f71f4722e0 Range check original hash ids
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-AECM0IZO.
2026-10-27 10:30:35 +01:00
Nassim Jahnke 62ea10d662 Only convert container items once
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-0RVR8JDH.
2026-10-27 10:30:35 +01:00
Nassim Jahnke ca49a1b4ad Don't presize large arrays
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-0DCBHFG1.
2026-10-27 10:30:35 +01:00
Nassim Jahnke 746740accf Update map types in some data reading
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-HV60WOZ3.
2026-10-27 10:30:35 +01:00
Nassim Jahnke b90f379538 Deactivate on unknown handshake intent
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-RJ1MXTAQ.
2026-10-27 10:30:35 +01:00
Nassim Jahnke 8b9a78aa02 Avoid exceptions in UUID parsing
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-65KLCX8F.
2026-10-27 10:30:35 +01:00
Nassim Jahnke 3487e8ce5f Add sanity checks for config state switching
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-891675B2.
2026-10-27 08:24:44 +01:00
Florian Reuth 73dc869595 Remap trim item sprites to the items atlas in 1.21.9->1.21.11 (#5122) 2026-10-23 10:48:16 +02:00
Nassim Jahnke 66c064ad7d Update deps, move from minotaur to mod-publish-plugin
Minotaur is barely maintained, and mod-publish-plugin lets us remove the annoying version handling
2026-10-21 13:36:23 +02:00
Nassim Jahnke 8d56c71974 Handle hidden_components in show item hovers
Fixes #5128
2026-10-19 20:31:20 +02:00
Nassim Jahnke 28d16ec7d3 Init lastWarning correctly 2026-10-19 20:31:14 +02:00
Florian Reuth fc4b13f0a1 Fix up checks in 1.12.2->1.13 wall connection handling (#5124) 2026-10-19 14:02:26 +02:00
Nassim Jahnke b712c0055d Add some missing sound rewriting 2026-10-18 22:07:58 +02:00
mfishma 08776de832 [ci skip] Update README (#5118) 2026-10-12 18:40:57 +02:00
Florian Reuth 77b7054629 Remove duplicated items tag in 1.20.3->1.20.5 (#5121) 2026-10-12 18:40:14 +02:00
Nassim Jahnke 63769b3661 Fix continuous block breaking sounds
Fixes #5120
2026-10-11 20:08:24 +02:00
mfishma 12d75daf04 Fix item_name restoration when added_custom_name is present (#5109) 2026-10-11 09:48:29 +02:00
Florian Reuth 413268eb15 Delete empty tag in 1.12.2->1.13 (#5115) 2026-10-11 09:41:02 +02:00
Florian Reuth b27e2a5514 Handle existing entity tag properly in 1.12.2->1.13 (#5114) 2026-10-11 09:40:49 +02:00
Florian Reuth c59dfb4d93 Fix NPE if color is missing in patterns in 1.12.2->1.13 (#5113) 2026-10-11 09:40:31 +02:00
Florian Reuth f66cbd6941 Remove empty tags in 1.8->1.9 (#5112) 2026-10-11 09:40:16 +02:00
mfishma 1574f343f7 Downgrade new 26.3 map decorations to target_x (#5110) 2026-10-11 09:39:41 +02:00
Nassim Jahnke 022fb96f83 Handle visible items in block entities
Fixes #1351
2026-10-09 16:43:07 +02:00
Nassim Jahnke 7cbc751fea Bump version to 5.12.2-SNAPSHOT 2026-10-09 16:42:46 +02:00
Nassim Jahnke 366f08d951 Separate Fabric platform on modrinth
... or it'll fail when specifying versions below 1.14
2026-10-08 16:05:13 +02:00
Nassim Jahnke d628f09334 5.12.1 Release 2026-10-08 15:41:05 +02:00
Nassim JahnkeandWhiteProject1 6fecd58773 Check full type length if possible
Closes #5104

Co-authored-by: WhiteProject1 <Odunyasi167@gmail.com>
2026-10-08 15:38:29 +02:00
冰砚炽andNassim Jahnke da7619c82d Fix custom enchantment value_check predicate is not rewritten for 26.3 clients (#5101)
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2026-10-08 15:12:20 +02:00
Nassim Jahnke b83268efa0 Update deps 2026-10-08 11:02:33 +02:00
Florian Reuth 309c3e57c1 Remove map number tag again in 1.16.4->1.17 (#5099) 2026-10-04 08:55:53 +02:00
Florian Reuth 34a445efcd Copy mutable item before updating in 1.20.3->1.20.5 (#5102) 2026-10-04 08:54:12 +02:00
Florian Reuth 3a949d70eb Don't error on invalid filtered pages in 1.20.2->1.20.3 (#5098) 2026-10-02 14:55:26 +02:00
Rocco 99bfa389b4 Fix backwards conversion of new 1.20.5 trim patterns (#5078) 2026-10-01 19:07:09 +02:00
Florian Reuth 327d4739f5 Fix HIDE_ADDITIONAL_TOOLTIP disappearing in 1.21.4->1.21.5 (#5092) 2026-10-01 18:43:08 +02:00
Florian Reuth 3a7a8e06a8 Backup & restore map color in 26.2->26.3 (#5096) 2026-10-01 15:06:47 +02:00
Florian Reuth 85db19200f Upgrade empty swing animation as well in 26.2->26.3 (#5097) 2026-10-01 15:03:49 +02:00
Florian Reuth 226609b864 Don't lose bypassed_by tag key in 1.21.11->26.1 (#5095) 2026-09-30 20:56:50 +02:00
Florian Reuth 7049f7eaf1 Fix bees not spawning after creative mode in 1.21.7->1.21.9 (#5094) 2026-09-30 20:55:49 +02:00
Florian Reuth a062d0905b Don't use random unique id for attribute conversion in 1.20.5->1.21 (#5093) 2026-09-30 08:43:07 +02:00
Florian Reuth 7ac58c104b Fix Consumable animation hashing between 1.21.5 and 1.21.11 (#5087) 2026-09-29 18:37:42 +02:00
Florian Reuth 7a6e838eb1 Inline damage type for 1.21.11 (#5090) 2026-09-29 18:37:24 +02:00
Florian Reuth 57bc53346c Fix sulfur_cube_content network type (#5091) 2026-09-29 16:03:05 +02:00
terbin 3df7c76c8c Add statistic mappings for 1.16.2->1.17 (#5089) 2026-09-29 14:46:34 +02:00
Florian Reuth 3f35005051 Fix default-count item template hashing (#5088) 2026-09-29 14:17:24 +02:00
Florian Reuth f9d21e073b Fix conditional hashing of a bunch of item data components (#5082) 2026-09-29 14:12:08 +02:00
Florian Reuth e813796d9a Handle remaining components in registry data (#5077) 2026-09-28 19:31:54 +02:00
Florian Reuth 271b6e7ff7 Fix splash potion sounds not playing in 26.2->26.3 (#5076) 2026-09-28 19:30:57 +02:00
LeonTG 7bf4a30075 Fix ender dragon having the wrong dragon phase in 1.8->1.9 (#5070) 2026-09-28 08:55:09 +02:00
PolangandFlorian Reuth 414e978829 Skip removal markers in 1.20.5+ item cost type (#5073)
Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-09-28 08:45:15 +02:00
Nassim Jahnke 637a81e569 Handle dripstone particles dimension attribute registry data
Fixes #5074
2026-09-27 14:21:22 +02:00
Florian Reuth b4023127f6 Force set health back >0 in 1.19.1->1.19.3 (#5061) 2026-09-20 14:54:56 +02:00
Nassim Jahnke 46a61058dd Add missing tags across protocols, reduce stored tags size 2026-09-20 13:06:35 +02:00
Nassim Jahnke 66b90a5b2a Add missing tag for newly placed solid blocks letting through rain
Fixes #5060
2026-09-20 11:59:58 +02:00
Florian Reuth 8f03d6bcef Cancel axe stripping for pre 1.13, shovel dirth paths for pre 1.9 (#5057) 2026-09-20 09:44:19 +02:00
Florian Reuth e1809674fd Send brewing recipes for vanilla servers not syncing the inventory in 26.2->26.3 (#5056) 2026-09-19 19:50:12 +02:00
Pantera 4d8b0d1f54 Add missing 26.3 block tags for client predictions 2026-09-19 15:16:57 +02:00
Rocco d1a0c58f5f Restore 26.2 interpolation steps for 26.2->26.3 (#5052) 2026-09-19 12:55:02 +02:00
Dainsleif 724460d2e7 Skip repeated end credits on 1.21+ clients (#5055) 2026-09-19 12:37:59 +02:00
chico ferreira f2e3f53486 Send original client version in Velocity player details (#5043) 2026-09-19 12:20:59 +02:00
Nassim Jahnke 7190e365e4 Bump version to 5.12.1-SNAPSHOT 2026-09-19 12:20:06 +02:00
Nassim Jahnke 50aa9fa3ad 5.12.0 Release 2026-09-18 16:04:35 +02:00
Nassim Jahnke 7497103bac Null check block state Name
Shouldn't be valid, but ?
2026-09-18 16:00:13 +02:00
Nassim Jahnke ff83e2eb6e Fix brewing stand brewing time and fuel display 2026-09-18 16:00:13 +02:00
Nassim JahnkeandCamden Cove 2a75917ef5 Always create mapped chunk type
Types are cached now and need to be created in case global palette bits count changed

Co-authored-by: Camden Cove <me@camdencove.com>
2026-09-18 16:00:13 +02:00
Nassim Jahnke f634879d64 Handle ambient_particles for block state format changes 2026-09-18 16:00:13 +02:00
Nassim Jahnke e6222b38ef Replace new map decoration types when downgrading items 2026-09-18 16:00:13 +02:00
Nassim Jahnke 745a41322f Remove snapshot bit 2026-09-18 16:00:12 +02:00
Nassim Jahnke 72c679acc6 Fix expected move after teleport
Also changes the ViaCodecHandler to a MessageToMessageCodec, as otherwise sync encodes aren't properly processed due to buffering
2026-09-18 16:00:12 +02:00
Florian Reuth 5996340a33 Handle block_state_property registry changes
Fixes joining an Paper 1.8 server with 26.3

Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:12 +02:00
Florian Reuth a2c1841e25 Fix consume effects hashing key in 1.21.2+
Not related to the 26.3 update but easier than having to deal with merge conflicts after the public release.

Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:12 +02:00
Florian Reuth 3483d652e8 Update shared registrations to 26.3 changes
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:12 +02:00
Florian Reuth da29a96844 Replace mask type in player_chat
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:12 +02:00
Florian Reuth 735a4cacb1 Fix trim plaette id<->asset name conversion
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:11 +02:00
Florian Reuth d236db51e0 Fix resolvable primitive types write
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:11 +02:00
Florian Reuth 44167439dd Handle new type in player_action
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:11 +02:00
Nassim Jahnke 67b7ae4cac Strip namespace in 26.3->26.2 trim data 2026-09-18 16:00:11 +02:00
Florian Reuth cef5abdc73 Handle worldgen/block_state_provider going forward
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:11 +02:00
Florian Reuth ad7f53c3cd Fix states updating in block state provider
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:11 +02:00
Florian Reuth e5554c7a8e Handle decorated_pot block entity data
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:10 +02:00
Florian Reuth cbe42132be Fix consume effect conversion
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:10 +02:00
Florian Reuth 8023d8237b Remove compostable on item downgrade
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:10 +02:00
Florian Reuth 0611b95183 Fix pot decorations network type
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 16:00:10 +02:00
Nassim Jahnke d647f169e6 26.3-rc-2 2026-09-18 16:00:10 +02:00
Nassim Jahnke c44b94f96a Fix swing animation handling 2026-09-18 16:00:10 +02:00
Nassim Jahnke 29be35be1c 26.3-rc-1 2026-09-18 16:00:10 +02:00
Nassim Jahnke 9d2c429624 26.3-pre-3 2026-09-18 16:00:10 +02:00
Nassim Jahnke d2ab957ceb Update and restore trim material 2026-09-18 16:00:09 +02:00
Nassim Jahnke 4bbae432b4 26.3-pre-2 2026-09-18 16:00:09 +02:00
Nassim Jahnke bfc2713102 26.3-pre-1 2026-09-18 16:00:09 +02:00
Nassim Jahnke 155863a4c4 26.3-snapshot-10 2026-09-18 16:00:09 +02:00
Nassim Jahnke 8fc649fba9 26.3-snapshot-9 2026-09-18 16:00:09 +02:00
Nassim Jahnke 7edc17759f 26.3-snapshot-8 2026-09-18 16:00:08 +02:00
Nassim Jahnke b607f96988 26.3-snapshot-7 2026-09-18 16:00:08 +02:00
Nassim Jahnke f6b2f6b3ad 26.3-snapshot-6 2026-09-18 16:00:08 +02:00
Nassim Jahnke a7186fb162 26.3-snapshot-5 2026-09-18 16:00:08 +02:00
Nassim Jahnke d2e636bc6f 26.3-snapshot-4 2026-09-18 16:00:08 +02:00
Nassim Jahnke 9cbb829853 Fix movement tick offset 2026-09-18 16:00:07 +02:00
Nassim Jahnke f04f0a07a8 26.3-snapshot-3 2026-09-18 16:00:07 +02:00
Nassim Jahnke 7cf85e38c5 Fix DEBUG_SUBSCRIPTION_REQUEST and GAME_TEST_HIGHLIGHT_POS packet names 2026-09-18 16:00:07 +02:00
Nassim Jahnke 55f449ee74 Implement BlockTransformData rewrite
See code comments
2026-09-18 16:00:07 +02:00
Nassim Jahnke 460214efe2 26.3-snapshot-2 2026-09-18 16:00:07 +02:00
Nassim Jahnke e5cf0fc6c5 26.3-snapshot-1 2026-09-18 15:59:58 +02:00
Nassim Jahnke d2eed62794 Fix 1.9 identifiers file 2026-09-18 08:51:56 +02:00
Beanes 9946d3e9f6 Add 1.9 identifiers (#4976) 2026-09-16 18:08:00 +02:00
EvModder 5f98cba45b Preserve legacy world clocks (#5040) 2026-09-04 20:54:17 +02:00
LeonTG 15a7d8c853 Remove removed stats in 1.11->1.11.1 (#5037) 2026-08-30 17:19:12 +02:00
Beanes 2645d83eb7 Improve hide-scoreboard-numbers to only work for sidebar (#5034) 2026-08-29 14:34:20 +02:00
Nassim Jahnke c23ec2c26f [ci skip] Build script updates 2026-08-23 15:10:06 +02:00
Emir 42380596ee [ci skip] Use instanceof check in ConfigSection#getSerializedComponent (#5025) 2026-08-23 15:09:55 +02:00
Nassim Jahnke 03e0f231ea Fix compilation 2026-08-19 20:38:46 +02:00
Nassim Jahnke eed3de9e3d Move index to AbstractProtocol directly 2026-08-19 20:35:14 +02:00
Nassim Jahnke feb08a31a3 Skip base protocols in hasher protocol loop 2026-08-19 20:10:14 +02:00
Nassim Jahnke e8bbb5da5b Update ViaNBT 2026-08-14 16:03:56 +02:00
Nassim Jahnke 034390dd24 Update deps 2026-08-12 21:50:16 +02:00
Nassim Jahnke 89f80172b1 Move a bunch of protocol storables to direct array lookups
Also has the benefit of making them thread-safe
2026-08-05 16:57:54 +02:00
Nassim Jahnke 5f93e73b8a Create NbtOps
Useful for backup tags in VB
2026-08-05 16:09:13 +02:00
Nassim Jahnke c124b309e0 Ignore invalid legacy show_item in 1.11->1.12
Closes #5014
2026-07-30 09:17:59 +02:00
Nassim Jahnke 122b81dd33 Add shears breaking speed block tags in 26.1->26.2
Fixes #5010
2026-07-23 17:46:09 +02:00
Nassim Jahnke a2896b64fa Fix 1.21.11->26.1 damage_resistant rewriting
Fixes #5008
2026-07-23 16:13:03 +02:00
Nassim Jahnke 2d78597a59 Handle nested identifiers in item hover events
Actually rewrite the important ones (i.e. relevant to tooltips) and remove the others.
2026-07-23 15:23:33 +02:00
Nassim Jahnke 432918645e Fix boat rotation unpacking in 1.21->1.21.2 2026-07-18 19:24:44 +02:00
RiceChen_andNassim Jahnke c20c29fa3a Sort stonecutter recipes by output registry key (#5006)
Fixes #4242

Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2026-07-15 16:58:08 +02:00
Nassim Jahnke ab85b0e0f0 Build script cleanup
This also moves Fabric and Sponge resource files into the universal dir; we do not publish platform specific jars and this otherwise only leads to a more complicated project setup
2026-07-15 13:12:58 +02:00
Nassim Jahnke 3a5d9541c8 Use netty Buf I/O streams on Java 24+
The JDK had already gotten similar (but much better still) allocation and performance improvements to read and writeUtf methods
2026-07-14 12:39:49 +02:00
Nassim Jahnke 356a5e25f4 Bump version to 5.11.1-SNAPSHOT 2026-07-14 12:05:55 +02:00
Nassim Jahnke d9a3a4493d 5.11.0 Release 2026-07-12 18:05:51 +02:00
Nassim Jahnke 0fe1b6d6db Move 1.13 blockstate file reading to separate util 2026-07-12 18:02:19 +02:00
Nassim Jahnke fbb92c0fac Reduce mapping data file size by about 35% (30% uncompressed)
The general idea behind the four storage types is the same, except ids are now
1) written as id deltas instead of absolute values
2) written with zigzag encoding (always positive)
3) written as varints

The previous commit made extra mapping data go down by 85% (both uncompressed and compressed).

Combined, this is about a megabyte less uncompressed and 125kb less compressed data bundled right now (not including ViaBackwards).

Reading has become ever so slightly faster due to the reduced file reads (despite now needing more complex decoding), but that's negligible anyway
2026-07-12 10:55:27 +02:00
Nassim Jahnke 384cf91125 Reduce extra mapping data file size by a lot
Merge en_US properties and lang mappings into a single file that still decodes into the same values; store block connections as shared "profiles", since there are only so many different ways of connecting to neighboring blocks; store block states with shared properties; reduce redundant info in the recipes file and store the rest in a more compact/type-specific way as well.

On top of that, store ranges of ids much more concisely (by far the biggest win)
2026-07-12 09:55:44 +02:00
Nassim Jahnke c6736b3532 Fix 26.1->26.2 multi-field entity predicates
Mainly removing the `return` after handling type_specific key changes, but also handling nested predicates
2026-07-09 20:55:25 +02:00
Nassim Jahnke 8c4bbd1a2f [ci skip] Update deps
Moves actions with immutable releases back to proper tag names. setup-gradle and upload-artifact unfortunately do not have these yet
2026-07-09 18:14:18 +02:00
Nassim Jahnke 386c607917 Small improvements here in there
The main ones are not id checking every single block in the 1.9.3 protocol transition, and caching chunk types
2026-07-09 17:48:21 +02:00
Nassim Jahnke c30c787c63 Fix boat tracker null check 2026-07-09 16:18:25 +02:00
Nassim Jahnke 6951dda36e Simplify StoredEntityData into TrackedEntity; directly return tracked entity in track and untrack methods 2026-07-09 15:07:24 +02:00
Nassim Jahnke c9d8610c05 Fix loom in 1.18.2->1.19
Fixes #2938
2026-07-08 20:24:22 +02:00
Nassim Jahnke 68ad7c115b Bump version to 5.11.0-SNAPSHOT
Slightly late, but this version contains a few smaller breaking, and larger changes
2026-07-08 16:33:44 +02:00
Nassim Jahnke 3c50cdf639 Remove removed environment attributes
At the moment only relevant for ViaBackwards, but the client throws when getting environment attributes it does not recognize in biome or dimension registry data
2026-07-08 16:03:32 +02:00
Nassim Jahnke e211692ca1 Make BlockIdData allocation less dumb 2026-07-08 15:29:20 +02:00
Nassim Jahnke 8937bdd986 Fix custom dimension lighting in 1.21.11->26.1
Fixes #4950
2026-07-08 14:18:16 +02:00
Nassim Jahnke b49f995ef1 Fix missing arm swing on sugar cane placement in 1.21.11->26.1
Fixes #4941
2026-07-08 13:37:48 +02:00
Nassim Jahnke 7ab825cc77 Fix missing door sounds from level events in 1.19.1->1.19.3
Fixes #3376
2026-07-08 12:11:22 +02:00
Nassim Jahnke a00226f452 Add missing block rewriter in 1.17.1->1.18
Fixes #4972
2026-07-08 10:17:45 +02:00
Nassim Jahnke 9370534b23 Fix 26.1->1.21.11 damage resistant id list handling 2026-07-06 21:53:06 +02:00
Florian Reuth 5fe54eb1e5 Fix 26.2 damage event handler on <1.19.4 servers (#4997) 2026-07-05 20:50:02 +02:00
Nassim Jahnke a090a909ee Don't check real entity ids for fake entity 2026-07-05 13:51:07 +02:00
Nassim Jahnke 72084b2599 Refactor passthrough token handling
Only uses a single long now with a simple long set instead of an expiring cache. When an invalid token is read, it no longer throws but moves back the reader and continues to maybe possibly not interfere with other plugins for some reason sending their own data with the packet id 1000
2026-07-05 13:44:53 +02:00
Nassim Jahnke a3dcaa6f56 Use a shared session id; replace UUID generation elsewhere
Entirely negligible on most servers, but if running on the backend with many players and newly added entities / other uses of it across many different threads, random UUID generation is synchronized with its shared SecureRandom use
2026-07-05 13:44:53 +02:00
Nassim Jahnke 88f9c05be0 Add a few early returns/cached checks
The last few things that shouldn't show up in a profile
2026-07-05 11:43:51 +02:00
Nassim Jahnke 7b167b0eed Don't load identifier files twice if loaded concurrently
If two protocols were trying to load the same identifier file very close to one another, the first load attempt would not have been added to the cache yet, so the second would have also started loading the file. This makes it wait for the first load instead
2026-07-05 10:16:39 +02:00
Nassim Jahnke 21ff1758d5 Small improvements to string reading and key path checking
Very very small ones, but given they are (or will be) incredibly hot methods, those easy improvements don't hurt
2026-07-05 10:15:41 +02:00
Nassim Jahnke 9793ca2291 Improve protocol path calc 2026-07-05 10:11:51 +02:00
Nassim Jahnke 5768a5c0da Optimize block checking in chunk packets
Instead of going through each of the 4096 blocks in a chunk section, check the palette values first to then also skip actual id getting and simply check palette indexes via an array lookup
2026-07-05 10:11:51 +02:00
Nassim Jahnke decab8fe7f Optimize chunk I/O by a lot
After the improvements from a month ago it already became a good bit better, but was still the most time and memory consuming task by far. This cuts down up to 90% of it by skipping raw data extraction entirely unless actually needed in fairly rare cases as well as making packing and unpacking much more direct
2026-07-04 12:17:26 +02:00
Florian Reuth 354a736e0c Fix entity id == 0 in 26.1->26.2 (#4985) 2026-07-04 10:41:51 +02:00
Nassim Jahnke 56d23661b0 Split hash storage method in two for VB 2026-07-03 19:54:59 +02:00
Nassim Jahnke ca616c74fe Make some StructuredItemRewriter protected 2026-07-03 16:45:05 +02:00
Nassim Jahnke 168d13e143 Don't load block connection data if not necessary
As opposed to other mapping data that is easy to load and may be needed by VB, this was more expensive and can be loaded separately
2026-07-03 12:49:02 +02:00
itslilscorp 229d4fc6e1 Add option to fix default color of legacy reddust particles (#4954)
The multi count fix is disabled by default as it can spam clients to death with larger particle clouds
2026-07-02 13:51:58 +02:00
Bastit 33e426fddc Fix error when downgrading null lock component (#4984) 2026-07-02 13:14:20 +02:00
Nassim Jahnke 0c6023e489 Keep parsed Keys for registry mappings
Avoids frequent re-parsing and makes use of cached full/minimized output
2026-07-01 21:21:53 +02:00
Nassim Jahnke 8ebe3a523c Add registry key to damage resistant 2026-06-27 13:48:50 +02:00
Nassim Jahnke 8239577c23 Minimize Minecraft Identifier allocations 2026-06-21 15:10:06 +02:00
OverwriteMC 670fde5d0f Fix incorrect pre-size calculation in writeString (#4980) 2026-06-21 09:17:06 +02:00
Nassim JahnkeandOverwriteMC d2a715862a Use faster Java 9+ CRC32C implementation if possible
Co-authored-by: OverwriteMC <artyom030803@gmail.com>
2026-06-20 17:12:03 +02:00
LeonTG 6438f99600 Make 1.21.10 servers and below able to detect left-clicking for spectator mode players (#4977) 2026-06-19 22:30:51 +02:00
Nassim Jahnke 1e31aafd83 Change entity counter on sub 1.14 servers from 0 to 1
26.2 clients no longer accept the entity ID 0. Since this doesn't normally happen, just make sure it won't via a server-specific reflective setter
2026-06-19 21:49:46 +02:00
Nassim Jahnke 736ba3c53c 5.10.0 Release 2026-06-19 19:17:37 +02:00
Pantera 6d47c5af61 Add limit to debug sample subscription count (#4975) 2026-06-19 19:17:19 +02:00
Nassim Jahnke 2b42e38300 Add infiniburn tags if missing
They are from 1.16, but some servers might not send them and they are required now.
2026-06-16 19:10:22 +02:00
Nassim Jahnke 4260095390 Remove snapshot bit 2026-06-16 08:09:51 +02:00
riku6460 769498661d Add recipe rewriter 2026-06-16 08:08:52 +02:00
Florian Reuth 1c25e5011a Hide player list heads on offline mode servers
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-06-15 11:50:35 +02:00
Nassim Jahnke bb3de8ef8d Update deps 2026-06-13 12:15:45 +02:00
Nassim Jahnke 770dd485ec 26.2-rc-2 2026-06-12 17:59:11 +02:00
Nassim Jahnke 964a1b2581 26.2-rc-1 2026-06-12 17:20:54 +02:00
Nassim Jahnke bb1a5e745a 26.2-pre-6 2026-06-12 17:20:54 +02:00
Nassim Jahnke a0a31641e3 26.2-pre-5 2026-06-12 17:20:54 +02:00
Nassim Jahnke 4e493f61cd 26.2-pre-4 2026-06-12 17:20:53 +02:00
Nassim Jahnke ae7f1f07d5 26.2-pre-3 2026-06-12 17:20:53 +02:00
Nassim Jahnke be89cb52b3 26.2-pre-2 2026-06-12 17:20:53 +02:00
Nassim Jahnke b63765a970 26.2-pre-1 2026-06-12 17:20:53 +02:00
Nassim Jahnke 2047b42d78 26.2-snapshot-8 2026-06-12 17:20:53 +02:00
Nassim Jahnke a1179887cd 26.2-snapshot-7 2026-06-12 17:20:53 +02:00
Nassim Jahnke b2a3512bbd 26.2-snapshot-6 2026-06-12 17:20:52 +02:00
Nassim Jahnke 2fe81b5b70 26.2-snapshot-5 2026-06-12 17:20:52 +02:00
Nassim Jahnke 1da2fb89c4 26.2-snapshot-4 2026-06-12 17:20:52 +02:00
Nassim Jahnke 53e18c1f8d 26.2-snapshot-3 2026-06-12 17:20:52 +02:00
Nassim Jahnke 9460a65f02 26.2-snapshot-2 2026-06-12 17:20:52 +02:00
Nassim Jahnke 150a516006 26.2-snapshot-1 2026-06-12 17:20:52 +02:00
427 changed files with 21485 additions and 30231 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
.gitea/CODEOWNERS linguist-detectable=false
-3
View File
@@ -1,3 +0,0 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+3
View File
@@ -1,4 +1,5 @@
version: 2
# Disabled for the time being, being done manually instead
updates:
- package-ecosystem: "github-actions"
cooldown:
@@ -6,9 +7,11 @@ updates:
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
- package-ecosystem: "gradle"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 0
+4 -4
View File
@@ -11,16 +11,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
uses: gradle/actions/setup-gradle@v6.4.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@v6.1.0
with:
distribution: 'temurin'
java-version: 17
@@ -30,7 +30,7 @@ jobs:
- name: Upload Artifacts to GitHub
# Only upload on the preview repository as there is no CI job for it.
if: ${{ github.repository == 'ViaVersion/ViaVersionDev' && github.ref_name == 'preview' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: Artifacts
path: build/libs/
+4 -4
View File
@@ -16,16 +16,16 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
uses: gradle/actions/setup-gradle@v6.4.0
with:
cache-disabled: false
cache-provider: basic
- name: Set up JDK 17
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@v6.1.0
with:
distribution: 'temurin'
java-version: 17
@@ -40,5 +40,5 @@ jobs:
- name: Publish to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew modrinth --stacktrace
run: ./gradlew publishMods --stacktrace
continue-on-error: true
+2
View File
@@ -37,3 +37,5 @@ bin/
run/
AGENTS.override.md
.claude/
!.claude/CLAUDE.md
+13 -8
View File
@@ -14,18 +14,23 @@ or as [a standalone proxy](https://github.com/ViaVersion/ViaProxy) to join from
any server version from the past decade. **See [HERE](https://viaversion.com) for an overview of the different Via\*
projects.**
Note that ViaVersion will be able to **run best on either Paper servers or
through [ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus?tab=readme-ov-file#viafabricplus)** due to having
direct access to client/server state and more extensive API.
**Note:** While ViaVersion works reliably across all listed platforms, it has the highest performance and state accuracy on **Paper** servers and through **[ViaFabricPlus](https://github.com/ViaVersion/ViaFabricPlus)** on clients, since both offer more extensive APIs and direct access to client/server state.
Supported Versions:
### Supported Versions
![Table (https://ci.viaversion.com/userContent/images/T.png)](https://ci.viaversion.com/userContent/images/T.png)
| Players joining with… | Install |
| :--- | :--- |
| **Newer** versions than the server | ViaVersion |
| **Older** versions than the server, down to 1.9 | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) |
| **1.7.x – 1.8.x** | ViaVersion + [ViaBackwards](https://github.com/ViaVersion/ViaBackwards) + [ViaRewind](https://github.com/ViaVersion/ViaRewind) |
**User Docs:** https://docs.viaversion.com
If you're stuck, try [this tool](https://viaversion.com/setup) to work out what plugins you need.
**User Docs:** [docs.viaversion.com](https://docs.viaversion.com)
Snapshot Support
----------
Snapshot support
--------
**ViaVersion will only be released a few days *after* a Minecraft update** unless the protocol changes of the update
were trivial. If you want early-access, usually days or even weeks before the final release, you can subscribe to
either:
@@ -66,7 +66,7 @@ public interface ViaAPI<T> {
* @return API version incremented with meaningful API changes
*/
default int apiVersion() {
return 26;
return 27;
}
/**
@@ -261,6 +261,8 @@ public interface ViaVersionConfig extends Config {
*/
boolean isChunkBorderFix();
boolean isMultiReddustColorFix();
/**
* Should we make team colours based on the last colour in team prefix
*
@@ -498,6 +500,13 @@ public interface ViaVersionConfig extends Config {
*/
boolean handleInvalidItemCount();
/**
* Backs up the original data of nested items (e.g. in containers or bundles) for 1.20.5+ clients on 1.20.4 and older servers.
*
* @return true if enabled
*/
boolean backupNestedItems();
/**
* Tries to cancel block break/place sounds sent by 1.8 servers to 1.9+ clients to prevent them from playing twice
*
@@ -506,7 +515,7 @@ public interface ViaVersionConfig extends Config {
boolean cancelBlockSounds();
/**
* Hides scoreboard numbers for 1.20.3+ clients on older server versions.
* Hides sidebar scoreboard numbers for 1.20.3+ clients on older server versions.
*
* @return true if enabled
*/
@@ -0,0 +1,45 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.connection;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.item.ItemHasher;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
/**
* Holds protocol-specific data. Protocols may extend the data wrapper to hold extra data.
*/
public interface ProtocolStorables {
EntityTracker entityTracker();
void setEntityTracker(EntityTracker tracker);
ClientWorld clientWorld();
void setClientWorld(ClientWorld clientWorld);
ItemHasher itemHasher();
void setItemHasher(ItemHasher itemHasher);
}
@@ -22,6 +22,8 @@
*/
package com.viaversion.viaversion.api.connection;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.data.item.ItemHasher;
@@ -36,18 +38,18 @@ import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.CodecException;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface UserConnection {
/**
* Get an object from the storage.
* Get an object from the storage. Useful where protocol instances aren't readily available.
*
* @param objectClass The class of the object to get
* @param <T> The type of the class you want to get.
* @return The requested object
* @see #storables(Protocol)
*/
@Nullable <T extends StorableObject> T get(Class<T> objectClass);
@@ -69,27 +71,56 @@ public interface UserConnection {
@Nullable <T extends StorableObject> T remove(Class<T> objectClass);
/**
* Put an object into the stored objects based on class.
* Put an object into the stored objects based on class. Useful where protocol instances aren't readily available.
*
* @param object The object to store.
* @see #storables(Protocol)
*/
void put(StorableObject object);
/**
* Holds protocol-specific data. Where protocol instances aren't readily available, use {@link #put(StorableObject)}.
*
* @param protocol protocol
* @param <T> protocol-specific storables type
* @return protocol storables
* @throws IllegalArgumentException if the protocol does not have a valid return value for {@link Protocol#index()}
*/
<T extends ProtocolStorables> T storables(Protocol<?, ?, ?, ?> protocol);
/**
* Holds protocol-specific data. Prefer using {@link #storables(Protocol)} where possible.
*
* @param protocolClass protocol class
* @param <T> protocol-specific storables type
* @return protocol storables
* @throws IllegalArgumentException if the protocol does not have a valid return value for {@link Protocol#index()}
* @see #storables(Protocol)
*/
default <T extends ProtocolStorables> T storables(final Class<? extends Protocol> protocolClass) {
return storables(Via.getManager().getProtocolManager().getProtocol(protocolClass));
}
/**
* Returns a collection of entity trackers currently registered.
*
* @return collection of entity trackers currently registered
*/
@Deprecated(forRemoval = true)
Collection<EntityTracker> getEntityTrackers();
@Deprecated(forRemoval = true)
@Nullable <T extends EntityTracker> T getEntityTracker(Class<? extends Protocol> protocolClass);
/**
* Returns the entity tracker by the given protocol class if present.
*
* @param protocolClass protocol class
* @param <T> entity tracker type
* @param protocol protocol
* @param <T> entity tracker type
* @return entity tracker if present
* @see com.viaversion.viaversion.api.rewriter.EntityRewriter#tracker(UserConnection)
*/
@Nullable <T extends EntityTracker> T getEntityTracker(Class<? extends Protocol> protocolClass);
@Nullable <T extends EntityTracker> T getEntityTracker(Protocol<?, ?, ?, ?> protocol);
/**
* Adds an entity tracker to the user connection.
@@ -97,34 +128,43 @@ public interface UserConnection {
*
* @param protocolClass protocol class
* @param tracker entity tracker
* @deprecated use {@link #storables(Protocol)}
*/
@Deprecated(forRemoval = true)
void addEntityTracker(Class<? extends Protocol> protocolClass, EntityTracker tracker);
/**
* Returns the item hasher by the given class if present.
*
* @param protocol protocol
* @param <T> item hasher type
* @return item hasher if present
*/
@Nullable <T extends ItemHasher> T getItemHasher(Protocol<?, ?, ?, ?> protocol);
/**
* Adds an item hasher to the user connection.
*
* @param protocolClass protocol class
* @param itemHasher item hasher
* @deprecated use {@link #storables(Protocol)}
*/
@Deprecated(forRemoval = true)
void addItemHasher(Class<? extends Protocol> protocolClass, ItemHasher itemHasher);
/**
* Returns the item hasher by the given protocol class if present.
*
* @param protocolClass protocol class
* @param <T> item hasher type
* @return item hasher if present
*/
@Deprecated(forRemoval = true)
@Nullable <T extends ItemHasher> T getItemHasher(Class<? extends Protocol> protocolClass);
/**
* Returns the client world by the given protocol class if present.
* Returns the client world by the given class if present.
*
* @param protocolClass protocol class
* @param <T> client world type
* @return client world if present
* @deprecated use {@link #storables(Protocol)}
*/
@Nullable
@Deprecated(forRemoval = true)
<T extends ClientWorld> T getClientWorld(Class<? extends Protocol> protocolClass);
/**
@@ -134,6 +174,7 @@ public interface UserConnection {
* @param protocolClass protocol class
* @param clientWorld client world
*/
@Deprecated(forRemoval = true)
void addClientWorld(Class<? extends Protocol> protocolClass, ClientWorld clientWorld);
/**
@@ -365,10 +406,10 @@ public interface UserConnection {
}
/**
* Returns a newly generated uuid that will let a packet be passed through without
* Returns a newly generated token that will let a packet be passed through without
* transforming its contents if used together with {@link PacketWrapper#PASSTHROUGH_ID}.
*
* @return generated passthrough token
*/
UUID generatePassthroughToken();
long generatePassthroughToken();
}
@@ -22,88 +22,29 @@
*/
package com.viaversion.viaversion.api.data;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public class FullIdentityMappings implements FullMappings {
private static final String[] EMPTY_ARRAY = new String[0];
private final Object2IntMap<String> stringToId;
private final String[] idToString;
private final Mappings mappings;
public class FullIdentityMappings extends FullMappingsBase {
public FullIdentityMappings(final IdentifiersPair identifiersPair, final Mappings mappings) {
Preconditions.checkNotNull(mappings, "Mappings cannot be null");
this.mappings = mappings;
this.stringToId = FullMappingsBase.toInverseMap(identifiersPair.unmapped());
this.idToString = identifiersPair.unmapped().toArray(EMPTY_ARRAY);
this(toInverseMap(identifiersPair.unmapped()), identifiersPair.unmapped().stream().map(Key::of).toArray(Key[]::new), mappings);
}
private FullIdentityMappings(final Object2IntMap<String> stringToId, final String[] idToString, final Mappings mappings) {
this.stringToId = stringToId;
this.idToString = idToString;
this.mappings = mappings;
}
@Override
public int id(final String identifier) {
return stringToId.getInt(Key.stripMinecraftNamespace(identifier));
}
@Override
public int mappedId(final String mappedIdentifier) {
return id(mappedIdentifier);
}
@Override
public String identifier(final int id) {
if (id < 0 || id >= idToString.length) {
return null;
}
final String identifier = idToString[id];
return Key.namespaced(identifier);
private FullIdentityMappings(final Object2IntMap<String> stringToId, final Key[] idToKey, final Mappings mappings) {
super(stringToId, stringToId, idToKey, idToKey, mappings);
}
@Override
public @Nullable String identifier(final String mappedIdentifier) {
final int mappedId = mappedId(mappedIdentifier);
if (mappedId == -1) {
return null;
}
final int id = mappings.inverse().getNewId(mappedId);
return id != -1 ? identifier(id) : null;
return mappedIdentifier(mappedIdentifier);
}
@Override
public @Nullable String mappedIdentifier(final int mappedId) {
return identifier(mappedId);
}
@Override
public @Nullable String mappedIdentifier(final String identifier) {
return identifier(identifier);
}
@Override
public int getNewId(int id) {
return mappings.getNewId(id);
}
@Override
public void setNewId(int id, int mappedId) {
mappings.setNewId(id, mappedId);
}
@Override
public int size() {
return mappings.size();
}
@Override
public int mappedSize() {
return mappings.mappedSize();
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
return mappedKeyFromKey(mappedIdentifier);
}
@Override
@@ -117,7 +58,7 @@ public class FullIdentityMappings implements FullMappings {
}
@Override
public FullMappings inverse() {
return new FullIdentityMappings(stringToId, idToString, mappings.inverse());
public FullIdentityMappings inverse() {
return new FullIdentityMappings(stringToId, idToKey, mappings.inverse());
}
}
@@ -22,6 +22,7 @@
*/
package com.viaversion.viaversion.api.data;
import com.viaversion.viaversion.util.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@@ -51,7 +52,10 @@ public interface FullMappings extends BiMappings {
* @param id unmapped id
* @return unmapped string identifier, or null if out of bounds
*/
@Nullable String identifier(int id);
default @Nullable String identifier(int id) {
final Key key = keyFromId(id);
return key != null ? key.toString() : null;
}
/**
* Returns the unmapped string identifier for the given mapped identifier.
@@ -59,7 +63,10 @@ public interface FullMappings extends BiMappings {
* @param mappedIdentifier mapped identifier
* @return unmapped string identifier, or null if not found
*/
@Nullable String identifier(String mappedIdentifier);
default @Nullable String identifier(String mappedIdentifier) {
final Key key = keyFromMappedKey(mappedIdentifier);
return key != null ? key.toString() : null;
}
/**
* Returns the mapped string identifier for the given mapped id.
@@ -67,7 +74,10 @@ public interface FullMappings extends BiMappings {
* @param mappedId mapped id
* @return mapped string identifier, or null if out of bounds
*/
@Nullable String mappedIdentifier(int mappedId);
default @Nullable String mappedIdentifier(int mappedId) {
final Key key = mappedKeyFromMappedId(mappedId);
return key != null ? key.toString() : null;
}
/**
* Returns the mapped string identifier for the given unmapped identifier.
@@ -75,7 +85,42 @@ public interface FullMappings extends BiMappings {
* @param identifier unmapped identifier
* @return mapped string identifier, or null if not found
*/
@Nullable String mappedIdentifier(String identifier);
default @Nullable String mappedIdentifier(String identifier) {
final Key key = mappedKeyFromKey(identifier);
return key != null ? key.toString() : null;
}
/**
* Returns the unmapped key for the given mapped id.
*
* @param id unmapped id
* @return unmapped key, or null if out of bounds
*/
@Nullable Key keyFromId(int id);
/**
* Returns the unmapped key for the given mapped identifier.
*
* @param mappedIdentifier mapped identifier
* @return unmapped key, or null if not found
*/
@Nullable Key keyFromMappedKey(String mappedIdentifier);
/**
* Returns the mapped key for the given mapped id.
*
* @param mappedId mapped id
* @return mapped key, or null if out of bounds
*/
@Nullable Key mappedKeyFromMappedId(int mappedId);
/**
* Returns the mapped key for the given unmapped identifier.
*
* @param identifier unmapped identifier
* @return mapped key, or null if not found
*/
@Nullable Key mappedKeyFromKey(String identifier);
/**
* Returns whether the int id mappings are identity mappings.
@@ -31,27 +31,26 @@ import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
public class FullMappingsBase implements FullMappings {
private static final String[] EMPTY_ARRAY = new String[0];
private final Object2IntMap<String> stringToId;
private final Object2IntMap<String> mappedStringToId;
private final String[] idToString;
private final String[] mappedIdToString;
private final Mappings mappings;
protected final Object2IntMap<String> stringToId;
protected final Object2IntMap<String> mappedStringToId;
protected final Key[] idToKey;
protected final Key[] mappedIdToKey;
protected final Mappings mappings;
public FullMappingsBase(final IdentifiersPair identifiersPair, final Mappings mappings) {
Preconditions.checkNotNull(mappings, "Mappings cannot be null");
this.mappings = mappings;
this.stringToId = toInverseMap(identifiersPair.unmapped());
this.idToString = identifiersPair.unmapped().toArray(EMPTY_ARRAY);
this.idToKey = identifiersPair.unmapped().stream().map(Key::of).toArray(Key[]::new);
this.mappedStringToId = toInverseMap(identifiersPair.mapped());
this.mappedIdToString = identifiersPair.mapped().toArray(EMPTY_ARRAY);
this.mappedIdToKey = identifiersPair.mapped().stream().map(Key::of).toArray(Key[]::new);
}
private FullMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final String[] idToString, final String[] mappedIdToString, final Mappings mappings) {
protected FullMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final Key[] idToKey, final Key[] mappedIdToKey, final Mappings mappings) {
this.stringToId = stringToId;
this.mappedStringToId = mappedStringToId;
this.idToString = idToString;
this.mappedIdToString = mappedIdToString;
this.idToKey = idToKey;
this.mappedIdToKey = mappedIdToKey;
this.mappings = mappings;
}
@@ -79,45 +78,43 @@ public class FullMappingsBase implements FullMappings {
}
@Override
public String identifier(final int id) {
if (id < 0 || id >= idToString.length) {
public @Nullable Key keyFromId(final int id) {
if (id < 0 || id >= idToKey.length) {
return null;
}
final String identifier = idToString[id];
return Key.namespaced(identifier);
return idToKey[id];
}
@Override
public @Nullable String identifier(final String mappedIdentifier) {
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
final int mappedId = mappedId(mappedIdentifier);
if (mappedId == -1) {
return null;
}
final int id = mappings.inverse().getNewId(mappedId);
return id != -1 ? identifier(id) : null;
return id != -1 ? keyFromId(id) : null;
}
@Override
public @Nullable String mappedIdentifier(final int mappedId) {
if (mappedId < 0 || mappedId >= mappedIdToString.length) {
public @Nullable Key mappedKeyFromMappedId(final int mappedId) {
if (mappedId < 0 || mappedId >= mappedIdToKey.length) {
return null;
}
final String identifier = mappedIdToString[mappedId];
return Key.namespaced(identifier);
return mappedIdToKey[mappedId];
}
@Override
public @Nullable String mappedIdentifier(final String identifier) {
public @Nullable Key mappedKeyFromKey(final String identifier) {
final int id = id(identifier);
if (id == -1) {
return null;
}
final int mappedId = mappings.getNewId(id);
return mappedId != -1 ? mappedIdentifier(mappedId) : null;
return mappedId != -1 ? mappedKeyFromMappedId(mappedId) : null;
}
@Override
@@ -147,6 +144,6 @@ public class FullMappingsBase implements FullMappings {
@Override
public FullMappings inverse() {
return new FullMappingsBase(mappedStringToId, stringToId, mappedIdToString, idToString, mappings.inverse());
return new FullMappingsBase(mappedStringToId, stringToId, mappedIdToKey, idToKey, mappings.inverse());
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.data;
import com.viaversion.nbt.tag.ByteArrayTag;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.function.IntConsumer;
/**
* Reads sets of non-negative ints stored as ranges, packed into a byte array of varints.
* Each range is stored as an offset and length pair. The offset is the start of the first range and the
* difference to the previous range's exclusive end; the length is the inclusive end minus the start.
*/
public final class IdRanges {
/**
* Returns the encoded id set as an int set.
*
* @param rangesTag tag with the encoded ranges
* @return decoded ids
*/
public static IntSet decode(final ByteArrayTag rangesTag) {
final IntSet ids = new IntOpenHashSet();
forEachId(rangesTag, ids::add);
return ids;
}
public static IntList decodeAsList(final ByteArrayTag rangesTag) {
final IntList ids = new IntArrayList();
forEachId(rangesTag, ids::add);
return ids;
}
public static void forEachId(final ByteArrayTag rangesTag, final IntConsumer consumer) {
final ByteBuf buf = Unpooled.wrappedBuffer(rangesTag.getValue());
int prevEnd = 0;
while (buf.isReadable()) {
final int start = prevEnd + Types.VAR_INT.readPrimitive(buf);
final int end = start + Types.VAR_INT.readPrimitive(buf);
for (int id = start; id <= end; id++) {
consumer.accept(id);
}
prevEnd = end + 1;
}
}
}
@@ -0,0 +1,111 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.data;
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Full mappings for versions where required identifiers hadn't yet followed the resource location format (i.e., pre-1.9.4 sounds).
*/
public class LegacyFullStringMappingsBase extends FullMappingsBase {
private final String[] idToString;
private final String[] mappedIdToString;
public LegacyFullStringMappingsBase(final IdentifiersPair identifiersPair, final Mappings mappings) {
super(toInverseMap(identifiersPair.unmapped()), toInverseMap(identifiersPair.mapped()), new Key[0], new Key[0], mappings);
this.idToString = identifiersPair.unmapped().toArray(new String[0]);
this.mappedIdToString = identifiersPair.mapped().toArray(new String[0]);
}
private LegacyFullStringMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final String[] idToKey, final String[] mappedIdToString, final Mappings mappings) {
super(stringToId, mappedStringToId, new Key[0], new Key[0], mappings);
this.idToString = idToKey;
this.mappedIdToString = mappedIdToString;
}
@Override
public String identifier(final int id) {
if (id < 0 || id >= idToString.length) {
return null;
}
return Key.namespaced(idToString[id]);
}
@Override
public @Nullable String identifier(final String mappedIdentifier) {
final int mappedId = mappedId(mappedIdentifier);
if (mappedId == -1) {
return null;
}
final int id = mappings.inverse().getNewId(mappedId);
return id != -1 ? identifier(id) : null;
}
@Override
public @Nullable String mappedIdentifier(final int mappedId) {
if (mappedId < 0 || mappedId >= mappedIdToString.length) {
return null;
}
return Key.namespaced(mappedIdToString[mappedId]);
}
@Override
public @Nullable String mappedIdentifier(final String identifier) {
final int id = id(identifier);
if (id == -1) {
return null;
}
final int mappedId = mappings.getNewId(id);
return mappedId != -1 ? mappedIdentifier(mappedId) : null;
}
@Override
public @Nullable Key keyFromId(final int id) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable Key mappedKeyFromMappedId(final int mappedId) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable Key mappedKeyFromKey(final String identifier) {
throw new UnsupportedOperationException();
}
@Override
public FullMappings inverse() {
return new LegacyFullStringMappingsBase(mappedStringToId, stringToId, mappedIdToString, idToString, mappings.inverse());
}
}
@@ -29,6 +29,7 @@ import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface MappingData {
@@ -190,15 +191,27 @@ public interface MappingData {
*/
@Nullable IntSet changedBlocks();
default boolean hasBlockChanged(final int blockId) {
return changedBlocks() != null && changedBlocks().contains(blockId);
}
@Nullable Set<String> changedEnvironmentAttributes();
/**
* Type of mappings. Currently only relevant for ops writing of generic holder classes and expanded when needed.
*/
enum MappingType implements RegistryKey {
ITEM, BLOCK, SOUND, ENTITY_TYPE;
private final Key key;
MappingType() {
this.key = Key.of(name().toLowerCase(Locale.ROOT));
}
@Override
public Key key() {
return Key.of(name().toLowerCase(Locale.ROOT));
public Key registry() {
return this.key;
}
}
}
@@ -22,8 +22,11 @@
*/
package com.viaversion.viaversion.api.data;
import com.viaversion.nbt.tag.ByteArrayTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntArrayTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
@@ -35,7 +38,9 @@ import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.Nullable;
public class MappingDataBase implements MappingData {
@@ -63,6 +68,7 @@ public class MappingDataBase implements MappingData {
* Set of block ids that have had type or property changes.
*/
protected IntSet changedBlocks;
protected Set<String> changedEnvironmentAttributes;
public MappingDataBase(final String unmappedVersion, final String mappedVersion) {
this.unmappedVersion = unmappedVersion;
@@ -118,6 +124,7 @@ public class MappingDataBase implements MappingData {
loadTags(RegistryType.ITEM, tagsTag);
loadTags(RegistryType.BLOCK, tagsTag);
loadTags(RegistryType.ENTITY, tagsTag);
loadTags(RegistryType.FLUID, tagsTag);
}
final IntArrayTag changedBlocks = data.getIntArrayTag("changed_blocks");
@@ -125,6 +132,11 @@ public class MappingDataBase implements MappingData {
this.changedBlocks = new IntOpenHashSet(changedBlocks.getValue());
}
final ListTag<StringTag> changedEnvironmentAttributes = data.getListTag("changed_environment_attributes", StringTag.class);
if (changedEnvironmentAttributes != null) {
this.changedEnvironmentAttributes = changedEnvironmentAttributes.stream().map(StringTag::getValue).collect(Collectors.toSet());
}
loadExtras(data);
}
@@ -167,6 +179,10 @@ public class MappingDataBase implements MappingData {
if (mappings == null) {
mappings = new IdentityMappings(identifiersPair.unmapped().size(), identifiersPair.mapped().size());
}
return createFullMappings(identifiersPair, mappings);
}
protected FullMappings createFullMappings(final IdentifiersPair identifiersPair, final Mappings mappings) {
return FullMappingsBase.of(identifiersPair, mappings);
}
@@ -183,8 +199,14 @@ public class MappingDataBase implements MappingData {
final List<TagData> tagsList = new ArrayList<>(tags.size());
for (final Map.Entry<String, Tag> entry : tag.entrySet()) {
final IntArrayTag entries = (IntArrayTag) entry.getValue();
tagsList.add(new TagData(entry.getKey(), entries.getValue()));
final int[] entries;
if (entry.getValue() instanceof final ByteArrayTag rangesTag) {
entries = IdRanges.decodeAsList(rangesTag).toIntArray();
} else {
// Old format / if the values need to be sorted
entries = ((IntArrayTag) entry.getValue()).getValue();
}
tagsList.add(new TagData(entry.getKey(), entries));
}
this.tags.put(type, tagsList);
@@ -349,6 +371,11 @@ public class MappingDataBase implements MappingData {
return changedBlocks;
}
@Override
public @Nullable Set<String> changedEnvironmentAttributes() {
return changedEnvironmentAttributes;
}
protected Logger getLogger() {
return Via.getPlatform().getLogger();
}
@@ -31,13 +31,17 @@ import com.google.gson.JsonSyntaxException;
import com.viaversion.nbt.io.NBTIO;
import com.viaversion.nbt.io.TagReader;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntArrayTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.GsonUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.BufferedInputStream;
@@ -51,6 +55,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -59,15 +64,16 @@ public class MappingDataLoader {
public static final MappingDataLoader INSTANCE = new MappingDataLoader(MappingDataLoader.class, "assets/viaversion/data/");
public static final TagReader<CompoundTag> MAPPINGS_READER = NBTIO.reader(CompoundTag.class).named();
private static final Map<String, String[]> GLOBAL_IDENTIFIER_INDEXES = new HashMap<>();
private static final int VERSION = 2;
private static final byte DIRECT_ID = 0;
private static final byte SHIFTS_ID = 1;
private static final byte CHANGES_ID = 2;
private static final byte IDENTITY_ID = 3;
private final Map<String, CompoundTag> mappingsCache = new HashMap<>();
private final Map<String, CompoundTag> mappingsCache = new ConcurrentHashMap<>();
private final Class<?> dataLoaderClass;
private final String dataPath;
private boolean cacheValid = true;
private volatile boolean cacheValid = true;
public MappingDataLoader(final Class<?> dataLoaderClass, final String dataPath) {
this.dataLoaderClass = dataLoaderClass;
@@ -104,14 +110,14 @@ public class MappingDataLoader {
throw new IllegalArgumentException("Unknown global identifier key: " + registry);
}
if (globalId < 0 || globalId >= array.length) {
throw new IllegalArgumentException("Unknown global identifier index: " + globalId);
throw new IllegalArgumentException("Unknown global identifier index: " + registry + "/" + globalId);
}
return array[globalId];
}
public void clearCache() {
mappingsCache.clear();
cacheValid = false;
mappingsCache.clear();
}
/**
@@ -160,17 +166,14 @@ public class MappingDataLoader {
return loadNBTFromFile(name);
}
CompoundTag data = mappingsCache.get(name);
if (data != null) {
return data;
if (cache) {
// Also blocks concurrent loaders of the same file until the first one has finished
return mappingsCache.computeIfAbsent(name, this::loadNBTFromFile);
}
data = loadNBTFromFile(name);
if (cache && data != null) {
mappingsCache.put(name, data);
}
return data;
// Still do a lookup, but don't store it
final CompoundTag data = mappingsCache.get(name);
return data != null ? data : loadNBTFromFile(name);
}
public @Nullable CompoundTag loadNBT(final String name) {
@@ -211,15 +214,29 @@ public class MappingDataLoader {
return null;
}
final int version = mappingsTag.getInt("version", -1);
if (version != VERSION) {
throw new IllegalArgumentException("Invalid version: " + version);
}
final int mappedSize = tag.getInt("mappedSize", -1);
final byte strategy = tag.getByteTag("id").asByte();
final V mappings;
if (strategy == DIRECT_ID) {
final IntArrayTag valuesTag = tag.getIntArrayTag("val");
return IntArrayMappings.of(valuesTag.getValue(), mappedSize);
// Zigzag varints of the difference to the previous mapped id
final int size = tag.getIntTag("size").asInt();
final ByteBuf buf = Unpooled.wrappedBuffer(tag.getByteArrayTag("val").getValue());
final int[] values = new int[size];
int prev = 0;
for (int i = 0; i < size; i++) {
prev += readZigZagVarInt(buf);
values[i] = prev;
}
return IntArrayMappings.of(values, mappedSize);
} else if (strategy == SHIFTS_ID) {
final int[] shiftsAt = tag.getIntArrayTag("at").getValue();
final int[] shiftsTo = tag.getIntArrayTag("to").getValue();
final ValuePairs pairs = readAtValuePairs(tag);
final int[] shiftsAt = pairs.first;
final int[] shiftsTo = pairs.second;
final int size = tag.getIntTag("size").asInt();
mappings = holderSupplier.get(size);
@@ -242,8 +259,9 @@ public class MappingDataLoader {
}
}
} else if (strategy == CHANGES_ID) {
final int[] changesAt = tag.getIntArrayTag("at").getValue();
final int[] values = tag.getIntArrayTag("val").getValue();
final ValuePairs pairs = readAtValuePairs(tag);
final int[] changesAt = pairs.first;
final int[] values = pairs.second;
final int size = tag.getIntTag("size").asInt();
final boolean fillBetween = tag.get("nofill") == null;
mappings = holderSupplier.get(size);
@@ -262,6 +280,13 @@ public class MappingDataLoader {
// Assign the changed value
addConsumer.addTo(mappings, changedId, values[i]);
}
// Fill the remainder
if (fillBetween && nextUnhandledId != size) {
for (int id = nextUnhandledId; id < size; id++) {
addConsumer.addTo(mappings, id, id);
}
}
} else if (strategy == IDENTITY_ID) {
final IntTag sizeTag = tag.getIntTag("size");
return new IdentityMappings(sizeTag.asInt(), mappedSize);
@@ -271,6 +296,37 @@ public class MappingDataLoader {
return mappingsSupplier.create(mappings, mappedSize);
}
/**
* Reads alternating id and value pairs from varints. The id as the difference to the previous id,
* the value as the zigzag difference to the previous value.
*
* @param tag tag to read the packed values from
* @return array of ids and array of values
*/
private static ValuePairs readAtValuePairs(final CompoundTag tag) {
final ByteBuf buf = Unpooled.wrappedBuffer(tag.getByteArrayTag("val").getValue());
final IntList at = new IntArrayList();
final IntList values = new IntArrayList();
int prevAt = -1;
int prevValue = 0;
while (buf.isReadable()) {
prevAt = prevAt + 1 + Types.VAR_INT.readPrimitive(buf);
prevValue += readZigZagVarInt(buf);
at.add(prevAt);
values.add(prevValue);
}
return new ValuePairs(at.toIntArray(), values.toIntArray());
}
private static int readZigZagVarInt(final ByteBuf buf) {
// https://lemire.me/blog/2022/11/25/making-all-your-integers-positive-with-zigzag-encoding/
// In short, using deltas to previous ids makes the numbers small, and using zigzag encoding means they're all positive.
// Being all positive means we can use varints to make most of the data much shorter than 8 bytes.
// All of that combined ends up having a decent effect when compressed in the jar file as well.
final int value = Types.VAR_INT.readPrimitive(buf);
return (value >>> 1) ^ -(value & 1);
}
public @Nullable List<String> identifiersFromGlobalIds(final CompoundTag mappingsTag, final String key) {
final Mappings mappings = loadMappings(mappingsTag, key);
return mappings != null ? identifiers(mappings, key) : null;
@@ -315,6 +371,10 @@ public class MappingDataLoader {
continue;
}
if (mappedGlobalId == -1) {
throw new IllegalArgumentException("Mapped global id -1 for unmapped global id: " + key + "/" + unmappedIdentifier + " (" + unmappedGlobalId + ")");
}
identity = false;
unmappedIdentifiers.add(unmappedIdentifier);
mappedIdentifiers.add(identifierFromGlobalId(key, mappedGlobalId));
@@ -323,8 +383,9 @@ public class MappingDataLoader {
}
private List<String> identifiers(final Mappings mappings, final String key) {
final List<String> identifiers = new ArrayList<>(mappings.size());
for (int i = 0; i < mappings.size(); i++) {
final int size = mappings.size();
final List<String> identifiers = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final int globalId = mappings.getNewId(i);
identifiers.add(identifierFromGlobalId(key, globalId));
}
@@ -401,4 +462,7 @@ public class MappingDataLoader {
this(identifiers, identifiers, true);
}
}
private record ValuePairs(int[] first, int[] second) {
}
}
@@ -23,7 +23,9 @@
package com.viaversion.viaversion.api.data.entity;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.KeyMappings;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -43,8 +45,9 @@ public interface EntityTracker {
*
* @param id entity id
* @param type entity type
* @return tracked entity
*/
void addEntity(int id, EntityType type);
TrackedEntity addEntity(int id, EntityType type);
/**
* Returns whether the entity is currently tracked.
@@ -75,8 +78,9 @@ public interface EntityTracker {
* Untracks an entity.
*
* @param id entity id
* @return previously tracked entity
*/
void removeEntity(int id);
@Nullable TrackedEntity removeEntity(int id);
/**
* Clears stored entity types and data, only leaving behind the client entity.
@@ -88,25 +92,6 @@ public interface EntityTracker {
*/
void clear();
/**
* Returns the stored entity data if an entity with the id is tracked, else null.
* If no data has been initialized yet, it will be done and returned by this method.
*
* @param id entity id
* @return stored entity data if an entity with the id is tracked, else null
* @throws IllegalArgumentException if entity data storage has not been enabled via the implementation
*/
@Nullable StoredEntityData entityData(int id);
/**
* Returns stored entity data if it has previously been initialized by {@link #entityData(int)}, else null.
*
* @param id entity id
* @return stored entity data if it has previously been initialized by {@link #entityData(int)}
* @throws IllegalArgumentException if entity data storage has not been enabled via the implementation
*/
@Nullable StoredEntityData entityDataIfPresent(int id);
/**
* Returns whether the client entity id has been set.
*
@@ -204,6 +189,24 @@ public interface EntityTracker {
void setBiomesSent(int biomesSent);
/**
* Returns the cached chunk type for the current dimension if set.
*
* @param mapped whether to return the mapped chunk type
* @return cached chunk type
*/
@Nullable Type<Chunk> chunkType(boolean mapped);
/**
* Caches the chunk type for the current dimension.
* <p>
* Invalidates when the world section height or the number of biomes change.
*
* @param mapped whether to set the mapped chunk type
* @param chunkType chunk type
*/
void setChunkType(boolean mapped, @Nullable Type<Chunk> chunkType);
EntityType playerType();
@Nullable DimensionData dimensionData(String dimension);
@@ -22,7 +22,9 @@
*/
package com.viaversion.viaversion.api.data.entity;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface TrackedEntity {
@@ -34,18 +36,37 @@ public interface TrackedEntity {
EntityType entityType();
/**
* Object to hold arbitrary additional data.
* Checks if the storage contains an object of the given type.
*
* @return entity data
* @param objectClass object class to check
* @return whether an object of the given type is in the storage
*/
StoredEntityData data();
boolean has(Class<? extends StorableObject> objectClass);
/**
* Returns whether the stored entity currently has any additional data.
* Returns an object from the storage if present.
*
* @return whether the stored entity currently has additional data
* @param objectClass class of the object to get
* @param <T> object type
* @return object if present
*/
boolean hasData();
@Nullable <T extends StorableObject> T get(Class<T> objectClass);
/**
* Removes and returns an object from the storage if present.
*
* @param objectClass class of the object to remove
* @param <T> object type
* @return removed object if present
*/
@Nullable <T extends StorableObject> T remove(Class<T> objectClass);
/**
* Stores an object based on its class.
*
* @param object object to store
*/
void put(StorableObject object);
/**
* Returns whether entity data has already been sent at least once for this entity.
@@ -79,8 +79,8 @@ public record PaintingVariant(int width, int height, String assetId, @Nullable T
.write("width", Types.INT, value.width())
.write("height", Types.INT, value.height())
.write("asset_id", Types.IDENTIFIER, Key.of(value.assetId()))
.writeOptional("title", Types.TRUSTED_TAG, value.title())
.writeOptional("author", Types.TRUSTED_TAG, value.author()));
.writeOptional("title", Types.TEXT_COMPONENT_TAG, value.title())
.writeOptional("author", Types.TEXT_COMPONENT_TAG, value.author()));
}
@Override
@@ -34,7 +34,7 @@ import com.viaversion.viaversion.util.Key;
*/
public interface RegistryKey {
Key key();
Key registry();
static RegistryKey of(final String key) {
return new SimpleRegistryKey(Key.of(key));
@@ -43,7 +43,7 @@ public enum RegistryType implements RegistryKey {
static {
for (RegistryType type : getValues()) {
MAP.put(type.identifier, type);
MAP.put(type.identifier(), type);
}
}
@@ -55,18 +55,18 @@ public enum RegistryType implements RegistryKey {
return MAP.get(identifier);
}
private final String identifier;
private final Key key;
RegistryType(final String identifier) {
this.identifier = identifier;
this.key = Key.of(identifier);
}
public String identifier() {
return identifier;
return key.path();
}
@Override
public Key key() {
return Key.of(identifier);
public Key registry() {
return this.key;
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft;
import com.viaversion.viaversion.util.EitherImpl;
public final class ResolvableFloat extends EitherImpl<Float, String> {
private ResolvableFloat(final Float left, final String right) {
super(left, right);
}
public static ResolvableFloat of(final String value) {
return new ResolvableFloat(null, value);
}
public static ResolvableFloat of(final Float value) {
return new ResolvableFloat(value, null);
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft;
import com.viaversion.viaversion.util.EitherImpl;
public final class ResolvableInt extends EitherImpl<Integer, String> {
private ResolvableInt(final Integer left, final String right) {
super(left, right);
}
public static ResolvableInt of(final String value) {
return new ResolvableInt(null, value);
}
public static ResolvableInt of(final Integer value) {
return new ResolvableInt(value, null);
}
}
@@ -24,10 +24,10 @@ package com.viaversion.viaversion.api.minecraft;
import com.viaversion.viaversion.util.Key;
record SimpleRegistryKey(Key key) implements RegistryKey {
record SimpleRegistryKey(Key registry) implements RegistryKey {
@Override
public String toString() {
return key.toString();
return registry.toString();
}
}
@@ -39,4 +39,11 @@ public record SoundEvent(String identifier, @Nullable Float fixedRange) {
}
return holder.updateId(soundRewriteFunction, () -> UNKNOWN);
}
public static @Nullable Holder<SoundEvent> rewriteOptionalHolder(@Nullable final Holder<SoundEvent> holder, final Int2IntFunction soundRewriteFunction) {
if (holder == null) {
return null;
}
return holder.updateId(soundRewriteFunction, () -> null);
}
}
@@ -22,6 +22,9 @@
*/
package com.viaversion.viaversion.api.minecraft.chunks;
import com.viaversion.viaversion.util.CompactArrayUtil;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.function.IntUnaryOperator;
public interface DataPalette {
@@ -146,6 +149,27 @@ public interface DataPalette {
}
}
/**
* Runs over all section coordinate indices that match the given predicate.
*
* @param idPredicate predicate to test ids
* @param coordinateConsumer consumer to run over applicable coordinate indices (not ids!)
* @see #idAt(int)
*/
void forEachMatchingCoordinate(IntPredicate idPredicate, IntConsumer coordinateConsumer);
/**
* Packs the section values into a compact long array as used by the protocol.
*
* @param bitsPerValue bits used per value
* @param entries number of values
* @param rawIds whether to pack raw ids instead of palette indexes (direct palette)
* @return compact long array of the values
*/
default long[] createPackedValues(final int bitsPerValue, final int entries, final boolean rawIds) {
return CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, entries, rawIds ? this::idAt : this::paletteIndexAt);
}
/**
* Returns the size of the palette.
*
@@ -22,9 +22,13 @@
*/
package com.viaversion.viaversion.api.minecraft.chunks;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.util.CompactArrayUtil;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.function.IntUnaryOperator;
public final class DataPaletteImpl implements DataPalette {
@@ -32,8 +36,8 @@ public final class DataPaletteImpl implements DataPalette {
private static final int DEFAULT_INITIAL_SIZE = 16;
private final IntArrayList palette;
private final Int2IntMap inversePalette;
private final int sizeBits;
private Int2IntMap inversePalette;
private ChunkData values;
public DataPaletteImpl(final int valuesLength) {
@@ -43,11 +47,30 @@ public final class DataPaletteImpl implements DataPalette {
public DataPaletteImpl(final int valuesLength, final int initialSize) {
values = new SingleChunkData(valuesLength);
sizeBits = Integer.numberOfTrailingZeros(valuesLength) / 3;
// Pre-size the palette array/map
// Pre-size the palette array
palette = new IntArrayList(initialSize);
// To get an initial table size of initialSize, need to scale it down by load factor.
inversePalette = new Int2IntOpenHashMap((int) (initialSize * Int2IntOpenHashMap.DEFAULT_LOAD_FACTOR));
inversePalette.defaultReturnValue(-1);
}
private Int2IntMap inversePalette() {
// Lazily build the inverse palette. Most palettes never need it
if (inversePalette == null) {
inversePalette = new Int2IntOpenHashMap(palette.size());
inversePalette.defaultReturnValue(-1);
for (int i = 0; i < palette.size(); i++) {
inversePalette.put(palette.getInt(i), i);
}
}
return inversePalette;
}
private Int2IntMap emptyInversePalette() {
if (inversePalette == null) {
inversePalette = new Int2IntOpenHashMap(palette.size());
inversePalette.defaultReturnValue(-1);
} else {
inversePalette.clear();
}
return inversePalette;
}
@Override
@@ -63,7 +86,7 @@ public final class DataPaletteImpl implements DataPalette {
@Override
public void setIdAt(final int sectionCoordinate, final int id) {
int index = inversePalette.get(id);
int index = inversePalette().get(id);
if (index == -1) {
index = palette.size();
palette.add(id);
@@ -83,6 +106,38 @@ public final class DataPaletteImpl implements DataPalette {
values.set(sectionCoordinate, index);
}
/**
* Stores palette indexes from a compact long array, replacing all current values.
* The data is kept packed unless written to; reads extract bits the slow way, since almost all work will use replaceIds or will only have a few reads.
* <p>
* Only valid for local palettes (bitsPerValue &lt;= 8).
*
* @param data compact long array
* @param bitsPerValue bits per value
* @param valuesPerLong packked values per long
*/
public void setPaletteIndexes(final long[] data, final int bitsPerValue, final int valuesPerLong) {
Preconditions.checkArgument(bitsPerValue <= 8, "Cannot pack global palette");
values = new PackedChunkData(data, bitsPerValue, valuesPerLong, values.size());
}
@Override
public long[] createPackedValues(final int bitsPerValue, final int entries, final boolean rawIds) {
if (rawIds) {
return CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, entries, this::idAt);
}
if (values instanceof final PackedChunkData packed && packed.bitsPerValue == bitsPerValue) {
// Values were never modified and the width still matches - write them back as-is
return packed.data;
}
final int valuesPerLong = (char) (64 / bitsPerValue);
final long[] data = new long[(entries + valuesPerLong - 1) / valuesPerLong];
values.packInto(data, bitsPerValue);
return data;
}
@Override
public int size() {
return palette.size();
@@ -96,7 +151,7 @@ public final class DataPaletteImpl implements DataPalette {
@Override
public void setIdByIndex(final int index, final int id) {
final int oldId = palette.set(index, id);
if (oldId == id) return;
if (oldId == id || inversePalette == null) return;
inversePalette.put(id, index);
if (inversePalette.get(oldId) == index) {
@@ -112,7 +167,7 @@ public final class DataPaletteImpl implements DataPalette {
@Override
public void replaceId(final int oldId, final int newId) {
final int index = inversePalette.remove(oldId);
final int index = inversePalette().remove(oldId);
if (index == -1) return;
inversePalette.put(newId, index);
@@ -130,12 +185,12 @@ public final class DataPaletteImpl implements DataPalette {
return;
}
inversePalette.clear();
int writeIndex = 0;
int[] indexRemap = null;
int firstCollision = 0;
final int[] palette = this.palette.elements();
final Int2IntMap inversePalette = emptyInversePalette();
for (int readIndex = 0; readIndex < oldSize; readIndex++) {
final int mappedId = mapper.applyAsInt(palette[readIndex]);
final int existingIndex = inversePalette.putIfAbsent(mappedId, writeIndex);
@@ -172,14 +227,51 @@ public final class DataPaletteImpl implements DataPalette {
@Override
public void addId(final int id) {
inversePalette.put(id, palette.size());
if (inversePalette != null) {
inversePalette.put(id, palette.size());
}
palette.add(id);
}
private static final ThreadLocal<boolean[]> MATCHING_INDEXES = ThreadLocal.withInitial(() -> new boolean[ChunkSection.SIZE]);
@Override
public void forEachMatchingCoordinate(final IntPredicate idPredicate, final IntConsumer coordinateConsumer) {
final int size = size();
if (size > ChunkSection.SIZE) { // Shouldn't ever happen normally, but is technically possible...
for (int idx = 0, len = this.values.size(); idx < len; idx++) {
if (idPredicate.test(idAt(idx))) {
coordinateConsumer.accept(idx);
}
}
return;
}
// Instead of going through every single coordinate value (4096 for block sections), check the palette ids first.
// Reuse a thread-local array for quick checking against palette indexes.
final boolean[] matching = MATCHING_INDEXES.get();
boolean any = false;
for (int i = 0; i < size; i++) {
final boolean match = idPredicate.test(idByIndex(i));
matching[i] = match;
any |= match;
}
if (!any) {
return;
}
for (int idx = 0, len = this.values.size(); idx < len; idx++) {
if (matching[paletteIndexAt(idx)]) {
coordinateConsumer.accept(idx);
}
}
}
@Override
public void clear() {
palette.clear();
inversePalette.clear();
inversePalette = null;
}
interface ChunkData {
@@ -188,6 +280,11 @@ public final class DataPaletteImpl implements DataPalette {
void set(int idx, int val);
int size();
/**
* Packs all values into the given compact long array.
*/
void packInto(long[] data, int bitsPerValue);
}
private final class SingleChunkData implements ChunkData {
@@ -215,6 +312,11 @@ public final class DataPaletteImpl implements DataPalette {
public int size() {
return size;
}
@Override
public void packInto(final long[] data, final int bitsPerValue) {
// All values are 0, nothing to write
}
}
private final class ByteChunkData implements ChunkData {
@@ -224,6 +326,10 @@ public final class DataPaletteImpl implements DataPalette {
this.data = new byte[size];
}
public ByteChunkData(byte[] data) {
this.data = data;
}
@Override
public int get(int idx) {
return data[idx] & 0xFF;
@@ -244,6 +350,11 @@ public final class DataPaletteImpl implements DataPalette {
public int size() {
return data.length;
}
@Override
public void packInto(final long[] out, final int bitsPerValue) {
CompactArrayUtil.packWithPadding(data, out, bitsPerValue);
}
}
private static final class ShortChunkData implements ChunkData {
@@ -270,6 +381,63 @@ public final class DataPaletteImpl implements DataPalette {
public int size() {
return data.length;
}
@Override
public void packInto(final long[] out, final int bitsPerValue) {
CompactArrayUtil.packWithPadding(data, out, bitsPerValue);
}
}
/**
* Raw chunk data as read from the buffer.
*/
private final class PackedChunkData implements ChunkData {
private final long[] data;
private final int bitsPerValue;
private final int valuesPerLong;
private final long maxEntryValue;
private final int size;
private PackedChunkData(final long[] data, final int bitsPerValue, final int valuesPerLong, final int size) {
this.data = data;
this.bitsPerValue = bitsPerValue;
this.valuesPerLong = valuesPerLong;
this.size = size;
this.maxEntryValue = (1L << bitsPerValue) - 1;
}
@Override
public int get(final int idx) {
// Called so rarely that the cheap storage outweighs the more complex getter
final int cellIndex = idx / valuesPerLong;
final int bitIndex = (idx - cellIndex * valuesPerLong) * bitsPerValue;
return (int) (data[cellIndex] >>> bitIndex & maxEntryValue);
}
@Override
public void set(final int idx, final int val) {
// First write access, actually read out for fast value access
final ChunkData byteChunkData = extractData();
values = byteChunkData;
byteChunkData.set(idx, val);
}
private ChunkData extractData() {
final byte[] unpackedData = new byte[size];
CompactArrayUtil.unpackWithPadding(this.data, unpackedData, bitsPerValue);
return new ByteChunkData(unpackedData);
}
@Override
public int size() {
return size;
}
@Override
public void packInto(final long[] out, final int newBitsPerValue) {
// Always called with bitsPerValue != newBitsPerValue, which should only really happen if the server sends an unncessarily high bit width,
// or if the number of ids shrunk in replaceIds. Rare enough to not need its own special method
extractData().packInto(out, newBitsPerValue);
}
}
}
@@ -38,6 +38,8 @@ public interface CodecContext {
UserConnection connection();
Protocol<?, ?, ?, ?> protocol();
interface RegistryAccess {
UserConnection connection();
@@ -58,7 +58,9 @@ public interface Ops {
void writeMap(Consumer<MapSerializer> consumer);
<V> void write(final Type<V> type, final V value);
default <V> void write(final Type<V> type, final V value) {
type.write(this, value);
}
interface ListSerializer {
@@ -39,8 +39,8 @@ final class RegistryAccessImpl implements CodecContext.RegistryAccess {
private final boolean mapped;
RegistryAccessImpl(final Protocol<?, ?, ?, ?> protocol, final UserConnection connection) {
this.mappingData = Preconditions.checkNotNull(protocol.getMappingData());
this.entityTracker = Preconditions.checkNotNull(protocol.getEntityRewriter().tracker(connection));
this.mappingData = Preconditions.checkNotNull(protocol.getMappingData(), "No mapping data present for protocol");
this.entityTracker = Preconditions.checkNotNull(protocol.getEntityRewriter().tracker(connection), "Entity tracker has to be added first");
this.connection = connection;
this.mapped = false;
}
@@ -102,17 +102,16 @@ final class RegistryAccessImpl implements CodecContext.RegistryAccess {
public Key registryKey(final String registry, final int id) {
final KeyMappings mappings = entityTracker.registryKeys(registry);
Preconditions.checkNotNull(mappings, "No registry mappings for registry: %s", registry);
final String identifier = id >= 0 && id < mappings.size() ? mappings.idToKey(id) : null;
return key(identifier, id);
return keyOrUnknown(mappings.key(id), id);
}
private Key key(final FullMappings mappings, final int id) {
final String identifier = mapped ? mappings.mappedIdentifier(id) : mappings.identifier(id);
return key(identifier, id);
final Key key = mapped ? mappings.mappedKeyFromMappedId(id) : mappings.keyFromId(id);
return keyOrUnknown(key, id);
}
private Key key(@Nullable final String identifier, final int id) {
return identifier != null ? Key.of(identifier) : Key.of("viaversion", "unknown/" + id);
private Key keyOrUnknown(@Nullable final Key key, final int id) {
return key != null ? key : Key.of("viaversion", "unknown/" + id);
}
@Override
@@ -270,7 +270,7 @@ public final class StructuredDataContainer implements Copyable {
@Override
public StructuredDataContainer copy() {
final Reference2ObjectOpenHashMap<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>();
final Reference2ObjectOpenHashMap<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>(data.size());
for (final StructuredData<?> value : data.values()) {
map.put(value.key(), value.copy());
}
@@ -30,14 +30,15 @@ import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.PaintingVariant;
import com.viaversion.viaversion.api.minecraft.RegistryKey;
import com.viaversion.viaversion.api.minecraft.ResolvableProfile;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_20_5;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;
import com.viaversion.viaversion.api.minecraft.item.data.AdventureModePredicate;
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrim;
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimMaterial;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrim1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrim26_3;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ArmorTrimMaterial26_3;
import com.viaversion.viaversion.api.minecraft.item.data.AttackRange;
import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.AttributeModifiers1_21;
@@ -46,11 +47,14 @@ import com.viaversion.viaversion.api.minecraft.item.data.Bee;
import com.viaversion.viaversion.api.minecraft.item.data.BlockEntityData;
import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties;
import com.viaversion.viaversion.api.minecraft.item.data.BlocksAttacks;
import com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2;
import com.viaversion.viaversion.api.minecraft.item.data.BreakSound;
import com.viaversion.viaversion.api.minecraft.item.data.BrewingFuel;
import com.viaversion.viaversion.api.minecraft.item.data.Compostable;
import com.viaversion.viaversion.api.minecraft.item.data.consumable.Consumable1_21_2;
import com.viaversion.viaversion.api.minecraft.item.data.CookingFuel;
import com.viaversion.viaversion.api.minecraft.item.data.CustomModelData1_21_4;
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant1_21_2;
import com.viaversion.viaversion.api.minecraft.item.data.DamageResistant26_1;
import com.viaversion.viaversion.api.minecraft.item.data.DamageType;
import com.viaversion.viaversion.api.minecraft.item.data.DeathProtection;
import com.viaversion.viaversion.api.minecraft.item.data.DebugStickState;
import com.viaversion.viaversion.api.minecraft.item.data.DyedColor;
@@ -71,12 +75,14 @@ import com.viaversion.viaversion.api.minecraft.item.data.JukeboxPlayable.Jukebox
import com.viaversion.viaversion.api.minecraft.item.data.KineticWeapon;
import com.viaversion.viaversion.api.minecraft.item.data.LockCode;
import com.viaversion.viaversion.api.minecraft.item.data.LodestoneTracker;
import com.viaversion.viaversion.api.minecraft.item.data.MobVisibility;
import com.viaversion.viaversion.api.minecraft.item.data.PiercingWeapon;
import com.viaversion.viaversion.api.minecraft.item.data.PotDecorations;
import com.viaversion.viaversion.api.minecraft.item.data.PotDecorations1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.PotionContents;
import com.viaversion.viaversion.api.minecraft.item.data.ProvidesBannerPatterns;
import com.viaversion.viaversion.api.minecraft.item.data.ProvidesTrimMaterial;
import com.viaversion.viaversion.api.minecraft.item.data.trim.ProvidesTrimMaterial1_20_5;
import com.viaversion.viaversion.api.minecraft.item.data.Repairable;
import com.viaversion.viaversion.api.minecraft.item.data.SignText;
import com.viaversion.viaversion.api.minecraft.item.data.SuspiciousStewEffect;
import com.viaversion.viaversion.api.minecraft.item.data.SwingAnimation;
import com.viaversion.viaversion.api.minecraft.item.data.ToolProperties;
@@ -85,6 +91,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.TropicalFishPattern;
import com.viaversion.viaversion.api.minecraft.item.data.Unbreakable;
import com.viaversion.viaversion.api.minecraft.item.data.UseCooldown;
import com.viaversion.viaversion.api.minecraft.item.data.UseEffects;
import com.viaversion.viaversion.api.minecraft.item.data.VillagerFood;
import com.viaversion.viaversion.api.minecraft.item.data.Weapon;
import com.viaversion.viaversion.api.minecraft.item.data.WritableBook;
import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook;
@@ -117,7 +124,7 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Unit> UNBREAKABLE1_21_5 = new StructuredDataKey<>("unbreakable", Types.EMPTY);
public static final StructuredDataKey<Tag> CUSTOM_NAME = new StructuredDataKey<>("custom_name", Types.TEXT_COMPONENT_TAG);
public static final StructuredDataKey<Float> MINIMUM_ATTACK_CHARGE = new StructuredDataKey<>("minimum_attack_charge", Types.FLOAT);
public static final StructuredDataKey<DamageType> DAMAGE_TYPE1_21_11 = new StructuredDataKey<>("damage_type", DamageType.TYPE1_21_11);
public static final StructuredDataKey<Either<Integer, String>> DAMAGE_TYPE1_21_11 = new StructuredDataKey<>("damage_type", new SynchronizedRegistryEitherType(RegistryKey.of("damage_type")));
public static final StructuredDataKey<Integer> DAMAGE_TYPE26_1 = new StructuredDataKey<>("damage_type", new SynchronizedRegistryValueType(RegistryKey.of("damage_type")));
public static final StructuredDataKey<Tag> ITEM_NAME = new StructuredDataKey<>("item_name", Types.TEXT_COMPONENT_TAG);
public static final StructuredDataKey<ItemModel> ITEM_MODEL = new StructuredDataKey<>("item_model", ItemModel.TYPE);
@@ -144,7 +151,10 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<FoodProperties1_20_5> FOOD1_20_5 = new StructuredDataKey<>("food", FoodProperties1_20_5.TYPE1_20_5);
public static final StructuredDataKey<FoodProperties1_20_5> FOOD1_21 = new StructuredDataKey<>("food", FoodProperties1_20_5.TYPE1_21);
public static final StructuredDataKey<FoodProperties1_21_2> FOOD1_21_2 = new StructuredDataKey<>("food", FoodProperties1_21_2.TYPE);
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE1_21_2 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE);
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE1_21_2 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE1_21_2);
// 1.21.5-1.21.9 only, animation id 5 was still named spear
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE1_21_5 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE1_21_5);
public static final StructuredDataKey<Consumable1_21_2> CONSUMABLE26_3 = new StructuredDataKey<>("consumable", Consumable1_21_2.TYPE26_3);
public static final StructuredDataKey<UseCooldown> USE_COOLDOWN = new StructuredDataKey<>("use_cooldown", UseCooldown.TYPE);
public static final StructuredDataKey<Unit> FIRE_RESISTANT = new StructuredDataKey<>("fire_resistant", Types.EMPTY);
public static final StructuredDataKey<DamageResistant1_21_2> DAMAGE_RESISTANT1_21_2 = new StructuredDataKey<>("damage_resistant", DamageResistant1_21_2.TYPE);
@@ -160,13 +170,18 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Repairable> REPAIRABLE = new StructuredDataKey<>("repairable", Repairable.TYPE);
public static final StructuredDataKey<Unit> GLIDER = new StructuredDataKey<>("glider", Types.EMPTY);
public static final StructuredDataKey<Key> TOOLTIP_STYLE = new StructuredDataKey<>("tooltip_style", Types.IDENTIFIER);
public static final StructuredDataKey<DeathProtection> DEATH_PROTECTION = new StructuredDataKey<>("death_protection", DeathProtection.TYPE);
public static final StructuredDataKey<DeathProtection> DEATH_PROTECTION1_21_2 = new StructuredDataKey<>("death_protection", DeathProtection.TYPE1_21_2);
public static final StructuredDataKey<DeathProtection> DEATH_PROTECTION26_3 = new StructuredDataKey<>("death_protection", DeathProtection.TYPE26_3);
public static final StructuredDataKey<BlocksAttacks> BLOCKS_ATTACKS1_21_5 = new StructuredDataKey<>("blocks_attacks", BlocksAttacks.TYPE1_21_5);
public static final StructuredDataKey<BlocksAttacks> BLOCKS_ATTACKS26_1 = new StructuredDataKey<>("blocks_attacks", BlocksAttacks.TYPE26_1);
public static final StructuredDataKey<PiercingWeapon> PIERCING_WEAPON = new StructuredDataKey<>("piercing_weapon", PiercingWeapon.TYPE);
public static final StructuredDataKey<KineticWeapon> KINETIC_WEAPON = new StructuredDataKey<>("kinetic_weapon", KineticWeapon.TYPE);
public static final StructuredDataKey<SwingAnimation> SWING_ANIMATION = new StructuredDataKey<>("swing_animation", SwingAnimation.TYPE);
public static final StructuredDataKey<SwingAnimation> ATTACK_ANIMATION = new StructuredDataKey<>("attack_animation", SwingAnimation.TYPE);
public static final StructuredDataKey<SwingAnimation> INTERACT_ANIMATION = new StructuredDataKey<>("interact_animation", SwingAnimation.TYPE);
public static final StructuredDataKey<Integer> ADDITIONAL_TRADE_COST = new StructuredDataKey<>("additional_trade_cost", Types.VAR_INT);
public static final StructuredDataKey<Integer> BLOCK_TRANSFORMER = new StructuredDataKey<>("block_transformer", new SynchronizedRegistryValueType(RegistryKey.of("block_transformer")));
public static final StructuredDataKey<VillagerFood> VILLAGER_FOOD = new StructuredDataKey<>("villager_food", VillagerFood.TYPE);
public static final StructuredDataKey<Enchantments> STORED_ENCHANTMENTS1_20_5 = new StructuredDataKey<>("stored_enchantments", Enchantments.TYPE1_20_5);
public static final StructuredDataKey<Enchantments> STORED_ENCHANTMENTS1_21_5 = new StructuredDataKey<>("stored_enchantments", Enchantments.TYPE1_21_5);
public static final StructuredDataKey<Integer> DYE = new StructuredDataKey<>("dye", EnumTypes.DYE_COLOR);
@@ -182,10 +197,11 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<SuspiciousStewEffect[]> SUSPICIOUS_STEW_EFFECTS = new StructuredDataKey<>("suspicious_stew_effects", SuspiciousStewEffect.ARRAY_TYPE);
public static final StructuredDataKey<WritableBook> WRITABLE_BOOK_CONTENT = new StructuredDataKey<>("writable_book_content", WritableBook.TYPE);
public static final StructuredDataKey<WrittenBook> WRITTEN_BOOK_CONTENT = new StructuredDataKey<>("written_book_content", WrittenBook.TYPE);
public static final StructuredDataKey<ArmorTrim> TRIM1_20_5 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_20_5);
public static final StructuredDataKey<ArmorTrim> TRIM1_21_2 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_21_2);
public static final StructuredDataKey<ArmorTrim> TRIM1_21_4 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_21_4);
public static final StructuredDataKey<ArmorTrim> TRIM1_21_5 = new StructuredDataKey<>("trim", ArmorTrim.TYPE1_21_5);
public static final StructuredDataKey<ArmorTrim1_20_5> TRIM1_20_5 = new StructuredDataKey<>("trim", ArmorTrim1_20_5.TYPE1_20_5);
public static final StructuredDataKey<ArmorTrim1_20_5> TRIM1_21_2 = new StructuredDataKey<>("trim", ArmorTrim1_20_5.TYPE1_21_2);
public static final StructuredDataKey<ArmorTrim1_20_5> TRIM1_21_4 = new StructuredDataKey<>("trim", ArmorTrim1_20_5.TYPE1_21_4);
public static final StructuredDataKey<ArmorTrim1_20_5> TRIM1_21_5 = new StructuredDataKey<>("trim", ArmorTrim1_20_5.TYPE1_21_5);
public static final StructuredDataKey<ArmorTrim26_3> TRIM26_3 = new StructuredDataKey<>("trim", ArmorTrim26_3.TYPE);
public static final StructuredDataKey<DebugStickState> DEBUG_STICK_STATE = new StructuredDataKey<>("debug_stick_state", DebugStickState.TYPE);
public static final StructuredDataKey<CompoundTag> ENTITY_DATA1_20_5 = new StructuredDataKey<>("entity_data", Types.COMPOUND_TAG);
public static final StructuredDataKey<EntityData> ENTITY_DATA1_21_9 = new StructuredDataKey<>("entity_data", EntityData.TYPE);
@@ -193,11 +209,13 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<CompoundTag> BLOCK_ENTITY_DATA1_20_5 = new StructuredDataKey<>("block_entity_data", Types.COMPOUND_TAG);
public static final StructuredDataKey<BlockEntityData> BLOCK_ENTITY_DATA1_21_9 = new StructuredDataKey<>("block_entity_data", BlockEntityData.TYPE);
public static final StructuredDataKey<Holder<Instrument1_20_5>> INSTRUMENT1_20_5 = new StructuredDataKey<>("instrument", Instrument1_20_5.TYPE);
public static final StructuredDataKey<Holder<Instrument1_21_2>> INSTRUMENT1_21_2 = new StructuredDataKey<>("instrument", Instrument1_21_2.TYPE);
public static final StructuredDataKey<Holder<Instrument1_21_2>> INSTRUMENT1_21_2 = new StructuredDataKey<>("instrument", Instrument1_21_2.TYPE1_21_2);
public static final StructuredDataKey<EitherHolder<Instrument1_21_2>> INSTRUMENT1_21_5 = new StructuredDataKey<>("instrument", Instrument1_21_2.EITHER_HOLDER_TYPE);
public static final StructuredDataKey<Holder<Instrument1_21_2>> INSTRUMENT26_1 = new StructuredDataKey<>("instrument", Instrument1_21_2.TYPE);
public static final StructuredDataKey<ProvidesTrimMaterial> PROVIDES_TRIM_MATERIAL1_21_5 = new StructuredDataKey<>("provides_trim_material", ProvidesTrimMaterial.TYPE);
public static final StructuredDataKey<Holder<ArmorTrimMaterial>> PROVIDES_TRIM_MATERIAL26_1 = new StructuredDataKey<>("provides_trim_material", ArmorTrimMaterial.TYPE1_21_5);
public static final StructuredDataKey<Holder<Instrument1_21_2>> INSTRUMENT26_1 = new StructuredDataKey<>("instrument", Instrument1_21_2.TYPE1_21_2);
public static final StructuredDataKey<Holder<Instrument1_21_2>> INSTRUMENT26_3 = new StructuredDataKey<>("instrument", Instrument1_21_2.TYPE26_3);
public static final StructuredDataKey<ProvidesTrimMaterial1_20_5> PROVIDES_TRIM_MATERIAL1_21_5 = new StructuredDataKey<>("provides_trim_material", ProvidesTrimMaterial1_20_5.TYPE);
public static final StructuredDataKey<Holder<ArmorTrimMaterial1_20_5>> PROVIDES_TRIM_MATERIAL26_1 = new StructuredDataKey<>("provides_trim_material", ArmorTrimMaterial1_20_5.TYPE1_21_5);
public static final StructuredDataKey<Holder<ArmorTrimMaterial26_3>> PROVIDES_TRIM_MATERIAL26_3 = new StructuredDataKey<>("provides_trim_material", ArmorTrimMaterial26_3.TYPE);
public static final StructuredDataKey<Integer> OMINOUS_BOTTLE_AMPLIFIER = new StructuredDataKey<>("ominous_bottle_amplifier", Types.VAR_INT);
public static final StructuredDataKey<JukeboxPlayable> JUKEBOX_PLAYABLE1_21 = new StructuredDataKey<>("jukebox_playable", JukeboxPlayable.TYPE1_21);
public static final StructuredDataKey<JukeboxPlayable> JUKEBOX_PLAYABLE1_21_5 = new StructuredDataKey<>("jukebox_playable", JukeboxPlayable.TYPE1_21_5);
@@ -213,14 +231,24 @@ public record StructuredDataKey<T>(String identifier, Type<T> type) {
public static final StructuredDataKey<Key> NOTE_BLOCK_SOUND = new StructuredDataKey<>("note_block_sound", Types.IDENTIFIER);
public static final StructuredDataKey<BannerPatternLayer[]> BANNER_PATTERNS = new StructuredDataKey<>("banner_patterns", BannerPatternLayer.ARRAY_TYPE);
public static final StructuredDataKey<Integer> BASE_COLOR = new StructuredDataKey<>("base_color", EnumTypes.DYE_COLOR);
public static final StructuredDataKey<PotDecorations> POT_DECORATIONS = new StructuredDataKey<>("pot_decorations", PotDecorations.TYPE);
public static final StructuredDataKey<PotDecorations1_20_5> POT_DECORATIONS1_20_5 = new StructuredDataKey<>("pot_decorations", PotDecorations1_20_5.TYPE);
// 26.3+ pot decorations are in their own key classes
public static final StructuredDataKey<BlockStateProperties> BLOCK_STATE = new StructuredDataKey<>("block_state", BlockStateProperties.TYPE);
public static final StructuredDataKey<Bee[]> BEES1_20_5 = new StructuredDataKey<>("bees", Bee.ARRAY_TYPE1_20_5);
public static final StructuredDataKey<Bee[]> BEES1_21_9 = new StructuredDataKey<>("bees", Bee.ARRAY_TYPE1_21_9);
public static final StructuredDataKey<Tag> LOCK1_20_5 = new StructuredDataKey<>("lock", Types.TAG);
public static final StructuredDataKey<LockCode> LOCK1_21_2 = new StructuredDataKey<>("lock", LockCode.TYPE);
public static final StructuredDataKey<CompoundTag> CONTAINER_LOOT = new StructuredDataKey<>("container_loot", Types.COMPOUND_TAG);
public static final StructuredDataKey<Holder<SoundEvent>> BREAK_SOUND = new StructuredDataKey<>("break_sound", Types.SOUND_EVENT);
public static final StructuredDataKey<BreakSound> BREAK_SOUND = new StructuredDataKey<>("break_sound", BreakSound.TYPE);
public static final StructuredDataKey<Compostable> COMPOSTABLE = new StructuredDataKey<>("compostable", Compostable.TYPE);
public static final StructuredDataKey<CookingFuel> COOKING_FUEL = new StructuredDataKey<>("cooking_fuel", CookingFuel.TYPE);
public static final StructuredDataKey<BrewingFuel> BREWING_FUEL = new StructuredDataKey<>("brewing_fuel", BrewingFuel.TYPE);
public static final StructuredDataKey<MobVisibility> MOB_VISIBILITY = new StructuredDataKey<>("mob_visibility", MobVisibility.TYPE);
public static final StructuredDataKey<Integer> PROVIDES_POTTERY_PATTERN = new StructuredDataKey<>("provides_pottery_pattern", new SynchronizedRegistryValueType(RegistryKey.of("decorated_pot_pattern")));
public static final StructuredDataKey<SignText> SIGN_TEXT_FRONT = new StructuredDataKey<>("sign_text_front", SignText.TYPE);
public static final StructuredDataKey<SignText> SIGN_TEXT_BACK = new StructuredDataKey<>("sign_text_back", SignText.TYPE);
public static final StructuredDataKey<Unit> WAXED = new StructuredDataKey<>("waxed", Types.EMPTY);
public static final StructuredDataKey<Integer> CUSHION_COLOR = new StructuredDataKey<>("cushion/color", EnumTypes.DYE_COLOR);
public static final StructuredDataKey<Integer> VILLAGER_VARIANT = new StructuredDataKey<>("villager/variant", EnumTypes.VILLAGER_TYPE);
public static final StructuredDataKey<Integer> WOLF_VARIANT = new StructuredDataKey<>("wolf/variant", new SynchronizedRegistryValueType(RegistryKey.of("wolf_variant")));
@@ -33,8 +33,9 @@ public class StructuredDataKeys1_20_5 extends VersionedStructuredDataKeys {
public final StructuredDataKey<Item[]> bundleContents;
public StructuredDataKeys1_20_5(final VersionedTypesHolder types) {
this.container = add("container", types.itemArray());
this.chargedProjectiles = add("charged_projectiles", types.itemArray());
this.bundleContents = add("bundle_contents", types.itemArray());
// Also includes later vanilla limits
this.container = add("container", types.itemArray().withMaxLength(256));
this.chargedProjectiles = add("charged_projectiles", types.itemArray().withMaxLength(1024));
this.bundleContents = add("bundle_contents", types.itemArray().withMaxLength(1024));
}
}
@@ -36,8 +36,8 @@ public class StructuredDataKeys1_21_2 extends VersionedStructuredDataKeys {
public StructuredDataKeys1_21_2(final VersionedTypesHolder types) {
this.container = add("container", new ContainerContentsType(types.optionalItemTemplate()));
this.chargedProjectiles = add("charged_projectiles", types.itemTemplateArray());
this.bundleContents = add("bundle_contents", types.itemTemplateArray());
this.chargedProjectiles = add("charged_projectiles", types.itemTemplateArray().withMaxLength(1024));
this.bundleContents = add("bundle_contents", types.itemTemplateArray().withMaxLength(1024));
this.useRemainder = add("use_remainder", types.itemTemplate());
}
}
@@ -0,0 +1,37 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.data.version;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
public class StructuredDataKeys26_2 extends StructuredDataKeys1_21_11 {
public final StructuredDataKey<Item> sulfurCubeContent;
public StructuredDataKeys26_2(final VersionedTypesHolder types) {
super(types);
this.sulfurCubeContent = add("sulfur_cube_content", types.itemTemplate());
}
}
@@ -0,0 +1,37 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.data.version;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.data.PotDecorations26_3;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
public class StructuredDataKeys26_3 extends StructuredDataKeys26_2 {
public final StructuredDataKey<PotDecorations26_3> potDecorations;
public StructuredDataKeys26_3(final VersionedTypesHolder types) {
super(types);
this.potDecorations = add("pot_decorations", new PotDecorations26_3.PotDecorationsType(types.optionalItemTemplate()));
}
}
@@ -0,0 +1,332 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.entities;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.util.EntityTypeUtil;
import com.viaversion.viaversion.util.Key;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.Nullable;
public enum EntityTypes26_2 implements EntityType {
ENTITY(null, null),
AREA_EFFECT_CLOUD(ENTITY),
END_CRYSTAL(ENTITY),
EVOKER_FANGS(ENTITY),
EXPERIENCE_ORB(ENTITY),
EYE_OF_ENDER(ENTITY),
FALLING_BLOCK(ENTITY),
ITEM(ENTITY),
TNT(ENTITY),
OMINOUS_ITEM_SPAWNER(ENTITY),
MARKER(ENTITY),
LIGHTNING_BOLT(ENTITY),
INTERACTION(ENTITY),
DISPLAY(ENTITY, null),
BLOCK_DISPLAY(DISPLAY),
ITEM_DISPLAY(DISPLAY),
TEXT_DISPLAY(DISPLAY),
// Hanging entities
HANGING_ENTITY(ENTITY, null),
LEASH_KNOT(HANGING_ENTITY),
PAINTING(HANGING_ENTITY),
ITEM_FRAME(HANGING_ENTITY),
GLOW_ITEM_FRAME(ITEM_FRAME),
// Projectiles
PROJECTILE(ENTITY, null),
ITEM_PROJECTILE(PROJECTILE, null),
SNOWBALL(ITEM_PROJECTILE),
ENDER_PEARL(ITEM_PROJECTILE),
EGG(ITEM_PROJECTILE),
EXPERIENCE_BOTTLE(ITEM_PROJECTILE),
FIREWORK_ROCKET(PROJECTILE),
LLAMA_SPIT(PROJECTILE),
SHULKER_BULLET(PROJECTILE),
FISHING_BOBBER(PROJECTILE),
WITHER_SKULL(PROJECTILE),
DRAGON_FIREBALL(PROJECTILE), // Doesn't actually inherit fireball
ABSTRACT_THROWN_POTION(ITEM_PROJECTILE, null),
SPLASH_POTION(ABSTRACT_THROWN_POTION),
LINGERING_POTION(ABSTRACT_THROWN_POTION),
ABSTRACT_ARROW(PROJECTILE, null),
ARROW(ABSTRACT_ARROW),
SPECTRAL_ARROW(ABSTRACT_ARROW),
TRIDENT(ABSTRACT_ARROW),
ABSTRACT_FIREBALL(ENTITY, null),
FIREBALL(ABSTRACT_FIREBALL),
SMALL_FIREBALL(ABSTRACT_FIREBALL),
ABSTRACT_WIND_CHARGE(PROJECTILE, null),
WIND_CHARGE(ABSTRACT_WIND_CHARGE),
BREEZE_WIND_CHARGE(ABSTRACT_WIND_CHARGE),
// Vehicles
VEHICLE(ENTITY, null),
ABSTRACT_MINECART(VEHICLE, null),
MINECART(ABSTRACT_MINECART),
FURNACE_MINECART(ABSTRACT_MINECART),
COMMAND_BLOCK_MINECART(ABSTRACT_MINECART),
TNT_MINECART(ABSTRACT_MINECART),
SPAWNER_MINECART(ABSTRACT_MINECART),
ABSTRACT_MINECART_CONTAINER(ABSTRACT_MINECART, null),
CHEST_MINECART(ABSTRACT_MINECART_CONTAINER),
HOPPER_MINECART(ABSTRACT_MINECART_CONTAINER),
ABSTRACT_BOAT(VEHICLE, null),
ABSTRACT_CHEST_BOAT(ABSTRACT_BOAT, null),
ACACIA_BOAT(ABSTRACT_BOAT),
ACACIA_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
BAMBOO_CHEST_RAFT(ABSTRACT_CHEST_BOAT),
BAMBOO_RAFT(ABSTRACT_BOAT),
BIRCH_BOAT(ABSTRACT_BOAT),
BIRCH_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
CHERRY_BOAT(ABSTRACT_BOAT),
CHERRY_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
DARK_OAK_BOAT(ABSTRACT_BOAT),
DARK_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
JUNGLE_BOAT(ABSTRACT_BOAT),
JUNGLE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
MANGROVE_BOAT(ABSTRACT_BOAT),
MANGROVE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
OAK_BOAT(ABSTRACT_BOAT),
OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
PALE_OAK_BOAT(ABSTRACT_BOAT),
PALE_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
SPRUCE_BOAT(ABSTRACT_BOAT),
SPRUCE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
// Living entities as a larger subclass
LIVING_ENTITY(ENTITY, null),
ARMOR_STAND(LIVING_ENTITY),
// Players
AVATAR(LIVING_ENTITY, null),
MANNEQUIN(AVATAR),
PLAYER(AVATAR),
// Mobs as a larger subclass
MOB(LIVING_ENTITY, null),
ENDER_DRAGON(MOB),
// Ambient mobs
AMBIENT_CREATURE(MOB, null),
BAT(AMBIENT_CREATURE),
// Flying mobs
FLYING_MOB(MOB, null),
GHAST(FLYING_MOB),
PHANTOM(FLYING_MOB),
// Pathfinder mobs and its subclasses
PATHFINDER_MOB(MOB, null),
ALLAY(PATHFINDER_MOB),
ABSTRACT_GOLEM(PATHFINDER_MOB, null),
SNOW_GOLEM(ABSTRACT_GOLEM),
IRON_GOLEM(ABSTRACT_GOLEM),
SHULKER(ABSTRACT_GOLEM),
COPPER_GOLEM(ABSTRACT_GOLEM),
// Ageable mobs and (tamable) animals
ABSTRACT_AGEABLE(PATHFINDER_MOB, null),
ABSTRACT_VILLAGER(ABSTRACT_AGEABLE, null),
VILLAGER(ABSTRACT_VILLAGER),
WANDERING_TRADER(ABSTRACT_VILLAGER),
ABSTRACT_CUBE_MOB(ABSTRACT_AGEABLE, null),
SLIME(ABSTRACT_CUBE_MOB),
MAGMA_CUBE(ABSTRACT_CUBE_MOB),
SULFUR_CUBE(ABSTRACT_CUBE_MOB),
ABSTRACT_ANIMAL(ABSTRACT_AGEABLE, null),
AXOLOTL(ABSTRACT_ANIMAL),
CHICKEN(ABSTRACT_ANIMAL),
PANDA(ABSTRACT_ANIMAL),
PIG(ABSTRACT_ANIMAL),
POLAR_BEAR(ABSTRACT_ANIMAL),
RABBIT(ABSTRACT_ANIMAL),
SHEEP(ABSTRACT_ANIMAL),
BEE(ABSTRACT_ANIMAL),
TURTLE(ABSTRACT_ANIMAL),
FOX(ABSTRACT_ANIMAL),
FROG(ABSTRACT_ANIMAL),
GOAT(ABSTRACT_ANIMAL),
HOGLIN(ABSTRACT_ANIMAL),
STRIDER(ABSTRACT_ANIMAL),
SNIFFER(ABSTRACT_ANIMAL),
ARMADILLO(ABSTRACT_ANIMAL),
HAPPY_GHAST(ABSTRACT_ANIMAL),
OCELOT(ABSTRACT_ANIMAL),
ABSTRACT_COW(ABSTRACT_ANIMAL, null),
COW(ABSTRACT_COW),
MOOSHROOM(ABSTRACT_COW),
TAMABLE_ANIMAL(ABSTRACT_ANIMAL, null),
CAT(TAMABLE_ANIMAL),
WOLF(TAMABLE_ANIMAL),
PARROT(TAMABLE_ANIMAL),
ABSTRACT_NAUTILUS(TAMABLE_ANIMAL, null),
NAUTILUS(ABSTRACT_NAUTILUS),
ZOMBIE_NAUTILUS(ABSTRACT_NAUTILUS),
ABSTRACT_HORSE(ABSTRACT_ANIMAL, null),
HORSE(ABSTRACT_HORSE),
SKELETON_HORSE(ABSTRACT_HORSE),
ZOMBIE_HORSE(ABSTRACT_HORSE),
CAMEL(ABSTRACT_HORSE),
CAMEL_HUSK(CAMEL),
ABSTRACT_CHESTED_HORSE(ABSTRACT_HORSE, null),
DONKEY(ABSTRACT_CHESTED_HORSE),
MULE(ABSTRACT_CHESTED_HORSE),
LLAMA(ABSTRACT_CHESTED_HORSE),
TRADER_LLAMA(LLAMA),
// Monsters
ABSTRACT_MONSTER(PATHFINDER_MOB, null),
BLAZE(ABSTRACT_MONSTER),
CREEPER(ABSTRACT_MONSTER),
ENDERMITE(ABSTRACT_MONSTER),
ENDERMAN(ABSTRACT_MONSTER),
GIANT(ABSTRACT_MONSTER),
SILVERFISH(ABSTRACT_MONSTER),
VEX(ABSTRACT_MONSTER),
WITHER(ABSTRACT_MONSTER),
BREEZE(ABSTRACT_MONSTER),
ZOGLIN(ABSTRACT_MONSTER),
WARDEN(ABSTRACT_MONSTER),
CREAKING(ABSTRACT_MONSTER),
ABSTRACT_SKELETON(ABSTRACT_MONSTER, null),
SKELETON(ABSTRACT_SKELETON),
STRAY(ABSTRACT_SKELETON),
WITHER_SKELETON(ABSTRACT_SKELETON),
BOGGED(ABSTRACT_SKELETON),
PARCHED(ABSTRACT_SKELETON),
ZOMBIE(ABSTRACT_MONSTER),
DROWNED(ZOMBIE),
HUSK(ZOMBIE),
ZOMBIFIED_PIGLIN(ZOMBIE),
ZOMBIE_VILLAGER(ZOMBIE),
GUARDIAN(ABSTRACT_MONSTER),
ELDER_GUARDIAN(GUARDIAN),
SPIDER(ABSTRACT_MONSTER),
CAVE_SPIDER(SPIDER),
ABSTRACT_PIGLIN(ABSTRACT_MONSTER, null),
PIGLIN(ABSTRACT_PIGLIN),
PIGLIN_BRUTE(ABSTRACT_PIGLIN),
// Water mobs
AGEABLE_WATER_CREATURE(ABSTRACT_AGEABLE, null),
DOLPHIN(AGEABLE_WATER_CREATURE),
SQUID(AGEABLE_WATER_CREATURE),
GLOW_SQUID(SQUID),
WATER_ANIMAL(PATHFINDER_MOB, null),
ABSTRACT_FISH(WATER_ANIMAL, null),
PUFFERFISH(ABSTRACT_FISH),
TADPOLE(ABSTRACT_FISH),
ABSTRACT_SCHOOLING_FISH(ABSTRACT_FISH, null),
COD(ABSTRACT_SCHOOLING_FISH),
SALMON(ABSTRACT_SCHOOLING_FISH),
TROPICAL_FISH(ABSTRACT_SCHOOLING_FISH),
// Raiders
ABSTRACT_RAIDER(ABSTRACT_MONSTER, null),
WITCH(ABSTRACT_RAIDER),
RAVAGER(ABSTRACT_RAIDER),
ABSTRACT_ILLAGER(ABSTRACT_RAIDER, null),
SPELLCASTER_ILLAGER(ABSTRACT_ILLAGER, null),
VINDICATOR(ABSTRACT_ILLAGER),
PILLAGER(ABSTRACT_ILLAGER),
EVOKER(SPELLCASTER_ILLAGER),
ILLUSIONER(SPELLCASTER_ILLAGER);
private static final EntityType[] TYPES = EntityTypeUtil.createSizedArray(values());
private final EntityType parent;
private final String identifier;
private int id = -1;
EntityTypes26_2(final EntityType parent) {
this.parent = parent;
this.identifier = Key.namespaced(name().toLowerCase(Locale.ROOT));
}
EntityTypes26_2(final EntityType parent, @Nullable final String identifier) {
this.parent = parent;
this.identifier = identifier;
}
@Override
public int getId() {
if (id == -1) {
throw new IllegalStateException("Ids have not been initialized yet (type " + name() + ")");
}
return id;
}
@Override
public String identifier() {
Preconditions.checkArgument(identifier != null, "Called identifier method on abstract type");
return identifier;
}
@Override
public @Nullable EntityType getParent() {
return parent;
}
@Override
public boolean isAbstractType() {
return identifier == null;
}
public static EntityType getTypeFromId(final int typeId) {
return EntityTypeUtil.getTypeFromId(TYPES, typeId, PIG);
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -0,0 +1,336 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.entities;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.util.EntityTypeUtil;
import com.viaversion.viaversion.util.Key;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.Nullable;
public enum EntityTypes26_3 implements EntityType {
ENTITY(null, null),
AREA_EFFECT_CLOUD(ENTITY),
END_CRYSTAL(ENTITY),
EVOKER_FANGS(ENTITY),
EXPERIENCE_ORB(ENTITY),
EYE_OF_ENDER(ENTITY),
FALLING_BLOCK(ENTITY),
ITEM(ENTITY),
TNT(ENTITY),
OMINOUS_ITEM_SPAWNER(ENTITY),
MARKER(ENTITY),
LIGHTNING_BOLT(ENTITY),
INTERACTION(ENTITY),
DISPLAY(ENTITY, null),
BLOCK_DISPLAY(DISPLAY),
ITEM_DISPLAY(DISPLAY),
TEXT_DISPLAY(DISPLAY),
// Hanging entities
HANGING_ENTITY(ENTITY, null),
LEASH_KNOT(HANGING_ENTITY),
PAINTING(HANGING_ENTITY),
ITEM_FRAME(HANGING_ENTITY),
GLOW_ITEM_FRAME(ITEM_FRAME),
CUSHION(ENTITY),
// Projectiles
PROJECTILE(ENTITY, null),
ITEM_PROJECTILE(PROJECTILE, null),
SNOWBALL(ITEM_PROJECTILE),
ENDER_PEARL(ITEM_PROJECTILE),
EGG(ITEM_PROJECTILE),
EXPERIENCE_BOTTLE(ITEM_PROJECTILE),
FIREWORK_ROCKET(PROJECTILE),
LLAMA_SPIT(PROJECTILE),
SHULKER_BULLET(PROJECTILE),
FISHING_BOBBER(PROJECTILE),
WITHER_SKULL(PROJECTILE),
DRAGON_FIREBALL(PROJECTILE), // Doesn't actually inherit fireball
ABSTRACT_THROWN_POTION(ITEM_PROJECTILE, null),
SPLASH_POTION(ABSTRACT_THROWN_POTION),
LINGERING_POTION(ABSTRACT_THROWN_POTION),
ABSTRACT_ARROW(PROJECTILE, null),
ARROW(ABSTRACT_ARROW),
SPECTRAL_ARROW(ABSTRACT_ARROW),
TRIDENT(ABSTRACT_ARROW),
ABSTRACT_FIREBALL(ENTITY, null),
FIREBALL(ABSTRACT_FIREBALL),
SMALL_FIREBALL(ABSTRACT_FIREBALL),
ABSTRACT_WIND_CHARGE(PROJECTILE, null),
WIND_CHARGE(ABSTRACT_WIND_CHARGE),
BREEZE_WIND_CHARGE(ABSTRACT_WIND_CHARGE),
// Vehicles
VEHICLE(ENTITY, null),
ABSTRACT_MINECART(VEHICLE, null),
MINECART(ABSTRACT_MINECART),
FURNACE_MINECART(ABSTRACT_MINECART),
COMMAND_BLOCK_MINECART(ABSTRACT_MINECART),
TNT_MINECART(ABSTRACT_MINECART),
SPAWNER_MINECART(ABSTRACT_MINECART),
ABSTRACT_MINECART_CONTAINER(ABSTRACT_MINECART, null),
CHEST_MINECART(ABSTRACT_MINECART_CONTAINER),
HOPPER_MINECART(ABSTRACT_MINECART_CONTAINER),
ABSTRACT_BOAT(VEHICLE, null),
ABSTRACT_CHEST_BOAT(ABSTRACT_BOAT, null),
ACACIA_BOAT(ABSTRACT_BOAT),
ACACIA_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
BAMBOO_CHEST_RAFT(ABSTRACT_CHEST_BOAT),
BAMBOO_RAFT(ABSTRACT_BOAT),
BIRCH_BOAT(ABSTRACT_BOAT),
BIRCH_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
CHERRY_BOAT(ABSTRACT_BOAT),
CHERRY_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
DARK_OAK_BOAT(ABSTRACT_BOAT),
DARK_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
JUNGLE_BOAT(ABSTRACT_BOAT),
JUNGLE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
MANGROVE_BOAT(ABSTRACT_BOAT),
MANGROVE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
OAK_BOAT(ABSTRACT_BOAT),
OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
PALE_OAK_BOAT(ABSTRACT_BOAT),
PALE_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
SPRUCE_BOAT(ABSTRACT_BOAT),
SPRUCE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
POPLAR_BOAT(ABSTRACT_BOAT),
POPLAR_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
// Living entities as a larger subclass
LIVING_ENTITY(ENTITY, null),
ARMOR_STAND(LIVING_ENTITY),
// Players
AVATAR(LIVING_ENTITY, null),
MANNEQUIN(AVATAR),
PLAYER(AVATAR),
// Mobs as a larger subclass
MOB(LIVING_ENTITY, null),
ENDER_DRAGON(MOB),
// Ambient mobs
AMBIENT_CREATURE(MOB, null),
BAT(AMBIENT_CREATURE),
// Flying mobs
FLYING_MOB(MOB, null),
GHAST(FLYING_MOB),
PHANTOM(FLYING_MOB),
// Pathfinder mobs and its subclasses
PATHFINDER_MOB(MOB, null),
ALLAY(PATHFINDER_MOB),
ABSTRACT_GOLEM(PATHFINDER_MOB, null),
SNOW_GOLEM(ABSTRACT_GOLEM),
IRON_GOLEM(ABSTRACT_GOLEM),
SHULKER(ABSTRACT_GOLEM),
COPPER_GOLEM(ABSTRACT_GOLEM),
// Ageable mobs and (tamable) animals
ABSTRACT_AGEABLE(PATHFINDER_MOB, null),
ABSTRACT_VILLAGER(ABSTRACT_AGEABLE, null),
VILLAGER(ABSTRACT_VILLAGER),
WANDERING_TRADER(ABSTRACT_VILLAGER),
ABSTRACT_CUBE_MOB(ABSTRACT_AGEABLE, null),
SLIME(ABSTRACT_CUBE_MOB),
MAGMA_CUBE(ABSTRACT_CUBE_MOB),
SULFUR_CUBE(ABSTRACT_CUBE_MOB),
ABSTRACT_ANIMAL(ABSTRACT_AGEABLE, null),
AXOLOTL(ABSTRACT_ANIMAL),
CHICKEN(ABSTRACT_ANIMAL),
PANDA(ABSTRACT_ANIMAL),
PIG(ABSTRACT_ANIMAL),
POLAR_BEAR(ABSTRACT_ANIMAL),
RABBIT(ABSTRACT_ANIMAL),
SHEEP(ABSTRACT_ANIMAL),
BEE(ABSTRACT_ANIMAL),
TURTLE(ABSTRACT_ANIMAL),
FOX(ABSTRACT_ANIMAL),
FROG(ABSTRACT_ANIMAL),
GOAT(ABSTRACT_ANIMAL),
HOGLIN(ABSTRACT_ANIMAL),
STRIDER(ABSTRACT_ANIMAL),
SNIFFER(ABSTRACT_ANIMAL),
ARMADILLO(ABSTRACT_ANIMAL),
HAPPY_GHAST(ABSTRACT_ANIMAL),
OCELOT(ABSTRACT_ANIMAL),
ABSTRACT_COW(ABSTRACT_ANIMAL, null),
COW(ABSTRACT_COW),
MOOSHROOM(ABSTRACT_COW),
TAMABLE_ANIMAL(ABSTRACT_ANIMAL, null),
CAT(TAMABLE_ANIMAL),
WOLF(TAMABLE_ANIMAL),
PARROT(TAMABLE_ANIMAL),
ABSTRACT_NAUTILUS(TAMABLE_ANIMAL, null),
NAUTILUS(ABSTRACT_NAUTILUS),
ZOMBIE_NAUTILUS(ABSTRACT_NAUTILUS),
ABSTRACT_HORSE(ABSTRACT_ANIMAL, null),
HORSE(ABSTRACT_HORSE),
SKELETON_HORSE(ABSTRACT_HORSE),
ZOMBIE_HORSE(ABSTRACT_HORSE),
CAMEL(ABSTRACT_HORSE),
CAMEL_HUSK(CAMEL),
ABSTRACT_CHESTED_HORSE(ABSTRACT_HORSE, null),
DONKEY(ABSTRACT_CHESTED_HORSE),
MULE(ABSTRACT_CHESTED_HORSE),
LLAMA(ABSTRACT_CHESTED_HORSE),
TRADER_LLAMA(LLAMA),
// Monsters
ABSTRACT_MONSTER(PATHFINDER_MOB, null),
BLAZE(ABSTRACT_MONSTER),
CREEPER(ABSTRACT_MONSTER),
ENDERMITE(ABSTRACT_MONSTER),
ENDERMAN(ABSTRACT_MONSTER),
GIANT(ABSTRACT_MONSTER),
SILVERFISH(ABSTRACT_MONSTER),
VEX(ABSTRACT_MONSTER),
WITHER(ABSTRACT_MONSTER),
BREEZE(ABSTRACT_MONSTER),
ZOGLIN(ABSTRACT_MONSTER),
WARDEN(ABSTRACT_MONSTER),
CREAKING(ABSTRACT_MONSTER),
ABSTRACT_SKELETON(ABSTRACT_MONSTER, null),
SKELETON(ABSTRACT_SKELETON),
STRAY(ABSTRACT_SKELETON),
WITHER_SKELETON(ABSTRACT_SKELETON),
BOGGED(ABSTRACT_SKELETON),
PARCHED(ABSTRACT_SKELETON),
ZOMBIE(ABSTRACT_MONSTER),
DROWNED(ZOMBIE),
HUSK(ZOMBIE),
ZOMBIFIED_PIGLIN(ZOMBIE),
ZOMBIE_VILLAGER(ZOMBIE),
GUARDIAN(ABSTRACT_MONSTER),
ELDER_GUARDIAN(GUARDIAN),
SPIDER(ABSTRACT_MONSTER),
CAVE_SPIDER(SPIDER),
ABSTRACT_PIGLIN(ABSTRACT_MONSTER, null),
PIGLIN(ABSTRACT_PIGLIN),
PIGLIN_BRUTE(ABSTRACT_PIGLIN),
// Water mobs
AGEABLE_WATER_CREATURE(ABSTRACT_AGEABLE, null),
DOLPHIN(AGEABLE_WATER_CREATURE),
SQUID(AGEABLE_WATER_CREATURE),
GLOW_SQUID(SQUID),
WATER_ANIMAL(PATHFINDER_MOB, null),
ABSTRACT_FISH(WATER_ANIMAL, null),
PUFFERFISH(ABSTRACT_FISH),
TADPOLE(ABSTRACT_FISH),
ABSTRACT_SCHOOLING_FISH(ABSTRACT_FISH, null),
COD(ABSTRACT_SCHOOLING_FISH),
SALMON(ABSTRACT_SCHOOLING_FISH),
TROPICAL_FISH(ABSTRACT_SCHOOLING_FISH),
// Raiders
ABSTRACT_RAIDER(ABSTRACT_MONSTER, null),
WITCH(ABSTRACT_RAIDER),
RAVAGER(ABSTRACT_RAIDER),
ABSTRACT_ILLAGER(ABSTRACT_RAIDER, null),
SPELLCASTER_ILLAGER(ABSTRACT_ILLAGER, null),
VINDICATOR(ABSTRACT_ILLAGER),
PILLAGER(ABSTRACT_ILLAGER),
EVOKER(SPELLCASTER_ILLAGER),
ILLUSIONER(SPELLCASTER_ILLAGER);
private static final EntityType[] TYPES = EntityTypeUtil.createSizedArray(values());
private final EntityType parent;
private final String identifier;
private int id = -1;
EntityTypes26_3(final EntityType parent) {
this.parent = parent;
this.identifier = Key.namespaced(name().toLowerCase(Locale.ROOT));
}
EntityTypes26_3(final EntityType parent, @Nullable final String identifier) {
this.parent = parent;
this.identifier = identifier;
}
@Override
public int getId() {
if (id == -1) {
throw new IllegalStateException("Ids have not been initialized yet (type " + name() + ")");
}
return id;
}
@Override
public String identifier() {
Preconditions.checkArgument(identifier != null, "Called identifier method on abstract type");
return identifier;
}
@Override
public @Nullable EntityType getParent() {
return parent;
}
@Override
public boolean isAbstractType() {
return identifier == null;
}
public static EntityType getTypeFromId(final int typeId) {
return EntityTypeUtil.getTypeFromId(TYPES, typeId, PIG);
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.entitydata.types;
import com.viaversion.viaversion.api.minecraft.PaintingVariant;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
public final class EntityDataTypes26_3 extends AbstractEntityDataTypes {
public final EntityDataType byteType = add(0, Types.BYTE);
public final EntityDataType varIntType = add(1, Types.VAR_INT);
public final EntityDataType longType = add(2, Types.VAR_LONG);
public final EntityDataType floatType = add(3, Types.FLOAT);
public final EntityDataType stringType = add(4, Types.STRING);
public final EntityDataType componentType = add(5, Types.TRUSTED_TAG);
public final EntityDataType optionalComponentType = add(6, Types.TRUSTED_OPTIONAL_TAG);
public final EntityDataType itemType;
public final EntityDataType booleanType = add(8, Types.BOOLEAN);
public final EntityDataType rotationsType = add(9, Types.ROTATIONS);
public final EntityDataType blockPositionType = add(10, Types.BLOCK_POSITION1_14);
public final EntityDataType optionalBlockPositionType = add(11, Types.OPTIONAL_POSITION_1_14);
public final EntityDataType directionType = add(12, Types.VAR_INT);
public final EntityDataType optionalUUIDType = add(13, Types.OPTIONAL_UUID);
public final EntityDataType blockStateType = add(14, Types.VAR_INT);
public final EntityDataType optionalBlockStateType = add(15, Types.VAR_INT);
public final EntityDataType particleType;
public final EntityDataType particlesType;
public final EntityDataType villagerDataType = add(18, Types.VILLAGER_DATA);
public final EntityDataType optionalVarIntType = add(19, Types.OPTIONAL_VAR_INT);
public final EntityDataType poseType = add(20, Types.VAR_INT);
public final EntityDataType catVariantType = add(21, Types.VAR_INT);
public final EntityDataType catSoundVariant = add(22, Types.VAR_INT);
public final EntityDataType cowVariantType = add(23, Types.VAR_INT);
public final EntityDataType cowSoundVariant = add(24, Types.VAR_INT);
public final EntityDataType wolfVariantType = add(25, Types.VAR_INT);
public final EntityDataType wolfSoundVariantType = add(26, Types.VAR_INT);
public final EntityDataType frogVariantType = add(27, Types.VAR_INT);
public final EntityDataType pigVariantType = add(28, Types.VAR_INT);
public final EntityDataType pigSoundVariant = add(29, Types.VAR_INT);
public final EntityDataType chickenVariantType = add(30, Types.VAR_INT);
public final EntityDataType chickenSoundVariant = add(31, Types.VAR_INT);
public final EntityDataType zombieNautilusVariantType = add(32, Types.VAR_INT);
public final EntityDataType optionalGlobalPosition = add(33, Types.OPTIONAL_GLOBAL_POSITION);
public final EntityDataType paintingVariantType = add(34, PaintingVariant.TYPE1_21_2);
public final EntityDataType snifferState = add(35, Types.VAR_INT);
public final EntityDataType armadilloState = add(36, Types.VAR_INT);
public final EntityDataType copperGolemState = add(37, Types.VAR_INT);
public final EntityDataType weatheringCopperState = add(38, Types.VAR_INT);
public final EntityDataType vector3FType = add(39, Types.VECTOR3F);
public final EntityDataType quaternionType = add(40, Types.QUATERNION);
public final EntityDataType mannequinProfileType = add(41, Types.RESOLVABLE_PROFILE);
public final EntityDataType humanoidArmType = add(42, Types.VAR_INT);
public final EntityDataType dyeColorType = add(43, Types.VAR_INT);
public EntityDataTypes26_3(final VersionedTypesHolder types) {
super(44);
this.itemType = add(7, types.item());
this.particleType = add(16, types.particle());
this.particlesType = add(17, types.particles());
}
}
@@ -27,7 +27,7 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Copyable;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.HashMap;
import java.util.Map;
public record BlockStateProperties(Map<String, String> properties) implements Copyable {
@@ -36,7 +36,7 @@ public record BlockStateProperties(Map<String, String> properties) implements Co
@Override
public BlockStateProperties read(final ByteBuf buffer) {
final int size = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> properties = new Object2ObjectOpenHashMap<>();
final Map<String, String> properties = new HashMap<>();
for (int i = 0; i < size; i++) {
properties.put(Types.STRING.read(buffer), Types.STRING.read(buffer));
}
@@ -64,6 +64,6 @@ public record BlockStateProperties(Map<String, String> properties) implements Co
@Override
public BlockStateProperties copy() {
return new BlockStateProperties(new Object2ObjectOpenHashMap<>(properties));
return new BlockStateProperties(new HashMap<>(properties));
}
}
@@ -130,8 +130,8 @@ public record BlocksAttacks(
@Override
public BlocksAttacks rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> blockSound = SoundEvent.rewriteHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> disableSound = SoundEvent.rewriteHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> blockSound = SoundEvent.rewriteOptionalHolder(this.blockSound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> disableSound = SoundEvent.rewriteOptionalHolder(this.disableSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return new BlocksAttacks(this.blockDelaySeconds, this.disableCooldownScale, this.damageReductions, this.itemDamage, this.bypassedBy, blockSound, disableSound);
}
@@ -0,0 +1,43 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
public record BreakSound(Holder<SoundEvent> sound) implements Rewritable {
public static final Type<BreakSound> TYPE = TransformingType.of(Types.SOUND_EVENT, BreakSound.class, BreakSound::new, BreakSound::sound);
@Override
public BreakSound rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return this.sound == sound ? this : new BreakSound(sound);
}
}
@@ -0,0 +1,55 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.minecraft.ResolvableFloat;
import com.viaversion.viaversion.api.minecraft.ResolvableInt;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
public record BrewingFuel(ResolvableInt uses, ResolvableFloat speedMultiplier) {
public static final Type<BrewingFuel> TYPE = new Type<>(BrewingFuel.class) {
@Override
public BrewingFuel read(final ByteBuf buffer) {
final ResolvableInt uses = Types.RESOLVABLE_INT.read(buffer);
final ResolvableFloat speedMultiplier = Types.RESOLVABLE_FLOAT.read(buffer);
return new BrewingFuel(uses, speedMultiplier);
}
@Override
public void write(final ByteBuf buffer, final BrewingFuel value) {
Types.RESOLVABLE_INT.write(buffer, value.uses);
Types.RESOLVABLE_FLOAT.write(buffer, value.speedMultiplier);
}
@Override
public void write(final Ops ops, final BrewingFuel value) {
ops.writeMap(map -> map
.write("uses", Types.RESOLVABLE_INT, value.uses)
.write("speed_multiplier", Types.RESOLVABLE_FLOAT, value.speedMultiplier));
}
};
}
@@ -0,0 +1,40 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.minecraft.ResolvableInt;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
public record Compostable(ResolvableInt layers) {
public static final Type<Compostable> TYPE = new TransformingType<>(Types.RESOLVABLE_INT, Compostable.class, Compostable::new, Compostable::layers) {
@Override
public void write(final Ops ops, final Compostable value) {
ops.writeMap(map -> map.write("layers", Types.RESOLVABLE_INT, value.layers));
}
};
}
@@ -1,200 +0,0 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
import com.viaversion.viaversion.api.type.types.misc.HolderType;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<SoundEvent> sound,
boolean hasConsumeParticles,
ConsumeEffect<?>[] consumeEffects) implements Copyable, Rewritable {
private static final HolderSetType REMOVE_EFFECTS_TYPE = new HolderSetType() {
@Override
public void write(final Ops ops, final HolderSet value) {
ops.writeMap(map -> map.write("effects", new HolderSetType(EnumTypes.MOB_EFFECT), value));
}
};
private static final Type<Float> TELEPORT_RANDOMLY_TYPE = new Type<>(Float.class) {
@Override
public void write(final ByteBuf buffer, final Float value) {
Types.FLOAT.writePrimitive(buffer, value);
}
@Override
public Float read(final ByteBuf buffer) {
return Types.FLOAT.readPrimitive(buffer);
}
@Override
public void write(final Ops ops, final Float value) {
ops.writeMap(map -> map.write("diameter", Types.FLOAT, 16F));
}
};
private static final HolderType<SoundEvent> PLAY_SOUND_TYPE = new HolderType<>(MappingData.MappingType.SOUND) {
@Override
public SoundEvent readDirect(final ByteBuf buffer) {
return Types.SOUND_EVENT.readDirect(buffer);
}
@Override
public void writeDirect(final ByteBuf buffer, final SoundEvent value) {
Types.SOUND_EVENT.writeDirect(buffer, value);
}
@Override
public void write(final Ops ops, final Holder<SoundEvent> value) {
ops.writeMap(map -> map.write("sound", Types.SOUND_EVENT, value));
}
};
public static final Type<?>[] EFFECT_TYPES = {
ApplyStatusEffects.TYPE,
REMOVE_EFFECTS_TYPE,
Types.EMPTY, // clear all effects
TELEPORT_RANDOMLY_TYPE,
PLAY_SOUND_TYPE
};
public static final Type<Consumable1_21_2> TYPE = new Type<>(Consumable1_21_2.class) {
@Override
public Consumable1_21_2 read(final ByteBuf buffer) {
final float consumeSeconds = buffer.readFloat();
final int animationType = Types.VAR_INT.readPrimitive(buffer);
final Holder<SoundEvent> sound = Types.SOUND_EVENT.read(buffer);
final boolean hasConsumeParticles = buffer.readBoolean();
final ConsumeEffect<?>[] consumeEffects = ConsumeEffect.ARRAY_TYPE.read(buffer);
return new Consumable1_21_2(consumeSeconds, animationType, sound, hasConsumeParticles, consumeEffects);
}
@Override
public void write(final ByteBuf buffer, final Consumable1_21_2 value) {
buffer.writeFloat(value.consumeSeconds);
Types.VAR_INT.writePrimitive(buffer, value.animationType);
Types.SOUND_EVENT.write(buffer, value.sound);
buffer.writeBoolean(value.hasConsumeParticles);
ConsumeEffect.ARRAY_TYPE.write(buffer, value.consumeEffects);
}
@Override
public void write(final Ops ops, final Consumable1_21_2 value) {
final Holder<SoundEvent> defaultSound = Holder.of(ops.context().registryAccess().id(MappingData.MappingType.SOUND, "entity.generic.eat"));
ops.writeMap(map -> map
.writeOptional("consume_seconds", Types.FLOAT, value.consumeSeconds, 1.6F)
.writeOptional("animation", EnumTypes.ITEM_USE_ANIMATION, value.animationType, 1)
.writeOptional("sound", Types.SOUND_EVENT, value.sound, defaultSound)
.writeOptional("has_consume_particles", Types.BOOLEAN, value.hasConsumeParticles, true)
.writeOptional("consume_effects", ConsumeEffect.ARRAY_TYPE, value.consumeEffects, new ConsumeEffect<?>[0]));
}
};
public record ConsumeEffect<T>(int id, Type<T> type, T value) {
public static final Type<ConsumeEffect<?>> TYPE = new Type<>(ConsumeEffect.class) {
@Override
public ConsumeEffect<?> read(final ByteBuf buffer) {
// Oh no...
final int effectType = Types.VAR_INT.readPrimitive(buffer);
final Type<?> type = EFFECT_TYPES[effectType];
final Object value = type.read(buffer);
return ConsumeEffect.of(effectType, type, value);
}
@Override
public void write(final ByteBuf buffer, final ConsumeEffect<?> value) {
Types.VAR_INT.writePrimitive(buffer, value.id);
value.writeValue(buffer);
}
@Override
public void write(final Ops ops, final ConsumeEffect<?> value) {
writeGeneric(ops, value);
}
private <E> void writeGeneric(final Ops ops, final ConsumeEffect<E> value) {
ops.writeMap(map -> map
.write("type", EnumTypes.CONSUME_EFFECT, value.id)
.writeInlinedMap(value.type, value.value));
}
};
public static final Type<ConsumeEffect<?>[]> ARRAY_TYPE = new ArrayType<>(TYPE);
static <T> ConsumeEffect<T> of(final int id, final Type<T> type, final Object value) {
//noinspection unchecked
return new ConsumeEffect<>(id, type, (T) value);
}
void writeValue(final ByteBuf buf) {
this.type.write(buf, this.value);
}
}
public record ApplyStatusEffects(PotionEffect[] effects, float probability) {
public static final Type<ApplyStatusEffects> TYPE = new Type<>(ApplyStatusEffects.class) {
@Override
public ApplyStatusEffects read(final ByteBuf buffer) {
final PotionEffect[] effects = PotionEffect.ARRAY_TYPE.read(buffer);
final float probability = buffer.readFloat();
return new ApplyStatusEffects(effects, probability);
}
@Override
public void write(final ByteBuf buffer, final ApplyStatusEffects value) {
PotionEffect.ARRAY_TYPE.write(buffer, value.effects);
buffer.writeFloat(value.probability);
}
@Override
public void write(final Ops ops, final ApplyStatusEffects value) {
ops.writeMap(map -> map
.write("effects", PotionEffect.ARRAY_TYPE, value.effects)
.writeOptional("probability", Types.FLOAT, value.probability, 1F));
}
};
}
@Override
public Consumable1_21_2 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> soundHolder = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return soundHolder == this.sound ? this : new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
}
@Override
public Consumable1_21_2 copy() {
return new Consumable1_21_2(consumeSeconds, animationType, sound, hasConsumeParticles, Copyable.copy(consumeEffects));
}
}
@@ -0,0 +1,55 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.minecraft.ResolvableFloat;
import com.viaversion.viaversion.api.minecraft.ResolvableInt;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
public record CookingFuel(ResolvableInt burnTime, ResolvableFloat speedMultiplier) {
public static final Type<CookingFuel> TYPE = new Type<>(CookingFuel.class) {
@Override
public CookingFuel read(final ByteBuf buffer) {
final ResolvableInt burnTime = Types.RESOLVABLE_INT.read(buffer);
final ResolvableFloat speedMultiplier = Types.RESOLVABLE_FLOAT.read(buffer);
return new CookingFuel(burnTime, speedMultiplier);
}
@Override
public void write(final ByteBuf buffer, final CookingFuel value) {
Types.RESOLVABLE_INT.write(buffer, value.burnTime);
Types.RESOLVABLE_FLOAT.write(buffer, value.speedMultiplier);
}
@Override
public void write(final Ops ops, final CookingFuel value) {
ops.writeMap(map -> map
.write("burn_time", Types.RESOLVABLE_INT, value.burnTime)
.write("speed_multiplier", Types.RESOLVABLE_FLOAT, value.speedMultiplier));
}
};
}
@@ -23,10 +23,12 @@
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.RegistryKey;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
public record DamageResistant26_1(HolderSet types) {
@@ -34,7 +36,7 @@ public record DamageResistant26_1(HolderSet types) {
@Override
public void write(final Ops ops, final DamageResistant26_1 value) {
ops.writeMap(map -> map.write("types", Types.HOLDER_SET, value.types));
ops.writeMap(map -> map.write("types", new HolderSetType(RegistryKey.of("damage_type")), value.types));
}
};
}
@@ -22,20 +22,35 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2.ConsumeEffect;
import com.viaversion.viaversion.api.minecraft.item.data.consumable.ConsumeEffect;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Rewritable;
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable {
public record DeathProtection(ConsumeEffect<?>[] deathEffects) implements Copyable, Rewritable {
public static final Type<DeathProtection> TYPE = new TransformingType<>(ConsumeEffect.ARRAY_TYPE, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
public static final Type<DeathProtection> TYPE1_21_2 = new TransformingType<>(ConsumeEffect.ARRAY_TYPE1_21_2, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
@Override
public void write(final Ops ops, final DeathProtection value) {
ops.writeMap(map -> map.write("death_effects", ConsumeEffect.ARRAY_TYPE, value.deathEffects));
ops.writeMap(map -> map.writeOptional("death_effects", ConsumeEffect.ARRAY_TYPE1_21_2, value.deathEffects, new ConsumeEffect<?>[0]));
}
};
public static final Type<DeathProtection> TYPE26_3 = new TransformingType<>(ConsumeEffect.ARRAY_TYPE26_3, DeathProtection.class, DeathProtection::new, DeathProtection::deathEffects) {
@Override
public void write(final Ops ops, final DeathProtection value) {
ops.writeMap(map -> map.writeOptional("death_effects", ConsumeEffect.ARRAY_TYPE26_3, value.deathEffects, new ConsumeEffect<?>[0]));
}
};
@Override
public DeathProtection rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final ConsumeEffect<?>[] deathEffects = ConsumeEffect.rewrite(this.deathEffects, connection, protocol, clientbound);
return deathEffects == this.deathEffects ? this : new DeathProtection(deathEffects);
}
@Override
public DeathProtection copy() {
@@ -38,12 +38,12 @@ public record DebugStickState(CompoundTag tag) implements Rewritable, Copyable {
@Override
public DebugStickState rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
CompoundTag updatedTag = tag;
if (clientbound && protocol.getMappingData() != null && protocol.getMappingData().changedBlocks() != null) {
if (protocol.getMappingData() != null && protocol.getMappingData().changedBlocks() != null) {
updatedTag = tag.copy();
// Anything beyond this isn't worth the disk space/handling
updatedTag.entrySet().removeIf(entry -> {
final int blockId = protocol.getMappingData().getFullBlockMappings().id(entry.getKey());
return protocol.getMappingData().changedBlocks().contains(blockId);
return blockId == -1 || protocol.getMappingData().changedBlocks().contains(blockId);
});
}
return new DebugStickState(updatedTag);
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Limit;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -41,7 +42,7 @@ public record Enchantments(Int2IntMap enchantments, boolean showInTooltip) imple
@Override
public Enchantments read(final ByteBuf buffer) {
final Int2IntMap enchantments = new Int2IntOpenHashMap();
final int size = Types.VAR_INT.readPrimitive(buffer);
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
for (int i = 0; i < size; i++) {
final int id = Types.VAR_INT.readPrimitive(buffer);
final int level = Types.VAR_INT.readPrimitive(buffer);
@@ -65,7 +66,7 @@ public record Enchantments(Int2IntMap enchantments, boolean showInTooltip) imple
@Override
public Enchantments read(final ByteBuf buffer) {
final Int2IntMap enchantments = new Int2IntOpenHashMap();
final int size = Types.VAR_INT.readPrimitive(buffer);
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
for (int i = 0; i < size; i++) {
final int id = Types.VAR_INT.readPrimitive(buffer);
final int level = Types.VAR_INT.readPrimitive(buffer);
@@ -46,7 +46,8 @@ public final class EnumTypes {
public static final EnumType LLAMA_VARIANT = new EnumType(Fallback.CLAMP, "creamy", "white", "brown", "gray");
public static final EnumType AXOLOTL_VARIANT = new EnumType("lucy", "wild", "gold", "cyan", "blue");
public static final EnumType EQUIPMENT_SLOT = new EnumType("mainhand", "feet", "legs", "chest", "head", "offhand", "body", "saddle");
public static final EnumType SWING_ANIMATION = new EnumType("none", "whack", "stab");
public static final EnumType SWING_ANIMATION1_21_11 = new EnumType("none", "whack", "stab");
public static final EnumType ITEM_USE_ANIMATION1_21_5 = new EnumType("none", "eat", "drink", "block", "bow", "spear", "crossbow", "spyglass", "toot_horn", "brush", "bundle");
public static final EnumType ITEM_USE_ANIMATION = new EnumType("none", "eat", "drink", "block", "bow", "trident", "crossbow", "spyglass", "toot_horn", "brush", "bundle", "spear");
// Enums with non-ordinal ids
public static final FakeEnumType RABBIT_VARIANT = new FakeEnumType(List.of("brown", "white", "black", "white_splotched", "gold", "salt"), of(99, "evil"));
@@ -34,7 +34,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class FilterableComponent extends Filterable<Tag> implements Copyable, Rewritable {
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
public static final Type<FilterableComponent> TYPE = new FilterableType<>(Types.TEXT_COMPONENT_TAG, Types.OPTIONAL_TAG, FilterableComponent.class) {
@Override
protected FilterableComponent create(final Tag raw, final Tag filtered) {
return new FilterableComponent(raw, filtered);
@@ -37,9 +37,13 @@ import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record Instrument1_21_2(Holder<SoundEvent> soundEvent, float useDuration, float range,
Tag description) implements Copyable, Rewritable {
int durabilityDamage, Tag description) implements Copyable, Rewritable {
public static final HolderType<Instrument1_21_2> TYPE = new HolderType<>() {
public Instrument1_21_2(final Holder<SoundEvent> soundEvent, final float useDuration, final float range, final Tag description) {
this(soundEvent, useDuration, range, 0, description);
}
public static final HolderType<Instrument1_21_2> TYPE1_21_2 = new HolderType<>() {
@Override
public Instrument1_21_2 readDirect(final ByteBuf buffer) {
final Holder<SoundEvent> soundEvent = Types.SOUND_EVENT.read(buffer);
@@ -63,7 +67,44 @@ public record Instrument1_21_2(Holder<SoundEvent> soundEvent, float useDuration,
.write("sound_event", Types.SOUND_EVENT, value.soundEvent())
.write("use_duration", Types.FLOAT, value.useDuration())
.write("range", Types.FLOAT, value.range())
.write("description", Types.TAG, value.description()));
.write("description", Types.TEXT_COMPONENT_TAG, value.description()));
}
@Override
protected Key identifier(final Ops ops, final int id) {
return ops.context().registryAccess().registryKey("instrument", id);
}
};
public static final EitherHolderType<Instrument1_21_2> EITHER_HOLDER_TYPE = new EitherHolderType<>(TYPE1_21_2);
public static final HolderType<Instrument1_21_2> TYPE26_3 = new HolderType<>() {
@Override
public Instrument1_21_2 readDirect(final ByteBuf buffer) {
final Holder<SoundEvent> soundEvent = Types.SOUND_EVENT.read(buffer);
final float useDuration = Types.FLOAT.readPrimitive(buffer);
final float range = Types.FLOAT.readPrimitive(buffer);
final int durabilityDamage = Types.VAR_INT.readPrimitive(buffer);
final Tag description = Types.TAG.read(buffer);
return new Instrument1_21_2(soundEvent, useDuration, range, durabilityDamage, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final Instrument1_21_2 value) {
Types.SOUND_EVENT.write(buffer, value.soundEvent());
Types.FLOAT.writePrimitive(buffer, value.useDuration());
Types.FLOAT.writePrimitive(buffer, value.range());
Types.VAR_INT.writePrimitive(buffer, value.durabilityDamage());
Types.TAG.write(buffer, value.description());
}
@Override
public void writeDirect(final Ops ops, final Instrument1_21_2 value) {
ops.writeMap(map -> map
.write("sound_event", Types.SOUND_EVENT, value.soundEvent())
.write("use_duration", Types.FLOAT, value.useDuration())
.write("range", Types.FLOAT, value.range())
.writeOptional("durability_damage", Types.VAR_INT, value.durabilityDamage(), 0)
.write("description", Types.TEXT_COMPONENT_TAG, value.description()));
}
@Override
@@ -71,16 +112,15 @@ public record Instrument1_21_2(Holder<SoundEvent> soundEvent, float useDuration,
return ops.context().registryAccess().registryKey("instrument", id);
}
};
public static final EitherHolderType<Instrument1_21_2> EITHER_HOLDER_TYPE = new EitherHolderType<>(TYPE);
@Override
public Instrument1_21_2 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> soundEvent = SoundEvent.rewriteHolder(this.soundEvent, Rewritable.soundRewriteFunction(protocol, clientbound));
return soundEvent == this.soundEvent ? this : new Instrument1_21_2(soundEvent, useDuration, range, description);
return soundEvent == this.soundEvent ? this : new Instrument1_21_2(soundEvent, useDuration, range, durabilityDamage, description);
}
@Override
public Instrument1_21_2 copy() {
return new Instrument1_21_2(soundEvent, useDuration, range, description.copy());
return new Instrument1_21_2(soundEvent, useDuration, range, durabilityDamage, description.copy());
}
}
@@ -22,19 +22,22 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable Condition dismountConditions,
@Nullable Condition knockbackConditions, @Nullable Condition damageConditions,
float forwardMovement, float damageMultiplier,
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
public static final Type<KineticWeapon> TYPE = new Type<>(KineticWeapon.class) {
@Override
@@ -80,6 +83,13 @@ public record KineticWeapon(int contactCooldownTicks, int delayTicks, @Nullable
}
};
@Override
public KineticWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return sound == this.sound && hitSound == this.hitSound ? this : new KineticWeapon(contactCooldownTicks, delayTicks, dismountConditions, knockbackConditions, damageConditions, forwardMovement, damageMultiplier, sound, hitSound);
}
public record Condition(int maxDurationTicks, float minSpeed, float minRelativeSpeed) {
public static final Type<Condition> TYPE = new Type<>(Condition.class) {
@@ -0,0 +1,56 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
import io.netty.buffer.ByteBuf;
public record MobVisibility(HolderSet targetingEntityTypes, float visibility) {
public static final Type<MobVisibility> TYPE = new Type<>(MobVisibility.class) {
@Override
public MobVisibility read(final ByteBuf buffer) {
final HolderSet targetingEntityTypes = Types.HOLDER_SET.read(buffer);
final float visibility = Types.FLOAT.readPrimitive(buffer);
return new MobVisibility(targetingEntityTypes, visibility);
}
@Override
public void write(final ByteBuf buffer, final MobVisibility value) {
Types.HOLDER_SET.write(buffer, value.targetingEntityTypes);
Types.FLOAT.writePrimitive(buffer, value.visibility);
}
@Override
public void write(final Ops ops, final MobVisibility value) {
ops.writeMap(map -> map
.write("targeting_entity_types", new HolderSetType(MappingData.MappingType.ENTITY_TYPE), value.targetingEntityTypes)
.write("visibility", Types.FLOAT, value.visibility));
}
};
}
@@ -22,16 +22,19 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) {
@Nullable Holder<SoundEvent> sound, @Nullable Holder<SoundEvent> hitSound) implements Rewritable {
public static final Type<PiercingWeapon> TYPE = new Type<>(PiercingWeapon.class) {
@Override
@@ -61,4 +64,11 @@ public record PiercingWeapon(boolean dealsKnockback, boolean dismounts,
);
}
};
@Override
public PiercingWeapon rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> sound = SoundEvent.rewriteOptionalHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
final Holder<SoundEvent> hitSound = SoundEvent.rewriteOptionalHolder(this.hitSound, Rewritable.soundRewriteFunction(protocol, clientbound));
return sound == this.sound && hitSound == this.hitSound ? this : new PiercingWeapon(dealsKnockback, dismounts, sound, hitSound);
}
}
@@ -29,22 +29,25 @@ import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Rewritable;
public record PotDecorations(int[] itemIds) implements Copyable, Rewritable {
public record PotDecorations1_20_5(int[] itemIds) implements Copyable, Rewritable {
public static final Type<PotDecorations> TYPE = new TransformingType<>(Types.VAR_INT_ARRAY_PRIMITIVE, PotDecorations.class, PotDecorations::new, PotDecorations::itemIds) {
private static final Key BRICK = Key.ofPath("brick");
public static final Type<PotDecorations1_20_5> TYPE = new TransformingType<>(Types.VAR_INT_ARRAY_PRIMITIVE, PotDecorations1_20_5.class, PotDecorations1_20_5::new, PotDecorations1_20_5::itemIds) {
@Override
public void write(final Ops ops, final PotDecorations value) {
public void write(final Ops ops, final PotDecorations1_20_5 value) {
// Vanilla always encodes all 4 sides, filling missing ones with bricks
ops.writeList(list -> {
for (final int itemId : value.itemIds) {
list.write(Types.IDENTIFIER, ops.context().registryAccess().item(itemId));
for (int i = 0; i < 4; i++) {
list.write(Types.IDENTIFIER, i < value.itemIds.length ? ops.context().registryAccess().item(value.itemIds[i]) : BRICK);
}
});
}
};
public PotDecorations(final int backItem, final int leftItem, final int rightItem, final int frontItem) {
public PotDecorations1_20_5(final int backItem, final int leftItem, final int rightItem, final int frontItem) {
this(new int[]{backItem, leftItem, rightItem, frontItem});
}
@@ -69,16 +72,16 @@ public record PotDecorations(int[] itemIds) implements Copyable, Rewritable {
}
@Override
public PotDecorations rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
public PotDecorations1_20_5 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final int[] newItems = new int[itemIds.length];
for (int i = 0; i < itemIds.length; i++) {
newItems[i] = Rewritable.rewriteItem(protocol, clientbound, itemIds[i]);
}
return new PotDecorations(newItems);
return new PotDecorations1_20_5(newItems);
}
@Override
public PotDecorations copy() {
return new PotDecorations(Copyable.copy(itemIds));
public PotDecorations1_20_5 copy() {
return new PotDecorations1_20_5(Copyable.copy(itemIds));
}
}
@@ -0,0 +1,96 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.viaversion.viaversion.util.Rewritable.rewriteItem;
public record PotDecorations26_3(@Nullable Item back, @Nullable Item left, @Nullable Item right,
@Nullable Item front) implements Copyable, Rewritable {
public static final class PotDecorationsType extends Type<PotDecorations26_3> {
private final Type<Item> itemTemplateType;
public PotDecorationsType(final Type<Item> itemTemplateType) {
super(PotDecorations26_3.class);
this.itemTemplateType = itemTemplateType;
}
@Override
public void write(final ByteBuf buffer, final PotDecorations26_3 value) {
itemTemplateType.write(buffer, value.back());
itemTemplateType.write(buffer, value.left());
itemTemplateType.write(buffer, value.right());
itemTemplateType.write(buffer, value.front());
}
@Override
public PotDecorations26_3 read(final ByteBuf buffer) {
final Item back = itemTemplateType.read(buffer);
final Item left = itemTemplateType.read(buffer);
final Item right = itemTemplateType.read(buffer);
final Item front = itemTemplateType.read(buffer);
return new PotDecorations26_3(back, left, right, front);
}
@Override
public void write(final Ops ops, final PotDecorations26_3 value) {
ops.writeMap(map -> map
.writeOptional("back", itemTemplateType, value.back())
.writeOptional("left", itemTemplateType, value.left())
.writeOptional("right", itemTemplateType, value.right())
.writeOptional("front", itemTemplateType, value.front())
);
}
}
@Override
public PotDecorations26_3 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
return new PotDecorations26_3(
rewriteItem(connection, protocol, clientbound, back),
rewriteItem(connection, protocol, clientbound, left),
rewriteItem(connection, protocol, clientbound, right),
rewriteItem(connection, protocol, clientbound, front)
);
}
@Override
public PotDecorations26_3 copy() {
return new PotDecorations26_3(
back == null ? null : back.copy(),
left == null ? null : left.copy(),
right == null ? null : right.copy(),
front == null ? null : front.copy()
);
}
}
@@ -40,7 +40,7 @@ public record PotionEffectData(int amplifier, int duration, boolean ambient, boo
final boolean ambient = buffer.readBoolean();
final boolean showParticles = buffer.readBoolean();
final boolean showIcon = buffer.readBoolean();
final PotionEffectData hiddenEffect = OPTIONAL_TYPE.read(buffer);
final PotionEffectData hiddenEffect = OPTIONAL_TYPE.readTrackingDepth(buffer);
return new PotionEffectData(amplifier, duration, ambient, showParticles, showIcon, hiddenEffect);
}
@@ -0,0 +1,61 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public record SignText(Tag[] messages, Tag @Nullable [] filteredMessages, int color, boolean hasGlowingText) {
public static final Type<SignText> TYPE = new Type<>(SignText.class) {
@Override
public SignText read(final ByteBuf buffer) {
final Tag[] messages = Types.SIGN_TEXT.read(buffer);
final Tag[] filteredMessages = Types.OPTIONAL_SIGN_TEXT.read(buffer);
final int color = Types.VAR_INT.readPrimitive(buffer);
final boolean hasGlowingText = buffer.readBoolean();
return new SignText(messages, filteredMessages, color, hasGlowingText);
}
@Override
public void write(final ByteBuf buffer, final SignText value) {
Types.SIGN_TEXT.write(buffer, value.messages);
Types.OPTIONAL_SIGN_TEXT.write(buffer, value.filteredMessages);
Types.VAR_INT.writePrimitive(buffer, value.color);
Types.BOOLEAN.write(buffer, value.hasGlowingText);
}
@Override
public void write(final Ops ops, final SignText value) {
ops.writeMap(map -> map
.write("messages", Types.TEXT_COMPONENT_TAG_ARRAY, value.messages)
.writeOptional("filtered_messages", Types.TEXT_COMPONENT_TAG_ARRAY, value.filteredMessages)
.write("color", EnumTypes.DYE_COLOR, value.color)
.write("has_glowing_text", Types.BOOLEAN, value.hasGlowingText));
}
};
}
@@ -29,9 +29,17 @@ import io.netty.buffer.ByteBuf;
public record SwingAnimation(int type, int duration) {
private static final int DEFAULT_TYPE = EnumTypes.SWING_ANIMATION.idFromName("whack");
public static final Type<SwingAnimation> TYPE = new SwingAnimationType(EnumTypes.SWING_ANIMATION1_21_11.idFromName("whack"));
private static class SwingAnimationType extends Type<SwingAnimation> {
private final int defaultType;
public SwingAnimationType(final int defaultType) {
super(SwingAnimation.class);
this.defaultType = defaultType;
}
public static final Type<SwingAnimation> TYPE = new Type<>(SwingAnimation.class) {
@Override
public SwingAnimation read(final ByteBuf buffer) {
final int type = Types.VAR_INT.readPrimitive(buffer);
@@ -48,9 +56,8 @@ public record SwingAnimation(int type, int duration) {
@Override
public void write(final Ops ops, final SwingAnimation value) {
ops.writeMap(map -> map
.writeOptional("type", EnumTypes.SWING_ANIMATION, value.type, DEFAULT_TYPE)
.writeOptional("duration", Types.INT, value.duration, 6)
);
.writeOptional("type", EnumTypes.SWING_ANIMATION1_21_11, value.type, this.defaultType)
.writeOptional("duration", Types.INT, value.duration, 6));
}
};
}
}
@@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Limit;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
@@ -42,7 +43,7 @@ public record TooltipDisplay(boolean hideTooltip,
public TooltipDisplay read(final ByteBuf buffer) {
final boolean hideTooltip = buffer.readBoolean();
final IntSortedSet hiddenComponents = new IntLinkedOpenHashSet();
final int size = Types.VAR_INT.readPrimitive(buffer);
final int size = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
for (int i = 0; i < size; i++) {
hiddenComponents.add(Types.VAR_INT.readPrimitive(buffer));
}
@@ -22,18 +22,18 @@
*/
package com.viaversion.viaversion.api.minecraft.item.data;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.TransformingType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.EitherType;
import com.viaversion.viaversion.util.Either;
/**
* 1.21.11-only damage type.
*
* @param id registry int or string id
*/
public record DamageType(Either<Integer, String> id) {
public record VillagerFood(int nutrition) {
public static final Type<DamageType> TYPE1_21_11 = TransformingType.of(new EitherType<>(Types.VAR_INT, Types.STRING), DamageType.class, DamageType::new, DamageType::id);
public static final Type<VillagerFood> TYPE = new TransformingType<>(Types.VAR_INT, VillagerFood.class, VillagerFood::new, VillagerFood::nutrition) {
@Override
public void write(final Ops ops, final VillagerFood value) {
ops.writeMap(map -> map.write("nutrition", Types.VAR_INT, value.nutrition));
}
};
}
@@ -0,0 +1,54 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.PotionEffect;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
public record ApplyStatusEffects(PotionEffect[] effects, float probability) {
public static final Type<ApplyStatusEffects> TYPE = new Type<>(ApplyStatusEffects.class) {
@Override
public ApplyStatusEffects read(final ByteBuf buffer) {
final PotionEffect[] effects = PotionEffect.ARRAY_TYPE.read(buffer);
final float probability = buffer.readFloat();
return new ApplyStatusEffects(effects, probability);
}
@Override
public void write(final ByteBuf buffer, final ApplyStatusEffects value) {
PotionEffect.ARRAY_TYPE.write(buffer, value.effects);
buffer.writeFloat(value.probability);
}
@Override
public void write(final Ops ops, final ApplyStatusEffects value) {
ops.writeMap(map -> map
.write("effects", PotionEffect.ARRAY_TYPE, value.effects)
.writeOptional("probability", Types.FLOAT, value.probability, 1F));
}
};
}
@@ -0,0 +1,101 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.EnumTypes;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.EnumType;
import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record Consumable1_21_2(float consumeSeconds, int animationType, Holder<SoundEvent> sound,
boolean hasConsumeParticles,
ConsumeEffect<?>[] consumeEffects) implements Copyable, Rewritable {
public static final Type<Consumable1_21_2> TYPE1_21_2 = new ConsumableType(ConsumeEffect.ARRAY_TYPE1_21_2, EnumTypes.ITEM_USE_ANIMATION);
public static final Type<Consumable1_21_2> TYPE1_21_5 = new ConsumableType(ConsumeEffect.ARRAY_TYPE1_21_2, EnumTypes.ITEM_USE_ANIMATION1_21_5);
public static final Type<Consumable1_21_2> TYPE26_3 = new ConsumableType(ConsumeEffect.ARRAY_TYPE26_3, EnumTypes.ITEM_USE_ANIMATION);
private static final class ConsumableType extends Type<Consumable1_21_2> {
private final Type<ConsumeEffect<?>[]> consumeEffectType;
private final EnumType animationType;
private ConsumableType(final Type<ConsumeEffect<?>[]> consumeEffectType, final EnumType animationType) {
super(Consumable1_21_2.class);
this.consumeEffectType = consumeEffectType;
this.animationType = animationType;
}
@Override
public Consumable1_21_2 read(final ByteBuf buffer) {
final float consumeSeconds = buffer.readFloat();
final int animationType = Types.VAR_INT.readPrimitive(buffer);
final Holder<SoundEvent> sound = Types.SOUND_EVENT.read(buffer);
final boolean hasConsumeParticles = buffer.readBoolean();
final ConsumeEffect<?>[] consumeEffects = consumeEffectType.read(buffer);
return new Consumable1_21_2(consumeSeconds, animationType, sound, hasConsumeParticles, consumeEffects);
}
@Override
public void write(final ByteBuf buffer, final Consumable1_21_2 value) {
buffer.writeFloat(value.consumeSeconds);
Types.VAR_INT.writePrimitive(buffer, value.animationType);
Types.SOUND_EVENT.write(buffer, value.sound);
buffer.writeBoolean(value.hasConsumeParticles);
consumeEffectType.write(buffer, value.consumeEffects);
}
@Override
public void write(final Ops ops, final Consumable1_21_2 value) {
final Holder<SoundEvent> defaultSound = Holder.of(ops.context().registryAccess().id(MappingData.MappingType.SOUND, "entity.generic.eat"));
ops.writeMap(map -> map
.writeOptional("consume_seconds", Types.FLOAT, value.consumeSeconds, 1.6F)
.writeOptional("animation", animationType, value.animationType, 1)
.writeOptional("sound", Types.SOUND_EVENT, value.sound, defaultSound)
.writeOptional("has_consume_particles", Types.BOOLEAN, value.hasConsumeParticles, true)
.writeOptional("on_consume_effects", consumeEffectType, value.consumeEffects, new ConsumeEffect<?>[0]));
}
}
@Override
public Consumable1_21_2 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
final Holder<SoundEvent> soundHolder = SoundEvent.rewriteHolder(this.sound, Rewritable.soundRewriteFunction(protocol, clientbound));
final ConsumeEffect<?>[] consumeEffects = ConsumeEffect.rewrite(this.consumeEffects, connection, protocol, clientbound);
return soundHolder == this.sound && consumeEffects == this.consumeEffects ? this
: new Consumable1_21_2(consumeSeconds, animationType, soundHolder, hasConsumeParticles, consumeEffects);
}
@Override
public Consumable1_21_2 copy() {
return new Consumable1_21_2(consumeSeconds, animationType, sound, hasConsumeParticles, Copyable.copy(consumeEffects));
}
}
@@ -0,0 +1,194 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.HolderSet;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.minecraft.item.data.EnumTypes;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.ArrayType;
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
import com.viaversion.viaversion.api.type.types.misc.HolderType;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record ConsumeEffect<T>(int id, Type<T> type, T value) implements Rewritable {
private static final HolderSetType REMOVE_EFFECTS_TYPE = new HolderSetType() {
@Override
public void write(final Ops ops, final HolderSet value) {
ops.writeMap(map -> map.write("effects", new HolderSetType(EnumTypes.MOB_EFFECT), value));
}
};
public static final Type<Float> TELEPORT_RANDOMLY_TYPE1_21_2 = new Type<>(Float.class) {
@Override
public void write(final ByteBuf buffer, final Float value) {
Types.FLOAT.writePrimitive(buffer, value);
}
@Override
public Float read(final ByteBuf buffer) {
return Types.FLOAT.readPrimitive(buffer);
}
@Override
public void write(final Ops ops, final Float value) {
ops.writeMap(map -> map.writeOptional("diameter", Types.FLOAT, value, 16F));
}
};
public static final Type<TeleportRandomlyConsumeEffect> TELEPORT_RANDOMLY_TYPE26_3 = new Type<>(TeleportRandomlyConsumeEffect.class) {
@Override
public void write(final ByteBuf buffer, final TeleportRandomlyConsumeEffect value) {
Types.FLOAT.writePrimitive(buffer, value.diameter());
Types.BOOLEAN.write(buffer, value.directionalParticles());
}
@Override
public TeleportRandomlyConsumeEffect read(final ByteBuf buffer) {
final float diameter = Types.FLOAT.readPrimitive(buffer);
final boolean directionalParticles = Types.BOOLEAN.read(buffer);
return new TeleportRandomlyConsumeEffect(diameter, directionalParticles);
}
@Override
public void write(final Ops ops, final TeleportRandomlyConsumeEffect value) {
ops.writeMap(map -> map
.writeOptional("diameter", Types.FLOAT, value.diameter(), 16F)
.writeOptional("directional_particles", Types.BOOLEAN, value.directionalParticles(), true)
);
}
};
private static final HolderType<SoundEvent> PLAY_SOUND_TYPE = new HolderType<>(MappingData.MappingType.SOUND) {
@Override
public SoundEvent readDirect(final ByteBuf buffer) {
return Types.SOUND_EVENT.readDirect(buffer);
}
@Override
public void writeDirect(final ByteBuf buffer, final SoundEvent value) {
Types.SOUND_EVENT.writeDirect(buffer, value);
}
@Override
public void write(final Ops ops, final Holder<SoundEvent> value) {
ops.writeMap(map -> map.write("sound", Types.SOUND_EVENT, value));
}
};
public static final Type<?>[] EFFECT_TYPES1_21_2 = {
ApplyStatusEffects.TYPE,
REMOVE_EFFECTS_TYPE,
Types.EMPTY, // clear all effects
TELEPORT_RANDOMLY_TYPE1_21_2,
PLAY_SOUND_TYPE
};
public static final Type<?>[] EFFECT_TYPES26_3 = {
ApplyStatusEffects.TYPE,
REMOVE_EFFECTS_TYPE,
Types.EMPTY, // clear all effects
TELEPORT_RANDOMLY_TYPE26_3,
PLAY_SOUND_TYPE
};
public static final Type<ConsumeEffect<?>> TYPE1_21_2 = new ConsumeEffectType(EFFECT_TYPES1_21_2);
public static final Type<ConsumeEffect<?>[]> ARRAY_TYPE1_21_2 = new ArrayType<>(TYPE1_21_2);
public static final Type<ConsumeEffect<?>> TYPE26_3 = new ConsumeEffectType(EFFECT_TYPES26_3);
public static final Type<ConsumeEffect<?>[]> ARRAY_TYPE26_3 = new ArrayType<>(TYPE26_3);
static <T> ConsumeEffect<T> of(final int id, final Type<T> type, final Object value) {
//noinspection unchecked
return new ConsumeEffect<>(id, type, (T) value);
}
@Override
public ConsumeEffect<T> rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
if (this.type != PLAY_SOUND_TYPE) {
return this;
}
//noinspection unchecked
final Holder<SoundEvent> sound = (Holder<SoundEvent>) this.value;
final Holder<SoundEvent> rewrittenSound = SoundEvent.rewriteHolder(sound, Rewritable.soundRewriteFunction(protocol, clientbound));
return rewrittenSound == sound ? this : ConsumeEffect.of(this.id, this.type, rewrittenSound);
}
public static ConsumeEffect<?>[] rewrite(final ConsumeEffect<?>[] effects, final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
ConsumeEffect<?>[] rewrittenEffects = effects;
for (int i = 0; i < effects.length; i++) {
final ConsumeEffect<?> rewrittenEffect = effects[i].rewrite(connection, protocol, clientbound);
if (rewrittenEffect != effects[i]) {
if (rewrittenEffects == effects) {
rewrittenEffects = effects.clone();
}
rewrittenEffects[i] = rewrittenEffect;
}
}
return rewrittenEffects;
}
void writeValue(final ByteBuf buf) {
this.type.write(buf, this.value);
}
private static final class ConsumeEffectType extends Type<ConsumeEffect<?>> {
private final Type<?>[] types;
private ConsumeEffectType(final Type<?>[] types) {
super(ConsumeEffect.class);
this.types = types;
}
@Override
public ConsumeEffect<?> read(final ByteBuf buffer) {
// Oh no...
final int effectType = Types.VAR_INT.readPrimitive(buffer);
final Type<?> type = types[effectType];
final Object value = type.read(buffer);
return ConsumeEffect.of(effectType, type, value);
}
@Override
public void write(final ByteBuf buffer, final ConsumeEffect<?> value) {
Types.VAR_INT.writePrimitive(buffer, value.id);
value.writeValue(buffer);
}
@Override
public void write(final Ops ops, final ConsumeEffect<?> value) {
writeGeneric(ops, value);
}
private <E> void writeGeneric(final Ops ops, final ConsumeEffect<E> value) {
ops.writeMap(map -> map
.write("type", EnumTypes.CONSUME_EFFECT, value.id)
.writeInlinedMap(value.type, value.value));
}
}
}
@@ -0,0 +1,26 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.consumable;
public record TeleportRandomlyConsumeEffect(float diameter, boolean directionalParticles) {
}
@@ -20,7 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
@@ -30,86 +30,86 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record ArmorTrim(Holder<ArmorTrimMaterial> material, Holder<ArmorTrimPattern> pattern,
boolean showInTooltip) implements Rewritable {
public record ArmorTrim1_20_5(Holder<ArmorTrimMaterial1_20_5> material, Holder<ArmorTrimPattern> pattern,
boolean showInTooltip) implements Rewritable {
public ArmorTrim(Holder<ArmorTrimMaterial> material, Holder<ArmorTrimPattern> pattern) {
public ArmorTrim1_20_5(Holder<ArmorTrimMaterial1_20_5> material, Holder<ArmorTrimPattern> pattern) {
this(material, pattern, true);
}
public static final Type<ArmorTrim> TYPE1_20_5 = new Type<>(ArmorTrim.class) {
public static final Type<ArmorTrim1_20_5> TYPE1_20_5 = new Type<>(ArmorTrim1_20_5.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_20_5.read(buffer);
public ArmorTrim1_20_5 read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial1_20_5> material = ArmorTrimMaterial1_20_5.TYPE1_20_5.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE1_20_5.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
return new ArmorTrim1_20_5(material, pattern, showInTooltip);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_20_5.write(buffer, value.material);
public void write(final ByteBuf buffer, final ArmorTrim1_20_5 value) {
ArmorTrimMaterial1_20_5.TYPE1_20_5.write(buffer, value.material);
ArmorTrimPattern.TYPE1_20_5.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
};
public static final Type<ArmorTrim> TYPE1_21_2 = new Type<>(ArmorTrim.class) {
public static final Type<ArmorTrim1_20_5> TYPE1_21_2 = new Type<>(ArmorTrim1_20_5.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_21_2.read(buffer);
public ArmorTrim1_20_5 read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial1_20_5> material = ArmorTrimMaterial1_20_5.TYPE1_21_2.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE1_20_5.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
return new ArmorTrim1_20_5(material, pattern, showInTooltip);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_21_2.write(buffer, value.material);
public void write(final ByteBuf buffer, final ArmorTrim1_20_5 value) {
ArmorTrimMaterial1_20_5.TYPE1_21_2.write(buffer, value.material);
ArmorTrimPattern.TYPE1_20_5.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
};
public static final Type<ArmorTrim> TYPE1_21_4 = new Type<>(ArmorTrim.class) {
public static final Type<ArmorTrim1_20_5> TYPE1_21_4 = new Type<>(ArmorTrim1_20_5.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_21_4.read(buffer);
public ArmorTrim1_20_5 read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial1_20_5> material = ArmorTrimMaterial1_20_5.TYPE1_21_4.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE1_20_5.read(buffer);
final boolean showInTooltip = buffer.readBoolean();
return new ArmorTrim(material, pattern, showInTooltip);
return new ArmorTrim1_20_5(material, pattern, showInTooltip);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_21_4.write(buffer, value.material);
public void write(final ByteBuf buffer, final ArmorTrim1_20_5 value) {
ArmorTrimMaterial1_20_5.TYPE1_21_4.write(buffer, value.material);
ArmorTrimPattern.TYPE1_20_5.write(buffer, value.pattern);
buffer.writeBoolean(value.showInTooltip);
}
};
public static final Type<ArmorTrim> TYPE1_21_5 = new Type<>(ArmorTrim.class) {
public static final Type<ArmorTrim1_20_5> TYPE1_21_5 = new Type<>(ArmorTrim1_20_5.class) {
@Override
public ArmorTrim read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial> material = ArmorTrimMaterial.TYPE1_21_5.read(buffer);
public ArmorTrim1_20_5 read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial1_20_5> material = ArmorTrimMaterial1_20_5.TYPE1_21_5.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE1_21_5.read(buffer);
return new ArmorTrim(material, pattern);
return new ArmorTrim1_20_5(material, pattern);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim value) {
ArmorTrimMaterial.TYPE1_21_5.write(buffer, value.material);
public void write(final ByteBuf buffer, final ArmorTrim1_20_5 value) {
ArmorTrimMaterial1_20_5.TYPE1_21_5.write(buffer, value.material);
ArmorTrimPattern.TYPE1_21_5.write(buffer, value.pattern);
}
@Override
public void write(final Ops ops, final ArmorTrim value) {
public void write(final Ops ops, final ArmorTrim1_20_5 value) {
ops.writeMap(map -> map
.write("material", ArmorTrimMaterial.TYPE1_21_5, value.material)
.write("material", ArmorTrimMaterial1_20_5.TYPE1_21_5, value.material)
.write("pattern", ArmorTrimPattern.TYPE1_21_5, value.pattern));
}
};
@Override
public ArmorTrim rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
Holder<ArmorTrimMaterial> material = this.material;
public ArmorTrim1_20_5 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
Holder<ArmorTrimMaterial1_20_5> material = this.material;
if (material.isDirect()) {
material = Holder.of(material.value().rewrite(connection, protocol, clientbound));
}
@@ -118,6 +118,6 @@ public record ArmorTrim(Holder<ArmorTrimMaterial> material, Holder<ArmorTrimPatt
if (pattern.isDirect()) {
pattern = Holder.of(pattern.value().rewrite(connection, protocol, clientbound));
}
return new ArmorTrim(material, pattern, showInTooltip);
return new ArmorTrim1_20_5(material, pattern, showInTooltip);
}
}
@@ -0,0 +1,66 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Holder;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record ArmorTrim26_3(Holder<ArmorTrimMaterial26_3> material,
Holder<ArmorTrimPattern> pattern) implements Rewritable {
public static final Type<ArmorTrim26_3> TYPE = new Type<>(ArmorTrim26_3.class) {
@Override
public ArmorTrim26_3 read(final ByteBuf buffer) {
final Holder<ArmorTrimMaterial26_3> material = ArmorTrimMaterial26_3.TYPE.read(buffer);
final Holder<ArmorTrimPattern> pattern = ArmorTrimPattern.TYPE1_21_5.read(buffer);
return new ArmorTrim26_3(material, pattern);
}
@Override
public void write(final ByteBuf buffer, final ArmorTrim26_3 value) {
ArmorTrimMaterial26_3.TYPE.write(buffer, value.material);
ArmorTrimPattern.TYPE1_21_5.write(buffer, value.pattern);
}
@Override
public void write(final Ops ops, final ArmorTrim26_3 value) {
ops.writeMap(map -> map
.write("material", ArmorTrimMaterial26_3.TYPE, value.material)
.write("pattern", ArmorTrimPattern.TYPE1_21_5, value.pattern));
}
};
@Override
public ArmorTrim26_3 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
Holder<ArmorTrimPattern> pattern = this.pattern;
if (pattern.isDirect()) {
pattern = Holder.of(pattern.value().rewrite(connection, protocol, clientbound));
}
return new ArmorTrim26_3(material, pattern);
}
}
@@ -20,7 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
@@ -32,32 +32,32 @@ import com.viaversion.viaversion.util.Copyable;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import java.util.HashMap;
import java.util.Map;
public record ArmorTrimMaterial(String assetName, int itemId, float itemModelIndex,
Map<String, String> overrideArmorMaterials,
Tag description) implements Copyable, Rewritable {
public record ArmorTrimMaterial1_20_5(String assetName, int itemId, float itemModelIndex,
Map<String, String> overrideArmorMaterials,
Tag description) implements Copyable, Rewritable {
public ArmorTrimMaterial(final String assetName, final int itemId, final Map<String, String> overrideArmorMaterials, final Tag description) {
public ArmorTrimMaterial1_20_5(final String assetName, final int itemId, final Map<String, String> overrideArmorMaterials, final Tag description) {
this(assetName, itemId, 0F, overrideArmorMaterials, description);
}
public ArmorTrimMaterial(final String assetName, final Map<String, String> overrideArmorMaterials, final Tag description) {
public ArmorTrimMaterial1_20_5(final String assetName, final Map<String, String> overrideArmorMaterials, final Tag description) {
this(assetName, 0, 0F, overrideArmorMaterials, description);
}
public static final HolderType<ArmorTrimMaterial> TYPE1_20_5 = new HolderType<>() {
public static final HolderType<ArmorTrimMaterial1_20_5> TYPE1_20_5 = new HolderType<>() {
// The override key is an int, but given we don't use it at all and that creating a new type is annoying,
// we'll just store it in the string map:tm:
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
public ArmorTrimMaterial1_20_5 readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int item = Types.VAR_INT.readPrimitive(buffer);
final float itemModelIndex = buffer.readFloat();
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
final Map<String, String> overrideArmorMaterials = new HashMap<>();
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final int key = Types.VAR_INT.readPrimitive(buffer);
final String value = Types.STRING.read(buffer);
@@ -65,11 +65,11 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, item, itemModelIndex, overrideArmorMaterials, description);
return new ArmorTrimMaterial1_20_5(assetName, item, itemModelIndex, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial1_20_5 value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.itemId());
buffer.writeFloat(value.itemModelIndex());
@@ -83,15 +83,15 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
Types.TAG.write(buffer, value.description());
}
};
public static final HolderType<ArmorTrimMaterial> TYPE1_21_2 = new HolderType<>() {
public static final HolderType<ArmorTrimMaterial1_20_5> TYPE1_21_2 = new HolderType<>() {
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
public ArmorTrimMaterial1_20_5 readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int item = Types.VAR_INT.readPrimitive(buffer);
final float itemModelIndex = buffer.readFloat();
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
final Map<String, String> overrideArmorMaterials = new HashMap<>();
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final String key = Types.STRING.read(buffer);
final String value = Types.STRING.read(buffer);
@@ -99,11 +99,11 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, item, itemModelIndex, overrideArmorMaterials, description);
return new ArmorTrimMaterial1_20_5(assetName, item, itemModelIndex, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial1_20_5 value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.itemId());
buffer.writeFloat(value.itemModelIndex());
@@ -117,14 +117,14 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
Types.TAG.write(buffer, value.description());
}
};
public static final HolderType<ArmorTrimMaterial> TYPE1_21_4 = new HolderType<>() {
public static final HolderType<ArmorTrimMaterial1_20_5> TYPE1_21_4 = new HolderType<>() {
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
public ArmorTrimMaterial1_20_5 readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int item = Types.VAR_INT.readPrimitive(buffer);
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
final Map<String, String> overrideArmorMaterials = new HashMap<>();
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final String key = Types.STRING.read(buffer);
final String value = Types.STRING.read(buffer);
@@ -132,11 +132,11 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, item, overrideArmorMaterials, description);
return new ArmorTrimMaterial1_20_5(assetName, item, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial1_20_5 value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.itemId());
@@ -149,13 +149,13 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
Types.TAG.write(buffer, value.description());
}
};
public static final HolderType<ArmorTrimMaterial> TYPE1_21_5 = new HolderType<>() {
public static final HolderType<ArmorTrimMaterial1_20_5> TYPE1_21_5 = new HolderType<>() {
@Override
public ArmorTrimMaterial readDirect(final ByteBuf buffer) {
public ArmorTrimMaterial1_20_5 readDirect(final ByteBuf buffer) {
final String assetName = Types.STRING.read(buffer);
final int overrideArmorMaterialsSize = Types.VAR_INT.readPrimitive(buffer);
final Map<String, String> overrideArmorMaterials = new Object2ObjectArrayMap<>();
final Map<String, String> overrideArmorMaterials = new HashMap<>();
for (int i = 0; i < overrideArmorMaterialsSize; i++) {
final String key = Types.STRING.read(buffer);
final String value = Types.STRING.read(buffer);
@@ -163,11 +163,11 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
}
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial(assetName, overrideArmorMaterials, description);
return new ArmorTrimMaterial1_20_5(assetName, overrideArmorMaterials, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial value) {
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial1_20_5 value) {
Types.STRING.write(buffer, value.assetName());
Types.VAR_INT.writePrimitive(buffer, value.overrideArmorMaterials().size());
@@ -180,7 +180,7 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
}
@Override
public void writeDirect(final Ops ops, final ArmorTrimMaterial object) {
public void writeDirect(final Ops ops, final ArmorTrimMaterial1_20_5 object) {
ops.writeMap(map -> {
map.write("asset_name", Types.STRING, object.assetName());
if (!object.overrideArmorMaterials.isEmpty()) {
@@ -201,12 +201,12 @@ public record ArmorTrimMaterial(String assetName, int itemId, float itemModelInd
};
@Override
public ArmorTrimMaterial rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
return new ArmorTrimMaterial(assetName, Rewritable.rewriteItem(protocol, clientbound, itemId), itemModelIndex, overrideArmorMaterials, description);
public ArmorTrimMaterial1_20_5 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
return new ArmorTrimMaterial1_20_5(assetName, Rewritable.rewriteItem(protocol, clientbound, itemId), itemModelIndex, overrideArmorMaterials, description);
}
@Override
public ArmorTrimMaterial copy() {
return new ArmorTrimMaterial(assetName, itemId, itemModelIndex, new Object2ObjectArrayMap<>(overrideArmorMaterials), description);
public ArmorTrimMaterial1_20_5 copy() {
return new ArmorTrimMaterial1_20_5(assetName, itemId, itemModelIndex, new HashMap<>(overrideArmorMaterials), description);
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.misc.HolderType;
import com.viaversion.viaversion.util.Key;
import io.netty.buffer.ByteBuf;
public record ArmorTrimMaterial26_3(String paletteId, Tag description) {
public static final HolderType<ArmorTrimMaterial26_3> TYPE = new HolderType<>() {
@Override
public ArmorTrimMaterial26_3 readDirect(final ByteBuf buffer) {
final String paletteId = Types.STRING.read(buffer);
final Tag description = Types.TAG.read(buffer);
return new ArmorTrimMaterial26_3(paletteId, description);
}
@Override
public void writeDirect(final ByteBuf buffer, final ArmorTrimMaterial26_3 value) {
Types.STRING.write(buffer, value.paletteId());
Types.TAG.write(buffer, value.description());
}
@Override
public void writeDirect(final Ops ops, final ArmorTrimMaterial26_3 object) {
ops.writeMap(map -> {
map.write("palette_id", Types.IDENTIFIER, Key.of(object.paletteId()));
map.write("description", Types.TEXT_COMPONENT_TAG, object.description());
});
}
@Override
protected Key identifier(final Ops ops, final int id) {
return ops.context().registryAccess().registryKey("trim_material", id);
}
};
}
@@ -20,7 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viaversion.api.connection.UserConnection;
@@ -20,7 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.minecraft.item.data;
package com.viaversion.viaversion.api.minecraft.item.data.trim;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.EitherHolder;
@@ -32,34 +32,34 @@ import com.viaversion.viaversion.api.type.types.misc.EitherHolderType;
import com.viaversion.viaversion.util.Rewritable;
import io.netty.buffer.ByteBuf;
public record ProvidesTrimMaterial(EitherHolder<ArmorTrimMaterial> material) implements Rewritable {
public record ProvidesTrimMaterial1_20_5(EitherHolder<ArmorTrimMaterial1_20_5> material) implements Rewritable {
public static final Type<ProvidesTrimMaterial> TYPE = new Type<>(ProvidesTrimMaterial.class) {
public static final Type<ProvidesTrimMaterial1_20_5> TYPE = new Type<>(ProvidesTrimMaterial1_20_5.class) {
@Override
public ProvidesTrimMaterial read(final ByteBuf buffer) {
final EitherHolder<ArmorTrimMaterial> position = EitherHolderType.read(buffer, ArmorTrimMaterial.TYPE1_21_5);
return new ProvidesTrimMaterial(position);
public ProvidesTrimMaterial1_20_5 read(final ByteBuf buffer) {
final EitherHolder<ArmorTrimMaterial1_20_5> position = EitherHolderType.read(buffer, ArmorTrimMaterial1_20_5.TYPE1_21_5);
return new ProvidesTrimMaterial1_20_5(position);
}
@Override
public void write(final ByteBuf buffer, final ProvidesTrimMaterial value) {
EitherHolderType.write(buffer, value.material, ArmorTrimMaterial.TYPE1_21_5);
public void write(final ByteBuf buffer, final ProvidesTrimMaterial1_20_5 value) {
EitherHolderType.write(buffer, value.material, ArmorTrimMaterial1_20_5.TYPE1_21_5);
}
@Override
public void write(final Ops ops, final ProvidesTrimMaterial value) {
EitherHolderType.write(ops, value.material, ArmorTrimMaterial.TYPE1_21_5);
public void write(final Ops ops, final ProvidesTrimMaterial1_20_5 value) {
EitherHolderType.write(ops, value.material, ArmorTrimMaterial1_20_5.TYPE1_21_5);
}
};
@Override
public ProvidesTrimMaterial rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
public ProvidesTrimMaterial1_20_5 rewrite(final UserConnection connection, final Protocol<?, ?, ?, ?> protocol, final boolean clientbound) {
if (material.hasKey() || material.holder().hasId()) {
return this;
}
final ArmorTrimMaterial trimMaterial = material.holder().value();
return new ProvidesTrimMaterial(EitherHolder.of(Holder.of(trimMaterial.rewrite(connection, protocol, clientbound))));
final ArmorTrimMaterial1_20_5 trimMaterial = material.holder().value();
return new ProvidesTrimMaterial1_20_5(EitherHolder.of(Holder.of(trimMaterial.rewrite(connection, protocol, clientbound))));
}
}
@@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.protocol;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.connection.ProtocolStorables;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
@@ -33,6 +34,7 @@ import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.rewriter.ComponentRewriter;
import com.viaversion.viaversion.api.rewriter.EntityRewriter;
import com.viaversion.viaversion.api.rewriter.ItemRewriter;
@@ -58,6 +60,22 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*/
public interface Protocol<CU extends ClientboundPacketType, CM extends ClientboundPacketType, SM extends ServerboundPacketType, SU extends ServerboundPacketType> {
/**
* Returns the server protocol version for this protocol,
* or null if not set (e.g. for base protocols).
*
* @return the server protocol version
*/
@Nullable ProtocolVersion getServerVersion();
/**
* Returns the client protocol version for this protocol,
* or null if not set (e.g. for base protocols).
*
* @return the client protocol version for this protocol
*/
@Nullable ProtocolVersion getClientVersion();
default void registerClientbound(State state, ClientboundPacketType packetType, @Nullable PacketHandler handler) {
Preconditions.checkArgument(packetType.state() == state);
registerClientbound(state, packetType.getId(), packetType.getId(), handler, false);
@@ -460,6 +478,15 @@ public interface Protocol<CU extends ClientboundPacketType, CM extends Clientbou
return false;
}
/**
* Returns the index assigned to this protocol during registration.
*
* @return protocol index, or -1 if not yet registered
*/
int index();
ProtocolStorables createStorables();
/**
* Marks this protocol as dependent on another. Before being initialized, it will wait for the dependency's completion.
*
@@ -318,4 +318,12 @@ public interface ProtocolManager {
* @return whether the mappings have been loaded
*/
boolean hasLoadedMappings();
/**
* Returns the number of registered protocols.
* Used to size per-connection protocol data arrays.
*
* @return number of registered protocols
*/
int registeredProtocolCount();
}
@@ -165,7 +165,7 @@ public class PacketTracker {
// Warning system
private long warningPeriodStart = System.nanoTime();
private long lastWarning = Long.MIN_VALUE;
private long lastWarning = warningPeriodStart - SECOND_NANOS;
private int warnings;
public RateTracker(int windowSize) {
@@ -28,5 +28,11 @@ public enum State {
STATUS,
LOGIN,
CONFIGURATION,
PLAY
PLAY;
private static final int STATES_COUNT = values().length;
public static int statesCount() {
return STATES_COUNT;
}
}
@@ -27,7 +27,7 @@ import java.util.Arrays;
import org.checkerframework.checker.nullness.qual.Nullable;
final class PacketArrayMappings implements PacketMappings {
private final PacketMapping[][] packets = new PacketMapping[State.values().length][];
private final PacketMapping[][] packets = new PacketMapping[State.statesCount()][];
@Override
public @Nullable PacketMapping mappedPacket(final State state, final int unmappedId) {
@@ -94,6 +94,8 @@ public class ProtocolVersion implements Comparable<ProtocolVersion> {
public static final ProtocolVersion v1_21_9 = register(773, "1.21.9-1.21.10", new SubVersionRange("1.21", 9, 10));
public static final ProtocolVersion v1_21_11 = register(774, "1.21.11");
public static final ProtocolVersion v26_1 = register(775, "26.1-26.1.2", new SubVersionRange("26.1", 0, 2));
public static final ProtocolVersion v26_2 = register(776, "26.2");
public static final ProtocolVersion v26_3 = register(777, "26.3");
public static final ProtocolVersion unknown = new ProtocolVersion(VersionType.SPECIAL, -1, -1, "UNKNOWN", null);
static {
@@ -87,6 +87,6 @@ public interface EntityRewriter<T extends Protocol<?, ?, ?, ?>> extends Rewriter
* @return entity tracker
*/
default <E extends EntityTracker> E tracker(UserConnection connection) {
return connection.getEntityTracker(protocol().getClass());
return connection.getEntityTracker(protocol());
}
}
@@ -36,6 +36,8 @@ public interface RegistryDataRewriter {
void updateDialog(UserConnection connection, CompoundTag tag);
boolean updateBlockStateProvider(CompoundTag tag);
/**
* Sends registries that had additional entries configured, but weren't sent by the server at all
* by the time finish_configuration was sent to the client.
@@ -23,6 +23,8 @@
package com.viaversion.viaversion.api.type;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.util.NestingLimiter;
import io.netty.buffer.ByteBuf;
/**
* Type for buffer reading and writing.
@@ -63,6 +65,22 @@ public abstract class Type<T> implements ByteBufReader<T>, ByteBufWriter<T>, Cod
return typeName != null && !typeName.isEmpty() ? typeName : this.getClass().getName();
}
/**
* Reads a value and counts it towards the nesting depth limit of the current thread, to be used by recursive types.
*
* @param buffer buffer to read from
* @return read value
* @see NestingLimiter
*/
public T readTrackingDepth(final ByteBuf buffer) {
NestingLimiter.increment();
try {
return read(buffer);
} finally {
NestingLimiter.decrement();
}
}
@Override
public void write(Ops ops, T value) {
throw new UnsupportedOperationException("Write operation not supported for type: " + getTypeName());
@@ -36,6 +36,8 @@ import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;
import com.viaversion.viaversion.api.minecraft.ProfileKey;
import com.viaversion.viaversion.api.minecraft.Quaternion;
import com.viaversion.viaversion.api.minecraft.RegistryEntry;
import com.viaversion.viaversion.api.minecraft.ResolvableFloat;
import com.viaversion.viaversion.api.minecraft.ResolvableInt;
import com.viaversion.viaversion.api.minecraft.ResolvableProfile;
import com.viaversion.viaversion.api.minecraft.SoundEvent;
import com.viaversion.viaversion.api.minecraft.Vector;
@@ -58,6 +60,7 @@ import com.viaversion.viaversion.api.type.types.ByteType;
import com.viaversion.viaversion.api.type.types.ComponentType;
import com.viaversion.viaversion.api.type.types.DoubleType;
import com.viaversion.viaversion.api.type.types.EmptyType;
import com.viaversion.viaversion.api.type.types.FixedLengthArrayType;
import com.viaversion.viaversion.api.type.types.FloatArrayType;
import com.viaversion.viaversion.api.type.types.FloatType;
import com.viaversion.viaversion.api.type.types.IntArrayType;
@@ -105,6 +108,7 @@ import com.viaversion.viaversion.api.type.types.math.EulerAngleType;
import com.viaversion.viaversion.api.type.types.math.GlobalBlockPositionType;
import com.viaversion.viaversion.api.type.types.math.LowPrecisionVectorType;
import com.viaversion.viaversion.api.type.types.math.QuaternionType;
import com.viaversion.viaversion.api.type.types.math.Vector3dType;
import com.viaversion.viaversion.api.type.types.math.Vector3fType;
import com.viaversion.viaversion.api.type.types.math.VectorType;
import com.viaversion.viaversion.api.type.types.misc.CompoundTagHolderType;
@@ -118,6 +122,8 @@ import com.viaversion.viaversion.api.type.types.misc.NamedCompoundTagType;
import com.viaversion.viaversion.api.type.types.misc.PlayerMessageSignatureType;
import com.viaversion.viaversion.api.type.types.misc.ProfileKeyType;
import com.viaversion.viaversion.api.type.types.misc.ProfilePropertyType;
import com.viaversion.viaversion.api.type.types.misc.ResolvableFloatType;
import com.viaversion.viaversion.api.type.types.misc.ResolvableIntType;
import com.viaversion.viaversion.api.type.types.misc.ResolvableProfileType;
import com.viaversion.viaversion.api.type.types.misc.SoundEventType;
import com.viaversion.viaversion.api.type.types.misc.TagKeyType;
@@ -179,10 +185,13 @@ public final class Types {
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Types.UUID);
public static final VarIntType VAR_INT = new VarIntType();
public static final OptionalVarIntType OPTIONAL_VAR_INT = new OptionalVarIntType();
public static final OptionalVarIntType OPTIONAL_VAR_INT = new OptionalVarIntType(); // compact
public static final OptionalType<Integer> BOOL_OPTIONAL_VAR_INT = new VarIntType.OptionalVarIntType(); // regular boolean prefixed
public static final Type<int[]> VAR_INT_ARRAY_PRIMITIVE = new VarIntArrayType();
public static final VarLongType VAR_LONG = new VarLongType();
public static final BitSetType BIT_SET = new BitSetType(); // Generic length
/* MC Types */
public static final Type<byte[]> SERVERBOUND_CUSTOM_PAYLOAD_DATA = new RemainingBytesType(Short.MAX_VALUE);
@@ -191,6 +200,9 @@ public final class Types {
public static final Type<Key[]> IDENTIFIER_ARRAY = new ArrayType<>(IDENTIFIER);
public static final Type<Key> TAG_KEY = new TagKeyType();
public static final Type<ResolvableFloat> RESOLVABLE_FLOAT = new ResolvableFloatType();
public static final Type<ResolvableInt> RESOLVABLE_INT = new ResolvableIntType();
public static final Type<BlockPosition> BLOCK_POSITION1_8 = new BlockPositionType1_8();
public static final Type<BlockPosition> OPTIONAL_POSITION1_8 = new BlockPositionType1_8.OptionalBlockPositionType();
public static final Type<BlockPosition> BLOCK_POSITION1_14 = new BlockPositionType1_14();
@@ -198,6 +210,7 @@ public final class Types {
public static final Type<EulerAngle> ROTATIONS = new EulerAngleType();
public static final Type<Vector> VECTOR = new VectorType();
public static final Type<Vector3f> VECTOR3F = new Vector3fType();
public static final Type<Vector3d> VECTOR3D = new Vector3dType();
public static final Type<Quaternion> QUATERNION = new QuaternionType();
public static final Type<Vector3d> LOW_PRECISION_VECTOR = new LowPrecisionVectorType();
@Deprecated(forRemoval = true)
@@ -214,7 +227,11 @@ public final class Types {
public static final Type<Tag[]> TAG_ARRAY = new ArrayType<>(TAG);
public static final Type<Tag> OPTIONAL_TAG = TagType.OptionalTagType.type();
public static final Type<Tag> TEXT_COMPONENT_TAG = new TextComponentTagType(); // only strictly needed for hashing
public static final Type<Tag[]> TEXT_COMPONENT_TAG_ARRAY = new ArrayType<>(Types.TEXT_COMPONENT_TAG);
public static final Type<Tag> CUSTOM_CLICK_ACTION_TAG = new LengthPrefixedTagType(65536);
public static final FixedLengthArrayType<Tag> SIGN_TEXT = new FixedLengthArrayType<>(TAG, 4);
public static final Type<Tag[]> OPTIONAL_SIGN_TEXT = SIGN_TEXT.new OptionalFixedLengthArrayType();
public static final Type<Tag> TRUSTED_TAG = new TagType(false);
public static final Type<Tag> TRUSTED_OPTIONAL_TAG = TagType.OptionalTagType.trustedType();
@@ -279,6 +296,7 @@ public final class Types {
public static final Type<List<EntityData>> ENTITY_DATA_LIST1_9 = new EntityDataListType(ENTITY_DATA1_9);
public static final Type<List<EntityData>> ENTITY_DATA_LIST1_12 = new EntityDataListType(ENTITY_DATA1_12);
// No longer used past 1.20.2
public static final Type<Item> ITEM1_8 = new ItemType1_8();
public static final Type<Item> ITEM1_13 = new ItemType1_13();
@@ -31,7 +31,8 @@ import java.util.ArrayList;
import java.util.List;
public class ArrayType<T> extends Type<T[]> {
private final Type<T> elementType;
private static final int MAX_PRESIZED_LENGTH = 128;
protected final Type<T> elementType;
private final int maxLength;
public ArrayType(Type<T> type) {
@@ -45,6 +46,12 @@ public class ArrayType<T> extends Type<T[]> {
this.maxLength = maxLength;
}
private ArrayType(Class<? super T[]> outputClass, Type<T> type, int maxLength) {
super(type.getTypeName() + " Array", outputClass);
this.elementType = type;
this.maxLength = maxLength;
}
public static Class<?> getArrayClass(Class<?> componentType) {
// Should only happen once per class init.
return Array.newInstance(componentType, 0).getClass();
@@ -57,10 +64,10 @@ public class ArrayType<T> extends Type<T[]> {
throw new IllegalArgumentException("Array length " + amount + " is longer than maximum " + maxLength);
}
return amount < Short.MAX_VALUE ? readArray(buffer, amount) : readList(buffer, amount);
return amount <= MAX_PRESIZED_LENGTH ? readArray(buffer, amount) : readList(buffer, amount);
}
private T[] readArray(ByteBuf buffer, int length) {
protected T[] readArray(ByteBuf buffer, int length) {
T[] array = createArray(length);
for (int i = 0; i < length; i++) {
array[i] = elementType.read(buffer);
@@ -101,4 +108,18 @@ public class ArrayType<T> extends Type<T[]> {
}
});
}
public ArrayType<T> withMaxLength(final int maxLength) {
return new ArrayType<>(this.getOutputClass(), this.elementType, maxLength);
}
static int checkLength(final int length, final int elementBytes, final int readable) {
if (length < 0) {
throw new IllegalArgumentException("Length " + length + " is negative");
}
if ((long) length * elementBytes > readable) {
throw new IllegalArgumentException("Length " + length + " is larger than readable bytes");
}
return length;
}
}
@@ -24,6 +24,7 @@ package com.viaversion.viaversion.api.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import java.util.BitSet;
@@ -39,17 +40,31 @@ public class BitSetType extends Type<BitSet> {
this.bytesLength = (int) Math.ceil(length / 8D);
}
public BitSetType() {
super(BitSet.class);
this.length = -1;
this.bytesLength = -1;
}
@Override
public BitSet read(ByteBuf buffer) {
final byte[] bytes = new byte[bytesLength];
buffer.readBytes(bytes);
return BitSet.valueOf(bytes);
if (this.length != -1) {
final byte[] bytes = new byte[bytesLength];
buffer.readBytes(bytes);
return BitSet.valueOf(bytes);
} else {
return BitSet.valueOf(Types.BYTE_ARRAY_PRIMITIVE.read(buffer));
}
}
@Override
public void write(ByteBuf buffer, BitSet object) {
Preconditions.checkArgument(object.length() <= length, "BitSet of length %s larger than max length %s", object.length(), length);
final byte[] bytes = object.toByteArray();
buffer.writeBytes(bytes.length == bytesLength ? bytes : Arrays.copyOf(bytes, bytesLength));
if (this.length != -1) {
Preconditions.checkArgument(object.length() <= length, "BitSet of length %s larger than max length %s", object.length(), length);
final byte[] bytes = object.toByteArray();
buffer.writeBytes(bytes.length == bytesLength ? bytes : Arrays.copyOf(bytes, bytesLength));
} else {
Types.BYTE_ARRAY_PRIMITIVE.write(buffer, object.toByteArray());
}
}
}
@@ -57,7 +57,7 @@ public class BooleanArrayType extends Type<boolean[]> {
@Override
public boolean[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
Preconditions.checkArgument(buffer.isReadable(length), "Length is larger than readable bytes");
final boolean[] array = new boolean[length];
for (int i = 0; i < length; i++) {
array[i] = buffer.readBoolean();
@@ -32,15 +32,30 @@ import io.netty.buffer.ByteBuf;
public class ByteArrayType extends Type<byte[]> {
private final int length;
private final int maxLength;
public ByteArrayType(final int length) {
super(byte[].class);
this.length = length;
this(length, -1);
}
public ByteArrayType() {
this(-1, -1);
}
private ByteArrayType(final int length, final int maxLength) {
super(byte[].class);
this.length = -1;
this.length = length;
this.maxLength = maxLength;
}
/**
* Returns a length-prefixed byte array type that rejects arrays longer than the given maximum.
*
* @param maxLength maximum array length
* @return length-prefixed byte array type with the given maximum length
*/
public static ByteArrayType withMaxLength(final int maxLength) {
return new ByteArrayType(-1, maxLength);
}
@Override
@@ -48,6 +63,7 @@ public class ByteArrayType extends Type<byte[]> {
if (this.length != -1) {
Preconditions.checkArgument(length == object.length, "Length does not match expected length");
} else {
checkMaxLength(object.length);
Types.VAR_INT.writePrimitive(buffer, object.length);
}
buffer.writeBytes(object);
@@ -55,13 +71,20 @@ public class ByteArrayType extends Type<byte[]> {
@Override
public byte[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final int length = this.length == -1 ? checkMaxLength(Types.VAR_INT.readPrimitive(buffer)) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is larger than readable bytes");
final byte[] array = new byte[length];
buffer.readBytes(array);
return array;
}
private int checkMaxLength(final int length) {
if (maxLength != -1 && length > maxLength) {
throw new IllegalArgumentException("Byte array length " + length + " is longer than maximum " + maxLength);
}
return length;
}
@Override
public void write(final Ops ops, final byte[] value) {
ops.writeBytes(value);
@@ -76,4 +76,16 @@ public final class EnumType extends VarIntType {
public enum Fallback {
ZERO, WRAP, CLAMP
}
public final class EnumArrayType extends VarIntArrayType {
@Override
public void write(final Ops ops, final int[] value) {
ops.writeList(list -> {
for (final int ordinal : value) {
list.write(Types.STRING, nameFromId(ordinal));
}
});
}
}
}
@@ -0,0 +1,56 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2026 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.type.types;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
public class FixedLengthArrayType<T> extends ArrayType<T> {
private final int length;
public FixedLengthArrayType(final Type<T> type, final int length) {
super(type);
this.length = length;
}
@Override
public T[] read(final ByteBuf buffer) {
return readArray(buffer, this.length);
}
@Override
public void write(final ByteBuf buffer, final T[] object) {
for (final T o : object) {
elementType.write(buffer, o);
}
}
public final class OptionalFixedLengthArrayType extends OptionalType<T[]> {
public OptionalFixedLengthArrayType() {
super(FixedLengthArrayType.this);
}
}
}
@@ -27,6 +27,8 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
public class FloatArrayType extends Type<float[]> {
private final int length;
@@ -55,8 +57,7 @@ public class FloatArrayType extends Type<float[]> {
@Override
public float[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Float.BYTES, buffer.readableBytes());
final float[] array = new float[length];
for (int i = 0; i < length; i++) {
array[i] = buffer.readFloat();
@@ -28,6 +28,8 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
public class IntArrayType extends Type<int[]> {
private final int length;
@@ -56,8 +58,7 @@ public class IntArrayType extends Type<int[]> {
@Override
public int[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Integer.BYTES, buffer.readableBytes());
final int[] array = new int[length];
for (int i = 0; i < length; i++) {
array[i] = buffer.readInt();
@@ -28,6 +28,8 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import static com.viaversion.viaversion.api.type.types.ArrayType.checkLength;
public class LongArrayType extends Type<long[]> {
private final int length;
@@ -43,8 +45,7 @@ public class LongArrayType extends Type<long[]> {
@Override
public long[] read(final ByteBuf buffer) {
final int length = this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length;
Preconditions.checkArgument(buffer.isReadable(length), "Length is fewer than readable bytes");
final int length = checkLength(this.length == -1 ? Types.VAR_INT.readPrimitive(buffer) : this.length, Long.BYTES, buffer.readableBytes());
return readFixedLength(buffer, length);
}
@@ -22,7 +22,6 @@
*/
package com.viaversion.viaversion.api.type.types;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
@@ -35,6 +34,7 @@ public class StringType extends Type<String> {
private static final int MAX_CHAR_UTF_8_LENGTH = Character.toString(Character.MAX_VALUE)
.getBytes(StandardCharsets.UTF_8).length;
private final int maxLength;
private final int maxByteLength;
public StringType() {
this(Short.MAX_VALUE);
@@ -43,21 +43,22 @@ public class StringType extends Type<String> {
public StringType(int maxLength) {
super(String.class);
this.maxLength = maxLength;
this.maxByteLength = maxLength * MAX_CHAR_UTF_8_LENGTH;
}
@Override
public String read(ByteBuf buffer) {
int len = Types.VAR_INT.readPrimitive(buffer);
Preconditions.checkArgument(len <= maxLength * MAX_CHAR_UTF_8_LENGTH,
"Cannot receive string longer than " + maxLength + " * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got %s bytes)", len);
if (len > maxByteLength) {
throw new IllegalArgumentException("Cannot receive string longer than " + maxLength + " * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got " + len + " bytes)");
}
String string = buffer.toString(buffer.readerIndex(), len, StandardCharsets.UTF_8);
buffer.skipBytes(len);
Preconditions.checkArgument(string.length() <= maxLength,
"Cannot receive string longer than " + maxLength + " characters (got %s bytes)", string.length());
if (string.length() > maxLength) {
throw new IllegalArgumentException("Cannot receive string longer than " + maxLength + " characters (got " + string.length() + ")");
}
return string;
}
@@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.type.types;
import com.viaversion.viaversion.api.minecraft.codec.Ops;
import com.viaversion.viaversion.api.type.OptionalType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.TypeConverter;
import com.viaversion.viaversion.api.type.Types;
@@ -108,4 +109,11 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
public static int varIntLength(final int value) {
return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)];
}
public static final class OptionalVarIntType extends OptionalType<Integer> {
public OptionalVarIntType() {
super(Types.VAR_INT);
}
}
}
@@ -83,8 +83,11 @@ public class PaletteType1_18 extends PaletteTypeBase {
final int valuesPerLong = (char) (64 / bitsPerValue);
final int expectedLength = (type.size() + valuesPerLong - 1) / valuesPerLong;
if (values.length == expectedLength) {
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values,
bitsPerValue == globalPaletteBits ? palette::setIdAt : palette::setPaletteIndexAt);
if (bitsPerValue == globalPaletteBits) {
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values, palette::setIdAt);
} else {
palette.setPaletteIndexes(values, bitsPerValue, valuesPerLong);
}
}
}
@@ -120,11 +123,7 @@ public class PaletteType1_18 extends PaletteTypeBase {
return;
}
final long[] values = CompactArrayUtil.createCompactArrayWithPadding(
bitsPerValue,
type.size(),
bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt
);
final long[] values = palette.createPackedValues(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits);
Types.LONG_ARRAY_PRIMITIVE.write(buffer, values);
}
@@ -46,12 +46,11 @@ public final class PaletteType1_21_5 extends PaletteType1_18 {
final int expectedLength = (type.size() + valuesPerLong - 1) / valuesPerLong;
final long[] values = LongArrayType.readFixedLength(buffer, expectedLength);
if (values.length != 0) {
CompactArrayUtil.iterateCompactArrayWithPadding(
bitsPerValue,
type.size(),
values,
bitsPerValue == globalPaletteBits ? palette::setIdAt : palette::setPaletteIndexAt
);
if (bitsPerValue == globalPaletteBits) {
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values, palette::setIdAt);
} else {
palette.setPaletteIndexes(values, bitsPerValue, valuesPerLong);
}
}
}
@@ -61,11 +60,7 @@ public final class PaletteType1_21_5 extends PaletteType1_18 {
return;
}
final long[] values = CompactArrayUtil.createCompactArrayWithPadding(
bitsPerValue,
type.size(),
bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt
);
final long[] values = palette.createPackedValues(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits);
LongArrayType.writeFixedLength(buffer, values);
}
@@ -34,6 +34,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
public class HashedItemType1_21_5 extends Type<HashedItem> {
private static final int MAX_DATA_TYPE_ID = 255; // Needs adjusting if we ever get beyond this
public HashedItemType1_21_5() {
super(HashedItem.class);
@@ -51,7 +52,7 @@ public class HashedItemType1_21_5 extends Type<HashedItem> {
final int addedComponentsSize = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
final Int2IntMap dataHashes = new Int2IntOpenHashMap(addedComponentsSize);
for (int i = 0; i < addedComponentsSize; i++) {
final int dataType = Types.VAR_INT.readPrimitive(buffer);
final int dataType = readDataType(buffer);
final int hash = Types.INT.readPrimitive(buffer);
dataHashes.put(dataType, hash);
}
@@ -59,12 +60,20 @@ public class HashedItemType1_21_5 extends Type<HashedItem> {
final int removedComponentsSize = Limit.max(Types.VAR_INT.readPrimitive(buffer), 256);
final IntSet removedData = new IntOpenHashSet(removedComponentsSize);
for (int i = 0; i < removedComponentsSize; i++) {
final int dataType = Types.VAR_INT.readPrimitive(buffer);
final int dataType = readDataType(buffer);
removedData.add(dataType);
}
return new HashedStructuredItem(id, amount, dataHashes, removedData);
}
private static int readDataType(final ByteBuf buffer) {
final int dataType = Types.VAR_INT.readPrimitive(buffer);
if (dataType < 0 || dataType > MAX_DATA_TYPE_ID) {
throw new IllegalArgumentException("Invalid data component type " + dataType);
}
return dataType;
}
@Override
public void write(final ByteBuf buffer, final HashedItem value) {
if (value.isEmpty()) {
Loaded 100 of 427 files, more files were not shown because too many files have changed in this diff. Show more