diff --git a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/BedrockCodecHelper_v291.java b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/BedrockCodecHelper_v291.java index 13137450e..663638656 100644 --- a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/BedrockCodecHelper_v291.java +++ b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/BedrockCodecHelper_v291.java @@ -375,7 +375,6 @@ public O readOptional(ByteBuf buffer, O emptyValue, Function fun @Override public void writeOptional(ByteBuf buffer, Predicate isPresent, T object, BiConsumer consumer) { checkNotNull(consumer, "read consumer"); - boolean exists = isPresent.test(object); buffer.writeBoolean(exists); if (exists) { diff --git a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/serializer/AvailableCommandsSerializer_v291.java b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/serializer/AvailableCommandsSerializer_v291.java index 830af7728..43f996b19 100644 --- a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/serializer/AvailableCommandsSerializer_v291.java +++ b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v291/serializer/AvailableCommandsSerializer_v291.java @@ -258,11 +258,10 @@ protected CommandParamData readParameter(ByteBuf buffer, BedrockCodecHelper help param.setEnumData(enums.get(symbol & ~(ARG_FLAG_ENUM | ARG_FLAG_VALID))); } else { int parameterTypeId = symbol & ~ARG_FLAG_VALID; - CommandParam type = paramTypeMap.getType(parameterTypeId); + CommandParam type = paramTypeMap.getTypeUnsafe(parameterTypeId); if (type == null) { - throw new IllegalStateException("Invalid parameter type: " + parameterTypeId); + throw new IllegalStateException("Invalid parameter type: " + parameterTypeId + ", Symbol: " + symbol); } - param.setType(type); } } else { throw new IllegalStateException("No param type specified: " + param.getName()); diff --git a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v594/Bedrock_v594.java b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v594/Bedrock_v594.java index c258644a6..baf4b1e06 100644 --- a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v594/Bedrock_v594.java +++ b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/codec/v594/Bedrock_v594.java @@ -4,9 +4,11 @@ import org.cloudburstmc.protocol.bedrock.codec.EntityDataTypeMap; import org.cloudburstmc.protocol.bedrock.codec.v575.BedrockCodecHelper_v575; import org.cloudburstmc.protocol.bedrock.codec.v575.Bedrock_v575; +import org.cloudburstmc.protocol.bedrock.codec.v582.Bedrock_v582; import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589; import org.cloudburstmc.protocol.bedrock.codec.v594.serializer.AgentAnimationSerializer_v594; import org.cloudburstmc.protocol.bedrock.codec.v594.serializer.AvailableCommandsSerializer_v594; +import org.cloudburstmc.protocol.bedrock.data.command.CommandParam; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataFormat; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; @@ -30,13 +32,18 @@ public class Bedrock_v594 extends Bedrock_v589 { .update(EntityDataTypes.FLAGS_2, new FlagTransformer(ENTITY_FLAGS, 1)) .build(); + protected static final TypeMap COMMAND_PARAMS = Bedrock_v582.COMMAND_PARAMS.toBuilder() + .insert(134217728, CommandParam.CHAINED_COMMAND) + .build(); + + @SuppressWarnings("deprecation") public static final BedrockCodec CODEC = Bedrock_v589.CODEC.toBuilder() .raknetProtocolVersion(11) .protocolVersion(594) .minecraftVersion("1.20.10") .helper(() -> new BedrockCodecHelper_v575(ENTITY_DATA, GAME_RULE_TYPES, ITEM_STACK_REQUEST_TYPES, CONTAINER_SLOT_TYPES, PLAYER_ABILITIES, TEXT_PROCESSING_ORIGINS)) .deregisterPacket(ScriptCustomEventPacket.class) - .updateSerializer(AvailableCommandsPacket.class, new AvailableCommandsSerializer_v594(COMMAND_PARAMS)) // TODO: chained command deserialization needs more work + .updateSerializer(AvailableCommandsPacket.class, new AvailableCommandsSerializer_v594(COMMAND_PARAMS)) .registerPacket(AgentAnimationPacket::new, new AgentAnimationSerializer_v594(), 304) .build(); } diff --git a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParam.java b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParam.java index 175f9f387..23acced70 100644 --- a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParam.java +++ b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParam.java @@ -84,6 +84,7 @@ public class CommandParam { public static final CommandParam BLOCK_STATES_CONT = new CommandParam(CommandParamType.BLOCK_STATES_CONT); public static final CommandParam COMMAND = new CommandParam(CommandParamType.COMMAND); public static final CommandParam SLASH_COMMAND = new CommandParam(CommandParamType.SLASH_COMMAND); + public static final CommandParam CHAINED_COMMAND = new CommandParam(CommandParamType.CHAINED_COMMAND); private final CommandParamType paramType; private final int defaultValue; diff --git a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParamType.java b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParamType.java index 68eafaf52..b56182bfe 100644 --- a/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParamType.java +++ b/bedrock-codec/src/main/java/org/cloudburstmc/protocol/bedrock/data/command/CommandParamType.java @@ -76,5 +76,6 @@ public enum CommandParamType { BLOCK_STATES, BLOCK_STATES_CONT, // TODO: unknown - maybe count? COMMAND, - SLASH_COMMAND + SLASH_COMMAND, + CHAINED_COMMAND } diff --git a/common/src/main/java/org/cloudburstmc/protocol/common/util/TypeMap.java b/common/src/main/java/org/cloudburstmc/protocol/common/util/TypeMap.java index e345072c4..c7d14edcb 100644 --- a/common/src/main/java/org/cloudburstmc/protocol/common/util/TypeMap.java +++ b/common/src/main/java/org/cloudburstmc/protocol/common/util/TypeMap.java @@ -2,9 +2,7 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -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.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -17,7 +15,8 @@ import java.util.TreeMap; import java.util.function.BiConsumer; -import static org.cloudburstmc.protocol.common.util.Preconditions.*; +import static org.cloudburstmc.protocol.common.util.Preconditions.checkArgument; +import static org.cloudburstmc.protocol.common.util.Preconditions.checkNotNull; public final class TypeMap { @@ -51,6 +50,10 @@ public T getType(int id) { return value; } + public T getTypeUnsafe(int id) { + return toObject.get(id); + } + public Builder toBuilder() { Builder builder = new Builder<>(type); this.toObject.forEach(builder::insert); @@ -118,26 +121,12 @@ public static > TypeMap fromEnum(Class clazz, int maxInd @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public static class Builder { private final String type; - private Object[] types = new Object[0]; - - private void ensureIndex(int index) { - ensureCapacity(index + 1); - } - - private void ensureCapacity(int size) { - if (size > this.types.length) { - int newSize = powerOfTwoCeiling(size + 1); - Object[] newTypes = new Object[newSize]; - System.arraycopy(types, 0, newTypes, 0, this.types.length); - this.types = newTypes; - } - } + private final Int2ObjectAVLTreeMap types = new Int2ObjectAVLTreeMap<>(); public Builder insert(int index, T value) { checkNotNull(value, "value"); - this.ensureIndex(index); - checkArgument(this.types[index] == null, "Cannot insert into non-null value at index " + index); - this.types[index] = value; + checkArgument(this.types.get(index) == null, "Cannot insert into non-null value at index " + index); + this.types.put(index, value); return this; } @@ -149,28 +138,13 @@ public Builder insert(int index, T value) { * @return */ public Builder shift(int startIndex, int amount) { - return shift(startIndex, amount, this.types.length - startIndex); - } - - /** - * Shifts values from a specific start index - * - * @param startIndex - * @param amount - * @param length - * @return - */ - public Builder shift(int startIndex, int amount, int length) { - checkArgument(startIndex < this.types.length, "Start index is out of bounds"); - int endIndex = startIndex + length; - checkArgument(endIndex <= this.types.length, "Length exceeds array bounds"); - this.ensureCapacity(this.types.length + amount); - System.arraycopy(this.types, startIndex, this.types, startIndex + amount, length); - - // Clear old values - for (int i = 0; i < amount; i++) { - this.types[startIndex + i] = null; + Int2ObjectSortedMap shiftMap = types.tailMap(startIndex); + Int2ObjectArrayMap tmp = new Int2ObjectArrayMap<>(shiftMap.size()); + for (Int2ObjectMap.Entry entry : shiftMap.int2ObjectEntrySet()) { + tmp.put(entry.getIntKey() + amount, entry.getValue()); + types.put(entry.getIntKey(), null); } + types.putAll(tmp); return this; } @@ -184,19 +158,16 @@ public Builder shift(int startIndex, int amount, int length) { */ public Builder replace(int index, T value) { checkNotNull(value, "value"); - checkArgument(index < this.types.length, "Cannot update out of bounds value"); - checkArgument(this.types[index] != null, "Cannot update null value"); - this.types[index] = value; + checkArgument(this.types.get(index) != null, "Cannot update null value"); + this.types.put(index, value); return this; } public Builder update(int oldIndex, int newIndex, T value) { checkNotNull(value, "value"); - checkArgument(oldIndex < this.types.length, "Cannot update out of bounds value"); - checkArgument(this.types[oldIndex] == value, "oldIndex value does not equal expected"); - this.ensureIndex(newIndex); - this.types[oldIndex] = null; - this.types[newIndex] = value; + checkArgument(this.types.get(oldIndex) == value, "oldIndex value does not equal expected"); + this.types.remove(oldIndex); + this.types.put(newIndex, value); return this; } @@ -205,16 +176,13 @@ public Builder insert(int offset, TypeMap map) { map.toObject.forEach((index, value) -> { int newIndex = index + offset; checkNotNull(value, "value"); - this.ensureIndex(newIndex); - this.types[newIndex] = value; + this.types.put(newIndex, value); }); return this; } public Builder remove(int index) { - checkElementIndex(index, this.types.length); - // checkArgument(this.types[index] != null, "Cannot remove null value"); - this.types[index] = null; + this.types.remove(index); return this; } @@ -224,11 +192,11 @@ public TypeMap build() { toId.defaultReturnValue(-1); Int2ObjectMap toObject = new Int2ObjectOpenHashMap<>(); - for (int i = 0; i < this.types.length; i++) { - Object type = this.types[i]; + for (Int2ObjectMap.Entry entry : types.int2ObjectEntrySet()) { + Object type = entry.getValue(); if (type != null) { - toId.put((T) type, i); - toObject.put(i, (T) type); + toId.put((T) type, entry.getIntKey()); + toObject.put(entry.getIntKey(), (T) type); } } return new TypeMap<>(this.type, toId, toObject);