Merge branch 'master' into 1.17
This commit is contained in:
commit
c8b5dbb26d
9 files changed
+897
-177
No files matched your search
@@ -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,24 +17,32 @@ 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;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
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 +109,62 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return host;
|
||||
}
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
|
||||
{
|
||||
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveForExecution(ctx.getSource());
|
||||
List<String> 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<Value> 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<ServerCommandSource> addPathToCommand(
|
||||
LiteralArgumentBuilder<ServerCommandSource> command,
|
||||
List<CommandToken> path,
|
||||
FunctionArgument<Value> functionSpec
|
||||
) throws CommandSyntaxException
|
||||
{
|
||||
String hostName = main.getName();
|
||||
List<String> 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<CommandToken> reversedPath = new ArrayList<>(path);
|
||||
Collections.reverse(reversedPath);
|
||||
ArgumentBuilder<ServerCommandSource, ?> 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<ServerCommandSource> getNewCommandTree(
|
||||
List<Pair<List<CommandToken>,FunctionArgument<Value>>> entries
|
||||
) throws CommandSyntaxException
|
||||
{
|
||||
String hostName = main.getName();
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
|
||||
requires((player) -> CarpetServer.scriptServer.modules.containsKey(hostName));
|
||||
for (Pair<List<CommandToken>,FunctionArgument<Value>> commandData : entries)
|
||||
{
|
||||
command = this.addPathToCommand(command, commandData.getKey(), commandData.getValue());
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptHost duplicate()
|
||||
{
|
||||
@@ -150,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<String, Value> 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;
|
||||
@@ -162,24 +227,59 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean readCommands()
|
||||
static class ListComparator<T extends Comparable<T>> implements Comparator<Pair<List<T>,?>>
|
||||
{
|
||||
@Override
|
||||
public int compare(Pair<List<T>,?> p1, Pair<List<T>,?> p2) {
|
||||
List<T> o1 = p1.getKey();
|
||||
List<T> o2 = p2.getKey();
|
||||
for (int i = 0; i < Math.min(o1.size(), o2.size()); i++) {
|
||||
int c = o1.get(i).compareTo(o2.get(i));
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return Integer.compare(o1.size(), o2.size());
|
||||
}
|
||||
}
|
||||
|
||||
public LiteralArgumentBuilder<ServerCommandSource> 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<Value, Value> commandsData : ((MapValue)commands).getMap().entrySet())
|
||||
throw CommandArgument.error("'commands' element in config should be a map");
|
||||
List<Pair<List<CommandToken>,FunctionArgument<Value>>> commandEntries = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<Value, Value> commandsData : ((MapValue)commands).getMap().entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList()))
|
||||
{
|
||||
String path = commandsData.getKey().getString();
|
||||
List<CommandToken> elements = CommandToken.parseSpec(commandsData.getKey().getString(), this);
|
||||
FunctionArgument<Value> funSpec = FunctionArgument.fromCommandSpec(this, commandsData.getValue());
|
||||
//if (fun)
|
||||
|
||||
//... WIP
|
||||
|
||||
//Map<String, Value> 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;
|
||||
commandEntries.sort(new ListComparator<>());
|
||||
if (!appConfig.getOrDefault(StringValue.of("allow_command_conflicts"), Value.FALSE).getBoolean())
|
||||
{
|
||||
for (int i = 0; i < commandEntries.size()-1; i++)
|
||||
{
|
||||
List<CommandToken> first = commandEntries.get(i).getKey();
|
||||
List<CommandToken> other = commandEntries.get(i+1).getKey();
|
||||
int checkSize = Math.min(first.size(), other.size());
|
||||
for (int t = 0; t < checkSize; t++)
|
||||
{
|
||||
CommandToken tik = first.get(t);
|
||||
CommandToken tok = other.get(t);
|
||||
if (tik.isArgument && tok.isArgument && !tik.surface.equals(tok.surface))
|
||||
throw CommandArgument.error("Conflicting commands: \n"+
|
||||
" - [" +first.stream().map(tt -> tt.surface).collect(Collectors.joining(" ")) + "] at "+tik.surface+"\n"+
|
||||
" - [" +other.stream().map(tt -> tt.surface).collect(Collectors.joining(" ")) + "] at "+tok.surface+"\n");
|
||||
if (!tik.equals(tok))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.getNewCommandTree(commandEntries);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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<ServerCommandSource> 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");
|
||||
@@ -283,11 +303,16 @@ public class CarpetScriptServer
|
||||
|
||||
for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
||||
{
|
||||
if (host.appConfig.containsKey(StringValue.of("legacy_command_type_support"))) // temporary stuff for testing
|
||||
if (host.appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean())
|
||||
{
|
||||
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<ServerCommandSource> ctx, String hostName, FunctionValue function, List<String> paramNames) throws CommandSyntaxException
|
||||
{
|
||||
CarpetScriptHost cHost = modules.get(hostName).retrieveForExecution(ctx.getSource());
|
||||
List<String> 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<Value> 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<ServerCommandSource> getFancyCommand(LiteralArgumentBuilder<ServerCommandSource> command, CarpetScriptHost host, FunctionValue function ) throws CommandSyntaxException
|
||||
{
|
||||
List<String> 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<String> reversedArgs = new ArrayList<>(argNames);
|
||||
Collections.reverse(reversedArgs);
|
||||
RequiredArgumentBuilder<ServerCommandSource, ?> 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)
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ public class FunctionArgument<T> 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());
|
||||
|
||||
@@ -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;
|
||||
@@ -89,6 +96,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<? extends CommandArgument> baseTypes = Lists.newArrayList(
|
||||
// default
|
||||
new StringArgument(),
|
||||
@@ -241,10 +253,10 @@ 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);
|
||||
String suffix = components[components.length-1];
|
||||
CommandArgument arg = host.appArgTypes.get(suffix);
|
||||
if (arg != null) return arg;
|
||||
return builtIns.getOrDefault(suffix, DEFAULT);
|
||||
@@ -254,13 +266,16 @@ 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<String> examples;
|
||||
protected boolean needsMatching = false;
|
||||
protected boolean needsMatching;
|
||||
protected boolean caseSensitive = true;
|
||||
protected SuggestionProvider<ServerCommandSource> suggestionProvider;
|
||||
protected FunctionArgument<Value> customSuggester;
|
||||
|
||||
|
||||
protected CommandArgument(
|
||||
String suffix,
|
||||
@@ -287,23 +302,29 @@ public abstract class CommandArgument
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config)
|
||||
public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
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);
|
||||
CommandArgument variant = builtIns.get(baseType).builder().get();
|
||||
variant.configure(config);
|
||||
CommandArgument variant = builtIns.get(baseType).factory().get();
|
||||
variant.configure(config, host);
|
||||
variant.suffix = suffix;
|
||||
return variant;
|
||||
}
|
||||
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
caseSensitive = config.getOrDefault("case_sensitive", Value.TRUE).getBoolean();
|
||||
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()
|
||||
@@ -311,34 +332,59 @@ public abstract class CommandArgument
|
||||
.collect(Collectors.toSet());
|
||||
if (!examples.isEmpty()) needsMatching = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> suggest(
|
||||
CommandContext<ServerCommandSource> context,
|
||||
SuggestionsBuilder suggestionsBuilder
|
||||
)
|
||||
SuggestionsBuilder suggestionsBuilder,
|
||||
CarpetScriptHost host
|
||||
) throws CommandSyntaxException
|
||||
{
|
||||
String prefix = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
suggestFor(prefix).forEach(suggestionsBuilder::suggest);
|
||||
String prefix = suggestionsBuilder.getRemaining();
|
||||
if (!caseSensitive) prefix = prefix.toLowerCase(Locale.ROOT);
|
||||
suggestFor(context, prefix, host).forEach(suggestionsBuilder::suggest);
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
|
||||
protected List<String> suggestFor(String prefix)
|
||||
protected List<String> suggestFor(CommandContext<ServerCommandSource> 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<String> getOptions()
|
||||
protected Collection<String> getOptions(CommandContext<ServerCommandSource> context, CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
if (customSuggester != null)
|
||||
{
|
||||
Map<Value, Value> params = new HashMap<>();
|
||||
for(ParsedCommandNode<ServerCommandSource> pnode : context.getNodes())
|
||||
{
|
||||
CommandNode<ServerCommandSource> node = pnode.getNode();
|
||||
if (node instanceof ArgumentCommandNode)
|
||||
{
|
||||
params.put(StringValue.of(node.getName()), CommandArgument.getValue(context, node.getName(), host));
|
||||
}
|
||||
}
|
||||
List<Value> 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)
|
||||
{
|
||||
if (!caseSensitive)
|
||||
{
|
||||
//prefix = prefix.toLowerCase(Locale.ROOT);
|
||||
option = option.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
for(int i = 0; !option.startsWith(prefix, i); ++i)
|
||||
{
|
||||
i = option.indexOf('_', i);
|
||||
@@ -347,12 +393,11 @@ public abstract class CommandArgument
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract Supplier<CommandArgument> builder();
|
||||
protected abstract Supplier<CommandArgument> factory();
|
||||
|
||||
private static class StringArgument extends CommandArgument
|
||||
{
|
||||
Set<String> validOptions = Collections.emptySet();
|
||||
boolean caseSensitive = false;
|
||||
private StringArgument()
|
||||
{
|
||||
super("string", StringArgumentType.StringType.QUOTABLE_PHRASE.getExamples(), true);
|
||||
@@ -377,25 +422,27 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
caseSensitive = config.getOrDefault("case_sensitive", Value.FALSE).getBoolean();
|
||||
super.configure(config, host);
|
||||
if (config.containsKey("options"))
|
||||
{
|
||||
Value optionsValue = config.get("options");
|
||||
if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom sting type requires options passed as a list");
|
||||
validOptions = ((ListValue) optionsValue).getItems().stream()
|
||||
.map(v -> caseSensitive?v.getString():v.getString().toLowerCase(Locale.ROOT))
|
||||
.map(v -> caseSensitive?v.getString():(v.getString().toLowerCase(Locale.ROOT)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getOptions() { return validOptions.isEmpty()?super.getOptions():validOptions; }
|
||||
protected Collection<String> getOptions(CommandContext<ServerCommandSource> context, CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
return validOptions.isEmpty()?super.getOptions(context, host):validOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder() { return WordArgument::new; }
|
||||
protected Supplier<CommandArgument> factory() { return WordArgument::new; }
|
||||
}
|
||||
|
||||
private static class WordArgument extends StringArgument
|
||||
@@ -404,7 +451,7 @@ public abstract class CommandArgument
|
||||
@Override
|
||||
public ArgumentType<?> getArgumentType() { return StringArgumentType.word(); }
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder() { return WordArgument::new; }
|
||||
protected Supplier<CommandArgument> factory() { return WordArgument::new; }
|
||||
}
|
||||
|
||||
private static class GreedyStringArgument extends StringArgument
|
||||
@@ -413,7 +460,7 @@ public abstract class CommandArgument
|
||||
@Override
|
||||
public ArgumentType<?> getArgumentType() { return StringArgumentType.greedyString(); }
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder() { return GreedyStringArgument::new; }
|
||||
protected Supplier<CommandArgument> factory() { return GreedyStringArgument::new; }
|
||||
}
|
||||
|
||||
private static class BlockPosArgument extends CommandArgument
|
||||
@@ -441,14 +488,14 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
mustBeLoaded = config.getOrDefault("loaded", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return BlockPosArgument::new;
|
||||
}
|
||||
@@ -476,14 +523,14 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
blockCentered = config.getOrDefault("block_centered", Value.TRUE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return LocationArgument::new;
|
||||
}
|
||||
@@ -496,7 +543,7 @@ public abstract class CommandArgument
|
||||
|
||||
private EntityArgument()
|
||||
{
|
||||
super("entity", EntityArgumentType.entities().getExamples(), false);
|
||||
super("entities", EntityArgumentType.entities().getExamples(), false);
|
||||
onlyFans = false;
|
||||
single = false;
|
||||
}
|
||||
@@ -524,15 +571,15 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
onlyFans = config.getOrDefault("players", Value.FALSE).getBoolean();
|
||||
single = config.getOrDefault("single", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return EntityArgument::new;
|
||||
}
|
||||
@@ -544,7 +591,7 @@ public abstract class CommandArgument
|
||||
|
||||
private PlayerProfileArgument()
|
||||
{
|
||||
super("player", GameProfileArgumentType.gameProfile().getExamples(), false);
|
||||
super("players", GameProfileArgumentType.gameProfile().getExamples(), false);
|
||||
single = false;
|
||||
}
|
||||
@Override
|
||||
@@ -565,14 +612,14 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
single = config.getOrDefault("single", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return PlayerProfileArgument::new;
|
||||
}
|
||||
@@ -606,14 +653,14 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
single = config.getOrDefault("single", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return PlayerProfileArgument::new;
|
||||
}
|
||||
@@ -643,14 +690,14 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
mapRequired = !config.getOrDefault("allow_element", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return TagArgument::new;
|
||||
}
|
||||
@@ -683,15 +730,15 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return CustomIdentifierArgument::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
if (config.containsKey("options"))
|
||||
{
|
||||
Value optionsValue = config.get("options");
|
||||
@@ -731,9 +778,9 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
if (config.containsKey("min"))
|
||||
{
|
||||
min = NumericValue.asNumber(config.get("min"), "min").getDouble();
|
||||
@@ -746,7 +793,7 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return FloatArgument::new;
|
||||
}
|
||||
@@ -782,9 +829,9 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
protected void configure(Map<String, Value> config, CarpetScriptHost host)
|
||||
{
|
||||
super.configure(config);
|
||||
super.configure(config, host);
|
||||
if (config.containsKey("min"))
|
||||
{
|
||||
min = NumericValue.asNumber(config.get("min"), "min").getLong();
|
||||
@@ -797,9 +844,9 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return FloatArgument::new;
|
||||
return IntArgument::new;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,7 +914,7 @@ public abstract class CommandArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
protected Supplier<CommandArgument> factory()
|
||||
{
|
||||
return () -> new VanillaUnconfigurableArgument(getTypeSuffix(), argumentTypeSupplier, valueExtractor, providesExamples);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
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 java.util.Objects;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class CommandToken implements Comparable<CommandToken>
|
||||
{
|
||||
public String surface;
|
||||
public boolean isArgument;
|
||||
public CommandArgument type;
|
||||
|
||||
private CommandToken(String surface, CommandArgument type )
|
||||
{
|
||||
this.surface = surface;
|
||||
this.type = type;
|
||||
isArgument = type != null;
|
||||
}
|
||||
|
||||
public static CommandToken getToken(String source, CarpetScriptHost host)
|
||||
{
|
||||
// todo add more type checking and return null
|
||||
if (!source.startsWith("<"))
|
||||
{
|
||||
if (!source.matches("[_a-zA-Z]+")) return null;
|
||||
return new CommandToken(source, null);
|
||||
}
|
||||
source = source.substring(1, source.length()-1);
|
||||
if (!source.matches("[_a-zA-Z]+")) return null;
|
||||
CommandArgument arg = CommandArgument.getTypeForArgument(source, host);
|
||||
return new CommandToken(source, arg);
|
||||
}
|
||||
|
||||
public static List<CommandToken> parseSpec(String spec, CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
spec = spec.trim();
|
||||
if (spec.isEmpty()) return Collections.emptyList();
|
||||
List<CommandToken> 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<String> tokens = Lists.newArrayList(function.getString());
|
||||
for (String arg : function.getArguments()) tokens.add("<"+arg+">");
|
||||
return String.join(" ", tokens);
|
||||
}
|
||||
|
||||
|
||||
public ArgumentBuilder<ServerCommandSource, ?> getCommandNode(CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
if (isArgument)
|
||||
return CommandArgument.argumentNode(surface, host);
|
||||
return literal(surface);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CommandToken that = (CommandToken) o;
|
||||
return surface.equals(that.surface) &&
|
||||
Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(surface, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CommandToken o)
|
||||
{
|
||||
if (isArgument && !o.isArgument) return 1;
|
||||
if (!isArgument && o.isArgument) return -1;
|
||||
return surface.compareTo(o.surface);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user