From 9f6771123fe4a7661be3185d78faa229ced9d738 Mon Sep 17 00:00:00 2001 From: gnembon Date: Thu, 12 Nov 2020 02:55:18 -0400 Subject: [PATCH] custom command trees, no validation so far --- .../java/carpet/script/CarpetScriptHost.java | 88 ++++++++++++++++--- .../carpet/script/CarpetScriptServer.java | 84 +++++++----------- .../script/argument/FunctionArgument.java | 2 +- .../script/command/CommandArgument.java | 9 +- .../carpet/script/command/CommandToken.java | 68 ++++++++++++++ 5 files changed, 185 insertions(+), 66 deletions(-) create mode 100644 src/main/java/carpet/script/command/CommandToken.java diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 267ff70b..d0b3fec9 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -5,6 +5,7 @@ import carpet.script.api.Auxiliary; import carpet.script.argument.FunctionArgument; import carpet.script.bundled.Module; import carpet.script.command.CommandArgument; +import carpet.script.command.CommandToken; import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; @@ -16,14 +17,20 @@ import carpet.script.value.StringValue; import carpet.script.value.Value; import carpet.CarpetSettings; import carpet.utils.Messenger; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.nbt.Tag; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.LiteralText; import net.minecraft.util.math.BlockPos; +import org.apache.commons.lang3.tuple.Pair; import java.math.BigInteger; import java.util.ArrayList; @@ -34,6 +41,7 @@ import java.util.Map; import java.util.stream.Collectors; import static java.lang.Math.max; +import static net.minecraft.server.command.CommandManager.literal; public class CarpetScriptHost extends ScriptHost { @@ -100,6 +108,62 @@ public class CarpetScriptHost extends ScriptHost return host; } + private static int execute(CommandContext ctx, String hostName, FunctionArgument funcSpec, List paramNames) throws CommandSyntaxException + { + CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveForExecution(ctx.getSource()); + List argNames = funcSpec.function.getArguments(); + if ((argNames.size()-funcSpec.args.size()) != paramNames.size()) + throw new SimpleCommandExceptionType(new LiteralText("Target function "+funcSpec.function.getPrettyString()+" as wrong number of arguments, required "+paramNames.size()+", found "+argNames.size()+" with "+funcSpec.args.size()+" provided")).create(); + List args = new ArrayList<>(argNames.size()); + for (String s : paramNames) + { + args.add(CommandArgument.getValue(ctx, s, cHost)); + } + args.addAll(funcSpec.args); + Value response = cHost.handleCommand(ctx.getSource(), funcSpec.function, args); + // will skip prints for new + //if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString()); + return (int) response.readInteger(); + } + + public LiteralArgumentBuilder addPathToCommand( + LiteralArgumentBuilder command, + List path, + FunctionArgument functionSpec + ) throws CommandSyntaxException + { + String hostName = main.getName(); + List commandArgs = path.stream().filter(t -> t.isArgument).map(t -> t.surface).collect(Collectors.toList()); + if (commandArgs.size() != (functionSpec.function.getNumParams()-functionSpec.args.size()) ) + throw CommandArgument.error("Number of parameters in function doesn't match parameters for a command"); + if (path.isEmpty()) + { + return command.executes((c) -> execute(c, hostName, functionSpec, Collections.emptyList())); + } + List reversedPath = new ArrayList<>(path); + Collections.reverse(reversedPath); + ArgumentBuilder argChain = reversedPath.get(0).getCommandNode(this).executes(c -> execute(c, hostName, functionSpec, commandArgs)); + for (int i = 1; i < reversedPath.size(); i++) + { + argChain = reversedPath.get(i).getCommandNode(this).then(argChain); + } + return command.then(argChain); + } + + public LiteralArgumentBuilder getNewCommandTree( + List,FunctionArgument>> entries + ) throws CommandSyntaxException + { + String hostName = main.getName(); + LiteralArgumentBuilder command = literal(hostName). + requires((player) -> CarpetServer.scriptServer.modules.containsKey(hostName)); + for (Pair,FunctionArgument> commandData : entries) + { + command = this.addPathToCommand(command, commandData.getKey(), commandData.getValue()); + } + return command; + } + @Override protected ScriptHost duplicate() { @@ -162,24 +226,24 @@ public class CarpetScriptHost extends ScriptHost return true; } - private boolean readCommands() + public LiteralArgumentBuilder readCommands() throws CommandSyntaxException { Value commands = appConfig.get(StringValue.of("commands")); - if (commands == null) return true; + if (commands == null) return null; if (!(commands instanceof MapValue)) - throw new InternalExpressionException("'commands' element in config should be a map"); - for (Map.Entry commandsData : ((MapValue)commands).getMap().entrySet()) + throw CommandArgument.error("'commands' element in config should be a map"); + List,FunctionArgument>> commandEntries = new ArrayList<>(); + for (Map.Entry commandsData : ((MapValue)commands).getMap().entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList())) { - String path = commandsData.getKey().getString(); + List elements = CommandToken.parseSpec(commandsData.getKey().getString(), this); FunctionArgument funSpec = FunctionArgument.fromCommandSpec(this, commandsData.getValue()); - //if (fun) - - //... WIP - - //Map specData = ((MapValue) spec).getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), Map.Entry::getValue)); - //appArgTypes.put(argument, CommandArgument.buildFromConfig(argument, specData)); + commandEntries.add(Pair.of(elements, funSpec)); } - return true; + // validate + // ... + // + + return this.getNewCommandTree(commandEntries); } @Override diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 0a299aba..61458ab1 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -1,11 +1,12 @@ package carpet.script; +import carpet.script.argument.FunctionArgument; import carpet.script.bundled.BundledModule; import carpet.CarpetSettings; import carpet.CarpetServer; import carpet.script.bundled.FileModule; import carpet.script.bundled.Module; -import carpet.script.command.CommandArgument; +import carpet.script.command.CommandToken; import carpet.script.exception.InvalidCallbackException; import carpet.script.value.FunctionValue; import carpet.script.value.StringValue; @@ -13,21 +14,16 @@ import carpet.script.value.Value; import carpet.utils.Messenger; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.LiteralText; import net.minecraft.util.WorldSavePath; import net.minecraft.util.math.BlockPos; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -240,7 +236,31 @@ public class CarpetScriptServer return false; } //addEvents(source, name); - if (!addLegacyCommand(source, name, reload, !isRuleApp)) // this needs to be moved to config reader, only supporting legacy command here + String action = reload?"reloaded":"loaded"; + if (newHost.appConfig.get(StringValue.of("commands")) != null) + { + try + { + LiteralArgumentBuilder command = newHost.readCommands(); + if (command != null) + { + if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command"); + server.getCommandManager().getDispatcher().register(command); + CarpetServer.settingsManager.notifyPlayersCommandsChanged(); + } + else { + if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action); + } + } + catch (CommandSyntaxException cse) + { + removeScriptHost(source, name, false, false); + Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app ", cse.getRawMessage()); + return false; + } + + } + else if (!addLegacyCommand(source, name, reload, !isRuleApp)) // this needs to be moved to config reader, only supporting legacy command here { removeScriptHost(source, name, false, false); Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app"); @@ -287,7 +307,12 @@ public class CarpetScriptServer { try { - command = getFancyCommand(command, host, host.getFunction(function)); + FunctionValue functionValue = host.getFunction(function); + command = host.addPathToCommand( + command, + CommandToken.parseSpec(CommandToken.specFromSignature(functionValue), host), + FunctionArgument.fromCommandSpec(host, functionValue) + ); } catch (CommandSyntaxException e) { @@ -320,49 +345,6 @@ public class CarpetScriptServer return true; } - private int execute(CommandContext ctx, String hostName, FunctionValue function, List paramNames) throws CommandSyntaxException - { - CarpetScriptHost cHost = modules.get(hostName).retrieveForExecution(ctx.getSource()); - List argNames = function.getArguments(); - if (argNames.size() != paramNames.size()) - throw new SimpleCommandExceptionType(new LiteralText("Target function "+function.getPrettyString()+" as wrong number of arguments, required "+paramNames.size()+", found "+argNames.size())).create(); - List args = new ArrayList<>(argNames.size()); - for (String s : paramNames) - { - args.add(CommandArgument.getValue(ctx, s, cHost)); - } - Value response = cHost.handleCommand(ctx.getSource(), function, args); - if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString()); - return (int) response.readInteger(); - } - - private LiteralArgumentBuilder getFancyCommand(LiteralArgumentBuilder command, CarpetScriptHost host, FunctionValue function ) throws CommandSyntaxException - { - List argNames = function.getArguments(); - String hostName = host.getName(); - if (argNames.size() == 0) - { - return command.then(literal(function.getString()). - requires((player) -> modules.containsKey(hostName)). - executes((c) -> execute(c, hostName, function, Collections.emptyList()))); - } - else - { - List reversedArgs = new ArrayList<>(argNames); - Collections.reverse(reversedArgs); - RequiredArgumentBuilder argChain = CommandArgument.argumentNode(reversedArgs.get(0), host).executes(c -> execute(c, hostName, function, argNames)); - for (int i = 1; i < reversedArgs.size(); i++) - { - argChain = CommandArgument.argumentNode(reversedArgs.get(i), host).then(argChain); - } - return command.then(literal(function.getString()). - requires((player) -> modules.containsKey(hostName)). - then(argChain) - ); - } - } - - public boolean removeScriptHost(ServerCommandSource source, String name, boolean notifySource, boolean isRuleApp) { diff --git a/src/main/java/carpet/script/argument/FunctionArgument.java b/src/main/java/carpet/script/argument/FunctionArgument.java index 928dd58a..b942e7f7 100644 --- a/src/main/java/carpet/script/argument/FunctionArgument.java +++ b/src/main/java/carpet/script/argument/FunctionArgument.java @@ -85,7 +85,7 @@ public class FunctionArgument extends Argument } else { - String name = funSpec.getString(); + String name = first.getString(); function = host.getAssertFunction(host.main, name); } if (params.size() > 1) args = params.subList(1,params.size()); diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index b65e2cc6..c4f13d88 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -89,6 +89,11 @@ import static net.minecraft.server.command.CommandManager.argument; public abstract class CommandArgument { + public static CommandSyntaxException error(String text) + { + return new SimpleCommandExceptionType(new LiteralText(text)).create(); + } + private static final List baseTypes = Lists.newArrayList( // default new StringArgument(), @@ -241,7 +246,7 @@ public abstract class CommandArgument public static final CommandArgument DEFAULT = baseTypes.get(0); - private static CommandArgument getTypeForArgument(String argument, CarpetScriptHost host) + public static CommandArgument getTypeForArgument(String argument, CarpetScriptHost host) { String[] components = argument.split("_"); String suffix = components[components.length-1].toLowerCase(Locale.ROOT); @@ -290,7 +295,7 @@ public abstract class CommandArgument public static CommandArgument buildFromConfig(String suffix, Map config) { if (!config.containsKey("type")) - throw new InternalExpressionException("Custom types should at least specify the base type"); + throw new InternalExpressionException("Custom types should at least specify the type"); String baseType = config.get("type").getString(); if (!builtIns.containsKey(baseType)) throw new InternalExpressionException("Unknown base type: "+baseType); diff --git a/src/main/java/carpet/script/command/CommandToken.java b/src/main/java/carpet/script/command/CommandToken.java new file mode 100644 index 00000000..cc3606ae --- /dev/null +++ b/src/main/java/carpet/script/command/CommandToken.java @@ -0,0 +1,68 @@ +package carpet.script.command; + +import carpet.script.CarpetScriptHost; +import carpet.script.value.FunctionValue; +import com.google.common.collect.Lists; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.server.command.ServerCommandSource; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static net.minecraft.server.command.CommandManager.literal; + +public class CommandToken +{ + public String surface; + public boolean isArgument; + + private CommandToken(String surface, CommandArgument type ) + { + this.surface = surface; + isArgument = type != null; + } + + public static CommandToken getToken(String source, CarpetScriptHost host) + { + // todo add more type checking and return null + if (!source.startsWith("<")) + { + return new CommandToken(source, null); + } + source = source.substring(1, source.length()-1); + CommandArgument arg = CommandArgument.getTypeForArgument(source, host); + return new CommandToken(source, arg); + } + + public static List parseSpec(String spec, CarpetScriptHost host) throws CommandSyntaxException + { + spec = spec.trim(); + if (spec.isEmpty()) return Collections.emptyList(); + List elements = new ArrayList<>(); + for (String el: spec.split("\\s+")) + { + CommandToken tok = CommandToken.getToken(el, host); + if (tok == null) throw CommandArgument.error("Unrecognized command token: "+ el); + elements.add(tok); + } + return elements; + } + + public static String specFromSignature(FunctionValue function) + { + List tokens = Lists.newArrayList(function.getString()); + for (String arg : function.getArguments()) tokens.add("<"+arg+">"); + return String.join(" ", tokens); + } + + + public ArgumentBuilder getCommandNode(CarpetScriptHost host) throws CommandSyntaxException + { + if (isArgument) + return CommandArgument.argumentNode(surface, host); + return literal(surface); + + } +}