diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index ff52e50d..84028fac 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -215,7 +215,7 @@ public class CarpetScriptHost extends ScriptHost Value spec = typeData.getValue(); if (!(spec instanceof MapValue)) throw new InternalExpressionException("Spec for '"+argument+"' should be a map"); Map specData = ((MapValue) spec).getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), Map.Entry::getValue)); - appArgTypes.put(argument, CommandArgument.buildFromConfig(argument, specData)); + appArgTypes.put(argument, CommandArgument.buildFromConfig(argument, specData, this)); } } appConfig = config; diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index 271d1d95..af410f91 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -2,11 +2,13 @@ package carpet.script.command; import carpet.fakes.BlockStateArgumentInterface; import carpet.script.CarpetScriptHost; +import carpet.script.argument.FunctionArgument; import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; import carpet.script.value.EntityValue; import carpet.script.value.FormattedTextValue; import carpet.script.value.ListValue; +import carpet.script.value.MapValue; import carpet.script.value.NBTSerializableValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; @@ -22,11 +24,14 @@ import com.mojang.brigadier.arguments.LongArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.ParsedCommandNode; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.CommandNode; import net.minecraft.advancement.Advancement; import net.minecraft.command.CommandSource; import net.minecraft.command.argument.AngleArgumentType; @@ -74,8 +79,10 @@ import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec2f; import net.minecraft.util.registry.Registry; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -259,13 +266,15 @@ public abstract class CommandArgument { CommandArgument arg = getTypeForArgument(param, host); if (arg.suggestionProvider != null) return argument(param, arg.getArgumentType()).suggests(arg.suggestionProvider); - return arg.needsMatching? argument(param, arg.getArgumentType()).suggests(arg::suggest) : argument(param, arg.getArgumentType()); + return arg.needsMatching? argument(param, arg.getArgumentType()).suggests((c, b) -> arg.suggest(c, b, host)) : argument(param, arg.getArgumentType()); } protected String suffix; protected Collection examples; protected boolean needsMatching = false; protected SuggestionProvider suggestionProvider; + protected FunctionArgument customSuggester; + protected CommandArgument( String suffix, @@ -292,7 +301,7 @@ public abstract class CommandArgument return suffix; } - public static CommandArgument buildFromConfig(String suffix, Map config) + public static CommandArgument buildFromConfig(String suffix, Map config, CarpetScriptHost host) { if (!config.containsKey("type")) throw new InternalExpressionException("Custom types should at least specify the type"); @@ -300,15 +309,20 @@ public abstract class CommandArgument if (!builtIns.containsKey(baseType)) throw new InternalExpressionException("Unknown base type: "+baseType); CommandArgument variant = builtIns.get(baseType).builder().get(); - variant.configure(config); + variant.configure(config, host); variant.suffix = suffix; return variant; } - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { + if (config.containsKey("suggester")) + { + customSuggester = FunctionArgument.fromCommandSpec(host, config.get("suggester")); + } if (config.containsKey("suggest")) { + if (config.containsKey("suggester")) throw new InternalExpressionException("Attempted to provide 'suggest' list while 'suggester' is present"); Value suggestionValue = config.get("suggest"); if (!(suggestionValue instanceof ListValue)) throw new InternalExpressionException("Argument suggestions needs to be a list"); examples = ((ListValue) suggestionValue).getItems().stream() @@ -316,34 +330,55 @@ public abstract class CommandArgument .collect(Collectors.toSet()); if (!examples.isEmpty()) needsMatching = true; } - }; + } public CompletableFuture suggest( CommandContext context, - SuggestionsBuilder suggestionsBuilder - ) + SuggestionsBuilder suggestionsBuilder, + CarpetScriptHost host + ) throws CommandSyntaxException { String prefix = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT); - suggestFor(prefix).forEach(suggestionsBuilder::suggest); + suggestFor(context, prefix, host).forEach(suggestionsBuilder::suggest); return suggestionsBuilder.buildFuture(); } - protected List suggestFor(String prefix) + protected List suggestFor(CommandContext context, String prefix, CarpetScriptHost host) throws CommandSyntaxException { - return getOptions().stream().filter(s -> optionMatchesPrefix(prefix, s)).collect(Collectors.toList()); + return getOptions(context, host).stream().filter(s -> optionMatchesPrefix(prefix, s)).collect(Collectors.toList()); } - protected Collection getOptions() + protected Collection getOptions(CommandContext context, CarpetScriptHost host) throws CommandSyntaxException { + if (customSuggester != null) + { + Map params = new HashMap<>(); + for(ParsedCommandNode pnode : context.getNodes()) + { + CommandNode node = pnode.getNode(); + if (node instanceof ArgumentCommandNode) + { + params.put(StringValue.of(node.getName()), CommandArgument.getValue(context, node.getName(), host)); + } + } + List args = new ArrayList<>(customSuggester.args.size()+1); + args.add(MapValue.wrap(params)); + args.addAll(customSuggester.args); + Value response = host.handleCommand(context.getSource(), customSuggester.function, args); + if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"); + return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList()); + } + if (needsMatching) return examples; //return Lists.newArrayList(""); // better than nothing I guess // nothing is such a bad default. - if (needsMatching) return examples; return Collections.singletonList("... "+getTypeSuffix()); } protected boolean optionMatchesPrefix(String prefix, String option) { + //prefix = prefix.toLowerCase(Locale.ROOT); + //option = option.toLowerCase(Locale.ROOT); for(int i = 0; !option.startsWith(prefix, i); ++i) { i = option.indexOf('_', i); @@ -382,9 +417,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); caseSensitive = config.getOrDefault("case_sensitive", Value.FALSE).getBoolean(); if (config.containsKey("options")) { @@ -397,7 +432,10 @@ public abstract class CommandArgument } @Override - protected Collection getOptions() { return validOptions.isEmpty()?super.getOptions():validOptions; } + protected Collection getOptions(CommandContext context, CarpetScriptHost host) throws CommandSyntaxException + { + return validOptions.isEmpty()?super.getOptions(context, host):validOptions; + } @Override protected Supplier builder() { return WordArgument::new; } @@ -446,9 +484,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); mustBeLoaded = config.getOrDefault("loaded", Value.FALSE).getBoolean(); } @@ -481,9 +519,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); blockCentered = config.getOrDefault("block_centered", Value.TRUE).getBoolean(); } @@ -529,9 +567,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); onlyFans = config.getOrDefault("players", Value.FALSE).getBoolean(); single = config.getOrDefault("single", Value.FALSE).getBoolean(); } @@ -570,9 +608,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); single = config.getOrDefault("single", Value.FALSE).getBoolean(); } @@ -611,9 +649,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); single = config.getOrDefault("single", Value.FALSE).getBoolean(); } @@ -648,9 +686,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); mapRequired = !config.getOrDefault("allow_element", Value.FALSE).getBoolean(); } @@ -694,9 +732,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); if (config.containsKey("options")) { Value optionsValue = config.get("options"); @@ -736,9 +774,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); if (config.containsKey("min")) { min = NumericValue.asNumber(config.get("min"), "min").getDouble(); @@ -787,9 +825,9 @@ public abstract class CommandArgument } @Override - protected void configure(Map config) + protected void configure(Map config, CarpetScriptHost host) { - super.configure(config); + super.configure(config, host); if (config.containsKey("min")) { min = NumericValue.asNumber(config.get("min"), "min").getLong();