From e51d7b3fdbf4ca98ce47d27d8d235ab54e379d9e Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Thu, 29 Feb 2024 16:05:30 +0100 Subject: [PATCH] Fix initial item sending, add some data translation and more rigid data structures --- .../viaversion/api/data/MappingData.java | 6 +- .../api/minecraft/data/StructuredData.java | 23 ++-- .../data/StructuredDataContainer.java | 120 ++++++++++++++++ .../api/minecraft/data/StructuredDataKey.java | 105 ++++++++++++++ .../api/minecraft/item/DataItem.java | 16 +-- .../viaversion/api/minecraft/item/Item.java | 10 +- .../api/minecraft/item/StructuredItem.java | 26 +--- .../api/minecraft/item/data/Enchantments.java | 6 +- .../api/type/types/item/ItemType1_20_5.java | 5 +- .../type/types/item/StructuredDataType.java | 24 ++-- .../packets/InventoryPackets.java | 7 +- .../Protocol1_20_5To1_20_3.java | 89 ++++++------ .../data/EnchantmentMappings.java | 83 +++++++++++ .../BlockItemPacketRewriter1_20_5.java | 130 +++++++++++++++--- .../viaversion/rewriter/EntityRewriter.java | 2 +- .../viaversion/rewriter/ItemRewriter.java | 22 +-- gradle/libs.versions.toml | 2 +- 17 files changed, 522 insertions(+), 154 deletions(-) create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java create mode 100644 common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/EnchantmentMappings.java diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java b/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java index 66389e99a..7a74752d7 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/MappingData.java @@ -109,13 +109,13 @@ public interface MappingData { @Nullable Mappings getAttributeMappings(); + @Nullable Mappings getPaintingMappings(); + @Nullable FullMappings getEntityMappings(); @Nullable FullMappings getArgumentTypeMappings(); @Nullable FullMappings getRecipeSerializerMappings(); - FullMappings getDataComponentSerializerMappings(); - - @Nullable Mappings getPaintingMappings(); + @Nullable FullMappings getDataComponentSerializerMappings(); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java index 9ea4e5821..d1f5afea1 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredData.java @@ -29,43 +29,38 @@ import io.netty.buffer.ByteBuf; public final class StructuredData implements IdHolder { - private final Type type; + private final StructuredDataKey key; private T value; private int id; - public StructuredData(final Type type, final T value, final int id) { - this.type = type; + public StructuredData(final StructuredDataKey key, final T value, final int id) { + this.key = key; this.value = value; this.id = id; } - public static StructuredData empty(final int id) { - // Indicates empty structures, whereas an empty optional is used to remove default values - return new StructuredData<>(Type.UNIT, Unit.INSTANCE, id); - } - public boolean isEmpty() { - return type == Type.UNIT; + return key.type() == Type.UNIT; } public void setValue(final T value) { - if (value != null && !type.getOutputClass().isAssignableFrom(value.getClass())) { - throw new IllegalArgumentException("Item data type and value are incompatible. Type=" + type + if (value != null && !key.type().getOutputClass().isAssignableFrom(value.getClass())) { + throw new IllegalArgumentException("Item data type and value are incompatible. Type=" + key + ", value=" + value + " (" + value.getClass().getSimpleName() + ")"); } this.value = value; } public void write(final ByteBuf buffer) throws Exception { - type.write(buffer, value); + key.type().write(buffer, value); } public void setId(final int id) { this.id = id; } - public Type type() { - return type; + public StructuredDataKey key() { + return key; } public T value() { diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java new file mode 100644 index 000000000..2b0fcf657 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataContainer.java @@ -0,0 +1,120 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.data; + +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.protocol.Protocol; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.util.Optional; + +public final class StructuredDataContainer { + + private final Int2ObjectMap>> data; // Bless Optionals in a map + + public StructuredDataContainer(final Int2ObjectMap>> data) { + this.data = data; + } + + public StructuredDataContainer() { + this(new Int2ObjectOpenHashMap<>()); + } + + /** + * Returns structured data by id. You might want to call {@link #contains(int)} first. + * + * @param id serializer id + * @param data type + * @return structured data + */ + public Optional> get(final int id) { + final Optional> data = this.data.getOrDefault(id, Optional.empty()); + //noinspection unchecked + return data.map(value -> (StructuredData) value); + } + + /** + * Returns structured data by id. You might want to call {@link #contains(Protocol, StructuredDataKey)} first. + * + * @param protocol protocol to retreive the id of the serializer from + * @param key serializer id + * @param data type + * @return structured data + */ + public Optional> get(final Protocol protocol, final StructuredDataKey key) { + final Optional> data = this.data.getOrDefault(serializerId(protocol, key), Optional.empty()); + //noinspection unchecked + return data.map(value -> (StructuredData) value); + } + + public void add(final StructuredData data) { + this.data.put(data.id(), Optional.of(data)); + } + + public void add(final Protocol protocol, final StructuredDataKey key, final T value) { + final int id = serializerId(protocol, key); + if (id != -1) { + add(new StructuredData<>(key, value, id)); + } + } + + public void addEmpty(final Protocol protocol, final StructuredDataKey key) { + final int id = serializerId(protocol, key); + if (id != -1) { + this.data.put(id, Optional.empty()); + } + } + + public void remove(final int id) { + this.data.remove(id); + } + + public void removeDefault(final int id) { + // Empty optional to override the Minecraft default + this.data.put(id, Optional.empty()); + } + + public boolean contains(final int id) { + return this.data.containsKey(id); + } + + public boolean contains(final Protocol protocol, final StructuredDataKey key) { + return this.data.containsKey(serializerId(protocol, key)); + } + + public StructuredDataContainer copy() { + return new StructuredDataContainer(new Int2ObjectOpenHashMap<>(data)); + } + + private int serializerId(final Protocol protocol, final StructuredDataKey key) { + final int id = protocol.getMappingData().getDataComponentSerializerMappings().mappedId(key.identifier()); + if (id == -1) { + Via.getPlatform().getLogger().severe("Could not find item data serializer for type " + key); + } + return id; + } + + public Int2ObjectMap>> data() { + return data; + } +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java new file mode 100644 index 000000000..1a266b8ca --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/data/StructuredDataKey.java @@ -0,0 +1,105 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.data; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.api.minecraft.item.data.BannerPattern; +import com.viaversion.viaversion.api.minecraft.item.data.Bee; +import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties; +import com.viaversion.viaversion.api.minecraft.item.data.DyedColor; +import com.viaversion.viaversion.api.minecraft.item.data.Enchantments; +import com.viaversion.viaversion.api.minecraft.item.data.GameProfile; +import com.viaversion.viaversion.api.minecraft.item.data.LodestoneTarget; +import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook; +import com.viaversion.viaversion.api.type.Type; +import com.viaversion.viaversion.api.type.types.version.Types1_20_5; +import com.viaversion.viaversion.util.Unit; + +public final class StructuredDataKey { + + public static final StructuredDataKey CUSTOM_DATA = new StructuredDataKey<>("custom_data", Type.COMPOUND_TAG); + public static final StructuredDataKey DAMAGE = new StructuredDataKey<>("damage", Type.VAR_INT); + public static final StructuredDataKey UNBREAKABLE = new StructuredDataKey<>("unbreakable", Type.BOOLEAN); + public static final StructuredDataKey CUSTOM_NAME = new StructuredDataKey<>("custom_name", Type.TAG); + public static final StructuredDataKey LORE = new StructuredDataKey<>("lore", Type.TAG_ARRAY); + public static final StructuredDataKey ENCHANTMENTS = new StructuredDataKey<>("enchantments", Enchantments.TYPE); + public static final StructuredDataKey CAN_PLACE_ON = new StructuredDataKey<>("can_place_on", Type.UNIT); // TODO + public static final StructuredDataKey CAN_BREAK = new StructuredDataKey<>("can_break", Type.UNIT); // TODO + public static final StructuredDataKey ATTRIBUTE_MODIFIERS = new StructuredDataKey<>("attribute_modifiers", Type.UNIT); // TODO + public static final StructuredDataKey CUSTOM_MODEL_DATA = new StructuredDataKey<>("custom_model_data", Type.VAR_INT); + public static final StructuredDataKey HIDE_ADDITIONAL_TOOLTIP = new StructuredDataKey<>("hide_additional_tooltip", Type.UNIT); + public static final StructuredDataKey REPAIR_COST = new StructuredDataKey<>("repair_cost", Type.VAR_INT); + public static final StructuredDataKey CREATIVE_SLOT_LOCK = new StructuredDataKey<>("creative_slot_lock", Type.UNIT); + public static final StructuredDataKey ENCHANTMENT_GLINT_OVERRIDE = new StructuredDataKey<>("enchantment_glint_override", Type.BOOLEAN); + public static final StructuredDataKey INTANGIBLE_PROJECTILE = new StructuredDataKey<>("intangible_projectile", Type.UNIT); + public static final StructuredDataKey STORED_ENCHANTMENTS = new StructuredDataKey<>("storded_enchantments", Enchantments.TYPE); + public static final StructuredDataKey DYED_COLOR = new StructuredDataKey<>("dyed_color", DyedColor.TYPE); + public static final StructuredDataKey MAP_COLOR = new StructuredDataKey<>("map_color", Type.INT); + public static final StructuredDataKey MAP_ID = new StructuredDataKey<>("map_id", Type.VAR_INT); + public static final StructuredDataKey MAP_DECORATIONS = new StructuredDataKey<>("map_decorations", Type.COMPOUND_TAG); + public static final StructuredDataKey MAP_POST_PROCESSING = new StructuredDataKey<>("map_post_processing", Type.VAR_INT); + public static final StructuredDataKey CHARGED_PROJECTILES = new StructuredDataKey<>("charged_projectiles", Types1_20_5.ITEM_ARRAY); + public static final StructuredDataKey BUNDLE_CONTENTS = new StructuredDataKey<>("bundle_contents", Types1_20_5.ITEM_ARRAY); + public static final StructuredDataKey POTION_CONTENTS = new StructuredDataKey<>("potion_contents", Type.UNIT); // TODO + public static final StructuredDataKey SUSPICIOUS_STEW_EFFECTS = new StructuredDataKey<>("suspicious_stew_effects", Type.UNIT); // TODO + public static final StructuredDataKey WRITABLE_BOOK_CONTENT = new StructuredDataKey<>("writable_book_content", Type.STRING_ARRAY); + public static final StructuredDataKey WRITTEN_BOOK_CONTENT = new StructuredDataKey<>("written_book_content", WrittenBook.TYPE); + public static final StructuredDataKey TRIM = new StructuredDataKey<>("trim", Type.UNIT); // TODO + public static final StructuredDataKey DEBUG_STICK_STATE = new StructuredDataKey<>("debug_stick_state", Type.COMPOUND_TAG); + public static final StructuredDataKey ENTITY_DATA = new StructuredDataKey<>("entity_data", Type.COMPOUND_TAG); + public static final StructuredDataKey BUCKET_ENTITY_DATA = new StructuredDataKey<>("bucket_entity_data", Type.COMPOUND_TAG); + public static final StructuredDataKey BLOCK_ENTITY_DATA = new StructuredDataKey<>("block_entity_data", Type.COMPOUND_TAG); + public static final StructuredDataKey INSTRUMENT = new StructuredDataKey<>("instrument", Type.UNIT); // TODO + public static final StructuredDataKey RECIPES = new StructuredDataKey<>("recipes", Type.STRING_ARRAY); + public static final StructuredDataKey LODESTONE_TARGET = new StructuredDataKey<>("lodestone_target", LodestoneTarget.TYPE); + public static final StructuredDataKey FIREWORK_EXPLOSION = new StructuredDataKey<>("firework_explosion", Type.UNIT); // TODO + public static final StructuredDataKey FIREWORKS = new StructuredDataKey<>("fireworks", Type.UNIT); // TODO + public static final StructuredDataKey PROFILE = new StructuredDataKey<>("profile", GameProfile.TYPE); + public static final StructuredDataKey NOTE_BLOCK_SOUND = new StructuredDataKey<>("note_block_sound", Type.STRING); + public static final StructuredDataKey BANNER_PATTERNS = new StructuredDataKey<>("banner_patterns", BannerPattern.ARRAY_TYPE); + public static final StructuredDataKey BASE_COLOR = new StructuredDataKey<>("base_color", Type.VAR_INT); + public static final StructuredDataKey POT_DECORATIONS = new StructuredDataKey<>("pot_decorations", Type.VAR_INT_ARRAY_PRIMITIVE); + public static final StructuredDataKey CONTAINER = new StructuredDataKey<>("container", Types1_20_5.ITEM_ARRAY); + public static final StructuredDataKey BLOCK_STATE = new StructuredDataKey<>("block_state", BlockStateProperties.TYPE); + public static final StructuredDataKey BEES = new StructuredDataKey<>("bees", Bee.ARRAY_TYPE); + public static final StructuredDataKey LOCK = new StructuredDataKey<>("lock", Type.COMPOUND_TAG); + public static final StructuredDataKey CONTAINER_LOOT = new StructuredDataKey<>("container_loot", Type.COMPOUND_TAG); + + private final String identifier; + private final Type type; + + public StructuredDataKey(final String identifier, Type type) { + this.identifier = identifier; + this.type = type; + } + + public Type type() { + return type; + } + + public String identifier() { + return identifier; + } +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/DataItem.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/DataItem.java index 8daa7374e..dfc7c2f03 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/DataItem.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/DataItem.java @@ -24,10 +24,8 @@ package com.viaversion.viaversion.api.minecraft.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.google.gson.annotations.SerializedName; -import com.viaversion.viaversion.api.minecraft.data.StructuredData; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; import java.util.Objects; -import java.util.Optional; import org.checkerframework.checker.nullness.qual.Nullable; public class DataItem implements Item { @@ -95,17 +93,7 @@ public class DataItem implements Item { } @Override - public Int2ObjectMap>> structuredData() { - throw new UnsupportedOperationException(); - } - - @Override - public void addData(final StructuredData data) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeDefaultData(final int id) { + public StructuredDataContainer structuredData() { throw new UnsupportedOperationException(); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/Item.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/Item.java index 7022e7573..2990b4315 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/Item.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/Item.java @@ -23,9 +23,7 @@ package com.viaversion.viaversion.api.minecraft.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.viaversion.viaversion.api.minecraft.data.StructuredData; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import java.util.Optional; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; import org.checkerframework.checker.nullness.qual.Nullable; public interface Item { @@ -91,11 +89,7 @@ public interface Item { */ void setTag(@Nullable CompoundTag tag); - Int2ObjectMap>> structuredData(); - - void addData(StructuredData data); - - void removeDefaultData(int id); + StructuredDataContainer structuredData(); /** * Returns a copy of the item. diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/StructuredItem.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/StructuredItem.java index 5b5c23a39..2fa028e03 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/StructuredItem.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/StructuredItem.java @@ -23,22 +23,19 @@ package com.viaversion.viaversion.api.minecraft.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.viaversion.viaversion.api.minecraft.data.StructuredData; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.util.Optional; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; import org.checkerframework.checker.nullness.qual.Nullable; public class StructuredItem implements Item { - private final Int2ObjectMap>> data; + private final StructuredDataContainer data; private int identifier; private byte amount; public StructuredItem() { - this(0, (byte) 0, new Int2ObjectOpenHashMap<>()); + this(0, (byte) 0, new StructuredDataContainer()); } - public StructuredItem(final int identifier, final byte amount, final Int2ObjectMap>> data) { + public StructuredItem(final int identifier, final byte amount, final StructuredDataContainer data) { this.identifier = identifier; this.amount = amount; this.data = data; @@ -78,24 +75,13 @@ public class StructuredItem implements Item { } @Override - public Int2ObjectMap>> structuredData() { + public StructuredDataContainer structuredData() { return data; } - @Override - public void addData(final StructuredData data) { - this.data.put(data.id(), Optional.of(data)); - } - - @Override - public void removeDefaultData(final int id) { - // Empty optional to override the Minecraft default - this.data.put(id, Optional.empty()); - } - @Override public StructuredItem copy() { - return new StructuredItem(identifier, amount, new Int2ObjectOpenHashMap<>(data)); + return new StructuredItem(identifier, amount, data.copy()); } @Override diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Enchantments.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Enchantments.java index f0bce4ed5..87180420e 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Enchantments.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Enchantments.java @@ -47,7 +47,7 @@ public final class Enchantments { public void write(final ByteBuf buffer, final Enchantments value) { Type.VAR_INT.writePrimitive(buffer, value.enchantments.size()); for (final Int2IntMap.Entry entry : value.enchantments.int2IntEntrySet()) { - Type.VAR_INT.writePrimitive(buffer, entry.getIntValue()); + Type.VAR_INT.writePrimitive(buffer, entry.getIntKey()); Type.VAR_INT.writePrimitive(buffer, entry.getIntValue()); } buffer.writeBoolean(value.showInTooltip()); @@ -66,6 +66,10 @@ public final class Enchantments { return enchantments; } + public int size() { + return enchantments.size(); + } + public boolean showInTooltip() { return showInTooltip; } diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemType1_20_5.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemType1_20_5.java index a82e3feae..b1773d41a 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemType1_20_5.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemType1_20_5.java @@ -22,6 +22,7 @@ */ package com.viaversion.viaversion.api.type.types.item; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.minecraft.data.StructuredData; import com.viaversion.viaversion.api.minecraft.item.StructuredItem; @@ -50,7 +51,7 @@ public class ItemType1_20_5 extends Type { final int id = Type.VAR_INT.readPrimitive(buffer); final Int2ObjectMap>> data = readData(buffer); - return new StructuredItem(id, amount, data); + return new StructuredItem(id, amount, new StructuredDataContainer(data)); } private Int2ObjectMap>> readData(final ByteBuf buffer) throws Exception { @@ -83,7 +84,7 @@ public class ItemType1_20_5 extends Type { buffer.writeByte(object.amount()); Type.VAR_INT.writePrimitive(buffer, object.identifier()); - final Int2ObjectMap>> data = object.structuredData(); + final Int2ObjectMap>> data = object.structuredData().data(); int valuesSize = 0; int markersSize = 0; for (final Int2ObjectMap.Entry>> entry : data.int2ObjectEntrySet()) { diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/item/StructuredDataType.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/StructuredDataType.java index 522e1d8ae..6ef84b73f 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/item/StructuredDataType.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/StructuredDataType.java @@ -24,6 +24,7 @@ package com.viaversion.viaversion.api.type.types.item; import com.viaversion.viaversion.api.data.FullMappings; import com.viaversion.viaversion.api.minecraft.data.StructuredData; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; import com.viaversion.viaversion.api.protocol.Protocol; import com.viaversion.viaversion.api.type.Type; import io.netty.buffer.ByteBuf; @@ -32,7 +33,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public class StructuredDataType extends Type> { - private final Int2ObjectMap> types = new Int2ObjectOpenHashMap<>(); + private final Int2ObjectMap> types = new Int2ObjectOpenHashMap<>(); public StructuredDataType() { super(StructuredData.class); @@ -47,15 +48,15 @@ public class StructuredDataType extends Type> { @Override public StructuredData read(final ByteBuf buffer) throws Exception { final int id = Type.VAR_INT.readPrimitive(buffer); - final Type type = this.types.get(id); - if (type != null) { - return readData(buffer, type, id); + final StructuredDataKey key = this.types.get(id); + if (key != null) { + return readData(buffer, key, id); } - throw new IllegalArgumentException("Unknown item data type id: " + id); + throw new IllegalArgumentException("No data component serializer found for id " + id); } - private StructuredData readData(final ByteBuf buffer, final Type type, final int id) throws Exception { - return new StructuredData<>(type, type.read(buffer), id); + private StructuredData readData(final ByteBuf buffer, final StructuredDataKey key, final int id) throws Exception { + return new StructuredData<>(key, key.type().read(buffer), id); } public DataFiller filler(final Protocol protocol) { @@ -76,13 +77,8 @@ public class StructuredDataType extends Type> { this.useMappedNames = useMappedNames; } - public DataFiller reader(final String identifier, final Type reader) { - types.put(useMappedNames ? mappings.mappedId(identifier) : mappings.id(identifier), reader); - return this; - } - - public DataFiller reader(final int id, final Type type) { - types.put(id, type); + public DataFiller add(final StructuredDataKey reader) { + types.put(useMappedNames ? mappings.mappedId(reader.identifier()) : mappings.id(reader.identifier()), reader); return this; } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/InventoryPackets.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/InventoryPackets.java index 21a492e28..7f484f840 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/InventoryPackets.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13to1_12_2/packets/InventoryPackets.java @@ -71,7 +71,12 @@ public class InventoryPackets extends ItemRewriter { + Item[] items = wrapper.get(Type.ITEM1_13_SHORT_ARRAY, 0); + for (Item item : items) { + handleItemToClient(item); + } + }); } }); protocol.registerClientbound(ClientboundPackets1_12_1.WINDOW_PROPERTY, new PacketHandlers() { diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/Protocol1_20_5To1_20_3.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/Protocol1_20_5To1_20_3.java index 975848357..74967e577 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/Protocol1_20_5To1_20_3.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/Protocol1_20_5To1_20_3.java @@ -21,6 +21,7 @@ import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.data.MappingDataBase; import com.viaversion.viaversion.api.minecraft.RegistryType; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_20_5; import com.viaversion.viaversion.api.minecraft.item.data.BannerPattern; import com.viaversion.viaversion.api.minecraft.item.data.Bee; @@ -120,47 +121,53 @@ public final class Protocol1_20_5To1_20_3 extends AbstractProtocol. + */ +package com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.data; + +import com.viaversion.viaversion.util.Key; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class EnchantmentMappings { + + public static final String[] ENCHANTMENTS = { + "protection", + "fire_protection", + "feather_falling", + "blast_protection", + "projectile_protection", + "respiration", + "aqua_affinity", + "thorns", + "depth_strider", + "frost_walker", + "binding_curse", + "soul_speed", + "swift_sneak", + "sharpness", + "smite", + "bane_of_arthropods", + "knockback", + "fire_aspect", + "looting", + "sweeping", + "efficiency", + "silk_touch", + "unbreaking", + "fortune", + "power", + "punch", + "flame", + "infinity", + "luck_of_the_sea", + "lure", + "loyalty", + "impaling", + "riptide", + "channeling", + "multishot", + "quick_charge", + "piercing", + "mending", + "vanishing_curse" + }; + private static final Object2IntMap STRING_TO_ID = new Object2IntOpenHashMap<>(); + + static { + for (int i = 0; i < ENCHANTMENTS.length; i++) { + STRING_TO_ID.put(ENCHANTMENTS[i], i); + } + } + + public static @Nullable String enchantment(final int id) { + return id >= 0 && id < ENCHANTMENTS.length ? ENCHANTMENTS[id] : null; + } + + public static int id(final String attribute) { + return STRING_TO_ID.getOrDefault(Key.stripMinecraftNamespace(attribute), -1); + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java index e1a8e5445..021946df1 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java @@ -18,14 +18,20 @@ package com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.rewriter; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.NumberTag; -import com.viaversion.viaversion.api.Via; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.viaversion.viaversion.api.data.ParticleMappings; import com.viaversion.viaversion.api.minecraft.Particle; import com.viaversion.viaversion.api.minecraft.data.StructuredData; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; import com.viaversion.viaversion.api.minecraft.item.DataItem; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.minecraft.item.StructuredItem; +import com.viaversion.viaversion.api.minecraft.item.data.BlockStateProperties; +import com.viaversion.viaversion.api.minecraft.item.data.Enchantments; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_20_2; import com.viaversion.viaversion.api.type.types.version.Types1_20_3; @@ -34,12 +40,16 @@ import com.viaversion.viaversion.protocols.protocol1_20_3to1_20_2.packet.Clientb import com.viaversion.viaversion.protocols.protocol1_20_3to1_20_2.packet.ClientboundPackets1_20_3; import com.viaversion.viaversion.protocols.protocol1_20_3to1_20_2.rewriter.RecipeRewriter1_20_3; import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.Protocol1_20_5To1_20_3; +import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.data.EnchantmentMappings; import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.ServerboundPacket1_20_5; import com.viaversion.viaversion.protocols.protocol1_20_5to1_20_3.packet.ServerboundPackets1_20_5; import com.viaversion.viaversion.rewriter.BlockRewriter; import com.viaversion.viaversion.rewriter.ItemRewriter; import com.viaversion.viaversion.util.Key; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import org.checkerframework.checker.nullness.qual.Nullable; public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter { @@ -186,44 +196,128 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter> customData = data.get(protocol, StructuredDataKey.CUSTOM_DATA); + + // Start out with custom data and add the rest on top + final CompoundTag tag = customData.map(StructuredData::value).orElse(new CompoundTag()); + // TODO + return new DataItem(item.identifier(), (byte) item.amount(), (short) 0, tag); } - public static Item toStructuredItem(final Item old) { + public Item toStructuredItem(final Item old) { final CompoundTag tag = old.tag(); - final StructuredItem item = new StructuredItem(old.identifier(), (byte) old.amount(), new Int2ObjectOpenHashMap<>()); + final StructuredItem item = new StructuredItem(old.identifier(), (byte) old.amount(), new StructuredDataContainer()); + final StructuredDataContainer data = item.structuredData(); if (tag == null) { return item; } // Rewrite nbt to new data structures final NumberTag damage = tag.getNumberTag("Damage"); - if (damage != null) { - addData(item, "damage", Type.VAR_INT, damage.asInt()); + if (damage != null && damage.asInt() != 0) { + tag.remove("Damage"); + data.add(protocol, StructuredDataKey.DAMAGE, damage.asInt()); } final NumberTag repairCost = tag.getNumberTag("RepairCost"); - if (repairCost != null) { - addData(item, "repair_cost", Type.VAR_INT, repairCost.asInt()); + if (repairCost != null && repairCost.asInt() != 0) { + tag.remove("RepairCost"); + data.add(protocol, StructuredDataKey.REPAIR_COST, repairCost.asInt()); } + + final NumberTag customModelData = tag.getNumberTag("CustomModelData"); + if (customModelData != null) { + tag.remove("CustomModelData"); + data.add(protocol, StructuredDataKey.CUSTOM_MODEL_DATA, customModelData.asInt()); + } + + final CompoundTag blockState = tag.getCompoundTag("BlockStateTag"); + if (blockState != null) { + tag.remove("BlockStateTag"); + final Map properties = new HashMap<>(); + for (final Map.Entry entry : blockState.entrySet()) { + if (entry.getValue() instanceof StringTag) { + properties.put(entry.getKey(), ((StringTag) entry.getValue()).getValue()); + } + } + data.add(protocol, StructuredDataKey.BLOCK_STATE, new BlockStateProperties(properties)); + } + + final CompoundTag entityTag = tag.getCompoundTag("EntityTag"); + if (entityTag != null) { + tag.remove("EntityTag"); + data.add(protocol, StructuredDataKey.ENTITY_DATA, entityTag); + } + + final CompoundTag blockEntityTag = tag.getCompoundTag("BlockEntityTag"); + if (blockEntityTag != null) { + // TODO lots of stuff + // item.structuredData().add(protocol, "block_entity_data", Type.COMPOUND_TAG, blockEntityTag); + } + + final NumberTag hideFlags = tag.getNumberTag("HideFlags"); + final int hideFlagsValue = hideFlags != null ? hideFlags.asInt() : 0; + tag.remove("HideFlags"); + + final NumberTag unbreakable = tag.getNumberTag("Unbreakable"); + if (unbreakable != null && unbreakable.asBoolean()) { + tag.remove("Unbreakable"); + if ((hideFlagsValue & 0x04) != 0) { + data.add(protocol, StructuredDataKey.UNBREAKABLE, true); // TODO Value is hide, should have a wrapper + } else { + data.addEmpty(protocol, StructuredDataKey.UNBREAKABLE); + } + } + + updateEnchantments(data, tag, "Enchantments", StructuredDataKey.ENCHANTMENTS, (hideFlagsValue & 0x01) != 0); + updateEnchantments(data, tag, "StoredEnchantments", StructuredDataKey.STORED_ENCHANTMENTS, (hideFlagsValue & 0x20) != 0); + // TODO + + // Add the rest as custom data + data.add(protocol, StructuredDataKey.CUSTOM_DATA, tag); return item; } - private static void addData(final StructuredItem item, final String serializer, final Type type, final T value) { - final int id = serializerId(serializer); - if (id == -1) { - Via.getPlatform().getLogger().severe("Could not find item data serializer for type " + type); + private void updateEnchantments(final StructuredDataContainer data, final CompoundTag tag, final String key, + final StructuredDataKey newKey, final boolean hide) { + final ListTag enchantmentsTag = tag.getListTag(key); + if (enchantmentsTag == null) { return; } - item.addData(new StructuredData<>(type, value, id)); - } + tag.remove(key); - private static int serializerId(final String type) { - return Protocol1_20_5To1_20_3.MAPPINGS.getDataComponentSerializerMappings().mappedId(type); + final Enchantments enchantments = new Enchantments(new Int2IntOpenHashMap(), !hide); + for (final Tag enchantment : enchantmentsTag) { + if (!(enchantment instanceof CompoundTag)) { + continue; + } + + final CompoundTag compound = (CompoundTag) enchantment; + final StringTag id = compound.getStringTag("id"); + final NumberTag lvl = compound.getNumberTag("lvl"); + if (id == null || lvl == null) { + continue; + } + + final int intId = EnchantmentMappings.id(id.getValue()); + if (intId == -1) { + continue; + } + + enchantments.enchantments().put(intId, lvl.asInt()); + } + + data.add(protocol, newKey, enchantments); + + // Add glint if none of the enchantments were valid + if (enchantments.size() == 0 && !enchantmentsTag.isEmpty()) { + data.add(protocol, StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true); + } } } \ No newline at end of file diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java index 4eb5fcf90..ab6c66626 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/EntityRewriter.java @@ -214,7 +214,7 @@ public abstract class EntityRewriter { final MetaType type = meta.metaType(); if (type == itemType) { - protocol.getItemRewriter().handleItemToClient(meta.value()); + meta.setValue(protocol.getItemRewriter().handleItemToClient(meta.value())); } else if (type == blockStateType) { int data = meta.value(); meta.setValue(protocol.getMappingData().getNewBlockStateId(data)); diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java index 8d204510c..c29e5a5ec 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java @@ -78,8 +78,8 @@ public class ItemRewriter { Item[] items = wrapper.read(itemArrayType); wrapper.write(mappedItemArrayType, items); - for (Item item : items) { - handleItemToClient(item); + for (int i = 0; i < items.length; i++) { + items[i] = handleItemToClient(items[i]); } }); } @@ -95,8 +95,8 @@ public class ItemRewriter { Item[] items = wrapper.read(itemArrayType); wrapper.write(mappedItemArrayType, items); - for (Item item : items) { - handleItemToClient(item); + for (int i = 0; i < items.length; i++) { + items[i] = handleItemToClient(items[i]); } handleClientboundItem(wrapper); @@ -519,15 +519,6 @@ public class ItemRewriter type) { - return wrapper -> { - Item[] items = wrapper.get(type, 0); - for (Item item : items) { - handleItemToClient(item); - } - }; - } - private void handleClientboundItem(final PacketWrapper wrapper) throws Exception { final Item item = handleItemToClient(wrapper.read(itemType)); wrapper.write(mappedItemType, item); @@ -540,14 +531,13 @@ public class ItemRewriter data = particle.getArgument(0); data.setValue(protocol.getMappingData().getNewBlockStateId(data.getValue())); } else if (mappings.isItemParticle(id)) { Particle.ParticleData data = particle.getArgument(0); - Item item = data.getValue(); - handleItemToClient(item); + data.setValue(handleItemToClient(data.getValue())); } particle.setId(protocol.getMappingData().getNewParticleId(id)); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ca515d782..656c55659 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ metadata.format.version = "1.1" gson = "2.10.1" fastutil = "8.5.12" -vianbt = "4.2.0" +vianbt = "4.3.0" mcstructs = "2.4.2-SNAPSHOT" # Common provided