diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 3e876568..c9bf4c77 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -283,21 +283,7 @@ public class CarpetScriptHost extends ScriptHost Map config = ((MapValue) ret).getMap(); setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player")); persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.TRUE).getBoolean(); - // read custom arguments - Value arguments = config.get(StringValue.of("arguments")); - if (arguments != null) - { - if (!(arguments instanceof MapValue)) - throw new InternalExpressionException("'arguments' element in config should be a map"); - for (Map.Entry typeData : ((MapValue)arguments).getMap().entrySet()) - { - String argument = typeData.getKey().getString(); - 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, this)); - } - } + appConfig = config; } catch (NullPointerException ignored) @@ -323,8 +309,37 @@ public class CarpetScriptHost extends ScriptHost } } + public void readCustomArgumentTypes() throws CommandSyntaxException + { + // read custom arguments + Value arguments = appConfig.get(StringValue.of("arguments")); + if (arguments != null) + { + if (!(arguments instanceof MapValue)) + throw CommandArgument.error("'arguments' element in config should be a map"); + appArgTypes.clear(); + for (Map.Entry typeData : ((MapValue)arguments).getMap().entrySet()) + { + String argument = typeData.getKey().getString(); + Value spec = typeData.getValue(); + if (!(spec instanceof MapValue)) throw CommandArgument.error("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, this)); + } + } + } + public Boolean addAppCommands(Consumer notifier) { + try + { + readCustomArgumentTypes(); + } + catch (CommandSyntaxException e) + { + notifier.accept(Messenger.c("Error when handling of setting up custom argument types: "+e.getMessage())); + return false; + } if (appConfig.get(StringValue.of("commands")) != null) { try @@ -342,7 +357,7 @@ public class CarpetScriptHost extends ScriptHost catch (CommandSyntaxException cse) { // failed - notifier.accept(Messenger.c("r Failed to build command system for "+getName()+" thus failed to load the app ", cse.getRawMessage())); + notifier.accept(Messenger.c("r Failed to build command system for "+getName()+" thus failed to load the app: ", cse.getRawMessage())); return null; } @@ -382,9 +397,11 @@ public class CarpetScriptHost extends ScriptHost return (int)response.readInteger(); }); + boolean hasTypeSupport = appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean(); + for (String function : globaFunctionNames(main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList())) { - if (appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean()) + if (hasTypeSupport) { try { diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index 1428e029..73018c8e 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -338,6 +338,8 @@ public class Auxiliary { expression.addLazyFunction("draw_shape", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; + ServerWorld world = cc.s.getWorld(); + MinecraftServer server = world.getServer(); ServerPlayerEntity player[] = {null}; List>> shapes = new ArrayList<>(); if (lv.size() == 1) // bulk @@ -347,14 +349,14 @@ public class Auxiliary { for (Value list : ((ListValue) specLoad).getItems()) { if (!(list instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs"); - shapes.add( ShapeDispatcher.fromFunctionArgs(cc, ((ListValue) list).getItems(), player)); + shapes.add( ShapeDispatcher.fromFunctionArgs(server, world, ((ListValue) list).getItems(), player)); } } else { List params = new ArrayList<>(); for (LazyValue v : lv) params.add(v.evalValue(c)); - shapes.add(ShapeDispatcher.fromFunctionArgs(cc, params, player)); + shapes.add(ShapeDispatcher.fromFunctionArgs(server, world, params, player)); } ShapeDispatcher.sendShape( diff --git a/src/main/java/carpet/script/argument/FunctionArgument.java b/src/main/java/carpet/script/argument/FunctionArgument.java index b942e7f7..8c52c8d5 100644 --- a/src/main/java/carpet/script/argument/FunctionArgument.java +++ b/src/main/java/carpet/script/argument/FunctionArgument.java @@ -4,10 +4,12 @@ import carpet.script.Context; import carpet.script.LazyValue; import carpet.script.ScriptHost; import carpet.script.bundled.Module; +import carpet.script.command.CommandArgument; import carpet.script.exception.InternalExpressionException; import carpet.script.value.FunctionValue; import carpet.script.value.ListValue; import carpet.script.value.Value; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.util.ArrayList; import java.util.Collections; @@ -70,14 +72,13 @@ public class FunctionArgument extends Argument return new FunctionArgument<>(fun, offset + 1 + argsize, lvargs); } - public static FunctionArgument fromCommandSpec(ScriptHost host, Value funSpec) - { + public static FunctionArgument fromCommandSpec(ScriptHost host, Value funSpec) throws CommandSyntaxException { FunctionValue function; List args = Collections.emptyList(); if (!(funSpec instanceof ListValue)) funSpec = ListValue.of(funSpec); List params = ((ListValue) funSpec).getItems(); - if (params.isEmpty()) throw new InternalExpressionException("Function has empty spec"); + if (params.isEmpty()) throw CommandArgument.error("Function has empty spec"); Value first = params.get(0); if (first instanceof FunctionValue) { @@ -86,7 +87,8 @@ public class FunctionArgument extends Argument else { String name = first.getString(); - function = host.getAssertFunction(host.main, name); + function = host.getFunction(name); + if (function == null) throw CommandArgument.error("Function "+name+" is not defined yet"); } if (params.size() > 1) args = params.subList(1,params.size()); diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index 6406373a..2a8f39bc 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -319,20 +319,20 @@ public abstract class CommandArgument return suffix; } - public static CommandArgument buildFromConfig(String suffix, Map config, CarpetScriptHost host) + public static CommandArgument buildFromConfig(String suffix, Map config, CarpetScriptHost host) throws CommandSyntaxException { if (!config.containsKey("type")) - throw new InternalExpressionException("Custom type "+suffix+" should at least specify the type"); + throw CommandArgument.error("Custom type "+suffix+" should at least specify the type"); String baseType = config.get("type").getString(); if (!builtIns.containsKey(baseType)) - throw new InternalExpressionException("Unknown base type "+baseType+" for custom type "+suffix); + throw CommandArgument.error("Unknown base type "+baseType+" for custom type "+suffix); CommandArgument variant = builtIns.get(baseType).factory().get(); variant.suffix = suffix; variant.configure(config, host); return variant; } - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { caseSensitive = config.getOrDefault("case_sensitive", Value.TRUE).getBoolean(); if (config.containsKey("suggester")) @@ -341,9 +341,9 @@ public abstract class CommandArgument } if (config.containsKey("suggest")) { - if (config.containsKey("suggester")) throw new InternalExpressionException("Attempted to provide 'suggest' list while 'suggester' is present"+" for custom type "+suffix); + if (config.containsKey("suggester")) throw error("Attempted to provide 'suggest' list while 'suggester' is present"+" for custom type "+suffix); Value suggestionValue = config.get("suggest"); - if (!(suggestionValue instanceof ListValue)) throw new InternalExpressionException("Argument suggestions needs to be a list"+" for custom type "+suffix); + if (!(suggestionValue instanceof ListValue)) throw error("Argument suggestions needs to be a list"+" for custom type "+suffix); examples = ((ListValue) suggestionValue).getItems().stream() .map(Value::getString) .collect(Collectors.toSet()); @@ -442,13 +442,13 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); if (config.containsKey("options")) { Value optionsValue = config.get("options"); - if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom string type requires options passed as a list"+" for custom type "+suffix); + if (!(optionsValue instanceof ListValue)) throw error("Custom string type requires options passed as a list"+" for custom type "+suffix); validOptions = ((ListValue) optionsValue).getItems().stream() .map(v -> caseSensitive?v.getString():(v.getString().toLowerCase(Locale.ROOT))) .collect(Collectors.toSet()); @@ -508,7 +508,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); mustBeLoaded = config.getOrDefault("loaded", Value.FALSE).getBoolean(); @@ -543,7 +543,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); blockCentered = config.getOrDefault("block_centered", Value.TRUE).getBoolean(); @@ -591,7 +591,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); onlyFans = config.getOrDefault("players", Value.FALSE).getBoolean(); @@ -632,7 +632,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); single = config.getOrDefault("single", Value.FALSE).getBoolean(); @@ -673,7 +673,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); single = config.getOrDefault("single", Value.FALSE).getBoolean(); @@ -710,7 +710,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); mapRequired = !config.getOrDefault("allow_element", Value.FALSE).getBoolean(); @@ -756,13 +756,13 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { 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"+" for custom type "+suffix); + if (!(optionsValue instanceof ListValue)) throw error("Custom sting type requires options passed as a list"+" for custom type "+suffix); validOptions = ((ListValue) optionsValue).getItems().stream().map(v -> new Identifier(v.getString())).collect(Collectors.toSet()); } } @@ -798,7 +798,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); if (config.containsKey("min")) @@ -809,7 +809,7 @@ public abstract class CommandArgument { max = NumericValue.asNumber(config.get("max"), "max").getDouble(); } - if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"+" for custom type "+suffix); + if (max != null && min == null) throw error("Double types cannot be only upper-bounded"+" for custom type "+suffix); } @Override @@ -849,7 +849,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); if (config.containsKey("min")) @@ -860,7 +860,7 @@ public abstract class CommandArgument { max = NumericValue.asNumber(config.get("max"), "max").getLong(); } - if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"+" for custom type "+suffix); + if (max != null && min == null) throw error("Double types cannot be only upper-bounded"+" for custom type "+suffix); } @Override @@ -939,7 +939,7 @@ public abstract class CommandArgument } @Override - protected void configure(Map config, CarpetScriptHost host) + protected void configure(Map config, CarpetScriptHost host) throws CommandSyntaxException { super.configure(config, host); if (config.containsKey("restrict")) @@ -947,7 +947,7 @@ public abstract class CommandArgument restrict = config.get("restrict").getString().toLowerCase(Locale.ROOT); needsMatching = true; if (!RESTRICTED_CONTAINERS.containsKey(restrict)) - throw new InternalExpressionException("Incorrect slot restriction "+restrict+" for custom type "+suffix); + throw error("Incorrect slot restriction "+restrict+" for custom type "+suffix); } } diff --git a/src/main/java/carpet/script/utils/ShapeDispatcher.java b/src/main/java/carpet/script/utils/ShapeDispatcher.java index 1e48f591..2a8b21e7 100644 --- a/src/main/java/carpet/script/utils/ShapeDispatcher.java +++ b/src/main/java/carpet/script/utils/ShapeDispatcher.java @@ -31,6 +31,7 @@ import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtString; import net.minecraft.nbt.NbtElement; import net.minecraft.particle.ParticleEffect; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; @@ -62,7 +63,7 @@ public class ShapeDispatcher private static final Map particleCache = new HashMap<>(); public static Pair> fromFunctionArgs( - CarpetContext cc, + MinecraftServer server, ServerWorld world, List lv, ServerPlayerEntity[] playerRef ) @@ -91,18 +92,18 @@ public class ShapeDispatcher for (int i=2; i < lv.size(); i++) paramList.add(lv.get(i)); params = ShapeDispatcher.parseParams(paramList); } - params.putIfAbsent("dim", new StringValue(cc.s.getWorld().getRegistryKey().getValue().toString())); + params.putIfAbsent("dim", new StringValue(world.getRegistryKey().getValue().toString())); params.putIfAbsent("duration", duration); if (params.containsKey("player")) { - ServerPlayerEntity player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), params.get("player")); + ServerPlayerEntity player = EntityValue.getPlayerByValue(server, params.get("player")); if (player == null) throw new InternalExpressionException("'player' parameter needs to represent an existing player"); params.remove("player"); playerRef[0] = player; } - return Pair.of(ShapeDispatcher.create(cc, shapeType, params), params); + return Pair.of(ShapeDispatcher.create(server, shapeType, params), params); } public static void sendShape(List players, List>> shapes) @@ -180,13 +181,13 @@ public class ShapeDispatcher return param; } - public static ExpiringShape create(CarpetContext cc, String shapeType, Map userParams) + public static ExpiringShape create(MinecraftServer server, String shapeType, Map userParams) { userParams.put("shape", new StringValue(shapeType)); userParams.keySet().forEach(key -> { Param param = Param.of.get(key); if (param==null) throw new InternalExpressionException("Unknown feature for shape: "+key); - userParams.put(key, param.validate(userParams, cc, userParams.get(key))); + userParams.put(key, param.validate(userParams, server, userParams.get(key))); }); Function,ExpiringShape> factory = ExpiringShape.shapeProviders.get(shapeType); if (factory == null) throw new InternalExpressionException("Unknown shape: "+shapeType); @@ -891,7 +892,7 @@ public class ShapeDispatcher } public abstract NbtElement toTag(Value value); //validates value, returning null if not necessary to keep it and serialize - public abstract Value validate(Map options, CarpetContext cc, Value value); // makes sure the value is proper + public abstract Value validate(Map options, MinecraftServer server, Value value); // makes sure the value is proper public abstract Value decode(NbtElement tag); } @@ -910,7 +911,7 @@ public class ShapeDispatcher super(id); } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { return value; } @@ -923,7 +924,7 @@ public class ShapeDispatcher super(id); } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { if (!(value instanceof FormattedTextValue)) value = new FormattedTextValue(new LiteralText(value.getString())); @@ -957,7 +958,7 @@ public class ShapeDispatcher @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { if (this.options.contains(value.getString())) return value; return null; @@ -969,14 +970,14 @@ public class ShapeDispatcher protected DimensionParam() { super("dim"); } @Override - public Value validate(Map options, CarpetContext cc, Value value) { return value; } + public Value validate(Map options, MinecraftServer server, Value value) { return value; } } public static class ShapeParam extends StringParam { protected ShapeParam() { super("shape"); } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { String shape = value.getString(); if (!ExpiringShape.shapeProviders.containsKey(shape)) @@ -989,7 +990,7 @@ public class ShapeDispatcher protected NumericParam(String id) { super(id); } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { if (!(value instanceof NumericValue)) throw new InternalExpressionException("'" + id + "' needs to be a number"); @@ -1027,9 +1028,9 @@ public class ShapeDispatcher public static abstract class PositiveParam extends NumericParam { protected PositiveParam(String id) { super(id); } - @Override public Value validate(Map options, CarpetContext cc, Value value) + @Override public Value validate(Map options, MinecraftServer server, Value value) { - Value ret = super.validate(options, cc, value); + Value ret = super.validate(options, server, value); if (((NumericValue)ret).getDouble()<=0) throw new InternalExpressionException("'"+id+"' should be positive"); return ret; } @@ -1059,9 +1060,9 @@ public class ShapeDispatcher public Value decode(NbtElement tag) { return new NumericValue(((NbtInt)tag).intValue()); } @Override public NbtElement toTag(Value value) { return NbtInt.of(NumericValue.asNumber(value, id).getInt()); } - @Override public Value validate(Map options, CarpetContext cc, Value value) + @Override public Value validate(Map options, MinecraftServer server, Value value) { - Value ret = super.validate(options, cc, value); + Value ret = super.validate(options, server, value); if (((NumericValue)ret).getDouble()<0) throw new InternalExpressionException("'"+id+"' should be non-negative"); return ret; } @@ -1073,9 +1074,9 @@ public class ShapeDispatcher public Value decode(NbtElement tag) { return new NumericValue(((NbtFloat)tag).floatValue()); } @Override public NbtElement toTag(Value value) { return NbtFloat.of(NumericValue.asNumber(value, id).getFloat()); } - @Override public Value validate(Map options, CarpetContext cc, Value value) + @Override public Value validate(Map options, MinecraftServer server, Value value) { - Value ret = super.validate(options, cc, value); + Value ret = super.validate(options, server, value); if (((NumericValue)ret).getDouble()<0) throw new InternalExpressionException("'"+id+"' should be non-negative"); return ret; } @@ -1093,11 +1094,11 @@ public class ShapeDispatcher roundsUpForBlocks = doesRoundUpForBlocks; } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { - return validate(this, options, cc, value, roundsUpForBlocks); + return validate(this, options, value, roundsUpForBlocks); } - public static Value validate(Param p, Map options, CarpetContext cc, Value value, boolean roundsUp) + public static Value validate(Param p, Map options, Value value, boolean roundsUp) { if (value instanceof BlockValue) { @@ -1165,13 +1166,13 @@ public class ShapeDispatcher super(id); } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { if (!(value instanceof ListValue)) throw new InternalExpressionException(id+ " parameter should be a list"); List points = new ArrayList<>(); for (Value point: ((ListValue) value).getItems()) - points.add(Vec3Param.validate(this, options, cc, point, false)); + points.add(Vec3Param.validate(this, options, point, false)); return ListValue.wrap(points); } @@ -1233,10 +1234,10 @@ public class ShapeDispatcher } @Override - public Value validate(Map options, CarpetContext cc, Value value) + public Value validate(Map options, MinecraftServer server, Value value) { if (value instanceof EntityValue) return new NumericValue(((EntityValue) value).getEntity().getId()); - ServerPlayerEntity player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), value); + ServerPlayerEntity player = EntityValue.getPlayerByValue(server, value); if (player == null) throw new InternalExpressionException(id+" parameter needs to represent an entity or player"); return new NumericValue(player.getId());