From 52286701310cdbed3a392d1089f2d0f06d21330d Mon Sep 17 00:00:00 2001 From: Anon Date: Fri, 5 Jun 2026 20:26:29 +0200 Subject: [PATCH] Fix inventory handling across protocol versions --- MinecraftClient/Commands/Useblock.cs | 22 ++- MinecraftClient/Mapping/World.cs | 64 +++++++-- MinecraftClient/McClient.cs | 106 ++++++++++++++- MinecraftClient/MinecraftClient.csproj | 27 +--- .../PacketPalettes/PacketPalette119.cs | 43 +++--- .../PacketPalettes/PacketPalette19.cs | 127 ++++++++++++++++++ .../Protocol/Handlers/PacketType18Handler.cs | 1 + .../Protocol/Handlers/PacketTypesIn.cs | 2 +- .../Protocol/Handlers/Protocol18.cs | 61 +++++---- 9 files changed, 364 insertions(+), 89 deletions(-) create mode 100644 MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs diff --git a/MinecraftClient/Commands/Useblock.cs b/MinecraftClient/Commands/Useblock.cs index 7e482ba4..d4f964a6 100644 --- a/MinecraftClient/Commands/Useblock.cs +++ b/MinecraftClient/Commands/Useblock.cs @@ -1,3 +1,4 @@ +using System; using Brigadier.NET; using Brigadier.NET.Builder; using MinecraftClient.CommandHandler; @@ -53,8 +54,27 @@ namespace MinecraftClient.Commands Location current = handler.GetCurrentLocation(); block = block.ToAbsolute(current).ToFloor(); Location blockCenter = block.ToCenter(); - bool res = handler.PlaceBlock(block, Direction.Down, hand, lookAtBlock: true); + bool res = handler.PlaceBlock(block, GetFaceNearestPlayer(current, blockCenter), hand, lookAtBlock: true); return r.SetAndReturn(string.Format(Translations.cmd_useblock_use, blockCenter.X, blockCenter.Y, blockCenter.Z, res ? "succeeded" : "failed"), res); } + + private static Direction GetFaceNearestPlayer(Location playerLocation, Location blockCenter) + { + double dx = playerLocation.X - blockCenter.X; + double dy = playerLocation.Y - blockCenter.Y; + double dz = playerLocation.Z - blockCenter.Z; + + double absX = Math.Abs(dx); + double absY = Math.Abs(dy); + double absZ = Math.Abs(dz); + + if (absX >= absY && absX >= absZ) + return dx >= 0 ? Direction.East : Direction.West; + + if (absY >= absZ) + return dy >= 0 ? Direction.Up : Direction.Down; + + return dz >= 0 ? Direction.South : Direction.North; + } } } diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index aacfe1ec..0b6330ce 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -69,7 +69,16 @@ namespace MinecraftClient.Mapping /// Registry Codec nbt data public static void StoreDimensionList(Dictionary registryCodec) { - var dimensionListNbt = (object[])(((Dictionary)registryCodec["minecraft:dimension_type"])["value"]); + const string namespacedDimensionTypeKey = "minecraft:dimension_type"; + const string legacyDimensionTypeKey = "dimension_type"; + + if (!registryCodec.TryGetValue(namespacedDimensionTypeKey, out var dimensionTypeRegistry) + && !registryCodec.TryGetValue(legacyDimensionTypeKey, out dimensionTypeRegistry)) + { + return; + } + + var dimensionListNbt = (object[])(((Dictionary)dimensionTypeRegistry)["value"]); foreach (var (dimensionName, dimensionType) in from Dictionary dimensionNbt in dimensionListNbt let dimensionName = (string)dimensionNbt["name"] let dimensionType = (Dictionary)dimensionNbt["element"] @@ -324,19 +333,48 @@ namespace MinecraftClient.Mapping } // If not found, check if name lacks 'minecraft:' prefix and try again - if (!name.StartsWith("minecraft:")) - { - string prefixedName = "minecraft:" + name; - if (dimensionList.TryGetValue(prefixedName, out dimension)) - { - curDimension = dimension; - return; // Dimension found with prefixed name - } - } + if (!name.StartsWith("minecraft:")) + { + string prefixedName = "minecraft:" + name; + if (dimensionList.TryGetValue(prefixedName, out dimension)) + { + curDimension = dimension; + return; // Dimension found with prefixed name + } + } + else + { + string unprefixedName = name["minecraft:".Length..]; + if (dimensionList.TryGetValue(unprefixedName, out dimension)) + { + curDimension = dimension; + return; + } + } - // If still not found, dimension does not exist - throw new KeyNotFoundException($"Dimension '{name}' not found in dimensions dictionary."); - } + if (TryStoreDefaultVanillaDimension(name) + && dimensionList.TryGetValue(name, out dimension)) + { + curDimension = dimension; + return; + } + + // If still not found, dimension does not exist + throw new KeyNotFoundException($"Dimension '{name}' not found in dimensions dictionary."); + } + + private static bool TryStoreDefaultVanillaDimension(string name) + { + var normalizedName = name.StartsWith("minecraft:") + ? name + : "minecraft:" + name; + + if (normalizedName is not ("minecraft:overworld" or "minecraft:the_nether" or "minecraft:the_end")) + return false; + + StoreOneDimension(name, new Dictionary()); + return true; + } diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index cde3ad55..b02e3269 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -2022,6 +2022,90 @@ namespace MinecraftClient }; } + private static bool TryGetMirroredPlayerInventoryRange(Container inventory, out int firstWindowSlot, out int lastWindowSlot) + { + firstWindowSlot = -1; + lastWindowSlot = -1; + + if (inventory.Type == ContainerType.PlayerInventory) + return false; + + const int mirroredPlayerInventorySlotCount = 36; + int slotCount = inventory.Type.SlotCount(); + if (slotCount < mirroredPlayerInventorySlotCount) + return false; + + firstWindowSlot = slotCount - mirroredPlayerInventorySlotCount; + lastWindowSlot = slotCount - 1; + return true; + } + + private static bool TryGetMirroredPlayerInventorySlot(Container inventory, int windowSlot, out int playerInventorySlot) + { + playerInventorySlot = -1; + + if (!TryGetMirroredPlayerInventoryRange(inventory, out int firstWindowSlot, out int lastWindowSlot)) + return false; + + if (windowSlot < firstWindowSlot || windowSlot > lastWindowSlot) + return false; + + playerInventorySlot = windowSlot - firstWindowSlot + 9; + return true; + } + + private static bool AreSameInventorySlot(Item? left, Item? right) + { + if (left is null || left.IsEmpty) + return right is null || right.IsEmpty; + if (right is null || right.IsEmpty) + return false; + + return left.Type == right.Type + && left.Count == right.Count + && left.Data == right.Data + && ReferenceEquals(left.NBT, right.NBT) + && ReferenceEquals(left.Components, right.Components); + } + + private bool SetPlayerInventorySlot(int playerInventorySlot, Item? item) + { + if (!inventories.TryGetValue(0, out Container? playerInventory)) + return false; + + playerInventory.Items.TryGetValue(playerInventorySlot, out Item? previousItem); + if (AreSameInventorySlot(previousItem, item)) + return false; + + if (item is null || item.IsEmpty) + playerInventory.Items.Remove(playerInventorySlot); + else + playerInventory.Items[playerInventorySlot] = item; + + return true; + } + + private bool SyncPlayerInventorySlotFromWindow(Container inventory, int windowSlot) + { + if (!TryGetMirroredPlayerInventorySlot(inventory, windowSlot, out int playerInventorySlot)) + return false; + + inventory.Items.TryGetValue(windowSlot, out Item? item); + return SetPlayerInventorySlot(playerInventorySlot, item); + } + + private bool SyncPlayerInventorySlotsFromWindow(Container inventory) + { + if (!TryGetMirroredPlayerInventoryRange(inventory, out int firstWindowSlot, out int lastWindowSlot)) + return false; + + bool changed = false; + for (int windowSlot = firstWindowSlot; windowSlot <= lastWindowSlot; windowSlot++) + changed |= SyncPlayerInventorySlotFromWindow(inventory, windowSlot); + + return changed; + } + /// /// Click a slot in the specified window /// @@ -2748,6 +2832,8 @@ namespace MinecraftClient changedSlots.Add(new Tuple((short)slotId, null)); break; } + + SyncPlayerInventorySlotsFromWindow(inventory); } return handler.SendWindowAction(windowId, slotId, action, item, changedSlots, inventories[windowId].StateID); @@ -2764,7 +2850,16 @@ namespace MinecraftClient /// TRUE if item given successfully public bool DoCreativeGive(int slot, ItemType itemType, int count, Dictionary? nbt = null) { - return InvokeOnMainThread(() => handler.SendCreativeInventoryAction(slot, itemType, count, nbt)); + return InvokeOnMainThread(() => + { + if (!handler.SendCreativeInventoryAction(slot, itemType, count, nbt)) + return false; + + if (slot is >= 1 and <= 45) + SetPlayerInventorySlot(slot, new Item(itemType, count, nbt)); + + return true; + }); } /// @@ -3780,6 +3875,9 @@ namespace MinecraftClient { inventories[inventoryID].Items = itemList; inventories[inventoryID].StateID = stateId; + bool playerInventoryChanged = SyncPlayerInventorySlotsFromWindow(inventories[inventoryID]); + if (playerInventoryChanged) + DispatchBotEvent(bot => bot.OnInventoryUpdate(0)); DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); } } @@ -3820,6 +3918,9 @@ namespace MinecraftClient inventories[inventoryID].Items.Remove(slotID); } else inventories[inventoryID].Items[slotID] = item; + + if (SyncPlayerInventorySlotFromWindow(inventories[inventoryID], slotID)) + DispatchBotEvent(bot => bot.OnInventoryUpdate(0)); } } DispatchBotEvent(bot => bot.OnInventoryUpdate(inventoryID)); @@ -4676,6 +4777,9 @@ namespace MinecraftClient { switch (reason) { + case 3: + OnGamemodeUpdate(Guid.Empty, (int)value); + break; case 7: DispatchBotEvent(bot => bot.OnRainLevelChange(value)); break; diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 9a9c8baa..4307393e 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -59,32 +59,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs index 0757dcb8..7a7e9eb6 100644 --- a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette119.cs @@ -118,9 +118,9 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x00, PacketTypesOut.TeleportConfirm }, // (Wiki name: Confirm Teleportation) { 0x01, PacketTypesOut.QueryBlockNBT }, // (Wiki name: Query Block Entity Tag) { 0x02, PacketTypesOut.SetDifficulty }, // (Wiki name: Change Difficutly) - { 0x03, PacketTypesOut.MessageAcknowledgment }, // - { 0x04, PacketTypesOut.ChatCommand }, // Added in 1.19 - { 0x05, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x03, PacketTypesOut.ChatCommand }, // Added in 1.19 + { 0x04, PacketTypesOut.ChatMessage }, // Changed in 1.19 (Completely changed) (Wiki name: Chat) + { 0x05, PacketTypesOut.ChatPreview }, // Added in 1.19 (Wiki name: Chat Preview (serverbound)) { 0x06, PacketTypesOut.ClientStatus }, // (Wiki name: Client Command) { 0x07, PacketTypesOut.ClientSettings }, // (Wiki name: Client Information) { 0x08, PacketTypesOut.TabComplete }, // (Wiki name: Command Suggestions Request) @@ -147,25 +147,24 @@ namespace MinecraftClient.Protocol.Handlers.PacketPalettes { 0x1D, PacketTypesOut.EntityAction }, // (Wiki name: Player Command) { 0x1E, PacketTypesOut.SteerVehicle }, // (Wiki name: Player Input) { 0x1F, PacketTypesOut.Pong }, // (Wiki name: Pong (play)) - { 0x20, PacketTypesOut.PlayerSession }, // Added in 1.19.3 - { 0x21, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) - { 0x22, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) - { 0x23, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) - { 0x24, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) - { 0x25, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) - { 0x26, PacketTypesOut.SelectTrade }, // - { 0x27, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) - { 0x28, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) - { 0x29, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) - { 0x2A, PacketTypesOut.UpdateCommandBlockMinecart }, // - { 0x2B, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) - { 0x2C, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) - { 0x2D, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) - { 0x2E, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) - { 0x2F, PacketTypesOut.Animation }, // (Wiki name: Swing) - { 0x30, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) - { 0x31, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) - { 0x32, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) + { 0x20, PacketTypesOut.SetDisplayedRecipe }, // (Wiki name: Recipe Book Change Settings) + { 0x21, PacketTypesOut.SetRecipeBookState }, // (Wiki name: Recipe Book Seen Recipe) + { 0x22, PacketTypesOut.NameItem }, // (Wiki name: Rename Item) + { 0x23, PacketTypesOut.ResourcePackStatus }, // (Wiki name: Resource Pack (serverbound)) + { 0x24, PacketTypesOut.AdvancementTab }, // (Wiki name: Seen Advancements) + { 0x25, PacketTypesOut.SelectTrade }, // + { 0x26, PacketTypesOut.SetBeaconEffect }, // Changed in 1.19 (Added a "Secondary Effect Present" and "Secondary Effect" fields) (Wiki name: Set Beacon) - (No need to be implemented) + { 0x27, PacketTypesOut.HeldItemChange }, // (Wiki name: Set Carried Item (serverbound)) + { 0x28, PacketTypesOut.UpdateCommandBlock }, // (Wiki name: Set Command Block) + { 0x29, PacketTypesOut.UpdateCommandBlockMinecart }, // + { 0x2A, PacketTypesOut.CreativeInventoryAction }, // (Wiki name: Set Creative Mode Slot) + { 0x2B, PacketTypesOut.UpdateJigsawBlock }, // (Wiki name: Set Jigsaw Block) + { 0x2C, PacketTypesOut.UpdateStructureBlock }, // (Wiki name: Set Structure Block) + { 0x2D, PacketTypesOut.UpdateSign }, // (Wiki name: Sign Update) + { 0x2E, PacketTypesOut.Animation }, // (Wiki name: Swing) + { 0x2F, PacketTypesOut.Spectate }, // (Wiki name: Teleport To Entity) + { 0x30, PacketTypesOut.PlayerBlockPlacement }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item On) + { 0x31, PacketTypesOut.UseItem }, // Changed in 1.19 (Added a "Sequence" field) (Wiki name: Use Item) }; protected override Dictionary GetListIn() => typeIn; diff --git a/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs new file mode 100644 index 00000000..ebffc31a --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/PacketPalettes/PacketPalette19.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; + +namespace MinecraftClient.Protocol.Handlers.PacketPalettes +{ + public class PacketPalette19 : PacketTypePalette + { + private readonly Dictionary typeIn = new() + { + { 0x00, PacketTypesIn.SpawnEntity }, + { 0x01, PacketTypesIn.SpawnExperienceOrb }, + { 0x02, PacketTypesIn.SpawnWeatherEntity }, + { 0x03, PacketTypesIn.SpawnLivingEntity }, + { 0x04, PacketTypesIn.SpawnPainting }, + { 0x05, PacketTypesIn.SpawnPlayer }, + { 0x06, PacketTypesIn.EntityAnimation }, + { 0x07, PacketTypesIn.Statistics }, + { 0x08, PacketTypesIn.BlockBreakAnimation }, + { 0x09, PacketTypesIn.BlockEntityData }, + { 0x0A, PacketTypesIn.BlockAction }, + { 0x0B, PacketTypesIn.BlockChange }, + { 0x0C, PacketTypesIn.BossBar }, + { 0x0D, PacketTypesIn.ServerDifficulty }, + { 0x0E, PacketTypesIn.TabComplete }, + { 0x0F, PacketTypesIn.ChatMessage }, + { 0x10, PacketTypesIn.MultiBlockChange }, + { 0x11, PacketTypesIn.WindowConfirmation }, + { 0x12, PacketTypesIn.CloseWindow }, + { 0x13, PacketTypesIn.OpenWindow }, + { 0x14, PacketTypesIn.WindowItems }, + { 0x15, PacketTypesIn.WindowProperty }, + { 0x16, PacketTypesIn.SetSlot }, + { 0x17, PacketTypesIn.SetCooldown }, + { 0x18, PacketTypesIn.PluginMessage }, + { 0x19, PacketTypesIn.NamedSoundEffect }, + { 0x1A, PacketTypesIn.Disconnect }, + { 0x1B, PacketTypesIn.EntityStatus }, + { 0x1C, PacketTypesIn.Explosion }, + { 0x1D, PacketTypesIn.UnloadChunk }, + { 0x1E, PacketTypesIn.ChangeGameState }, + { 0x1F, PacketTypesIn.KeepAlive }, + { 0x20, PacketTypesIn.ChunkData }, + { 0x21, PacketTypesIn.Effect }, + { 0x22, PacketTypesIn.Particle }, + { 0x23, PacketTypesIn.JoinGame }, + { 0x24, PacketTypesIn.MapData }, + { 0x25, PacketTypesIn.EntityPosition }, + { 0x26, PacketTypesIn.EntityPositionAndRotation }, + { 0x27, PacketTypesIn.EntityRotation }, + { 0x28, PacketTypesIn.EntityMovement }, + { 0x29, PacketTypesIn.VehicleMove }, + { 0x2A, PacketTypesIn.OpenSignEditor }, + { 0x2B, PacketTypesIn.PlayerAbilities }, + { 0x2C, PacketTypesIn.CombatEvent }, + { 0x2D, PacketTypesIn.PlayerInfo }, + { 0x2E, PacketTypesIn.PlayerPositionAndLook }, + { 0x2F, PacketTypesIn.UseBed }, + { 0x30, PacketTypesIn.DestroyEntities }, + { 0x31, PacketTypesIn.RemoveEntityEffect }, + { 0x32, PacketTypesIn.ResourcePackSend }, + { 0x33, PacketTypesIn.Respawn }, + { 0x34, PacketTypesIn.EntityHeadLook }, + { 0x35, PacketTypesIn.WorldBorder }, + { 0x36, PacketTypesIn.Camera }, + { 0x37, PacketTypesIn.HeldItemChange }, + { 0x38, PacketTypesIn.DisplayScoreboard }, + { 0x39, PacketTypesIn.EntityMetadata }, + { 0x3A, PacketTypesIn.AttachEntity }, + { 0x3B, PacketTypesIn.EntityVelocity }, + { 0x3C, PacketTypesIn.EntityEquipment }, + { 0x3D, PacketTypesIn.SetExperience }, + { 0x3E, PacketTypesIn.UpdateHealth }, + { 0x3F, PacketTypesIn.ScoreboardObjective }, + { 0x40, PacketTypesIn.SetPassengers }, + { 0x41, PacketTypesIn.Teams }, + { 0x42, PacketTypesIn.UpdateScore }, + { 0x43, PacketTypesIn.SpawnPosition }, + { 0x44, PacketTypesIn.TimeUpdate }, + { 0x45, PacketTypesIn.Title }, + { 0x46, PacketTypesIn.UpdateSign }, + { 0x47, PacketTypesIn.SoundEffect }, + { 0x48, PacketTypesIn.PlayerListHeaderAndFooter }, + { 0x49, PacketTypesIn.CollectItem }, + { 0x4A, PacketTypesIn.EntityTeleport }, + { 0x4B, PacketTypesIn.EntityProperties }, + { 0x4C, PacketTypesIn.EntityEffect }, + }; + + private readonly Dictionary typeOut = new() + { + { 0x00, PacketTypesOut.TeleportConfirm }, + { 0x01, PacketTypesOut.TabComplete }, + { 0x02, PacketTypesOut.ChatMessage }, + { 0x03, PacketTypesOut.ClientStatus }, + { 0x04, PacketTypesOut.ClientSettings }, + { 0x05, PacketTypesOut.WindowConfirmation }, + { 0x06, PacketTypesOut.EnchantItem }, + { 0x07, PacketTypesOut.ClickWindow }, + { 0x08, PacketTypesOut.CloseWindow }, + { 0x09, PacketTypesOut.PluginMessage }, + { 0x0A, PacketTypesOut.InteractEntity }, + { 0x0B, PacketTypesOut.KeepAlive }, + { 0x0C, PacketTypesOut.PlayerPosition }, + { 0x0D, PacketTypesOut.PlayerPositionAndRotation }, + { 0x0E, PacketTypesOut.PlayerRotation }, + { 0x0F, PacketTypesOut.PlayerMovement }, + { 0x10, PacketTypesOut.VehicleMove }, + { 0x11, PacketTypesOut.SteerBoat }, + { 0x12, PacketTypesOut.PlayerAbilities }, + { 0x13, PacketTypesOut.PlayerDigging }, + { 0x14, PacketTypesOut.EntityAction }, + { 0x15, PacketTypesOut.SteerVehicle }, + { 0x16, PacketTypesOut.ResourcePackStatus }, + { 0x17, PacketTypesOut.HeldItemChange }, + { 0x18, PacketTypesOut.CreativeInventoryAction }, + { 0x19, PacketTypesOut.UpdateSign }, + { 0x1A, PacketTypesOut.Animation }, + { 0x1B, PacketTypesOut.Spectate }, + { 0x1C, PacketTypesOut.PlayerBlockPlacement }, + { 0x1D, PacketTypesOut.UseItem }, + }; + + protected override Dictionary GetListIn() => typeIn; + protected override Dictionary GetListOut() => typeOut; + protected override Dictionary GetConfigurationListIn() => new(); + protected override Dictionary GetConfigurationListOut() => new(); + } +} diff --git a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs index 21515782..82ee6e69 100644 --- a/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs +++ b/MinecraftClient/Protocol/Handlers/PacketType18Handler.cs @@ -56,6 +56,7 @@ namespace MinecraftClient.Protocol.Handlers <= 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_9_2_Version => new PacketPalette19(), <= Protocol18Handler.MC_1_11_2_Version => new PacketPalette110(), <= Protocol18Handler.MC_1_12_Version => new PacketPalette112(), <= Protocol18Handler.MC_1_12_2_Version => new PacketPalette1122(), diff --git a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs index a36bc4ba..8aee524a 100644 --- a/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs +++ b/MinecraftClient/Protocol/Handlers/PacketTypesIn.cs @@ -146,7 +146,7 @@ namespace MinecraftClient.Protocol.Handlers UpdateHealth, // UpdateLight, // UpdateScore, // - UpdateSign, // For 1.8 or below + UpdateSign, // For 1.8 or below, and 1.9-1.9.2 UpdateSimulationDistance, // UpdateViewDistance, // UpdateViewPosition, // diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 9f56a622..5f71b9af 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -45,6 +45,7 @@ namespace MinecraftClient.Protocol.Handlers internal const int MC_1_8_Version = 47; internal const int MC_1_9_Version = 107; internal const int MC_1_9_1_Version = 108; + internal const int MC_1_9_2_Version = 109; internal const int MC_1_10_Version = 210; internal const int MC_1_11_Version = 315; internal const int MC_1_11_2_Version = 316; @@ -835,15 +836,15 @@ namespace MinecraftClient.Protocol.Handlers dimensionTypeName = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier break; - case >= MC_1_16_2_Version: - dimensionType = - dataTypes.ReadNextNbt( - packetData); // Dimension Type: NBT Tag Compound - break; - default: - dataTypes.ReadNextString(packetData); - break; - } + case >= MC_1_16_2_Version: + dimensionType = + dataTypes.ReadNextNbt( + packetData); // Dimension Type: NBT Tag Compound + break; + default: + dimensionTypeName = dataTypes.ReadNextString(packetData); + break; + } currentDimension = 0; break; @@ -1408,14 +1409,14 @@ namespace MinecraftClient.Protocol.Handlers dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); // Dimension Type: Identifier break; - case >= MC_1_16_2_Version: - dimensionTypeRespawn = - dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound - break; - default: - dataTypes.ReadNextString(packetData); - break; - } + case >= MC_1_16_2_Version: + dimensionTypeRespawn = + dataTypes.ReadNextNbt(packetData); // Dimension Type: NBT Tag Compound + break; + default: + dimensionTypeNameRespawn = dataTypes.ReadNextString(packetData); + break; + } currentDimension = 0; } @@ -4027,6 +4028,21 @@ namespace MinecraftClient.Protocol.Handlers int blockEntityCount = dataTypes.ReadNextVarInt(packetData); for (int i = 0; i < blockEntityCount; i++) { + if (protocolVersion < MC_1_18_1_Version) + { + Dictionary? blockEntityNbt = dataTypes.ReadNextNbt(packetData); + if (blockEntityNbt.TryGetValue("x", out var nbtX) + && blockEntityNbt.TryGetValue("y", out var nbtY) + && blockEntityNbt.TryGetValue("z", out var nbtZ)) + { + handler.OnBlockEntityData( + new Location(Convert.ToInt32(nbtX), Convert.ToInt32(nbtY), Convert.ToInt32(nbtZ)), + blockEntityNbt); + } + + continue; + } + int packedXZ = dataTypes.ReadNextByte(packetData); int y = dataTypes.ReadNextShort(packetData); dataTypes.ReadNextVarInt(packetData); // Block entity type registry id @@ -5616,7 +5632,7 @@ namespace MinecraftClient.Protocol.Handlers private static byte ToLegacyBlockPlacementCursor(float cursor) { - return (byte)Math.Clamp((int)(cursor * 16.0f), 0, byte.MaxValue); + return (byte)Math.Clamp((int)(cursor * 16.0f), 0, 15); } public bool SendHeldItemChange(short slot) @@ -5752,16 +5768,11 @@ namespace MinecraftClient.Protocol.Handlers switch (protocolVersion) { - // 1.18+ - case >= MC_1_18_1_Version: + // 1.17.1+ + case >= MC_1_17_1_Version: packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID break; - // 1.17.1 - case MC_1_17_1_Version: - packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID - packet.AddRange(DataTypes.GetVarInt(stateId)); // State ID - break; // Older default: packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID