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 index e0b8afbe0..0808cbecd 100644 --- 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 @@ -59,6 +59,7 @@ import com.viaversion.viaversion.api.minecraft.item.data.UseCooldown; import com.viaversion.viaversion.api.minecraft.item.data.WrittenBook; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.api.type.types.ArrayType; import com.viaversion.viaversion.api.type.types.version.Types1_20_5; import com.viaversion.viaversion.api.type.types.version.Types1_21; import com.viaversion.viaversion.api.type.types.version.Types1_21_2; @@ -74,7 +75,7 @@ public record StructuredDataKey(String identifier, Type type) { public static final StructuredDataKey CUSTOM_NAME = new StructuredDataKey<>("custom_name", Types.TAG); public static final StructuredDataKey ITEM_NAME = new StructuredDataKey<>("item_name", Types.TAG); public static final StructuredDataKey ITEM_MODEL = new StructuredDataKey<>("item_model", Types.STRING); - public static final StructuredDataKey LORE = new StructuredDataKey<>("lore", Types.TAG_ARRAY); + public static final StructuredDataKey LORE = new StructuredDataKey<>("lore", new ArrayType<>(Types.TAG, 256)); public static final StructuredDataKey RARITY = new StructuredDataKey<>("rarity", Types.VAR_INT); public static final StructuredDataKey ENCHANTMENTS = new StructuredDataKey<>("enchantments", Enchantments.TYPE); public static final StructuredDataKey CAN_PLACE_ON = new StructuredDataKey<>("can_place_on", AdventureModePredicate.TYPE); @@ -140,7 +141,7 @@ public record StructuredDataKey(String identifier, Type type) { public static final StructuredDataKey POT_DECORATIONS = new StructuredDataKey<>("pot_decorations", PotDecorations.TYPE); public static final StructuredDataKey CONTAINER1_20_5 = new StructuredDataKey<>("container", Types1_20_5.ITEM_ARRAY); public static final StructuredDataKey CONTAINER1_21 = new StructuredDataKey<>("container", Types1_21.ITEM_ARRAY); - public static final StructuredDataKey CONTAINER1_21_2 = new StructuredDataKey<>("container", Types1_21_2.ITEM_ARRAY); + public static final StructuredDataKey CONTAINER1_21_2 = new StructuredDataKey<>("container", new ArrayType<>(Types1_21_2.ITEM, 256)); 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", Types.TAG); diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/Types.java b/api/src/main/java/com/viaversion/viaversion/api/type/Types.java index d77d3c1c3..3696f29d5 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/Types.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/Types.java @@ -149,6 +149,8 @@ public final class Types { public static final VarLongType VAR_LONG = new VarLongType(); /* MC Types */ + public static final Type SERVERBOUND_CUSTOM_PAYLOAD_DATA = new RemainingBytesType(Short.MAX_VALUE); + public static final Type BLOCK_POSITION1_8 = new BlockPositionType1_8(); public static final Type OPTIONAL_POSITION1_8 = new BlockPositionType1_8.OptionalBlockPositionType(); public static final Type BLOCK_POSITION1_14 = new BlockPositionType1_14(); diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/ArrayType.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/ArrayType.java index 6f1970e53..51ec67ca7 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/ArrayType.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/ArrayType.java @@ -26,13 +26,22 @@ import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Types; import io.netty.buffer.ByteBuf; import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; public class ArrayType extends Type { private final Type elementType; + private final int maxLength; public ArrayType(Type type) { + this(type, -1); + } + + public ArrayType(Type type, int maxLength) { + //noinspection unchecked super(type.getTypeName() + " Array", (Class) getArrayClass(type.getOutputClass())); this.elementType = type; + this.maxLength = maxLength; } public static Class getArrayClass(Class componentType) { @@ -43,16 +52,40 @@ public class ArrayType extends Type { @Override public T[] read(ByteBuf buffer) { int amount = Types.VAR_INT.readPrimitive(buffer); - T[] array = (T[]) Array.newInstance(elementType.getOutputClass(), amount); + if (maxLength != -1 && amount > maxLength) { + throw new IllegalArgumentException("Array length " + amount + " is longer than maximum " + maxLength); + } - for (int i = 0; i < amount; i++) { + return amount < Short.MAX_VALUE ? readArray(buffer, amount) : readList(buffer, amount); + } + + private T[] readArray(ByteBuf buffer, int length) { + T[] array = createArray(length); + for (int i = 0; i < length; i++) { array[i] = elementType.read(buffer); } return array; } + private T[] readList(ByteBuf buffer, int length) { + List list = new ArrayList<>(); + for (int i = 0; i < length; i++) { + list.add(elementType.read(buffer)); + } + return list.toArray(createArray(0)); + } + + private T[] createArray(int length) { + //noinspection unchecked + return (T[]) Array.newInstance(elementType.getOutputClass(), length); + } + @Override public void write(ByteBuf buffer, T[] object) { + if (maxLength != -1 && object.length > maxLength) { + throw new IllegalArgumentException("Array length " + object.length + " is longer than maximum " + maxLength); + } + Types.VAR_INT.writePrimitive(buffer, object.length); for (T o : object) { elementType.write(buffer, o); diff --git a/api/src/main/java/com/viaversion/viaversion/api/type/types/RemainingBytesType.java b/api/src/main/java/com/viaversion/viaversion/api/type/types/RemainingBytesType.java index 212a0e114..998c73e3c 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/type/types/RemainingBytesType.java +++ b/api/src/main/java/com/viaversion/viaversion/api/type/types/RemainingBytesType.java @@ -26,19 +26,35 @@ import com.viaversion.viaversion.api.type.Type; import io.netty.buffer.ByteBuf; public class RemainingBytesType extends Type { + private final int maxLength; + public RemainingBytesType() { + this(-1); + } + + public RemainingBytesType(final int maxLength) { super(byte[].class); + this.maxLength = maxLength; } @Override - public byte[] read(ByteBuf buffer) { - byte[] array = new byte[buffer.readableBytes()]; + public byte[] read(final ByteBuf buffer) { + final int bytes = buffer.readableBytes(); + if (maxLength != -1 && bytes > maxLength) { + throw new RuntimeException("Remaining bytes cannot be longer than " + maxLength + " (got " + bytes + ")"); + } + + final byte[] array = new byte[bytes]; buffer.readBytes(array); return array; } @Override - public void write(ByteBuf buffer, byte[] object) { + public void write(final ByteBuf buffer, final byte[] object) { + if (maxLength != -1 && object.length > maxLength) { + throw new RuntimeException("Remaining bytes cannot be longer than " + maxLength + " (got " + object.length + ")"); + } + buffer.writeBytes(object); } } 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 89ef06f88..a713473e5 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 @@ -63,7 +63,7 @@ public class ItemType1_20_5 extends Type { return new Reference2ObjectOpenHashMap<>(); } - final Map, StructuredData> map = new Reference2ObjectOpenHashMap<>(); + final Map, StructuredData> map = new Reference2ObjectOpenHashMap<>(Math.min(valuesSize + markersSize, 128)); for (int i = 0; i < valuesSize; i++) { final StructuredData value = dataType.read(buffer); final StructuredDataKey key = dataType.key(value.id()); diff --git a/api/src/main/java/com/viaversion/viaversion/util/Limit.java b/api/src/main/java/com/viaversion/viaversion/util/Limit.java new file mode 100644 index 000000000..2251fd8a8 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/util/Limit.java @@ -0,0 +1,28 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.util; + +public final class Limit { + + public static int max(final int value, final int max) { + if (value > max) { + throw new IllegalArgumentException("Value " + value + " is higher than the maximum " + max); + } + return value; + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/ItemPacketRewriter1_13.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/ItemPacketRewriter1_13.java index 3c6e6be07..7a52f44b6 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/ItemPacketRewriter1_13.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_12_2to1_13/rewriter/ItemPacketRewriter1_13.java @@ -211,7 +211,7 @@ public class ItemPacketRewriter1_13 extends ItemRewriter rewrittenChannels = new ArrayList<>(); for (String s : channels) { String rewritten = getOldPluginChannelId(s); @@ -221,7 +221,7 @@ public class ItemPacketRewriter1_13 extends ItemRewriter checkedChannels = new ArrayList<>(channels.length); for (String registeredChannel : channels) { if (registeredChannel.length() > 32) { @@ -195,7 +195,7 @@ public class Protocol1_15_2To1_16 extends AbstractProtocol { @@ -88,13 +89,11 @@ public final class Protocol1_17To1_17_1 extends AbstractProtocol pagesTag = new ListTag<>(StringTag.class); for (int i = 0; i < pages; i++) { String page = wrapper.read(PAGE_STRING_TYPE); - if (i < 200) { // Apply network limit as per game code - pagesTag.add(new StringTag(page)); - } + pagesTag.add(new StringTag(page)); } // Legacy servers don't like an empty pages list diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java index 58319b783..2f9a58584 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_21to1_21_2/rewriter/BlockItemPacketRewriter1_21_2.java @@ -65,6 +65,7 @@ import com.viaversion.viaversion.rewriter.SoundRewriter; import com.viaversion.viaversion.rewriter.StructuredItemRewriter; import com.viaversion.viaversion.util.ComponentUtil; import com.viaversion.viaversion.util.Key; +import com.viaversion.viaversion.util.Limit; import com.viaversion.viaversion.util.SerializerVersion; import com.viaversion.viaversion.util.TagUtil; import com.viaversion.viaversion.util.Unit; @@ -154,7 +155,7 @@ public final class BlockItemPacketRewriter1_21_2 extends StructuredItemRewriter< wrapper.passthrough(Types.SHORT); // Slot wrapper.passthrough(Types.BYTE); // Button wrapper.passthrough(Types.VAR_INT); // Mode - final int length = wrapper.passthrough(Types.VAR_INT); + final int length = Limit.max(wrapper.passthrough(Types.VAR_INT), 128); for (int i = 0; i < length; i++) { wrapper.passthrough(Types.SHORT); // Slot passthroughServerboundItem(wrapper); 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 99d26f35f..ba7d4257c 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java @@ -32,6 +32,7 @@ import com.viaversion.viaversion.api.rewriter.ComponentRewriter; import com.viaversion.viaversion.api.rewriter.RewriterBase; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Types; +import com.viaversion.viaversion.util.Limit; import org.checkerframework.checker.nullness.qual.Nullable; public class ItemRewriter