custom args WIP

This commit is contained in:
gnembon committed 2020-11-01 01:29:30 -04:00
1 parent c827b04126
commit f8e3b2e000
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;
}
}
}