Separate ScriptHost from CarpetScriptHost

This commit is contained in:
gnembon committed 2019-11-19 01:31:56 -04:00
1 parent bd61340e46
commit 9e529fe967
5 files changed
+290 -256

No files matched your search

@@ -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;
});
}
@@ -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<Integer> coords, String arg)
{
if (CarpetServer.scriptServer.stopAll)
return "SCARPET PAUSED";
FunctionValue function = globalFunctions.get(call);
if (function == null)
return "UNDEFINED";
List<LazyValue> 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<String> 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<args.size()?args.get(i):"??")+" => "+(i<argv.size()?argv.get(i).evalValue(null).getString():"??")+"\n";
}
return error;
}
try
{
// TODO: this is just for now - invoke would be able to invoke other hosts scripts
Context context = new CarpetContext(this, source, BlockPos.ORIGIN);
return Expression.evalValue(
() -> 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<LazyValue> 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();
}
}
}
}
@@ -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<String, ScriptHost> modules;
public CarpetScriptHost globalHost;
public Map<String, CarpetScriptHost> modules;
long tickStart;
public boolean stopAll;
Set<String> 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<LazyValue> 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();
}
+9 -243
View File
@@ -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<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
public Map<String, LazyValue> 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<Integer> coords, String arg)
{
if (CarpetServer.scriptServer.stopAll)
return "SCARPET PAUSED";
FunctionValue function = globalFunctions.get(call);
if (function == null)
return "UNDEFINED";
List<LazyValue> 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<String> 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<args.size()?args.get(i):"??")+" => "+(i<argv.size()?argv.get(i).evalValue(null).getString():"??")+"\n";
}
return error;
}
try
{
// TODO: this is just for now - invoke would be able to invoke other hosts scripts
Context context = new CarpetContext(this, source, BlockPos.ORIGIN);
return Expression.evalValue(
() -> 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<LazyValue> 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)
{