From 0922b181f261348e9826f24222ba55212dd13800 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Fri, 29 Nov 2024 15:51:22 +0000 Subject: [PATCH] Fix equippable slot reading and add support for all consume animations --- .../components/readers/EquippableReader.java | 20 ++++++++-- .../CustomItemRegistryPopulator_v2.java | 40 +++++++++++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/components/readers/EquippableReader.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/components/readers/EquippableReader.java index 8c5e1d6c3..b317955f6 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/components/readers/EquippableReader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/components/readers/EquippableReader.java @@ -34,7 +34,15 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable; import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound; +import java.util.Map; + public class EquippableReader extends DataComponentReader { + private static final Map SLOTS = Map.of( + "head", EquipmentSlot.HELMET, + "chest", EquipmentSlot.CHESTPLATE, + "legs", EquipmentSlot.LEGGINGS, + "feet", EquipmentSlot.BOOTS + ); public EquippableReader() { super(DataComponentType.EQUIPPABLE); @@ -44,12 +52,16 @@ public class EquippableReader extends DataComponentReader { protected Equippable readDataComponent(@NonNull JsonNode node) throws InvalidCustomMappingsFileException { requireObject(node); - JsonNode slot = node.get("slot"); - if (slot == null || !slot.isTextual()) { - throw new InvalidCustomMappingsFileException("Expected slot to be helmet, chestplate, leggings or boots"); + JsonNode slotNode = node.get("slot"); + if (slotNode == null) { + throw new InvalidCustomMappingsFileException("Expected slot to be present"); + } + EquipmentSlot slot = SLOTS.get(slotNode.asText()); + if (slot == null) { + throw new InvalidCustomMappingsFileException("Expected slot to be head, chest, legs or feet"); } - return new Equippable(EquipmentSlot.valueOf(slot.asText().toUpperCase()), BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, + return new Equippable(slot, BuiltinSound.ITEM_ARMOR_EQUIP_GENERIC, null, null, null, false, false, false); // Other properties are unused } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator_v2.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator_v2.java index a6c0dbc32..3d9876894 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator_v2.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomItemRegistryPopulator_v2.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.registry.populator; import com.google.common.collect.Multimap; -import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; @@ -61,6 +60,18 @@ import java.util.Map; import java.util.Set; public class CustomItemRegistryPopulator_v2 { + // In behaviour packs and Java components this is set to a text value, such as "eat" or "drink"; over Bedrock network it's sent as an int. + private static final Map BEDROCK_ANIMATIONS = Map.of( + Consumable.ItemUseAnimation.NONE, 0, + Consumable.ItemUseAnimation.EAT, 1, + Consumable.ItemUseAnimation.DRINK, 2, + Consumable.ItemUseAnimation.BLOCK, 3, + Consumable.ItemUseAnimation.BOW, 4, + Consumable.ItemUseAnimation.SPEAR, 6, + Consumable.ItemUseAnimation.CROSSBOW, 9, + Consumable.ItemUseAnimation.SPYGLASS, 10, + Consumable.ItemUseAnimation.BRUSH, 12 + ); public static void populate(Map items, Multimap customItems, List nonVanillaCustomItems /* TODO */) { // TODO @@ -256,34 +267,31 @@ public class CustomItemRegistryPopulator_v2 { case BOOTS -> { componentBuilder.putString("minecraft:render_offsets", "boots"); componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt()); - componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); - itemProperties.putString("enchantable_slot", "armor_feet"); - itemProperties.putInt("enchantable_value", 15); + //itemProperties.putString("enchantable_slot", "armor_feet"); + //itemProperties.putInt("enchantable_value", 15); TODO } case CHESTPLATE -> { componentBuilder.putString("minecraft:render_offsets", "chestplates"); componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt()); - componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); - itemProperties.putString("enchantable_slot", "armor_torso"); - itemProperties.putInt("enchantable_value", 15); + //itemProperties.putString("enchantable_slot", "armor_torso"); + //itemProperties.putInt("enchantable_value", 15); TODO } case LEGGINGS -> { componentBuilder.putString("minecraft:render_offsets", "leggings"); componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt()); - componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); - itemProperties.putString("enchantable_slot", "armor_legs"); - itemProperties.putInt("enchantable_value", 15); + //itemProperties.putString("enchantable_slot", "armor_legs"); + //itemProperties.putInt("enchantable_value", 15); TODO } case HELMET -> { componentBuilder.putString("minecraft:render_offsets", "helmets"); componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt()); - componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); + //componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build()); - itemProperties.putString("enchantable_slot", "armor_head"); - itemProperties.putInt("enchantable_value", 15); + //itemProperties.putString("enchantable_slot", "armor_head"); + //itemProperties.putInt("enchantable_value", 15); } } } @@ -291,9 +299,9 @@ public class CustomItemRegistryPopulator_v2 { private static void computeConsumableProperties(Consumable consumable, boolean canAlwaysEat, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) { // this is the duration of the use animation in ticks; note that in behavior packs this is set as a float in seconds, but over the network it is an int in ticks itemProperties.putInt("use_duration", (int) (consumable.consumeSeconds() * 20)); // TODO check and confirm - // this dictates that the item will use the eat or drink animation (in the first person) and play eat or drink sounds - // note that in behavior packs this is set as the string "eat" or "drink", but over the network it as an int, with these values being 1 and 2 respectively - itemProperties.putInt("use_animation", 0); // TODO + + itemProperties.putInt("use_animation", BEDROCK_ANIMATIONS.get(consumable.animation())); + // this component is required to allow the eat animation to play componentBuilder.putCompound("minecraft:food", NbtMap.builder().putBoolean("can_always_eat", canAlwaysEat).build()); }