From 8e9f862c88b76739733fb6ba6dcf199e2ef3a71d Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Wed, 26 Aug 2020 22:51:11 +0100 Subject: [PATCH] Merge commands with the same parameters --- .../java/JavaDeclareCommandsTranslator.java | 214 +++++++++++------- 1 file changed, 129 insertions(+), 85 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java index 8f524336..beed8232 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java @@ -33,94 +33,57 @@ import com.nukkitx.protocol.bedrock.data.command.CommandEnumData; import com.nukkitx.protocol.bedrock.data.command.CommandParamData; import com.nukkitx.protocol.bedrock.data.command.CommandParamType; import com.nukkitx.protocol.bedrock.packet.AvailableCommandsPacket; +import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; @Translator(packet = ServerDeclareCommandsPacket.class) public class JavaDeclareCommandsTranslator extends PacketTranslator { - @Override - public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) { - // Don't send command suggestions if they are disabled - if (!session.getConnector().getConfig().isCommandSuggestions()) { - session.getConnector().getLogger().debug("Not sending command suggestions as they are disabled."); - return; + + private static final String[] ENUM_BOOLEAN = {"true", "false"}; + + private static final Hash.Strategy PARAM_STRATEGY = new Hash.Strategy() { + @Override + public int hashCode(CommandParamData[][] o) { + return Arrays.deepHashCode(o); } - List commandData = new ArrayList<>(); - Int2ObjectMap commands = new Int2ObjectOpenHashMap<>(); - Int2ObjectMap> commandArgs = new Int2ObjectOpenHashMap<>(); - // Get the first node, it should be a root node - CommandNode rootNode = packet.getNodes()[packet.getFirstNodeIndex()]; + @Override + public boolean equals(CommandParamData[][] a, CommandParamData[][] b) { + if (a == b) return true; + if (a == null || b == null) return false; + if (a.length != b.length) return false; + for (int i = 0; i < a.length; i++) { + CommandParamData[] a1 = a[i]; + CommandParamData[] b1 = b[i]; + if (a1.length != b1.length) return false; - // Loop through the root nodes to get all commands - for (int nodeIndex : rootNode.getChildIndices()) { - CommandNode node = packet.getNodes()[nodeIndex]; - - // Make sure we don't have duplicated commands (happens if there is more than 1 root node) - if (commands.containsKey(nodeIndex)) { continue; } - if (commands.containsValue(node.getName())) { continue; } - - // Get and update the commandArgs list with the found arguments - if (node.getChildIndices().length >= 1) { - for (int childIndex : node.getChildIndices()) { - commandArgs.putIfAbsent(nodeIndex, new ArrayList<>()); - commandArgs.get(nodeIndex).add(packet.getNodes()[childIndex]); + for (int i2 = 0; i2 < a1.length; i2++) { + if (!a1[i].equals(b1[i])) return false; } } - - // Insert the command name into the list - commands.put(nodeIndex, node.getName()); + return true; } - - // The command flags, not sure what these do apart from break things - List flags = new ArrayList<>(); - - // Loop through all the found commands - for (int commandID : commands.keySet()) { - String commandName = commands.get(commandID); - - // Create a basic alias - CommandEnumData aliases = new CommandEnumData( commandName + "Aliases", new String[] { commandName.toLowerCase() }, false); - - // Get and parse all params - CommandParamData[][] params = getParams(packet.getNodes()[commandID], packet.getNodes()); - - // Build the completed command and add it to the final list - CommandData data = new CommandData(commandName, session.getConnector().getCommandManager().getDescription(commandName), flags, (byte) 0, aliases, params); - commandData.add(data); - } - - // Add our commands to the AvailableCommandsPacket for the bedrock client - AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket(); - for (CommandData data : commandData) { - availableCommandsPacket.getCommands().add(data); - } - - GeyserConnector.getInstance().getLogger().debug("Sending command packet of " + commandData.size() + " commands"); - - // Finally, send the commands to the client - session.sendUpstreamPacket(availableCommandsPacket); - } + }; /** * Build the command parameter array for the given command * * @param commandNode The command to build the parameters for - * @param allNodes Every command node - * + * @param allNodes Every command node * @return An array of parameter option arrays */ - private CommandParamData[][] getParams(CommandNode commandNode, CommandNode[] allNodes) { + private static CommandParamData[][] getParams(CommandNode commandNode, CommandNode[] allNodes) { // Check if the command is an alias and redirect it if (commandNode.getRedirectIndex() != -1) { GeyserConnector.getInstance().getLogger().debug("Redirecting command " + commandNode.getName() + " to " + allNodes[commandNode.getRedirectIndex()].getName()); @@ -133,16 +96,8 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator treeData = rootParam.getTree(); - CommandParamData[][] params = new CommandParamData[treeData.size()][]; - // Fill the nested params array - int i = 0; - for (CommandParamData[] tree : treeData) { - params[i] = tree; - i++; - } - - return params; + return treeData.toArray(new CommandParamData[0][]); } return new CommandParamData[0][0]; @@ -152,14 +107,17 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator commandData = new ArrayList<>(); + IntSet commandNodes = new IntOpenHashSet(); + Set knownAliases = new HashSet<>(); + Map> commands = new Object2ObjectOpenCustomHashMap<>(PARAM_STRATEGY); + Int2ObjectMap> commandArgs = new Int2ObjectOpenHashMap<>(); + + // Get the first node, it should be a root node + CommandNode rootNode = nodes[packet.getFirstNodeIndex()]; + + // Loop through the root nodes to get all commands + for (int nodeIndex : rootNode.getChildIndices()) { + CommandNode node = nodes[nodeIndex]; + + // Make sure we don't have duplicated commands (happens if there is more than 1 root node) + if (!commandNodes.add(nodeIndex) || !knownAliases.add(node.getName().toLowerCase())) continue; + + // Get and update the commandArgs list with the found arguments + if (node.getChildIndices().length >= 1) { + for (int childIndex : node.getChildIndices()) { + commandArgs.putIfAbsent(nodeIndex, new ArrayList<>()); + commandArgs.get(nodeIndex).add(nodes[childIndex]); + } + } + + // Get and parse all params + CommandParamData[][] params = getParams(nodes[nodeIndex], nodes); + + // Insert the alias name into the command list + commands.computeIfAbsent(params, index -> new HashSet<>()).add(node.getName().toLowerCase()); + } + + // The command flags, not sure what these do apart from break things + List flags = new ArrayList<>(); + + // Loop through all the found commands + + for (Map.Entry> entry : commands.entrySet()) { + String commandName = entry.getValue().iterator().next(); // We know this has a value + + // Create a basic alias + CommandEnumData aliases = new CommandEnumData(commandName + "Aliases", entry.getValue().toArray(new String[0]), false); + + // Build the completed command and add it to the final list + CommandData data = new CommandData(commandName, session.getConnector().getCommandManager().getDescription(commandName), flags, (byte) 0, aliases, entry.getKey()); + commandData.add(data); + } + + // Add our commands to the AvailableCommandsPacket for the bedrock client + AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket(); + for (CommandData data : commandData) { + availableCommandsPacket.getCommands().add(data); + } + + GeyserConnector.getInstance().getLogger().debug("Sending command packet of " + commandData.size() + " commands"); + + // Finally, send the commands to the client + session.sendUpstreamPacket(availableCommandsPacket); + } + @Getter - private class ParamInfo { + private static class ParamInfo { private CommandNode paramNode; private CommandParamData paramData; private List children; @@ -259,8 +286,17 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator