Skip to content

Commit

Permalink
Implement commands codec and mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Shevchik committed May 28, 2022
1 parent 1be3c57 commit 1c6039b
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,123 @@
package protocolsupport.protocol.packet.middle.base.clientbound.play;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import io.netty.buffer.ByteBuf;
import protocolsupport.protocol.codec.ArrayCodec;
import protocolsupport.protocol.codec.MiscDataCodec;
import protocolsupport.protocol.packet.middle.MiddlePacketCancelException;
import protocolsupport.protocol.codec.StringCodec;
import protocolsupport.protocol.codec.VarNumberCodec;
import protocolsupport.protocol.packet.middle.base.clientbound.ClientBoundMiddlePacket;
import protocolsupport.protocol.utils.ProtocolVersionsHelper;
import protocolsupport.protocol.types.command.CommandNode;
import protocolsupport.protocol.types.command.CommandNodeDoubleProperties;
import protocolsupport.protocol.types.command.CommandNodeEntityProperties;
import protocolsupport.protocol.types.command.CommandNodeIntegerProperties;
import protocolsupport.protocol.types.command.CommandNodeLongProperties;
import protocolsupport.protocol.types.command.CommandNodeProperties;
import protocolsupport.protocol.types.command.CommandNodeRangeProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceOrTagProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceProperties;
import protocolsupport.protocol.types.command.CommandNodeScoreHolderProperties;
import protocolsupport.protocol.types.command.CommandNodeSimpleProperties;
import protocolsupport.protocol.types.command.CommandNodeStringProperties;
import protocolsupport.utils.BitUtils;

