rework of the event system, allowing apps to function more independently and allowing for player scoped apps to handle global events. fixes #596

This commit is contained in:
gnembon committed 2020-12-02 01:02:36 -04:00
1 parent 926c1a8e63
commit d29f49967f
13 files changed
+367 -246

No files matched your search

+14 -1
View File
@@ -5,7 +5,19 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import carpet.commands.*;
import carpet.commands.CounterCommand;
import carpet.commands.DistanceCommand;
import carpet.commands.DrawCommand;
import carpet.commands.InfoCommand;
import carpet.commands.LogCommand;
import carpet.commands.MobAICommand;
import carpet.commands.PerimeterInfoCommand;
import carpet.commands.PlayerCommand;
import carpet.commands.ProfileCommand;
import carpet.commands.ScriptCommand;
import carpet.commands.SpawnCommand;
import carpet.commands.TestCommand;
import carpet.commands.TickCommand;
import carpet.network.ServerNetworkHandler;
import carpet.helpers.HopperCounter;
import carpet.helpers.TickSpeed;
@@ -143,6 +155,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
{
ServerNetworkHandler.onPlayerJoin(player);
LoggerRegistry.playerConnected(player);
scriptServer.onPlayerJoin(player);
extensions.forEach(e -> e.onPlayerLoggedIn(player));
}
@@ -18,6 +18,7 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.block.Block;
@@ -84,7 +85,7 @@ public class ScriptCommand
private static CompletableFuture<Suggestions> suggestCode(
CommandContext<ServerCommandSource> context,
SuggestionsBuilder suggestionsBuilder
)
) throws CommandSyntaxException
{
CarpetScriptHost currentHost = getHost(context);
String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
@@ -291,7 +292,7 @@ public class ScriptCommand
)?1:0)))))).
then(literal("remove_from").
then(argument("event", StringArgumentType.word()).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().map(ev -> ev.name).collect(Collectors.toList()) ,bb)).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().filter(CarpetEventServer.Event::isNeeded).map(ev -> ev.name).collect(Collectors.toList()) ,bb)).
then(argument("call", StringArgumentType.greedyString()).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.getEvent(StringArgumentType.getString(cc, "event"), CarpetServer.scriptServer).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)).
executes( (cc) -> CarpetServer.scriptServer.events.removeEventFromCommand(
@@ -311,14 +312,14 @@ 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 CarpetScriptHost getHost(CommandContext<ServerCommandSource> context)
private static CarpetScriptHost getHost(CommandContext<ServerCommandSource> context) throws CommandSyntaxException
{
CarpetScriptHost host;
try
{
String name = StringArgumentType.getString(context, "app").toLowerCase(Locale.ROOT);
CarpetScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost);
host = parentHost.retrieveForExecution(context.getSource());
host = parentHost.retrieveOwnForExecution(context.getSource());
}
catch (IllegalArgumentException ignored)
{
@@ -327,7 +328,7 @@ public class ScriptCommand
host.setChatErrorSnooper(context.getSource());
return host;
}
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c)
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c) throws CommandSyntaxException
{
CarpetScriptHost host = getHost(c);
return host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList());
@@ -350,7 +351,7 @@ public class ScriptCommand
}
return 1;
}
private static int listGlobals(CommandContext<ServerCommandSource> context, boolean all)
private static int listGlobals(CommandContext<ServerCommandSource> context, boolean all) throws CommandSyntaxException
{
CarpetScriptHost host = getHost(context);
ServerCommandSource source = context.getSource();
@@ -425,7 +426,7 @@ public class ScriptCommand
//host.resetErrorSnooper(); // lets say no need to reset the snooper in case something happens on the way
}
private static int invoke(CommandContext<ServerCommandSource> context, String call, BlockPos pos1, BlockPos pos2, String args)
private static int invoke(CommandContext<ServerCommandSource> context, String call, BlockPos pos1, BlockPos pos2, String args) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -453,7 +454,7 @@ public class ScriptCommand
}
private static int compute(CommandContext<ServerCommandSource> context, String expr)
private static int compute(CommandContext<ServerCommandSource> context, String expr) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -463,7 +464,7 @@ public class ScriptCommand
});
}
private static int scriptScan(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr)
private static int scriptScan(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -512,7 +513,7 @@ public class ScriptCommand
private static int scriptFill(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr,
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, String mode)
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, String mode) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -14,6 +14,7 @@ import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.settings.ParsedRule;
import carpet.utils.Messenger;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.damage.DamageSource;
@@ -47,8 +48,9 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -65,59 +67,84 @@ public class CarpetEventServer
public static class Callback
{
public final String host;
public final String optionalTarget;
public final FunctionValue function;
public final List<Value> parametrizedArgs;
public Callback(String host, FunctionValue function, List<Value> parametrizedArgs)
public Callback(String host, String target, FunctionValue function, List<Value> parametrizedArgs)
{
this.host = host;
this.function = function;
this.optionalTarget = target;
this.parametrizedArgs = parametrizedArgs==null?NOARGS:parametrizedArgs;
}
/**
* Used also in entity events
* @param asSource - entity command source
* @param sender - entity command source
* @param runtimeArgs = options
*/
public boolean execute(ServerCommandSource asSource, List<Value> runtimeArgs)
public boolean execute(ServerCommandSource sender, List<Value> runtimeArgs)
{
if (this.parametrizedArgs.isEmpty())
return CarpetServer.scriptServer.runas(
asSource.withLevel(CarpetSettings.runPermissionLevel),
host, function, runtimeArgs);
else
if (!this.parametrizedArgs.isEmpty())
{
List<Value> combinedArgs = new ArrayList<>(runtimeArgs);
combinedArgs.addAll(this.parametrizedArgs);
return CarpetServer.scriptServer.runas(
asSource.withLevel(CarpetSettings.runPermissionLevel),
host, function, combinedArgs);
runtimeArgs = new ArrayList<>(runtimeArgs);
runtimeArgs.addAll(this.parametrizedArgs);
}
return CarpetServer.scriptServer.runEventCall(
sender.withLevel(CarpetSettings.runPermissionLevel),
host, optionalTarget, function, runtimeArgs
);
}
/**
* Used also in entity events
* @param recipient - optional target player argument
* @param sender - sender of the signal
* @param optionalRecipient - optional target player argument
* @param runtimeArgs = options
*/
public Boolean targetPlayer(ServerPlayerEntity recipient, List<Value> runtimeArgs)
public int 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.runForPlayer(recipient, host, function, args);
return CarpetServer.scriptServer.signal(sender, optionalRecipient, host, function, args, false);
}
@Override
public String toString()
{
return function.getString()+((host==null)?"":"(from "+host+")");
return function.getString()+((host==null)?"":"(from "+host+(optionalTarget == null?"":"/"+optionalTarget)+")");
}
public static class Signature
{
String function;
String host;
String target;
public Signature(String fun, String h, String t)
{
function = fun;
host = h;
target = t;
}
}
public static Signature fromString(String str)
{
Pattern find = Pattern.compile("(\\w+)(?:\\(from (\\w+)(?:/(\\w+))?\\))?");
Matcher matcher = find.matcher(str);
if(matcher.matches())
{
return new Signature(matcher.group(1), matcher.group(2), matcher.group(3));
}
return new Signature(str, null, null);
}
}
@@ -129,7 +156,8 @@ public class CarpetEventServer
public ScheduledCall(CarpetContext context, FunctionValue function, List<Value> args, long dueTime)
{
super(context.host.getName(), function, args);
// ignoring target as we will be always calling self
super(context.host.getName(), null, function, args);
this.ctx = context;
this.dueTime = dueTime;
}
@@ -139,7 +167,7 @@ public class CarpetEventServer
*/
public void execute()
{
CarpetServer.scriptServer.runas(ctx.origin, ctx.s, host, function, parametrizedArgs);
CarpetServer.scriptServer.runScheduledCall(ctx.origin, ctx.s, host, (CarpetScriptHost) ctx.host, function, parametrizedArgs);
}
@@ -173,7 +201,7 @@ public class CarpetEventServer
}
}
public int callCustom(ServerPlayerEntity receipient, List<Value> callArg)
public int signal(ServerCommandSource sender, ServerPlayerEntity optinoalReceipient, List<Value> callArg)
{
if (callList.isEmpty()) return 0;
//List<Callback> fails = new ArrayList<>();
@@ -181,15 +209,13 @@ public class CarpetEventServer
int successes = 0;
for (Callback call: callList)
{
Boolean result = call.targetPlayer(receipient, callArg);
//if (result == Boolean.FALSE) fails.add(call);
if (result == Boolean.TRUE) successes++;
successes += Math.max(0, call.signal(sender, optinoalReceipient, callArg));
}
//for (Callback call : fails) callList.remove(call);
return successes;
}
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function<ScriptHost, Boolean> verifier)
public boolean addFromExternal(ServerCommandSource source, String hostName, String funName, Consumer<ScriptHost> hostOnEventHandler)
{
ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName);
if (host == null)
@@ -198,11 +224,7 @@ public class CarpetEventServer
Messenger.m(source, "r Unknown app "+hostName);
return false;
}
if (!verifier.apply(host))
{
Messenger.m(source, "r Global event can only be added to apps with global scope");
return false;
}
hostOnEventHandler.accept(host);
FunctionValue udf = host.getFunction(funName);
if (udf == null || udf.getArguments().size() != reqArgs)
{
@@ -210,11 +232,24 @@ public class CarpetEventServer
Messenger.m(source, "r Callback doesn't expect required number of arguments: "+reqArgs);
return false;
}
String target = null;
if (host.isPerUser())
{
try
{
target = source.getPlayer().getEntityName();
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "r Cannot add event to a player scoped app from a command without a player context");
return false;
}
}
//all clear
//remove duplicates
removeEventCall(hostName, udf.getString());
callList.add(new Callback(hostName, udf, null));
removeEventCall(hostName, target, udf.getString());
callList.add(new Callback(hostName, target, udf, null));
return true;
}
public boolean addEventCallInternal(ScriptHost host, FunctionValue function, List<Value> args)
@@ -225,20 +260,39 @@ public class CarpetEventServer
}
//all clear
//remove duplicates
removeEventCall(host.getName(), function.getString());
callList.add(new Callback(host.getName(), function, args));
removeEventCall(host.getName(), host.user, function.getString());
callList.add(new Callback(host.getName(), host.user, function, args));
return true;
}
public void removeEventCall(String hostName, String funName)
public void removeEventCall(String hostName, String target, String funName)
{
callList.removeIf((c)-> c.function.getString().equals(funName) && ( hostName == c.host || (c.host != null && c.host.equalsIgnoreCase(hostName) )) );
callList.removeIf((c)-> c.function.getString().equals(funName)
&& (Objects.equals(c.host, hostName))
&& (Objects.equals(c.optionalTarget, target))
);
}
public void removeAllCalls(CarpetScriptHost host)
{
callList.removeIf((c)-> c.host != null && c.host.equals(host.getName()));
callList.removeIf((c)-> (Objects.equals(c.host, host.getName()))
&& (Objects.equals(c.optionalTarget, host.user)));
}
public void createChildEvents(CarpetScriptHost host)
{
List<Callback> copyCalls = new ArrayList<>();
callList.forEach((c)->
{
if ((Objects.equals(c.host, host.getName()))
&& c.optionalTarget == null)
{
copyCalls.add(new Callback(c.host, host.user, c.function, c.parametrizedArgs));
}
});
callList.addAll(copyCalls);
}
public void clearEverything()
{
callList.clear();
@@ -801,6 +855,12 @@ public class CarpetEventServer
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.removeAllCalls(host));
}
public static void transferAllHostEventsToChild(CarpetScriptHost host)
{
byName.values().forEach((e) -> e.handler.createChildEvents(host));
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.createChildEvents(host));
}
public static void clearAllBuiltinEvents()
{
byName.values().forEach(e -> e.handler.clearEverything());
@@ -888,7 +948,7 @@ public class CarpetEventServer
{
return false;
}
boolean added = ev.handler.addEventCall(source, host, funName, h -> canAddEvent(ev, h));
boolean added = ev.handler.addFromExternal(source, host, funName, h -> onEventAddedToHost(ev, h));
if (added) Messenger.m(source, "gi Added " + funName + " to " + event);
return added;
}
@@ -897,8 +957,7 @@ public class CarpetEventServer
{
// this is globals only
Event ev = Event.byName.get(event);
if (!canAddEvent(ev, host))
throw new InternalExpressionException("Global event "+event+" can only be added to apps with global scope");
onEventAddedToHost(ev, host);
boolean success = ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
if (!success) throw new InternalExpressionException("Global event "+event+" requires "+ev.handler.reqArgs+", not "+(function.getNumParams()-((args==null)?0:args.size())));
}
@@ -906,22 +965,21 @@ public class CarpetEventServer
public boolean handleCustomEvent(String event, CarpetScriptHost host, FunctionValue function, List<Value> args)
{
Event ev = Event.getOrCreateCustom(event, host.getScriptServer());
if (!canAddEvent(ev, host))
throw new InternalExpressionException("Global built-in event "+event+" can only be handled in apps with global scope");
onEventAddedToHost(ev, host);
return ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
}
public int signalEvent(String event, CarpetScriptHost host, ServerPlayerEntity player, List<Value> callArgs)
public int signalEvent(String event, CarpetContext cc, ServerPlayerEntity optionalTarget, List<Value> callArgs)
{
Event ev = Event.getEvent(event, host.getScriptServer());
Event ev = Event.getEvent(event, ((CarpetScriptHost)cc.host).getScriptServer());
if (ev == null) return -1;
return ev.handler.callCustom(player, callArgs);
return ev.handler.signal(cc.s, optionalTarget, callArgs);
}
private boolean canAddEvent(Event event, ScriptHost host)
private void onEventAddedToHost(Event event, ScriptHost host)
{
if (event.deprecated()) host.issueDeprecation(event.name+" event");
return !(event.globalOnly && (host.perUser || host.parent != null));
//return !(event.globalOnly && (host.perUser || host.parent != null));
}
public boolean removeEventFromCommand(ServerCommandSource source, String event, String funName)
@@ -932,8 +990,8 @@ public class CarpetEventServer
Messenger.m(source, "r Unknown event: " + event);
return false;
}
Pair<String,String> call = decodeCallback(funName);
ev.handler.removeEventCall(call.getLeft(), call.getRight());
Callback.Signature call = Callback.fromString(funName);
ev.handler.removeEventCall(call.host, call.target, call.function);
// could verified if actually removed
Messenger.m(source, "gi Removed event: " + funName + " from "+event);
return true;
@@ -949,13 +1007,15 @@ public class CarpetEventServer
public void removeBuiltInEvent(String event, CarpetScriptHost host, String funName)
{
Event ev = Event.getEvent(event, host.getScriptServer());
if (ev != null) ev.handler.removeEventCall(host.getName(), funName);
if (ev != null) ev.handler.removeEventCall(host.getName(), host.user, funName);
}
public void removeAllHostEvents(CarpetScriptHost host)
{
// remove event handlers
Event.removeAllHostEvents(host);
if (host.isPerUser())
for (ScriptHost child: host.userHosts.values()) Event.removeAllHostEvents((CarpetScriptHost) child);
// remove scheduled calls
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
}
@@ -1,6 +1,7 @@
package carpet.script;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.script.api.Auxiliary;
import carpet.script.argument.FunctionArgument;
import carpet.script.bundled.Module;
@@ -15,7 +16,6 @@ import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.CarpetSettings;
import carpet.utils.Messenger;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -117,7 +117,7 @@ public class CarpetScriptHost extends ScriptHost
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
{
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveForExecution(ctx.getSource());
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
List<String> argNames = funcSpec.function.getArguments();
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
throw new SimpleCommandExceptionType(new LiteralText("Target function "+funcSpec.function.getPrettyString()+" as wrong number of arguments, required "+paramNames.size()+", found "+argNames.size()+" with "+funcSpec.args.size()+" provided")).create();
@@ -177,6 +177,14 @@ public class CarpetScriptHost extends ScriptHost
return new CarpetScriptHost(scriptServer, main, false, this, appConfig, appArgTypes, commandValidator, isRuleApp);
}
@Override
protected void transferToChild(ScriptHost host)
{
super.transferToChild(host);
// transfer Events
CarpetEventServer.Event.transferAllHostEventsToChild((CarpetScriptHost) host);
}
@Override
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
{
@@ -319,38 +327,58 @@ public class CarpetScriptHost extends ScriptHost
}
}
public CarpetScriptHost retrieveForExecution(ServerCommandSource source)
public List<CarpetScriptHost> retrieveForExecution(ServerCommandSource source, String optionalTarget)
{
CarpetScriptHost host = this;
List<CarpetScriptHost> targets = new ArrayList<>();
if (perUser)
{
try
if (optionalTarget == null)
{
ServerPlayerEntity player = source.getPlayer();
host = (CarpetScriptHost) retrieveForExecution(player.getName().getString());
for (ServerPlayerEntity player : source.getMinecraftServer().getPlayerManager().getPlayerList())
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
catch (CommandSyntaxException e)
else
{
host = (CarpetScriptHost) retrieveForExecution((String) null);
ServerPlayerEntity player = source.getMinecraftServer().getPlayerManager().getPlayer(optionalTarget);
if (player != null)
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
}
if (host.errorSnooper == null) host.setChatErrorSnooper(source);
return host;
else
{
targets.add(this);
if (this.errorSnooper == null) this.setChatErrorSnooper(source);
}
return targets;
}
public CarpetScriptHost retrieveForExecution(ServerPlayerEntity target)
public CarpetScriptHost retrieveOwnForExecution(ServerCommandSource source) throws CommandSyntaxException
{
// allowing only run
if (perUser == (target == null)) return null;
if (!perUser)
{
if (errorSnooper == null) setChatErrorSnooper(this.scriptServer.server.getCommandSource());
if (errorSnooper == null) setChatErrorSnooper(source);
return this;
}
// user based
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(target.getName().getString());
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(target.getCommandSource());
ServerPlayerEntity player;
try
{
player = source.getPlayer();
}
catch (CommandSyntaxException ignored)
{
throw new SimpleCommandExceptionType(new LiteralText("Cannot run player based apps without the player context")).create();
}
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(player.getEntityName());
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(source);
return userHost;
}
@@ -1,8 +1,8 @@
package carpet.script;
import carpet.CarpetSettings;
import carpet.script.argument.FunctionArgument;
import carpet.script.bundled.BundledModule;
import carpet.CarpetSettings;
import carpet.CarpetServer;
import carpet.script.bundled.FileModule;
import carpet.script.bundled.Module;
@@ -296,6 +296,11 @@ public class CarpetScriptServer
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app");
return false;
}
if (newHost.isPerUser())
{
// that will provide player hosts right at the startup
newHost.retrieveForExecution(source, null);
}
long end = System.nanoTime();
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
return true;
@@ -325,8 +330,8 @@ public class CarpetScriptServer
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player)).
executes( (c) ->
{
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(),"__command", null, "");
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),"__command", null, "");
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
return (int)response.readInteger();
});
@@ -355,15 +360,15 @@ public class CarpetScriptServer
then(literal(function).
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
executes((c) -> {
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(), function, null, "");
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, "");
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
return (int) response.readInteger();
}).
then(argument("args...", StringArgumentType.greedyString()).
executes( (c) -> {
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(), function,null, StringArgumentType.getString(c, "args..."));
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, StringArgumentType.getString(c, "args..."));
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
return (int)response.readInteger();
})));
@@ -394,54 +399,84 @@ public class CarpetScriptServer
return true;
}
public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List<Value> argv)
{
return runas(BlockPos.ORIGIN, source, hostname, udf, argv);
}
public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List<Value> argv)
public boolean runEventCall(ServerCommandSource sender, String hostname, String optionalTarget, FunctionValue udf, List<Value> argv)
{
CarpetScriptHost host = globalHost;
try
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)
{
if (hostname != null)
host = modules.get(hostname).retrieveForExecution(source);
host.callUDF(origin, source, udf, argv);
target = sender.getMinecraftServer().getPlayerManager().getPlayer(optionalTarget);
if (target == null) return false;
}
catch (NullPointerException | InvalidCallbackException npe)
{
return false;
}
return true;
int successes = signal(sender, target, hostname, udf, argv, true );
return successes >= 0;
}
public Boolean runForPlayer(ServerPlayerEntity recipient, String hostname, FunctionValue udf, List<Value> argv)
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)
{
CarpetScriptHost host;
ServerCommandSource source;
if (hostname == null)
{
if (recipient != null) return null;
host = globalHost;
source = server.getCommandSource();
if (optionalRecipient != null) return 0;
try
{
globalHost.callUDF(BlockPos.ORIGIN, sender, udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
return reportFails?-1:0;
}
return 1;
}
else
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))
{
host = modules.get(hostname).retrieveForExecution(recipient);
if (host == null) // not applicable
return null;
source = (recipient == null)? server.getCommandSource() : recipient.getCommandSource();
// getPlayer will always return nonnull cause retrieve for execution only returns hosts for existing players.
ServerCommandSource executingSource = host.perUser
? source.getMinecraftServer().getPlayerManager().getPlayer(host.user).getCommandSource()
: source.getMinecraftServer().getCommandSource();
try
{
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
if (reportFails) return -1;
continue;
}
successes ++;
}
try
{
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
return false;
}
return true;
return successes;
}
public void tick()
@@ -461,6 +496,23 @@ public class CarpetScriptServer
}
}
public void onPlayerJoin(ServerPlayerEntity player)
{
modules.values().forEach(h ->
{
if (h.isPerUser())
{
try
{
h.retrieveOwnForExecution(player.getCommandSource());
}
catch (CommandSyntaxException ignored)
{
}
}
});
}
static class TransferData
{
boolean perUser;
@@ -9,6 +9,7 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Arrays;
import java.util.Collections;
@@ -19,7 +20,7 @@ import java.util.Map;
public class EntityEventsGroup
{
private final Map<Event, Map<String, CarpetEventServer.Callback>> actions;
private final Map<Event, Map<Pair<String,String>, CarpetEventServer.Callback>> actions;
private final Entity entity;
public EntityEventsGroup(Entity e)
{
@@ -30,37 +31,47 @@ public class EntityEventsGroup
public void onEvent(Event type, Object... args)
{
if (actions.isEmpty()) return; // most of the cases, trying to be nice
Map<String, CarpetEventServer.Callback> actionSet = actions.get(type);
Map<Pair<String,String>, CarpetEventServer.Callback> actionSet = actions.get(type);
if (actionSet == null) return;
for (Iterator<Map.Entry<String, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
for (Iterator<Map.Entry<Pair<String,String>, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
{
Map.Entry<String, CarpetEventServer.Callback> action = iterator.next();
ScriptHost host = CarpetServer.scriptServer.getHostByName(action.getKey());
Map.Entry<Pair<String,String>, CarpetEventServer.Callback> action = iterator.next();
Pair<String,String> key = action.getKey();
ScriptHost host = CarpetServer.scriptServer.getHostByName(key.getLeft());
if (host == null)
{
iterator.remove();
continue;
}
if (key.getRight() != null)
{
if (entity.getServer().getPlayerManager().getPlayer(key.getRight())==null)
{
iterator.remove();
continue;
}
}
if (!type.call(action.getValue(), entity, args))
iterator.remove();
}
if (actionSet.isEmpty()) actions.remove(type);
}
public void addEvent(Event type, String hostName, FunctionValue fun, List<Value> extraargs)
public void addEvent(Event type, ScriptHost host, FunctionValue fun, List<Value> extraargs)
{
Pair<String,String> key = Pair.of(host.getName(), host.user);
if (fun != null)
{
CarpetEventServer.Callback call = type.create(hostName, fun, extraargs);
CarpetEventServer.Callback call = type.create(key, fun, extraargs);
if (call == null)
throw new InternalExpressionException("wrong number of arguments for callback, required "+type.argcount);
actions.computeIfAbsent(type, k -> new HashMap<>()).put(hostName, call);
actions.computeIfAbsent(type, k -> new HashMap<>()).put(key, call);
}
else
{
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(hostName);
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(key);
if (actions.get(type).isEmpty())
actions.remove(type);
}
@@ -107,13 +118,13 @@ public class EntityEventsGroup
argcount = args+1; // entity is not extra
byName.put(identifier, this);
}
public CarpetEventServer.Callback create(String hostName, FunctionValue function, List<Value> extraArgs)
public CarpetEventServer.Callback create(Pair<String,String> key, FunctionValue function, List<Value> extraArgs)
{
if ((function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argcount)
{
return null;
}
return new CarpetEventServer.Callback(hostName, function, extraArgs);
return new CarpetEventServer.Callback(key.getLeft(), key.getRight(), function, extraArgs);
}
public boolean call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
{
+10 -4
View File
@@ -318,15 +318,21 @@ public abstract class ScriptHost
ScriptHost oldUserHost = userHosts.get(user);
if (oldUserHost != null) return oldUserHost;
ScriptHost userHost = this.duplicate();
userHost.modules.putAll(this.modules);
this.moduleData.forEach((key, value) -> userHost.moduleData.put(key, new ModuleData(key, value)));
// fixing imports
userHost.moduleData.forEach((module, data) -> data.setImportsBasedOn(userHost, this.moduleData.get(data.parent)));
userHost.user = user;
this.transferToChild(userHost);
userHosts.put(user, userHost);
return userHost;
}
protected void transferToChild(ScriptHost host)
{
// adding imports
host.modules.putAll(this.modules);
this.moduleData.forEach((key, value) -> host.moduleData.put(key, new ModuleData(key, value)));
// fixing imports
host.moduleData.forEach((module, data) -> data.setImportsBasedOn(host, this.moduleData.get(data.parent)));
}
public void handleExpressionException(String msg, ExpressionException exc)
{
System.out.println(msg+": "+exc);
@@ -961,6 +961,7 @@ public class Auxiliary {
{
if (lv.size() == 0)
throw new InternalExpressionException("'signal' requires at least one argument");
CarpetContext cc = (CarpetContext)c;
CarpetScriptServer server = ((CarpetScriptHost)c.host).getScriptServer();
String eventName = lv.get(0).evalValue(c).getString();
// no such event yet
@@ -975,10 +976,10 @@ public class Auxiliary {
args = FunctionValue.resolveArgs(lv.subList(2, lv.size()), c, t);
}
}
int counts = ((CarpetScriptHost)c.host).getScriptServer().events.signalEvent(eventName, (CarpetScriptHost) c.host, player, args);
int counts = ((CarpetScriptHost)c.host).getScriptServer().events.signalEvent(eventName, cc, player, args);
if (counts < 0) return LazyValue.NULL;
Value ret = new NumericValue(counts);
return (cc, tt) -> ret;
return (c_, t_) -> ret;
});
}
@@ -1372,7 +1372,7 @@ public class EntityValue extends Value
EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName);
if (event == null)
throw new InternalExpressionException("Unknown entity event: " + eventName);
((EntityInterface)entity).getEventContainer().addEvent(event, cc.host.getName(), fun, args);
((EntityInterface)entity).getEventContainer().addEvent(event, cc.host, fun, args);
}
@Override
@@ -90,12 +90,9 @@ calculate(end)->(
)
);
_carpets() -> system_info('world_carpet_rules'):'carpets'=='true';
on_player_places_block(player, item_tuple, hand, block) ->
(
if(block==global_assist_block && _carpets(),
if(block==global_assist_block,
effective_pos = pos(block)+[0.5, 0, 0.5];
if(global_current_start==null||player~'sneaking',//wont complain for first carpet
set_start(effective_pos)
@@ -107,7 +104,7 @@ on_player_places_block(player, item_tuple, hand, block) ->
on_player_uses_item(player, item_tuple, hand) ->
(
if (item_tuple:0 == global_assist_block && hand == 'mainhand' && _carpets(),
if (item_tuple:0 == global_assist_block && hand == 'mainhand',
if (player ~'sneaking',
global_current_mode = global_display_modes:(global_display_modes ~ global_current_mode + 1);
display_title(player, 'actionbar', format('w Distance mode: ','e '+global_current_mode));