1 file changed
+251
@@ -0,0 +1,251 @@
|
||||
package carpet.script.command;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
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.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.command.argument.BlockPosArgumentType;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class CommandArgument
|
||||
{
|
||||
private static final List<? extends CommandArgument> baseTypes = Lists.newArrayList(
|
||||
new WordArgument(), new PosArgument(), new FloatArgument()
|
||||
);
|
||||
|
||||
public static final Map<String, CommandArgument> builtIns = baseTypes.stream().collect(Collectors.toMap(CommandArgument::getTypeSuffix, a -> a));
|
||||
|
||||
private String suffix;
|
||||
private final Collection<String> examples;
|
||||
|
||||
protected CommandArgument(
|
||||
String suffix,
|
||||
Collection<String> examples)
|
||||
{
|
||||
this.suffix = suffix;
|
||||
this.examples = examples;
|
||||
}
|
||||
|
||||
public abstract ArgumentType<?> getArgument();
|
||||
|
||||
public abstract Value getValueFromContext(CommandContext<ServerCommandSource> context, String param) throws CommandSyntaxException;
|
||||
|
||||
|
||||
|
||||
public String getTypeSuffix()
|
||||
{
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public Collection<String> getExamples()
|
||||
{
|
||||
return examples;
|
||||
}
|
||||
|
||||
|
||||
public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config)
|
||||
{
|
||||
if (!config.containsKey("type"))
|
||||
throw new InternalExpressionException("Custom types should at least specify the base 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);
|
||||
variant.suffix = suffix;
|
||||
return variant;
|
||||
};
|
||||
|
||||
protected abstract void configure(Map<String, Value> config);
|
||||
|
||||
|
||||
protected CompletableFuture<Suggestions> suggest(
|
||||
CommandContext<ServerCommandSource> context,
|
||||
SuggestionsBuilder suggestionsBuilder
|
||||
)
|
||||
{
|
||||
String prefix = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
suggestFor(prefix).forEach(suggestionsBuilder::suggest);
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
|
||||
protected List<String> suggestFor(String prefix)
|
||||
{
|
||||
return getOptions().stream().filter(s -> optionMatchesPrefix(prefix, s)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected Collection<String> getOptions()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected boolean optionMatchesPrefix(String prefix, String option)
|
||||
{
|
||||
for(int i = 0; !option.startsWith(prefix, i); ++i)
|
||||
{
|
||||
i = option.indexOf('_', i);
|
||||
if (i < 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract Supplier<CommandArgument> builder();
|
||||
|
||||
public static class WordArgument extends CommandArgument
|
||||
{
|
||||
Set<String> validOptions = Collections.emptySet();
|
||||
boolean caseSensitive = false;
|
||||
private WordArgument()
|
||||
{
|
||||
super("string", StringArgumentType.StringType.SINGLE_WORD.getExamples());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgumentType<?> getArgument()
|
||||
{
|
||||
return StringArgumentType.word();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValueFromContext(CommandContext<ServerCommandSource> context, String param) throws CommandSyntaxException
|
||||
{
|
||||
String choseValue = StringArgumentType.getString(context, param);
|
||||
if (!caseSensitive) choseValue = choseValue.toLowerCase(Locale.ROOT);
|
||||
if (!validOptions.isEmpty() && !validOptions.contains(choseValue))
|
||||
{
|
||||
throw new SimpleCommandExceptionType(new LiteralText("")).create();
|
||||
}
|
||||
return StringValue.of(choseValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
{
|
||||
caseSensitive = config.getOrDefault("case_sensitive", Value.FALSE).getBoolean();
|
||||
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))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getOptions() { return validOptions; }
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder() { return WordArgument::new; }
|
||||
}
|
||||
|
||||
public static class PosArgument extends CommandArgument
|
||||
{
|
||||
private boolean mustBeLoaded = false;
|
||||
|
||||
private PosArgument()
|
||||
{
|
||||
super("pos", BlockPosArgumentType.blockPos().getExamples());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgumentType<?> getArgument()
|
||||
{
|
||||
return BlockPosArgumentType.blockPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValueFromContext(CommandContext<ServerCommandSource> context, String param) throws CommandSyntaxException
|
||||
{
|
||||
BlockPos pos = mustBeLoaded
|
||||
? BlockPosArgumentType.getLoadedBlockPos(context, param)
|
||||
: BlockPosArgumentType.getBlockPos(context, param);
|
||||
return ValueConversions.fromPos(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
{
|
||||
mustBeLoaded = config.getOrDefault("loaded", Value.FALSE).getBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
{
|
||||
return PosArgument::new;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FloatArgument extends CommandArgument
|
||||
{
|
||||
private Double min = null;
|
||||
private Double max = null;
|
||||
private FloatArgument()
|
||||
{
|
||||
super("float", DoubleArgumentType.doubleArg().getExamples());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgumentType<?> getArgument()
|
||||
{
|
||||
if (min != null)
|
||||
{
|
||||
if (max != null)
|
||||
{
|
||||
return DoubleArgumentType.doubleArg(min, max);
|
||||
}
|
||||
return DoubleArgumentType.doubleArg(min);
|
||||
}
|
||||
return DoubleArgumentType.doubleArg();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValueFromContext(CommandContext<ServerCommandSource> context, String param) throws CommandSyntaxException
|
||||
{
|
||||
return new NumericValue(DoubleArgumentType.getDouble(context, param));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Map<String, Value> config)
|
||||
{
|
||||
if (config.containsKey("min"))
|
||||
{
|
||||
min = NumericValue.asNumber(config.get("min"), "min").getDouble();
|
||||
}
|
||||
if (config.containsKey("max"))
|
||||
{
|
||||
max = NumericValue.asNumber(config.get("max"), "max").getDouble();
|
||||
}
|
||||
if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<CommandArgument> builder()
|
||||
{
|
||||
return FloatArgument::new;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user