diff --git a/engineering/testcounts/expected_counts.json b/engineering/testcounts/expected_counts.json
index 08e9b6f..36aed39 100644
--- a/engineering/testcounts/expected_counts.json
+++ b/engineering/testcounts/expected_counts.json
@@ -7,13 +7,13 @@
"Umpk.Data.Lang.Tests.dll": 80,
"Umpk.DataGen.Tests.dll": 67,
"Umpk.Game.Tests.dll": 484,
- "Umpk.IntegrationTests.dll": 46,
+ "Umpk.IntegrationTests.dll": 47,
"Umpk.Nbt.Tests.dll": 118,
"Umpk.PacketRecorder.Tests.dll": 14,
"Umpk.Pathfinding.Tests.dll": 1045,
"Umpk.Physics.Tests.dll": 292,
"Umpk.Protocol.Java.Conformance.dll": 881,
- "Umpk.Protocol.Java.Tests.dll": 4670,
+ "Umpk.Protocol.Java.Tests.dll": 4688,
"Umpk.Realms.Tests.dll": 53,
"Umpk.Text.Tests.dll": 218
}
diff --git a/fixtures/codec-identity/770.txt b/fixtures/codec-identity/770.txt
index 21e44bd..f980c8f 100644
--- a/fixtures/codec-identity/770.txt
+++ b/fixtures/codec-identity/770.txt
@@ -59,9 +59,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_5 fault:ProtocolViolationException shape:varint*node,varint|argtypes/940a0fa5,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table770) read:4 shape:varint,varint,varint*stack,stack|components/3e625a95
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table770) read:4 shape:varint,varint,varint*stack,stack|components/0995b74c
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table770) read:5 shape:varint,varint,short,stack|components/3e625a95
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table770) read:5 shape:varint,varint,short,stack|components/0995b74c
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -82,11 +82,11 @@ Play Clientbound 0x25 minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x26 minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x27 minecraft:level_chunk_with_light ChunkCodecs.V1_21_5 read:256 shape:chunk_modern(FixedSize,fluid=0,biome=1,min=6)
Play Clientbound 0x28 minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_5 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/3e625a95,alwaysshow=1)
+Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_5 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/0995b74c,alwaysshow=1)
Play Clientbound 0x2A minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x2B minecraft:login JoinGameCodecs.V1_21_2 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x2C minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table770) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/3e625a95
+Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table770) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/0995b74c
Play Clientbound 0x2E minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x2F minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x30 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -108,7 +108,7 @@ Play Clientbound 0x3F minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x40 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x41 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x42 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table770)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/3e625a95)
+Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table770)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/0995b74c)
Play Clientbound 0x44 minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x45 minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x46 minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -130,10 +130,10 @@ Play Clientbound 0x55 minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x56 minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x57 minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x58 minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table770) read:1 shape:stack|components/3e625a95
+Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table770) read:1 shape:stack|components/0995b74c
Play Clientbound 0x5A minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_17 read:12 shape:opaque
Play Clientbound 0x5B minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_5 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/3e625a95,particles=components/3e625a95
+Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_5 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/0995b74c,particles=components/0995b74c
Play Clientbound 0x5D minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x5E minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotion read:7 shape:opaque
Play Clientbound 0x5F minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -142,7 +142,7 @@ Play Clientbound 0x61 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x62 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x63 minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x64 minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table770) read:2 shape:varint,stack|components/3e625a95
+Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table770) read:2 shape:varint,stack|components/0995b74c
Play Clientbound 0x66 minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x67 minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x68 minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -164,7 +164,7 @@ Play Clientbound 0x77 minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x78 minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x79 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x7A minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_5 read:5 shape:update_advancements(icon=countfirst/components/3e625a95,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_5 read:5 shape:update_advancements(icon=countfirst/components/0995b74c,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x7C minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x7D minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x7E minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -188,7 +188,7 @@ Play Serverbound 0x0C minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0D minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x0E minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x0F minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x10 minecraft:container_click MakeContainerClick(Table770) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/3e625a95
+Play Serverbound 0x10 minecraft:container_click MakeContainerClick(Table770) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/0995b74c
Play Serverbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x12 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x13 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -226,7 +226,7 @@ Play Serverbound 0x32 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x33 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x34 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x35 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x36 minecraft:set_creative_mode_slot MakeCreativeSlot(Table770) read:3 shape:short,delimited_stack|components/3e625a95
+Play Serverbound 0x36 minecraft:set_creative_mode_slot MakeCreativeSlot(Table770) read:3 shape:short,delimited_stack|components/0995b74c
Play Serverbound 0x37 minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x38 minecraft:set_structure_block marker n/a shape:opaque
Play Serverbound 0x39 minecraft:set_test_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/771.txt b/fixtures/codec-identity/771.txt
index 2d9d362..920fae5 100644
--- a/fixtures/codec-identity/771.txt
+++ b/fixtures/codec-identity/771.txt
@@ -62,9 +62,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_6 fault:ProtocolViolationException shape:varint*node,varint|argtypes/116da89b,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table771) read:4 shape:varint,varint,varint*stack,stack|components/b2846a3a
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table771) read:4 shape:varint,varint,varint*stack,stack|components/7a1eb30a
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table771) read:5 shape:varint,varint,short,stack|components/b2846a3a
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table771) read:5 shape:varint,varint,short,stack|components/7a1eb30a
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -85,11 +85,11 @@ Play Clientbound 0x25 minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x26 minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x27 minecraft:level_chunk_with_light ChunkCodecs.V1_21_5 read:256 shape:chunk_modern(FixedSize,fluid=0,biome=1,min=6)
Play Clientbound 0x28 minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_6 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/b2846a3a,alwaysshow=1)
+Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_6 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/7a1eb30a,alwaysshow=1)
Play Clientbound 0x2A minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x2B minecraft:login JoinGameCodecs.V1_21_2 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x2C minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table771) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/b2846a3a
+Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table771) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/7a1eb30a
Play Clientbound 0x2E minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x2F minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x30 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -111,7 +111,7 @@ Play Clientbound 0x3F minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x40 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x41 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x42 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table771)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/b2846a3a)
+Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table771)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/7a1eb30a)
Play Clientbound 0x44 minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x45 minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x46 minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -133,10 +133,10 @@ Play Clientbound 0x55 minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x56 minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x57 minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x58 minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table771) read:1 shape:stack|components/b2846a3a
+Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table771) read:1 shape:stack|components/7a1eb30a
Play Clientbound 0x5A minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_17 read:12 shape:opaque
Play Clientbound 0x5B minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_6 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/b2846a3a,particles=components/b2846a3a
+Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_6 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/7a1eb30a,particles=components/7a1eb30a
Play Clientbound 0x5D minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x5E minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotion read:7 shape:opaque
Play Clientbound 0x5F minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -145,7 +145,7 @@ Play Clientbound 0x61 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x62 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x63 minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x64 minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table771) read:2 shape:varint,stack|components/b2846a3a
+Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table771) read:2 shape:varint,stack|components/7a1eb30a
Play Clientbound 0x66 minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x67 minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x68 minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -167,7 +167,7 @@ Play Clientbound 0x77 minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x78 minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x79 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x7A minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_6 read:5 shape:update_advancements(icon=countfirst/components/b2846a3a,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_6 read:5 shape:update_advancements(icon=countfirst/components/7a1eb30a,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x7C minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x7D minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x7E minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -195,7 +195,7 @@ Play Serverbound 0x0D minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0E minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x0F minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x10 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table771) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/b2846a3a
+Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table771) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/7a1eb30a
Play Serverbound 0x12 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x13 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x14 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -233,7 +233,7 @@ Play Serverbound 0x33 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x34 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x35 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x36 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table771) read:3 shape:short,delimited_stack|components/b2846a3a
+Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table771) read:3 shape:short,delimited_stack|components/7a1eb30a
Play Serverbound 0x38 minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x39 minecraft:set_structure_block marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_test_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/772.txt b/fixtures/codec-identity/772.txt
index 100b2cb..d70170e 100644
--- a/fixtures/codec-identity/772.txt
+++ b/fixtures/codec-identity/772.txt
@@ -62,9 +62,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_6 fault:ProtocolViolationException shape:varint*node,varint|argtypes/116da89b,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table771) read:4 shape:varint,varint,varint*stack,stack|components/b2846a3a
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table771) read:4 shape:varint,varint,varint*stack,stack|components/7a1eb30a
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table771) read:5 shape:varint,varint,short,stack|components/b2846a3a
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table771) read:5 shape:varint,varint,short,stack|components/7a1eb30a
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -85,11 +85,11 @@ Play Clientbound 0x25 minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x26 minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x27 minecraft:level_chunk_with_light ChunkCodecs.V1_21_5 read:256 shape:chunk_modern(FixedSize,fluid=0,biome=1,min=6)
Play Clientbound 0x28 minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_6 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/b2846a3a,alwaysshow=1)
+Play Clientbound 0x29 minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_6 read:47 shape:level_particles_modern(shapes=b2aecf14,icons=components/7a1eb30a,alwaysshow=1)
Play Clientbound 0x2A minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x2B minecraft:login JoinGameCodecs.V1_21_2 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x2C minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table771) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/b2846a3a
+Play Clientbound 0x2D minecraft:merchant_offers MakeMerchantOffers(Table771) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/7a1eb30a
Play Clientbound 0x2E minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x2F minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x30 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -111,7 +111,7 @@ Play Clientbound 0x3F minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x40 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x41 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x42 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table771)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/b2846a3a)
+Play Clientbound 0x43 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table771)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/7a1eb30a)
Play Clientbound 0x44 minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x45 minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x46 minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -133,10 +133,10 @@ Play Clientbound 0x55 minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x56 minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x57 minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x58 minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table771) read:1 shape:stack|components/b2846a3a
+Play Clientbound 0x59 minecraft:set_cursor_item MakeSetCursorItem(Table771) read:1 shape:stack|components/7a1eb30a
Play Clientbound 0x5A minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_17 read:12 shape:opaque
Play Clientbound 0x5B minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_6 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/b2846a3a,particles=components/b2846a3a
+Play Clientbound 0x5C minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_6 fault:ProtocolViolationException shape:varint,metadata|metadata/59245ff2,Modern,countfirst/components/7a1eb30a,particles=components/7a1eb30a
Play Clientbound 0x5D minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x5E minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotion read:7 shape:opaque
Play Clientbound 0x5F minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -145,7 +145,7 @@ Play Clientbound 0x61 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x62 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x63 minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x64 minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table771) read:2 shape:varint,stack|components/b2846a3a
+Play Clientbound 0x65 minecraft:set_player_inventory MakeSetPlayerInventory(Table771) read:2 shape:varint,stack|components/7a1eb30a
Play Clientbound 0x66 minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x67 minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x68 minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -167,7 +167,7 @@ Play Clientbound 0x77 minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x78 minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x79 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x7A minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_6 read:5 shape:update_advancements(icon=countfirst/components/b2846a3a,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x7B minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_6 read:5 shape:update_advancements(icon=countfirst/components/7a1eb30a,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x7C minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x7D minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x7E minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -195,7 +195,7 @@ Play Serverbound 0x0D minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0E minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x0F minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x10 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table771) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/b2846a3a
+Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table771) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/7a1eb30a
Play Serverbound 0x12 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x13 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x14 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -233,7 +233,7 @@ Play Serverbound 0x33 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x34 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x35 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x36 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table771) read:3 shape:short,delimited_stack|components/b2846a3a
+Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table771) read:3 shape:short,delimited_stack|components/7a1eb30a
Play Serverbound 0x38 minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x39 minecraft:set_structure_block marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_test_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/773.txt b/fixtures/codec-identity/773.txt
index 4ad0bd6..3b35a63 100644
--- a/fixtures/codec-identity/773.txt
+++ b/fixtures/codec-identity/773.txt
@@ -64,9 +64,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_6 fault:ProtocolViolationException shape:varint*node,varint|argtypes/116da89b,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table773) read:4 shape:varint,varint,varint*stack,stack|components/989bb708
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table773) read:4 shape:varint,varint,varint*stack,stack|components/25dbf4b2
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table773) read:5 shape:varint,varint,short,stack|components/989bb708
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table773) read:5 shape:varint,varint,short,stack|components/25dbf4b2
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -92,11 +92,11 @@ Play Clientbound 0x2A minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x2B minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x2C minecraft:level_chunk_with_light ChunkCodecs.V1_21_5 read:256 shape:chunk_modern(FixedSize,fluid=0,biome=1,min=6)
Play Clientbound 0x2D minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x2E minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_9 read:47 shape:level_particles_modern(shapes=1e5467a5,icons=components/989bb708,alwaysshow=1)
+Play Clientbound 0x2E minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_9 read:47 shape:level_particles_modern(shapes=1e5467a5,icons=components/25dbf4b2,alwaysshow=1)
Play Clientbound 0x2F minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x30 minecraft:login JoinGameCodecs.V1_21_2 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x31 minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x32 minecraft:merchant_offers MakeMerchantOffers(Table773) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/989bb708
+Play Clientbound 0x32 minecraft:merchant_offers MakeMerchantOffers(Table773) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/25dbf4b2
Play Clientbound 0x33 minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x34 minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x35 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -118,7 +118,7 @@ Play Clientbound 0x44 minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x45 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x46 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x47 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x48 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table773)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/989bb708)
+Play Clientbound 0x48 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table773)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/25dbf4b2)
Play Clientbound 0x49 minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x4A minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x4B minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -140,10 +140,10 @@ Play Clientbound 0x5A minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x5B minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x5C minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x5D minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x5E minecraft:set_cursor_item MakeSetCursorItem(Table773) read:1 shape:stack|components/989bb708
+Play Clientbound 0x5E minecraft:set_cursor_item MakeSetCursorItem(Table773) read:1 shape:stack|components/25dbf4b2
Play Clientbound 0x5F minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_21_9 read:17 shape:opaque
Play Clientbound 0x60 minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x61 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_9 fault:ProtocolViolationException shape:varint,metadata|metadata/2680d497,Modern,countfirst/components/989bb708,particles=components/989bb708
+Play Clientbound 0x61 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_9 fault:ProtocolViolationException shape:varint,metadata|metadata/2680d497,Modern,countfirst/components/25dbf4b2,particles=components/25dbf4b2
Play Clientbound 0x62 minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x63 minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotionV1_21_9 read:2 shape:opaque
Play Clientbound 0x64 minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -152,7 +152,7 @@ Play Clientbound 0x66 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x67 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x68 minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x69 minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x6A minecraft:set_player_inventory MakeSetPlayerInventory(Table773) read:2 shape:varint,stack|components/989bb708
+Play Clientbound 0x6A minecraft:set_player_inventory MakeSetPlayerInventory(Table773) read:2 shape:varint,stack|components/25dbf4b2
Play Clientbound 0x6B minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x6C minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x6D minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -174,7 +174,7 @@ Play Clientbound 0x7C minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x7D minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x7E minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x7F minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x80 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_9 read:5 shape:update_advancements(icon=countfirst/components/989bb708,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x80 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_9 read:5 shape:update_advancements(icon=countfirst/components/25dbf4b2,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x81 minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x82 minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x83 minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -202,7 +202,7 @@ Play Serverbound 0x0D minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0E minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x0F minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x10 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table773) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/989bb708
+Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table773) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/25dbf4b2
Play Serverbound 0x12 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x13 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x14 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -240,7 +240,7 @@ Play Serverbound 0x33 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x34 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x35 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x36 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table773) read:3 shape:short,delimited_stack|components/989bb708
+Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table773) read:3 shape:short,delimited_stack|components/25dbf4b2
Play Serverbound 0x38 minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x39 minecraft:set_structure_block marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_test_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/774.txt b/fixtures/codec-identity/774.txt
index 1797e88..abe4a90 100644
--- a/fixtures/codec-identity/774.txt
+++ b/fixtures/codec-identity/774.txt
@@ -64,9 +64,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_6 fault:ProtocolViolationException shape:varint*node,varint|argtypes/116da89b,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table774) read:4 shape:varint,varint,varint*stack,stack|components/624de208
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table774) read:4 shape:varint,varint,varint*stack,stack|components/0e4c7eb2
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table774) read:5 shape:varint,varint,short,stack|components/624de208
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table774) read:5 shape:varint,varint,short,stack|components/0e4c7eb2
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -92,11 +92,11 @@ Play Clientbound 0x2A minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x2B minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x2C minecraft:level_chunk_with_light ChunkCodecs.V1_21_5 read:256 shape:chunk_modern(FixedSize,fluid=0,biome=1,min=6)
Play Clientbound 0x2D minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x2E minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_11 read:47 shape:level_particles_modern(shapes=1e5467a5,icons=components/624de208,alwaysshow=1)
+Play Clientbound 0x2E minecraft:level_particles WorldEffectCodecs.LevelParticlesV1_21_11 read:47 shape:level_particles_modern(shapes=1e5467a5,icons=components/0e4c7eb2,alwaysshow=1)
Play Clientbound 0x2F minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x30 minecraft:login JoinGameCodecs.V1_21_2 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x31 minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x32 minecraft:merchant_offers MakeMerchantOffers(Table774) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/624de208
+Play Clientbound 0x32 minecraft:merchant_offers MakeMerchantOffers(Table774) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/0e4c7eb2
Play Clientbound 0x33 minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x34 minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x35 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -118,7 +118,7 @@ Play Clientbound 0x44 minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x45 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x46 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x47 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x48 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table774)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/624de208)
+Play Clientbound 0x48 minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V1_21_5(Table774)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+Item+ItemStack+Tag+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=countfirst/components/0e4c7eb2)
Play Clientbound 0x49 minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x4A minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x4B minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -140,10 +140,10 @@ Play Clientbound 0x5A minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x5B minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x5C minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x5D minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x5E minecraft:set_cursor_item MakeSetCursorItem(Table774) read:1 shape:stack|components/624de208
+Play Clientbound 0x5E minecraft:set_cursor_item MakeSetCursorItem(Table774) read:1 shape:stack|components/0e4c7eb2
Play Clientbound 0x5F minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_21_9 read:17 shape:opaque
Play Clientbound 0x60 minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x61 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_11 fault:ProtocolViolationException shape:varint,metadata|metadata/7aa7f842,Modern,countfirst/components/624de208,particles=components/624de208
+Play Clientbound 0x61 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV1_21_11 fault:ProtocolViolationException shape:varint,metadata|metadata/7aa7f842,Modern,countfirst/components/0e4c7eb2,particles=components/0e4c7eb2
Play Clientbound 0x62 minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x63 minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotionV1_21_9 read:2 shape:opaque
Play Clientbound 0x64 minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -152,7 +152,7 @@ Play Clientbound 0x66 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x67 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x68 minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x69 minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x6A minecraft:set_player_inventory MakeSetPlayerInventory(Table774) read:2 shape:varint,stack|components/624de208
+Play Clientbound 0x6A minecraft:set_player_inventory MakeSetPlayerInventory(Table774) read:2 shape:varint,stack|components/0e4c7eb2
Play Clientbound 0x6B minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x6C minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x6D minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -174,7 +174,7 @@ Play Clientbound 0x7C minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x7D minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x7E minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x7F minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x80 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_11 read:5 shape:update_advancements(icon=countfirst/components/624de208,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x80 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV1_21_11 read:5 shape:update_advancements(icon=countfirst/components/0e4c7eb2,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x81 minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x82 minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x83 minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -202,7 +202,7 @@ Play Serverbound 0x0D minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0E minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x0F minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x10 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table774) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/624de208
+Play Serverbound 0x11 minecraft:container_click MakeContainerClick(Table774) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/0e4c7eb2
Play Serverbound 0x12 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x13 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x14 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -240,7 +240,7 @@ Play Serverbound 0x33 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x34 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x35 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x36 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table774) read:3 shape:short,delimited_stack|components/624de208
+Play Serverbound 0x37 minecraft:set_creative_mode_slot MakeCreativeSlot(Table774) read:3 shape:short,delimited_stack|components/0e4c7eb2
Play Serverbound 0x38 minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x39 minecraft:set_structure_block marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_test_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/775.txt b/fixtures/codec-identity/775.txt
index c6c4aaa..31a2a26 100644
--- a/fixtures/codec-identity/775.txt
+++ b/fixtures/codec-identity/775.txt
@@ -64,9 +64,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V1_21_6 fault:ProtocolViolationException shape:varint*node,varint|argtypes/116da89b,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table775) read:4 shape:varint,varint,varint*stack,stack|components/5edef15d
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table775) read:4 shape:varint,varint,varint*stack,stack|components/8ef00e5f
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table775) read:5 shape:varint,varint,short,stack|components/5edef15d
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table775) read:5 shape:varint,varint,short,stack|components/8ef00e5f
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -93,12 +93,12 @@ Play Clientbound 0x2B minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x2C minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x2D minecraft:level_chunk_with_light ChunkCodecs.V26_1 read:256 shape:chunk_modern(FixedSize,fluid=1,biome=1,min=6)
Play Clientbound 0x2E minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x2F minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_1 read:47 shape:level_particles_modern(shapes=989757f2,icons=components/5edef15d,alwaysshow=1)
+Play Clientbound 0x2F minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_1 read:47 shape:level_particles_modern(shapes=989757f2,icons=components/8ef00e5f,alwaysshow=1)
Play Clientbound 0x30 minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x31 minecraft:login JoinGameCodecs.V26_1 read:30 shape:join_game(legacy=0,onlinemode=0)
Play Clientbound 0x32 minecraft:low_disk_space_warning marker n/a shape:opaque
Play Clientbound 0x33 minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x34 minecraft:merchant_offers MakeMerchantOffers(Table775) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/5edef15d
+Play Clientbound 0x34 minecraft:merchant_offers MakeMerchantOffers(Table775) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/8ef00e5f
Play Clientbound 0x35 minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x36 minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x37 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -120,7 +120,7 @@ Play Clientbound 0x46 minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x47 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x48 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x49 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x4A minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_1(Table775)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=template/components/5edef15d)
+Play Clientbound 0x4A minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_1(Table775)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=template/components/8ef00e5f)
Play Clientbound 0x4B minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x4C minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x4D minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -142,10 +142,10 @@ Play Clientbound 0x5C minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x5D minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x5E minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x5F minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x60 minecraft:set_cursor_item MakeSetCursorItem(Table775) read:1 shape:stack|components/5edef15d
+Play Clientbound 0x60 minecraft:set_cursor_item MakeSetCursorItem(Table775) read:1 shape:stack|components/8ef00e5f
Play Clientbound 0x61 minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_21_9 read:17 shape:opaque
Play Clientbound 0x62 minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x63 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_1 fault:ProtocolViolationException shape:varint,metadata|metadata/d121417b,Modern,countfirst/components/5edef15d,particles=components/5edef15d
+Play Clientbound 0x63 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_1 fault:ProtocolViolationException shape:varint,metadata|metadata/d121417b,Modern,countfirst/components/8ef00e5f,particles=components/8ef00e5f
Play Clientbound 0x64 minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x65 minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotionV1_21_9 read:2 shape:opaque
Play Clientbound 0x66 minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -154,7 +154,7 @@ Play Clientbound 0x68 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x69 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x6A minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x6B minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x6C minecraft:set_player_inventory MakeSetPlayerInventory(Table775) read:2 shape:varint,stack|components/5edef15d
+Play Clientbound 0x6C minecraft:set_player_inventory MakeSetPlayerInventory(Table775) read:2 shape:varint,stack|components/8ef00e5f
Play Clientbound 0x6D minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV1_21_5 fault:ComponentFormatException shape:set_player_team(V1_21_5)
Play Clientbound 0x6E minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x6F minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -176,7 +176,7 @@ Play Clientbound 0x7E minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x7F minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x80 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x81 minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x82 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_1 read:5 shape:update_advancements(icon=template/components/5edef15d,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x82 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_1 read:5 shape:update_advancements(icon=template/components/8ef00e5f,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x83 minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x84 minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x85 minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -205,7 +205,7 @@ Play Serverbound 0x0E minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0F minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x10 minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x11 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table775) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/5edef15d
+Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table775) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/8ef00e5f
Play Serverbound 0x13 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x14 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x15 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -243,7 +243,7 @@ Play Serverbound 0x34 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x35 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x36 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x37 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x38 minecraft:set_creative_mode_slot MakeCreativeSlot(Table775) read:3 shape:short,delimited_stack|components/5edef15d
+Play Serverbound 0x38 minecraft:set_creative_mode_slot MakeCreativeSlot(Table775) read:3 shape:short,delimited_stack|components/8ef00e5f
Play Serverbound 0x39 minecraft:set_game_rule marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x3B minecraft:set_structure_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/776.txt b/fixtures/codec-identity/776.txt
index b58082c..c0ebe5f 100644
--- a/fixtures/codec-identity/776.txt
+++ b/fixtures/codec-identity/776.txt
@@ -64,9 +64,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V26_2 fault:ProtocolViolationException shape:varint*node,varint|argtypes/95708f8c,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table776) read:4 shape:varint,varint,varint*stack,stack|components/58dbda2b
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table776) read:4 shape:varint,varint,varint*stack,stack|components/7a2ba02b
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table776) read:5 shape:varint,varint,short,stack|components/58dbda2b
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table776) read:5 shape:varint,varint,short,stack|components/7a2ba02b
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -93,12 +93,12 @@ Play Clientbound 0x2B minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x2C minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x2D minecraft:level_chunk_with_light ChunkCodecs.V26_1 read:256 shape:chunk_modern(FixedSize,fluid=1,biome=1,min=6)
Play Clientbound 0x2E minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x2F minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_2 read:47 shape:level_particles_modern(shapes=c199cc77,icons=components/58dbda2b,alwaysshow=1)
+Play Clientbound 0x2F minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_2 read:47 shape:level_particles_modern(shapes=c199cc77,icons=components/7a2ba02b,alwaysshow=1)
Play Clientbound 0x30 minecraft:light_update WorldStateCodecs.LightUpdateV1_20 read:8 shape:opaque
Play Clientbound 0x31 minecraft:login JoinGameCodecs.V26_2 read:31 shape:join_game(legacy=0,onlinemode=1)
Play Clientbound 0x32 minecraft:low_disk_space_warning marker n/a shape:opaque
Play Clientbound 0x33 minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x34 minecraft:merchant_offers MakeMerchantOffers(Table776) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/58dbda2b
+Play Clientbound 0x34 minecraft:merchant_offers MakeMerchantOffers(Table776) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/7a2ba02b
Play Clientbound 0x35 minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV1_9 read:8 shape:opaque
Play Clientbound 0x36 minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV1_9 read:10 shape:opaque
Play Clientbound 0x37 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -120,7 +120,7 @@ Play Clientbound 0x46 minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x47 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x48 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x49 minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x4A minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_1(Table776)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=template/components/58dbda2b)
+Play Clientbound 0x4A minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_1(Table776)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,stacks=template/components/7a2ba02b)
Play Clientbound 0x4B minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x4C minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x4D minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -142,10 +142,10 @@ Play Clientbound 0x5C minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x5D minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x5E minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x5F minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x60 minecraft:set_cursor_item MakeSetCursorItem(Table776) read:1 shape:stack|components/58dbda2b
+Play Clientbound 0x60 minecraft:set_cursor_item MakeSetCursorItem(Table776) read:1 shape:stack|components/7a2ba02b
Play Clientbound 0x61 minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_21_9 read:17 shape:opaque
Play Clientbound 0x62 minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x63 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_2 fault:ProtocolViolationException shape:varint,metadata|metadata/d121417b,Modern,countfirst/components/58dbda2b,particles=components/58dbda2b
+Play Clientbound 0x63 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_2 fault:ProtocolViolationException shape:varint,metadata|metadata/d121417b,Modern,countfirst/components/7a2ba02b,particles=components/7a2ba02b
Play Clientbound 0x64 minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x65 minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotionV1_21_9 read:2 shape:opaque
Play Clientbound 0x66 minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -154,7 +154,7 @@ Play Clientbound 0x68 minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x69 minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x6A minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x6B minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x6C minecraft:set_player_inventory MakeSetPlayerInventory(Table776) read:2 shape:varint,stack|components/58dbda2b
+Play Clientbound 0x6C minecraft:set_player_inventory MakeSetPlayerInventory(Table776) read:2 shape:varint,stack|components/7a2ba02b
Play Clientbound 0x6D minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV26_2 fault:ComponentFormatException shape:set_player_team(V26_2)
Play Clientbound 0x6E minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x6F minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -176,7 +176,7 @@ Play Clientbound 0x7E minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x7F minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x80 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x81 minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x82 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_2 read:5 shape:update_advancements(icon=template/components/58dbda2b,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
+Play Clientbound 0x82 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_2 read:5 shape:update_advancements(icon=template/components/7a2ba02b,text=nbt/modern,criteria=0,telemetry=1,showadv=1)
Play Clientbound 0x83 minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x84 minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x85 minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -205,7 +205,7 @@ Play Serverbound 0x0E minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0F minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x10 minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x11 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table776) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/58dbda2b
+Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table776) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/7a2ba02b
Play Serverbound 0x13 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x14 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x15 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -243,7 +243,7 @@ Play Serverbound 0x34 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x35 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x36 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x37 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x38 minecraft:set_creative_mode_slot MakeCreativeSlot(Table776) read:3 shape:short,delimited_stack|components/58dbda2b
+Play Serverbound 0x38 minecraft:set_creative_mode_slot MakeCreativeSlot(Table776) read:3 shape:short,delimited_stack|components/7a2ba02b
Play Serverbound 0x39 minecraft:set_game_rule marker n/a shape:opaque
Play Serverbound 0x3A minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x3B minecraft:set_structure_block marker n/a shape:opaque
diff --git a/fixtures/codec-identity/777.txt b/fixtures/codec-identity/777.txt
index 8e62c9a..8641487 100644
--- a/fixtures/codec-identity/777.txt
+++ b/fixtures/codec-identity/777.txt
@@ -65,9 +65,9 @@ Play Clientbound 0x0E minecraft:clear_titles TitleCodecs.ClearTitlesV1_17 read:1
Play Clientbound 0x0F minecraft:command_suggestions CommandSuggestionCodecs.CommandSuggestionsV1_20_3 read:4 shape:opaque
Play Clientbound 0x10 minecraft:commands CommandTreeCodecs.V26_3 fault:ProtocolViolationException shape:varint*node,varint|argtypes/35bcc283,timemin=1
Play Clientbound 0x11 minecraft:container_close ContainerCodecs.ContainerCloseClientModern read:1 shape:opaque
-Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table777) read:4 shape:varint,varint,varint*stack,stack|components/9cadf86a
+Play Clientbound 0x12 minecraft:container_set_content MakeContainerSetContent(Table777) read:4 shape:varint,varint,varint*stack,stack|components/05a9934d
Play Clientbound 0x13 minecraft:container_set_data ContainerCodecs.ContainerSetDataModern read:5 shape:opaque
-Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table777) read:5 shape:varint,varint,short,stack|components/9cadf86a
+Play Clientbound 0x14 minecraft:container_set_slot MakeContainerSetSlot(Table777) read:5 shape:varint,varint,short,stack|components/05a9934d
Play Clientbound 0x15 minecraft:cookie_request PlayCommonCodecs.CookieRequest fault:FormatException shape:identifier
Play Clientbound 0x16 minecraft:cooldown UiMiscCodecs.CooldownV1_21_2 fault:FormatException shape:opaque
Play Clientbound 0x17 minecraft:custom_chat_completions ChatDisplayCodecs.CustomChatCompletionsV1_19_1 read:2 shape:opaque
@@ -95,12 +95,12 @@ Play Clientbound 0x2C minecraft:initialize_border WorldBorderCodecs.InitializeBo
Play Clientbound 0x2D minecraft:keep_alive PlayKeepAliveCodecs.ClientV1_12_2 read:8 shape:long
Play Clientbound 0x2E minecraft:level_chunk_with_light ChunkCodecs.V26_1 read:256 shape:chunk_modern(FixedSize,fluid=1,biome=1,min=6)
Play Clientbound 0x2F minecraft:level_event WorldEffectCodecs.LevelEventV1_14 read:17 shape:opaque
-Play Clientbound 0x30 minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_3 read:53 shape:level_particles_777(shapes=3274b2ff,icons=components/9cadf86a,alwaysshow=1)
+Play Clientbound 0x30 minecraft:level_particles WorldEffectCodecs.LevelParticlesV26_3 read:53 shape:level_particles_777(shapes=3274b2ff,icons=components/05a9934d,alwaysshow=1)
Play Clientbound 0x31 minecraft:light_update WorldStateCodecs.LightUpdateV26_3 read:8 shape:opaque
Play Clientbound 0x32 minecraft:login JoinGameCodecs.V26_2 read:31 shape:join_game(legacy=0,onlinemode=1)
Play Clientbound 0x33 minecraft:low_disk_space_warning marker n/a shape:opaque
Play Clientbound 0x34 minecraft:map_item_data WorldMapCodecs.MapItemDataV1_21_5 read:5 shape:varint,byte,bool,opt(varint*icon),patch|nbt/modern
-Play Clientbound 0x35 minecraft:merchant_offers MakeMerchantOffers(Table777) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/9cadf86a
+Play Clientbound 0x35 minecraft:merchant_offers MakeMerchantOffers(Table777) read:6 shape:varint,varint*offer,varint,varint,bool,bool|components/05a9934d
Play Clientbound 0x36 minecraft:move_entity_pos EntityMoveCodecs.MoveEntityPosV26_3 read:8 shape:varint,varint,varint*(varint,short,short,short)
Play Clientbound 0x37 minecraft:move_entity_pos_rot EntityMoveCodecs.MoveEntityPosRotV26_3 read:10 shape:varint,varint,varint*(varint,short,short,short),angle,angle
Play Clientbound 0x38 minecraft:move_minecart_along_track marker n/a shape:opaque
@@ -122,7 +122,7 @@ Play Clientbound 0x47 minecraft:player_info_update PlayerListCodecs.PlayerInfoUp
Play Clientbound 0x48 minecraft:player_look_at marker n/a shape:opaque
Play Clientbound 0x49 minecraft:player_position EntityMoveCodecs.PlayerPositionV1_21_2 read:61 shape:opaque
Play Clientbound 0x4A minecraft:player_rotation marker n/a shape:opaque
-Play Clientbound 0x4B minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_3(Table777)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,tagholderset=1,stacks=template/components/9cadf86a)
+Play Clientbound 0x4B minecraft:recipe_book_add RecipeCodecs.RecipeBookAdd(RecipeBookWire.V26_3(Table777)) read:2 shape:recipe_book_add(slots=Empty+AnyFuel+WithAnyPotion+OnlyWithComponent+Item+ItemStack+Tag+Dyed+SmithingTrim+WithRemainder+Composite,trimholder=1,tagholderset=1,stacks=template/components/05a9934d)
Play Clientbound 0x4C minecraft:recipe_book_remove RecipeCodecs.RecipeBookRemoveModern read:1 shape:opaque
Play Clientbound 0x4D minecraft:recipe_book_settings RecipeCodecs.RecipeBookSettingsModern read:8 shape:opaque
Play Clientbound 0x4E minecraft:remove_entities EntitySpawnCodecs.RemoveEntities read:1 shape:opaque
@@ -145,10 +145,10 @@ Play Clientbound 0x5E minecraft:set_border_warning_distance WorldBorderCodecs.Se
Play Clientbound 0x5F minecraft:set_camera EntityStateCodecs.SetCamera read:1 shape:opaque
Play Clientbound 0x60 minecraft:set_chunk_cache_center WorldStateCodecs.SetChunkCacheCenterV1_14 read:2 shape:opaque
Play Clientbound 0x61 minecraft:set_chunk_cache_radius WorldStateCodecs.SetChunkCacheRadiusV1_14 read:1 shape:opaque
-Play Clientbound 0x62 minecraft:set_cursor_item MakeSetCursorItem(Table777) read:1 shape:stack|components/9cadf86a
+Play Clientbound 0x62 minecraft:set_cursor_item MakeSetCursorItem(Table777) read:1 shape:stack|components/05a9934d
Play Clientbound 0x63 minecraft:set_default_spawn_position WorldStateCodecs.SetDefaultSpawnV1_21_9 read:17 shape:opaque
Play Clientbound 0x64 minecraft:set_display_objective ScoreboardCodecs.SetDisplayObjectiveV1_20_2 read:2 shape:opaque
-Play Clientbound 0x65 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_3 fault:ProtocolViolationException shape:varint,metadata|metadata/dca908d6,Modern,countfirst/components/9cadf86a,particles=components/9cadf86a
+Play Clientbound 0x65 minecraft:set_entity_data EntityDataCodecs.SetEntityDataV26_3 fault:ProtocolViolationException shape:varint,metadata|metadata/dca908d6,Modern,countfirst/components/05a9934d,particles=components/05a9934d
Play Clientbound 0x66 minecraft:set_entity_link EntityStateCodecs.SetEntityLinkV1_9 read:8 shape:opaque
Play Clientbound 0x67 minecraft:set_entity_motion EntityMoveCodecs.SetEntityMotionV1_21_9 read:2 shape:opaque
Play Clientbound 0x68 minecraft:set_equipment EntityEquipmentCodecs.SetEquipmentV1_16 read:256 shape:opaque
@@ -157,7 +157,7 @@ Play Clientbound 0x6A minecraft:set_health EntityStateCodecs.SetHealth read:9 sh
Play Clientbound 0x6B minecraft:set_held_slot EntityStateCodecs.SetHeldSlotV1_21_4 read:1 shape:opaque
Play Clientbound 0x6C minecraft:set_objective ScoreboardCodecs.SetObjectiveV1_21_5 fault:ComponentFormatException shape:opaque
Play Clientbound 0x6D minecraft:set_passengers EntityStateCodecs.SetPassengers read:2 shape:opaque
-Play Clientbound 0x6E minecraft:set_player_inventory MakeSetPlayerInventory(Table777) read:2 shape:varint,stack|components/9cadf86a
+Play Clientbound 0x6E minecraft:set_player_inventory MakeSetPlayerInventory(Table777) read:2 shape:varint,stack|components/05a9934d
Play Clientbound 0x6F minecraft:set_player_team ScoreboardCodecs.SetPlayerTeamV26_2 fault:ComponentFormatException shape:set_player_team(V26_2)
Play Clientbound 0x70 minecraft:set_score ScoreboardCodecs.SetScoreV1_21_5 read:5 shape:opaque
Play Clientbound 0x71 minecraft:set_simulation_distance WorldStateCodecs.SetSimulationDistanceV1_18 read:1 shape:opaque
@@ -180,7 +180,7 @@ Play Clientbound 0x81 minecraft:test_instance_block_status marker n/a shape:opaq
Play Clientbound 0x82 minecraft:ticking_state marker n/a shape:opaque
Play Clientbound 0x83 minecraft:ticking_step marker n/a shape:opaque
Play Clientbound 0x84 minecraft:transfer PlayCommonCodecs.Transfer read:2 shape:string,varint
-Play Clientbound 0x85 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_3 read:5 shape:update_advancements(icon=template/components/9cadf86a,text=nbt/modern,criteria=0,telemetry=1,showadv=1,positions=1,displaycoords=0)
+Play Clientbound 0x85 minecraft:update_advancements AdvancementCodecs.UpdateAdvancementsV26_3 read:5 shape:update_advancements(icon=template/components/05a9934d,text=nbt/modern,criteria=0,telemetry=1,showadv=1,positions=1,displaycoords=0)
Play Clientbound 0x86 minecraft:update_attributes EntityEquipmentCodecs.UpdateAttributesV1_21 read:2 shape:opaque
Play Clientbound 0x87 minecraft:update_mob_effect EntityEffectCodecs.UpdateMobEffectV1_20_5 read:5 shape:opaque
Play Clientbound 0x88 minecraft:update_recipes RecipeCodecs.UpdateRecipesModern read:256 shape:opaque
@@ -209,7 +209,7 @@ Play Serverbound 0x0E minecraft:client_information PlayClientInformationCodecs.V
Play Serverbound 0x0F minecraft:command_suggestion CommandSuggestionCodecs.ServerCommandSuggestionV1_13 read:2 shape:opaque
Play Serverbound 0x10 minecraft:configuration_acknowledged ConfigurationCodecs.ConfigurationAcknowledged read:0 shape:opaque
Play Serverbound 0x11 minecraft:container_button_click ContainerCodecs.ContainerButtonClickModern read:2 shape:opaque
-Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table777) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/9cadf86a
+Play Serverbound 0x12 minecraft:container_click MakeContainerClick(Table777) read:8 shape:varint,varint,short,byte,varint,varint*(short,hashed_stack),hashed_stack|components/05a9934d
Play Serverbound 0x13 minecraft:container_close ContainerCodecs.ContainerCloseServerModern read:1 shape:opaque
Play Serverbound 0x14 minecraft:container_slot_state_changed ContainerCodecs.ContainerSlotStateChangedModern read:3 shape:opaque
Play Serverbound 0x15 minecraft:cookie_response PlayCommonCodecs.CookieResponse fault:FormatException shape:identifier,opt_bytes
@@ -248,7 +248,7 @@ Play Serverbound 0x35 minecraft:set_beacon marker n/a shape:opaque
Play Serverbound 0x36 minecraft:set_carried_item EntityServerboundCodecs.SetCarriedItem read:2 shape:opaque
Play Serverbound 0x37 minecraft:set_command_block WorldBlockCodecs.SetCommandBlockV1_14 read:11 shape:opaque
Play Serverbound 0x38 minecraft:set_command_minecart marker n/a shape:opaque
-Play Serverbound 0x39 minecraft:set_creative_mode_slot MakeCreativeSlot(Table777) read:3 shape:short,delimited_stack|components/9cadf86a
+Play Serverbound 0x39 minecraft:set_creative_mode_slot MakeCreativeSlot(Table777) read:3 shape:short,delimited_stack|components/05a9934d
Play Serverbound 0x3A minecraft:set_game_rule marker n/a shape:opaque
Play Serverbound 0x3B minecraft:set_jigsaw_block marker n/a shape:opaque
Play Serverbound 0x3C minecraft:set_structure_block marker n/a shape:opaque
diff --git a/src/Umpk.Game/Items/Components/ComponentValueTypes.cs b/src/Umpk.Game/Items/Components/ComponentValueTypes.cs
index 3771f17..d22c6d5 100644
--- a/src/Umpk.Game/Items/Components/ComponentValueTypes.cs
+++ b/src/Umpk.Game/Items/Components/ComponentValueTypes.cs
@@ -186,6 +186,19 @@ public sealed record InstrumentDetails(
float Range,
Component? Description);
+/// The inline form of a painting variant (PaintingVariant.DIRECT_STREAM_CODEC).
+/// The painting width in blocks.
+/// The painting height in blocks.
+/// The painting texture asset identifier.
+/// The optional translated or literal title.
+/// The optional translated or literal author.
+public sealed record PaintingVariantDetails(
+ int Width,
+ int Height,
+ Identifier AssetId,
+ Component? Title,
+ Component? Author);
+
/// The inline (direct-holder) form of a jukebox song (JukeboxSong.DIRECT_STREAM_CODEC).
/// The song's sound event.
/// The song's description component.
diff --git a/src/Umpk.Game/Items/Components/Components.cs b/src/Umpk.Game/Items/Components/Components.cs
index 2d001a2..8269c84 100644
--- a/src/Umpk.Game/Items/Components/Components.cs
+++ b/src/Umpk.Game/Items/Components/Components.cs
@@ -348,6 +348,11 @@ public sealed record EquippableComponent(
/// The registry key identifier, for the 770-774 either-right branch.
public sealed record InstrumentComponent(int? HolderId, InstrumentDetails? Direct, Identifier? ReferenceKey = null);
+/// The painting variant applied by a painting item (minecraft:painting/variant, 1.21.5+).
+/// The painting-variant registry network id, for the reference branch.
+/// The inline painting variant, for the direct branch.
+public sealed record PaintingVariantComponent(int? HolderId, PaintingVariantDetails? Direct);
+
/// The music-disc song an item plays in a jukebox (minecraft:jukebox_playable, 1.21+).
/// The jukebox-song registry network id, for the reference branch.
/// The inline song, for the direct branch.
diff --git a/src/Umpk.Game/Items/DataComponents.cs b/src/Umpk.Game/Items/DataComponents.cs
index b1177ca..a7ae913 100644
--- a/src/Umpk.Game/Items/DataComponents.cs
+++ b/src/Umpk.Game/Items/DataComponents.cs
@@ -241,6 +241,9 @@ private static DataComponentType Make(string path)
/// minecraft:instrument.
public static DataComponentType Instrument { get; } = Make("instrument");
+ /// minecraft:painting/variant (1.21.5+).
+ public static DataComponentType PaintingVariant { get; } = Make("painting/variant");
+
/// minecraft:jukebox_playable (1.21+).
public static DataComponentType JukeboxPlayable { get; } = Make("jukebox_playable");
@@ -277,7 +280,7 @@ private static DataComponentType Make(string path)
WolfCollar, CatCollar, SheepColor, ShulkerColor, TropicalFishBaseColor,
TropicalFishPatternColor, Dye, MinimumAttackCharge, AdditionalTradeCost,
Bees, CanBreak, CanPlaceOn, Consumable, DamageResistant, DeathProtection, Enchantable,
- Equippable, Instrument, JukeboxPlayable, LodestoneTracker, Repairable, UseCooldown,
+ Equippable, Instrument, PaintingVariant, JukeboxPlayable, LodestoneTracker, Repairable, UseCooldown,
LegacyNbt,
];
}
diff --git a/src/Umpk.Game/PublicAPI.Unshipped.txt b/src/Umpk.Game/PublicAPI.Unshipped.txt
index 9fb61db..a0f11b1 100644
--- a/src/Umpk.Game/PublicAPI.Unshipped.txt
+++ b/src/Umpk.Game/PublicAPI.Unshipped.txt
@@ -2988,6 +2988,41 @@ static Umpk.Game.Items.DataComponents.Unbreakable.get -> Umpk.Game.Items.DataCom
static Umpk.Game.Items.DataComponents.Unmodeled(Umpk.Identifier id) -> Umpk.Game.Items.DataComponentType!
static Umpk.Game.Items.DataComponents.UseCooldown.get -> Umpk.Game.Items.DataComponentType!
static Umpk.Game.Items.DataComponents.UseRemainder.get -> Umpk.Game.Items.DataComponentType!
+Umpk.Game.Items.Components.PaintingVariantComponent
+Umpk.Game.Items.Components.PaintingVariantComponent.$() -> Umpk.Game.Items.Components.PaintingVariantComponent!
+Umpk.Game.Items.Components.PaintingVariantComponent.Deconstruct(out int? HolderId, out Umpk.Game.Items.Components.PaintingVariantDetails? Direct) -> void
+Umpk.Game.Items.Components.PaintingVariantComponent.Direct.get -> Umpk.Game.Items.Components.PaintingVariantDetails?
+Umpk.Game.Items.Components.PaintingVariantComponent.Direct.init -> void
+Umpk.Game.Items.Components.PaintingVariantComponent.Equals(Umpk.Game.Items.Components.PaintingVariantComponent? other) -> bool
+Umpk.Game.Items.Components.PaintingVariantComponent.HolderId.get -> int?
+Umpk.Game.Items.Components.PaintingVariantComponent.HolderId.init -> void
+Umpk.Game.Items.Components.PaintingVariantComponent.PaintingVariantComponent(int? HolderId, Umpk.Game.Items.Components.PaintingVariantDetails? Direct) -> void
+Umpk.Game.Items.Components.PaintingVariantDetails
+Umpk.Game.Items.Components.PaintingVariantDetails.$() -> Umpk.Game.Items.Components.PaintingVariantDetails!
+Umpk.Game.Items.Components.PaintingVariantDetails.AssetId.get -> Umpk.Identifier
+Umpk.Game.Items.Components.PaintingVariantDetails.AssetId.init -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.Author.get -> Umpk.Text.Component?
+Umpk.Game.Items.Components.PaintingVariantDetails.Author.init -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.Deconstruct(out int Width, out int Height, out Umpk.Identifier AssetId, out Umpk.Text.Component? Title, out Umpk.Text.Component? Author) -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.Equals(Umpk.Game.Items.Components.PaintingVariantDetails? other) -> bool
+Umpk.Game.Items.Components.PaintingVariantDetails.Height.get -> int
+Umpk.Game.Items.Components.PaintingVariantDetails.Height.init -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.PaintingVariantDetails(int Width, int Height, Umpk.Identifier AssetId, Umpk.Text.Component? Title, Umpk.Text.Component? Author) -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.Title.get -> Umpk.Text.Component?
+Umpk.Game.Items.Components.PaintingVariantDetails.Title.init -> void
+Umpk.Game.Items.Components.PaintingVariantDetails.Width.get -> int
+Umpk.Game.Items.Components.PaintingVariantDetails.Width.init -> void
+override Umpk.Game.Items.Components.PaintingVariantComponent.Equals(object? obj) -> bool
+override Umpk.Game.Items.Components.PaintingVariantComponent.GetHashCode() -> int
+override Umpk.Game.Items.Components.PaintingVariantComponent.ToString() -> string!
+override Umpk.Game.Items.Components.PaintingVariantDetails.Equals(object? obj) -> bool
+override Umpk.Game.Items.Components.PaintingVariantDetails.GetHashCode() -> int
+override Umpk.Game.Items.Components.PaintingVariantDetails.ToString() -> string!
+static Umpk.Game.Items.Components.PaintingVariantComponent.operator !=(Umpk.Game.Items.Components.PaintingVariantComponent? left, Umpk.Game.Items.Components.PaintingVariantComponent? right) -> bool
+static Umpk.Game.Items.Components.PaintingVariantComponent.operator ==(Umpk.Game.Items.Components.PaintingVariantComponent? left, Umpk.Game.Items.Components.PaintingVariantComponent? right) -> bool
+static Umpk.Game.Items.Components.PaintingVariantDetails.operator !=(Umpk.Game.Items.Components.PaintingVariantDetails? left, Umpk.Game.Items.Components.PaintingVariantDetails? right) -> bool
+static Umpk.Game.Items.Components.PaintingVariantDetails.operator ==(Umpk.Game.Items.Components.PaintingVariantDetails? left, Umpk.Game.Items.Components.PaintingVariantDetails? right) -> bool
+static Umpk.Game.Items.DataComponents.PaintingVariant.get -> Umpk.Game.Items.DataComponentType!
static Umpk.Game.Items.DataComponents.WolfCollar.get -> Umpk.Game.Items.DataComponentType!
static Umpk.Game.Items.DataComponents.WritableBookContent.get -> Umpk.Game.Items.DataComponentType!
static Umpk.Game.Items.DataComponents.WrittenBookContent.get -> Umpk.Game.Items.DataComponentType!
diff --git a/src/Umpk.Protocol.Java/Wire/Components/ItemComponentCodecs.LateComponents.cs b/src/Umpk.Protocol.Java/Wire/Components/ItemComponentCodecs.LateComponents.cs
index a25c646..2e9f1da 100644
--- a/src/Umpk.Protocol.Java/Wire/Components/ItemComponentCodecs.LateComponents.cs
+++ b/src/Umpk.Protocol.Java/Wire/Components/ItemComponentCodecs.LateComponents.cs
@@ -74,6 +74,9 @@ internal static partial class ItemComponentCodecs
public static ItemComponentCodec InstrumentV26_1 { get; } =
new InstrumentCodecImpl(v1_21_2Form: true, eitherWrapped: false, ComponentWireEra.Modern);
+ /// minecraft:painting/variant from protocol 770: a registry holder whose inline branch is width, height, asset id, optional title, and optional author.
+ public static ItemComponentCodec PaintingVariant { get; } = new PaintingVariantCodecImpl();
+
/// minecraft:jukebox_playable on 767-769: a leading boolean chooses a holder or bare registry-key identifier, followed by a showInTooltip boolean. The component does not exist on 766.
public static ItemComponentCodec JukeboxPlayableV1_21 { get; } =
new JukeboxPlayableCodecImpl(eitherWrapped: true, hasShowInTooltip: true, ComponentWireEra.Legacy);
@@ -713,6 +716,72 @@ public override int Hash(in HashOps ops, object value, PacketCodecContext contex
}
}
+ /// minecraft:painting/variant; stable from protocol 770 onward.
+ private sealed class PaintingVariantCodecImpl() : ItemComponentCodec(DataComponents.PaintingVariant)
+ {
+ public override object Decode(ref PacketReader reader, PacketCodecContext context)
+ {
+ int marker = reader.ReadVarInt();
+ if (marker != 0)
+ return new PaintingVariantComponent(marker - 1, null);
+
+ int width = reader.ReadVarInt();
+ int height = reader.ReadVarInt();
+ Identifier assetId = Identifier.Parse(reader.ReadString());
+ Component? title = reader.ReadOptional(static (ref PacketReader r) =>
+ ItemCodecPrimitives.ReadNetworkComponent(ref r, ComponentWireEra.Modern));
+ Component? author = reader.ReadOptional(static (ref PacketReader r) =>
+ ItemCodecPrimitives.ReadNetworkComponent(ref r, ComponentWireEra.Modern));
+ return new PaintingVariantComponent(
+ null,
+ new PaintingVariantDetails(width, height, assetId, title, author));
+ }
+
+ public override void Encode(ref PacketWriter writer, object value, PacketCodecContext context)
+ {
+ var painting = (PaintingVariantComponent)value;
+ if (painting.HolderId is { } id)
+ {
+ writer.WriteVarInt(id + 1);
+ return;
+ }
+
+ PaintingVariantDetails direct = painting.Direct
+ ?? throw new ProtocolViolationException("A painting-variant component carries neither a holder id nor an inline variant.");
+
+ writer.WriteVarInt(0);
+ writer.WriteVarInt(direct.Width);
+ writer.WriteVarInt(direct.Height);
+ writer.WriteString(direct.AssetId.ToString());
+ writer.WriteOptional(direct.Title, static (ref PacketWriter w, Component component) =>
+ ItemCodecPrimitives.WriteNetworkComponent(ref w, component, ComponentWireEra.Modern));
+ writer.WriteOptional(direct.Author, static (ref PacketWriter w, Component component) =>
+ ItemCodecPrimitives.WriteNetworkComponent(ref w, component, ComponentWireEra.Modern));
+ }
+
+ public override int Hash(in HashOps ops, object value, PacketCodecContext context)
+ {
+ var painting = (PaintingVariantComponent)value;
+ if (painting.HolderId is { } id)
+ return ops.Int(id);
+
+ PaintingVariantDetails direct = painting.Direct!;
+ var entries = new List<(int, int)>(5)
+ {
+ (ops.String("width"), ops.Int(direct.Width)),
+ (ops.String("height"), ops.Int(direct.Height)),
+ (ops.String("asset_id"), ops.String(direct.AssetId.ToString())),
+ };
+ if (direct.Title is { } title)
+ entries.Add((ops.String("title"), ItemCodecPrimitives.HashNbt(ops, ComponentNbt.To(title, ComponentWireEra.Modern))));
+
+ if (direct.Author is { } author)
+ entries.Add((ops.String("author"), ItemCodecPrimitives.HashNbt(ops, ComponentNbt.To(author, ComponentWireEra.Modern))));
+
+ return ops.Map(entries);
+ }
+ }
+
/// minecraft:jukebox_playable from 767; three era shapes, see the bound properties.
private sealed class JukeboxPlayableCodecImpl(bool eitherWrapped, bool hasShowInTooltip, ComponentWireEra era)
: ItemComponentCodec(DataComponents.JukeboxPlayable)
diff --git a/src/Umpk.Protocol.Java/Wire/Components/ItemComponentTable.cs b/src/Umpk.Protocol.Java/Wire/Components/ItemComponentTable.cs
index 8779103..1a57eda 100644
--- a/src/Umpk.Protocol.Java/Wire/Components/ItemComponentTable.cs
+++ b/src/Umpk.Protocol.Java/Wire/Components/ItemComponentTable.cs
@@ -246,6 +246,7 @@ private static ItemComponentTable BuildModern(ItemComponentLayout layout)
ItemComponentCodecs.ItemModel,
ItemComponentCodecs.TooltipStyle,
ItemComponentCodecs.NoteBlockSound,
+ ItemComponentCodecs.PaintingVariant,
ItemComponentCodecs.BaseColor,
ItemComponentCodecs.TooltipDisplay,
ItemComponentCodecs.CustomModelData,
diff --git a/tests/Umpk.IntegrationTests/PaintingVariantLiveTests.cs b/tests/Umpk.IntegrationTests/PaintingVariantLiveTests.cs
new file mode 100644
index 0000000..b17ccea
--- /dev/null
+++ b/tests/Umpk.IntegrationTests/PaintingVariantLiveTests.cs
@@ -0,0 +1,185 @@
+using Umpk;
+using Umpk.Client;
+using Umpk.Data.Java;
+using Umpk.Game.Items;
+using Umpk.Game.Items.Components;
+using Umpk.Protocol.Java;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Umpk.IntegrationTests;
+
+/// Against real vanilla servers: a painting with minecraft:painting/variant must survive both the live slot update and the reconnect-time full inventory packet.
+[Collection(LiveServerCollection.Name)]
+public sealed class PaintingVariantLiveTests
+{
+ private const string Username = "UmpkPaintingTest";
+
+ private static readonly Guid ProfileId = Guid.Parse("b2f324ac-106f-4079-b729-a4d7085545c1");
+
+ private readonly ITestOutputHelper _out;
+
+ public PaintingVariantLiveTests(ITestOutputHelper output) => _out = output;
+
+ public static TheoryData Versions => new()
+ {
+ { "1.21.5", 770 },
+ { "1.21.6", 771 },
+ { "1.21.8", 772 },
+ { "1.21.10", 773 },
+ { "1.21.11", 774 },
+ { "26.1", 775 },
+ { "26.2", 776 },
+ { "26.3", 777 },
+ };
+
+ [NightlyTheory]
+ [MemberData(nameof(Versions))]
+ public async Task PaintingVariant_SurvivesSlotUpdateAndReconnectInventory(string versionName, int protocol)
+ {
+ string serverDir = IntegrationConfig.ServerDir(versionName);
+ Assert.True(
+ Directory.Exists(serverDir),
+ $"Server directory for {versionName} not provisioned ({serverDir}). Set UMPK_SERVER_ROOT.");
+ Assert.True(
+ JavaVersions.TryGetByProtocol(protocol, out JavaVersion? version) && version is not null,
+ $"No JavaVersion for protocol {protocol}.");
+
+ using var ct = new CancellationTokenSource(TimeSpan.FromSeconds(300));
+ await using LocalServer server = await LocalServer.StartAsync(
+ serverDir, JavaRuntimes.ForProtocol(protocol), TimeSpan.FromSeconds(180), ct.Token);
+
+ var logs = new CapturingLoggerFactory();
+ UmpkClient seededClient = await ConnectAsync(version!, protocol, logs, ct.Token);
+ await using (seededClient)
+ {
+ await WaitForSpawnAsync(seededClient, versionName, ct.Token);
+ await server.SendConsoleAsync($"gamemode creative {Username}", ct.Token);
+
+ await server.SendConsoleAsync($"give {Username} minecraft:stone 1", ct.Token);
+ Assert.True(
+ await WaitUntilAsync(() => AnySlotFilled(seededClient), TimeSpan.FromSeconds(15), ct.Token),
+ $"{versionName}: the control item never reached the inventory.");
+
+ await server.SendConsoleAsync($"clear {Username}", ct.Token);
+ Assert.True(
+ await WaitUntilAsync(() => !AnySlotFilled(seededClient), TimeSpan.FromSeconds(15), ct.Token),
+ $"{versionName}: the control item was not cleared.");
+
+ await server.SendConsoleAsync(
+ $"give {Username} minecraft:painting[minecraft:painting/variant=alban] 1", ct.Token);
+ Assert.True(
+ await WaitUntilAsync(
+ () => FindPaintingVariant(seededClient) is not null,
+ TimeSpan.FromSeconds(20),
+ ct.Token),
+ $"{versionName}: the painting variant did not decode from the live slot update.");
+ Assert.Equal(ClientStatus.Playing, seededClient.Status);
+ await seededClient.DisconnectAsync(CancellationToken.None);
+ }
+
+ UmpkClient reconnectedClient = await ConnectAsync(version!, protocol, logs, ct.Token);
+ await using (reconnectedClient)
+ {
+ await WaitForSpawnAsync(reconnectedClient, versionName, ct.Token);
+ PaintingVariantComponent? variant = null;
+ Assert.True(
+ await WaitUntilAsync(
+ () => (variant = FindPaintingVariant(reconnectedClient)) is not null,
+ TimeSpan.FromSeconds(20),
+ ct.Token),
+ $"{versionName}: the painting variant did not decode from reconnect inventory content.");
+ Assert.Equal(ClientStatus.Playing, reconnectedClient.Status);
+ Assert.NotNull(variant!.HolderId);
+ Assert.Null(variant.Direct);
+ _out.WriteLine(
+ $"[{versionName}/{protocol}] painting holder={variant.HolderId}, status={reconnectedClient.Status}");
+
+ await Task.Delay(TimeSpan.FromSeconds(2), ct.Token);
+ Assert.Equal(ClientStatus.Playing, reconnectedClient.Status);
+ }
+ }
+
+ private static async Task WaitForSpawnAsync(
+ UmpkClient client, string versionName, CancellationToken ct)
+ {
+ Assert.Equal(ClientStatus.Playing, client.Status);
+ Assert.True(
+ await WaitUntilAsync(() => client.State.Self.HasSpawned, TimeSpan.FromSeconds(30), ct),
+ $"{versionName}: never reached spawn.");
+ }
+
+ private static PaintingVariantComponent? FindPaintingVariant(UmpkClient client)
+ {
+ if (!client.State.Features.Inventory)
+ return null;
+
+ foreach (ItemStack slot in client.State.Inventory.PlayerSlots)
+ if (!slot.IsEmpty &&
+ slot.Components.TryGet(
+ DataComponents.PaintingVariant,
+ out PaintingVariantComponent? variant))
+ return variant;
+
+ return null;
+ }
+
+ private static bool AnySlotFilled(UmpkClient client)
+ {
+ if (!client.State.Features.Inventory)
+ return false;
+
+ foreach (ItemStack slot in client.State.Inventory.PlayerSlots)
+ if (!slot.IsEmpty)
+ return true;
+
+ return false;
+ }
+
+ private static async Task ConnectAsync(
+ JavaVersion version, int protocol, CapturingLoggerFactory logs, CancellationToken ct)
+ {
+ var endpoint = new ServerEndpoint("127.0.0.1", (ushort)LocalServer.Port);
+ Exception? last = null;
+ for (int attempt = 0; attempt < 25; attempt++)
+ {
+ ct.ThrowIfCancellationRequested();
+ UmpkClient client = new UmpkClientBuilder()
+ .UseVersion(version)
+ .UseProfile(new GameProfile(ProfileId, Username))
+ .UseStaticRegistries(JavaGameData.Registries(protocol))
+ .UseLoggerFactory(logs)
+ .ConfigureFeatures(f => { f.Physics = false; f.Pathfinding = false; })
+ .ConfigureOptions(o => o.AutoSendPosition = false)
+ .Build();
+
+ try
+ {
+ await client.ConnectAsync(endpoint, ct).ConfigureAwait(false);
+ return client;
+ }
+ catch (Exception ex)
+ {
+ last = ex;
+ await client.DisposeAsync().ConfigureAwait(false);
+ await Task.Delay(1500, ct).ConfigureAwait(false);
+ }
+ }
+
+ throw new InvalidOperationException("Could not connect after retries.", last);
+ }
+
+ private static async Task WaitUntilAsync(Func condition, TimeSpan timeout, CancellationToken ct)
+ {
+ DateTime deadline = DateTime.UtcNow + timeout;
+ while (DateTime.UtcNow < deadline)
+ {
+ if (condition())
+ return true;
+
+ await Task.Delay(250, ct).ConfigureAwait(false);
+ }
+
+ return condition();
+ }
+}
diff --git a/tests/Umpk.Protocol.Java.Tests/Item/EnchantmentAndFireworkComponentTests.cs b/tests/Umpk.Protocol.Java.Tests/Item/EnchantmentAndFireworkComponentTests.cs
index df4de15..50d1817 100644
--- a/tests/Umpk.Protocol.Java.Tests/Item/EnchantmentAndFireworkComponentTests.cs
+++ b/tests/Umpk.Protocol.Java.Tests/Item/EnchantmentAndFireworkComponentTests.cs
@@ -580,7 +580,7 @@ public void AStillUnmodeledComponent_StillRaisesThePacketScopedFault()
/// How many components each era DECLARES versus how many UMPK types. Every untyped one is a packet-dropper the moment it appears on a compact patch, so this number is the real blast radius and it belongs in the suite rather than in a report that goes stale. Measured by walking each table's wire ids until and counting the ids with no codec.
///
/// Typing enchantments, stored_enchantments, fireworks and firework_explosion moved these from 18/19/25/25/40/40/40/47/51/51. Typing pot_decorations on BuildPreV1_21_5 then moved 766/767/768/769 down by one more each (14/15/21/21 -> 13/14/20/20).
- /// The current numbers come from typing the fifteen components that had no codec on ANY protocol (banner_patterns, bees, can_break, can_place_on, consumable, damage_resistant, death_protection, enchantable, equippable, instrument, jukebox_playable, lodestone_tracker, repairable, suspicious_stew_effects, use_cooldown), moving the untyped counts 13/14/20/20/38/38/38/45/49/49 to 6/6/5/5/25/25/25/32/36/36 for 766/767/768/769/770/771/773/774/775/776. The per-era deltas differ because the eras declare different subsets: 766 gains 7 (banner_patterns, bees, can_break, can_place_on, instrument, lodestone_tracker, suspicious_stew_effects), 767 gains those 8 with jukebox_playable, 768/769 gain all 15, and 770-776 gain 13 - everything except can_break and can_place_on, which stay untyped there because 1.21.5 appended DataComponentMatchers to BlockPredicate (see ItemComponentCodecs.AdventureModePredicateCodecImpl's remarks).
+ /// Typing the fifteen components that previously had no codec on any protocol moved the untyped counts to 6/6/5/5/25/25/25/32/36/36 for 766/767/768/769/770/771/773/774/775/776. Typing painting/variant then moved the 770+ counts down once more to 24/24/24/24/31/35/35/47 for 770/771/772/773/774/775/776/777. It remains absent before 770, matching vanilla's introduction boundary.
/// What is still untyped is now dominated by 1.21.5+ additions no normal session needs to decode (weapon, blocks_attacks, the mob */variant family, break_sound), plus can_break/can_place_on on 770+, and on 766-769 the six components whose pre-1.21.5 wire shape genuinely differs (attribute_modifiers, dyed_color, potion_contents, trim, tool, and on 766/767 food). A drop in a number here without a corresponding new codec means the table lost entries, which is the opposite of progress.
///
[Theory]
@@ -588,12 +588,14 @@ public void AStillUnmodeledComponent_StillRaisesThePacketScopedFault()
[InlineData(767, 57, 6)]
[InlineData(768, 67, 5)]
[InlineData(769, 67, 5)]
- [InlineData(770, 96, 25)]
- [InlineData(771, 96, 25)]
- [InlineData(773, 96, 25)]
- [InlineData(774, 104, 32)]
- [InlineData(775, 110, 36)]
- [InlineData(776, 111, 36)]
+ [InlineData(770, 96, 24)]
+ [InlineData(771, 96, 24)]
+ [InlineData(772, 96, 24)]
+ [InlineData(773, 96, 24)]
+ [InlineData(774, 104, 31)]
+ [InlineData(775, 110, 35)]
+ [InlineData(776, 111, 35)]
+ [InlineData(777, 122, 47)]
public void WireLayoutTable_DeclaresAndTypesTheseManyComponents(int protocol, int declared, int untyped)
{
ItemComponentTable table = TableFor(protocol);
@@ -629,10 +631,12 @@ public void WireLayoutTable_DeclaresAndTypesTheseManyComponents(int protocol, in
769 => ItemComponentTable.V1_21_4(),
770 => ItemComponentTable.V1_21_5(),
771 => ItemComponentTable.V1_21_6(),
+ 772 => ItemComponentTable.V1_21_6(),
773 => ItemComponentTable.V1_21_9(),
774 => ItemComponentTable.V1_21_11(),
775 => ItemComponentTable.V26_1(),
- _ => ItemComponentTable.V26_2(),
+ 776 => ItemComponentTable.V26_2(),
+ _ => ItemComponentTable.V26_3(),
};
// Helpers.
diff --git a/tests/Umpk.Protocol.Java.Tests/Item/PaintingVariantComponentTests.cs b/tests/Umpk.Protocol.Java.Tests/Item/PaintingVariantComponentTests.cs
new file mode 100644
index 0000000..e115f44
--- /dev/null
+++ b/tests/Umpk.Protocol.Java.Tests/Item/PaintingVariantComponentTests.cs
@@ -0,0 +1,167 @@
+using System.Buffers;
+using Umpk.Game.Items;
+using Umpk.Game.Items.Components;
+using Umpk.Nbt;
+using Umpk.Protocol.Java.Codecs;
+using Umpk.Protocol.Java.Packets;
+using Umpk.Protocol.Java.Tests.Support;
+using Umpk.Text;
+using Umpk.Text.Serialization;
+using Xunit;
+
+namespace Umpk.Protocol.Java.Tests.Item;
+
+/// Regression coverage for minecraft:painting/variant, whose unmodeled compact payload dropped the containing inventory or entity-metadata packet.
+public class PaintingVariantComponentTests
+{
+ private const string SetContent = "minecraft:container_set_content";
+ private const string SetSlot = "minecraft:container_set_slot";
+
+ /// The component appears from protocol 770 onward. Its registry holder wire form is stable while its component-table id moves.
+ public static TheoryData WireIds => new()
+ {
+ { 770, 89 }, { 771, 89 }, { 772, 89 }, { 773, 89 },
+ { 774, 97 }, { 775, 102 }, { 776, 103 }, { 777, 109 },
+ };
+
+ [Theory]
+ [InlineData(766)]
+ [InlineData(767)]
+ [InlineData(768)]
+ [InlineData(769)]
+ public void Component_IsAbsentBeforeItsVanillaIntroduction(int protocol)
+ {
+ ItemComponentTable table = protocol switch
+ {
+ 766 => ItemComponentTable.V1_20_5(),
+ 767 => ItemComponentTable.V1_21(),
+ 768 => ItemComponentTable.V1_21_2(),
+ _ => ItemComponentTable.V1_21_4(),
+ };
+
+ Assert.False(table.Contains(DataComponents.PaintingVariant));
+ }
+
+ [Theory]
+ [MemberData(nameof(WireIds))]
+ public void ReferenceHolder_DecodesOnEverySupportedEra(int protocol, int wireId)
+ {
+ byte[] frame = SetSlotFrame(wireId, static (ref PacketWriter writer) => writer.WriteVarInt(4));
+ BoundPacketCodec bound = BoundCodec.At(protocol, PacketFlow.Clientbound, SetSlot);
+
+ var packet = Assert.IsType(bound.DecodeFrame(frame));
+
+ Assert.True(packet.Item.Components.TryGet(DataComponents.PaintingVariant, out PaintingVariantComponent? variant));
+ Assert.Equal(3, variant!.HolderId);
+ Assert.Null(variant.Direct);
+ Assert.Equal(frame, bound.Encode(packet));
+ }
+
+ [Theory]
+ [InlineData(770, 89)]
+ [InlineData(777, 109)]
+ public void InlineHolder_RoundTripsEveryField(int protocol, int wireId)
+ {
+ byte[] frame = SetSlotFrame(wireId, static (ref PacketWriter writer) =>
+ {
+ writer.WriteVarInt(0);
+ writer.WriteVarInt(2);
+ writer.WriteVarInt(3);
+ writer.WriteString("example:wide_landscape");
+ writer.WriteBool(true);
+ writer.WriteComponent(Component.Text("Wide Landscape"), ComponentWireEra.Modern, NbtWireFormat.JavaUnnamedRoot);
+ writer.WriteBool(true);
+ writer.WriteComponent(Component.Text("Example Artist"), ComponentWireEra.Modern, NbtWireFormat.JavaUnnamedRoot);
+ });
+ BoundPacketCodec bound = BoundCodec.At(protocol, PacketFlow.Clientbound, SetSlot);
+
+ var packet = Assert.IsType(bound.DecodeFrame(frame));
+
+ Assert.True(packet.Item.Components.TryGet(DataComponents.PaintingVariant, out PaintingVariantComponent? variant));
+ PaintingVariantDetails direct = Assert.IsType(variant!.Direct);
+ Assert.Equal(2, direct.Width);
+ Assert.Equal(3, direct.Height);
+ Assert.Equal(new Identifier("example", "wide_landscape"), direct.AssetId);
+ Assert.Equal(Component.Text("Wide Landscape"), direct.Title);
+ Assert.Equal(Component.Text("Example Artist"), direct.Author);
+ Assert.Equal(frame, bound.Encode(packet));
+ }
+
+ [Fact]
+ public void ContainerSetContent_777_PaintingDoesNotDiscardFollowingSlots()
+ {
+ var buffer = new ArrayBufferWriter();
+ var writer = new PacketWriter(buffer);
+ writer.WriteVarInt(0);
+ writer.WriteVarInt(1);
+ writer.WriteVarInt(2);
+ WriteStack(ref writer, ItemTestRegistries.Stone, componentWireId: 109, static (ref PacketWriter payload) => payload.WriteVarInt(4));
+ WriteStack(ref writer, ItemTestRegistries.DiamondSword, componentWireId: null, null);
+ writer.WriteVarInt(0);
+ byte[] frame = buffer.WrittenSpan.ToArray();
+ BoundPacketCodec bound = BoundCodec.At(777, PacketFlow.Clientbound, SetContent);
+
+ var packet = Assert.IsType(bound.DecodeFrame(frame));
+
+ Assert.Equal(2, packet.Items.Count);
+ Assert.True(packet.Items[0].Components.Has(DataComponents.PaintingVariant));
+ Assert.Equal(ItemTestRegistries.DiamondSword, packet.Items[1].Item.NetworkId);
+ Assert.Equal(frame, bound.Encode(packet));
+ }
+
+ [Fact]
+ public void InlineHolder_HashUsesTheVanillaDataShape()
+ {
+ ItemComponentCodec codec = ItemPacketCodecShared.Table777.ByKey(DataComponents.PaintingVariant);
+ var value = new PaintingVariantComponent(
+ null,
+ new PaintingVariantDetails(
+ 2,
+ 3,
+ new Identifier("example", "wide_landscape"),
+ Component.Text("Wide Landscape"),
+ null));
+ var ops = new HashOps(forceSoftwareCrc: true);
+ int expected = ops.Map(
+ [
+ (ops.String("width"), ops.Int(2)),
+ (ops.String("height"), ops.Int(3)),
+ (ops.String("asset_id"), ops.String("example:wide_landscape")),
+ (ops.String("title"), ItemCodecPrimitives.HashNbt(
+ ops,
+ ComponentNbt.To(Component.Text("Wide Landscape"), ComponentWireEra.Modern))),
+ ]);
+
+ Assert.Equal(expected, codec.Hash(ops, value, ItemTestRegistries.Context));
+ }
+
+ private delegate void PayloadWriter(ref PacketWriter writer);
+
+ private static byte[] SetSlotFrame(int componentWireId, PayloadWriter write)
+ {
+ var buffer = new ArrayBufferWriter();
+ var writer = new PacketWriter(buffer);
+ writer.WriteVarInt(0);
+ writer.WriteVarInt(1);
+ writer.WriteShort(0);
+ WriteStack(ref writer, ItemTestRegistries.Stone, componentWireId, write);
+ return buffer.WrittenSpan.ToArray();
+ }
+
+ private static void WriteStack(
+ ref PacketWriter writer,
+ int itemId,
+ int? componentWireId,
+ PayloadWriter? write)
+ {
+ writer.WriteVarInt(1);
+ writer.WriteVarInt(itemId);
+ writer.WriteVarInt(componentWireId.HasValue ? 1 : 0);
+ writer.WriteVarInt(0);
+ if (componentWireId is not { } id)
+ return;
+
+ writer.WriteVarInt(id);
+ write!(ref writer);
+ }
+}