Merge branch 'master' into 1.17
This commit is contained in:
commit
0bacbcc2f5
18 files changed
+669
-349
No files matched your search
+6
-1
@@ -6,6 +6,10 @@ out/
|
|||||||
classes/
|
classes/
|
||||||
publish/
|
publish/
|
||||||
|
|
||||||
|
# git
|
||||||
|
|
||||||
|
.shelf/
|
||||||
|
|
||||||
# idea
|
# idea
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
@@ -27,4 +31,5 @@ bin/
|
|||||||
|
|
||||||
# fabric
|
# fabric
|
||||||
|
|
||||||
run/
|
run/
|
||||||
|
logs/
|
||||||
+107
-101
@@ -1733,10 +1733,10 @@ the app state and all players interactions run in the same context, sharing defi
|
|||||||
explicitly keyed with players, player names, uuids, etc.
|
explicitly keyed with players, player names, uuids, etc.
|
||||||
Even for `'player'` scoped apps, you can access specific player app with with commandblocks using
|
Even for `'player'` scoped apps, you can access specific player app with with commandblocks using
|
||||||
`/execute as <player> run script in <app> run ...`.
|
`/execute as <player> run script in <app> run ...`.
|
||||||
To access global/server state for a player app, you need to disown the command from any player,
|
To access global/server state for a player app, which you shouldn't do, you need to disown the command from any player,
|
||||||
so either use a command block, or any
|
so either use a command block, or any
|
||||||
arbitrary entity: `/execute as @e[type=bat,limit=1] run script in <app> globals` for instance, however
|
arbitrary entity: `/execute as @e[type=bat,limit=1] run script in <app> globals` for instance, however
|
||||||
running anything in the global scope for a `'player'` scoped app is not recommended.
|
running anything in the global scope for a `'player'` scoped app is not intended.
|
||||||
* `'stay_loaded'`: defaults to `false`. If true, and `/carpet scriptsAutoload` is turned on, the following apps will
|
* `'stay_loaded'`: defaults to `false`. If true, and `/carpet scriptsAutoload` is turned on, the following apps will
|
||||||
stay loaded after startup. Otherwise, after reading the app the first time, and fetching the config, server will drop them down.
|
stay loaded after startup. Otherwise, after reading the app the first time, and fetching the config, server will drop them down.
|
||||||
This is to allow to store multiple apps on the server/world and selectively decide which one should be running at
|
This is to allow to store multiple apps on the server/world and selectively decide which one should be running at
|
||||||
@@ -3816,10 +3816,8 @@ with `__on_<event>` and has the required arguments, will be bound automatically
|
|||||||
of such function would result in unbinding the app from this event.
|
of such function would result in unbinding the app from this event.
|
||||||
|
|
||||||
In case of `player` scoped apps,
|
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
|
all player action events will be directed to the appropriate player hosts. Global events, like `'tick'`, that don't have a specific
|
||||||
in `'player'` scoped app.
|
player target will be executed multiple times, once for each player, and once in `'global'` scoped apps.
|
||||||
|
|
||||||
`'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
|
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
|
for the programmer to handle them right away (as it happens, potentially affect the course of action by changing the
|
||||||
@@ -3838,8 +3836,8 @@ if it accepts required number of parameters.
|
|||||||
|
|
||||||
## Built-in global events
|
## Built-in global events
|
||||||
|
|
||||||
Handling global events is only allowed in apps with `'global'` scope. With the default scope (`'player'`) you
|
Global events will be handled once per app that is with `'global'` scope. With `player` scoped apps, each player instance
|
||||||
simply wouldn't know which player to hook up this event to.
|
is responsible independently from handling their events, so a global event may be executed multiple times for each player.
|
||||||
|
|
||||||
### `__on_tick()`
|
### `__on_tick()`
|
||||||
Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()`
|
Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()`
|
||||||
@@ -4012,8 +4010,11 @@ is handled before scoreboard values for these statistics are changed.
|
|||||||
|
|
||||||
App programmers can define and trigger their own custom events. Unlike built-in events, all custom events pass a single value
|
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
|
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
|
either targetting all apps instances for all players, including global apps, if no target player has been identified,
|
||||||
is specified.
|
or only player scoped apps, if the target player
|
||||||
|
is specified, running once for that player app. You cannot target global apps with player-targeted signals. Built-in events
|
||||||
|
do target global apps, since their first argument is clearly defined and passed. That may change in the future in case there is
|
||||||
|
a compelling argument to be able to target global apps with player scopes.
|
||||||
|
|
||||||
Programmers can also handle built-in events the same way as custom events, as well as triggering built-in events, which I have
|
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:
|
have no idea why you would need that. The following snippets have the same effect:
|
||||||
@@ -4037,11 +4038,11 @@ handle_event('player_breaks_block', null);
|
|||||||
And `signal_event` can be used as a trigger, called twice for player based built-in events
|
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', 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('player_breaks_block', null , player, block); // to target all global scoped apps and all player instances
|
||||||
```
|
```
|
||||||
or (for global only events)
|
or (for global events)
|
||||||
```
|
```
|
||||||
signal_event('tick') // trigger only global scoped apps with no extra arguments
|
signal_event('tick') // trigger all apps with a tick event
|
||||||
```
|
```
|
||||||
|
|
||||||
### `handle_event(event, callback ...)`
|
### `handle_event(event, callback ...)`
|
||||||
@@ -4055,6 +4056,8 @@ If extra arguments are provided, they will be appended to the argument list of t
|
|||||||
Returns `true` if subscription to the event was successful, or `false` if it failed (for instance wrong scope for built-in event,
|
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).
|
or incorect number of parameters for the event).
|
||||||
|
|
||||||
|
If a callback is specified as `null`, the given app (or player app instance )stops handling that event.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
foo(a) -> print(a);
|
foo(a) -> print(a);
|
||||||
handle_event('boohoo', 'foo');
|
handle_event('boohoo', 'foo');
|
||||||
@@ -4069,114 +4072,58 @@ handle_event('tick', null) // nah, ima good, kthxbai
|
|||||||
|
|
||||||
### `signal_event(event, target_player?, ... args?)`
|
### `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
|
Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list
|
||||||
was not matching the callee expected arguments, returns `null`,
|
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
|
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.
|
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.
|
If the `target_player` is omitted or `null`, it will target `global` scoped apps and all instances of `player` scoped apps.
|
||||||
Therefore each custom event trigger can trigger each app once, but calls to '`signal_event`' can be repeated to cover
|
Note that all built-in player events have a player as a first argument, so to trigger these events, you need to
|
||||||
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.
|
provide them twice - once to specify the target player scope and second - to provide as an argument to the handler function.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
signal_event('player_breaks_block', player, player, block); // to target all player scoped apps
|
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('player_breaks_block', null , player, block); // to target all global scoped apps and all player instances
|
||||||
signal_event('tick') // triggering tick event in all loaded apps
|
signal_event('tick') // trigger all apps with a tick event
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Custom events example
|
## Custom events example
|
||||||
|
|
||||||
The following example contains 4 apps, 2 of them defining a new event, `"player_farts"` triggered after 40 ticks of continuous
|
The following example shows how you can communicate between different instances of the same player scoped app. It important to note
|
||||||
squatting. Note that this could call the recipient event handlers twice, since `signal_event` will be called twice, but
|
that signals can trigger other apps as well, assuming the name of the event matches. In this case the request name is called
|
||||||
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
|
`tp_request` and is triggered with a command.
|
||||||
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
|
// tpa.sc
|
||||||
// detecting event in a tick loop
|
global_requester = null;
|
||||||
__config() -> {'scope' -> 'global'};
|
__config() -> {
|
||||||
|
'commands' -> {
|
||||||
global_sneaks = {};
|
'<player>' -> _(to) -> signal_event('tp_request', to, player()),
|
||||||
__on_tick() -> for(player('all'),
|
'accept' -> _() -> if(global_requester,
|
||||||
if (_~'sneaking',
|
run('tp '+global_requester~'command_name');
|
||||||
sneak_time = if (has(global_sneaks:_), tick_time()-global_sneaks:_ , global_sneaks:_=tick_time(); 0);
|
global_requester = null
|
||||||
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:_)
|
'arguments' -> {
|
||||||
)
|
'player' -> {'type' -> 'players', 'single' -> true}
|
||||||
)
|
}
|
||||||
|
};
|
||||||
|
handle_event('tp_request', _(req) -> (
|
||||||
|
global_requester = req;
|
||||||
|
print(player(), format(
|
||||||
|
'w '+req+' requested to teleport to you. Click ',
|
||||||
|
'yb here', '^yb here', '!/tpa accept',
|
||||||
|
'w to accept it.'
|
||||||
|
));
|
||||||
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
// 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
|
## `/script event` command
|
||||||
|
|
||||||
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
|
used to display current events and bounded functions. use `add_to` to register a 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
|
unbind a specific function from an event. Function to be bounded to an event needs to have the same number of
|
||||||
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
|
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
|
||||||
that have functions listed above will be automatically bounded and unbounded when script is unloaded.
|
that handle specific built-in events will be automatically bounded, and unbounded when script is unloaded.
|
||||||
# Scoreboard
|
# Scoreboard
|
||||||
|
|
||||||
### `scoreboard()`, `scoreboard(objective)`, `scoreboard(objective, key)`, `scoreboard(objective, key, value)`
|
### `scoreboard()`, `scoreboard(objective)`, `scoreboard(objective, key)`, `scoreboard(objective, key, value)`
|
||||||
@@ -4283,6 +4230,65 @@ team_property('admin','seeFriendlyInvisibles',true) Make all players in 'a
|
|||||||
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
|
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `bossbar()`, `bossbar(id)`, `bossbar(id,property,value?)`
|
||||||
|
|
||||||
|
Manage bossbars just like with the `/bossbar` command.
|
||||||
|
|
||||||
|
Without any arguments, returns a list of all bossbars.
|
||||||
|
|
||||||
|
When an id is specified, creates a bossbar with that `id` and returns the id of the created bossbar.
|
||||||
|
Bossbar ids need a namespace and a name. If no namespace is specified, it will automatically use `minecraft:`.
|
||||||
|
In that case you should keep track of the bossbar with the id that `bossbar(id)` returns, because a namespace may be added automatically.
|
||||||
|
If the id was invalid (for example by having more than one colon), returns `null`.
|
||||||
|
If the bossbar already exists, returns `false`.
|
||||||
|
|
||||||
|
`bossbar('timer') => 'minecraft:timer'` (Adds the namespace `minecraft:` because none is specified)
|
||||||
|
|
||||||
|
`bossbar('scarpet:test') => 'scarpet:test'` In this case there is already a namespace specified
|
||||||
|
|
||||||
|
`bossbar('foo:bar:baz') => null` Invalid identifier
|
||||||
|
|
||||||
|
`bossbar(id,property)` is used to query the `property` of a bossbar.
|
||||||
|
|
||||||
|
`bossbar(id,property,value)` can modify the `property` of the bossbar to a specified `value`.
|
||||||
|
|
||||||
|
Available properties are:
|
||||||
|
|
||||||
|
* color: can be `'pink'`, `'blue'`, `'red'`, `'green'`, `'yellow'`, `'purple'` or `'white'`
|
||||||
|
|
||||||
|
* style: can be `'progress'`, `'notched_6'`, `'notched_10'`, `'notched_12'` or `'notched_20'`
|
||||||
|
|
||||||
|
* value: value of the bossbar progress
|
||||||
|
|
||||||
|
* max: maximum value of the bossbar progress, by default 100
|
||||||
|
|
||||||
|
* name: Text to display above the bossbar, supports formatted text
|
||||||
|
|
||||||
|
* visible: whether the bossbar is visible or not
|
||||||
|
|
||||||
|
* players: List of players that can see the bossbar
|
||||||
|
|
||||||
|
* add_player: add a player to the players that can see this bossbar, this can only be used for modifying (`value` must be present)
|
||||||
|
|
||||||
|
* remove: remove this bossbar, no `value` required
|
||||||
|
|
||||||
|
```
|
||||||
|
bossbar('script:test','style','notched_12')
|
||||||
|
bossbar('script:test','value',74)
|
||||||
|
bossbar('script:test','name',format('rb Test')) -> Change text
|
||||||
|
bossbar('script:test','visible',false) -> removes visibility, but keeps players
|
||||||
|
bossbar('script:test','players',player('all')) -> Visible for all players
|
||||||
|
bossbar('script:test','players',player('Steve')) -> Visible for Steve only
|
||||||
|
bossbar('script:test','players',null) -> Invalid player, removing all players
|
||||||
|
bossbar('script:test','add_player',player('Alex')) -> Add Alex to the list of players that can see the bossbar
|
||||||
|
bossbar('script:test','remove') -> remove bossbar 'script:test'
|
||||||
|
for(bossbar(),bossbar(_,'remove')) -> remove all bossbars
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Auxiliary aspects
|
# Auxiliary aspects
|
||||||
|
|
||||||
Collection of other methods that control smaller, yet still important aspects of the game
|
Collection of other methods that control smaller, yet still important aspects of the game
|
||||||
|
|||||||
+46
-99
@@ -9,10 +9,8 @@ with `__on_<event>` and has the required arguments, will be bound automatically
|
|||||||
of such function would result in unbinding the app from this event.
|
of such function would result in unbinding the app from this event.
|
||||||
|
|
||||||
In case of `player` scoped apps,
|
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
|
all player action events will be directed to the appropriate player hosts. Global events, like `'tick'`, that don't have a specific
|
||||||
in `'player'` scoped app.
|
player target will be executed multiple times, once for each player, and once in `'global'` scoped apps.
|
||||||
|
|
||||||
`'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
|
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
|
for the programmer to handle them right away (as it happens, potentially affect the course of action by changing the
|
||||||
@@ -31,8 +29,8 @@ if it accepts required number of parameters.
|
|||||||
|
|
||||||
## Built-in global events
|
## Built-in global events
|
||||||
|
|
||||||
Handling global events is only allowed in apps with `'global'` scope. With the default scope (`'player'`) you
|
Global events will be handled once per app that is with `'global'` scope. With `player` scoped apps, each player instance
|
||||||
simply wouldn't know which player to hook up this event to.
|
is responsible independently from handling their events, so a global event may be executed multiple times for each player.
|
||||||
|
|
||||||
### `__on_tick()`
|
### `__on_tick()`
|
||||||
Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()`
|
Event triggers at the beginning of each tick, located in the overworld. You can use `in_dimension()`
|
||||||
@@ -205,8 +203,11 @@ is handled before scoreboard values for these statistics are changed.
|
|||||||
|
|
||||||
App programmers can define and trigger their own custom events. Unlike built-in events, all custom events pass a single value
|
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
|
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
|
either targetting all apps instances for all players, including global apps, if no target player has been identified,
|
||||||
is specified.
|
or only player scoped apps, if the target player
|
||||||
|
is specified, running once for that player app. You cannot target global apps with player-targeted signals. Built-in events
|
||||||
|
do target global apps, since their first argument is clearly defined and passed. That may change in the future in case there is
|
||||||
|
a compelling argument to be able to target global apps with player scopes.
|
||||||
|
|
||||||
Programmers can also handle built-in events the same way as custom events, as well as triggering built-in events, which I have
|
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:
|
have no idea why you would need that. The following snippets have the same effect:
|
||||||
@@ -230,11 +231,11 @@ handle_event('player_breaks_block', null);
|
|||||||
And `signal_event` can be used as a trigger, called twice for player based built-in events
|
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', 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('player_breaks_block', null , player, block); // to target all global scoped apps and all player instances
|
||||||
```
|
```
|
||||||
or (for global only events)
|
or (for global events)
|
||||||
```
|
```
|
||||||
signal_event('tick') // trigger only global scoped apps with no extra arguments
|
signal_event('tick') // trigger all apps with a tick event
|
||||||
```
|
```
|
||||||
|
|
||||||
### `handle_event(event, callback ...)`
|
### `handle_event(event, callback ...)`
|
||||||
@@ -248,6 +249,8 @@ If extra arguments are provided, they will be appended to the argument list of t
|
|||||||
Returns `true` if subscription to the event was successful, or `false` if it failed (for instance wrong scope for built-in event,
|
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).
|
or incorect number of parameters for the event).
|
||||||
|
|
||||||
|
If a callback is specified as `null`, the given app (or player app instance )stops handling that event.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
foo(a) -> print(a);
|
foo(a) -> print(a);
|
||||||
handle_event('boohoo', 'foo');
|
handle_event('boohoo', 'foo');
|
||||||
@@ -262,111 +265,55 @@ handle_event('tick', null) // nah, ima good, kthxbai
|
|||||||
|
|
||||||
### `signal_event(event, target_player?, ... args?)`
|
### `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
|
Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list
|
||||||
was not matching the callee expected arguments, returns `null`,
|
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
|
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.
|
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.
|
If the `target_player` is omitted or `null`, it will target `global` scoped apps and all instances of `player` scoped apps.
|
||||||
Therefore each custom event trigger can trigger each app once, but calls to '`signal_event`' can be repeated to cover
|
Note that all built-in player events have a player as a first argument, so to trigger these events, you need to
|
||||||
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.
|
provide them twice - once to specify the target player scope and second - to provide as an argument to the handler function.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
signal_event('player_breaks_block', player, player, block); // to target all player scoped apps
|
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('player_breaks_block', null , player, block); // to target all global scoped apps and all player instances
|
||||||
signal_event('tick') // triggering tick event in all loaded apps
|
signal_event('tick') // trigger all apps with a tick event
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Custom events example
|
## Custom events example
|
||||||
|
|
||||||
The following example contains 4 apps, 2 of them defining a new event, `"player_farts"` triggered after 40 ticks of continuous
|
The following example shows how you can communicate between different instances of the same player scoped app. It important to note
|
||||||
squatting. Note that this could call the recipient event handlers twice, since `signal_event` will be called twice, but
|
that signals can trigger other apps as well, assuming the name of the event matches. In this case the request name is called
|
||||||
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
|
`tp_request` and is triggered with a command.
|
||||||
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
|
// tpa.sc
|
||||||
// detecting event in a tick loop
|
global_requester = null;
|
||||||
__config() -> {'scope' -> 'global'};
|
__config() -> {
|
||||||
|
'commands' -> {
|
||||||
global_sneaks = {};
|
'<player>' -> _(to) -> signal_event('tp_request', to, player()),
|
||||||
__on_tick() -> for(player('all'),
|
'accept' -> _() -> if(global_requester,
|
||||||
if (_~'sneaking',
|
run('tp '+global_requester~'command_name');
|
||||||
sneak_time = if (has(global_sneaks:_), tick_time()-global_sneaks:_ , global_sneaks:_=tick_time(); 0);
|
global_requester = null
|
||||||
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:_)
|
'arguments' -> {
|
||||||
)
|
'player' -> {'type' -> 'players', 'single' -> true}
|
||||||
)
|
}
|
||||||
|
};
|
||||||
|
handle_event('tp_request', _(req) -> (
|
||||||
|
global_requester = req;
|
||||||
|
print(player(), format(
|
||||||
|
'w '+req+' requested to teleport to you. Click ',
|
||||||
|
'yb here', '^yb here', '!/tpa accept',
|
||||||
|
'w to accept it.'
|
||||||
|
));
|
||||||
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
// 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
|
## `/script event` command
|
||||||
|
|
||||||
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
|
used to display current events and bounded functions. use `add_to` to register a 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
|
unbind a specific function from an event. Function to be bounded to an event needs to have the same number of
|
||||||
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
|
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
|
||||||
that have functions listed above will be automatically bounded and unbounded when script is unloaded.
|
that handle specific built-in events will be automatically bounded, and unbounded when script is unloaded.
|
||||||
@@ -33,10 +33,10 @@ the app state and all players interactions run in the same context, sharing defi
|
|||||||
explicitly keyed with players, player names, uuids, etc.
|
explicitly keyed with players, player names, uuids, etc.
|
||||||
Even for `'player'` scoped apps, you can access specific player app with with commandblocks using
|
Even for `'player'` scoped apps, you can access specific player app with with commandblocks using
|
||||||
`/execute as <player> run script in <app> run ...`.
|
`/execute as <player> run script in <app> run ...`.
|
||||||
To access global/server state for a player app, you need to disown the command from any player,
|
To access global/server state for a player app, which you shouldn't do, you need to disown the command from any player,
|
||||||
so either use a command block, or any
|
so either use a command block, or any
|
||||||
arbitrary entity: `/execute as @e[type=bat,limit=1] run script in <app> globals` for instance, however
|
arbitrary entity: `/execute as @e[type=bat,limit=1] run script in <app> globals` for instance, however
|
||||||
running anything in the global scope for a `'player'` scoped app is not recommended.
|
running anything in the global scope for a `'player'` scoped app is not intended.
|
||||||
* `'stay_loaded'`: defaults to `false`. If true, and `/carpet scriptsAutoload` is turned on, the following apps will
|
* `'stay_loaded'`: defaults to `false`. If true, and `/carpet scriptsAutoload` is turned on, the following apps will
|
||||||
stay loaded after startup. Otherwise, after reading the app the first time, and fetching the config, server will drop them down.
|
stay loaded after startup. Otherwise, after reading the app the first time, and fetching the config, server will drop them down.
|
||||||
This is to allow to store multiple apps on the server/world and selectively decide which one should be running at
|
This is to allow to store multiple apps on the server/world and selectively decide which one should be running at
|
||||||
|
|||||||
@@ -104,3 +104,62 @@ team_property('admin','seeFriendlyInvisibles',true) Make all players in 'a
|
|||||||
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
|
team_property('admin','deathMessageVisibility','hideForOtherTeams') Make all players in 'admin' see other admins even when invisible
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `bossbar()`, `bossbar(id)`, `bossbar(id,property,value?)`
|
||||||
|
|
||||||
|
Manage bossbars just like with the `/bossbar` command.
|
||||||
|
|
||||||
|
Without any arguments, returns a list of all bossbars.
|
||||||
|
|
||||||
|
When an id is specified, creates a bossbar with that `id` and returns the id of the created bossbar.
|
||||||
|
Bossbar ids need a namespace and a name. If no namespace is specified, it will automatically use `minecraft:`.
|
||||||
|
In that case you should keep track of the bossbar with the id that `bossbar(id)` returns, because a namespace may be added automatically.
|
||||||
|
If the id was invalid (for example by having more than one colon), returns `null`.
|
||||||
|
If the bossbar already exists, returns `false`.
|
||||||
|
|
||||||
|
`bossbar('timer') => 'minecraft:timer'` (Adds the namespace `minecraft:` because none is specified)
|
||||||
|
|
||||||
|
`bossbar('scarpet:test') => 'scarpet:test'` In this case there is already a namespace specified
|
||||||
|
|
||||||
|
`bossbar('foo:bar:baz') => null` Invalid identifier
|
||||||
|
|
||||||
|
`bossbar(id,property)` is used to query the `property` of a bossbar.
|
||||||
|
|
||||||
|
`bossbar(id,property,value)` can modify the `property` of the bossbar to a specified `value`.
|
||||||
|
|
||||||
|
Available properties are:
|
||||||
|
|
||||||
|
* color: can be `'pink'`, `'blue'`, `'red'`, `'green'`, `'yellow'`, `'purple'` or `'white'`
|
||||||
|
|
||||||
|
* style: can be `'progress'`, `'notched_6'`, `'notched_10'`, `'notched_12'` or `'notched_20'`
|
||||||
|
|
||||||
|
* value: value of the bossbar progress
|
||||||
|
|
||||||
|
* max: maximum value of the bossbar progress, by default 100
|
||||||
|
|
||||||
|
* name: Text to display above the bossbar, supports formatted text
|
||||||
|
|
||||||
|
* visible: whether the bossbar is visible or not
|
||||||
|
|
||||||
|
* players: List of players that can see the bossbar
|
||||||
|
|
||||||
|
* add_player: add a player to the players that can see this bossbar, this can only be used for modifying (`value` must be present)
|
||||||
|
|
||||||
|
* remove: remove this bossbar, no `value` required
|
||||||
|
|
||||||
|
```
|
||||||
|
bossbar('script:test','style','notched_12')
|
||||||
|
bossbar('script:test','value',74)
|
||||||
|
bossbar('script:test','name',format('rb Test')) -> Change text
|
||||||
|
bossbar('script:test','visible',false) -> removes visibility, but keeps players
|
||||||
|
bossbar('script:test','players',player('all')) -> Visible for all players
|
||||||
|
bossbar('script:test','players',player('Steve')) -> Visible for Steve only
|
||||||
|
bossbar('script:test','players',null) -> Invalid player, removing all players
|
||||||
|
bossbar('script:test','add_player',player('Alex')) -> Add Alex to the list of players that can see the bossbar
|
||||||
|
bossbar('script:test','remove') -> remove bossbar 'script:test'
|
||||||
|
for(bossbar(),bossbar(_,'remove')) -> remove all bossbars
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ block_tick
|
|||||||
block_tags
|
block_tags
|
||||||
blocks_daylight
|
blocks_daylight
|
||||||
blocks_movement
|
blocks_movement
|
||||||
|
bossbar
|
||||||
brightness
|
brightness
|
||||||
chunk_tickets
|
chunk_tickets
|
||||||
crafting_remaining_item
|
crafting_remaining_item
|
||||||
@@ -92,10 +93,10 @@ scoreboard_remove
|
|||||||
scoreboard_display
|
scoreboard_display
|
||||||
tag_matches
|
tag_matches
|
||||||
team_add
|
team_add
|
||||||
team_list
|
|
||||||
team_remove
|
|
||||||
team_leave
|
team_leave
|
||||||
|
team_list
|
||||||
team_property
|
team_property
|
||||||
|
team_remove
|
||||||
read_file
|
read_file
|
||||||
see_sky
|
see_sky
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -64,6 +64,8 @@
|
|||||||
|
|
||||||
team_add team_list team_remove team_leave team_property
|
team_add team_list team_remove team_leave team_property
|
||||||
|
|
||||||
|
bossbar
|
||||||
|
|
||||||
player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types
|
player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types
|
||||||
|
|
||||||
stack_limit item_category recipe_data crafting_remaining_item
|
stack_limit item_category recipe_data crafting_remaining_item
|
||||||
|
|||||||
@@ -5,7 +5,19 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
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.network.ServerNetworkHandler;
|
||||||
import carpet.helpers.HopperCounter;
|
import carpet.helpers.HopperCounter;
|
||||||
import carpet.helpers.TickSpeed;
|
import carpet.helpers.TickSpeed;
|
||||||
@@ -143,6 +155,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
|
|||||||
{
|
{
|
||||||
ServerNetworkHandler.onPlayerJoin(player);
|
ServerNetworkHandler.onPlayerJoin(player);
|
||||||
LoggerRegistry.playerConnected(player);
|
LoggerRegistry.playerConnected(player);
|
||||||
|
scriptServer.onPlayerJoin(player);
|
||||||
extensions.forEach(e -> e.onPlayerLoggedIn(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.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.brigadier.suggestion.Suggestions;
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
@@ -84,7 +85,7 @@ public class ScriptCommand
|
|||||||
private static CompletableFuture<Suggestions> suggestCode(
|
private static CompletableFuture<Suggestions> suggestCode(
|
||||||
CommandContext<ServerCommandSource> context,
|
CommandContext<ServerCommandSource> context,
|
||||||
SuggestionsBuilder suggestionsBuilder
|
SuggestionsBuilder suggestionsBuilder
|
||||||
)
|
) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
CarpetScriptHost currentHost = getHost(context);
|
CarpetScriptHost currentHost = getHost(context);
|
||||||
String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||||
@@ -291,7 +292,7 @@ public class ScriptCommand
|
|||||||
)?1:0)))))).
|
)?1:0)))))).
|
||||||
then(literal("remove_from").
|
then(literal("remove_from").
|
||||||
then(argument("event", StringArgumentType.word()).
|
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()).
|
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)).
|
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(
|
executes( (cc) -> CarpetServer.scriptServer.events.removeEventFromCommand(
|
||||||
@@ -311,14 +312,14 @@ public class ScriptCommand
|
|||||||
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.modules.keySet(), bb)).
|
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))));
|
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;
|
CarpetScriptHost host;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String name = StringArgumentType.getString(context, "app").toLowerCase(Locale.ROOT);
|
String name = StringArgumentType.getString(context, "app").toLowerCase(Locale.ROOT);
|
||||||
CarpetScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost);
|
CarpetScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost);
|
||||||
host = parentHost.retrieveForExecution(context.getSource());
|
host = parentHost.retrieveOwnForExecution(context.getSource());
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ignored)
|
catch (IllegalArgumentException ignored)
|
||||||
{
|
{
|
||||||
@@ -327,7 +328,7 @@ public class ScriptCommand
|
|||||||
host.setChatErrorSnooper(context.getSource());
|
host.setChatErrorSnooper(context.getSource());
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c)
|
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
CarpetScriptHost host = getHost(c);
|
CarpetScriptHost host = getHost(c);
|
||||||
return host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList());
|
return host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList());
|
||||||
@@ -350,7 +351,7 @@ public class ScriptCommand
|
|||||||
}
|
}
|
||||||
return 1;
|
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);
|
CarpetScriptHost host = getHost(context);
|
||||||
ServerCommandSource source = context.getSource();
|
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
|
//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();
|
ServerCommandSource source = context.getSource();
|
||||||
CarpetScriptHost host = getHost(context);
|
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();
|
ServerCommandSource source = context.getSource();
|
||||||
CarpetScriptHost host = getHost(context);
|
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();
|
ServerCommandSource source = context.getSource();
|
||||||
CarpetScriptHost host = getHost(context);
|
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,
|
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();
|
ServerCommandSource source = context.getSource();
|
||||||
CarpetScriptHost host = getHost(context);
|
CarpetScriptHost host = getHost(context);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import carpet.script.value.Value;
|
|||||||
import carpet.script.value.ValueConversions;
|
import carpet.script.value.ValueConversions;
|
||||||
import carpet.settings.ParsedRule;
|
import carpet.settings.ParsedRule;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
@@ -47,8 +48,9 @@ import java.util.Iterator;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -65,59 +67,84 @@ public class CarpetEventServer
|
|||||||
public static class Callback
|
public static class Callback
|
||||||
{
|
{
|
||||||
public final String host;
|
public final String host;
|
||||||
|
public final String optionalTarget;
|
||||||
public final FunctionValue function;
|
public final FunctionValue function;
|
||||||
public final List<Value> parametrizedArgs;
|
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.host = host;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
|
this.optionalTarget = target;
|
||||||
this.parametrizedArgs = parametrizedArgs==null?NOARGS:parametrizedArgs;
|
this.parametrizedArgs = parametrizedArgs==null?NOARGS:parametrizedArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used also in entity events
|
* Used also in entity events
|
||||||
* @param asSource - entity command source
|
* @param sender - entity command source
|
||||||
* @param runtimeArgs = options
|
* @param runtimeArgs = options
|
||||||
*/
|
*/
|
||||||
public boolean execute(ServerCommandSource asSource, List<Value> runtimeArgs)
|
public boolean execute(ServerCommandSource sender, List<Value> runtimeArgs)
|
||||||
{
|
{
|
||||||
if (this.parametrizedArgs.isEmpty())
|
if (!this.parametrizedArgs.isEmpty())
|
||||||
return CarpetServer.scriptServer.runas(
|
|
||||||
asSource.withLevel(CarpetSettings.runPermissionLevel),
|
|
||||||
host, function, runtimeArgs);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
List<Value> combinedArgs = new ArrayList<>(runtimeArgs);
|
runtimeArgs = new ArrayList<>(runtimeArgs);
|
||||||
combinedArgs.addAll(this.parametrizedArgs);
|
runtimeArgs.addAll(this.parametrizedArgs);
|
||||||
return CarpetServer.scriptServer.runas(
|
|
||||||
asSource.withLevel(CarpetSettings.runPermissionLevel),
|
|
||||||
host, function, combinedArgs);
|
|
||||||
}
|
}
|
||||||
|
return CarpetServer.scriptServer.runEventCall(
|
||||||
|
sender.withLevel(CarpetSettings.runPermissionLevel),
|
||||||
|
host, optionalTarget, function, runtimeArgs
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used also in entity events
|
* 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
|
* @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;
|
List<Value> args = runtimeArgs;
|
||||||
if (!this.parametrizedArgs.isEmpty())
|
if (!this.parametrizedArgs.isEmpty())
|
||||||
{
|
{
|
||||||
args = new ArrayList<>(runtimeArgs);
|
args = new ArrayList<>(runtimeArgs);
|
||||||
args.addAll(this.parametrizedArgs);
|
args.addAll(this.parametrizedArgs);
|
||||||
}
|
}
|
||||||
return CarpetServer.scriptServer.runForPlayer(recipient, host, function, args);
|
return CarpetServer.scriptServer.signal(sender, optionalRecipient, host, function, args, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
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)
|
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.ctx = context;
|
||||||
this.dueTime = dueTime;
|
this.dueTime = dueTime;
|
||||||
}
|
}
|
||||||
@@ -139,7 +167,7 @@ public class CarpetEventServer
|
|||||||
*/
|
*/
|
||||||
public void execute()
|
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;
|
if (callList.isEmpty()) return 0;
|
||||||
//List<Callback> fails = new ArrayList<>();
|
//List<Callback> fails = new ArrayList<>();
|
||||||
@@ -181,15 +209,13 @@ public class CarpetEventServer
|
|||||||
int successes = 0;
|
int successes = 0;
|
||||||
for (Callback call: callList)
|
for (Callback call: callList)
|
||||||
{
|
{
|
||||||
Boolean result = call.targetPlayer(receipient, callArg);
|
successes += Math.max(0, call.signal(sender, optinoalReceipient, callArg));
|
||||||
//if (result == Boolean.FALSE) fails.add(call);
|
|
||||||
if (result == Boolean.TRUE) successes++;
|
|
||||||
}
|
}
|
||||||
//for (Callback call : fails) callList.remove(call);
|
//for (Callback call : fails) callList.remove(call);
|
||||||
return successes;
|
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);
|
ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName);
|
||||||
if (host == null)
|
if (host == null)
|
||||||
@@ -198,11 +224,7 @@ public class CarpetEventServer
|
|||||||
Messenger.m(source, "r Unknown app "+hostName);
|
Messenger.m(source, "r Unknown app "+hostName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!verifier.apply(host))
|
hostOnEventHandler.accept(host);
|
||||||
{
|
|
||||||
Messenger.m(source, "r Global event can only be added to apps with global scope");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
FunctionValue udf = host.getFunction(funName);
|
FunctionValue udf = host.getFunction(funName);
|
||||||
if (udf == null || udf.getArguments().size() != reqArgs)
|
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);
|
Messenger.m(source, "r Callback doesn't expect required number of arguments: "+reqArgs);
|
||||||
return false;
|
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
|
//all clear
|
||||||
//remove duplicates
|
//remove duplicates
|
||||||
|
|
||||||
removeEventCall(hostName, udf.getString());
|
removeEventCall(hostName, target, udf.getString());
|
||||||
callList.add(new Callback(hostName, udf, null));
|
callList.add(new Callback(hostName, target, udf, null));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public boolean addEventCallInternal(ScriptHost host, FunctionValue function, List<Value> args)
|
public boolean addEventCallInternal(ScriptHost host, FunctionValue function, List<Value> args)
|
||||||
@@ -225,20 +260,39 @@ public class CarpetEventServer
|
|||||||
}
|
}
|
||||||
//all clear
|
//all clear
|
||||||
//remove duplicates
|
//remove duplicates
|
||||||
removeEventCall(host.getName(), function.getString());
|
removeEventCall(host.getName(), host.user, function.getString());
|
||||||
callList.add(new Callback(host.getName(), function, args));
|
callList.add(new Callback(host.getName(), host.user, function, args));
|
||||||
return true;
|
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)
|
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()
|
public void clearEverything()
|
||||||
{
|
{
|
||||||
callList.clear();
|
callList.clear();
|
||||||
@@ -801,6 +855,12 @@ public class CarpetEventServer
|
|||||||
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.removeAllCalls(host));
|
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()
|
public static void clearAllBuiltinEvents()
|
||||||
{
|
{
|
||||||
byName.values().forEach(e -> e.handler.clearEverything());
|
byName.values().forEach(e -> e.handler.clearEverything());
|
||||||
@@ -888,7 +948,7 @@ public class CarpetEventServer
|
|||||||
{
|
{
|
||||||
return false;
|
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);
|
if (added) Messenger.m(source, "gi Added " + funName + " to " + event);
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
@@ -897,8 +957,7 @@ public class CarpetEventServer
|
|||||||
{
|
{
|
||||||
// this is globals only
|
// this is globals only
|
||||||
Event ev = Event.byName.get(event);
|
Event ev = Event.byName.get(event);
|
||||||
if (!canAddEvent(ev, host))
|
onEventAddedToHost(ev, host);
|
||||||
throw new InternalExpressionException("Global event "+event+" can only be added to apps with global scope");
|
|
||||||
boolean success = ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
|
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())));
|
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)
|
public boolean handleCustomEvent(String event, CarpetScriptHost host, FunctionValue function, List<Value> args)
|
||||||
{
|
{
|
||||||
Event ev = Event.getOrCreateCustom(event, host.getScriptServer());
|
Event ev = Event.getOrCreateCustom(event, host.getScriptServer());
|
||||||
if (!canAddEvent(ev, host))
|
onEventAddedToHost(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);
|
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;
|
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");
|
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)
|
public boolean removeEventFromCommand(ServerCommandSource source, String event, String funName)
|
||||||
@@ -932,8 +990,8 @@ public class CarpetEventServer
|
|||||||
Messenger.m(source, "r Unknown event: " + event);
|
Messenger.m(source, "r Unknown event: " + event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Pair<String,String> call = decodeCallback(funName);
|
Callback.Signature call = Callback.fromString(funName);
|
||||||
ev.handler.removeEventCall(call.getLeft(), call.getRight());
|
ev.handler.removeEventCall(call.host, call.target, call.function);
|
||||||
// could verified if actually removed
|
// could verified if actually removed
|
||||||
Messenger.m(source, "gi Removed event: " + funName + " from "+event);
|
Messenger.m(source, "gi Removed event: " + funName + " from "+event);
|
||||||
return true;
|
return true;
|
||||||
@@ -949,13 +1007,15 @@ public class CarpetEventServer
|
|||||||
public void removeBuiltInEvent(String event, CarpetScriptHost host, String funName)
|
public void removeBuiltInEvent(String event, CarpetScriptHost host, String funName)
|
||||||
{
|
{
|
||||||
Event ev = Event.getEvent(event, host.getScriptServer());
|
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)
|
public void removeAllHostEvents(CarpetScriptHost host)
|
||||||
{
|
{
|
||||||
// remove event handlers
|
// remove event handlers
|
||||||
Event.removeAllHostEvents(host);
|
Event.removeAllHostEvents(host);
|
||||||
|
if (host.isPerUser())
|
||||||
|
for (ScriptHost child: host.userHosts.values()) Event.removeAllHostEvents((CarpetScriptHost) child);
|
||||||
// remove scheduled calls
|
// remove scheduled calls
|
||||||
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
|
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package carpet.script;
|
package carpet.script;
|
||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.api.Auxiliary;
|
import carpet.script.api.Auxiliary;
|
||||||
import carpet.script.argument.FunctionArgument;
|
import carpet.script.argument.FunctionArgument;
|
||||||
import carpet.script.bundled.Module;
|
import carpet.script.bundled.Module;
|
||||||
@@ -15,7 +16,6 @@ import carpet.script.value.MapValue;
|
|||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.CarpetSettings;
|
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
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
|
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();
|
List<String> argNames = funcSpec.function.getArguments();
|
||||||
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
|
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();
|
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);
|
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
|
@Override
|
||||||
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
|
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)
|
if (perUser)
|
||||||
{
|
{
|
||||||
try
|
if (optionalTarget == null)
|
||||||
{
|
{
|
||||||
ServerPlayerEntity player = source.getPlayer();
|
for (ServerPlayerEntity player : source.getMinecraftServer().getPlayerManager().getPlayerList())
|
||||||
host = (CarpetScriptHost) retrieveForExecution(player.getName().getString());
|
{
|
||||||
|
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);
|
else
|
||||||
return host;
|
{
|
||||||
|
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 (!perUser)
|
||||||
{
|
{
|
||||||
if (errorSnooper == null) setChatErrorSnooper(this.scriptServer.server.getCommandSource());
|
if (errorSnooper == null) setChatErrorSnooper(source);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
// user based
|
// user based
|
||||||
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(target.getName().getString());
|
ServerPlayerEntity player;
|
||||||
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(target.getCommandSource());
|
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;
|
return userHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package carpet.script;
|
package carpet.script;
|
||||||
|
|
||||||
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.argument.FunctionArgument;
|
import carpet.script.argument.FunctionArgument;
|
||||||
import carpet.script.bundled.BundledModule;
|
import carpet.script.bundled.BundledModule;
|
||||||
import carpet.CarpetSettings;
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.script.bundled.FileModule;
|
import carpet.script.bundled.FileModule;
|
||||||
import carpet.script.bundled.Module;
|
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");
|
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (newHost.isPerUser())
|
||||||
|
{
|
||||||
|
// that will provide player hosts right at the startup
|
||||||
|
newHost.retrieveForExecution(source, null);
|
||||||
|
}
|
||||||
long end = System.nanoTime();
|
long end = System.nanoTime();
|
||||||
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
|
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
|
||||||
return true;
|
return true;
|
||||||
@@ -325,8 +330,8 @@ public class CarpetScriptServer
|
|||||||
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player)).
|
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player)).
|
||||||
executes( (c) ->
|
executes( (c) ->
|
||||||
{
|
{
|
||||||
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
handleCommandLegacy(c.getSource(),"__command", null, "");
|
Value response = targetHost.handleCommandLegacy(c.getSource(),"__command", null, "");
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
||||||
return (int)response.readInteger();
|
return (int)response.readInteger();
|
||||||
});
|
});
|
||||||
@@ -355,15 +360,15 @@ public class CarpetScriptServer
|
|||||||
then(literal(function).
|
then(literal(function).
|
||||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
|
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
|
||||||
executes((c) -> {
|
executes((c) -> {
|
||||||
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
handleCommandLegacy(c.getSource(), function, null, "");
|
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, "");
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
|
||||||
return (int) response.readInteger();
|
return (int) response.readInteger();
|
||||||
}).
|
}).
|
||||||
then(argument("args...", StringArgumentType.greedyString()).
|
then(argument("args...", StringArgumentType.greedyString()).
|
||||||
executes( (c) -> {
|
executes( (c) -> {
|
||||||
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
handleCommandLegacy(c.getSource(), function,null, StringArgumentType.getString(c, "args..."));
|
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, StringArgumentType.getString(c, "args..."));
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
||||||
return (int)response.readInteger();
|
return (int)response.readInteger();
|
||||||
})));
|
})));
|
||||||
@@ -394,54 +399,84 @@ public class CarpetScriptServer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List<Value> argv)
|
public boolean runEventCall(ServerCommandSource sender, String hostname, String optionalTarget, 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)
|
|
||||||
{
|
{
|
||||||
CarpetScriptHost host = globalHost;
|
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)
|
target = sender.getMinecraftServer().getPlayerManager().getPlayer(optionalTarget);
|
||||||
host = modules.get(hostname).retrieveForExecution(source);
|
if (target == null) return false;
|
||||||
host.callUDF(origin, source, udf, argv);
|
|
||||||
}
|
}
|
||||||
catch (NullPointerException | InvalidCallbackException npe)
|
int successes = signal(sender, target, hostname, udf, argv, true );
|
||||||
{
|
return successes >= 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (hostname == null)
|
||||||
{
|
{
|
||||||
if (recipient != null) return null;
|
if (optionalRecipient != null) return 0;
|
||||||
host = globalHost;
|
try
|
||||||
source = server.getCommandSource();
|
{
|
||||||
|
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);
|
// getPlayer will always return nonnull cause retrieve for execution only returns hosts for existing players.
|
||||||
if (host == null) // not applicable
|
ServerCommandSource executingSource = host.perUser
|
||||||
return null;
|
? source.getMinecraftServer().getPlayerManager().getPlayer(host.user).getCommandSource()
|
||||||
source = (recipient == null)? server.getCommandSource() : recipient.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
|
return successes;
|
||||||
{
|
|
||||||
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
|
|
||||||
}
|
|
||||||
catch (NullPointerException | InvalidCallbackException npe)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick()
|
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
|
static class TransferData
|
||||||
{
|
{
|
||||||
boolean perUser;
|
boolean perUser;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import carpet.script.value.StringValue;
|
|||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -19,7 +20,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class EntityEventsGroup
|
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;
|
private final Entity entity;
|
||||||
public EntityEventsGroup(Entity e)
|
public EntityEventsGroup(Entity e)
|
||||||
{
|
{
|
||||||
@@ -30,37 +31,47 @@ public class EntityEventsGroup
|
|||||||
public void onEvent(Event type, Object... args)
|
public void onEvent(Event type, Object... args)
|
||||||
{
|
{
|
||||||
if (actions.isEmpty()) return; // most of the cases, trying to be nice
|
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;
|
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();
|
Map.Entry<Pair<String,String>, CarpetEventServer.Callback> action = iterator.next();
|
||||||
ScriptHost host = CarpetServer.scriptServer.getHostByName(action.getKey());
|
Pair<String,String> key = action.getKey();
|
||||||
|
ScriptHost host = CarpetServer.scriptServer.getHostByName(key.getLeft());
|
||||||
if (host == null)
|
if (host == null)
|
||||||
{
|
{
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (key.getRight() != null)
|
||||||
|
{
|
||||||
|
if (entity.getServer().getPlayerManager().getPlayer(key.getRight())==null)
|
||||||
|
{
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!type.call(action.getValue(), entity, args))
|
if (!type.call(action.getValue(), entity, args))
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
if (actionSet.isEmpty()) actions.remove(type);
|
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)
|
if (fun != null)
|
||||||
{
|
{
|
||||||
CarpetEventServer.Callback call = type.create(hostName, fun, extraargs);
|
CarpetEventServer.Callback call = type.create(key, fun, extraargs);
|
||||||
if (call == null)
|
if (call == null)
|
||||||
throw new InternalExpressionException("wrong number of arguments for callback, required "+type.argcount);
|
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
|
else
|
||||||
{
|
{
|
||||||
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(hostName);
|
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(key);
|
||||||
if (actions.get(type).isEmpty())
|
if (actions.get(type).isEmpty())
|
||||||
actions.remove(type);
|
actions.remove(type);
|
||||||
}
|
}
|
||||||
@@ -107,13 +118,13 @@ public class EntityEventsGroup
|
|||||||
argcount = args+1; // entity is not extra
|
argcount = args+1; // entity is not extra
|
||||||
byName.put(identifier, this);
|
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)
|
if ((function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argcount)
|
||||||
{
|
{
|
||||||
return null;
|
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)
|
public boolean call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -318,15 +318,21 @@ public abstract class ScriptHost
|
|||||||
ScriptHost oldUserHost = userHosts.get(user);
|
ScriptHost oldUserHost = userHosts.get(user);
|
||||||
if (oldUserHost != null) return oldUserHost;
|
if (oldUserHost != null) return oldUserHost;
|
||||||
ScriptHost userHost = this.duplicate();
|
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;
|
userHost.user = user;
|
||||||
|
this.transferToChild(userHost);
|
||||||
userHosts.put(user, userHost);
|
userHosts.put(user, userHost);
|
||||||
return 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)
|
public void handleExpressionException(String msg, ExpressionException exc)
|
||||||
{
|
{
|
||||||
System.out.println(msg+": "+exc);
|
System.out.println(msg+": "+exc);
|
||||||
|
|||||||
@@ -961,6 +961,7 @@ public class Auxiliary {
|
|||||||
{
|
{
|
||||||
if (lv.size() == 0)
|
if (lv.size() == 0)
|
||||||
throw new InternalExpressionException("'signal' requires at least one argument");
|
throw new InternalExpressionException("'signal' requires at least one argument");
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
CarpetScriptServer server = ((CarpetScriptHost)c.host).getScriptServer();
|
CarpetScriptServer server = ((CarpetScriptHost)c.host).getScriptServer();
|
||||||
String eventName = lv.get(0).evalValue(c).getString();
|
String eventName = lv.get(0).evalValue(c).getString();
|
||||||
// no such event yet
|
// no such event yet
|
||||||
@@ -975,10 +976,10 @@ public class Auxiliary {
|
|||||||
args = FunctionValue.resolveArgs(lv.subList(2, lv.size()), c, t);
|
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;
|
if (counts < 0) return LazyValue.NULL;
|
||||||
Value ret = new NumericValue(counts);
|
Value ret = new NumericValue(counts);
|
||||||
return (cc, tt) -> ret;
|
return (c_, t_) -> ret;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import carpet.script.value.StringValue;
|
|||||||
import carpet.script.value.FormattedTextValue;
|
import carpet.script.value.FormattedTextValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.boss.BossBar;
|
||||||
|
import net.minecraft.entity.boss.BossBarManager;
|
||||||
|
import net.minecraft.entity.boss.CommandBossBar;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.scoreboard.Scoreboard;
|
import net.minecraft.scoreboard.Scoreboard;
|
||||||
import net.minecraft.scoreboard.ServerScoreboard;
|
import net.minecraft.scoreboard.ServerScoreboard;
|
||||||
@@ -20,9 +23,12 @@ import net.minecraft.scoreboard.ScoreboardObjective;
|
|||||||
import net.minecraft.scoreboard.ScoreboardPlayerScore;
|
import net.minecraft.scoreboard.ScoreboardPlayerScore;
|
||||||
import net.minecraft.scoreboard.Team;
|
import net.minecraft.scoreboard.Team;
|
||||||
import net.minecraft.scoreboard.AbstractTeam;
|
import net.minecraft.scoreboard.AbstractTeam;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Formatting;
|
import net.minecraft.util.Formatting;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.InvalidIdentifierException;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -396,6 +402,131 @@ public class Scoreboards {
|
|||||||
}
|
}
|
||||||
return LazyValue.TRUE;
|
return LazyValue.TRUE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expression.addLazyFunction("bossbar", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
BossBarManager bossBarManager = ((CarpetContext)c).s.getMinecraftServer().getBossBarManager();
|
||||||
|
if(lv.size() > 3) throw new InternalExpressionException("'bossbar' accepts max three arguments");
|
||||||
|
|
||||||
|
if(lv.size() == 0) {
|
||||||
|
Value ret = ListValue.wrap(bossBarManager.getAll().stream().map(CommandBossBar::getId).map(Identifier::toString).map(StringValue::of).collect(Collectors.toList()));
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = lv.get(0).evalValue(c).getString();
|
||||||
|
Identifier identifier;
|
||||||
|
try {
|
||||||
|
identifier = new Identifier(id);
|
||||||
|
} catch (InvalidIdentifierException invalidIdentifierException) {
|
||||||
|
return LazyValue.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lv.size() == 1) {
|
||||||
|
if(bossBarManager.get(identifier) != null) return LazyValue.FALSE;
|
||||||
|
Value ret = StringValue.of(bossBarManager.add(identifier,new LiteralText(id)).getId().toString());
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String property = lv.get(1).evalValue(c).getString();
|
||||||
|
|
||||||
|
CommandBossBar bossBar = bossBarManager.get(identifier);
|
||||||
|
if(bossBar == null) return LazyValue.NULL;
|
||||||
|
|
||||||
|
Value propertyValue = (lv.size() == 3)?lv.get(2).evalValue(c):null;
|
||||||
|
|
||||||
|
switch (property) {
|
||||||
|
case "color":
|
||||||
|
if(propertyValue == null) {
|
||||||
|
BossBar.Color color = (bossBar).getColor();
|
||||||
|
if(color == null) return LazyValue.NULL;
|
||||||
|
return (_c, _t) -> StringValue.of(color.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
BossBar.Color color = BossBar.Color.byName(propertyValue.getString());
|
||||||
|
if(color == null) return LazyValue.NULL;
|
||||||
|
bossBar.setColor(BossBar.Color.byName(propertyValue.getString()));
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "max":
|
||||||
|
if(propertyValue == null) return (_c, _t) -> NumericValue.of(bossBar.getMaxValue());
|
||||||
|
|
||||||
|
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||||
|
bossBar.setMaxValue(((NumericValue) propertyValue).getInt());
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "name":
|
||||||
|
if(propertyValue == null) return (_c, _t) -> new FormattedTextValue(bossBar.getName());
|
||||||
|
|
||||||
|
if(propertyValue instanceof FormattedTextValue) {
|
||||||
|
bossBar.setName(((FormattedTextValue) propertyValue).getText());
|
||||||
|
} else {
|
||||||
|
bossBar.setName(new LiteralText(propertyValue.getString()));
|
||||||
|
}
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "add_player":
|
||||||
|
if(propertyValue == null) throw new InternalExpressionException("Bossbar property " + property + " can't be queried, add a third parameter");
|
||||||
|
|
||||||
|
if(propertyValue instanceof ListValue) {
|
||||||
|
((ListValue) propertyValue).getItems().forEach((v)->{
|
||||||
|
ServerPlayerEntity player = EntityValue.getPlayerByValue(((CarpetContext)c).s.getMinecraftServer(),propertyValue);
|
||||||
|
if(player != null) bossBar.addPlayer(player);
|
||||||
|
});
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPlayerEntity player = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), propertyValue);
|
||||||
|
if(player != null) {
|
||||||
|
bossBar.addPlayer(player);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
return LazyValue.FALSE;
|
||||||
|
case "players":
|
||||||
|
if(propertyValue == null) {
|
||||||
|
return (_c, _t) -> ListValue.wrap(bossBar.getPlayers().stream().map(EntityValue::new).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(propertyValue instanceof ListValue) {
|
||||||
|
bossBar.clearPlayers();
|
||||||
|
((ListValue) propertyValue).getItems().forEach((v) -> {
|
||||||
|
ServerPlayerEntity p = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), v);
|
||||||
|
if (p != null) bossBar.addPlayer(p);
|
||||||
|
});
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ServerPlayerEntity p = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), propertyValue);
|
||||||
|
bossBar.clearPlayers();
|
||||||
|
if (p != null) {
|
||||||
|
bossBar.addPlayer(p);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
return LazyValue.FALSE;
|
||||||
|
case "style":
|
||||||
|
if(propertyValue == null) {
|
||||||
|
return (_c, _t) -> StringValue.of(bossBar.getOverlay().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
BossBar.Style style = BossBar.Style.byName(propertyValue.getString());
|
||||||
|
if(style == null) throw new InternalExpressionException("'" + propertyValue.getString() + "' is not a valid value for property " + property);
|
||||||
|
bossBar.setOverlay(style);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "value":
|
||||||
|
if(propertyValue == null) return (_c, _t) -> NumericValue.of(bossBar.getValue());
|
||||||
|
|
||||||
|
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||||
|
bossBar.setValue(((NumericValue) propertyValue).getInt());
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "visible":
|
||||||
|
if(propertyValue == null) return (_c, _t) -> new NumericValue(bossBar.isVisible());
|
||||||
|
|
||||||
|
bossBar.setVisible(propertyValue.getBoolean());
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
case "remove":
|
||||||
|
bossBarManager.remove(bossBar);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
default:
|
||||||
|
throw new InternalExpressionException("Unknown bossbar property " + property);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1387,7 +1387,7 @@ public class EntityValue extends Value
|
|||||||
EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName);
|
EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName);
|
||||||
if (event == null)
|
if (event == null)
|
||||||
throw new InternalExpressionException("Unknown entity event: " + eventName);
|
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
|
@Override
|
||||||
|
|||||||
@@ -90,12 +90,9 @@ calculate(end)->(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
_carpets() -> system_info('world_carpet_rules'):'carpets'=='true';
|
|
||||||
|
|
||||||
|
|
||||||
on_player_places_block(player, item_tuple, hand, block) ->
|
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];
|
effective_pos = pos(block)+[0.5, 0, 0.5];
|
||||||
if(global_current_start==null||player~'sneaking',//wont complain for first carpet
|
if(global_current_start==null||player~'sneaking',//wont complain for first carpet
|
||||||
set_start(effective_pos)
|
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) ->
|
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',
|
if (player ~'sneaking',
|
||||||
global_current_mode = global_display_modes:(global_display_modes ~ global_current_mode + 1);
|
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));
|
display_title(player, 'actionbar', format('w Distance mode: ','e '+global_current_mode));
|
||||||
|
|||||||
Reference in new issue
Block a user