From 9e529fe9673661b2cef5aceeff4cf63183f8ea56 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 19 Nov 2019 01:31:56 -0400 Subject: [PATCH] Separate ScriptHost from CarpetScriptHost --- .../java/carpet/commands/ScriptCommand.java | 7 +- .../java/carpet/script/CarpetExpression.java | 4 +- .../java/carpet/script/CarpetScriptHost.java | 267 ++++++++++++++++++ .../carpet/script/CarpetScriptServer.java | 16 +- src/main/java/carpet/script/ScriptHost.java | 252 +---------------- 5 files changed, 290 insertions(+), 256 deletions(-) create mode 100644 src/main/java/carpet/script/CarpetScriptHost.java diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index 9e779d9d..962e70da 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -1,6 +1,7 @@ package carpet.commands; import carpet.CarpetServer; +import carpet.script.CarpetScriptHost; import carpet.settings.CarpetSettings; import carpet.script.CarpetEventServer; import carpet.script.CarpetExpression; @@ -273,12 +274,12 @@ public class ScriptCommand suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.modules.keySet(), bb)). then(b).then(u).then(o).then(l).then(s).then(c).then(h).then(i).then(e).then(t)))); } - private static ScriptHost getHost(CommandContext context) + private static CarpetScriptHost getHost(CommandContext context) { try { String name = StringArgumentType.getString(context, "app").toLowerCase(Locale.ROOT); - ScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost); + CarpetScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost); return parentHost.retrieveForExecution(context.getSource()); } catch (IllegalArgumentException ignored) @@ -370,7 +371,7 @@ public class ScriptCommand private static int invoke(CommandContext context, String call, BlockPos pos1, BlockPos pos2, String args) { ServerCommandSource source = context.getSource(); - ScriptHost host = getHost(context); + CarpetScriptHost host = getHost(context); if (call.startsWith("__")) { Messenger.m(source, "r Hidden functions are only callable in scripts"); diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index f0b72463..46a8f0c6 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -3048,7 +3048,7 @@ public class CarpetExpression throw new InternalExpressionException("Cannot use "+file+" as resource name - must have some letters and numbers"); } } - Tag state = ((CarpetContext)c).host.getGlobalState(file); + Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file); if (state == null) return (cc, tt) -> Value.NULL; Value retVal = new NBTSerializableValue(state); @@ -3074,7 +3074,7 @@ public class CarpetExpression ? (NBTSerializableValue) val : new NBTSerializableValue(val.getString()); Tag tag = tagValue.getTag(); - ((CarpetContext)c).host.setGlobalState(tag, file); + ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file); return (cc, tt) -> Value.NULL; }); } diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java new file mode 100644 index 00000000..86ff2aa9 --- /dev/null +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -0,0 +1,267 @@ +package carpet.script; + +import carpet.CarpetServer; +import carpet.script.bundled.ModuleInterface; +import carpet.script.exception.ExpressionException; +import carpet.script.exception.InvalidCallbackException; +import carpet.script.value.FunctionValue; +import carpet.script.value.NumericValue; +import carpet.script.value.StringValue; +import carpet.script.value.Value; +import carpet.settings.CarpetSettings; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.nbt.Tag; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static java.lang.Math.max; + +public class CarpetScriptHost extends ScriptHost +{ + private CarpetScriptServer scriptServer; + + private Tag globalState; + private int saveTimeout; + + CarpetScriptHost(CarpetScriptServer server, String name, ModuleInterface code, boolean perUser, ScriptHost parent) + { + super(name, code, perUser, parent); + this.saveTimeout = 0; + this.scriptServer = server; + if (parent == null && name != null) + globalState = loadState(); + } + + @Override + protected ScriptHost duplicate() + { + return new CarpetScriptHost(scriptServer, this.getName(), myCode, false, this); + } + + @Override + public void addUserDefinedFunction(String funName, FunctionValue function) + { + super.addUserDefinedFunction(funName, function); + // mcarpet + if (funName.startsWith("__on_")) + { + // this is nasty, we have the host and function, yet we add it via names, but hey - works for now + String event = funName.replaceFirst("__on_",""); + if (CarpetEventServer.Event.byName.containsKey(event)) + scriptServer.events.addEventDirectly(event, this, function); + } + } + + public CarpetScriptHost retrieveForExecution(ServerCommandSource source) + { + if (!perUser) + return this; + try + { + ServerPlayerEntity player = source.getPlayer(); + return (CarpetScriptHost) retrieveForExecution(player.getName().getString()); + } + catch (CommandSyntaxException e) + { + return (CarpetScriptHost) retrieveForExecution((String)null); + } + } + + public String call(ServerCommandSource source, String call, List coords, String arg) + { + if (CarpetServer.scriptServer.stopAll) + return "SCARPET PAUSED"; + FunctionValue function = globalFunctions.get(call); + if (function == null) + return "UNDEFINED"; + List argv = new ArrayList<>(); + if (coords != null) + for (Integer i: coords) + argv.add( (c, t) -> new NumericValue(i)); + String sign = ""; + for (Tokenizer.Token tok : Tokenizer.simplepass(arg)) + { + switch (tok.type) + { + case VARIABLE: + if (globalVariables.containsKey(tok.surface.toLowerCase(Locale.ROOT))) + { + argv.add(globalVariables.get(tok.surface.toLowerCase(Locale.ROOT))); + break; + } + case STRINGPARAM: + argv.add((c, t) -> new StringValue(tok.surface)); + sign = ""; + break; + + case LITERAL: + try + { + String finalSign = sign; + argv.add((c, t) ->new NumericValue(finalSign+tok.surface)); + sign = ""; + } + catch (NumberFormatException exception) + { + return "Fail: "+sign+tok.surface+" seems like a number but it is not a number. Use quotes to ensure its a string"; + } + break; + case HEX_LITERAL: + try + { + String finalSign = sign; + argv.add((c, t) -> new NumericValue(new BigInteger(finalSign+tok.surface.substring(2), 16).doubleValue())); + sign = ""; + } + catch (NumberFormatException exception) + { + return "Fail: "+sign+tok.surface+" seems like a number but it is not a number. Use quotes to ensure its a string"; + } + break; + case OPERATOR: + case UNARY_OPERATOR: + if ((tok.surface.equals("-") || tok.surface.equals("-u")) && sign.isEmpty()) + { + sign = "-"; + } + else + { + return "Fail: operators, like " + tok.surface + " are not allowed in invoke"; + } + break; + case FUNCTION: + return "Fail: passing functions like "+tok.surface+"() to invoke is not allowed"; + case OPEN_PAREN: + case COMMA: + case CLOSE_PAREN: + case MARKER: + return "Fail: "+tok.surface+" is not allowed in invoke"; + } + } + List args = function.getArguments(); + if (argv.size() != args.size()) + { + String error = "Fail: stored function "+call+" takes "+args.size()+" arguments, not "+argv.size()+ ":\n"; + for (int i = 0; i < max(argv.size(), args.size()); i++) + { + error += (i "+(i function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), + context, + Context.VOID + ).getString(); + } + catch (ExpressionException e) + { + return e.getMessage(); + } + } + + public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException + { + if (CarpetServer.scriptServer.stopAll) + return Value.NULL; + if (argv.size() != fun.getArguments().size()) + { + throw new InvalidCallbackException(); + } + try + { + // TODO: this is just for now - invoke would be able to invoke other hosts scripts + Context context = new CarpetContext(this, source, pos); + return Expression.evalValue( + () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv), + context, + Context.VOID); + } + catch (ExpressionException e) + { + CarpetSettings.LOG.error("Callback failed: "+e.getMessage()); + } + return Value.NULL; + } + + @Override + public void onClose() + { + super.onClose(); + if (this.saveTimeout > 0) + dumpState(); + String markerName = ExpressionInspector.MARKER_STRING+"_"+((getName()==null)?"":getName()); + for (ServerWorld world : CarpetServer.minecraft_server.getWorlds()) + { + for (Entity e : world.getEntities(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName))) + { + e.remove(); + } + } + } + + private void dumpState() + { + myCode.saveData(null, globalState); + } + + private Tag loadState() + { + return myCode.getData(null); + } + + public Tag getGlobalState(String file) + { + if (getName() == null || myCode.isInternal()) return null; + if (file != null) + myCode.getData(file); + if (parent == null) + return globalState; + return ((CarpetScriptHost)parent).globalState; + } + + public void setGlobalState(Tag tag, String file) + { + if (getName() == null || myCode.isInternal()) return; + + if (file!= null) + { + myCode.saveData(file, tag); + return; + } + + CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this; + responsibleHost.globalState = tag; + if (responsibleHost.saveTimeout == 0) + { + responsibleHost.dumpState(); + responsibleHost.saveTimeout = 200; + } + } + + public void tick() + { + if (this.saveTimeout > 0) + { + this.saveTimeout --; + if (this.saveTimeout == 0) + { + dumpState(); + } + } + } + +} diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 58e623c7..32476bd6 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -36,8 +36,8 @@ import static net.minecraft.server.command.CommandManager.literal; public class CarpetScriptServer { //make static for now, but will change that later: - public ScriptHost globalHost; - public Map modules; + public CarpetScriptHost globalHost; + public Map modules; long tickStart; public boolean stopAll; Set holyMoly; @@ -129,9 +129,9 @@ public class CarpetScriptServer } - private ScriptHost createMinecraftScriptHost(String name, ModuleInterface module, boolean perPlayer, ServerCommandSource source) + private CarpetScriptHost createMinecraftScriptHost(String name, ModuleInterface module, boolean perPlayer, ServerCommandSource source) { - ScriptHost host = new ScriptHost(this, name, module, perPlayer, null ); + CarpetScriptHost host = new CarpetScriptHost(this, name, module, perPlayer, null ); host.globalVariables.put("_x", (c, t) -> Value.ZERO); host.globalVariables.put("_y", (c, t) -> Value.ZERO); host.globalVariables.put("_z", (c, t) -> Value.ZERO); @@ -166,7 +166,7 @@ public class CarpetScriptServer //TODO add per player modules to support player actions better on a server name = name.toLowerCase(Locale.ROOT); ModuleInterface module = getModule(name); - ScriptHost newHost = createMinecraftScriptHost(name, module, perPlayer, source); + CarpetScriptHost newHost = createMinecraftScriptHost(name, module, perPlayer, source); if (newHost == null) { Messenger.m(source, "r Failed to add "+name+" app"); @@ -200,7 +200,7 @@ public class CarpetScriptServer private boolean addConfig(ServerCommandSource source, String hostName) { - ScriptHost host = modules.get(hostName); + CarpetScriptHost host = modules.get(hostName); if (host == null || !host.globalFunctions.containsKey("__config")) { return false; @@ -374,7 +374,7 @@ public class CarpetScriptServer public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List argv) { - ScriptHost host = globalHost; + CarpetScriptHost host = globalHost; try { if (hostname != null) @@ -391,7 +391,7 @@ public class CarpetScriptServer public void tick() { events.tick(); - for (ScriptHost host : modules.values()) + for (CarpetScriptHost host : modules.values()) { host.tick(); } diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 19965625..9f4d1ed6 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -1,55 +1,30 @@ package carpet.script; -import carpet.CarpetServer; import carpet.script.bundled.ModuleInterface; import carpet.script.value.FunctionValue; -import carpet.settings.CarpetSettings; -import carpet.script.exception.ExpressionException; -import carpet.script.exception.InvalidCallbackException; -import carpet.script.value.NumericValue; -import carpet.script.value.StringValue; import carpet.script.value.Value; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.nbt.Tag; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import java.math.BigInteger; -import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; -import static java.lang.Math.max; - public class ScriptHost { private final Map userHosts = new Object2ObjectOpenHashMap<>(); public Map globalFunctions = new Object2ObjectOpenHashMap<>(); public Map globalVariables = new Object2ObjectOpenHashMap<>(); - private Tag globalState; - private int saveTimeout; - - private ScriptHost parent; + protected ScriptHost parent; + protected boolean perUser; private String name; public String getName() {return name;} - private final ModuleInterface myCode; - private boolean perUser; + protected final ModuleInterface myCode; - private CarpetScriptServer scriptServer; - - ScriptHost(CarpetScriptServer server, String name, ModuleInterface code, boolean perUser, ScriptHost parent) + ScriptHost(String name, ModuleInterface code, boolean perUser, ScriptHost parent) { - this.saveTimeout = 0; this.parent = parent; this.name = name; this.myCode = code; @@ -64,55 +39,30 @@ public class ScriptHost globalVariables.put("_", (c, t) -> Value.ZERO); globalVariables.put("_i", (c, t) -> Value.ZERO); globalVariables.put("_a", (c, t) -> Value.ZERO); - - if (parent == null && name != null) - globalState = loadState(); - - this.scriptServer = server; } public void addUserDefinedFunction(String funName, FunctionValue function) { - // generic globalFunctions.put(funName, function); - // mcarpet - if (funName.startsWith("__on_")) - { - // this is nasty, we have the host and function, yet we add it via names, but hey - works for now - String event = funName.replaceFirst("__on_",""); - if (CarpetEventServer.Event.byName.containsKey(event)) - scriptServer.events.addEventDirectly(event, this, function); - } - } - private ScriptHost retrieveForExecution(String /*Nullable*/ user) + public ScriptHost retrieveForExecution(String /*Nullable*/ user) { if (!perUser) return this; ScriptHost userHost = userHosts.get(user); if (userHost != null) return userHost; - userHost = new ScriptHost(this.scriptServer, this.name, myCode, false, this); + userHost = this.duplicate(); userHost.globalVariables.putAll(this.globalVariables); userHost.globalFunctions.putAll(this.globalFunctions); userHosts.put(user, userHost); return userHost; } - public ScriptHost retrieveForExecution(ServerCommandSource source) + protected ScriptHost duplicate() { - if (!perUser) - return this; - try - { - ServerPlayerEntity player = source.getPlayer(); - return retrieveForExecution(player.getName().getString()); - } - catch (CommandSyntaxException e) - { - return retrieveForExecution((String)null); - } + return new ScriptHost(this.name, myCode, false, this); } public Expression getExpressionForFunction(String name) @@ -133,191 +83,7 @@ public class ScriptHost return globalFunctions.keySet().stream().filter((str) -> all || !str.startsWith("__")).collect(Collectors.toList()); } - public String call(ServerCommandSource source, String call, List coords, String arg) - { - if (CarpetServer.scriptServer.stopAll) - return "SCARPET PAUSED"; - FunctionValue function = globalFunctions.get(call); - if (function == null) - return "UNDEFINED"; - List argv = new ArrayList<>(); - if (coords != null) - for (Integer i: coords) - argv.add( (c, t) -> new NumericValue(i)); - String sign = ""; - for (Tokenizer.Token tok : Tokenizer.simplepass(arg)) - { - switch (tok.type) - { - case VARIABLE: - if (globalVariables.containsKey(tok.surface.toLowerCase(Locale.ROOT))) - { - argv.add(globalVariables.get(tok.surface.toLowerCase(Locale.ROOT))); - break; - } - case STRINGPARAM: - argv.add((c, t) -> new StringValue(tok.surface)); - sign = ""; - break; - - case LITERAL: - try - { - String finalSign = sign; - argv.add((c, t) ->new NumericValue(finalSign+tok.surface)); - sign = ""; - } - catch (NumberFormatException exception) - { - return "Fail: "+sign+tok.surface+" seems like a number but it is not a number. Use quotes to ensure its a string"; - } - break; - case HEX_LITERAL: - try - { - String finalSign = sign; - argv.add((c, t) -> new NumericValue(new BigInteger(finalSign+tok.surface.substring(2), 16).doubleValue())); - sign = ""; - } - catch (NumberFormatException exception) - { - return "Fail: "+sign+tok.surface+" seems like a number but it is not a number. Use quotes to ensure its a string"; - } - break; - case OPERATOR: - case UNARY_OPERATOR: - if ((tok.surface.equals("-") || tok.surface.equals("-u")) && sign.isEmpty()) - { - sign = "-"; - } - else - { - return "Fail: operators, like " + tok.surface + " are not allowed in invoke"; - } - break; - case FUNCTION: - return "Fail: passing functions like "+tok.surface+"() to invoke is not allowed"; - case OPEN_PAREN: - case COMMA: - case CLOSE_PAREN: - case MARKER: - return "Fail: "+tok.surface+" is not allowed in invoke"; - } - } - List args = function.getArguments(); - if (argv.size() != args.size()) - { - String error = "Fail: stored function "+call+" takes "+args.size()+" arguments, not "+argv.size()+ ":\n"; - for (int i = 0; i < max(argv.size(), args.size()); i++) - { - error += (i "+(i function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), - context, - Context.VOID - ).getString(); - } - catch (ExpressionException e) - { - return e.getMessage(); - } - } - - public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException - { - if (CarpetServer.scriptServer.stopAll) - return Value.NULL; - if (argv.size() != fun.getArguments().size()) - { - throw new InvalidCallbackException(); - } - try - { - // TODO: this is just for now - invoke would be able to invoke other hosts scripts - Context context = new CarpetContext(this, source, pos); - return Expression.evalValue( - () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv), - context, - Context.VOID); - } - catch (ExpressionException e) - { - CarpetSettings.LOG.error("Callback failed: "+e.getMessage()); - } - return Value.NULL; - } - - private void dumpState() - { - myCode.saveData(null, globalState); - } - - private Tag loadState() - { - return myCode.getData(null); - } - - public Tag getGlobalState(String file) - { - if (name == null || myCode.isInternal()) return null; - if (file != null) - myCode.getData(file); - if (parent == null) - return globalState; - return parent.globalState; - } - - public void setGlobalState(Tag tag, String file) - { - if (name == null || myCode.isInternal()) return; - - if (file!= null) - { - myCode.saveData(file, tag); - return; - } - - ScriptHost responsibleHost = (parent != null)?parent:this; - responsibleHost.globalState = tag; - if (responsibleHost.saveTimeout == 0) - { - responsibleHost.dumpState(); - responsibleHost.saveTimeout = 200; - } - } - - public void tick() - { - if (this.saveTimeout > 0) - { - this.saveTimeout --; - if (this.saveTimeout == 0) - { - dumpState(); - } - } - } - - public void onClose() - { - if (this.saveTimeout > 0) - dumpState(); - String markerName = ExpressionInspector.MARKER_STRING+"_"+((getName()==null)?"":getName()); - for (ServerWorld world : CarpetServer.minecraft_server.getWorlds()) - { - for (Entity e : world.getEntities(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName))) - { - e.remove(); - } - } - } + public void onClose() { } public void setPerPlayer(boolean isPerUser) {