From 1fe23e4aeb064653b418e053c2ddb7e8e1bcc56c Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Sun, 7 Apr 2024 13:36:02 +0200 Subject: [PATCH] Fix trade list handling --- .../data/StructuredDataContainer.java | 11 +++ .../viaversion/api/minecraft/item/Item.java | 12 ++- .../api/minecraft/item/StructuredItem.java | 4 +- .../type/types/item/ItemCostType1_20_5.java | 64 +++++++++++++++ .../api/type/types/version/Types1_20_5.java | 12 ++- .../Protocol1_13_2To1_13_1.java | 5 +- .../Protocol1_13To1_12_2.java | 5 +- .../BlockItemPacketRewriter1_20_2.java | 5 +- .../Protocol1_20_3To1_20_2.java | 6 +- .../BlockItemPacketRewriter1_20_5.java | 78 +++++++++++++++---- .../packets/InventoryPackets.java | 5 +- .../viaversion/rewriter/ItemRewriter.java | 33 ++++---- .../rewriter/BlockItemPacketRewriter1_99.java | 2 +- 13 files changed, 182 insertions(+), 60 deletions(-) create mode 100644 api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemCostType1_20_5.java 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 index 94d0119c2..27a219651 100644 --- 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 @@ -41,6 +41,13 @@ public final class StructuredDataContainer { this.data = data; } + public StructuredDataContainer(final StructuredData[] dataArray) { + this(new Reference2ObjectOpenHashMap<>(dataArray.length)); + for (final StructuredData data : dataArray) { + add(data); + } + } + public StructuredDataContainer() { this(new Reference2ObjectOpenHashMap<>()); } @@ -152,6 +159,10 @@ public final class StructuredDataContainer { return data; } + private void add(final StructuredData data) { + set(data.key(), data.value()); + } + @Override public String toString() { return "StructuredDataContainer{" + 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 2990b4315..f1dc2d4a5 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 @@ -80,7 +80,8 @@ public interface Item { * * @return item tag */ - @Nullable CompoundTag tag(); + @Nullable + CompoundTag tag(); /** * Sets the item compound tag. @@ -97,4 +98,13 @@ public interface Item { * @return copy of the item */ Item copy(); + + /** + * Returns true if the item is empty. + * + * @return true if the item is empty + */ + default boolean isEmpty() { + return identifier() == 0 || amount() <= 0; + } } 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 9ca8419f7..239c7e237 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 @@ -31,8 +31,8 @@ public class StructuredItem implements Item { private int identifier; private int amount; - public StructuredItem() { - this(0, 0, new StructuredDataContainer()); + public StructuredItem(final int identifier, final int amount) { + this(identifier, amount, new StructuredDataContainer()); } public StructuredItem(final int identifier, final int amount, final StructuredDataContainer data) { diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemCostType1_20_5.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemCostType1_20_5.java new file mode 100644 index 000000000..2f5e694b6 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/item/ItemCostType1_20_5.java @@ -0,0 +1,64 @@ +/* + * 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.type.types.item; + +import com.viaversion.viaversion.api.minecraft.data.StructuredData; +import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; +import com.viaversion.viaversion.api.minecraft.item.Item; +import com.viaversion.viaversion.api.minecraft.item.StructuredItem; +import com.viaversion.viaversion.api.type.OptionalType; +import com.viaversion.viaversion.api.type.Type; +import io.netty.buffer.ByteBuf; + +// Very similar to normal items (and just results in an item), except it allows non-positive amounts and has id/amount swapped because ??? +public final class ItemCostType1_20_5 extends Type { + + private final Type[]> dataArrayType; + + public ItemCostType1_20_5(final Type[]> dataArrayType) { + super(Item.class); + this.dataArrayType = dataArrayType; + } + + @Override + public Item read(final ByteBuf buffer) throws Exception { + final int id = Type.VAR_INT.readPrimitive(buffer); + final int amount = Type.VAR_INT.readPrimitive(buffer); + final StructuredData[] dataArray = dataArrayType.read(buffer); + return new StructuredItem(id, amount, new StructuredDataContainer(dataArray)); + } + + @Override + public void write(final ByteBuf buffer, final Item object) throws Exception { + Type.VAR_INT.writePrimitive(buffer, object.identifier()); + Type.VAR_INT.writePrimitive(buffer, object.amount()); + dataArrayType.write(buffer, object.structuredData().data().values().toArray(new StructuredData[0])); + } + + public static final class OptionalItemCostType extends OptionalType { + + public OptionalItemCostType(final Type type) { + super(type); + } + } +} \ No newline at end of file diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/version/Types1_20_5.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/version/Types1_20_5.java index fee0cd852..c11ad0bca 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/version/Types1_20_5.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/version/Types1_20_5.java @@ -23,11 +23,13 @@ package com.viaversion.viaversion.api.type.types.version; import com.viaversion.viaversion.api.minecraft.Particle; +import com.viaversion.viaversion.api.minecraft.data.StructuredData; import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.minecraft.metadata.Metadata; import com.viaversion.viaversion.api.minecraft.metadata.types.MetaTypes1_20_5; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.types.ArrayType; +import com.viaversion.viaversion.api.type.types.item.ItemCostType1_20_5; import com.viaversion.viaversion.api.type.types.item.ItemType1_20_5; import com.viaversion.viaversion.api.type.types.item.StructuredDataType; import com.viaversion.viaversion.api.type.types.metadata.MetaListType; @@ -35,14 +37,18 @@ import com.viaversion.viaversion.api.type.types.metadata.MetadataType; import com.viaversion.viaversion.api.type.types.misc.ParticleType; import java.util.List; +// Most of these are only safe to use after protocol loading public final class Types1_20_5 { - // Most of these are only safe to use after protocol loading - public static final ParticleType PARTICLE = new ParticleType(); - public static final ArrayType PARTICLES = new ArrayType<>(PARTICLE); public static final StructuredDataType STRUCTURED_DATA = new StructuredDataType(); + public static final Type[]> STRUCTURED_DATA_ARRAY = new ArrayType<>(STRUCTURED_DATA); public static final Type ITEM = new ItemType1_20_5(STRUCTURED_DATA); public static final Type ITEM_ARRAY = new ArrayType<>(ITEM); + public static final Type ITEM_COST = new ItemCostType1_20_5(STRUCTURED_DATA_ARRAY); + public static final Type OPTIONAL_ITEM_COST = new ItemCostType1_20_5.OptionalItemCostType(ITEM_COST); + + public static final ParticleType PARTICLE = new ParticleType(); + public static final ArrayType PARTICLES = new ArrayType<>(PARTICLE); public static final MetaTypes1_20_5 META_TYPES = new MetaTypes1_20_5(PARTICLE, PARTICLES); public static final Type METADATA = new MetadataType(META_TYPES); public static final Type> METADATA_LIST = new MetaListType(METADATA); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13_2to1_13_1/Protocol1_13_2To1_13_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13_2to1_13_1/Protocol1_13_2To1_13_1.java index 12e4fcac2..f56818a3e 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13_2to1_13_1/Protocol1_13_2To1_13_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_13_2to1_13_1/Protocol1_13_2To1_13_1.java @@ -52,10 +52,7 @@ public class Protocol1_13_2To1_13_1 extends AbstractProtocol { + wrapper.passthrough(Type.BOOLEAN); // Reset/clear + int size = wrapper.passthrough(Type.VAR_INT); // Mapping size + for (int i = 0; i < size; i++) { + wrapper.passthrough(Type.STRING); // Identifier + wrapper.passthrough(Type.OPTIONAL_STRING); // Parent + + // Display data + if (wrapper.passthrough(Type.BOOLEAN)) { + wrapper.passthrough(Type.TAG); // Title + wrapper.passthrough(Type.TAG); // Description + + Item item = handleNonNullItemToClient(wrapper.read(itemType())); + wrapper.write(mappedItemType(), item); + + wrapper.passthrough(Type.VAR_INT); // Frame type + int flags = wrapper.passthrough(Type.INT); // Flags + if ((flags & 1) != 0) { + wrapper.passthrough(Type.STRING); // Background texture + } + wrapper.passthrough(Type.FLOAT); // X + wrapper.passthrough(Type.FLOAT); // Y + } + + int requirements = wrapper.passthrough(Type.VAR_INT); + for (int array = 0; array < requirements; array++) { + wrapper.passthrough(Type.STRING_ARRAY); + } + + wrapper.passthrough(Type.BOOLEAN); // Send telemetry + } + }); + protocol.registerClientbound(ClientboundPackets1_20_3.SPAWN_PARTICLE, wrapper -> { final int particleId = wrapper.read(Type.VAR_INT); @@ -167,7 +199,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter dataKey) { + private void updateItemList(final StructuredDataContainer data, final CompoundTag tag, final String key, final StructuredDataKey dataKey, final boolean allowEmpty) { final ListTag itemsTag = tag.getListTag(key, CompoundTag.class); if (itemsTag != null) { - final Item[] items = itemsTag.stream().limit(256).map(this::itemFromTag).filter(Objects::nonNull).toArray(Item[]::new); + final Item[] items = itemsTag.stream() + .limit(256) + .map(this::itemFromTag) + .filter(Objects::nonNull) + .filter(item -> allowEmpty || !item.isEmpty()) + .toArray(Item[]::new); data.set(dataKey, items); } } @@ -1111,7 +1157,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter costType, final Type mappedCostType, + final Type optionalCostType, final Type mappedOptionalCostType + ) { protocol.registerClientbound(packetType, wrapper -> { wrapper.passthrough(Type.VAR_INT); // Container id int size = wrapper.passthrough(Type.VAR_INT); for (int i = 0; i < size; i++) { - handleClientboundItem(wrapper); // Input - handleClientboundItem(wrapper); // Output - handleClientboundItem(wrapper); // Second item + final Item input = wrapper.read(costType); + wrapper.write(mappedCostType, handleItemToClient(input)); - wrapper.passthrough(Type.BOOLEAN); // Trade disabled - wrapper.passthrough(Type.INT); // Number of tools uses + handleClientboundItem(wrapper); // Result + + final Item secondInput = wrapper.read(optionalCostType); + wrapper.write(mappedOptionalCostType, handleItemToClient(secondInput)); + + wrapper.passthrough(Type.BOOLEAN); // Out of stock + wrapper.passthrough(Type.INT); // Number of trade uses wrapper.passthrough(Type.INT); // Maximum number of trade uses wrapper.passthrough(Type.INT); // XP wrapper.passthrough(Type.INT); // Special price wrapper.passthrough(Type.FLOAT); // Price multiplier wrapper.passthrough(Type.INT); // Demand - wrapper.passthrough(Type.BOOLEAN); // Ignore tags } }); } @@ -307,10 +315,7 @@ public class ItemRewriter