custom events, and low level event API
This commit is contained in:
1 parent
0e1a152510
commit
55643632ca
15 files changed
+466
-99
No files matched your search
+196
-18
@@ -1,39 +1,48 @@
|
||||
# Scarpet events system
|
||||
|
||||
Provides the ability to execute specific function whenever an event occurs. The functions to be registered need to
|
||||
conform with the arguments to the event specification. When loading module functions, each function that starts
|
||||
with `__on_...` and has the required arguments, will be bound automatically. In case of player specific modules,
|
||||
all player action events will be directed to the appropriate player space, and all tick events will be executed in
|
||||
the global context, so its not a good idea to mix these two, so use either of these, or use commands to call tick
|
||||
events directly, or handle player specific data inside an app.
|
||||
Scarpet provides the ability to execute specific function whenever an event occurs. The functions to be subscribed for an event
|
||||
need to conform with the arguments to the event specification. There are several built-in events triggered when certain in-game
|
||||
events occur, but app designers can create their own events and trigger them across all loaded apps.
|
||||
|
||||
Most events strive to report right before they take an effect in the game. The purpose of that is that this give a choice
|
||||
When loading the app, each function that starts
|
||||
with `__on_<event>` and has the required arguments, will be bound automatically to a corresponding built-in event. '`undef`'ying
|
||||
of such function would result in unbinding the app from this event.
|
||||
|
||||
In case of `player` scoped apps,
|
||||
all player action events will be directed to the appropriate player hosts. Global events, like `'tick'`, cannot be handled
|
||||
in `'player'` scoped app.
|
||||
|
||||
`'global'` scoped apps will receive both global and player built-in events.
|
||||
|
||||
Most built-in events strive to report right before they take an effect in the game. The purpose of that is that this give a choice
|
||||
for the programmer to handle them right away (as it happens, potentially affect the course of action by changing the
|
||||
environment right before it), or decide to handle it after by scheduling another call for the end of the tick. Or both -
|
||||
partially handle the event before it happens and handle the rest after. While in some cases this may lead to programmers
|
||||
confusion (like handling the respawn event still referring to player's original position and dimension), but gives much
|
||||
more control over these events.
|
||||
|
||||
Programmers can also define their own events and signal other events, including built-in events, and across all loaded apps.
|
||||
|
||||
## Event list
|
||||
|
||||
Here is a list of events that are handled by scarpet. This list includes prefixes for function names, allowing apps
|
||||
Here is a list of events that are handled by default in scarpet. This list includes prefixes for function names, allowing apps
|
||||
to autoload them, but you can always add any function to any event (using `/script event` command)
|
||||
if it accepts required number of parameters.
|
||||
|
||||
## Global events
|
||||
## Built-in global events
|
||||
|
||||
Handling global events is only allowed in apps with `'global'` scope. With the default scope (`'player'`) you
|
||||
simply wouldn't know which player to hook up this event to. All other events can be handled by any app type.
|
||||
simply wouldn't know which player to hook up this event to.
|
||||
|
||||
### `__on_tick()`
|
||||
Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()`
|
||||
to access other dimensions from there.
|
||||
|
||||
### `__on_tick_nether()` (Deprecated)
|
||||
Duplicate of `tick`, just automatically located in the nether.
|
||||
Duplicate of `tick`, just automatically located in the nether. Use `__on_tick() -> in_dimension('nether', ... ` instead.
|
||||
|
||||
### `__on_tick_ender()` (Deprecated)
|
||||
Duplicate of `tick`, just automatically located in the end.
|
||||
Duplicate of `tick`, just automatically located in the end. Use `__on_tick() -> in_dimension('end', ... ` instead.
|
||||
|
||||
### `__on_chunk_generated(x,z)`
|
||||
Called right after a chunk at a given coordinate is full generated. `x` and `z` correspond
|
||||
@@ -45,12 +54,18 @@ Triggered right after a lightning strikes. Lightning entity as well as potential
|
||||
already be spawned at that point. `mode` is `true` if the lightning did cause a trap to spawn.
|
||||
|
||||
### `__on_carpet_rule_changes(rule, new_value)`
|
||||
Triggered when a Carpet rule is changed. It includes extension rules, which will be namespaced as `namespace:rule`.
|
||||
|
||||
## Player events
|
||||
Triggered when a carpet mod rule is changed. It includes extension rules, not using default `/carpet` command,
|
||||
which will then be namespaced as `namespace:rule`.
|
||||
|
||||
These are triggered with a player context. For apps with a 'player' scope, they trigger once for the appropriate
|
||||
player. In apps with `global` scope they trigger once for all players.
|
||||
### entity load event -> check `entity_load_handler()`
|
||||
|
||||
These will trigger every time an entity of a given type is loaded into the game: spawned, added with a chunks,
|
||||
spawned from commands, anything really. Check `entity_load_handler()` in the entity section for details.
|
||||
|
||||
## Built-in player events
|
||||
|
||||
These are triggered with a player context. For apps with a `'player'` scope, they trigger once for the appropriate
|
||||
player. In apps with `global` scope they trigger once as well as a global event.
|
||||
|
||||
### `__on_player_uses_item(player, item_tuple, hand)`
|
||||
Triggers with a right click action. Event is triggered right after a server receives the packet, before the
|
||||
@@ -182,7 +197,170 @@ Triggered when a player statistic changes. Doesn't notify on periodic an rhythmi
|
||||
`time_since_death`, `time_since_rest`, and `played_one_minute` since these are triggered every tick. Event
|
||||
is handled before scoreboard values for these statistics are changed.
|
||||
|
||||
### `/script event` command
|
||||
## Custom events and hacking into scarpet event system
|
||||
|
||||
App programmers can define and trigger their own custom events. Unlike built-in events, all custom events pass a single value
|
||||
as an argument, but this doesn't mean that they cannot pass a complex list, map, or nbt tag as a message. Each event signal is
|
||||
either targetting all global context apps, if no target player has been identified, or only player scoped apps, if the target player
|
||||
is specified.
|
||||
|
||||
Programmers can also handle built-in events the same way as custom events, as well as triggering built-in events, which I have
|
||||
have no idea why you would need that. The following snippets have the same effect:
|
||||
|
||||
```
|
||||
__on_player_breaks_block(player, block) -> print(player+' broke '+block);
|
||||
```
|
||||
and
|
||||
```
|
||||
handle_event('player_breaks_block', _(player, block) -> print(player+' broke '+block));
|
||||
```
|
||||
|
||||
as well as
|
||||
```
|
||||
undef('__on_player_breaks_block');
|
||||
```
|
||||
and
|
||||
```
|
||||
handle_event('player_breaks_block', null);
|
||||
```
|
||||
And `signal_event` can be used as a trigger, called twice for player based built-in events
|
||||
```
|
||||
signal_event('player_breaks_block', player, player, block); // to target all player scoped apps
|
||||
signal_event('player_breaks_block', null , player, block); // to target all global scoped apps
|
||||
```
|
||||
or (for global only events)
|
||||
```
|
||||
signal_event('tick') // trigger only global scoped apps with no extra arguments
|
||||
```
|
||||
|
||||
### `handle_event(event, callback ...)`
|
||||
|
||||
Provides a handler for an event identified by the '`event`' argument. If the event doesn't exist yet, it will be created.
|
||||
All loaded apps globally can trigger that event, when they call corresponding `signal_event(event, ...)`. Callback can be
|
||||
defined as a function name, function value (or a lambda function), along with optional extra arguments that will be passed
|
||||
to it when the event is triggered. All custom events expect a function that takes one free argument, passed by the event trigger.
|
||||
If extra arguments are provided, they will be appended to the argument list of the callback function.
|
||||
|
||||
Returns `true` if subscription to the event was successful, or `false` if it failed (for instance wrong scope for built-in event,
|
||||
or incorect number of parameters for the event).
|
||||
|
||||
<pre>
|
||||
foo(a) -> print(a);
|
||||
handle_event('boohoo', 'foo');
|
||||
|
||||
bar(a, b, c) -> print([a, b, c]);
|
||||
handle_event('boohoo', 'bar', 2, 3) // using b = 2, c = 3, a - passed by the caller
|
||||
|
||||
handle_event('tick', _() -> foo('tick happened')); // built-in event
|
||||
|
||||
handle_event('tick', null) // nah, ima good, kthxbai
|
||||
</pre>
|
||||
|
||||
### `signal_event(event, target_player?, ... args?)`
|
||||
|
||||
Fires a specific event. If the event does not exist (only `handle_event` creates missing mising new events), or provided argument list
|
||||
was not matching the callee expected arguments, returns `null`,
|
||||
otherwise returns number of apps notified. If `target_player` is specified and not `null` triggers a player specific event, targetting
|
||||
only `player` scoped apps for that player. Apps with globals scope will not be notified even if they handle this event.
|
||||
If the `target_player` is omitted or `null`, it will instead only target `global` scoped apps and skip `player` scoped.
|
||||
Therefore each custom event trigger can trigger each app once, but calls to '`signal_event`' can be repeated to cover
|
||||
all app scopes. Note that all built-in player events have a player as a first argument, so to trigger these events, you need to
|
||||
provide them twice - once to specify the target player scope and second - to provide as an argument to the handler function.
|
||||
|
||||
<pre>
|
||||
signal_event('player_breaks_block', player, player, block); // to target all player scoped apps
|
||||
signal_event('player_breaks_block', null , player, block); // to target all global scoped apps
|
||||
signal_event('tick') // triggering tick event in all loaded apps
|
||||
</pre>
|
||||
|
||||
## Custom events example
|
||||
|
||||
The following example contains 4 apps, 2 of them defining a new event, `"player_farts"` triggered after 40 ticks of continuous
|
||||
squatting. Note that this could call the recipient event handlers twice, since `signal_event` will be called twice, but
|
||||
in this case the event is once called as a global event, and once as a player event, targeting each app once. Custom events is
|
||||
a good idea to share common event detection code with one app, and using the outcome of these events in multiple places.
|
||||
Obviously you would define each event handler once not twice in your app system.
|
||||
|
||||
In this
|
||||
case here we have two ways of detecting all players squatting for more than 40 ticks - one of those apps will produce cloud particles
|
||||
in an apprpriate location, and the other will produce a sound when it happens, and they both will proceed to signal the same event,
|
||||
thankfully for different scopes, otherwise other apps would trigger twice.
|
||||
|
||||
```
|
||||
// detector1.sc
|
||||
// detecting event in a tick loop
|
||||
__config() -> {'scope' -> 'global'};
|
||||
|
||||
global_sneaks = {};
|
||||
__on_tick() -> for(player('all'),
|
||||
if (_~'sneaking',
|
||||
sneak_time = if (has(global_sneaks:_), tick_time()-global_sneaks:_ , global_sneaks:_=tick_time(); 0);
|
||||
if (sneak_time > 20 && sneak_time < 40,
|
||||
particle('cloud', pos(_) + [0, _~'height'/2, 0] - _~'look', 1, 0.1)
|
||||
);
|
||||
if (sneak_time == 40,
|
||||
signal_event('player_farts', _, _) // triggering all player scoped apps from a global app
|
||||
)
|
||||
, // else
|
||||
delete(global_sneaks:_)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
```
|
||||
// detector2.sc
|
||||
// detecting events in a more appropriate way - player based and event based app
|
||||
__on_player_starts_sneaking(p) ->
|
||||
(
|
||||
global_sneaking = tick_time();
|
||||
schedule(40, 'check', p);
|
||||
);
|
||||
|
||||
check(p) ->
|
||||
if( global_sneaking && tick_time() - global_sneaking == 40,
|
||||
sound('entity.shulker.shoot', pos(p), 1, 0.6, 'player');
|
||||
signal_event('player_farts', null, p) // trigerring all global apps from a player scoped app
|
||||
);
|
||||
|
||||
__on_player_stops_sneaking(p) -> (global_sneaking = false);
|
||||
```
|
||||
|
||||
And then two other apps that meant to do something with this event. One mimics an explosion, and the other - grows crops around
|
||||
the player.
|
||||
|
||||
```
|
||||
client1.sc
|
||||
__config() -> {'scope' -> 'global'};
|
||||
|
||||
on_fart(p, msg) ->
|
||||
(
|
||||
print(p+msg);
|
||||
particle('explosion', pos(p));
|
||||
sound('entity.generic.explode', pos(p), 1, 0.5);
|
||||
);
|
||||
|
||||
handle_event('player_farts', 'on_fart', ' goes kaboom');
|
||||
```
|
||||
|
||||
```
|
||||
client2.sc
|
||||
longsneak(p) ->
|
||||
(
|
||||
ppos = pos(p);
|
||||
loop ( 500,
|
||||
target = ppos+[rand(12),rand(8),rand(12)]-[rand(12),rand(8),rand(12)];
|
||||
if (material(target) == 'plant' && ticks_randomly(target),
|
||||
particle('happy_villager', target, 2, 0.4)
|
||||
);
|
||||
random_tick(target)
|
||||
)
|
||||
);
|
||||
|
||||
handle_event('player_farts', 'longsneak');
|
||||
```
|
||||
|
||||
|
||||
## `/script event` command
|
||||
|
||||
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
|
||||
unbind a specific function from an event. Function to be bounded to an event needs to have the same number of
|
||||
|
||||
@@ -33,16 +33,22 @@ expr1 ; expr2 => expr2 // with expr1 as a side-effect
|
||||
|
||||
## Global variables
|
||||
|
||||
All defined functions are compiled, stored persistently, and available globally - accessible to all other scripts.
|
||||
All defined functions are compiled, stored persistently, and available globally within the app.
|
||||
Functions can only be undefined via call to `undef('fun')`, which would erase global entry for function `fun`.
|
||||
Since all variables have local scope inside each function, one way to share large objects is via global variables
|
||||
Since all variables have local scope inside each function, or each command script,
|
||||
global variables is a way to share the global state.
|
||||
|
||||
Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not only
|
||||
inside the current scope. If used directly in the chat window with the default app, it will persist across calls to `/script`
|
||||
function. Like functions, which are global, global variables can only be undefined via `undef`.
|
||||
|
||||
For apps running in `'global'` scope - all players will share the same global variables and defined functions,
|
||||
and with `player` scope, each player hosts its own state for each app, so function and global_variables are distinct.
|
||||
|
||||
Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not,
|
||||
inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it
|
||||
needs to define it, or initialize it explicitly, or undefine it via `undef`
|
||||
|
||||
<pre>
|
||||
a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2,3,1,1]
|
||||
/script run a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1, 2, 3, 1, 1]
|
||||
/script run a(); a(); global_list // => [1, 2, 3, 1, 1, 1, 1]
|
||||
</pre>
|
||||
|
||||
### `Operator ->`
|
||||
|
||||
@@ -34,6 +34,7 @@ flammable
|
||||
format
|
||||
game_tick
|
||||
generation_status
|
||||
handle_event
|
||||
hardness
|
||||
harvest
|
||||
in_dimension
|
||||
@@ -89,6 +90,7 @@ set
|
||||
set_biome
|
||||
set_poi
|
||||
set_structure
|
||||
signal_event
|
||||
sky_light
|
||||
solid
|
||||
sound
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
|
||||
format logger sound particle particle_line particle_rect system_info
|
||||
|
||||
handle_event signal_event
|
||||
|
||||
run save tick_time world_time day_time last_tick_times game_tick current_dimension in_dimension view_distance
|
||||
|
||||
plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers
|
||||
|
||||
@@ -74,8 +74,8 @@ public class ScriptCommand
|
||||
scarpetMatches.addAll(APIFunctions.stream().
|
||||
filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> s+"(").collect(Collectors.toList()));
|
||||
// not that useful in commandline, more so in external scripts, so skipping here
|
||||
if (eventPrefix != null) scarpetMatches.addAll(CarpetEventServer.Event.publicEvents().stream().
|
||||
filter(s -> s.startsWith(eventPrefix)).map(s -> "__on_"+s+"(").collect(Collectors.toList()));
|
||||
if (eventPrefix != null) scarpetMatches.addAll(CarpetEventServer.Event.publicEvents(null).stream().
|
||||
filter(e -> e.name.startsWith(eventPrefix)).map(s -> "__on_"+s+"(").collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globaFunctionNames(host.main, s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globaVariableNames(host.main, s -> s.startsWith(prefix)).collect(Collectors.toList()));
|
||||
return scarpetMatches;
|
||||
@@ -269,10 +269,10 @@ public class ScriptCommand
|
||||
executes( (cc) -> listEvents(cc.getSource())).
|
||||
then(literal("add_to").
|
||||
then(argument("event", StringArgumentType.word()).
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents() ,bb)).
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().map(ev -> ev.name).collect(Collectors.toList()),bb)).
|
||||
then(argument("call", StringArgumentType.word()).
|
||||
suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)).
|
||||
executes( (cc) -> CarpetServer.scriptServer.events.addEvent(
|
||||
executes( (cc) -> CarpetServer.scriptServer.events.addEventFromCommand(
|
||||
cc.getSource(),
|
||||
StringArgumentType.getString(cc, "event"),
|
||||
null,
|
||||
@@ -283,7 +283,7 @@ public class ScriptCommand
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.modules.keySet(), bb)).
|
||||
then(argument("call", StringArgumentType.word()).
|
||||
suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)).
|
||||
executes( (cc) -> CarpetServer.scriptServer.events.addEvent(
|
||||
executes( (cc) -> CarpetServer.scriptServer.events.addEventFromCommand(
|
||||
cc.getSource(),
|
||||
StringArgumentType.getString(cc, "event"),
|
||||
StringArgumentType.getString(cc, "app"),
|
||||
@@ -291,10 +291,10 @@ public class ScriptCommand
|
||||
)?1:0)))))).
|
||||
then(literal("remove_from").
|
||||
then(argument("event", StringArgumentType.word()).
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents() ,bb)).
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().map(ev -> ev.name).collect(Collectors.toList()) ,bb)).
|
||||
then(argument("call", StringArgumentType.greedyString()).
|
||||
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.get(StringArgumentType.getString(cc, "event")).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)).
|
||||
executes( (cc) -> CarpetServer.scriptServer.events.removeEvent(
|
||||
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(
|
||||
cc.getSource(),
|
||||
StringArgumentType.getString(cc, "event"),
|
||||
StringArgumentType.getString(cc, "call")
|
||||
@@ -335,7 +335,7 @@ public class ScriptCommand
|
||||
private static int listEvents(ServerCommandSource source)
|
||||
{
|
||||
Messenger.m(source, "w Lists ALL event handlers:");
|
||||
for (CarpetEventServer.Event event: CarpetEventServer.Event.byName.values())
|
||||
for (CarpetEventServer.Event event: CarpetEventServer.Event.getAllEvents(CarpetServer.scriptServer, null))
|
||||
{
|
||||
boolean shownEvent = false;
|
||||
for (CarpetEventServer.Callback c: event.handler.callList)
|
||||
|
||||
@@ -11,6 +11,7 @@ import carpet.script.value.NBTSerializableValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.settings.ParsedRule;
|
||||
import carpet.utils.Messenger;
|
||||
import net.minecraft.block.BlockState;
|
||||
@@ -48,6 +49,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -58,6 +60,7 @@ public class CarpetEventServer
|
||||
public final List<ScheduledCall> scheduledCalls = new LinkedList<>();
|
||||
public final MinecraftServer server;
|
||||
private static final List<Value> NOARGS = Collections.emptyList();
|
||||
public final Map<String, Event> customEvents = new HashMap<>();
|
||||
|
||||
public static class Callback
|
||||
{
|
||||
@@ -94,6 +97,23 @@ public class CarpetEventServer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used also in entity events
|
||||
* @param recipient - optional target player argument
|
||||
* @param runtimeArgs = options
|
||||
*/
|
||||
public Boolean targetPlayer(ServerPlayerEntity recipient, List<Value> runtimeArgs)
|
||||
{
|
||||
List<Value> args = runtimeArgs;
|
||||
if (!this.parametrizedArgs.isEmpty())
|
||||
{
|
||||
args = new ArrayList<>(runtimeArgs);
|
||||
args.addAll(this.parametrizedArgs);
|
||||
}
|
||||
return CarpetServer.scriptServer.runForPlayer(recipient, host, function, args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
@@ -152,6 +172,23 @@ public class CarpetEventServer
|
||||
for (Callback call : fails) callList.remove(call);
|
||||
}
|
||||
}
|
||||
|
||||
public int callCustom(ServerPlayerEntity receipient, List<Value> callArg)
|
||||
{
|
||||
if (callList.isEmpty()) return 0;
|
||||
//List<Callback> fails = new ArrayList<>();
|
||||
// skipping fails on purpose - its a player induced call.
|
||||
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++;
|
||||
}
|
||||
//for (Callback call : fails) callList.remove(call);
|
||||
return successes;
|
||||
}
|
||||
|
||||
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function<ScriptHost, Boolean> verifier)
|
||||
{
|
||||
ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName);
|
||||
@@ -198,9 +235,9 @@ public class CarpetEventServer
|
||||
callList.removeIf((c)-> c.function.getString().equals(funName) && ( hostName == c.host || (c.host != null && c.host.equalsIgnoreCase(hostName) )) );
|
||||
}
|
||||
|
||||
public void removeAllCalls(String hostName)
|
||||
public void removeAllCalls(CarpetScriptHost host)
|
||||
{
|
||||
callList.removeIf((c)-> c.host != null && c.host.equals(hostName));
|
||||
callList.removeIf((c)-> c.host != null && c.host.equals(host.getName()));
|
||||
}
|
||||
public void clearEverything()
|
||||
{
|
||||
@@ -211,9 +248,11 @@ public class CarpetEventServer
|
||||
public static class Event
|
||||
{
|
||||
public static final Map<String, Event> byName = new HashMap<>();
|
||||
public static List<String> publicEvents()
|
||||
public static List<Event> publicEvents(CarpetScriptServer server)
|
||||
{
|
||||
return byName.entrySet().stream().filter(e -> e.getValue().isPublic).map(Map.Entry::getKey).collect(Collectors.toList());
|
||||
List<Event> events = byName.values().stream().filter(e -> e.isPublic).collect(Collectors.toList());
|
||||
if (server != null) events.addAll(server.events.customEvents.values());
|
||||
return events;
|
||||
}
|
||||
|
||||
public static final Event TICK = new Event("tick", 0, true)
|
||||
@@ -674,15 +713,15 @@ public class CarpetEventServer
|
||||
}
|
||||
};
|
||||
|
||||
public static String getLoadEvent(EntityType<? extends Entity> et)
|
||||
public static String getEntityLoadEventName(EntityType<? extends Entity> et)
|
||||
{
|
||||
return "entity_loaded_" + Registry.ENTITY_TYPE.getId(et);
|
||||
return "entity_loaded_" + ValueConversions.of(Registry.ENTITY_TYPE.getId(et)).getString();
|
||||
}
|
||||
|
||||
public static final Map<EntityType<? extends Entity>, Event> ENTITY_LOAD= new HashMap<EntityType<? extends Entity>, Event>() {{
|
||||
EntityType.get("zombie");
|
||||
Registry.ENTITY_TYPE.forEach(et -> {
|
||||
put(et, new Event(getLoadEvent(et), 1, true, false)
|
||||
put(et, new Event(getEntityLoadEventName(et), 1, true, false)
|
||||
{
|
||||
@Override
|
||||
public void onEntityAction(Entity entity)
|
||||
@@ -715,6 +754,52 @@ public class CarpetEventServer
|
||||
this.isPublic = isPublic;
|
||||
byName.put(name, this);
|
||||
}
|
||||
|
||||
public static List<Event> getAllEvents(CarpetScriptServer server, Predicate<Event> predicate)
|
||||
{
|
||||
List<CarpetEventServer.Event> eventList = new ArrayList<>(CarpetEventServer.Event.byName.values());
|
||||
eventList.addAll(server.events.customEvents.values());
|
||||
if (predicate == null) return eventList;
|
||||
return eventList.stream().filter(predicate).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Event getEvent(String name, CarpetScriptServer server)
|
||||
{
|
||||
if (byName.containsKey(name)) return byName.get(name);
|
||||
return server.events.customEvents.get(name);
|
||||
}
|
||||
|
||||
public static Event getOrCreateCustom(String name, CarpetScriptServer server)
|
||||
{
|
||||
Event event = getEvent(name, server);
|
||||
if (event != null) return event;
|
||||
return new Event(name, server);
|
||||
}
|
||||
|
||||
public static void removeAllHostEvents(CarpetScriptHost host)
|
||||
{
|
||||
byName.values().forEach((e) -> e.handler.removeAllCalls(host));
|
||||
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.removeAllCalls(host));
|
||||
}
|
||||
|
||||
public static void clearAllBuiltinEvents()
|
||||
{
|
||||
byName.values().forEach(e -> e.handler.clearEverything());
|
||||
}
|
||||
|
||||
// custom event constructor
|
||||
private Event(String name, CarpetScriptServer server)
|
||||
{
|
||||
this.name = name;
|
||||
this.handler = new CallbackList(1);
|
||||
this.globalOnly = false;
|
||||
this.isPublic = true;
|
||||
server.events.customEvents.put(name, this);
|
||||
}
|
||||
|
||||
//handle_event('event', function...)
|
||||
//signal_event('event', player or null, args.... ) -> number of apps notified
|
||||
|
||||
public boolean isNeeded()
|
||||
{
|
||||
return handler.callList.size() > 0;
|
||||
@@ -748,8 +833,7 @@ public class CarpetEventServer
|
||||
public CarpetEventServer(MinecraftServer server)
|
||||
{
|
||||
this.server = server;
|
||||
for (Event e: Event.byName.values())
|
||||
e.handler.callList.clear();
|
||||
Event.clearAllBuiltinEvents();
|
||||
}
|
||||
|
||||
public void tick()
|
||||
@@ -777,20 +861,21 @@ public class CarpetEventServer
|
||||
scheduledCalls.add(new ScheduledCall(context, function, args, due));
|
||||
}
|
||||
|
||||
public boolean addEvent(ServerCommandSource source, String event, String host, String funName)
|
||||
public boolean addEventFromCommand(ServerCommandSource source, String event, String host, String funName)
|
||||
{
|
||||
if (!Event.byName.containsKey(event))
|
||||
Event ev = Event.getEvent(event, CarpetServer.scriptServer);
|
||||
if (ev == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Event ev = Event.byName.get(event);
|
||||
boolean added = ev.handler.addEventCall(source, host, funName, h -> canAddEvent(ev, h));
|
||||
if (added) Messenger.m(source, "gi Added " + funName + " to " + event);
|
||||
return added;
|
||||
}
|
||||
|
||||
public void addEventDirectly(String event, ScriptHost host, FunctionValue function, List<Value> args)
|
||||
public void addBuiltInEvent(String event, ScriptHost host, FunctionValue function, List<Value> args)
|
||||
{
|
||||
// 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");
|
||||
@@ -798,51 +883,61 @@ public class CarpetEventServer
|
||||
if (!success) throw new InternalExpressionException("Global event "+event+" requires "+ev.handler.reqArgs+", not "+(function.getNumParams()-((args==null)?0:args.size())));
|
||||
}
|
||||
|
||||
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");
|
||||
return ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
|
||||
}
|
||||
|
||||
public int signalEvent(String event, CarpetScriptHost host, ServerPlayerEntity player, List<Value> callArgs)
|
||||
{
|
||||
Event ev = Event.getEvent(event, host.getScriptServer());
|
||||
if (ev == null) return -1;
|
||||
return ev.handler.callCustom(player, callArgs);
|
||||
}
|
||||
|
||||
private boolean canAddEvent(Event event, ScriptHost host)
|
||||
{
|
||||
return !(event.globalOnly && (host.perUser || host.parent != null));
|
||||
}
|
||||
|
||||
|
||||
public boolean removeEvent(ServerCommandSource source, String event, String funName)
|
||||
public boolean removeEventFromCommand(ServerCommandSource source, String event, String funName)
|
||||
{
|
||||
|
||||
if (!Event.byName.containsKey(event))
|
||||
Event ev = Event.getEvent(event, CarpetServer.scriptServer);
|
||||
if (ev == null)
|
||||
{
|
||||
Messenger.m(source, "r Unknown event: " + event);
|
||||
return false;
|
||||
}
|
||||
Pair<String,String> call = decodeCallback(funName);
|
||||
Event.byName.get(event).handler.removeEventCall(call.getLeft(), call.getRight());
|
||||
ev.handler.removeEventCall(call.getLeft(), call.getRight());
|
||||
// could verified if actually removed
|
||||
Messenger.m(source, "gi Removed event: " + funName + " from "+event);
|
||||
return true;
|
||||
}
|
||||
public boolean removeEventDirectly(String event, ScriptHost host)
|
||||
public boolean removeBuiltInEvent(String event, CarpetScriptHost host)
|
||||
{
|
||||
|
||||
if (!Event.byName.containsKey(event))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Event.byName.get(event).handler.removeAllCalls(host.getName());
|
||||
Event ev = Event.getEvent(event, host.getScriptServer());
|
||||
if (ev == null) return false;
|
||||
ev.handler.removeAllCalls(host);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeEventDirectly(String event, ScriptHost host, String funName)
|
||||
public void removeBuiltInEvent(String event, CarpetScriptHost host, String funName)
|
||||
{
|
||||
Event.byName.get(event).handler.removeEventCall(host.getName(), funName);
|
||||
Event ev = Event.getEvent(event, host.getScriptServer());
|
||||
if (ev != null) ev.handler.removeEventCall(host.getName(), funName);
|
||||
}
|
||||
|
||||
public void removeAllHostEvents(String hostName)
|
||||
public void removeAllHostEvents(CarpetScriptHost host)
|
||||
{
|
||||
Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName));
|
||||
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(hostName));
|
||||
}
|
||||
|
||||
public void clearAll()
|
||||
{
|
||||
Event.byName.values().forEach(e -> e.handler.clearEverything());
|
||||
// remove event handlers
|
||||
Event.removeAllHostEvents(host);
|
||||
// remove scheduled calls
|
||||
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
|
||||
}
|
||||
|
||||
private Pair<String,String> decodeCallback(String funName)
|
||||
|
||||
@@ -117,7 +117,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
// 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, null);
|
||||
scriptServer.events.addBuiltInEvent(event, this, function, null);
|
||||
}
|
||||
else if (funName.equals("__config"))
|
||||
{
|
||||
@@ -188,8 +188,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
// 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.removeEventDirectly(event, this, funName);
|
||||
scriptServer.events.removeBuiltInEvent(event, this, funName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +211,22 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return host;
|
||||
}
|
||||
|
||||
public CarpetScriptHost retrieveForExecution(ServerPlayerEntity target)
|
||||
{
|
||||
// allowing only run
|
||||
if (perUser == (target == null)) return null;
|
||||
|
||||
if (!perUser)
|
||||
{
|
||||
if (errorSnooper == null) setChatErrorSnooper(this.scriptServer.server.getCommandSource());
|
||||
return this;
|
||||
}
|
||||
// user based
|
||||
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(target.getName().getString());
|
||||
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(target.getCommandSource());
|
||||
return userHost;
|
||||
}
|
||||
|
||||
public Value handleCommandLegacy(ServerCommandSource source, String call, List<Integer> coords, String arg)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -372,7 +373,7 @@ public class CarpetScriptServer
|
||||
return false;
|
||||
}
|
||||
// stop all events associated with name
|
||||
events.removeAllHostEvents(name);
|
||||
events.removeAllHostEvents(modules.get(name));
|
||||
modules.get(name).onClose();
|
||||
modules.remove(name);
|
||||
if (!isRuleApp) unloadableModules.remove(name);
|
||||
@@ -402,6 +403,35 @@ public class CarpetScriptServer
|
||||
return true;
|
||||
}
|
||||
|
||||
public Boolean runForPlayer(ServerPlayerEntity recipient, String hostname, FunctionValue udf, List<Value> argv)
|
||||
{
|
||||
CarpetScriptHost host;
|
||||
ServerCommandSource source;
|
||||
|
||||
if (hostname == null)
|
||||
{
|
||||
if (recipient != null) return null;
|
||||
host = globalHost;
|
||||
source = server.getCommandSource();
|
||||
}
|
||||
else
|
||||
{
|
||||
host = modules.get(hostname).retrieveForExecution(recipient);
|
||||
if (host == null) // not applicable
|
||||
return null;
|
||||
source = (recipient == null)? server.getCommandSource() : recipient.getCommandSource();
|
||||
}
|
||||
try
|
||||
{
|
||||
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
|
||||
}
|
||||
catch (NullPointerException | InvalidCallbackException npe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
events.tick();
|
||||
@@ -424,7 +454,7 @@ public class CarpetScriptServer
|
||||
Map<String, Boolean> apps = new HashMap<>();
|
||||
modules.forEach((s, h) -> apps.put(s, h.perUser));
|
||||
apps.keySet().forEach(s -> removeScriptHost(server.getCommandSource(), s, false, false));
|
||||
events.clearAll();
|
||||
CarpetEventServer.Event.clearAllBuiltinEvents();
|
||||
init();
|
||||
apps.forEach((s, pp) -> addScriptHost(server.getCommandSource(), s, pp, false, false));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import carpet.fakes.StatTypeInterface;
|
||||
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
||||
import carpet.helpers.FeatureGenerator;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.CarpetEventServer;
|
||||
import carpet.script.CarpetScriptHost;
|
||||
import carpet.script.CarpetScriptServer;
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
@@ -20,6 +22,7 @@ import carpet.script.utils.ShapeDispatcher;
|
||||
import carpet.script.utils.WorldTools;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.FormattedTextValue;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NBTSerializableValue;
|
||||
import carpet.script.value.NullValue;
|
||||
@@ -639,12 +642,12 @@ public class Auxiliary {
|
||||
throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name");
|
||||
long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
|
||||
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, false, true);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, true);
|
||||
|
||||
CarpetServer.scriptServer.events.scheduleCall(
|
||||
(CarpetContext) c,
|
||||
functionArgument.function,
|
||||
functionArgument.resolveArgs(c, Context.NONE),
|
||||
functionArgument.resolveArgs(c, t),
|
||||
delay
|
||||
);
|
||||
return (c_, t_) -> Value.TRUE;
|
||||
@@ -843,6 +846,50 @@ public class Auxiliary {
|
||||
return LazyValue.NULL;
|
||||
return (_c, _t) -> new NumericValue(player.getStatHandler().getStat(stat));
|
||||
});
|
||||
|
||||
//handle_event('event', function...)
|
||||
expression.addLazyFunction("handle_event", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() < 2)
|
||||
throw new InternalExpressionException("'handle_event' requires at least two arguments, event name, and a callback");
|
||||
String event = lv.get(0).evalValue(c).getString();
|
||||
FunctionArgument callback = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
|
||||
CarpetScriptHost host = ((CarpetScriptHost)c.host);
|
||||
Value success;
|
||||
if (callback.function == null)
|
||||
{
|
||||
success = host.getScriptServer().events.removeBuiltInEvent(event, host)?Value.TRUE:Value.FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = host.getScriptServer().events.handleCustomEvent(event, host, callback.function, FunctionValue.resolveArgs(callback.args, c, t) )?Value.TRUE:Value.FALSE;
|
||||
}
|
||||
return (cc, tt) -> success;
|
||||
});
|
||||
//signal_event('event', player or null, args.... ) -> number of apps notified
|
||||
expression.addLazyFunction("signal_event", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'signal' requires at least one argument");
|
||||
CarpetScriptServer server = ((CarpetScriptHost)c.host).getScriptServer();
|
||||
String eventName = lv.get(0).evalValue(c).getString();
|
||||
// no such event yet
|
||||
if (CarpetEventServer.Event.getEvent(eventName, server) == null) return LazyValue.NULL;
|
||||
ServerPlayerEntity player = null;
|
||||
List<Value> args = Collections.emptyList();
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
player = EntityValue.getPlayerByValue(server.server, lv.get(1).evalValue(c));
|
||||
if (lv.size() > 2)
|
||||
{
|
||||
args = FunctionValue.resolveArgs(lv.subList(2, lv.size()), c, t);
|
||||
}
|
||||
}
|
||||
int counts = ((CarpetScriptHost)c.host).getScriptServer().events.signalEvent(eventName, (CarpetScriptHost) c.host, player, args);
|
||||
if (counts < 0) return LazyValue.NULL;
|
||||
Value ret = new NumericValue(counts);
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
}
|
||||
|
||||
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
|
||||
|
||||
@@ -276,15 +276,15 @@ public class Entities {
|
||||
: Collections.singletonList(entityValue.getString());
|
||||
Set<EntityType<? extends Entity>> types = new HashSet<>();
|
||||
descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s).typeList));
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, false, true, false);
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
|
||||
CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events;
|
||||
if (funArg.function == null)
|
||||
{
|
||||
types.forEach(et -> events.removeEventDirectly(CarpetEventServer.Event.getLoadEvent(et), c.host));
|
||||
types.forEach(et -> events.removeBuiltInEvent(CarpetEventServer.Event.getEntityLoadEventName(et), (CarpetScriptHost) c.host));
|
||||
}
|
||||
else
|
||||
{
|
||||
types.forEach(et -> events.addEventDirectly(CarpetEventServer.Event.getLoadEvent(et), c.host, funArg.function, FunctionValue.resolveArgs(funArg.args, c, t)));
|
||||
types.forEach(et -> events.addBuiltInEvent(CarpetEventServer.Event.getEntityLoadEventName(et), c.host, funArg.function, FunctionValue.resolveArgs(funArg.args, c, t)));
|
||||
}
|
||||
Value ret = new NumericValue(types.size());
|
||||
return (cc, tt) -> ret;
|
||||
@@ -300,7 +300,7 @@ public class Entities {
|
||||
throw new InternalExpressionException("First argument to entity_event should be an entity");
|
||||
String what = lv.get(1).evalValue(c).getString();
|
||||
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, false, true, false);
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, true, false);
|
||||
|
||||
((EntityValue) v).setEvent((CarpetContext)c, what, funArg.function, FunctionValue.resolveArgs(funArg.args, c, t));
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ public class FunctionArgument extends Argument
|
||||
* @param module module
|
||||
* @param params list of lazy params
|
||||
* @param offset offset where to start looking for functional argument
|
||||
* @param prematureEvaluation evaluates eagerly arguments in current context before returning
|
||||
* @param allowNone none indicates no function present, otherwise it will croak
|
||||
* @param checkArgs whether the caller expects trailing parameters to fully resolve function argument list
|
||||
* if not - argument count check will not be performed and its up to the caller to verify
|
||||
@@ -40,7 +39,6 @@ public class FunctionArgument extends Argument
|
||||
Module module,
|
||||
List<LazyValue> params,
|
||||
int offset,
|
||||
boolean prematureEvaluation,
|
||||
boolean allowNone,
|
||||
boolean checkArgs)
|
||||
{
|
||||
@@ -66,18 +64,7 @@ public class FunctionArgument extends Argument
|
||||
throw new InternalExpressionException("Function " + fun.getPrettyString() + " requires " + fun.getArguments().size() + " arguments");
|
||||
}
|
||||
List<LazyValue> lvargs = new ArrayList<>();
|
||||
if (prematureEvaluation)
|
||||
{
|
||||
for (int i = offset+1, mx = params.size(); i < mx; i++)
|
||||
{
|
||||
Value arg = params.get(i).evalValue(c);
|
||||
lvargs.add((cc, tt) -> arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = offset+1, mx = params.size(); i < mx; i++) lvargs.add(params.get(i));
|
||||
}
|
||||
for (int i = offset+1, mx = params.size(); i < mx; i++) lvargs.add(params.get(i));
|
||||
return new FunctionArgument(fun, offset+1+argsize, lvargs);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Functions {
|
||||
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
|
||||
if (t != Context.SIGNATURE) // just call the function
|
||||
{
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, false, true);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
|
||||
FunctionValue fun = functionArgument.function;
|
||||
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c);
|
||||
return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
|
||||
|
||||
@@ -4,6 +4,7 @@ import carpet.script.Expression;
|
||||
import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.ThreadValue;
|
||||
import carpet.script.value.Value;
|
||||
@@ -16,8 +17,8 @@ public class Threading
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true, false, true);
|
||||
ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.args);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
|
||||
ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, FunctionValue.resolveArgs(functionArgument.args, c, t));
|
||||
Thread.yield();
|
||||
return (cc, tt) -> thread;
|
||||
});
|
||||
@@ -27,8 +28,8 @@ public class Threading
|
||||
if (lv.size() < 2)
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
Value queue = lv.get(0).evalValue(c);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, false, true);
|
||||
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, true);
|
||||
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, FunctionValue.resolveArgs(functionArgument.args, c, t));
|
||||
Thread.yield();
|
||||
return (cc, tt) -> thread;
|
||||
});
|
||||
|
||||
@@ -131,6 +131,10 @@ public class EntityValue extends Value
|
||||
player = (ServerPlayerEntity) e;
|
||||
}
|
||||
}
|
||||
else if (value.isNull())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
String playerName = value.getString();
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ThreadValue extends Value
|
||||
private final long id;
|
||||
private static long sequence = 0L;
|
||||
|
||||
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List<LazyValue> args)
|
||||
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List<Value> args)
|
||||
{
|
||||
id = sequence++;
|
||||
ExecutorService executor = ctx.host.getExecutor(pool);
|
||||
@@ -36,7 +36,7 @@ public class ThreadValue extends Value
|
||||
{
|
||||
try
|
||||
{
|
||||
return function.lazyEval(ctx, Context.NONE, expr, token, args).evalValue(ctx);
|
||||
return function.lazyEval(ctx, Context.NONE, expr, token, FunctionValue.lazify(args)).evalValue(ctx);
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user