From 9e8556f9d9504651df0022a57edddd2487dcb48b Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 9 Nov 2020 03:04:26 -0400 Subject: [PATCH] command arguments done --- .../carpet/script/CarpetScriptServer.java | 27 +- .../script/command/CommandArgument.java | 408 +++++++++++++++--- .../java/carpet/script/value/ListValue.java | 9 +- .../carpet/script/value/NumericValue.java | 7 + .../carpet/script/value/ValueConversions.java | 49 +++ 5 files changed, 422 insertions(+), 78 deletions(-) diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index d8a7ff66..dfe5ed28 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -239,30 +239,35 @@ public class CarpetScriptServer return false; } //addEvents(source, name); - addLegacyCommand(source, name, reload, !isRuleApp); // this needs to be moved to config reader, only supporting legacy command here + 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"); + return false; + } long end = System.nanoTime(); CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms"); return true; } - private void addLegacyCommand(ServerCommandSource source, String hostName, boolean isReload, boolean notifySource) + private boolean addLegacyCommand(ServerCommandSource source, String hostName, boolean isReload, boolean notifySource) { CarpetScriptHost host = modules.get(hostName); String loaded = isReload?"reloaded":"loaded"; if (host == null) { - return; + return true; } if (host.getFunction("__command") == null) { if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+"."); - return; + return true; } if (holyMoly.contains(hostName)) { Messenger.m(source, "gi "+hostName+" app "+loaded+" with no command."); Messenger.m(source, "gi Tried to mask vanilla command."); - return; + return true; } LiteralArgumentBuilder command = literal(hostName). @@ -279,7 +284,14 @@ public class CarpetScriptServer { if (host.appConfig.containsKey(StringValue.of("legacy_command_type_support"))) // temporary stuff for testing { - command = getFancyCommand(command, host, host.getFunction(function)); + try + { + command = getFancyCommand(command, host, host.getFunction(function)); + } + catch (CommandSyntaxException e) + { + return false; + } } else { @@ -304,6 +316,7 @@ public class CarpetScriptServer if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+" with /"+hostName+" command"); server.getCommandManager().getDispatcher().register(command); CarpetServer.settingsManager.notifyPlayersCommandsChanged(); + return true; } private int execute(CommandContext ctx, String hostName, FunctionValue function, List paramNames) throws CommandSyntaxException @@ -322,7 +335,7 @@ public class CarpetScriptServer return (int) response.readInteger(); } - private LiteralArgumentBuilder getFancyCommand(LiteralArgumentBuilder command, CarpetScriptHost host, FunctionValue function ) + private LiteralArgumentBuilder getFancyCommand(LiteralArgumentBuilder command, CarpetScriptHost host, FunctionValue function ) throws CommandSyntaxException { List argNames = function.getArguments(); String hostName = host.getName(); diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index 2f388d48..b65e2cc6 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -5,12 +5,15 @@ import carpet.script.CarpetScriptHost; 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.NBTSerializableValue; 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.authlib.GameProfile; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.DoubleArgumentType; @@ -21,8 +24,11 @@ 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.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.advancement.Advancement; +import net.minecraft.command.CommandSource; import net.minecraft.command.argument.AngleArgumentType; import net.minecraft.command.argument.BlockPosArgumentType; import net.minecraft.command.argument.BlockStateArgument; @@ -30,18 +36,43 @@ import net.minecraft.command.argument.BlockStateArgumentType; import net.minecraft.command.argument.ColorArgumentType; import net.minecraft.command.argument.ColumnPosArgumentType; import net.minecraft.command.argument.DimensionArgumentType; +import net.minecraft.command.argument.EntityAnchorArgumentType; import net.minecraft.command.argument.EntityArgumentType; +import net.minecraft.command.argument.EntitySummonArgumentType; +import net.minecraft.command.argument.GameProfileArgumentType; +import net.minecraft.command.argument.IdentifierArgumentType; +import net.minecraft.command.argument.ItemEnchantmentArgumentType; +import net.minecraft.command.argument.ItemStackArgumentType; +import net.minecraft.command.argument.MessageArgumentType; +import net.minecraft.command.argument.MobEffectArgumentType; +import net.minecraft.command.argument.NbtCompoundTagArgumentType; +import net.minecraft.command.argument.NbtPathArgumentType; +import net.minecraft.command.argument.NbtTagArgumentType; +import net.minecraft.command.argument.NumberRangeArgumentType; +import net.minecraft.command.argument.ObjectiveArgumentType; +import net.minecraft.command.argument.ObjectiveCriteriaArgumentType; +import net.minecraft.command.argument.ParticleArgumentType; import net.minecraft.command.argument.RotationArgumentType; +import net.minecraft.command.argument.ScoreHolderArgumentType; +import net.minecraft.command.argument.ScoreboardSlotArgumentType; import net.minecraft.command.argument.SwizzleArgumentType; import net.minecraft.command.argument.TimeArgumentType; +import net.minecraft.command.argument.UuidArgumentType; +import net.minecraft.command.argument.Vec2ArgumentType; import net.minecraft.command.argument.Vec3ArgumentType; +import net.minecraft.command.suggestion.SuggestionProviders; import net.minecraft.entity.Entity; +import net.minecraft.predicate.NumberRange; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.server.command.BossBarCommand; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.text.LiteralText; import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec2f; +import net.minecraft.util.registry.Registry; import java.util.Collection; import java.util.Collections; @@ -62,18 +93,17 @@ public abstract class CommandArgument // default new StringArgument(), // vanilla arguments as per https://minecraft.gamepedia.com/Argument_types - new VanillaUnconfigurableArgument( "bool", - BoolArgumentType::bool, (c, p) -> new NumericValue(BoolArgumentType.getBool(c, p)), false + new VanillaUnconfigurableArgument( "bool", BoolArgumentType::bool, + (c, p) -> new NumericValue(BoolArgumentType.getBool(c, p)), false ), new FloatArgument(), new IntArgument(), new WordArgument(), new GreedyStringArgument(), - new VanillaUnconfigurableArgument( "yaw", // angle - AngleArgumentType::angle, (c, p) -> new NumericValue(AngleArgumentType.getAngle(c, p)), true + new VanillaUnconfigurableArgument( "yaw", AngleArgumentType::angle, // angle + (c, p) -> new NumericValue(AngleArgumentType.getAngle(c, p)), true ), new BlockPosArgument(), - new VanillaUnconfigurableArgument( "block", - BlockStateArgumentType::blockState, + new VanillaUnconfigurableArgument( "block", BlockStateArgumentType::blockState, (c, p) -> { BlockStateArgument result = BlockStateArgumentType.getBlockState(c, p); return new BlockValue(result.getBlockState(), null, null, ((BlockStateArgumentInterface)result).getCMTag() ); @@ -81,42 +111,100 @@ public abstract class CommandArgument false ), // block_predicate todo - not sure about the returned format. Needs to match block tags used in the API (future) - new VanillaUnconfigurableArgument("color", - ColorArgumentType::color, + new VanillaUnconfigurableArgument("teamcolor", ColorArgumentType::color, (c, p) -> { Formatting format = ColorArgumentType.getColor(c, p); - return ListValue.of(StringValue.of(format.getName()), new NumericValue(format.getColorValue()*256+255)); + return ListValue.of(StringValue.of(format.getName()), ValueConversions.ofRGB(format.getColorValue())); }, false ), - new VanillaUnconfigurableArgument("columnpos", - ColumnPosArgumentType::columnPos, (c, p) -> ValueConversions.of(ColumnPosArgumentType.getColumnPos(c, p)), false + new VanillaUnconfigurableArgument("columnpos", ColumnPosArgumentType::columnPos, + (c, p) -> ValueConversions.of(ColumnPosArgumentType.getColumnPos(c, p)), false ), // component // raw json - new VanillaUnconfigurableArgument("dimension", - DimensionArgumentType::dimension, (c, p) -> ValueConversions.of(DimensionArgumentType.getDimensionArgument(c, p)), false + new VanillaUnconfigurableArgument("dimension", DimensionArgumentType::dimension, + (c, p) -> ValueConversions.of(DimensionArgumentType.getDimensionArgument(c, p)), false ), new EntityArgument(), - // entity - // anchor // entity_anchor - // entitytype // entity_summon - // floatrange + new VanillaUnconfigurableArgument("anchor", EntityAnchorArgumentType::entityAnchor, + (c, p) -> StringValue.of(EntityAnchorArgumentType.getEntityAnchor(c, p).name()), false + ), + new VanillaUnconfigurableArgument("entitytype", EntitySummonArgumentType::entitySummon, + (c, p) -> ValueConversions.of(EntitySummonArgumentType.getEntitySummon(c, p)), SuggestionProviders.SUMMONABLE_ENTITIES + ), + new VanillaUnconfigurableArgument("floatrange", NumberRangeArgumentType::method_30918, + (c, p) -> ValueConversions.of(c.getArgument(p, NumberRange.FloatRange.class)), true + ), // function?? - // player // game_profile - // intrange - // enchantment + + new PlayerProfileArgument(), + new VanillaUnconfigurableArgument("intrange", NumberRangeArgumentType::numberRange, + (c, p) -> ValueConversions.of(NumberRangeArgumentType.IntRangeArgumentType.getRangeArgument(c, p)), true + ), + new VanillaUnconfigurableArgument("enchantment", ItemEnchantmentArgumentType::itemEnchantment, + (c, p) -> ValueConversions.of(Registry.ENCHANTMENT.getId(ItemEnchantmentArgumentType.getEnchantment(c, p))), false + ), // item_predicate ?? //same as item but accepts tags, not sure right now // slot // item_slot - // item // no tags item_stack - // message ?? isn't it just because you can't do shit with texts in vanilla? - // effect // mob_effect - // tag // for nbt_compound_tag and nbt_tag - // nbtpath - // objective - // criterion + new VanillaUnconfigurableArgument("item", ItemStackArgumentType::itemStack, + (c, p) -> ValueConversions.of(ItemStackArgumentType.getItemStackArgument(c, p).createStack(1, false)), false + ), + new VanillaUnconfigurableArgument("message", MessageArgumentType::message, + (c, p) -> new FormattedTextValue(MessageArgumentType.getMessage(c, p)), true + ), + new VanillaUnconfigurableArgument("effect", MobEffectArgumentType::mobEffect, + (c, p) -> ValueConversions.of(Registry.STATUS_EFFECT.getId(MobEffectArgumentType.getMobEffect(c, p))),false + ), + new TagArgument(), // for nbt_compound_tag and nbt_tag + new VanillaUnconfigurableArgument("path", NbtPathArgumentType::nbtPath, + (c, p) -> StringValue.of(NbtPathArgumentType.getNbtPath(c, p).toString()), true + ), + new VanillaUnconfigurableArgument("objective", ObjectiveArgumentType::objective, + (c, p) -> ValueConversions.of(ObjectiveArgumentType.getObjective(c, p)), false + ), + new VanillaUnconfigurableArgument("criterion", ObjectiveCriteriaArgumentType::objectiveCriteria, + (c, p) -> StringValue.of(ObjectiveCriteriaArgumentType.getCriteria(c, p).getName()), false + ), // operation // not sure if we need it, you have scarpet for that - // particle - // resource ?? + new VanillaUnconfigurableArgument("particle", ParticleArgumentType::particle, + (c, p) -> ValueConversions.of(ParticleArgumentType.getParticle(c, p)), false + ), + + // resource / identifier section + + new VanillaUnconfigurableArgument("recipe", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getRecipeArgument(c, p).getId()), SuggestionProviders.ALL_RECIPES + ), + new VanillaUnconfigurableArgument("advancement", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getAdvancementArgument(c, p).getId()), (ctx, builder) -> CommandSource.suggestIdentifiers(ctx.getSource().getMinecraftServer().getAdvancementLoader().getAdvancements().stream().map(Advancement::getId), builder) + ), + new VanillaUnconfigurableArgument("lootcondition", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( Registry.LOOT_CONDITION_TYPE.getId(IdentifierArgumentType.method_23727(c, p).getType())), (ctx, builder) -> CommandSource.suggestIdentifiers(ctx.getSource().getMinecraftServer().getPredicateManager().getIds(), builder) + ), + new VanillaUnconfigurableArgument("loottable", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getIdentifier(c, p)), (ctx, builder) -> CommandSource.suggestIdentifiers(ctx.getSource().getMinecraftServer().getLootManager().getTableIds(), builder) + ), + new VanillaUnconfigurableArgument("attribute", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( Registry.ATTRIBUTE.getId(IdentifierArgumentType.method_27575(c, p))), (ctx, builder) -> CommandSource.suggestIdentifiers(Registry.ATTRIBUTE.getIds(), builder) + ), + new VanillaUnconfigurableArgument("boss", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getIdentifier(c, p)), BossBarCommand.SUGGESTION_PROVIDER + ), + new VanillaUnconfigurableArgument("biome", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getIdentifier(c, p)), SuggestionProviders.ALL_BIOMES + ), + new VanillaUnconfigurableArgument("sound", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getIdentifier(c, p)), SuggestionProviders.AVAILABLE_SOUNDS + ), + new VanillaUnconfigurableArgument("storekey", IdentifierArgumentType::identifier, + (c, p) -> ValueConversions.of( IdentifierArgumentType.getIdentifier(c, p)), (ctx, builder) -> CommandSource.suggestIdentifiers(ctx.getSource().getMinecraftServer().getDataCommandStorage().getIds(), builder) + ), + + // default + new CustomIdentifierArgument(), + + // end resource / identifier // I would be great if you guys have suggestions for that. + new VanillaUnconfigurableArgument("rotation", RotationArgumentType::rotation, (c, p) -> { @@ -125,19 +213,28 @@ public abstract class CommandArgument }, true ), - // scoreholder - // scoreboardslot - new VanillaUnconfigurableArgument("swizzle", - SwizzleArgumentType::swizzle, (c, p) -> StringValue.of(SwizzleArgumentType.getSwizzle(c, p).stream().map(Direction.Axis::asString).collect(Collectors.joining())), true + new ScoreholderArgument(), + new VanillaUnconfigurableArgument("scoreboardslot", ScoreboardSlotArgumentType::scoreboardSlot, + (c, p) -> StringValue.of(Scoreboard.getDisplaySlotName(ScoreboardSlotArgumentType.getScoreboardSlot(c, p))), false + ), + new VanillaUnconfigurableArgument("swizzle", SwizzleArgumentType::swizzle, + (c, p) -> StringValue.of(SwizzleArgumentType.getSwizzle(c, p).stream().map(Direction.Axis::asString).collect(Collectors.joining())), true ), // team - new VanillaUnconfigurableArgument("time", - TimeArgumentType::time, (c, p) -> new NumericValue(IntegerArgumentType.getInteger(c, p)), false + new VanillaUnconfigurableArgument("time", TimeArgumentType::time, + (c, p) -> new NumericValue(IntegerArgumentType.getInteger(c, p)), false + ), + new VanillaUnconfigurableArgument("uuid", UuidArgumentType::uuid, + (c, p) -> StringValue.of(UuidArgumentType.getUuid(c, p).toString()), false + ), + new VanillaUnconfigurableArgument("surfacelocation", Vec2ArgumentType::vec2, // vec2 + (c, p) -> { + Vec2f res = Vec2ArgumentType.getVec2(c, p); + return ListValue.of(NumericValue.of(res.x), NumericValue.of(res.y)); + }, + false ), - // uuid - // columnlocation // vec2 new LocationArgument() - // location // vec3 ); public static final Map builtIns = baseTypes.stream().collect(Collectors.toMap(CommandArgument::getTypeSuffix, a -> a)); @@ -153,32 +250,29 @@ public abstract class CommandArgument return builtIns.getOrDefault(suffix, DEFAULT); } - public static RequiredArgumentBuilder argumentNode(String param, CarpetScriptHost host) + public static RequiredArgumentBuilder argumentNode(String param, CarpetScriptHost host) throws CommandSyntaxException { 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()); } protected String suffix; protected Collection examples; protected boolean needsMatching = false; + protected SuggestionProvider suggestionProvider; protected CommandArgument( String suffix, Collection examples, - boolean needsMatching) + boolean suggestFromExamples) { this.suffix = suffix; this.examples = examples; - this.needsMatching = needsMatching; + this.needsMatching = suggestFromExamples; } - public static ArgumentType getArgument(String param, CarpetScriptHost host) - { - return getTypeForArgument(param, host).getArgumentType(); - } - - protected abstract ArgumentType getArgumentType(); + protected abstract ArgumentType getArgumentType() throws CommandSyntaxException; public static Value getValue(CommandContext context, String param, CarpetScriptHost host) throws CommandSyntaxException @@ -422,15 +516,11 @@ public abstract class CommandArgument @Override protected Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException { - List founds = (List)EntityArgumentType.getOptionalEntities(context, param); - if (single) - { - return founds.isEmpty()?Value.NULL:new EntityValue(founds.get(0)); - } - else - { - return ListValue.wrap(founds.stream().map(EntityValue::new).collect(Collectors.toList())); - } + Collection founds = EntityArgumentType.getOptionalEntities(context, param); + if (!single) return ListValue.wrap(founds.stream().map(EntityValue::new).collect(Collectors.toList())); + if (founds.size() == 0) return Value.NULL; + if (founds.size() == 1) return new EntityValue(founds.iterator().next()); + throw new SimpleCommandExceptionType(new LiteralText("Multiple entities returned while only one was requested")).create(); } @Override @@ -448,6 +538,169 @@ public abstract class CommandArgument } } + private static class PlayerProfileArgument extends CommandArgument + { + boolean single; + + private PlayerProfileArgument() + { + super("player", GameProfileArgumentType.gameProfile().getExamples(), false); + single = false; + } + @Override + protected ArgumentType getArgumentType() + { + return GameProfileArgumentType.gameProfile(); + } + + @Override + protected Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException + { + Collection profiles = GameProfileArgumentType.getProfileArgument(context, param); + if (!single) return ListValue.wrap(profiles.stream().map(p -> StringValue.of(p.getName())).collect(Collectors.toList())); + int size = profiles.size(); + if (size == 0) return Value.NULL; + if (size == 1) return StringValue.of(profiles.iterator().next().getName()); + throw new SimpleCommandExceptionType(new LiteralText("Multiple game profiles returned while only one was requested")).create(); + } + + @Override + protected void configure(Map config) + { + super.configure(config); + single = config.getOrDefault("single", Value.FALSE).getBoolean(); + } + + @Override + protected Supplier builder() + { + return PlayerProfileArgument::new; + } + } + + private static class ScoreholderArgument extends CommandArgument + { + boolean single; + + private ScoreholderArgument() + { + super("scoreholder", ScoreHolderArgumentType.scoreHolder().getExamples(), false); + single = false; + suggestionProvider = ScoreHolderArgumentType.SUGGESTION_PROVIDER; + } + @Override + protected ArgumentType getArgumentType() + { + return single?ScoreHolderArgumentType.scoreHolder():ScoreHolderArgumentType.scoreHolders(); + } + + @Override + protected Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException + { + Collection holders = ScoreHolderArgumentType.getScoreHolders(context, param); + if (!single) return ListValue.wrap(holders.stream().map(StringValue::of).collect(Collectors.toList())); + int size = holders.size(); + if (size == 0) return Value.NULL; + if (size == 1) return StringValue.of(holders.iterator().next()); + throw new SimpleCommandExceptionType(new LiteralText("Multiple score holders returned while only one was requested")).create(); + } + + @Override + protected void configure(Map config) + { + super.configure(config); + single = config.getOrDefault("single", Value.FALSE).getBoolean(); + } + + @Override + protected Supplier builder() + { + return PlayerProfileArgument::new; + } + } + + private static class TagArgument extends CommandArgument + { + boolean mapRequired; + private TagArgument() + { + super("tag", NbtCompoundTagArgumentType.nbtCompound().getExamples(), false); + mapRequired = true; + } + @Override + protected ArgumentType getArgumentType() + { + return mapRequired?NbtCompoundTagArgumentType.nbtCompound(): NbtTagArgumentType.nbtTag(); + } + + @Override + protected Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException + { + if (mapRequired) + return new NBTSerializableValue(NbtCompoundTagArgumentType.getCompoundTag(context, param)); + else + return new NBTSerializableValue(NbtTagArgumentType.getTag(context, param)); + } + + @Override + protected void configure(Map config) + { + super.configure(config); + mapRequired = !config.getOrDefault("allow_element", Value.FALSE).getBoolean(); + } + + @Override + protected Supplier builder() + { + return TagArgument::new; + } + } + + private static class CustomIdentifierArgument extends CommandArgument + { + Set validOptions = Collections.emptySet(); + + protected CustomIdentifierArgument() + { + super("identifier", Collections.emptyList(), true); + } + + @Override + protected ArgumentType getArgumentType() + { + return IdentifierArgumentType.identifier(); + } + + @Override + protected Value getValueFromContext(CommandContext context, String param) throws CommandSyntaxException + { + Identifier choseValue = IdentifierArgumentType.getIdentifier(context, param); + if (!validOptions.isEmpty() && !validOptions.contains(choseValue)) + { + throw new SimpleCommandExceptionType(new LiteralText("Incorrect value for "+param+": "+choseValue)).create(); + } + return ValueConversions.of(choseValue); + } + + @Override + protected Supplier builder() + { + return CustomIdentifierArgument::new; + } + + @Override + protected void configure(Map config) + { + super.configure(config); + 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 -> new Identifier(v.getString())).collect(Collectors.toSet()); + } + } + } + private static class FloatArgument extends CommandArgument { private Double min = null; @@ -556,26 +809,53 @@ public abstract class CommandArgument Value apply(CommandContext ctx, String param) throws CommandSyntaxException; } + @FunctionalInterface + private interface ArgumentProvider + { + ArgumentType get() throws CommandSyntaxException; + } + public static class VanillaUnconfigurableArgument extends CommandArgument { - private Supplier> argumentTypeSupplier; - private ValueExtractor valueExtractor; - private boolean providesExamples; + private final ArgumentProvider argumentTypeSupplier; + private final ValueExtractor valueExtractor; + private final boolean providesExamples; public VanillaUnconfigurableArgument( String suffix, - Supplier> argumentTypeSupplier, + ArgumentProvider argumentTypeSupplier, ValueExtractor valueExtractor, - boolean providesExamples + boolean suggestFromExamples ) { - super(suffix, argumentTypeSupplier.get().getExamples(), providesExamples); - this.providesExamples = providesExamples; + super(suffix, null, suggestFromExamples); + try + { + this.examples = argumentTypeSupplier.get().getExamples(); + } + catch (CommandSyntaxException e) + { + this.examples = Collections.emptyList(); + } + this.providesExamples = suggestFromExamples; + this.argumentTypeSupplier = argumentTypeSupplier; + this.valueExtractor = valueExtractor; + } + public VanillaUnconfigurableArgument( + String suffix, + ArgumentProvider argumentTypeSupplier, + ValueExtractor valueExtractor, + SuggestionProvider suggester + ) + { + super(suffix, Collections.emptyList(), false); + this.suggestionProvider = suggester; + this.providesExamples = false; this.argumentTypeSupplier = argumentTypeSupplier; this.valueExtractor = valueExtractor; } @Override - protected ArgumentType getArgumentType() + protected ArgumentType getArgumentType() throws CommandSyntaxException { return argumentTypeSupplier.get(); } diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index a05c3955..0c730b63 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -76,15 +76,10 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf items = list; } + @Deprecated public static Value fromItemStack(ItemStack itemstack) { - if (itemstack == null || itemstack.isEmpty()) - return Value.NULL; - return ListValue.of( - new StringValue(nameFromRegistryId(Registry.ITEM.getId(itemstack.getItem()))), - new NumericValue(itemstack.getCount()), - NBTSerializableValue.fromStack(itemstack) - ); + return ValueConversions.of(itemstack); } public static Value fromTriple(double a, double b, double c) diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index 128f5344..97ea3a0b 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -37,6 +37,13 @@ public class NumericValue extends Value return ((NumericValue) v1); } + public static Value of(T value) + { + if (value == null) return Value.NULL; + if (value.doubleValue() == value.longValue()) return new NumericValue(value.longValue()); + return new NumericValue(value.doubleValue()); + } + @Override public String getString() diff --git a/src/main/java/carpet/script/value/ValueConversions.java b/src/main/java/carpet/script/value/ValueConversions.java index a8b7c6a5..91f6c77e 100644 --- a/src/main/java/carpet/script/value/ValueConversions.java +++ b/src/main/java/carpet/script/value/ValueConversions.java @@ -1,12 +1,19 @@ package carpet.script.value; import carpet.script.exception.InternalExpressionException; +import carpet.utils.BlockInfo; +import net.minecraft.block.MaterialColor; import net.minecraft.entity.Entity; import net.minecraft.entity.ai.brain.LookTarget; import net.minecraft.entity.ai.brain.WalkTarget; import net.minecraft.entity.ai.pathing.Path; import net.minecraft.entity.ai.pathing.PathNode; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.item.ItemStack; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.predicate.NumberRange; +import net.minecraft.scoreboard.ScoreboardCriterion; +import net.minecraft.scoreboard.ScoreboardObjective; import net.minecraft.server.MinecraftServer; import net.minecraft.server.world.ServerWorld; @@ -57,6 +64,48 @@ public class ValueConversions return of(world.getRegistryKey().getValue()); } + public static Value of(MaterialColor color) {return ListValue.of(StringValue.of(BlockInfo.mapColourName.get(color)), ofRGB(color.color));} + + public static Value of(NumberRange range) { return ListValue.of(NumericValue.of(range.getMin()), NumericValue.of(range.getMax()));} + + public static Value of(ItemStack stack) + { + if (stack == null || stack.isEmpty()) + return Value.NULL; + return ListValue.of( + of(Registry.ITEM.getId(stack.getItem())), + new NumericValue(stack.getCount()), + NBTSerializableValue.fromStack(stack) + ); + } + + public static Value of(ScoreboardObjective objective) + { + return ListValue.of( + StringValue.of(objective.getName()), + StringValue.of(objective.getCriterion().getName()) + ); + } + + + public static Value of(ScoreboardCriterion criteria) + { + return ListValue.of( + StringValue.of(criteria.getName()), + new NumericValue(criteria.isReadOnly()) + ); + } + + + public static Value of(ParticleEffect particle) + { + String repr = particle.asString(); + if (repr.startsWith("minecraft:")) return StringValue.of(repr.substring(10)); + return StringValue.of(repr); + } + + public static Value ofRGB(int value) {return new NumericValue(value*256+255 );} + public static World dimFromValue(Value dimensionValue, MinecraftServer server) { if (dimensionValue instanceof EntityValue)