From 6e790b89c6dfa744f1311a5ba9e1c87d4ac32ca9 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Fri, 28 Mar 2025 21:22:21 +0000 Subject: [PATCH] Implement data component hashing in inventory transactions, disable printing tests --- .../geyser/inventory/click/ClickPlan.java | 14 +++------- .../item/hashing/DataComponentHashers.java | 28 ++++++++++++++++++- ...BedrockInventoryTransactionTranslator.java | 6 ++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 4d8af3882..40a934b84 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -26,12 +26,14 @@ package org.geysermc.geyser.inventory.click; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.SlotType; +import org.geysermc.geyser.item.hashing.DataComponentHashers; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.BundleInventoryTranslator; import org.geysermc.geyser.translator.inventory.CraftingInventoryTranslator; @@ -41,7 +43,6 @@ import org.geysermc.geyser.util.thirdparty.Fraction; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerActionType; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import org.geysermc.mcprotocollib.protocol.data.game.inventory.MoveToHotbarAction; -import org.geysermc.mcprotocollib.protocol.data.game.item.HashedStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSelectBundleItemPacket; @@ -51,8 +52,6 @@ import org.jetbrains.annotations.Contract; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; -import java.util.Map; -import java.util.Set; public final class ClickPlan { private final List plan = new ArrayList<>(); @@ -160,8 +159,8 @@ public final class ClickPlan { action.slot, action.click.actionType, action.click.action, - hashStack(clickedItemStack), - new Int2ObjectOpenHashMap<>() // TODO fixme + DataComponentHashers.hashStack(session, clickedItemStack), + Int2ObjectMaps.emptyMap() ); session.sendDownstreamGamePacket(clickPacket); @@ -517,9 +516,4 @@ public final class ClickPlan { private record ClickAction(Click click, int slot, boolean force) { } - - // TODO probably move this - public static HashedStack hashStack(ItemStack stack) { - return stack == null ? null : new HashedStack(stack.getId(), stack.getAmount(), Map.of(), Set.of()); // TODO this is WRONG. figure out stack hashing - } } diff --git a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java index 1eb5f6348..9d1a71d4a 100644 --- a/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java +++ b/core/src/main/java/org/geysermc/geyser/item/hashing/DataComponentHashers.java @@ -45,12 +45,14 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect; import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; +import org.geysermc.mcprotocollib.protocol.data.game.item.HashedStack; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlockStateProperties; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BlocksAttacks; import org.geysermc.mcprotocollib.protocol.data.game.item.component.Consumable; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ConsumeEffect; import org.geysermc.mcprotocollib.protocol.data.game.item.component.CustomModelData; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -75,8 +77,10 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.WrittenBookC import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; @SuppressWarnings("UnstableApiUsage") @@ -292,11 +296,33 @@ public class DataComponentHashers { public static HashCode hash(GeyserSession session, DataComponentType component, T value) { MinecraftHasher hasher = (MinecraftHasher) hashers.get(component); if (hasher == null) { - throw new IllegalStateException("Unregistered hasher for component " + component + "!"); // TODO we might not have hashers for every component, in which case, fix this + throw new IllegalStateException("Unregistered hasher for component " + component + "!"); } return hasher.hash(value, new MinecraftHashEncoder(session)); } + public static HashedStack hashStack(GeyserSession session, ItemStack stack) { + if (stack == null) { + return null; + } + + DataComponents patch = stack.getDataComponentsPatch(); + if (patch == null) { + return new HashedStack(stack.getId(), stack.getAmount(), Map.of(), Set.of()); + } + Map, DataComponent> components = patch.getDataComponents(); + Map, Integer> hashedAdditions = new HashMap<>(); + Set> removals = new HashSet<>(); + for (Map.Entry, DataComponent> component : components.entrySet()) { + if (component.getValue().getValue() == null) { + removals.add(component.getKey()); + } else { + hashedAdditions.put(component.getKey(), hash(session, (DataComponentType) component.getKey(), component.getValue().getValue()).asInt()); + } + } + return new HashedStack(stack.getId(), stack.getAmount(), hashedAdditions, removals); + } + // TODO better testing, at the moment this is just called when the player is spawned public static void testHashing(GeyserSession session) { // Hashed values generated by vanilla Java diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 404e213a7..5059a1554 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -52,9 +52,9 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; -import org.geysermc.geyser.inventory.click.ClickPlan; import org.geysermc.geyser.inventory.item.GeyserInstrument; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.hashing.DataComponentHashers; import org.geysermc.geyser.item.type.BlockItem; import org.geysermc.geyser.item.type.BoatItem; import org.geysermc.geyser.item.type.Item; @@ -143,11 +143,11 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator