feat: add MC 1.21.5 (protocol 770) support

Full protocol adaptation for Minecraft 1.21.5:

- Protocol version mapping: 770 -> 1.21.5
- Packet palette: AddExperienceOrb removed (S2C), TestInstanceBlockStatus
  added (S2C), SetTestBlock and TestInstanceBlockAction added (C2S)
- Entity metadata palette: 5 new serializer types (OptionalLivingEntityReference,
  CowVariant, WolfSoundVariant, PigVariant, ChickenVariant), OPTIONAL_UUID
  replaced by OPTIONAL_LIVING_ENTITY_REFERENCE
- Item palette: 11 new items (Bush, FireflyBush, DryShortGrass, DryTallGrass,
  Wildflowers, LeafLitter, CactusFlower, TestBlock, TestInstanceBlock,
  BlueEgg, BrownEgg)
- Entity palette: Potion split into SplashPotion and LingeringPotion
- Block palette: 9 new blocks (bush, cactus_flower, firefly_bush, leaf_litter,
  short_dry_grass, tall_dry_grass, test_block, test_instance_block, wildflowers)
- Structured components: 31 new components including tooltip_display, weapon,
  blocks_attacks, potion_duration_scale, provides_trim_material,
  provides_banner_patterns, break_sound, and 25 entity variant components;
  unbreakable changed from Bool to Unit; instrument changed to EitherHolder

Made-with: Cursor
This commit is contained in:
BruceChen 2026-03-21 00:48:27 +08:00
parent 9c1502600a
commit 8df39ba74a
33 changed files with 4445 additions and 13 deletions

View file

@ -826,6 +826,7 @@ namespace MinecraftClient.Protocol.Handlers
value = ReadNextVarInt(cache);
break;
case EntityMetaDataType.OptionalUuid: // Optional UUID
case EntityMetaDataType.OptionalLivingEntityReference: // Optional Living Entity Reference (1.21.5+, same wire format)
if (ReadNextBool(cache))
{
value = ReadNextUUID(cache);
@ -873,10 +874,14 @@ namespace MinecraftClient.Protocol.Handlers
case EntityMetaDataType.CatVariant: // Cat Variant
value = ReadNextVarInt(cache);
break;
case EntityMetaDataType.CowVariant: // Cow Variant (1.21.5+)
case EntityMetaDataType.WolfVariant: // Wolf Variant (1.20.6+)
case EntityMetaDataType.WolfSoundVariant: // Wolf Sound Variant (1.21.5+)
value = ReadNextVarInt(cache);
break;
case EntityMetaDataType.FrogVariant: // Frog Variant
case EntityMetaDataType.PigVariant: // Pig Variant (1.21.5+)
case EntityMetaDataType.ChickenVariant: // Chicken Variant (1.21.5+)
value = ReadNextVarInt(cache);
break;
case EntityMetaDataType.GlobalPosition: // GlobalPos

View file

@ -0,0 +1,247 @@
using System.Collections.Generic;
namespace MinecraftClient.Protocol.Handlers.PacketPalettes;
public class PacketPalette1215 : PacketTypePalette
{
private readonly Dictionary<int, PacketTypesIn> typeIn = new()
{
{ 0x00, PacketTypesIn.Bundle }, // Bundle delimiter
{ 0x01, PacketTypesIn.SpawnEntity }, // Add Entity
{ 0x02, PacketTypesIn.EntityAnimation }, // Animate (was 0x03 in 1.21.4; AddExperienceOrb removed)
{ 0x03, PacketTypesIn.Statistics }, // Award Stats
{ 0x04, PacketTypesIn.BlockChangedAck }, // Block Changed Ack
{ 0x05, PacketTypesIn.BlockBreakAnimation }, // Block Destruction
{ 0x06, PacketTypesIn.BlockEntityData }, // Block Entity Data
{ 0x07, PacketTypesIn.BlockAction }, // Block Event
{ 0x08, PacketTypesIn.BlockChange }, // Block Update
{ 0x09, PacketTypesIn.BossBar }, // Boss Event
{ 0x0A, PacketTypesIn.ServerDifficulty }, // Change Difficulty
{ 0x0B, PacketTypesIn.ChunkBatchFinished }, // Chunk Batch Finished
{ 0x0C, PacketTypesIn.ChunkBatchStarted }, // Chunk Batch Start
{ 0x0D, PacketTypesIn.ChunksBiomes }, // Chunks Biomes
{ 0x0E, PacketTypesIn.ClearTiles }, // Clear Titles
{ 0x0F, PacketTypesIn.TabComplete }, // Command Suggestions
{ 0x10, PacketTypesIn.DeclareCommands }, // Commands
{ 0x11, PacketTypesIn.CloseWindow }, // Container Close
{ 0x12, PacketTypesIn.WindowItems }, // Container Set Content
{ 0x13, PacketTypesIn.WindowProperty }, // Container Set Data
{ 0x14, PacketTypesIn.SetSlot }, // Container Set Slot
{ 0x15, PacketTypesIn.CookieRequest }, // Cookie Request
{ 0x16, PacketTypesIn.SetCooldown }, // Cooldown
{ 0x17, PacketTypesIn.ChatSuggestions }, // Custom Chat Completions
{ 0x18, PacketTypesIn.PluginMessage }, // Custom Payload
{ 0x19, PacketTypesIn.DamageEvent }, // Damage Event
{ 0x1A, PacketTypesIn.DebugSample }, // Debug Sample
{ 0x1B, PacketTypesIn.HideMessage }, // Delete Chat
{ 0x1C, PacketTypesIn.Disconnect }, // Disconnect
{ 0x1D, PacketTypesIn.ProfilelessChatMessage }, // Disguised Chat
{ 0x1E, PacketTypesIn.EntityStatus }, // Entity Event
{ 0x1F, PacketTypesIn.EntityPositionSync }, // Entity Position Sync
{ 0x20, PacketTypesIn.Explosion }, // Explode
{ 0x21, PacketTypesIn.UnloadChunk }, // Forget Level Chunk
{ 0x22, PacketTypesIn.ChangeGameState }, // Game Event
{ 0x23, PacketTypesIn.OpenHorseWindow }, // Horse Screen Open
{ 0x24, PacketTypesIn.HurtAnimation }, // Hurt Animation
{ 0x25, PacketTypesIn.InitializeWorldBorder }, // Initialize Border
{ 0x26, PacketTypesIn.KeepAlive }, // Keep Alive
{ 0x27, PacketTypesIn.ChunkData }, // Level Chunk With Light
{ 0x28, PacketTypesIn.Effect }, // Level Event
{ 0x29, PacketTypesIn.Particle }, // Level Particles
{ 0x2A, PacketTypesIn.UpdateLight }, // Light Update
{ 0x2B, PacketTypesIn.JoinGame }, // Login
{ 0x2C, PacketTypesIn.MapData }, // Map Item Data
{ 0x2D, PacketTypesIn.TradeList }, // Merchant Offers
{ 0x2E, PacketTypesIn.EntityPosition }, // Move Entity Pos
{ 0x2F, PacketTypesIn.EntityPositionAndRotation }, // Move Entity Pos Rot
{ 0x30, PacketTypesIn.MoveMinecartAlongTrack }, // Move Minecart Along Track
{ 0x31, PacketTypesIn.EntityRotation }, // Move Entity Rot
{ 0x32, PacketTypesIn.VehicleMove }, // Move Vehicle
{ 0x33, PacketTypesIn.OpenBook }, // Open Book
{ 0x34, PacketTypesIn.OpenWindow }, // Open Screen
{ 0x35, PacketTypesIn.OpenSignEditor }, // Open Sign Editor
{ 0x36, PacketTypesIn.Ping }, // Ping
{ 0x37, PacketTypesIn.PingResponse }, // Pong Response
{ 0x38, PacketTypesIn.CraftRecipeResponse }, // Place Ghost Recipe
{ 0x39, PacketTypesIn.PlayerAbilities }, // Player Abilities
{ 0x3A, PacketTypesIn.ChatMessage }, // Player Chat
{ 0x3B, PacketTypesIn.EndCombatEvent }, // Player Combat End
{ 0x3C, PacketTypesIn.EnterCombatEvent }, // Player Combat Enter
{ 0x3D, PacketTypesIn.DeathCombatEvent }, // Player Combat Kill
{ 0x3E, PacketTypesIn.PlayerRemove }, // Player Info Remove
{ 0x3F, PacketTypesIn.PlayerInfo }, // Player Info Update
{ 0x40, PacketTypesIn.FacePlayer }, // Player Look At
{ 0x41, PacketTypesIn.PlayerPositionAndLook }, // Player Position
{ 0x42, PacketTypesIn.PlayerRotation }, // Player Rotation
{ 0x43, PacketTypesIn.RecipeBookAdd }, // Recipe Book Add
{ 0x44, PacketTypesIn.RecipeBookRemove }, // Recipe Book Remove
{ 0x45, PacketTypesIn.RecipeBookSettings }, // Recipe Book Settings
{ 0x46, PacketTypesIn.DestroyEntities }, // Remove Entities
{ 0x47, PacketTypesIn.RemoveEntityEffect }, // Remove Mob Effect
{ 0x48, PacketTypesIn.ResetScore }, // Reset Score
{ 0x49, PacketTypesIn.RemoveResourcePack }, // Resource Pack Pop
{ 0x4A, PacketTypesIn.ResourcePackSend }, // Resource Pack Push
{ 0x4B, PacketTypesIn.Respawn }, // Respawn
{ 0x4C, PacketTypesIn.EntityHeadLook }, // Rotate Head
{ 0x4D, PacketTypesIn.MultiBlockChange }, // Section Blocks Update
{ 0x4E, PacketTypesIn.SelectAdvancementTab }, // Select Advancements Tab
{ 0x4F, PacketTypesIn.ServerData }, // Server Data
{ 0x50, PacketTypesIn.ActionBar }, // Set Action Bar Text
{ 0x51, PacketTypesIn.WorldBorderCenter }, // Set Border Center
{ 0x52, PacketTypesIn.WorldBorderLerpSize }, // Set Border Lerp Size
{ 0x53, PacketTypesIn.WorldBorderSize }, // Set Border Size
{ 0x54, PacketTypesIn.WorldBorderWarningDelay }, // Set Border Warning Delay
{ 0x55, PacketTypesIn.WorldBorderWarningReach }, // Set Border Warning Distance
{ 0x56, PacketTypesIn.Camera }, // Set Camera
{ 0x57, PacketTypesIn.UpdateViewPosition }, // Set Chunk Cache Center
{ 0x58, PacketTypesIn.UpdateViewDistance }, // Set Chunk Cache Radius
{ 0x59, PacketTypesIn.SetCursorItem }, // Set Cursor Item
{ 0x5A, PacketTypesIn.SpawnPosition }, // Set Default Spawn Position
{ 0x5B, PacketTypesIn.DisplayScoreboard }, // Set Display Objective
{ 0x5C, PacketTypesIn.EntityMetadata }, // Set Entity Data
{ 0x5D, PacketTypesIn.AttachEntity }, // Set Entity Link
{ 0x5E, PacketTypesIn.EntityVelocity }, // Set Entity Motion
{ 0x5F, PacketTypesIn.EntityEquipment }, // Set Equipment
{ 0x60, PacketTypesIn.SetExperience }, // Set Experience
{ 0x61, PacketTypesIn.UpdateHealth }, // Set Health
{ 0x62, PacketTypesIn.SetHeldSlot }, // Set Held Slot
{ 0x63, PacketTypesIn.ScoreboardObjective }, // Set Objective
{ 0x64, PacketTypesIn.SetPassengers }, // Set Passengers
{ 0x65, PacketTypesIn.SetPlayerInventory }, // Set Player Inventory
{ 0x66, PacketTypesIn.Teams }, // Set Player Team
{ 0x67, PacketTypesIn.UpdateScore }, // Set Score
{ 0x68, PacketTypesIn.UpdateSimulationDistance }, // Set Simulation Distance
{ 0x69, PacketTypesIn.SetTitleSubTitle }, // Set Subtitle Text
{ 0x6A, PacketTypesIn.TimeUpdate }, // Set Time
{ 0x6B, PacketTypesIn.SetTitleText }, // Set Title Text
{ 0x6C, PacketTypesIn.SetTitleTime }, // Set Titles Animation
{ 0x6D, PacketTypesIn.EntitySoundEffect }, // Sound Entity
{ 0x6E, PacketTypesIn.SoundEffect }, // Sound
{ 0x6F, PacketTypesIn.StartConfiguration }, // Start Configuration
{ 0x70, PacketTypesIn.StopSound }, // Stop Sound
{ 0x71, PacketTypesIn.StoreCookie }, // Store Cookie
{ 0x72, PacketTypesIn.SystemChat }, // System Chat
{ 0x73, PacketTypesIn.PlayerListHeaderAndFooter }, // Tab List
{ 0x74, PacketTypesIn.NBTQueryResponse }, // Tag Query
{ 0x75, PacketTypesIn.CollectItem }, // Take Item Entity
{ 0x76, PacketTypesIn.EntityTeleport }, // Teleport Entity
{ 0x77, PacketTypesIn.TestInstanceBlockStatus }, // Test Instance Block Status (new in 1.21.5)
{ 0x78, PacketTypesIn.SetTickingState }, // Ticking State
{ 0x79, PacketTypesIn.StepTick }, // Ticking Step
{ 0x7A, PacketTypesIn.Transfer }, // Transfer
{ 0x7B, PacketTypesIn.Advancements }, // Update Advancements
{ 0x7C, PacketTypesIn.EntityProperties }, // Update Attributes
{ 0x7D, PacketTypesIn.EntityEffect }, // Update Mob Effect
{ 0x7E, PacketTypesIn.DeclareRecipes }, // Update Recipes
{ 0x7F, PacketTypesIn.Tags }, // Update Tags
{ 0x80, PacketTypesIn.ProjectilePower }, // Projectile Power
{ 0x81, PacketTypesIn.CustomReportDetails }, // Custom Report Details
{ 0x82, PacketTypesIn.ServerLinks } // Server Links
};
private readonly Dictionary<int, PacketTypesOut> typeOut = new()
{
{ 0x00, PacketTypesOut.TeleportConfirm }, // Accept Teleportation
{ 0x01, PacketTypesOut.QueryBlockNBT }, // Block Entity Tag Query
{ 0x02, PacketTypesOut.BundleItemSelected }, // Bundle Item Selected
{ 0x03, PacketTypesOut.SetDifficulty }, // Change Difficulty
{ 0x04, PacketTypesOut.MessageAcknowledgment }, // Chat Ack
{ 0x05, PacketTypesOut.ChatCommand }, // Chat Command
{ 0x06, PacketTypesOut.SignedChatCommand }, // Chat Command Signed
{ 0x07, PacketTypesOut.ChatMessage }, // Chat
{ 0x08, PacketTypesOut.PlayerSession }, // Chat Session Update
{ 0x09, PacketTypesOut.ChunkBatchReceived }, // Chunk Batch Received
{ 0x0A, PacketTypesOut.ClientStatus }, // Client Command
{ 0x0B, PacketTypesOut.ClientTickEnd }, // Client Tick End
{ 0x0C, PacketTypesOut.ClientSettings }, // Client Information
{ 0x0D, PacketTypesOut.TabComplete }, // Command Suggestion
{ 0x0E, PacketTypesOut.AcknowledgeConfiguration }, // Configuration Acknowledged
{ 0x0F, PacketTypesOut.ClickWindowButton }, // Container Button Click
{ 0x10, PacketTypesOut.ClickWindow }, // Container Click
{ 0x11, PacketTypesOut.CloseWindow }, // Container Close
{ 0x12, PacketTypesOut.ChangeContainerSlotState }, // Container Slot State Changed
{ 0x13, PacketTypesOut.CookieResponse }, // Cookie Response
{ 0x14, PacketTypesOut.PluginMessage }, // Custom Payload
{ 0x15, PacketTypesOut.DebugSampleSubscription }, // Debug Sample Subscription
{ 0x16, PacketTypesOut.EditBook }, // Edit Book
{ 0x17, PacketTypesOut.EntityNBTRequest }, // Entity Tag Query
{ 0x18, PacketTypesOut.InteractEntity }, // Interact
{ 0x19, PacketTypesOut.GenerateStructure }, // Jigsaw Generate
{ 0x1A, PacketTypesOut.KeepAlive }, // Keep Alive
{ 0x1B, PacketTypesOut.LockDifficulty }, // Lock Difficulty
{ 0x1C, PacketTypesOut.PlayerPosition }, // Move Player Pos
{ 0x1D, PacketTypesOut.PlayerPositionAndRotation }, // Move Player Pos Rot
{ 0x1E, PacketTypesOut.PlayerRotation }, // Move Player Rot
{ 0x1F, PacketTypesOut.PlayerMovement }, // Move Player Status Only
{ 0x20, PacketTypesOut.VehicleMove }, // Move Vehicle
{ 0x21, PacketTypesOut.SteerBoat }, // Paddle Boat
{ 0x22, PacketTypesOut.PickItem }, // Pick Item From Block
{ 0x23, PacketTypesOut.PickItemFromEntity }, // Pick Item From Entity
{ 0x24, PacketTypesOut.PingRequest }, // Ping Request
{ 0x25, PacketTypesOut.CraftRecipeRequest }, // Place Recipe
{ 0x26, PacketTypesOut.PlayerAbilities }, // Player Abilities
{ 0x27, PacketTypesOut.PlayerDigging }, // Player Action
{ 0x28, PacketTypesOut.EntityAction }, // Player Command
{ 0x29, PacketTypesOut.SteerVehicle }, // Player Input
{ 0x2A, PacketTypesOut.PlayerLoaded }, // Player Loaded
{ 0x2B, PacketTypesOut.Pong }, // Pong
{ 0x2C, PacketTypesOut.SetDisplayedRecipe }, // Recipe Book Change Settings
{ 0x2D, PacketTypesOut.SetRecipeBookState }, // Recipe Book Seen Recipe
{ 0x2E, PacketTypesOut.NameItem }, // Rename Item
{ 0x2F, PacketTypesOut.ResourcePackStatus }, // Resource Pack
{ 0x30, PacketTypesOut.AdvancementTab }, // Seen Advancements
{ 0x31, PacketTypesOut.SelectTrade }, // Select Trade
{ 0x32, PacketTypesOut.SetBeaconEffect }, // Set Beacon
{ 0x33, PacketTypesOut.HeldItemChange }, // Set Carried Item
{ 0x34, PacketTypesOut.UpdateCommandBlock }, // Set Command Block
{ 0x35, PacketTypesOut.UpdateCommandBlockMinecart }, // Set Command Minecart
{ 0x36, PacketTypesOut.CreativeInventoryAction }, // Set Creative Mode Slot
{ 0x37, PacketTypesOut.UpdateJigsawBlock }, // Set Jigsaw Block
{ 0x38, PacketTypesOut.UpdateStructureBlock }, // Set Structure Block
{ 0x39, PacketTypesOut.SetTestBlock }, // Set Test Block (new in 1.21.5)
{ 0x3A, PacketTypesOut.UpdateSign }, // Sign Update
{ 0x3B, PacketTypesOut.Animation }, // Swing
{ 0x3C, PacketTypesOut.Spectate }, // Teleport To Entity
{ 0x3D, PacketTypesOut.TestInstanceBlockAction }, // Test Instance Block Action (new in 1.21.5)
{ 0x3E, PacketTypesOut.PlayerBlockPlacement }, // Use Item On
{ 0x3F, PacketTypesOut.UseItem }, // Use Item
};
private readonly Dictionary<int, ConfigurationPacketTypesIn> configurationTypesIn = new()
{
{ 0x00, ConfigurationPacketTypesIn.CookieRequest },
{ 0x01, ConfigurationPacketTypesIn.PluginMessage },
{ 0x02, ConfigurationPacketTypesIn.Disconnect },
{ 0x03, ConfigurationPacketTypesIn.FinishConfiguration },
{ 0x04, ConfigurationPacketTypesIn.KeepAlive },
{ 0x05, ConfigurationPacketTypesIn.Ping },
{ 0x06, ConfigurationPacketTypesIn.ResetChat },
{ 0x07, ConfigurationPacketTypesIn.RegistryData },
{ 0x08, ConfigurationPacketTypesIn.RemoveResourcePack },
{ 0x09, ConfigurationPacketTypesIn.ResourcePack },
{ 0x0A, ConfigurationPacketTypesIn.StoreCookie },
{ 0x0B, ConfigurationPacketTypesIn.Transfer },
{ 0x0C, ConfigurationPacketTypesIn.FeatureFlags },
{ 0x0D, ConfigurationPacketTypesIn.UpdateTags },
{ 0x0E, ConfigurationPacketTypesIn.KnownDataPacks },
{ 0x0F, ConfigurationPacketTypesIn.CustomReportDetails },
{ 0x10, ConfigurationPacketTypesIn.ServerLinks }
};
private readonly Dictionary<int, ConfigurationPacketTypesOut> configurationTypesOut = new()
{
{ 0x00, ConfigurationPacketTypesOut.ClientInformation },
{ 0x01, ConfigurationPacketTypesOut.CookieResponse },
{ 0x02, ConfigurationPacketTypesOut.PluginMessage },
{ 0x03, ConfigurationPacketTypesOut.FinishConfiguration },
{ 0x04, ConfigurationPacketTypesOut.KeepAlive },
{ 0x05, ConfigurationPacketTypesOut.Pong },
{ 0x06, ConfigurationPacketTypesOut.ResourcePackResponse },
{ 0x07, ConfigurationPacketTypesOut.KnownDataPacks }
};
protected override Dictionary<int, PacketTypesIn> GetListIn() => typeIn;
protected override Dictionary<int, PacketTypesOut> GetListOut() => typeOut;
protected override Dictionary<int, ConfigurationPacketTypesIn> GetConfigurationListIn() => configurationTypesIn!;
protected override Dictionary<int, ConfigurationPacketTypesOut> GetConfigurationListOut() => configurationTypesOut!;
}

View file

@ -48,8 +48,9 @@ namespace MinecraftClient.Protocol.Handlers
{
PacketTypePalette p = protocol switch
{
> Protocol18Handler.MC_1_21_4_Version => throw new NotImplementedException(Translations
> Protocol18Handler.MC_1_21_5_Version => throw new NotImplementedException(Translations
.exception_palette_packet),
<= Protocol18Handler.MC_1_21_5_Version and > Protocol18Handler.MC_1_21_4_Version => new PacketPalette1215(),
<= Protocol18Handler.MC_1_21_4_Version and > Protocol18Handler.MC_1_21_2_Version => new PacketPalette1214(),
<= Protocol18Handler.MC_1_8_Version => new PacketPalette17(),
<= Protocol18Handler.MC_1_11_2_Version => new PacketPalette110(),

View file

@ -134,6 +134,7 @@ namespace MinecraftClient.Protocol.Handlers
TabComplete, //
Tags, //
Teams, //
TestInstanceBlockStatus, // Added in 1.21.5
TimeUpdate, //
Title, //
TradeList, //

View file

@ -71,6 +71,8 @@ namespace MinecraftClient.Protocol.Handlers
UpdateJigsawBlock, //
UpdateSign, //
UpdateStructureBlock, //
SetTestBlock, // Added in 1.21.5
TestInstanceBlockAction, // Added in 1.21.5
UseItem, //
VehicleMove, //
WindowConfirmation, //

View file

@ -75,6 +75,7 @@ namespace MinecraftClient.Protocol.Handlers
internal const int MC_1_21_Version = 767;
internal const int MC_1_21_2_Version = 768;
internal const int MC_1_21_4_Version = 769;
internal const int MC_1_21_5_Version = 770;
private int compression_treshold = -1;
private int autocomplete_transaction_id = 0;
@ -126,21 +127,21 @@ namespace MinecraftClient.Protocol.Handlers
lastSeenMessagesCollector = protocolVersion >= MC_1_19_3_Version ? new(20) : new(5);
chunkBatchStartTime = GetNanos();
if (handler.GetTerrainEnabled() && protocolVersion > MC_1_21_4_Version)
if (handler.GetTerrainEnabled() && protocolVersion > MC_1_21_5_Version)
{
log.Error($"§c{Translations.extra_terrainandmovement_disabled}");
handler.SetTerrainEnabled(false);
}
if (handler.GetInventoryEnabled() &&
protocolVersion is < MC_1_8_Version or > MC_1_21_4_Version)
protocolVersion is < MC_1_8_Version or > MC_1_21_5_Version)
{
log.Error($"§c{Translations.extra_inventory_disabled}");
handler.SetInventoryEnabled(false);
}
if (handler.GetEntityHandlingEnabled() &&
protocolVersion is < MC_1_8_Version or > MC_1_21_4_Version)
protocolVersion is < MC_1_8_Version or > MC_1_21_5_Version)
{
log.Error($"§c{Translations.extra_entity_disabled}");
handler.SetEntityHandlingEnabled(false);
@ -149,8 +150,9 @@ namespace MinecraftClient.Protocol.Handlers
Block.Palette = protocolVersion switch
{
// Block palette
> MC_1_21_4_Version when handler.GetTerrainEnabled() =>
> MC_1_21_5_Version when handler.GetTerrainEnabled() =>
throw new NotImplementedException(Translations.exception_palette_block),
>= MC_1_21_5_Version => new Palette1215(),
>= MC_1_21_4_Version => new Palette1214(),
>= MC_1_21_2_Version => new Palette1212(),
>= MC_1_20_6_Version => new Palette1206(),
@ -170,8 +172,9 @@ namespace MinecraftClient.Protocol.Handlers
entityPalette = protocolVersion switch
{
// Entity palette
> MC_1_21_4_Version when handler.GetEntityHandlingEnabled() =>
> MC_1_21_5_Version when handler.GetEntityHandlingEnabled() =>
throw new NotImplementedException(Translations.exception_palette_entity),
>= MC_1_21_5_Version => new EntityPalette1215(),
>= MC_1_21_4_Version => new EntityPalette1214(),
>= MC_1_21_2_Version => new EntityPalette1212(),
>= MC_1_20_6_Version => new EntityPalette1206(),
@ -195,8 +198,9 @@ namespace MinecraftClient.Protocol.Handlers
itemPalette = protocolVersion switch
{
// Item palette
> MC_1_21_4_Version when handler.GetInventoryEnabled() =>
> MC_1_21_5_Version when handler.GetInventoryEnabled() =>
throw new NotImplementedException(Translations.exception_palette_item),
>= MC_1_21_5_Version => new ItemPalette1215(),
>= MC_1_21_4_Version => new ItemPalette1214(),
>= MC_1_21_2_Version => new ItemPalette1212(),
>= MC_1_21_Version => new ItemPalette121(),
@ -2631,7 +2635,7 @@ namespace MinecraftClient.Protocol.Handlers
// Also make a palette for field? Will be a lot of work
var healthField = protocolVersion switch
{
> MC_1_21_4_Version => throw new NotImplementedException(Translations
> MC_1_21_5_Version => throw new NotImplementedException(Translations
.exception_palette_healthfield),
// 1.17 and above
>= MC_1_17_Version => 9,

View file

@ -0,0 +1,83 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class BlocksAttacksComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public float BlockDelaySeconds { get; set; }
public float DisableCooldownScale { get; set; }
public List<byte[]> RawDamageReductions { get; set; } = [];
public float ItemDamageThreshold { get; set; }
public float ItemDamageBase { get; set; }
public float ItemDamageFactor { get; set; }
public override void Parse(Queue<byte> data)
{
BlockDelaySeconds = dataTypes.ReadNextFloat(data);
DisableCooldownScale = dataTypes.ReadNextFloat(data);
var reductionCount = dataTypes.ReadNextVarInt(data);
for (var i = 0; i < reductionCount; i++)
{
var horizontalBlockingAngle = dataTypes.ReadNextFloat(data);
var hasTypeFilter = dataTypes.ReadNextBool(data);
if (hasTypeFilter)
ReadHolderSet(data);
var baseDmg = dataTypes.ReadNextFloat(data);
var factor = dataTypes.ReadNextFloat(data);
}
ItemDamageThreshold = dataTypes.ReadNextFloat(data);
ItemDamageBase = dataTypes.ReadNextFloat(data);
ItemDamageFactor = dataTypes.ReadNextFloat(data);
var hasBypassedBy = dataTypes.ReadNextBool(data);
if (hasBypassedBy)
dataTypes.ReadNextString(data); // TagKey<DamageType> as ResourceLocation
var hasBlockSound = dataTypes.ReadNextBool(data);
if (hasBlockSound)
ReadSoundEventHolder(data);
var hasDisableSound = dataTypes.ReadNextBool(data);
if (hasDisableSound)
ReadSoundEventHolder(data);
}
private void ReadHolderSet(Queue<byte> data)
{
var sizeOrTag = dataTypes.ReadNextVarInt(data);
if (sizeOrTag == 0)
{
dataTypes.ReadNextString(data); // Tag ResourceLocation
}
else
{
var count = sizeOrTag - 1;
for (var i = 0; i < count; i++)
dataTypes.ReadNextVarInt(data); // Holder<DamageType> registry ids
}
}
private void ReadSoundEventHolder(Queue<byte> data)
{
var holderId = dataTypes.ReadNextVarInt(data);
if (holderId == 0)
{
dataTypes.ReadNextString(data); // ResourceLocation
var hasFixedRange = dataTypes.ReadNextBool(data);
if (hasFixedRange)
dataTypes.ReadNextFloat(data);
}
}
public override Queue<byte> Serialize()
{
return new Queue<byte>();
}
}

View file

@ -0,0 +1,51 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class EitherHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public bool IsHolder { get; set; }
public int HolderId { get; set; }
public string? ResourceKey { get; set; }
public override void Parse(Queue<byte> data)
{
IsHolder = dataTypes.ReadNextBool(data);
if (IsHolder)
{
HolderId = dataTypes.ReadNextVarInt(data);
// For simple entity variants, holderId > 0 means registry ref (id = holderId - 1)
// holderId == 0 means inline data; for most variants the inline is just the variant fields
// We skip inline data since MCC doesn't use variant details
if (HolderId == 0)
{
// Read inline variant data - varies by type, but most are simple
// For chicken/variant specifically this might have additional fields
// We'll consume what we can based on the pattern
// TODO: If needed, specialize per variant type
}
}
else
{
ResourceKey = dataTypes.ReadNextString(data);
}
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetBool(IsHolder));
if (IsHolder)
{
bytes.AddRange(DataTypes.GetVarInt(HolderId));
}
else
{
bytes.AddRange(DataTypes.GetString(ResourceKey ?? ""));
}
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,43 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class InstrumentComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public override void Parse(Queue<byte> data)
{
// EitherHolder<Instrument>: Bool + (Holder<Instrument> OR ResourceLocation)
var isHolder = dataTypes.ReadNextBool(data);
if (isHolder)
{
var holderId = dataTypes.ReadNextVarInt(data);
if (holderId == 0)
{
// Inline Instrument: SoundEvent holder + VarInt useDuration + Float range + Component description
var soundHolderId = dataTypes.ReadNextVarInt(data);
if (soundHolderId == 0)
{
dataTypes.ReadNextString(data); // ResourceLocation
var hasFixedRange = dataTypes.ReadNextBool(data);
if (hasFixedRange)
dataTypes.ReadNextFloat(data);
}
dataTypes.ReadNextVarInt(data); // useDuration
dataTypes.ReadNextFloat(data); // range
dataTypes.ReadNextString(data); // description (Component as JSON string)
}
}
else
{
dataTypes.ReadNextString(data); // ResourceLocation key
}
}
public override Queue<byte> Serialize()
{
return new Queue<byte>();
}
}

View file

@ -0,0 +1,35 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class PaintingVariantHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public override void Parse(Queue<byte> data)
{
// Holder<PaintingVariant>: VarInt discriminator
var holderId = dataTypes.ReadNextVarInt(data);
if (holderId == 0)
{
// Inline PaintingVariant: VarInt width + VarInt height + ResourceLocation assetId
dataTypes.ReadNextVarInt(data); // width
dataTypes.ReadNextVarInt(data); // height
dataTypes.ReadNextString(data); // assetId
// Optional<Component> title
if (dataTypes.ReadNextBool(data))
dataTypes.ReadNextString(data);
// Optional<Component> author
if (dataTypes.ReadNextBool(data))
dataTypes.ReadNextString(data);
}
}
public override Queue<byte> Serialize()
{
return new Queue<byte>();
}
}

View file

@ -0,0 +1,23 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class PotionDurationScaleComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public float Scale { get; set; }
public override void Parse(Queue<byte> data)
{
Scale = dataTypes.ReadNextFloat(data);
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetFloat(Scale));
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,23 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class ProvidesBannerPatternsComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public string TagKey { get; set; } = string.Empty;
public override void Parse(Queue<byte> data)
{
TagKey = dataTypes.ReadNextString(data); // ResourceLocation
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetString(TagKey));
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,42 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class ProvidesTrimMaterialComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public override void Parse(Queue<byte> data)
{
// EitherHolder<TrimMaterial>: Bool + (Holder<TrimMaterial> OR ResourceLocation)
var isHolder = dataTypes.ReadNextBool(data);
if (isHolder)
{
var holderId = dataTypes.ReadNextVarInt(data);
if (holderId == 0)
{
// Inline TrimMaterial: MaterialAssetGroup + Component description
// MaterialAssetGroup: string + map<ResourceLocation, string>
dataTypes.ReadNextString(data); // base asset suffix
var overrideCount = dataTypes.ReadNextVarInt(data);
for (var i = 0; i < overrideCount; i++)
{
dataTypes.ReadNextString(data); // ResourceKey<EquipmentAsset>
dataTypes.ReadNextString(data); // override suffix
}
// description Component
dataTypes.ReadNextString(data);
}
}
else
{
dataTypes.ReadNextString(data); // ResourceLocation key
}
}
public override Queue<byte> Serialize()
{
return new Queue<byte>();
}
}

View file

@ -0,0 +1,40 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class SoundEventHolderComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int HolderId { get; set; }
public string? SoundLocation { get; set; }
public bool HasFixedRange { get; set; }
public float FixedRange { get; set; }
public override void Parse(Queue<byte> data)
{
HolderId = dataTypes.ReadNextVarInt(data);
if (HolderId == 0)
{
SoundLocation = dataTypes.ReadNextString(data);
HasFixedRange = dataTypes.ReadNextBool(data);
if (HasFixedRange)
FixedRange = dataTypes.ReadNextFloat(data);
}
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetVarInt(HolderId));
if (HolderId == 0)
{
bytes.AddRange(DataTypes.GetString(SoundLocation ?? ""));
bytes.AddRange(DataTypes.GetBool(HasFixedRange));
if (HasFixedRange)
bytes.AddRange(DataTypes.GetFloat(FixedRange));
}
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,30 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class TooltipDisplayComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public bool HideTooltip { get; set; }
public List<int> HiddenComponentIds { get; set; } = [];
public override void Parse(Queue<byte> data)
{
HideTooltip = dataTypes.ReadNextBool(data);
var count = dataTypes.ReadNextVarInt(data);
for (var i = 0; i < count; i++)
HiddenComponentIds.Add(dataTypes.ReadNextVarInt(data));
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetBool(HideTooltip));
bytes.AddRange(DataTypes.GetVarInt(HiddenComponentIds.Count));
foreach (var id in HiddenComponentIds)
bytes.AddRange(DataTypes.GetVarInt(id));
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,23 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class VarIntComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int Value { get; set; }
public override void Parse(Queue<byte> data)
{
Value = dataTypes.ReadNextVarInt(data);
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetVarInt(Value));
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,26 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class WeaponComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int ItemDamagePerAttack { get; set; }
public float DisableBlockingForSeconds { get; set; }
public override void Parse(Queue<byte> data)
{
ItemDamagePerAttack = dataTypes.ReadNextVarInt(data);
DisableBlockingForSeconds = dataTypes.ReadNextFloat(data);
}
public override Queue<byte> Serialize()
{
var bytes = new List<byte>();
bytes.AddRange(DataTypes.GetVarInt(ItemDamagePerAttack));
bytes.AddRange(DataTypes.GetFloat(DisableBlockingForSeconds));
return new Queue<byte>(bytes);
}
}

View file

@ -0,0 +1,116 @@
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries;
public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
{
public StructuredComponentsRegistry1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: base(dataTypes, itemPalette, subComponentRegistry)
{
RegisterComponent<CustomDataComponent>(0, "minecraft:custom_data");
RegisterComponent<MaxStackSizeComponent>(1, "minecraft:max_stack_size");
RegisterComponent<MaxDamageComponent>(2, "minecraft:max_damage");
RegisterComponent<DamageComponent>(3, "minecraft:damage");
RegisterComponent<EmptyComponent>(4, "minecraft:unbreakable"); // Changed from Unbreakable (Bool) to Unit (empty) in 1.21.5
RegisterComponent<CustomNameComponent>(5, "minecraft:custom_name");
RegisterComponent<ItemNameComponent>(6, "minecraft:item_name");
RegisterComponent<ItemModelComponent>(7, "minecraft:item_model");
RegisterComponent<LoreNameComponent1206>(8, "minecraft:lore");
RegisterComponent<RarityComponent>(9, "minecraft:rarity");
RegisterComponent<EnchantmentsComponent>(10, "minecraft:enchantments");
RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(12, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(13, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(14, "minecraft:custom_model_data");
// 15: tooltip_display (NEW, replaces hide_additional_tooltip + hide_tooltip)
RegisterComponent<TooltipDisplayComponent>(15, "minecraft:tooltip_display");
RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost");
RegisterComponent<CreativeSlotLockComponent>(17, "minecraft:creative_slot_lock");
RegisterComponent<EnchantmentGlintOverrideComponent>(18, "minecraft:enchantment_glint_override");
RegisterComponent<IntangibleProjectileComponent>(19, "minecraft:intangible_projectile");
RegisterComponent<FoodComponent1212>(20, "minecraft:food");
RegisterComponent<ConsumableComponent>(21, "minecraft:consumable");
RegisterComponent<UseRemainderComponent>(22, "minecraft:use_remainder");
RegisterComponent<UseCooldownComponent>(23, "minecraft:use_cooldown");
RegisterComponent<DamageResistantComponent>(24, "minecraft:damage_resistant");
RegisterComponent<ToolComponent>(25, "minecraft:tool");
RegisterComponent<WeaponComponent>(26, "minecraft:weapon"); // NEW
RegisterComponent<EnchantableComponent>(27, "minecraft:enchantable");
RegisterComponent<EquippableComponent>(28, "minecraft:equippable");
RegisterComponent<RepairableComponent>(29, "minecraft:repairable");
RegisterComponent<GliderComponent>(30, "minecraft:glider");
RegisterComponent<TooltipStyleComponent>(31, "minecraft:tooltip_style");
RegisterComponent<DeathProtectionComponent>(32, "minecraft:death_protection");
RegisterComponent<BlocksAttacksComponent>(33, "minecraft:blocks_attacks"); // NEW
RegisterComponent<StoredEnchantmentsComponent>(34, "minecraft:stored_enchantments");
RegisterComponent<DyeColorComponent>(35, "minecraft:dyed_color");
RegisterComponent<MapColorComponent>(36, "minecraft:map_color");
RegisterComponent<MapIdComponent>(37, "minecraft:map_id");
RegisterComponent<MapDecorationsComponent>(38, "minecraft:map_decorations");
RegisterComponent<MapPostProcessingComponent>(39, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(40, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(41, "minecraft:bundle_contents");
RegisterComponent<PotionContentsComponent>(42, "minecraft:potion_contents");
RegisterComponent<PotionDurationScaleComponent>(43, "minecraft:potion_duration_scale"); // NEW
RegisterComponent<SuspiciousStewEffectsComponent>(44, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(45, "minecraft:writable_book_content");
RegisterComponent<WrittenBlookContentComponent>(46, "minecraft:written_book_content");
RegisterComponent<TrimComponent>(47, "minecraft:trim");
RegisterComponent<DebugStickStateComponent>(48, "minecraft:debug_stick_state");
RegisterComponent<EntityDataComponent>(49, "minecraft:entity_data");
RegisterComponent<BucketEntityDataComponent>(50, "minecraft:bucket_entity_data");
RegisterComponent<BlockEntityDataComponent>(51, "minecraft:block_entity_data");
RegisterComponent<InstrumentComponent1215>(52, "minecraft:instrument"); // Changed to EitherHolder<Instrument> in 1.21.5
RegisterComponent<ProvidesTrimMaterialComponent>(53, "minecraft:provides_trim_material"); // NEW
RegisterComponent<OmniousBottleAmplifierComponent>(54, "minecraft:ominous_bottle_amplifier");
RegisterComponent<JukeBoxPlayableComponent>(55, "minecraft:jukebox_playable");
RegisterComponent<ProvidesBannerPatternsComponent>(56, "minecraft:provides_banner_patterns"); // NEW
RegisterComponent<RecipesComponent>(57, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(58, "minecraft:lodestone_tracker");
RegisterComponent<FireworkExplosionComponent>(59, "minecraft:firework_explosion");
RegisterComponent<FireworksComponent>(60, "minecraft:fireworks");
RegisterComponent<ProfileComponent>(61, "minecraft:profile");
RegisterComponent<NoteBlockSoundComponent>(62, "minecraft:note_block_sound");
RegisterComponent<BannerPatternsComponent>(63, "minecraft:banner_patterns");
RegisterComponent<BaseColorComponent>(64, "minecraft:base_color");
RegisterComponent<PotDecorationsComponent>(65, "minecraft:pot_decorations");
RegisterComponent<ContainerComponent>(66, "minecraft:container");
RegisterComponent<BlockStateComponent>(67, "minecraft:block_state");
RegisterComponent<BeesComponent>(68, "minecraft:bees"); // Wire format unchanged in 1.21.5
RegisterComponent<LockComponent>(69, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(70, "minecraft:container_loot");
// Entity variant components (NEW in 1.21.5)
RegisterComponent<SoundEventHolderComponent>(71, "minecraft:break_sound");
RegisterComponent<VarIntComponent>(72, "minecraft:villager/variant");
RegisterComponent<VarIntComponent>(73, "minecraft:wolf/variant");
RegisterComponent<VarIntComponent>(74, "minecraft:wolf/sound_variant");
RegisterComponent<VarIntComponent>(75, "minecraft:wolf/collar"); // DyeColor as VarInt
RegisterComponent<VarIntComponent>(76, "minecraft:fox/variant");
RegisterComponent<VarIntComponent>(77, "minecraft:salmon/size");
RegisterComponent<VarIntComponent>(78, "minecraft:parrot/variant");
RegisterComponent<VarIntComponent>(79, "minecraft:tropical_fish/pattern");
RegisterComponent<VarIntComponent>(80, "minecraft:tropical_fish/base_color"); // DyeColor
RegisterComponent<VarIntComponent>(81, "minecraft:tropical_fish/pattern_color"); // DyeColor
RegisterComponent<VarIntComponent>(82, "minecraft:mooshroom/variant");
RegisterComponent<VarIntComponent>(83, "minecraft:rabbit/variant");
RegisterComponent<VarIntComponent>(84, "minecraft:pig/variant");
RegisterComponent<VarIntComponent>(85, "minecraft:cow/variant");
RegisterComponent<EitherHolderComponent>(86, "minecraft:chicken/variant"); // EitherHolder<ChickenVariant>
RegisterComponent<VarIntComponent>(87, "minecraft:frog/variant");
RegisterComponent<VarIntComponent>(88, "minecraft:horse/variant");
RegisterComponent<PaintingVariantHolderComponent>(89, "minecraft:painting/variant"); // Holder<PaintingVariant>
RegisterComponent<VarIntComponent>(90, "minecraft:llama/variant");
RegisterComponent<VarIntComponent>(91, "minecraft:axolotl/variant");
RegisterComponent<VarIntComponent>(92, "minecraft:cat/variant");
RegisterComponent<VarIntComponent>(93, "minecraft:cat/collar"); // DyeColor
RegisterComponent<VarIntComponent>(94, "minecraft:sheep/color"); // DyeColor
RegisterComponent<VarIntComponent>(95, "minecraft:shulker/color"); // DyeColor
}
}

View file

@ -21,6 +21,7 @@ public class StructuredComponentsHandler
{
Protocol18Handler.MC_1_20_6_Version => typeof(SubComponentRegistry1206),
Protocol18Handler.MC_1_21_Version => typeof(SubComponentRegistry121),
>= Protocol18Handler.MC_1_21_5_Version => typeof(SubComponentRegistry1212),
>= Protocol18Handler.MC_1_21_2_Version => typeof(SubComponentRegistry1212),
_ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for subcomponent registries!")
};
@ -33,6 +34,7 @@ public class StructuredComponentsHandler
{
Protocol18Handler.MC_1_20_6_Version => typeof(StructuredComponentsRegistry1206),
Protocol18Handler.MC_1_21_Version => typeof(StructuredComponentsRegistry121),
>= Protocol18Handler.MC_1_21_5_Version => typeof(StructuredComponentsRegistry1215),
>= Protocol18Handler.MC_1_21_2_Version => typeof(StructuredComponentsRegistry1212),
_ => throw new NotSupportedException($"Protocol version {protocolVersion} is not supported for structured component registries!")
};

View file

@ -154,7 +154,7 @@ namespace MinecraftClient.Protocol
{
4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573,
575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768,
769
769, 770
};
if (Array.IndexOf(suppoertedVersionsProtocol18, protocolVersion) > -1)
@ -358,6 +358,8 @@ namespace MinecraftClient.Protocol
return 768;
case "1.21.4":
return 769;
case "1.21.5":
return 770;
default:
return 0;
}
@ -441,6 +443,7 @@ namespace MinecraftClient.Protocol
767 => "1.21",
768 => "1.21.2",
769 => "1.21.4",
770 => "1.21.5",
_ => "0.0"
};
}