rework for events and other stored functions. Fixes #984

This commit is contained in:
gnembon committed 2021-07-22 22:22:38 -03:00
1 parent 7ebc990ef6
commit 0e9df3701f
9 files changed
+139 -187

No files matched your search

@@ -38,7 +38,7 @@ public abstract class ArmorStandEntity_scarpetMarkerMixin extends LivingEntity
if (owner.isPresent())
{
String hostName = StringUtils.removeStart(owner.get(),prefix);
if (!hostName.isEmpty() && CarpetServer.scriptServer.getHostByName(hostName) == null)
if (!hostName.isEmpty() && CarpetServer.scriptServer.getAppHostByName(hostName) == null)
{
discard(); //discard
}
@@ -3,7 +3,9 @@ package carpet.script;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.helpers.TickSpeed;
import carpet.script.exception.IntegrityException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.InvalidCallbackException;
import carpet.script.value.BlockValue;
import carpet.script.value.BooleanValue;
import carpet.script.value.EntityValue;
@@ -25,7 +27,6 @@ import net.minecraft.entity.TntEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.passive.MerchantEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -46,7 +47,6 @@ import net.minecraft.village.Merchant;
import net.minecraft.village.TradeOffer;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
@@ -70,10 +70,17 @@ import java.util.stream.Collectors;
public class CarpetEventServer
{
public final List<ScheduledCall> scheduledCalls = new LinkedList<>();
public final MinecraftServer server;
public final CarpetScriptServer scriptServer;
private static final List<Value> NOARGS = Collections.emptyList();
public final Map<String, Event> customEvents = new HashMap<>();
public enum CallbackResult
{
SUCCESS, PASS, FAIL
}
public static class Callback
{
public final String host;
@@ -81,7 +88,6 @@ public class CarpetEventServer
public final FunctionValue function;
public final List<Value> parametrizedArgs;
public Callback(String host, String target, FunctionValue function, List<Value> parametrizedArgs)
{
this.host = host;
@@ -95,18 +101,17 @@ public class CarpetEventServer
* @param sender - entity command source
* @param runtimeArgs = options
*/
public boolean execute(ServerCommandSource sender, List<Value> runtimeArgs)
public CallbackResult execute(ServerCommandSource sender, List<Value> runtimeArgs)
{
if (!this.parametrizedArgs.isEmpty())
{
runtimeArgs = new ArrayList<>(runtimeArgs);
runtimeArgs.addAll(this.parametrizedArgs);
}
if (CarpetServer.scriptServer == null) return false; // already stopped
return CarpetServer.scriptServer.runEventCall(
if (CarpetServer.scriptServer == null) return CallbackResult.FAIL; // already stopped
return CarpetServer.scriptServer.events.runEventCall(
sender.withLevel(CarpetSettings.runPermissionLevel),
host, optionalTarget, function, runtimeArgs
);
host, optionalTarget, function, runtimeArgs);
}
/**
@@ -115,18 +120,12 @@ public class CarpetEventServer
* @param optionalRecipient - optional target player argument
* @param runtimeArgs = options
*/
public int signal(ServerCommandSource sender, ServerPlayerEntity optionalRecipient, List<Value> runtimeArgs)
public CallbackResult signal(ServerCommandSource sender, ServerPlayerEntity optionalRecipient, List<Value> runtimeArgs)
{
// recipent of the call doesn't match the handlingHost
if (optionalRecipient != null && !optionalRecipient.getEntityName().equals(optionalTarget))
return 0;
List<Value> args = runtimeArgs;
if (!this.parametrizedArgs.isEmpty())
{
args = new ArrayList<>(runtimeArgs);
args.addAll(this.parametrizedArgs);
}
return CarpetServer.scriptServer.signal(sender, optionalRecipient, host, function, args, false);
return CallbackResult.FAIL;
return execute(sender, runtimeArgs);
}
@@ -178,10 +177,8 @@ public class CarpetEventServer
*/
public void execute()
{
CarpetServer.scriptServer.runScheduledCall(ctx.origin, ctx.s, host, (CarpetScriptHost) ctx.host, function, parametrizedArgs);
CarpetServer.scriptServer.events.runScheduledCall(ctx.origin, ctx.s, host, (CarpetScriptHost) ctx.host, function, parametrizedArgs);
}
}
public static class CallbackList
@@ -190,7 +187,6 @@ public class CarpetEventServer
public final List<Callback> callList;
public final int reqArgs;
final boolean isSystem;
final boolean isGlobalOnly;
final boolean perPlayerDistribution;
public CallbackList(int reqArgs, boolean isSystem, boolean isGlobalOnly)
@@ -198,7 +194,6 @@ public class CarpetEventServer
this.callList = new ArrayList<>();
this.reqArgs = reqArgs;
this.isSystem = isSystem;
this.isGlobalOnly = isGlobalOnly;
perPlayerDistribution = isSystem && !isGlobalOnly;
}
@@ -230,7 +225,7 @@ public class CarpetEventServer
// supressing calls where target player hosts simply don't match
// handling global hosts with player targets is left to when the host is resolved (few calls deeper).
if (nameCheck != null && call.optionalTarget != null && !nameCheck.equals(call.optionalTarget)) continue;
if (!call.execute(source, argv)) fails.add(call);
if (call.execute(source, argv) == CallbackResult.FAIL) fails.add(call);
}
for (Callback call : fails) callList.remove(call);
CarpetProfiler.end_current_section(currentSection);
@@ -245,7 +240,9 @@ public class CarpetEventServer
int successes = 0;
for (Callback call: callList)
{
successes += Math.max(0, call.signal(sender, optinoalReceipient, callArg));
//successes += Math.max(0, call.signal(sender, optinoalReceipient, callArg));
if (call.signal(sender, optinoalReceipient, callArg) == CallbackResult.SUCCESS) successes ++;
}
//for (Callback call : fails) callList.remove(call);
return successes;
@@ -253,7 +250,7 @@ public class CarpetEventServer
public boolean addFromExternal(ServerCommandSource source, String hostName, String funName, Consumer<ScriptHost> hostOnEventHandler)
{
ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName);
ScriptHost host = CarpetServer.scriptServer.getAppHostByName(hostName);
if (host == null)
{
// impossible call to add
@@ -991,8 +988,6 @@ public class CarpetEventServer
public final String name;
public final CallbackList handler;
@Deprecated
public final boolean globalOnly;
public final boolean isPublic; // public events can be targetted with __on_<event> defs
public Event(String name, int reqArgs, boolean isGlobalOnly)
{
@@ -1002,7 +997,6 @@ public class CarpetEventServer
{
this.name = name;
this.handler = new CallbackList(reqArgs, true, isGlobalOnly);
this.globalOnly = isGlobalOnly;
this.isPublic = isPublic;
byName.put(name, this);
}
@@ -1050,7 +1044,6 @@ public class CarpetEventServer
{
this.name = name;
this.handler = new CallbackList(1, false, false);
this.globalOnly = false;
this.isPublic = true;
server.events.customEvents.put(name, this);
}
@@ -1122,9 +1115,9 @@ public class CarpetEventServer
}
public CarpetEventServer(MinecraftServer server)
public CarpetEventServer(CarpetScriptServer scriptServer)
{
this.server = server;
this.scriptServer = scriptServer;
Event.clearAllBuiltinEvents();
}
@@ -1155,6 +1148,45 @@ public class CarpetEventServer
scheduledCalls.add(new ScheduledCall(context, function, args, due));
}
public void runScheduledCall(BlockPos origin, ServerCommandSource source, String hostname, CarpetScriptHost host, FunctionValue udf, List<Value> argv)
{
if (hostname != null && !scriptServer.modules.containsKey(hostname)) // well - scheduled call app got unloaded
return;
try
{
host.callUDF(origin, source, udf, argv);
}
catch (NullPointerException | InvalidCallbackException | IntegrityException ignored) { }
}
public CallbackResult runEventCall(ServerCommandSource sender, String hostname, String optionalTarget, FunctionValue udf, List<Value> argv)
{
CarpetScriptHost appHost = scriptServer.getAppHostByName(hostname);
// no such app
if (appHost == null) return CallbackResult.FAIL;
// dummy call for player apps that reside on the global copy - do not run them, but report as passes.
if (appHost.isPerUser() && optionalTarget==null) return CallbackResult.PASS;
ServerPlayerEntity target = null;
if (optionalTarget != null)
{
target = sender.getServer().getPlayerManager().getPlayer(optionalTarget);
if (target == null) return CallbackResult.FAIL;
}
ServerCommandSource source = sender.withLevel(CarpetSettings.runPermissionLevel);
CarpetScriptHost executingHost = appHost.retrieveForExecution(sender, target);
if (executingHost == null) return CallbackResult.FAIL;
try
{
executingHost.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
return CallbackResult.SUCCESS;
}
catch (NullPointerException | InvalidCallbackException | IntegrityException error)
{
CarpetScriptServer.LOG.error("Got exception: "+error.getMessage());
return CallbackResult.FAIL;
}
}
public boolean addEventFromCommand(ServerCommandSource source, String event, String host, String funName)
{
Event ev = Event.getEvent(event, CarpetServer.scriptServer);
@@ -1178,7 +1210,7 @@ public class CarpetEventServer
public boolean handleCustomEvent(String event, CarpetScriptHost host, FunctionValue function, List<Value> args)
{
Event ev = Event.getOrCreateCustom(event, host.getScriptServer());
Event ev = Event.getOrCreateCustom(event, scriptServer);
onEventAddedToHost(ev, host);
return ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
}
@@ -1233,15 +1265,4 @@ public class CarpetEventServer
// remove scheduled calls
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
}
private Pair<String,String> decodeCallback(String funName)
{
Pattern find = Pattern.compile("(\\w+)\\(from (\\w+)\\)");
Matcher matcher = find.matcher(funName);
if(matcher.matches())
{
return Pair.of(matcher.group(2), matcher.group(1));
}
return Pair.of(null, funName);
}
}
@@ -10,6 +10,7 @@ import carpet.script.command.CommandArgument;
import carpet.script.command.CommandToken;
import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.IntegrityException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.InvalidCallbackException;
import carpet.script.utils.AppStoreManager;
@@ -644,37 +645,19 @@ public class CarpetScriptHost extends ScriptHost
}
}
public List<CarpetScriptHost> retrieveForExecution(ServerCommandSource source, String optionalTarget)
public CarpetScriptHost retrieveForExecution(ServerCommandSource source, ServerPlayerEntity player)
{
List<CarpetScriptHost> targets = new ArrayList<>();
if (perUser)
CarpetScriptHost target = null;
if (!perUser)
{
if (optionalTarget == null)
{
for (ServerPlayerEntity player : source.getServer().getPlayerManager().getPlayerList())
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
else
{
ServerPlayerEntity player = source.getServer().getPlayerManager().getPlayer(optionalTarget);
if (player != null)
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
target = this;
}
else
else if (player != null)
{
targets.add(this);
if (this.errorSnooper == null) this.setChatErrorSnooper(source);
target = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
}
return targets;
if (target != null && target.errorSnooper == null) target.setChatErrorSnooper(source);
return target;
}
public CarpetScriptHost retrieveOwnForExecution(ServerCommandSource source) throws CommandSyntaxException
@@ -830,6 +813,7 @@ public class CarpetScriptHost extends ScriptHost
try
{
// TODO: this is just for now - invoke would be able to invoke other hosts scripts
assertAppIntegrity(function.getModule());
Context context = new CarpetContext(this, source, BlockPos.ORIGIN);
return function.getExpression().evalValue(
() -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv),
@@ -860,7 +844,7 @@ public class CarpetScriptHost extends ScriptHost
}
try
{
// TODO: this is just for now - invoke would be able to invoke other hosts scripts
assertAppIntegrity(function.getModule());
Context context = new CarpetContext(this, source, BlockPos.ORIGIN);
return function.getExpression().evalValue(
() -> function.execute(context, Context.VOID, function.getExpression(), function.getToken(), argv),
@@ -874,7 +858,7 @@ public class CarpetScriptHost extends ScriptHost
}
}
public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List<Value> argv) throws InvalidCallbackException
public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List<Value> argv) throws InvalidCallbackException, IntegrityException
{
if (CarpetServer.scriptServer.stopAll)
return Value.NULL;
@@ -889,7 +873,7 @@ public class CarpetScriptHost extends ScriptHost
}
try
{
// TODO: this is just for now - invoke would be able to invoke other hosts scripts
assertAppIntegrity(fun.getModule());
Context context = new CarpetContext(this, source, pos);
return fun.getExpression().evalValue(
() -> fun.execute(context, Context.VOID, fun.getExpression(), fun.getToken(), argv),
@@ -12,8 +12,6 @@ import carpet.script.bundled.BundledModule;
import carpet.CarpetServer;
import carpet.script.bundled.FileModule;
import carpet.script.bundled.Module;
import carpet.script.exception.IntegrityException;
import carpet.script.exception.InvalidCallbackException;
import carpet.script.language.Arithmetic;
import carpet.script.language.ControlFlow;
import carpet.script.language.DataStructures;
@@ -23,7 +21,6 @@ import carpet.script.language.Sys;
import carpet.script.language.Threading;
import carpet.script.utils.AppStoreManager;
import carpet.script.value.FunctionValue;
import carpet.script.value.Value;
import carpet.utils.CarpetProfiler;
import carpet.utils.Messenger;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
@@ -35,7 +32,6 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.WorldSavePath;
import net.minecraft.util.math.BlockPos;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -120,9 +116,9 @@ public class CarpetScriptServer
private void init()
{
ScriptHost.systemGlobals.clear();
events = new CarpetEventServer(server);
events = new CarpetEventServer(this);
modules = new HashMap<>();
unloadableModules = new HashSet<String>();
unloadableModules = new HashSet<>();
tickStart = 0L;
stopAll = false;
holyMoly = server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
@@ -232,7 +228,7 @@ public class CarpetScriptServer
return moduleNames;
}
public ScriptHost getHostByName(String name)
public CarpetScriptHost getAppHostByName(String name)
{
if (name == null)
return globalHost;
@@ -310,7 +306,10 @@ public class CarpetScriptServer
if (newHost.isPerUser())
{
// that will provide player hosts right at the startup
newHost.retrieveForExecution(source, null);
for (ServerPlayerEntity player : source.getServer().getPlayerManager().getPlayerList())
{
newHost.retrieveForExecution(player.getCommandSource(), player);
}
}
else
{
@@ -372,86 +371,6 @@ public class CarpetScriptServer
return false;
}
public boolean runEventCall(ServerCommandSource sender, String hostname, String optionalTarget, FunctionValue udf, List<Value> argv)
{
CarpetScriptHost host = globalHost;
if (hostname != null)
host = modules.get(hostname);
if (host == null) return false;
// dummy call for player apps that reside on the global copy - do not run them, but report as successes.
if (host.isPerUser() && optionalTarget==null) return true;
ServerPlayerEntity target = null;
if (optionalTarget != null)
{
target = sender.getServer().getPlayerManager().getPlayer(optionalTarget);
if (target == null) return false;
}
int successes = signal(sender, target, hostname, udf, argv, true );
return successes >= 0;
}
public void runScheduledCall(BlockPos origin, ServerCommandSource source, String hostname, CarpetScriptHost host, FunctionValue udf, List<Value> argv)
{
if (hostname != null && !modules.containsKey(hostname)) // well - scheduled call app got unloaded
return;
try
{
host.callUDF(origin, source, udf, argv);
}
catch (NullPointerException | InvalidCallbackException ignored)
{
}
}
/**
* returns number of successful calls in the host or -1 if failed exceptionally
* @param sender
* @param optionalRecipient
* @param hostname
* @param udf
* @param argv
* @param reportFails - returns -1 when any call failed
* @return
*/
public int signal(ServerCommandSource sender, ServerPlayerEntity optionalRecipient, String hostname, FunctionValue udf, List<Value> argv, boolean reportFails)
{
if (hostname == null)
{
if (optionalRecipient != null) return 0;
try
{
globalHost.callUDF(BlockPos.ORIGIN, sender, udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
return reportFails?-1:0;
}
return 1;
}
String hostRecipient = optionalRecipient == null?null:optionalRecipient.getEntityName();
ServerCommandSource source = (optionalRecipient == null)? sender : optionalRecipient.getCommandSource().withLevel(CarpetSettings.runPermissionLevel);
int successes = 0;
for (CarpetScriptHost host : modules.get(hostname).retrieveForExecution(sender, hostRecipient))
{
// getPlayer will always return nonnull cause retrieve for execution only returns hosts for existing players.
ServerCommandSource executingSource = host.perUser
? source.getServer().getPlayerManager().getPlayer(host.user).getCommandSource()
: source.getServer().getCommandSource();
try
{
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
}
catch (NullPointerException | InvalidCallbackException | IntegrityException npe)
{
if (reportFails) return -1;
continue;
}
successes ++;
}
return successes;
}
public void tick()
{
CarpetProfiler.ProfilerToken token;
@@ -38,7 +38,7 @@ public class EntityEventsGroup
{
Map.Entry<Pair<String,String>, CarpetEventServer.Callback> action = iterator.next();
Pair<String,String> key = action.getKey();
ScriptHost host = CarpetServer.scriptServer.getHostByName(key.getLeft());
ScriptHost host = CarpetServer.scriptServer.getAppHostByName(key.getLeft());
if (host == null)
{
iterator.remove();
@@ -52,7 +52,7 @@ public class EntityEventsGroup
continue;
}
}
if (!type.call(action.getValue(), entity, args))
if (type.call(action.getValue(), entity, args) == CarpetEventServer.CallbackResult.FAIL)
iterator.remove();
}
if (actionSet.isEmpty()) actions.remove(type);
@@ -125,7 +125,7 @@ public class EntityEventsGroup
}
return new CarpetEventServer.Callback(key.getLeft(), key.getRight(), function, extraArgs);
}
public boolean call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
public CarpetEventServer.CallbackResult call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
{
assert args.length == argcount-1;
return tickCall.execute(entity.getCommandSource(), makeArgs(entity, args));
@@ -305,6 +305,11 @@ public abstract class ScriptHost
return data;
}
protected void assertAppIntegrity(Module module)
{
getModuleData(module);
}
public void addUserDefinedFunction(Context ctx, Module module, String name, FunctionValue fun)
{
getModuleData(module).globalFunctions.put(name, fun);
@@ -262,7 +262,6 @@ public class Entities {
expression.addContextFunction("entity_load_handler", -1, (c, t, lv) ->
{
if (c.host.isPerUser()) throw new InternalExpressionException("'entity_load_handler' can only be called in apps with global scope");
if (lv.size() < 2) throw new InternalExpressionException("'entity_load_handler' required the entity type, and a function to call");
Value entityValue = lv.get(0);
List<String> descriptors = (entityValue instanceof ListValue)