Merge branch 'master' into 1.17
This commit is contained in:
commit
5c4744a93b
26 files changed
+497
-99
No files matched your search
@@ -417,20 +417,26 @@ public class CarpetSettings
|
||||
|
||||
private static class ModulePermissionLevel extends Validator<String> {
|
||||
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
||||
CarpetSettings.runPermissionLevel = SettingsManager.getCommandLevel(newValue);
|
||||
int permissionLevel = SettingsManager.getCommandLevel(newValue);
|
||||
if (source != null && !source.hasPermissionLevel(permissionLevel))
|
||||
return null;
|
||||
CarpetSettings.runPermissionLevel = permissionLevel;
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
return newValue;
|
||||
}
|
||||
@Override
|
||||
public String description() { return "Also controls permission level of commands executed via `run()`";}
|
||||
public String description() { return "When changing the rule, you must at least have the permission level you are trying to give it";}
|
||||
}
|
||||
@Rule(
|
||||
desc = "Enables restrictions for arbitrary code execution with scarpet",
|
||||
extra = {
|
||||
"Users that don't have this permission level",
|
||||
"won't be able to load apps or /script run"
|
||||
"won't be able to load apps or /script run.",
|
||||
"It is also the permission level apps will",
|
||||
"have when running commands with run()"
|
||||
},
|
||||
category = {COMMAND, SCARPET},
|
||||
validate = ModulePermissionLevel.class
|
||||
category = {SCARPET},
|
||||
validate = {Validator._COMMAND_LEVEL_VALIDATOR.class, ModulePermissionLevel.class}
|
||||
)
|
||||
public static String commandScriptACE = "ops";
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -136,7 +137,8 @@ public class CarpetScriptServer
|
||||
{
|
||||
try {
|
||||
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(folder);
|
||||
if (!Files.exists(folder))
|
||||
Files.createDirectories(folder);
|
||||
Optional<Path>
|
||||
scriptPath = Files.list(folder)
|
||||
.filter(script ->
|
||||
@@ -149,7 +151,8 @@ public class CarpetScriptServer
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
{
|
||||
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalFolder);
|
||||
if (!Files.exists(globalFolder))
|
||||
Files.createDirectories(globalFolder);
|
||||
scriptPath = Files.walk(globalFolder)
|
||||
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
||||
@@ -194,7 +197,8 @@ public class CarpetScriptServer
|
||||
}
|
||||
try {
|
||||
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(worldScripts);
|
||||
if (!Files.exists(worldScripts))
|
||||
Files.createDirectories(worldScripts);
|
||||
Files.list(worldScripts)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
@@ -202,8 +206,9 @@ public class CarpetScriptServer
|
||||
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
||||
{
|
||||
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts)
|
||||
if (!Files.exists(globalScripts))
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ResolvedException;
|
||||
import carpet.script.exception.ReturnStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.language.Arithmetic;
|
||||
import carpet.script.language.ControlFlow;
|
||||
import carpet.script.language.DataStructures;
|
||||
@@ -232,7 +231,7 @@ public class Expression
|
||||
if (exc instanceof ExitStatement)
|
||||
return exc;
|
||||
if (exc instanceof InternalExpressionException)
|
||||
return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack);
|
||||
return ((InternalExpressionException) exc).promote(c, e, token);
|
||||
if (exc instanceof ArithmeticException)
|
||||
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
|
||||
if (exc instanceof ResolvedException)
|
||||
@@ -624,9 +623,9 @@ public class Expression
|
||||
{
|
||||
return exprProvider.get().evalValue(c, expectedType);
|
||||
}
|
||||
catch (ContinueStatement | BreakStatement | ReturnStatement | ThrowStatement exc)
|
||||
catch (ContinueStatement | BreakStatement | ReturnStatement exc)
|
||||
{
|
||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break, throw or return, should only be used in loops, try blocks, and functions respectively.");
|
||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break or return, should only be used in loops, and functions respectively.");
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
{
|
||||
|
||||
@@ -406,6 +406,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
public int pos;
|
||||
public int linepos;
|
||||
public int lineno;
|
||||
public static final Token NONE = new Token();
|
||||
public Token morphedInto(TokenType newType, String newSurface)
|
||||
{
|
||||
Token created = new Token();
|
||||
|
||||
@@ -19,6 +19,8 @@ import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.FixedCommandSource;
|
||||
import carpet.script.utils.InputValidator;
|
||||
import carpet.script.utils.ScarpetJsonDeserializer;
|
||||
@@ -79,6 +81,7 @@ import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.dynamic.RegistryOps;
|
||||
@@ -166,10 +169,11 @@ public class Auxiliary {
|
||||
{
|
||||
expression.addLazyFunction("sound", -1, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Identifier soundName = new Identifier(lv.get(0).evalValue(c).getString());
|
||||
String rawString = lv.get(0).evalValue(c).getString();
|
||||
Identifier soundName = new Identifier(rawString);
|
||||
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
||||
if (Registry.SOUND_EVENT.get(soundName) == null)
|
||||
throw new InternalExpressionException("No such sound: "+soundName.getPath());
|
||||
throw new ThrowStatement(rawString, Throwables.UNKNOWN_SOUND);
|
||||
float volume = 1.0F;
|
||||
float pitch = 1.0F;
|
||||
SoundCategory mixer = SoundCategory.MASTER;
|
||||
@@ -876,7 +880,15 @@ public class Auxiliary {
|
||||
Value retVal;
|
||||
if (fdesc.getMiddle().equals("nbt"))
|
||||
{
|
||||
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
|
||||
Tag state;
|
||||
try
|
||||
{
|
||||
state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
|
||||
} catch (CrashException e)
|
||||
{
|
||||
throw new ThrowStatement((fdesc.getRight()?"shared/":"")+ fdesc.getLeft(), Throwables.NBT_READ);
|
||||
}
|
||||
|
||||
if (state == null) return LazyValue.NULL;
|
||||
retVal = new NBTSerializableValue(state);
|
||||
}
|
||||
@@ -889,10 +901,13 @@ public class Auxiliary {
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
Throwable exception = e;
|
||||
Throwable exc = e;
|
||||
if(e.getCause() != null)
|
||||
exception = e.getCause();
|
||||
throw new InternalExpressionException("Failed to read JSON file: "+exception.getMessage());
|
||||
exc = e.getCause();
|
||||
throw new ThrowStatement(MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("error"), StringValue.of(exc.getMessage()),
|
||||
StringValue.of("path"), StringValue.of((fdesc.getRight()?"shared/":"")+ fdesc.getLeft())
|
||||
)), Throwables.JSON_READ);
|
||||
}
|
||||
Value parsedJson = GSON.fromJson(json, Value.class);
|
||||
if (parsedJson == null)
|
||||
@@ -980,7 +995,14 @@ public class Auxiliary {
|
||||
shared = lv.get(1).evalValue(c).getBoolean();
|
||||
}
|
||||
}
|
||||
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
|
||||
Tag state;
|
||||
try
|
||||
{
|
||||
state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
|
||||
} catch (CrashException e)
|
||||
{
|
||||
throw new ThrowStatement("app data", Throwables.NBT_READ);
|
||||
}
|
||||
if (state == null)
|
||||
return (cc, tt) -> Value.NULL;
|
||||
Value retVal = new NBTSerializableValue(state);
|
||||
|
||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NBTSerializableValue;
|
||||
import carpet.script.value.NullValue;
|
||||
@@ -119,14 +121,7 @@ public class Inventories {
|
||||
}
|
||||
}
|
||||
List<Recipe<?>> recipes;
|
||||
try
|
||||
{
|
||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||
if (recipes.isEmpty())
|
||||
return LazyValue.NULL;
|
||||
List<Value> recipesOutput = new ArrayList<>();
|
||||
@@ -197,17 +192,9 @@ public class Inventories {
|
||||
{
|
||||
String itemStr = lv.get(0).evalValue(c).getString();
|
||||
Item item;
|
||||
try
|
||||
{
|
||||
Identifier id = new Identifier(itemStr);
|
||||
item = Registry.ITEM.get(id);
|
||||
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
|
||||
throw new InvalidIdentifierException("boo");
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect item: "+itemStr);
|
||||
}
|
||||
Identifier id = new Identifier(itemStr);
|
||||
item = Registry.ITEM.getOrEmpty(id).orElseThrow(() -> new ThrowStatement(itemStr, Throwables.UNKNOWN_ITEM));
|
||||
|
||||
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
|
||||
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
||||
return (_c, _t ) -> ret;
|
||||
|
||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NullValue;
|
||||
@@ -131,7 +133,7 @@ public class Scoreboards {
|
||||
criterion = ScoreboardCriterion.getOrCreateStatCriterion(critetionName).orElse(null);
|
||||
if (criterion==null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown scoreboard criterion: "+critetionName);
|
||||
throw new ThrowStatement(critetionName, Throwables.UNKNOWN_CRITERION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import carpet.script.LazyValue;
|
||||
import carpet.script.argument.BlockArgument;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.BiomeInfo;
|
||||
import carpet.script.utils.WorldTools;
|
||||
import carpet.script.value.BlockValue;
|
||||
@@ -59,7 +61,6 @@ import net.minecraft.item.SwordItem;
|
||||
import net.minecraft.item.TridentItem;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ChunkTicket;
|
||||
import net.minecraft.server.world.ChunkTicketType;
|
||||
@@ -106,7 +107,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -336,8 +336,8 @@ public class WorldAccess {
|
||||
String poiType = lv.get(locator.offset+1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||
if (!"any".equals(poiType))
|
||||
{
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiType));
|
||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiType)) return LazyValue.NULL;
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiType))
|
||||
.orElseThrow(() -> new ThrowStatement(poiType, Throwables.UNKNOWN_POI));
|
||||
condition = (tt) -> tt == type;
|
||||
}
|
||||
if (locator.offset + 2 < lv.size())
|
||||
@@ -391,9 +391,8 @@ public class WorldAccess {
|
||||
return LazyValue.FALSE;
|
||||
}
|
||||
String poiTypeString = poi.getString().toLowerCase(Locale.ROOT);
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiTypeString));
|
||||
// solving lack of null with defaulted registries
|
||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiTypeString)) throw new InternalExpressionException("Unknown POI type: "+poiTypeString);
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiTypeString))
|
||||
.orElseThrow(() -> new ThrowStatement(poiTypeString, Throwables.UNKNOWN_POI));
|
||||
int occupancy = 0;
|
||||
if (locator.offset + 1 < lv.size())
|
||||
{
|
||||
@@ -872,9 +871,8 @@ public class WorldAccess {
|
||||
{
|
||||
playerBreak = true;
|
||||
String itemString = val.getString();
|
||||
item = Registry.ITEM.get(new Identifier(itemString));
|
||||
if (item == Items.AIR && !itemString.equals("air"))
|
||||
throw new InternalExpressionException("Incorrect item: " + itemString);
|
||||
item = Registry.ITEM.getOrEmpty(new Identifier(itemString))
|
||||
.orElseThrow(() -> new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM));
|
||||
}
|
||||
}
|
||||
CompoundTag tag = null;
|
||||
@@ -1152,7 +1150,7 @@ public class WorldAccess {
|
||||
if (locator.replacement != null)
|
||||
{
|
||||
biome = world.getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(locator.replacement));
|
||||
if (biome == null) return LazyValue.NULL;
|
||||
if (biome == null) throw new ThrowStatement(locator.replacement, Throwables.UNKNOWN_BIOME) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1181,9 +1179,8 @@ public class WorldAccess {
|
||||
throw new InternalExpressionException("'set_biome' needs a biome name as an argument");
|
||||
String biomeName = lv.get(locator.offset+0).evalValue(c).getString();
|
||||
// from locatebiome command code
|
||||
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(biomeName));
|
||||
if (biome == null)
|
||||
throw new InternalExpressionException("Unknown biome: "+biomeName);
|
||||
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).getOrEmpty(new Identifier(biomeName))
|
||||
.orElseThrow(() -> new ThrowStatement(biomeName, Throwables.UNKNOWN_BIOME));
|
||||
boolean doImmediateUpdate = true;
|
||||
if (lv.size() > locator.offset+1)
|
||||
{
|
||||
@@ -1252,8 +1249,8 @@ public class WorldAccess {
|
||||
if (!(requested instanceof NullValue))
|
||||
{
|
||||
String reqString = requested.getString();
|
||||
structure = Registry.STRUCTURE_FEATURE.get(new Identifier(reqString));
|
||||
if (structure == null) throw new InternalExpressionException("Unknown structure: " + reqString);
|
||||
structure = Registry.STRUCTURE_FEATURE.getOrEmpty(new Identifier(reqString))
|
||||
.orElseThrow(() -> new ThrowStatement(reqString, Throwables.UNKNOWN_STRUCTURE));
|
||||
}
|
||||
if (lv.size() > locator.offset+1)
|
||||
{
|
||||
@@ -1333,7 +1330,7 @@ public class WorldAccess {
|
||||
throw new InternalExpressionException("'set_structure requires at least position and a structure name");
|
||||
String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||
ConfiguredStructureFeature<?, ?> configuredStructure = FeatureGenerator.resolveConfiguredStructure(structureName, world, pos);
|
||||
if (configuredStructure == null) throw new InternalExpressionException("Unknown structure: "+structureName);
|
||||
if (configuredStructure == null) throw new ThrowStatement(structureName, Throwables.UNKNOWN_STRUCTURE);
|
||||
// good 'ol pointer
|
||||
Value[] result = new Value[]{Value.NULL};
|
||||
// technically a world modification. Even if we could let it slide, we will still park it
|
||||
|
||||
@@ -8,28 +8,44 @@ import carpet.script.value.FunctionValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/* The expression evaluators exception class. */
|
||||
public class ExpressionException extends RuntimeException implements ResolvedException
|
||||
{
|
||||
public final Context context;
|
||||
public final Tokenizer.Token token;
|
||||
public final List<FunctionValue> stack = new ArrayList<>();
|
||||
private final Supplier<String> lazyMessage;
|
||||
private String cachedMessage = null;
|
||||
|
||||
public ExpressionException(Context c, Expression e, String message)
|
||||
{
|
||||
super(makeMessage(c, e, null, message));
|
||||
context = c;
|
||||
this(c, e, Tokenizer.Token.NONE, message);
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message)
|
||||
{
|
||||
super(makeMessage(c, e, t, message));
|
||||
super("Error");
|
||||
lazyMessage = () -> makeMessage(c, e, t, message);
|
||||
token = t;
|
||||
context = c;
|
||||
}
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List<FunctionValue> stack)
|
||||
{
|
||||
super(makeMessage(c, e, t, message));
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
lazyMessage = () -> makeMessage(c, e, t, message);
|
||||
token = t;
|
||||
context = c;
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, Supplier<String> messageSupplier, List<FunctionValue> stack)
|
||||
{
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
lazyMessage = () -> makeMessage(c, e, t, messageSupplier.get());
|
||||
token = t;
|
||||
context = c;
|
||||
}
|
||||
|
||||
@@ -68,4 +84,13 @@ public class ExpressionException extends RuntimeException implements ResolvedExc
|
||||
}
|
||||
return String.join("\n", errorMaker.apply(e, t, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (cachedMessage == null)
|
||||
{
|
||||
cachedMessage = lazyMessage.get();
|
||||
}
|
||||
return cachedMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer;
|
||||
import carpet.script.value.FunctionValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -13,4 +16,20 @@ public class InternalExpressionException extends RuntimeException
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Promotes this simple exception into one with context and extra information.
|
||||
*
|
||||
* <p>Provides a cleaner way of handling similar exceptions, in this case
|
||||
* {@link InternalExpressionException} and {@link ThrowStatement}
|
||||
* @param c Context
|
||||
* @param e Expression
|
||||
* @param token Token
|
||||
* @return The new {@link ExpressionException} (or {@link ProcessedThrowStatement}),
|
||||
* depending on the implementation.
|
||||
*/
|
||||
public ExpressionException promote(Context c, Expression e, Tokenizer.Token token)
|
||||
{
|
||||
return new ExpressionException(c, e, token, getMessage(), stack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer.Token;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ProcessedThrowStatement extends ExpressionException {
|
||||
public final Throwables thrownExceptionType;
|
||||
public final Value data;
|
||||
|
||||
public ProcessedThrowStatement(Context c, Expression e, Token token, List<FunctionValue> stack, Throwables thrownExceptionType, Value data) {
|
||||
super(c, e, token, () -> "Unhandled "+thrownExceptionType.getId()+" exception: "+data.getString(), stack);
|
||||
this.thrownExceptionType = thrownExceptionType;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,54 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer.Token;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ThrowStatement extends ExitStatement
|
||||
public class ThrowStatement extends InternalExpressionException
|
||||
{
|
||||
public ThrowStatement(Value value)
|
||||
private final Throwables type;
|
||||
private final Value data;
|
||||
|
||||
/**
|
||||
* Creates a throw exception from a value, and assigns it a specified message.
|
||||
* <p>To be used when throwing from Scarpet's {@code throw} function
|
||||
* @param data The value to pass
|
||||
* @param type Exception type
|
||||
*/
|
||||
public ThrowStatement(Value data, Throwables type)
|
||||
{
|
||||
super(value);
|
||||
super(type.getId());
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ThrowStatement(Value data, Throwables parent, String subtype)
|
||||
{
|
||||
super(subtype);
|
||||
this.data = data;
|
||||
this.type = new Throwables(subtype, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a throw exception.<br>
|
||||
* Conveniently creates a value from the {@code value} String
|
||||
* to be used easily in Java code
|
||||
* @param message The message to display when not handled
|
||||
* @param type An {@link Throwables} containing the inheritance data
|
||||
* for this exception. When throwing from Java,
|
||||
* those exceptions should be pre-registered.
|
||||
*/
|
||||
public ThrowStatement(String message, Throwables type)
|
||||
{
|
||||
super(type.getId());
|
||||
this.data = StringValue.of(message);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionException promote(Context c, Expression e, Token token) {
|
||||
return new ProcessedThrowStatement(c, e, token, stack, type, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class contains default Scarpet catchable types, as well as their inheritance and
|
||||
* methods to check whether filters are compatible with those.
|
||||
*/
|
||||
public class Throwables {
|
||||
private final String id;
|
||||
private final Throwables parent;
|
||||
|
||||
private static final Map<String, Throwables> byId = new HashMap<>();
|
||||
|
||||
public static final Throwables THROWN_EXCEPTION_TYPE = register("exception", null);
|
||||
public static final Throwables VALUE_EXCEPTION = register("value_exception", THROWN_EXCEPTION_TYPE);
|
||||
public static final Throwables UNKNOWN_ITEM = register("unknown_item", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_BLOCK = register("unknown_block", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_BIOME = register("unknown_biome", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_SOUND = register("unknown_sound", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_PARTICLE = register("unknown_particle", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_POI = register("unknown_poi", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION);
|
||||
public static final Throwables IO_EXCEPTION = register("io_exception", THROWN_EXCEPTION_TYPE);
|
||||
public static final Throwables NBT_READ = register("nbt_read_error", IO_EXCEPTION);
|
||||
public static final Throwables JSON_READ = register("json_read_error", IO_EXCEPTION);
|
||||
public static final Throwables USER_DEFINED = register("user_exception", THROWN_EXCEPTION_TYPE);
|
||||
|
||||
/**
|
||||
* Creates an exception and registers it to be used as parent for
|
||||
* user defined exceptions in Scarpet's throw function.
|
||||
* <p>Scarpet exceptions should have a top-level parent being {@link Throwables#THROWN_EXCEPTION_TYPE}
|
||||
* @param id The value for the exception as a {@link String}.
|
||||
* @param parent The parent of the exception being created, or <code>null</code> if top-level
|
||||
* @return The created exception
|
||||
*/
|
||||
public static Throwables register(String id, Throwables parent)
|
||||
{
|
||||
Throwables exc = new Throwables(id, parent);
|
||||
byId.put(id, exc);
|
||||
return exc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
* <p>Not suitable for creating exceptions that can't be caught.
|
||||
* Use an {@link InternalExpressionException} for that
|
||||
* @param id The exception's value as a {@link String}
|
||||
*/
|
||||
public Throwables(String id, Throwables parent)
|
||||
{
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public static Throwables getTypeForException(String type)
|
||||
{
|
||||
Throwables properType = byId.get(type);
|
||||
if (properType == null) throw new InternalExpressionException("Unknown exception type: "+type);
|
||||
return properType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given filter matches an instance of this exception, by checking equality
|
||||
* with itself and possible parents.
|
||||
* @param filter The type to check against
|
||||
* @return Whether or not the given value matches this exception's hierarchy
|
||||
*/
|
||||
public boolean isRelevantFor(String filter)
|
||||
{
|
||||
return (id.equals(filter) || (parent != null && parent.isRelevantFor(filter)));
|
||||
}
|
||||
|
||||
public boolean isUserException()
|
||||
{
|
||||
return this == USER_DEFINED || parent == USER_DEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the throwable type
|
||||
* @return The type of this exception
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,17 @@ import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ProcessedThrowStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.MapValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ControlFlow {
|
||||
public static void apply(Expression expression) // public just to get the javadoc right
|
||||
@@ -42,26 +51,98 @@ public class ControlFlow {
|
||||
|
||||
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||
|
||||
expression.addFunction("throw", (lv)-> {throw new ThrowStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||
expression.addLazyFunction("throw", -1, (c, t, lv)->
|
||||
{
|
||||
switch (lv.size())
|
||||
{
|
||||
case 0:
|
||||
throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
|
||||
case 1:
|
||||
throw new ThrowStatement(lv.get(0).evalValue(c), Throwables.USER_DEFINED );
|
||||
case 2:
|
||||
throw new ThrowStatement(lv.get(1).evalValue(c), Throwables.getTypeForException(lv.get(0).evalValue(c).getString()));
|
||||
case 3:
|
||||
throw new ThrowStatement(lv.get(2).evalValue(c), Throwables.getTypeForException(lv.get(1).evalValue(c).getString()), lv.get(0).evalValue(c).getString());
|
||||
default:
|
||||
throw new InternalExpressionException("throw() can't accept more than 3 parameters");
|
||||
}
|
||||
});
|
||||
|
||||
expression.addLazyFunction("try", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()==0)
|
||||
throw new InternalExpressionException("'try' needs at least an expression block");
|
||||
throw new InternalExpressionException("'try' needs at least an expression block, and either a catch_epr, or a number of pairs of filters and catch_expr");
|
||||
try
|
||||
{
|
||||
Value retval = lv.get(0).evalValue(c, t);
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
catch (ThrowStatement ret)
|
||||
catch (ProcessedThrowStatement ret)
|
||||
{
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
if (!ret.thrownExceptionType.isUserException())
|
||||
throw ret;
|
||||
return (c_, t_) -> Value.NULL;
|
||||
}
|
||||
if (lv.size() > 3 && lv.size() % 2 == 0)
|
||||
{
|
||||
throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch expression for user thrown exceptions, or a number of pairs of filters and catch expressions");
|
||||
}
|
||||
|
||||
Value val = null; // This is always assigned at some point, just the compiler doesn't know
|
||||
|
||||
LazyValue __ = c.getVariable("_");
|
||||
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
|
||||
Value val = lv.get(1).evalValue(c, t);
|
||||
c.setVariable("_",__);
|
||||
return (c_, t_) -> val;
|
||||
c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_"));
|
||||
LazyValue _trace = c.getVariable("_trace");
|
||||
c.setVariable("_trace", (__c, __t) -> MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("stack"), ListValue.wrap(ret.stack.stream().map(f -> ListValue.of(
|
||||
StringValue.of(f.getModule().getName()),
|
||||
StringValue.of(f.getString()),
|
||||
NumericValue.of(f.getToken().lineno+1),
|
||||
NumericValue.of(f.getToken().linepos+1)
|
||||
)).collect(Collectors.toList())),
|
||||
|
||||
StringValue.of("locals"), MapValue.wrap(ret.context.variables.entrySet().stream().filter(e -> !e.getKey().equals("_trace")).collect(Collectors.toMap(
|
||||
e -> StringValue.of(e.getKey()),
|
||||
e -> e.getValue().evalValue(ret.context)
|
||||
))),
|
||||
StringValue.of("token"), ListValue.of(
|
||||
StringValue.of(ret.token.surface),
|
||||
NumericValue.of(ret.token.lineno+1),
|
||||
NumericValue.of(ret.token.linepos+1)
|
||||
)
|
||||
)));
|
||||
|
||||
if (lv.size() == 2)
|
||||
{
|
||||
if (ret.thrownExceptionType.isUserException())
|
||||
val = lv.get(1).evalValue(c, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
int pointer = 1;
|
||||
while (pointer < lv.size() -1)
|
||||
{
|
||||
if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString()))
|
||||
{
|
||||
val = lv.get(pointer + 1).evalValue(c, t);
|
||||
break;
|
||||
}
|
||||
pointer += 2;
|
||||
}
|
||||
}
|
||||
c.setVariable("_", __);
|
||||
if (_trace != null)
|
||||
c.setVariable("_trace", _trace);
|
||||
else
|
||||
c.delVariable("_trace");
|
||||
if (val == null) // not handled
|
||||
{
|
||||
throw ret;
|
||||
}
|
||||
Value retval = val;
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import carpet.CarpetSettings;
|
||||
import carpet.network.ServerNetworkHandler;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.language.Sys;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.EntityValue;
|
||||
@@ -156,7 +158,7 @@ public class ShapeDispatcher
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException("No such particle: "+name);
|
||||
throw new ThrowStatement(name, Throwables.UNKNOWN_PARTICLE);
|
||||
}
|
||||
particleCache.put(name, particle);
|
||||
return particle;
|
||||
|
||||
@@ -2,6 +2,9 @@ package carpet.script.value;
|
||||
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.block.BlockState;
|
||||
@@ -73,7 +76,7 @@ public class BlockValue extends Value
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
throw new InternalExpressionException("Cannot parse block: "+str);
|
||||
throw new ThrowStatement(str, Throwables.UNKNOWN_BLOCK);
|
||||
}
|
||||
|
||||
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
||||
|
||||
@@ -215,7 +215,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
|
||||
}
|
||||
Value retVal;
|
||||
boolean rethrow = false;
|
||||
try
|
||||
{
|
||||
retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye
|
||||
@@ -228,15 +227,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
{
|
||||
retVal = returnStatement.retval;
|
||||
}
|
||||
catch (ThrowStatement throwStatement) // might not be really necessary
|
||||
{
|
||||
retVal = throwStatement.retval;
|
||||
rethrow = true;
|
||||
}
|
||||
if (rethrow)
|
||||
{
|
||||
throw new ThrowStatement(retVal);
|
||||
}
|
||||
Value otherRetVal = retVal;
|
||||
return (cc, tt) -> otherRetVal;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import carpet.fakes.InventoryBearerInterface;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.EquipmentInventory;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
@@ -297,7 +299,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect item: "+itemString);
|
||||
throw new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package carpet.script.value;
|
||||
|
||||
import carpet.fakes.BlockPredicateInterface;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.utils.BlockInfo;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.MapColor;
|
||||
@@ -158,7 +160,7 @@ public class ValueConversions
|
||||
}
|
||||
}
|
||||
if (dim == null)
|
||||
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
||||
throw new ThrowStatement(dimString, Throwables.UNKNOWN_DIMENSION);
|
||||
return server.getWorld(dim);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user