diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index e19ef806..d1971c69 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -449,7 +449,7 @@ public class ScriptCommand } //if (!(args.trim().isEmpty())) // arguments.addAll(Arrays.asList(args.trim().split("\\s+"))); - return handleCall(source, host, () -> host.call(source, call, positions, args)); + return handleCall(source, host, () -> host.callLegacy(source, call, positions, args)); } diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 11d546d0..789e66dd 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -40,6 +40,8 @@ public class CarpetScriptHost extends ScriptHost private int saveTimeout; public boolean persistenceRequired; + public Map appConfig = Collections.emptyMap(); + private CarpetScriptHost(CarpetScriptServer server, Module code, boolean perUser, ScriptHost parent) { super(code, perUser, parent); @@ -128,6 +130,7 @@ public class CarpetScriptHost extends ScriptHost Map config = ((MapValue) ret).getMap(); setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player")); persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.FALSE).getBoolean(); + this.appConfig = config; } catch (NullPointerException | InvalidCallbackException ignored) { @@ -185,11 +188,11 @@ public class CarpetScriptHost extends ScriptHost return host; } - public Value handleCommand(ServerCommandSource source, String call, List coords, String arg) + public Value handleCommandLegacy(ServerCommandSource source, String call, List coords, String arg) { try { - return call(source, call, coords, arg); + return callLegacy(source, call, coords, arg); } catch (CarpetExpressionException exc) { @@ -203,7 +206,25 @@ public class CarpetScriptHost extends ScriptHost } } - public Value call(ServerCommandSource source, String call, List coords, String arg) + public Value handleCommand(ServerCommandSource source, String call, List args) + { + try + { + return call(source, call, args); + } + catch (CarpetExpressionException exc) + { + handleErrorWithStack("Error while running custom command", exc); + return Value.NULL; + } + catch (ArithmeticException ae) + { + handleErrorWithStack("Math doesn't compute", ae); + return Value.NULL; + } + } + + public Value callLegacy(ServerCommandSource source, String call, List coords, String arg) { if (CarpetServer.scriptServer.stopAll) throw new CarpetExpressionException("SCARPET PAUSED", null); @@ -305,6 +326,42 @@ public class CarpetScriptHost extends ScriptHost } } + public Value call(ServerCommandSource source, String call, List suppliedArgs) + { + if (CarpetServer.scriptServer.stopAll) + throw new CarpetExpressionException("SCARPET PAUSED", null); + FunctionValue function = getFunction(call); + if (function == null) + throw new CarpetExpressionException("UNDEFINED", null); + + List argv = FunctionValue.lazify(suppliedArgs); + + List args = function.getArguments(); + if (argv.size() != args.size()) + { + String error = "Fail: stored function "+call+" takes "+args.size()+" arguments, not "+argv.size()+ ":\n"; + for (int i = 0; i < max(argv.size(), args.size()); i++) + { + error += (i "+(i function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), + context, + Context.VOID + ); + } + catch (ExpressionException e) + { + throw new CarpetExpressionException(e.getMessage(), e.stack); + } + } + public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException { if (CarpetServer.scriptServer.stopAll) diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 95ca7f02..059cbadc 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -5,15 +5,17 @@ import carpet.CarpetSettings; import carpet.CarpetServer; import carpet.script.bundled.FileModule; import carpet.script.bundled.Module; +import carpet.script.command.CommandArgument; import carpet.script.exception.InvalidCallbackException; import carpet.script.value.FunctionValue; -import carpet.script.value.MapValue; import carpet.script.value.StringValue; -import carpet.script.value.ThreadValue; 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.tree.CommandNode; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; @@ -199,7 +201,7 @@ public class CarpetScriptServer private void addCommand(ServerCommandSource source, String hostName, boolean isReload) { - ScriptHost host = modules.get(hostName); + CarpetScriptHost host = modules.get(hostName); String loaded = isReload?"reloaded":"loaded"; if (host == null) { @@ -222,35 +224,84 @@ public class CarpetScriptServer executes( (c) -> { Value response = modules.get(hostName).retrieveForExecution(c.getSource()). - handleCommand(c.getSource(),"__command", null, ""); + handleCommandLegacy(c.getSource(),"__command", null, ""); if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString()); return (int)response.readInteger(); }); for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList())) { - command = command. - then(literal(function). - requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null). - executes( (c) -> { - Value response = modules.get(hostName).retrieveForExecution(c.getSource()). - handleCommand(c.getSource(), function,null,""); - if (!response.isNull()) Messenger.m(c.getSource(),"gi "+response.getString()); - return (int)response.readInteger(); - }). - then(argument("args...", StringArgumentType.greedyString()). - executes( (c) -> { - Value response = modules.get(hostName).retrieveForExecution(c.getSource()). - handleCommand(c.getSource(), function,null, StringArgumentType.getString(c, "args...")); - if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString()); - return (int)response.readInteger(); - }))); + if (host.appConfig.containsKey(StringValue.of("test_command_type_support"))) // temporary stuff for testing + { + command = getFancyCommand(command, host, function); + } + else + { + command = command. + then(literal(function). + requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null). + executes((c) -> { + Value response = modules.get(hostName).retrieveForExecution(c.getSource()). + handleCommandLegacy(c.getSource(), function, null, ""); + if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString()); + return (int) response.readInteger(); + }). + then(argument("args...", StringArgumentType.greedyString()). + executes( (c) -> { + Value response = modules.get(hostName).retrieveForExecution(c.getSource()). + handleCommandLegacy(c.getSource(), function,null, StringArgumentType.getString(c, "args...")); + if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString()); + return (int)response.readInteger(); + }))); + } } Messenger.m(source, "gi "+hostName+" app "+loaded+" with /"+hostName+" command"); server.getCommandManager().getDispatcher().register(command); CarpetServer.settingsManager.notifyPlayersCommandsChanged(); } + private int execute(CommandContext ctx, String hostName, String funName) throws CommandSyntaxException + { + CarpetScriptHost cHost = modules.get(hostName).retrieveForExecution(ctx.getSource()); + List argNames = cHost.getFunction(funName).getArguments(); + List args = new ArrayList<>(argNames.size()); + for (String s : argNames) + { + args.add(CommandArgument.getValue(ctx, s)); + } + Value response = cHost.handleCommand(ctx.getSource(), funName, args); + if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString()); + return (int) response.readInteger(); + } + + private LiteralArgumentBuilder getFancyCommand(LiteralArgumentBuilder command, ScriptHost host, String funName ) + { + List argNames = host.getFunction(funName).getArguments(); + String hostName = host.getName(); + if (argNames.size() == 0) + { + return command.then(literal(funName). + requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(funName) != null). + executes((c) -> execute(c, hostName, funName))); + } + else + { + List reversedArgs = new ArrayList<>(argNames); + Collections.reverse(reversedArgs); + RequiredArgumentBuilder argChain = CommandArgument.argumentNode(reversedArgs.get(0)).executes(c -> execute(c, hostName, funName)); + for (int i = 1; i < reversedArgs.size(); i++) + { + argChain = CommandArgument.argumentNode(reversedArgs.get(i)).then(argChain); + } + return command.then(literal(funName). + requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(funName) != null). + then(argChain) + ); + } + } + + + public boolean removeScriptHost(ServerCommandSource source, String name, boolean notifySource) { name = name.toLowerCase(Locale.ROOT); diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index d0cd30cc..39468783 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -10,6 +10,7 @@ import com.google.common.collect.Lists; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.DoubleArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; @@ -30,6 +31,9 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import java.util.stream.Collectors; +import static net.minecraft.server.command.CommandManager.argument; + + public abstract class CommandArgument { private static final List baseTypes = Lists.newArrayList( @@ -38,20 +42,47 @@ public abstract class CommandArgument public static final Map builtIns = baseTypes.stream().collect(Collectors.toMap(CommandArgument::getTypeSuffix, a -> a)); + private static CommandArgument getTypeForArgument(String argument) + { + String[] components = argument.split("_"); + String suffix = components[components.length-1].toLowerCase(Locale.ROOT); + return builtIns.getOrDefault(suffix, builtIns.get("string")); + } + + public static RequiredArgumentBuilder argumentNode(String param) + { + CommandArgument arg = getTypeForArgument(param); + return arg.needsMatching? argument(param, arg.getArgumentType()).suggests(arg::suggest) : argument(param, arg.getArgumentType()); + } + private String suffix; private final Collection examples; + protected boolean needsMatching = false; protected CommandArgument( String suffix, - Collection examples) + Collection examples, + boolean needsMatching) { this.suffix = suffix; this.examples = examples; + this.needsMatching = needsMatching; } - public abstract ArgumentType getArgument(); + public static ArgumentType getArgument(String param) + { + return getTypeForArgument(param).getArgumentType(); + } - public abstract Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException; + protected abstract ArgumentType getArgumentType(); + + + public static Value getValue(CommandContext context, String param) throws CommandSyntaxException + { + return getTypeForArgument(param).getValueFromContext(context, param); + } + + protected abstract Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException; @@ -81,8 +112,7 @@ public abstract class CommandArgument protected abstract void configure(Map config); - - protected CompletableFuture suggest( + public CompletableFuture suggest( CommandContext context, SuggestionsBuilder suggestionsBuilder ) @@ -99,7 +129,10 @@ public abstract class CommandArgument protected Collection getOptions() { - return Collections.emptyList(); + //return Lists.newArrayList(""); + // better than nothing I guess + // nothing is such a bad default. + return Collections.singletonList("... "+getTypeSuffix()); } protected boolean optionMatchesPrefix(String prefix, String option) @@ -120,11 +153,11 @@ public abstract class CommandArgument boolean caseSensitive = false; private WordArgument() { - super("string", StringArgumentType.StringType.SINGLE_WORD.getExamples()); + super("string", StringArgumentType.StringType.SINGLE_WORD.getExamples(), true); } @Override - public ArgumentType getArgument() + public ArgumentType getArgumentType() { return StringArgumentType.word(); } @@ -156,7 +189,7 @@ public abstract class CommandArgument } @Override - protected Collection getOptions() { return validOptions; } + protected Collection getOptions() { return validOptions.isEmpty()?super.getOptions():validOptions; } @Override protected Supplier builder() { return WordArgument::new; } @@ -168,11 +201,11 @@ public abstract class CommandArgument private PosArgument() { - super("pos", BlockPosArgumentType.blockPos().getExamples()); + super("pos", BlockPosArgumentType.blockPos().getExamples(), false); } @Override - public ArgumentType getArgument() + public ArgumentType getArgumentType() { return BlockPosArgumentType.blockPos(); } @@ -205,11 +238,11 @@ public abstract class CommandArgument private Double max = null; private FloatArgument() { - super("float", DoubleArgumentType.doubleArg().getExamples()); + super("float", DoubleArgumentType.doubleArg().getExamples(), true); } @Override - public ArgumentType getArgument() + public ArgumentType getArgumentType() { if (min != null) {