public abstract class MiddleDeclareCommands extends ClientBoundMiddlePacket {

protected MiddleDeclareCommands(IMiddlePacketInit init) {
super(init);
}

//TODO: structure
protected ByteBuf data;
protected CommandNode[] nodes;
protected int rootNodeIndex;

@Override
protected void decode(ByteBuf serverdata) {
data = MiscDataCodec.readAllBytesSlice(serverdata);
nodes = ArrayCodec.readVarIntTArray(serverdata, CommandNode.class, MiddleDeclareCommands::readNode);
rootNodeIndex = VarNumberCodec.readVarInt(serverdata);
}

protected static final int NODE_FLAGS_TYPE_MASK = BitUtils.createIBitMaskFromBits(new int[] {0, 1});
protected static final int NODE_FLAGS_HAS_REDIRECT_BIT = 3;
protected static final int NODE_FLAGS_HAS_SUGGESTIONS_TYPE = 4;

protected static final int NODE_TYPE_ROOT = 0;
protected static final int NODE_TYPE_LITERAL = 1;
protected static final int NODE_TYPE_ARGUMENT = 2;

//TODO: remove after implementing
if (version.isBefore(ProtocolVersionsHelper.LATEST_PC)) {
throw MiddlePacketCancelException.INSTANCE;
protected static final Map<String, Function<ByteBuf, CommandNodeProperties>> propertiesDeserializer = new HashMap<>();
static {
propertiesDeserializer.put("brigadier:double", data -> {
int flags = data.readByte();
double min = BitUtils.isIBitSet(flags, 0) ? data.readDouble() : Double.MIN_VALUE;
double max = BitUtils.isIBitSet(flags, 1) ? data.readDouble() : Double.MAX_VALUE;
return new CommandNodeDoubleProperties(flags, min, max);
});
propertiesDeserializer.put("brigadier:float", data -> {
int flags = data.readByte();
float min = BitUtils.isIBitSet(flags, 0) ? data.readFloat() : Float.MIN_VALUE;
float max = BitUtils.isIBitSet(flags, 1) ? data.readFloat() : Float.MAX_VALUE;
return new CommandNodeDoubleProperties(flags, min, max);
});
propertiesDeserializer.put("brigadier:long", data -> {
int flags = data.readByte();
long min = BitUtils.isIBitSet(flags, 0) ? data.readLong() : Long.MIN_VALUE;
long max = BitUtils.isIBitSet(flags, 1) ? data.readLong() : Long.MAX_VALUE;
return new CommandNodeLongProperties(flags, min, max);
});
propertiesDeserializer.put("brigadier:integer", data -> {
int flags = data.readByte();
int min = BitUtils.isIBitSet(flags, 0) ? data.readInt() : Integer.MIN_VALUE;
int max = BitUtils.isIBitSet(flags, 1) ? data.readInt() : Integer.MAX_VALUE;
return new CommandNodeIntegerProperties(flags, min, max);
});
propertiesDeserializer.put("brigadier:string", data -> {
CommandNodeStringProperties.Type type = MiscDataCodec.readVarIntEnum(data, CommandNodeStringProperties.Type.CONSTANT_LOOKUP);
return new CommandNodeStringProperties(type);
});
propertiesDeserializer.put("minecraft:entity", data -> {
int flags = data.readByte();
return new CommandNodeEntityProperties(flags);
});
propertiesDeserializer.put("minecraft:score_holder", data -> {
int flags = data.readByte();
return new CommandNodeScoreHolderProperties(flags);
});
propertiesDeserializer.put("minecraft:range", data -> {
boolean allowDecimals = data.readBoolean();
return new CommandNodeRangeProperties(allowDecimals);
});
propertiesDeserializer.put("minecraft:resource_or_tag", data -> {
String identifier = StringCodec.readVarIntUTF8String(data);
return new CommandNodeResourceOrTagProperties(identifier);
});
propertiesDeserializer.put("minecraft:resource", data -> {
String identifier = StringCodec.readVarIntUTF8String(data);
return new CommandNodeResourceProperties(identifier);
});
}

protected static CommandNode readNode(ByteBuf data) {
byte flags = data.readByte();
int nodeType = flags & NODE_FLAGS_TYPE_MASK;
int[] childNodesIndexes = ArrayCodec.readVarIntVarIntArray(data);
int redirectNodeIndex = BitUtils.isIBitSet(flags, NODE_FLAGS_HAS_REDIRECT_BIT) ? VarNumberCodec.readVarInt(data) : -1;
String name = (nodeType == NODE_TYPE_LITERAL) || (nodeType == NODE_TYPE_ARGUMENT) ? StringCodec.readVarIntUTF8String(data) : null;
String parser = nodeType == NODE_TYPE_ARGUMENT ? StringCodec.readVarIntUTF8String(data) : null;
CommandNodeProperties properties = null;
if (parser != null) {
Function<ByteBuf, CommandNodeProperties> propertiesFunc = propertiesDeserializer.get(parser);
if (propertiesFunc != null) {
properties = propertiesFunc.apply(data);
} else {
properties = new CommandNodeSimpleProperties(parser);
}
}
String suggestionsType = BitUtils.isIBitSet(flags, NODE_FLAGS_HAS_SUGGESTIONS_TYPE) ? StringCodec.readVarIntUTF8String(data) : null;
return new CommandNode(flags, childNodesIndexes, redirectNodeIndex, name, parser, properties, suggestionsType);
}


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package protocolsupport.protocol.packet.middle.impl.clientbound.play.v_13_14r1_14r2_15_16r1_16r2_17r1_17r2_18;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

import io.netty.buffer.ByteBuf;
import protocolsupport.protocol.codec.ArrayCodec;
import protocolsupport.protocol.codec.MiscDataCodec;
import protocolsupport.protocol.codec.StringCodec;
import protocolsupport.protocol.codec.VarNumberCodec;
import protocolsupport.protocol.packet.ClientBoundPacketData;
import protocolsupport.protocol.packet.ClientBoundPacketType;
import protocolsupport.protocol.packet.middle.base.clientbound.play.MiddleDeclareCommands;
Expand All @@ -12,6 +21,21 @@
import protocolsupport.protocol.packet.middle.impl.clientbound.IClientboundMiddlePacketV17r1;
import protocolsupport.protocol.packet.middle.impl.clientbound.IClientboundMiddlePacketV17r2;
import protocolsupport.protocol.packet.middle.impl.clientbound.IClientboundMiddlePacketV18;
import protocolsupport.protocol.typeremapper.legacy.LegacyCommandDataRegistry;
import protocolsupport.protocol.typeremapper.legacy.LegacyCommandDataRegistry.LegacyCommandDataMappingTable;
import protocolsupport.protocol.types.command.CommandNodeDoubleProperties;
import protocolsupport.protocol.types.command.CommandNodeEntityProperties;
import protocolsupport.protocol.types.command.CommandNodeFloatProperties;
import protocolsupport.protocol.types.command.CommandNodeIntegerProperties;
import protocolsupport.protocol.types.command.CommandNodeLongProperties;
import protocolsupport.protocol.types.command.CommandNodeProperties;
import protocolsupport.protocol.types.command.CommandNodeRangeProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceOrTagProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceProperties;
import protocolsupport.protocol.types.command.CommandNodeScoreHolderProperties;
import protocolsupport.protocol.types.command.CommandNodeSimpleProperties;
import protocolsupport.protocol.types.command.CommandNodeStringProperties;
import protocolsupport.utils.BitUtils;

public class DeclareCommands extends MiddleDeclareCommands implements
IClientboundMiddlePacketV13,
Expand All @@ -28,11 +52,112 @@ public DeclareCommands(IMiddlePacketInit init) {
super(init);
}

protected final LegacyCommandDataMappingTable commanddataTabe = LegacyCommandDataRegistry.INSTANCE.getTable(version);

@Override
protected void write() {
ClientBoundPacketData declarecommands = ClientBoundPacketData.create(ClientBoundPacketType.PLAY_DECLARE_COMMANDS);
declarecommands.writeBytes(data);
ArrayCodec.writeVarIntTArray(declarecommands, nodes, (nodeData, node) -> {
int flags = node.getFlags();
int nodeType = flags & NODE_FLAGS_TYPE_MASK;
nodeData.writeByte(flags);
ArrayCodec.writeVarIntVarIntArray(nodeData, node.getChildNodesIndexes());
if (BitUtils.isIBitSet(flags, NODE_FLAGS_HAS_REDIRECT_BIT)) {
VarNumberCodec.writeVarInt(nodeData, node.getRedirectNodeIndex());
}
if (nodeType == NODE_TYPE_LITERAL || nodeType == NODE_TYPE_ARGUMENT) {
StringCodec.writeVarIntUTF8String(nodeData, node.getName());
}
if (nodeType == NODE_TYPE_ARGUMENT) {
CommandNodeProperties properties = node.getProperties();
properties = commanddataTabe.<CommandNodeProperties>get(properties.getClass()).apply(properties);
propertiesSerializer.get(properties.getClass()).accept(nodeData, properties);
}
if (BitUtils.isIBitSet(flags, NODE_FLAGS_HAS_SUGGESTIONS_TYPE)) {
StringCodec.writeVarIntUTF8String(nodeData, node.getSuggestType());
}
});
VarNumberCodec.writeVarInt(declarecommands, rootNodeIndex);
io.writeClientbound(declarecommands);
}

protected static final Map<Class<CommandNodeProperties>, BiConsumer<ByteBuf, CommandNodeProperties>> propertiesSerializer = new HashMap<>();
@SuppressWarnings("unchecked")
protected static <T extends CommandNodeProperties> void registerPropertiesSerializer(Class<T> clazz, BiConsumer<ByteBuf, T> serializer) {
propertiesSerializer.put((Class<CommandNodeProperties>) clazz, (BiConsumer<ByteBuf, CommandNodeProperties>) serializer);
}
static {
registerPropertiesSerializer(CommandNodeSimpleProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, properties.getParser());
});
registerPropertiesSerializer(CommandNodeDoubleProperties.class, (data, properties) -> {
int flags = properties.getFlags();
StringCodec.writeVarIntUTF8String(data, "brigadier:double");
data.writeByte(flags);
if (BitUtils.isIBitSet(flags, 0)) {
data.writeDouble(properties.getMin());
}
if (BitUtils.isIBitSet(flags, 1)) {
data.writeDouble(properties.getMax());
}
});
registerPropertiesSerializer(CommandNodeFloatProperties.class, (data, properties) -> {
int flags = properties.getFlags();
StringCodec.writeVarIntUTF8String(data, "brigadier:float");
data.writeByte(flags);
if (BitUtils.isIBitSet(flags, 0)) {
data.writeFloat(properties.getMin());
}
if (BitUtils.isIBitSet(flags, 1)) {
data.writeFloat(properties.getMax());
}
});
registerPropertiesSerializer(CommandNodeLongProperties.class, (data, properties) -> {
int flags = properties.getFlags();
StringCodec.writeVarIntUTF8String(data, "brigadier:long");
data.writeByte(flags);
if (BitUtils.isIBitSet(flags, 0)) {
data.writeLong(properties.getMin());
}
if (BitUtils.isIBitSet(flags, 1)) {
data.writeLong(properties.getMax());
}
});
registerPropertiesSerializer(CommandNodeIntegerProperties.class, (data, properties) -> {
int flags = properties.getFlags();
StringCodec.writeVarIntUTF8String(data, "brigadier:integer");
data.writeByte(flags);
if (BitUtils.isIBitSet(flags, 0)) {
data.writeInt(properties.getMin());
}
if (BitUtils.isIBitSet(flags, 1)) {
data.writeInt(properties.getMax());
}
});
registerPropertiesSerializer(CommandNodeStringProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "brigadier:string");
MiscDataCodec.writeVarIntEnum(data, properties.getType());
});
registerPropertiesSerializer(CommandNodeEntityProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "minecraft:entity");
data.writeByte(properties.getFlags());
});
registerPropertiesSerializer(CommandNodeScoreHolderProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "minecraft:score_holder");
data.writeByte(properties.getFlags());
});
registerPropertiesSerializer(CommandNodeRangeProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "minecraft:range");
data.writeBoolean(properties.allowDecimals());
});
registerPropertiesSerializer(CommandNodeResourceOrTagProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "minecraft:resource_or_tag");
StringCodec.writeVarIntUTF8String(data, properties.getIdentifier());
});
registerPropertiesSerializer(CommandNodeResourceProperties.class, (data, properties) -> {
StringCodec.writeVarIntUTF8String(data, "minecraft:resource");
StringCodec.writeVarIntUTF8String(data, properties.getIdentifier());
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package protocolsupport.protocol.typeremapper.legacy;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import protocolsupport.api.ProtocolVersion;
import protocolsupport.protocol.typeremapper.legacy.LegacyCommandDataRegistry.LegacyCommandDataMappingTable;
import protocolsupport.protocol.typeremapper.utils.MappingRegistry;
import protocolsupport.protocol.typeremapper.utils.MappingTable;
import protocolsupport.protocol.types.command.CommandNodeProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceOrTagProperties;
import protocolsupport.protocol.types.command.CommandNodeResourceProperties;
import protocolsupport.protocol.types.command.CommandNodeStringProperties;
import protocolsupport.protocol.utils.ProtocolVersionsHelper;
import protocolsupportbuildprocessor.Preload;

@Preload
public class LegacyCommandDataRegistry extends MappingRegistry<LegacyCommandDataMappingTable> {

public static final LegacyCommandDataRegistry INSTANCE = new LegacyCommandDataRegistry();

public LegacyCommandDataRegistry() {
register(CommandNodeResourceOrTagProperties.class, properties -> new CommandNodeStringProperties(CommandNodeStringProperties.Type.SINGLE_WORD), ProtocolVersionsHelper.DOWN_1_18);
register(CommandNodeResourceProperties.class, properties -> new CommandNodeStringProperties(CommandNodeStringProperties.Type.SINGLE_WORD), ProtocolVersionsHelper.DOWN_1_18);
}

protected <T extends CommandNodeProperties> void register(Class<T> clazz, Function<T, CommandNodeProperties> func, ProtocolVersion... versions) {
for (ProtocolVersion version : versions) {
getTable(version).set(clazz, func);
}
}

@Override
protected LegacyCommandDataMappingTable createTable() {
return new LegacyCommandDataMappingTable();
}

public static class LegacyCommandDataMappingTable extends MappingTable {

protected final Map<Class<? extends CommandNodeProperties>, Function<? extends CommandNodeProperties, CommandNodeProperties>> table = new HashMap<>();

public <T extends CommandNodeProperties> void set(Class<T> clazz, Function<T, CommandNodeProperties> func) {
table.put(clazz, func);
}

@SuppressWarnings("unchecked")
public <T extends CommandNodeProperties> Function<T, CommandNodeProperties> get(Class<? extends T> clazz) {
return (Function<T, CommandNodeProperties>) table.getOrDefault(clazz, UnaryOperator.identity());
}

}

}
57 changes: 57 additions & 0 deletions src/protocolsupport/protocol/types/command/CommandNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package protocolsupport.protocol.types.command;

public class CommandNode {

protected final int flags;
protected final int[] childNodesIndexes;
protected final int redirectNodeIndex;
protected final String name;
protected final String parser;
protected final CommandNodeProperties properties;
protected final String suggestType;

public CommandNode(int flags, int[] childNodesIndexes, int redirectNodeIndex, String name, String parser, CommandNodeProperties properties, String suggestType) {
this.flags = flags;
this.childNodesIndexes = childNodesIndexes;
this.redirectNodeIndex = redirectNodeIndex;
this.name = name;
this.parser = parser;
this.properties = properties;
this.suggestType = suggestType;
}

public int getFlags() {
return flags;
}


public int[] getChildNodesIndexes() {
return childNodesIndexes;
}


public int getRedirectNodeIndex() {
return redirectNodeIndex;
}


public String getName() {
return name;
}


public String getParser() {
return parser;
}


public CommandNodeProperties getProperties() {
return properties;
}


public String getSuggestType() {
return suggestType;
}

}
Loading

0 comments on commit 1c6039b

Please sign in to comment.