[Scarpet] Added ability to cancel events (#1497)

* Added ability to cancel events

* Made more events cancellable

* Added event_priority to __config

* Check for string values for performance

* Made player_finishes_using_item cancellable

* Made player_chooses_recipe cancellable

* Made player_swaps_hands cancellable

* Made player_message cancellable

* Added __on_player_placing_block (#1498)

* Added event to docs, keywords, event_test

* Added docs for event cancellation
This commit is contained in:
replaceitem authored and GitHub committed 2022-10-15 13:30:53 +02:00
1 parent 4bb4b557e5
commit 24b9c386ec
14 files changed
+243 -99

No files matched your search

+40 -1
View File
@@ -22,6 +22,14 @@ partially handle the event before it happens and handle the rest after. While in
confusion (like handling the respawn event still referring to player's original position and dimension), but gives much
more control over these events.
Some events also provide the ability to cancel minecraft's processing of the event by returning `'cancel'` from the event handler.
This only works for particular events that are triggered before they take an effect in the game.
However, cancelling the event will also stop events from subsequent apps from triggering.
The order of events being executed can be changed by specifying an `'event_priority'` in the app config,
with the highest value being executed first.
Note that cancelling some events might introduce a desynchronization to the client from the server,
creating ghost items or blocks. This can be solved by updating the inventory or block to the client, by using `inventory_set` or `set`.
Programmers can also define their own events and signal other events, including built-in events, and across all loaded apps.
## App scopes and event distribution
@@ -142,6 +150,8 @@ Triggers with a right click action. Event is triggered right after a server rece
game manages to do anything about it. Event triggers when player starts eating food, or starts drawing a bow.
Use `player_finishes_using_item`, or `player_releases_item` to capture the end of these events.
This event can be cancelled by returning `'cancel'`, which prevents the item from being used.
Event is not triggered when a player places a block, for that use
`player_right_clicks_block` or `player_places_block` event.
@@ -156,21 +166,34 @@ Player using of an item is done. This is controlled server side and is responsib
eating. The event is triggered after confirming that the action is valid, and sending the feedback back
to the client, but before triggering it and its effects in game.
This event can be cancelled by returning `'cancel'`, which prevents the player from finishing using the item.
### `__on_player_clicks_block(player, block, face)`
Representing left-click attack on a block, usually signifying start of breaking of a block. Triggers right after the server
receives a client packet, before anything happens on the server side.
receives a client packet, before anything happens on the server side.
This event can be cancelled by returning `'cancel'`, which stops the player from breaking a block.
### `__on_player_breaks_block(player, block)`
Called when player breaks a block, right before any changes to the world are done, but the decision is made to remove the block.
This event can be cancelled by returning `'cancel'`, which prevents the block from being placed.
### `__on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)`
Called when player right clicks on a block with anything, or interacts with a block. This event is triggered right
before other interaction events, like `'player_interacts_with_block'` or `'player_places_block'`.
This event can be cancelled by returning `'cancel'`, which prevents the player interaction.
### `__on_player_interacts_with_block(player, hand, block, face, hitvec)`
Called when player successfully interacted with a block, which resulted in activation of said block,
right after this happened.
### `__on_player_placing_block(player, item_tuple, hand, block)`
Triggered when player places a block, before block is placed in the world.
This event can be cancelled by returning `'cancel'`, which prevents the block from being placed.
### `__on_player_places_block(player, item_tuple, hand, block)`
Triggered when player places a block, after block is placed in the world, but before scoreboard is triggered or player inventory
@@ -181,6 +204,8 @@ Triggered when player right clicks (interacts) with an entity, even if the entit
the item they are holding. The event is invoked after receiving a packet from the client, before anything happens server side
with that interaction.
This event can be cancelled by returning `'cancel'`, which prevents the player interacting with the entity.
### `__on_player_trades(player, entity, buy_left, buy_right, sell)`
Triggered when player trades with a merchant. The event is invoked after the server allow the trade, but before the inventory
changes and merchant updates its trade-uses counter.
@@ -195,6 +220,8 @@ on the player.
Triggered when a player clicks a recipe in the crafting window from the crafting book, after server received
a client request, but before any items are moved from its inventory to the crafting menu.
This event can be cancelled by returning `'cancel'`, which prevents the recipe from being moved into the crafting grid.
### `__on_player_switches_slot(player, from, to)`
Triggered when a player changes their selected hotbar slot. Applied right after the server receives the message to switch
the slot.
@@ -202,6 +229,8 @@ the slot.
### `__on_player_swaps_hands(player)`
Triggered when a player sends a command to swap their offhand item. Executed before the effect is applied on the server.
This event can be cancelled by returning `'cancel'`, which prevents the hands from being swapped.
### `__on_player_swings_hand(player, hand)`
Triggered when a player starts swinging their hand. The event typically triggers after a corresponding event that caused it
(`player_uses_item`, `player_breaks_block`, etc.), but it triggers also after some failed events, like attacking the air. When
@@ -210,15 +239,21 @@ swinging continues as an effect of an action, no new swinging events will be iss
### `__on_player_attacks_entity(player, entity)`
Triggered when a player attacks entity, right before it happens server side.
This event can be cancelled by returning `'cancel'`, which prevents the player from attacking the entity.
### `__on_player_takes_damage(player, amount, source, source_entity)`
Triggered when a player is taking damage. Event is executed right after potential absorbtion was applied and before
the actual damage is applied to the player.
This event can be cancelled by returning `'cancel'`, which prevents the player from taking damage.
### `__on_player_deals_damage(player, amount, entity)`
Triggered when a player deals damage to another entity. Its applied in the same moment as `player_takes_damage` if both
sides of the event are players, and similar for all other entities, just their absorbtion is taken twice, just noone ever
notices that ¯\_(ツ)_/¯
This event can be cancelled by returning `'cancel'`, which prevents the damage from being dealt.
### `__on_player_dies(player)`
Triggered when a player dies. Player is already dead, so don't revive them then. Event applied before broadcasting messages
about players death and applying external effects (like mob anger etc).
@@ -267,6 +302,8 @@ Four events triggered when player controls for sneaking and sprinting toggle.
Triggered when the game receives the request from a player to drop one item or full stack from its inventory.
Event happens before anything is changed server side.
These events can be cancelled by returning `'cancel'`, which prevents the player dropping the items.
### `__on_player_picks_up_item(player, item)`
Triggered AFTER a player successfully ingested an item in its inventory. Item represents the total stack of items
ingested by the player. The exact position of these items is unknown as technically these
@@ -281,6 +318,8 @@ Triggered when a player sends a disconnect package or is forcefully disconnected
### `__on_player_message(player, message)`
Triggered when a player sends a chat message or runs a command.
This event can be cancelled by returning `'cancel'`, which prevents the message from being sent.
### `__on_statistic(player, category, event, value)`
Triggered when a player statistic changes. Doesn't notify on periodic an rhythmic events, i.e.
`time_since_death`, `time_since_rest`, and `played_one_minute` since these are triggered every tick. Event
+2
View File
@@ -60,6 +60,8 @@ To access global/server state for a player app, which you shouldn't do, you need
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
running anything in the global scope for a `'player'` scoped app is not intended.
* `'event_priority'`: defaults to `0`. This specifies the order in which events will be run, from highest to lowest.
This is need since cancelling an event will stop executing the event in subsequent apps with lower priority.
* `'stay_loaded'`: defaults to `true`. 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.
WARNING: all apps will run once at startup anyways, so be aware that their actions that are called
@@ -17,6 +17,7 @@ __on_player_interacts_with_block(player, hand, block, face, hitvec) ->
__on_player_interacts_with_entity(player, entity, hand) ->
__on_player_jumps(player) ->
__on_player_picks_up_item(player, item) ->
__on_player_placing_block(player, item_tuple, hand, block) ->
__on_player_places_block(player, item_tuple, hand, block) ->
__on_player_releases_item(player, item_tuple, hand) ->
__on_player_rides(player, forward, strafe, jumping, sneaking) ->
@@ -120,6 +120,7 @@
__on_player_interacts_with_entity(player, entity, hand) ->
__on_player_jumps(player) ->
__on_player_picks_up_item(player, item) ->
__on_player_placing_block(player, item_tuple, hand, block) ->
__on_player_places_block(player, item_tuple, hand, block) ->
__on_player_releases_item(player, item_tuple, hand) ->
__on_player_rides(player, forward, strafe, jumping, sneaking) ->
@@ -1,11 +1,13 @@
package carpet.mixins;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static carpet.script.CarpetEventServer.Event.PLAYER_PLACES_BLOCK;
import static carpet.script.CarpetEventServer.Event.PLAYER_PLACING_BLOCK;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
@@ -25,4 +27,14 @@ public class BlockItem_scarpetEventMixin
if (context.getPlayer() instanceof ServerPlayer && PLAYER_PLACES_BLOCK.isNeeded())
PLAYER_PLACES_BLOCK.onBlockPlaced((ServerPlayer) context.getPlayer(), context.getClickedPos(), context.getHand(), context.getItemInHand());
}
@Inject(method = "placeBlock", at = @At("HEAD"), cancellable = true)
private void beforePlacement(BlockPlaceContext blockPlaceContext, BlockState blockState, CallbackInfoReturnable<Boolean> cir) {
if (blockPlaceContext.getPlayer() instanceof ServerPlayer && PLAYER_PLACING_BLOCK.isNeeded()) {
if (PLAYER_PLACING_BLOCK.onBlockPlaced((ServerPlayer) blockPlaceContext.getPlayer(), blockPlaceContext.getClickedPos(), blockPlaceContext.getHand(), blockPlaceContext.getItemInHand())) {
cir.setReturnValue(false);
cir.cancel();
}
}
}
}
@@ -37,7 +37,7 @@ public abstract class LivingEntity_scarpetEventsMixin extends Entity implements
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.Event.ON_DEATH, damageSource_1.msgId);
}
@Inject(method = "actuallyHurt", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
@Inject(method = "actuallyHurt", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/LivingEntity;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F",
shift = At.Shift.BEFORE
@@ -52,7 +52,9 @@ public abstract class LivingEntity_scarpetEventsMixin extends Entity implements
//}
if (source.getEntity() instanceof ServerPlayer && PLAYER_DEALS_DAMAGE.isNeeded())
{
PLAYER_DEALS_DAMAGE.onDamage(this, amount, source);
if(PLAYER_DEALS_DAMAGE.onDamage(this, amount, source)) {
ci.cancel();
}
}
}
@@ -32,7 +32,7 @@ public abstract class Player_scarpetEventsMixin extends LivingEntity
super(type, world);
}
@Inject(method = "actuallyHurt", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
@Inject(method = "actuallyHurt", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F"
))
@@ -42,11 +42,15 @@ public abstract class Player_scarpetEventsMixin extends LivingEntity
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.Event.ON_DAMAGE, amount, source);
if (PLAYER_TAKES_DAMAGE.isNeeded())
{
PLAYER_TAKES_DAMAGE.onDamage(this, amount, source);
if(PLAYER_TAKES_DAMAGE.onDamage(this, amount, source)) {
ci.cancel();
}
}
if (source.getEntity() instanceof ServerPlayer && PLAYER_DEALS_DAMAGE.isNeeded())
{
PLAYER_DEALS_DAMAGE.onDamage(this, amount, source);
if(PLAYER_DEALS_DAMAGE.onDamage(this, amount, source)) {
ci.cancel();
}
}
}
@@ -59,21 +63,26 @@ public abstract class Player_scarpetEventsMixin extends LivingEntity
}
}
@Inject(method = "interactOn", at = @At("HEAD"))
@Inject(method = "interactOn", cancellable = true, at = @At("HEAD"))
private void doInteract(Entity entity, InteractionHand hand, CallbackInfoReturnable<InteractionResult> cir)
{
if (!level.isClientSide && PLAYER_INTERACTS_WITH_ENTITY.isNeeded())
{
PLAYER_INTERACTS_WITH_ENTITY.onEntityHandAction((ServerPlayer) (Object)this, entity, hand);
if(PLAYER_INTERACTS_WITH_ENTITY.onEntityHandAction((ServerPlayer) (Object)this, entity, hand)) {
cir.setReturnValue(InteractionResult.PASS);
cir.cancel();
}
}
}
@Inject(method = "attack", at = @At("HEAD"))
@Inject(method = "attack", at = @At("HEAD"), cancellable = true)
private void onAttack(Entity target, CallbackInfo ci)
{
if (!level.isClientSide && PLAYER_ATTACKS_ENTITY.isNeeded() && target.isAttackable())
{
PLAYER_ATTACKS_ENTITY.onEntityHandAction((ServerPlayer) (Object)this, target, null);
if(PLAYER_ATTACKS_ENTITY.onEntityHandAction((ServerPlayer) (Object)this, target, null)) {
ci.cancel();
}
}
}
}
@@ -61,7 +61,7 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
}
}
@Inject(method = "handlePlayerAction", at = @At(
@Inject(method = "handlePlayerAction", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayer;drop(Z)Z", // dropSelectedItem
ordinal = 0,
@@ -69,10 +69,12 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
))
private void onQItem(ServerboundPlayerActionPacket playerActionC2SPacket_1, CallbackInfo ci)
{
PLAYER_DROPS_ITEM.onPlayerEvent(player);
if(PLAYER_DROPS_ITEM.onPlayerEvent(player)) {
ci.cancel();
}
}
@Inject(method = "handlePlayerAction", at = @At(
@Inject(method = "handlePlayerAction", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayer;getItemInHand(Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/item/ItemStack;",
ordinal = 0,
@@ -80,10 +82,10 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
))
private void onHandSwap(ServerboundPlayerActionPacket playerActionC2SPacket_1, CallbackInfo ci)
{
PLAYER_SWAPS_HANDS.onPlayerEvent(player);
if(PLAYER_SWAPS_HANDS.onPlayerEvent(player)) ci.cancel();
}
@Inject(method = "handlePlayerAction", at = @At(
@Inject(method = "handlePlayerAction", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayer;drop(Z)Z", // dropSelectedItem
ordinal = 1,
@@ -91,7 +93,9 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
))
private void onCtrlQItem(ServerboundPlayerActionPacket playerActionC2SPacket_1, CallbackInfo ci)
{
PLAYER_DROPS_STACK.onPlayerEvent(player);
if(PLAYER_DROPS_STACK.onPlayerEvent(player)) {
ci.cancel();
}
}
@@ -104,7 +108,7 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
PLAYER_JUMPS.onPlayerEvent(player);
}
@Inject(method = "handlePlayerAction", at = @At(
@Inject(method = "handlePlayerAction", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayerGameMode;handleBlockBreakAction(Lnet/minecraft/core/BlockPos;Lnet/minecraft/network/protocol/game/ServerboundPlayerActionPacket$Action;Lnet/minecraft/core/Direction;II)V",
shift = At.Shift.BEFORE
@@ -112,7 +116,9 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
private void onClicked(ServerboundPlayerActionPacket packet, CallbackInfo ci)
{
if (packet.getAction() == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK)
PLAYER_CLICKS_BLOCK.onBlockAction(player, packet.getPos(), packet.getDirection());
if(PLAYER_CLICKS_BLOCK.onBlockAction(player, packet.getPos(), packet.getDirection())) {
ci.cancel();
}
}
@Redirect(method = "handlePlayerAction", at = @At(
@@ -134,7 +140,7 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
}
}
@Inject(method = "handleUseItemOn", at = @At(
@Inject(method = "handleUseItemOn", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayerGameMode;useItemOn(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/phys/BlockHitResult;)Lnet/minecraft/world/InteractionResult;"
))
@@ -144,11 +150,13 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
{
InteractionHand hand = playerInteractBlockC2SPacket_1.getHand();
BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitResult();
PLAYER_RIGHT_CLICKS_BLOCK.onBlockHit(player, hand, hitRes);
if(PLAYER_RIGHT_CLICKS_BLOCK.onBlockHit(player, hand, hitRes)) {
ci.cancel();
}
}
}
@Inject(method = "handleUseItem", at = @At(
@Inject(method = "handleUseItem", cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerPlayer;resetLastActionTime()V"
))
@@ -157,7 +165,9 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
if (PLAYER_USES_ITEM.isNeeded())
{
InteractionHand hand = playerInteractItemC2SPacket_1.getHand();
PLAYER_USES_ITEM.onItemAction(player, hand, player.getItemInHand(hand).copy());
if(PLAYER_USES_ITEM.onItemAction(player, hand, player.getItemInHand(hand).copy())) {
ci.cancel();
}
}
}
@@ -251,12 +261,12 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
// crafts not int the crafting window
//CarpetSettings.LOG.error("Player clicks button "+packet.getButtonId());
}
@Inject(method = "handlePlaceRecipe", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;resetLastActionTime()V"))
@Inject(method = "handlePlaceRecipe", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;resetLastActionTime()V"))
private void onRecipeSelectedInRecipeManager(ServerboundPlaceRecipePacket packet, CallbackInfo ci)
{
if (PLAYER_CHOOSES_RECIPE.isNeeded())
{
PLAYER_CHOOSES_RECIPE.onRecipeSelected(player, packet.getRecipe(), packet.isShiftDown());
if(PLAYER_CHOOSES_RECIPE.onRecipeSelected(player, packet.getRecipe(), packet.isShiftDown())) ci.cancel();
}
}
@@ -283,12 +293,13 @@ public class ServerGamePacketListenerImpl_scarpetEventsMixin
}
@Inject(method = "handleChat(Lnet/minecraft/network/protocol/game/ServerboundChatPacket;)V",
at = @At(value = "HEAD")
at = @At(value = "HEAD"),
cancellable = true
)
private void onChatMessage(ServerboundChatPacket serverboundChatPacket, CallbackInfo ci) {
if (PLAYER_MESSAGE.isNeeded())
{
PLAYER_MESSAGE.onPlayerMessage(player, serverboundChatPacket.message());
if(PLAYER_MESSAGE.onPlayerMessage(player, serverboundChatPacket.message())) ci.cancel();
}
}
}
@@ -38,14 +38,18 @@ public class ServerPlayerGameMode_scarpetEventsMixin implements ServerPlayerInte
@Shadow public ServerLevel level;
@Inject(method = "destroyBlock", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
@Inject(method = "destroyBlock", locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/block/Block;playerWillDestroy(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)V",
shift = At.Shift.BEFORE
))
private void onBlockBroken(BlockPos blockPos_1, CallbackInfoReturnable<Boolean> cir, BlockState blockState_1, BlockEntity be, Block b)
{
PLAYER_BREAK_BLOCK.onBlockBroken(player, blockPos_1, blockState_1);
if(PLAYER_BREAK_BLOCK.onBlockBroken(player, blockPos_1, blockState_1)) {
this.level.sendBlockUpdated(blockPos_1, blockState_1, blockState_1, 3);
cir.setReturnValue(false);
cir.cancel();
}
}
@Inject(method = "useItemOn", at = @At(
@@ -56,9 +56,10 @@ public abstract class ServerPlayer_scarpetEventMixin extends Player implements S
if (PLAYER_FINISHED_USING_ITEM.isNeeded())
{
InteractionHand hand = getUsedItemHand();
PLAYER_FINISHED_USING_ITEM.onItemAction((ServerPlayer) (Object)this, hand, getUseItem());
// do vanilla
super.completeUsingItem();
if(!PLAYER_FINISHED_USING_ITEM.onItemAction((ServerPlayer) (Object)this, hand, getUseItem())) {
// do vanilla
super.completeUsingItem();
}
}
else
{
@@ -25,6 +25,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -77,7 +78,7 @@ public class CarpetEventServer
public enum CallbackResult
{
SUCCESS, PASS, FAIL
SUCCESS, PASS, FAIL, CANCEL
}
public static class Callback
@@ -216,12 +217,13 @@ public class CarpetEventServer
* Handles only built-in events from the events system
* @param argumentSupplier
* @param cmdSourceSupplier
* @return Whether this event call has been cancelled
*/
public void call(Supplier<List<Value>> argumentSupplier, Supplier<CommandSourceStack> cmdSourceSupplier)
public boolean call(Supplier<List<Value>> argumentSupplier, Supplier<CommandSourceStack> cmdSourceSupplier)
{
if (callList.size() > 0 && CarpetServer.scriptServer != null)
{
CarpetServer.scriptServer.events.handleEvents.runIfEnabled( () -> {
return CarpetServer.scriptServer.events.handleEvents.runIfEnabled( () -> {
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL);
List<Value> argv = argumentSupplier.get(); // empty for onTickDone
CommandSourceStack source;
@@ -231,10 +233,11 @@ public class CarpetEventServer
}
catch (NullPointerException noReference) // todo figure out what happens when closing.
{
return;
return false;
}
String nameCheck = perPlayerDistribution ? source.getTextName() : null;
assert argv.size() == reqArgs;
boolean cancelled = false;
try
{
// we are ok with list growing in the meantime
@@ -246,7 +249,12 @@ public class CarpetEventServer
// supressing calls where target player hosts simply don't match
// handling global hosts with player targets is left to when the host is resolved (few calls deeper).
if (nameCheck != null && call.optionalTarget != null && !nameCheck.equals(call.optionalTarget)) continue;
if (call.execute(source, argv) == CallbackResult.FAIL) removedCalls.add(call);
CallbackResult result = call.execute(source, argv);
if(result == CallbackResult.CANCEL) {
cancelled = true;
break;
}
if (result == CallbackResult.FAIL) removedCalls.add(call);
}
}
finally
@@ -256,8 +264,10 @@ public class CarpetEventServer
for (Callback call : removedCalls) callList.remove(call);
removedCalls.clear();
CarpetProfiler.end_current_section(currentSection);
return cancelled;
});
}
return false;
}
public int signal(CommandSourceStack sender, ServerPlayer optinoalReceipient, List<Value> callArg)
@@ -363,6 +373,10 @@ public class CarpetEventServer
if (inSignal || inCall) callList = new ArrayList<>();
callList.clear();
}
public void sortByPriority(CarpetScriptServer scriptServer) {
callList.sort(Comparator.comparingDouble(c -> -scriptServer.getAppHostByName(c.host).eventPriority));
}
}
public static class Event
@@ -483,33 +497,37 @@ public class CarpetEventServer
public static final Event PLAYER_JUMPS = new Event("player_jumps", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_DEPLOYS_ELYTRA = new Event("player_deploys_elytra", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_WAKES_UP = new Event("player_wakes_up", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_ESCAPES_SLEEP = new Event("player_escapes_sleep", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_RIDES = new Event("player_rides", 5, false)
@@ -525,9 +543,9 @@ public class CarpetEventServer
public static final Event PLAYER_USES_ITEM = new Event("player_uses_item", 3, false)
{
@Override
public void onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
public boolean onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
{
handler.call( () ->
return handler.call( () ->
{
//ItemStack itemstack = player.getStackInHand(enumhand);
return Arrays.asList(
@@ -541,9 +559,9 @@ public class CarpetEventServer
public static final Event PLAYER_CLICKS_BLOCK = new Event("player_clicks_block", 3, false)
{
@Override
public void onBlockAction(ServerPlayer player, BlockPos blockpos, Direction facing)
public boolean onBlockAction(ServerPlayer player, BlockPos blockpos, Direction facing)
{
handler.call( () ->
return handler.call( () ->
{
return Arrays.asList(
new EntityValue(player),
@@ -556,9 +574,9 @@ public class CarpetEventServer
public static final Event PLAYER_RIGHT_CLICKS_BLOCK = new Event("player_right_clicks_block", 6, false)
{
@Override
public void onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d)
public boolean onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d)
{
handler.call( () ->
return handler.call( () ->
{
ItemStack itemstack = player.getItemInHand(enumhand);
BlockPos blockpos = hitRes.getBlockPos();
@@ -582,7 +600,7 @@ public class CarpetEventServer
public static final Event PLAYER_INTERACTS_WITH_BLOCK = new Event("player_interacts_with_block", 5, false)
{
@Override
public void onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes)
public boolean onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes)
{
handler.call( () ->
{
@@ -601,14 +619,15 @@ public class CarpetEventServer
)
);
}, player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_PLACES_BLOCK = new Event("player_places_block", 4, false)
public static final Event PLAYER_PLACING_BLOCK = new Event("player_placing_block", 4, false)
{
@Override
public void onBlockPlaced(ServerPlayer player, BlockPos pos, InteractionHand enumhand, ItemStack itemstack)
public boolean onBlockPlaced(ServerPlayer player, BlockPos pos, InteractionHand enumhand, ItemStack itemstack)
{
handler.call( () -> Arrays.asList(
return handler.call( () -> Arrays.asList(
new EntityValue(player),
ValueConversions.of(itemstack),
StringValue.of(enumhand == InteractionHand.MAIN_HAND ? "mainhand" : "offhand"),
@@ -616,12 +635,26 @@ public class CarpetEventServer
), player::createCommandSourceStack);
}
};
public static final Event PLAYER_PLACES_BLOCK = new Event("player_places_block", 4, false)
{
@Override
public boolean onBlockPlaced(ServerPlayer player, BlockPos pos, InteractionHand enumhand, ItemStack itemstack)
{
handler.call( () -> Arrays.asList(
new EntityValue(player),
ValueConversions.of(itemstack),
StringValue.of(enumhand == InteractionHand.MAIN_HAND ? "mainhand" : "offhand"),
new BlockValue(null, player.getLevel(), pos)
), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_BREAK_BLOCK = new Event("player_breaks_block", 2, false)
{
@Override
public void onBlockBroken(ServerPlayer player, BlockPos pos, BlockState previousBS)
public boolean onBlockBroken(ServerPlayer player, BlockPos pos, BlockState previousBS)
{
handler.call(
return handler.call(
() -> Arrays.asList(new EntityValue(player), new BlockValue(previousBS, player.getLevel(), pos)),
player::createCommandSourceStack
);
@@ -630,9 +663,9 @@ public class CarpetEventServer
public static final Event PLAYER_INTERACTS_WITH_ENTITY = new Event("player_interacts_with_entity", 3, false)
{
@Override
public void onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand)
public boolean onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand)
{
handler.call( () -> Arrays.asList(
return handler.call( () -> Arrays.asList(
new EntityValue(player), new EntityValue(entity), StringValue.of(enumhand==InteractionHand.MAIN_HAND?"mainhand":"offhand")
), player::createCommandSourceStack);
}
@@ -654,56 +687,61 @@ public class CarpetEventServer
public static final Event PLAYER_PICKS_UP_ITEM = new Event("player_picks_up_item", 2, false)
{
@Override
public void onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack) {
public boolean onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack) {
handler.call( () -> Arrays.asList(new EntityValue(player), ValueConversions.of(itemstack)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_ATTACKS_ENTITY = new Event("player_attacks_entity", 2, false)
{
@Override
public void onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand)
public boolean onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand)
{
handler.call( () -> Arrays.asList(new EntityValue(player), new EntityValue(entity)), player::createCommandSourceStack);
return handler.call( () -> Arrays.asList(new EntityValue(player), new EntityValue(entity)), player::createCommandSourceStack);
}
};
public static final Event PLAYER_STARTS_SNEAKING = new Event("player_starts_sneaking", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_STOPS_SNEAKING = new Event("player_stops_sneaking", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_STARTS_SPRINTING = new Event("player_starts_sprinting", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_STOPS_SPRINTING = new Event("player_stops_sprinting", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_RELEASED_ITEM = new Event("player_releases_item", 3, false)
{
@Override
public void onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
public boolean onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
{
// this.getStackInHand(this.getActiveHand()), this.activeItemStack)
handler.call( () ->
@@ -712,15 +750,16 @@ public class CarpetEventServer
ValueConversions.of(itemstack),
StringValue.of(enumhand == InteractionHand.MAIN_HAND ? "mainhand" : "offhand")
), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_FINISHED_USING_ITEM = new Event("player_finishes_using_item", 3, false)
{
@Override
public void onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
public boolean onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack)
{
// this.getStackInHand(this.getActiveHand()), this.activeItemStack)
handler.call( () ->
return handler.call( () ->
Arrays.asList(
new EntityValue(player),
ValueConversions.of(itemstack),
@@ -731,25 +770,25 @@ public class CarpetEventServer
public static final Event PLAYER_DROPS_ITEM = new Event("player_drops_item", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
}
};
public static final Event PLAYER_DROPS_STACK = new Event("player_drops_stack", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
}
};
public static final Event PLAYER_CHOOSES_RECIPE = new Event("player_chooses_recipe", 3, false)
{
@Override
public void onRecipeSelected(ServerPlayer player, ResourceLocation recipe, boolean fullStack)
public boolean onRecipeSelected(ServerPlayer player, ResourceLocation recipe, boolean fullStack)
{
handler.call( () ->
return handler.call( () ->
Arrays.asList(
new EntityValue(player),
StringValue.of(NBTSerializableValue.nameFromRegistryId(recipe)),
@@ -774,9 +813,9 @@ public class CarpetEventServer
public static final Event PLAYER_SWAPS_HANDS = new Event("player_swaps_hands", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
}
};
public static final Event PLAYER_SWINGS_HAND = new Event("player_swings_hand", 2, false)
@@ -794,9 +833,9 @@ public class CarpetEventServer
public static final Event PLAYER_TAKES_DAMAGE = new Event("player_takes_damage", 4, false)
{
@Override
public void onDamage(Entity target, float amount, DamageSource source)
public boolean onDamage(Entity target, float amount, DamageSource source)
{
handler.call( () ->
return handler.call( () ->
Arrays.asList(
new EntityValue(target),
new NumericValue(amount),
@@ -808,9 +847,9 @@ public class CarpetEventServer
public static final Event PLAYER_DEALS_DAMAGE = new Event("player_deals_damage", 3, false)
{
@Override
public void onDamage(Entity target, float amount, DamageSource source)
public boolean onDamage(Entity target, float amount, DamageSource source)
{
handler.call( () ->
return handler.call( () ->
Arrays.asList(new EntityValue(source.getEntity()), new NumericValue(amount), new EntityValue(target)),
() -> source.getEntity().createCommandSourceStack()
);
@@ -819,25 +858,28 @@ public class CarpetEventServer
public static final Event PLAYER_COLLIDES_WITH_ENTITY = new Event("player_collides_with_entity", 2, false)
{
@Override
public void onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand) {
public boolean onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand) {
handler.call( () -> Arrays.asList(new EntityValue(player), new EntityValue(entity)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_DIES = new Event("player_dies", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_RESPAWNS = new Event("player_respawns", 1, false)
{
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_CHANGES_DIMENSION = new Event("player_changes_dimension", 5, false)
@@ -856,23 +898,25 @@ public class CarpetEventServer
};
public static final Event PLAYER_CONNECTS = new Event("player_connects", 1, false) {
@Override
public void onPlayerEvent(ServerPlayer player)
public boolean onPlayerEvent(ServerPlayer player)
{
handler.call( () -> Collections.singletonList(new EntityValue(player)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_DISCONNECTS = new Event("player_disconnects", 2, false) {
@Override
public void onPlayerMessage(ServerPlayer player, String message)
public boolean onPlayerMessage(ServerPlayer player, String message)
{
handler.call( () -> Arrays.asList(new EntityValue(player), new StringValue(message)), player::createCommandSourceStack);
return false;
}
};
public static final Event PLAYER_MESSAGE = new Event("player_message", 2, false) {
@Override
public void onPlayerMessage(ServerPlayer player, String message) {
handler.call( () -> Arrays.asList(new EntityValue(player), new StringValue(message)), player::createCommandSourceStack);
public boolean onPlayerMessage(ServerPlayer player, String message) {
return handler.call( () -> Arrays.asList(new EntityValue(player), new StringValue(message)), player::createCommandSourceStack);
}
};
@@ -1103,21 +1147,21 @@ public class CarpetEventServer
//stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types
public void onTick() { }
public void onChunkEvent(ServerLevel world, ChunkPos chPos, boolean generated) { }
public void onPlayerEvent(ServerPlayer player) { }
public void onPlayerMessage(ServerPlayer player, String message) { }
public boolean onPlayerEvent(ServerPlayer player) {return false;}
public boolean onPlayerMessage(ServerPlayer player, String message) {return false;}
public void onPlayerStatistic(ServerPlayer player, Stat<?> stat, int amount) { }
public void onMountControls(ServerPlayer player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { }
public void onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack) { }
public void onBlockAction(ServerPlayer player, BlockPos blockpos, Direction facing) { }
public void onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes) { }
public void onBlockBroken(ServerPlayer player, BlockPos pos, BlockState previousBS) { }
public void onBlockPlaced(ServerPlayer player, BlockPos pos, InteractionHand enumhand, ItemStack itemstack) { }
public void onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand) { }
public boolean onItemAction(ServerPlayer player, InteractionHand enumhand, ItemStack itemstack) {return false;}
public boolean onBlockAction(ServerPlayer player, BlockPos blockpos, Direction facing) {return false;}
public boolean onBlockHit(ServerPlayer player, InteractionHand enumhand, BlockHitResult hitRes) {return false;}
public boolean onBlockBroken(ServerPlayer player, BlockPos pos, BlockState previousBS) {return false;}
public boolean onBlockPlaced(ServerPlayer player, BlockPos pos, InteractionHand enumhand, ItemStack itemstack) {return false;}
public boolean onEntityHandAction(ServerPlayer player, Entity entity, InteractionHand enumhand) {return false;}
public void onHandAction(ServerPlayer player, InteractionHand enumhand) { }
public void onEntityAction(Entity entity, boolean created) { }
public void onDimensionChange(ServerPlayer player, Vec3 from, Vec3 to, ResourceKey<Level> fromDim, ResourceKey<Level> dimTo) {}
public void onDamage(Entity target, float amount, DamageSource source) { }
public void onRecipeSelected(ServerPlayer player, ResourceLocation recipe, boolean fullStack) {}
public boolean onDamage(Entity target, float amount, DamageSource source) {return false;}
public boolean onRecipeSelected(ServerPlayer player, ResourceLocation recipe, boolean fullStack) {return false;}
public void onSlotSwitch(ServerPlayer player, int from, int to) {}
public void onTrade(ServerPlayer player, Merchant merchant, MerchantOffer tradeOffer) {}
@@ -1221,8 +1265,8 @@ public class CarpetEventServer
if (executingHost == null) return CallbackResult.FAIL;
try
{
executingHost.callUDF(source.withPermission(CarpetSettings.runPermissionLevel), udf, argv);
return CallbackResult.SUCCESS;
Value returnValue = executingHost.callUDF(source.withPermission(CarpetSettings.runPermissionLevel), udf, argv);
return returnValue instanceof StringValue && returnValue.getString().equals("cancel") ? CallbackResult.CANCEL : CallbackResult.SUCCESS;
}
catch (NullPointerException | InvalidCallbackException | IntegrityException error)
{
@@ -1269,6 +1313,7 @@ public class CarpetEventServer
private void onEventAddedToHost(Event event, ScriptHost host)
{
if (event.deprecated()) host.issueDeprecation(event.name+" event");
event.handler.sortByPriority(this.scriptServer);
//return !(event.globalOnly && (host.perUser || host.parent != null));
}
@@ -75,6 +75,7 @@ public class CarpetScriptHost extends ScriptHost
private Tag globalState;
private int saveTimeout;
public boolean persistenceRequired;
public double eventPriority;
public Map<Value, Value> appConfig;
public Map<String, CommandArgument> appArgTypes;
@@ -298,6 +299,7 @@ public class CarpetScriptHost extends ScriptHost
setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player"));
persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.TRUE).getBoolean();
strict = config.getOrDefault(StringValue.of("strict"), Value.FALSE).getBoolean();
eventPriority = config.getOrDefault(new StringValue("event_priority"), Value.ZERO).readDoubleNumber();
// check requires
Value loadRequirements = config.get(new StringValue("requires"));
if (loadRequirements instanceof FunctionValue)
@@ -48,21 +48,22 @@ public class GlocalFlag extends ThreadLocal<Boolean>
return result;
}
public boolean runIfEnabled(Runnable action)
public <T> T runIfEnabled(Supplier<T> action)
{
synchronized (this)
{
if (get() != initial) return false;
if (get() != initial) return null;
set(!initial);
}
T result;
try
{
action.run();
result = action.get();
}
finally
{
set(initial);
}
return true;
return result;
}
}
@@ -177,6 +177,20 @@ __on_player_interacts_with_block(player, hand, block, face, hitvec) ->
print(' - face: '+face);
print(' - hitvec: '+map(hitvec, str('%.2f',_)))
);
__on_player_placing_block(player, item_tuple, hand, block) ->
(
l(item, count, nbt) = item_tuple || l('None', 0, null);
print('');
print('__on_player_placing_block(player, item_tuple, hand, block)');
print('player about to place a block:');
print(' - player: '+player);
print(' - block: '+block+' at '+map(pos(block), str('%.2f',_)));
print(' - hand: '+hand);
print(' - item:');
print(' > name: '+item);
print(' > count: '+count);
print(' > nbt: '+nbt);
);
__on_player_places_block(player, item_tuple, hand, block) ->
(
l(item, count, nbt) = item_tuple || l('None', 0, null);