diff --git a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinBehaviorUtils.java b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinBehaviorUtils.java new file mode 100644 index 00000000..ac9e2c5d --- /dev/null +++ b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinBehaviorUtils.java @@ -0,0 +1,41 @@ +package net.silkmc.silk.core.mixin.entity; + +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.behavior.BehaviorUtils; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; +import net.silkmc.silk.core.event.EntityEvents; +import net.silkmc.silk.core.event.EventScopeProperty; +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.CallbackInfo; + +@Mixin(BehaviorUtils.class) +public class MixinBehaviorUtils { + + @Inject( + method = "throwItem(Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;F)V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z", + shift = At.Shift.BEFORE + ), + cancellable = true + ) + private static void throwItem(LivingEntity livingEntity, ItemStack itemStack, Vec3 vec3, Vec3 vec32, float f, CallbackInfo ci, @Local ItemEntity itemEntity) { + final var event = new EntityEvents.EntityDropItemEvent( + livingEntity, + itemEntity, + new EventScopeProperty<>(false) + ); + + EntityEvents.INSTANCE.getDropItem().invoke(event); + + if (event.isCancelled().get()) { + ci.cancel(); + } + } +} diff --git a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinEntity.java b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinEntity.java index 3dd6ddc9..9843f2be 100644 --- a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinEntity.java +++ b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinEntity.java @@ -1,7 +1,10 @@ package net.silkmc.silk.core.mixin.entity; +import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; import net.silkmc.silk.core.event.EntityEvents; import net.silkmc.silk.core.event.EventScopeProperty; import org.spongepowered.asm.mixin.Mixin; @@ -33,4 +36,29 @@ private void onIsInvulnerableTo(DamageSource damageSource, cir.setReturnValue(event.isInvulnerable().get()); } } + + @Inject( + method = "spawnAtLocation(Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z", + shift = At.Shift.BEFORE + ), + cancellable = true + ) + private void onSpawnAtLocation(ItemStack itemStack, float f, CallbackInfoReturnable cir, @Local ItemEntity itemEntity) { + if (cir.getReturnValue() == null) return; + + final var event = new EntityEvents.EntityDropItemEvent( + (Entity) (Object) this, + itemEntity, + new EventScopeProperty<>(false) + ); + + EntityEvents.INSTANCE.getDropItem().invoke(event); + + if (event.isCancelled().get()) { + cir.setReturnValue(null); + } + } } diff --git a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinLivingEntity.java b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinLivingEntity.java index be4c6b97..bf6ac153 100644 --- a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinLivingEntity.java +++ b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinLivingEntity.java @@ -1,15 +1,27 @@ package net.silkmc.silk.core.mixin.entity; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.silkmc.silk.core.event.EntityEvents; +import net.silkmc.silk.core.event.EventScopeProperty; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LivingEntity.class) -public class MixinLivingEntity { +public abstract class MixinLivingEntity { + @Shadow public abstract ItemStack eat(Level level, ItemStack itemStack); + + @Unique + private Boolean isDroppingLoot = false; @Inject( method = "actuallyHurt", @@ -21,4 +33,78 @@ private void onActuallyHurt(DamageSource damageSource, EntityEvents.INSTANCE.getDamageLivingEntity() .invoke(new EntityEvents.EntityDamageEvent((LivingEntity) (Object) this, amount, damageSource)); } + + @Inject( + method = "createWitherRose", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z", + shift = At.Shift.BEFORE + ), + cancellable = true + ) + private void onCreateWitherRose(LivingEntity livingEntity, CallbackInfo ci, @Local ItemEntity itemEntity) { + // Death event - start + if (isDroppingLoot) { + ci.cancel(); + return; + } + // Death event - end + + // Drop item event - start + final var event = new EntityEvents.EntityDropItemEvent( + livingEntity, + itemEntity, + new EventScopeProperty<>(false) + ); + + EntityEvents.INSTANCE.getDropItem().invoke(event); + + if (event.isCancelled().get()) { + ci.cancel(); + } + // Drop item event - end + } + + @Inject( + method = "die", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/damagesource/DamageSource;getEntity()Lnet/minecraft/world/entity/Entity;", + shift = At.Shift.BEFORE + ), + cancellable = true + ) + private void onDie(DamageSource source, CallbackInfo ci) { + var entity = (LivingEntity) (Object) this; + final var event = new EntityEvents.EntityDeathEvent( + entity, + source, + new EventScopeProperty<>(true), + new EventScopeProperty<>(false) + ); + + EntityEvents.INSTANCE.getDeath().invoke(event); + + if (event.isCancelled().get()) { + entity.setHealth(1F); + ci.cancel(); + return; + } + + isDroppingLoot = event.isDroppingLoot().get(); + } + + @Inject( + method = "dropAllDeathLoot", + at = @At("HEAD"), + cancellable = true + ) + private void onDropAllDeathLoot(ServerLevel serverLevel, DamageSource damageSource, CallbackInfo ci) { + // Death event - start + if (isDroppingLoot) { + ci.cancel(); + } + // Death event - end + } } diff --git a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayer.java b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayer.java new file mode 100644 index 00000000..bfc289b5 --- /dev/null +++ b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayer.java @@ -0,0 +1,36 @@ +package net.silkmc.silk.core.mixin.entity; + +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.damagesource.DamageSource; +import net.silkmc.silk.core.event.EventScopeProperty; +import net.silkmc.silk.core.event.PlayerEvents; +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.CallbackInfo; + +@Mixin(ServerPlayer.class) +public class MixinServerPlayer { + + @Inject( + method = "die", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketSendListener;)V", + shift = At.Shift.BEFORE + ) + ) + private void onDie(DamageSource damageSource, CallbackInfo ci, @Local LocalRef component) { + var event = new PlayerEvents.PlayerDeathMessageEvent( + (ServerPlayer) (Object) this, + new EventScopeProperty<>(component.get()) + ); + + PlayerEvents.INSTANCE.getDeathMessage().invoke(event); + + component.set(event.getDeathMessage().get()); + } +} diff --git a/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayerGameMode.java b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayerGameMode.java new file mode 100644 index 00000000..5dc7efee --- /dev/null +++ b/silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayerGameMode.java @@ -0,0 +1,43 @@ +package net.silkmc.silk.core.mixin.entity; + +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.silkmc.silk.core.event.EventScopeProperty; +import net.silkmc.silk.core.event.PlayerEvents; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ServerPlayerGameMode.class) +public class MixinServerPlayerGameMode { + + @Shadow + @Final + protected ServerPlayer player; + + @Inject( + method = "destroyBlock", + 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;)Lnet/minecraft/world/level/block/state/BlockState;", + shift = At.Shift.BEFORE + ), + cancellable = true + ) + private void onBlockDestroy(BlockPos blockPos, CallbackInfoReturnable cir, @Local BlockEntity entity, @Local BlockState state) { + var event = new PlayerEvents.PlayerBlockBreakEvent(player, blockPos, state, entity, new EventScopeProperty<>(false)); + + PlayerEvents.INSTANCE.getBlockBreak().invoke(event); + + if (event.isCancelled().get()) { + cir.setReturnValue(false); + } + } +} diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/event/EntityEvents.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/event/EntityEvents.kt index 7c95085f..3ace8607 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/event/EntityEvents.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/event/EntityEvents.kt @@ -3,6 +3,8 @@ package net.silkmc.silk.core.event import net.minecraft.world.damagesource.DamageSource import net.minecraft.world.entity.Entity import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.item.ItemEntity +import net.minecraft.world.item.ItemStack import net.silkmc.silk.core.annotations.ExperimentalSilkApi @ExperimentalSilkApi @@ -42,4 +44,35 @@ object EntityEvents { * event, since Minecraft performs its checks more than one time */ val checkInvulnerability = Event.onlySync() + + open class EntityDropItemEvent( + entity: Entity, + val item: ItemEntity, + val isCancelled: EventScopeProperty + ) : EntityEvent(entity) + + /** + * Called when an entity is about to drop an item. + * This event allows listeners to modify the item that is being dropped or cancel the drop. + * + * Note: this event is not called for items that are dropped as a result of the entity dying + * + * TODO: detect player dropping on single player worlds + */ + val dropItem = Event.onlySync() + + open class EntityDeathEvent( + entity: LivingEntity, + val source: DamageSource, + val isDroppingLoot: EventScopeProperty, + val isCancelled: EventScopeProperty + ) : EntityEvent(entity) + + /** + * Called when a [LivingEntity] is about to die. + * This event allows listeners to prevent loot dropping or cancel the death. + * + * Note: player deaths cannot be canceled using this event + */ + val death = Event.onlySync() } diff --git a/silk-core/src/main/kotlin/net/silkmc/silk/core/event/PlayerEvents.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/event/PlayerEvents.kt index 41941a09..8f530929 100644 --- a/silk-core/src/main/kotlin/net/silkmc/silk/core/event/PlayerEvents.kt +++ b/silk-core/src/main/kotlin/net/silkmc/silk/core/event/PlayerEvents.kt @@ -1,9 +1,13 @@ package net.silkmc.silk.core.event import com.mojang.authlib.GameProfile +import net.minecraft.core.BlockPos import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerPlayer import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState import net.silkmc.silk.core.annotations.ExperimentalSilkApi @ExperimentalSilkApi @@ -60,4 +64,27 @@ object PlayerEvents { * @see preQuit */ val quitDuringConfiguration = Event.syncAsync() + + open class PlayerDeathMessageEvent( + player: ServerPlayer, + val deathMessage: EventScopeProperty, + ): PlayerEvent(player) + + /** + * Called when a player dies and sends a death message. This event will not trigger if death messages are disabled. + */ + val deathMessage = Event.syncAsync() + + open class PlayerBlockBreakEvent( + player: Player, + val blockPos: BlockPos, + val blockState: BlockState, + val blockEntity: BlockEntity?, + val isCancelled: EventScopeProperty, + ): PlayerEvent(player) + + /** + * Called when a player breaks a block. This event is triggered before the block is broken. + */ + val blockBreak = Event.syncAsync() } diff --git a/silk-core/src/main/resources/silk-core.mixins.json b/silk-core/src/main/resources/silk-core.mixins.json index ec54db7e..1cc9b324 100644 --- a/silk-core/src/main/resources/silk-core.mixins.json +++ b/silk-core/src/main/resources/silk-core.mixins.json @@ -4,8 +4,11 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "block.AbstractBlockAccessor", + "entity.MixinBehaviorUtils", "entity.MixinEntity", "entity.MixinLivingEntity", + "entity.MixinServerPlayer", + "entity.MixinServerPlayerGameMode", "server.MixinMinecraftServer", "server.MixinPlayerList", "server.MixinServerConfigurationPacketListenerImpl", diff --git a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/conversions/NativeMcConversions.kt b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/conversions/NativeMcConversions.kt index 71c3ac09..3eeef479 100644 --- a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/conversions/NativeMcConversions.kt +++ b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/conversions/NativeMcConversions.kt @@ -1,9 +1,13 @@ package net.silkmc.silk.paper.conversions +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer +import net.minecraft.core.RegistryAccess +import net.minecraft.network.chat.Component import net.minecraft.server.MinecraftServer import net.minecraft.server.dedicated.DedicatedServer import net.minecraft.server.level.ServerEntity import net.minecraft.server.level.ServerPlayer +import net.silkmc.silk.core.text.serializeToPrettyJson import org.bukkit.Server import org.bukkit.craftbukkit.CraftServer import org.bukkit.craftbukkit.entity.CraftEntity @@ -29,3 +33,9 @@ val Player.mcPlayer: ServerPlayer */ val Entity.mcEntity: net.minecraft.world.entity.Entity get() = (this as CraftEntity).handle + +/** + * Converts a [Component] to an adventure [net.kyori.adventure.text.Component]. + */ +val Component.adventureComponent: net.kyori.adventure.text.Component + get() = GsonComponentSerializer.gson().deserialize(Component.Serializer.toJson(this, RegistryAccess.EMPTY)) diff --git a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt index 9a69a1ad..807aed3c 100644 --- a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt +++ b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt @@ -6,7 +6,10 @@ import net.silkmc.silk.core.event.EventScopeProperty import net.silkmc.silk.paper.conversions.mcEntity import net.silkmc.silk.paper.events.listenSilk import org.bukkit.craftbukkit.damage.CraftDamageSource +import org.bukkit.craftbukkit.entity.CraftItem import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.entity.EntityDeathEvent +import org.bukkit.event.entity.EntityDropItemEvent fun EntityEvents.setupPaper() { listenSilk { paperEvent -> @@ -29,5 +32,40 @@ fun EntityEvents.setupPaper() { amount = paperEvent.finalDamage.toFloat(), source = mcDamageSource)) } + // Damage event - end + } + + listenSilk { + val dropEvent = EntityEvents.EntityDropItemEvent( + it.entity.mcEntity, + (it.itemDrop as CraftItem).handle, + EventScopeProperty(it.isCancelled) + ) + + dropItem.invoke(dropEvent) + + if (dropEvent.isCancelled.get()) { + it.isCancelled = true + } + } + + listenSilk { + val deathEvent = EntityEvents.EntityDeathEvent( + it.entity.mcEntity as LivingEntity, + (it.damageSource as CraftDamageSource).handle, + EventScopeProperty(it.drops.isNotEmpty()), + EventScopeProperty(it.isCancelled) + ) + + death.invoke(deathEvent) + + if (deathEvent.isCancelled.get()) { + it.isCancelled = true + } + + if (!deathEvent.isDroppingLoot.get()) { + it.drops.clear() + it.droppedExp = 0 + } } } diff --git a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/PlayerEvents.kt b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/PlayerEvents.kt index 14b6a3ac..8034de70 100644 --- a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/PlayerEvents.kt +++ b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/PlayerEvents.kt @@ -1,9 +1,14 @@ package net.silkmc.silk.paper.events.internal import io.papermc.paper.adventure.AdventureComponent +import net.silkmc.silk.core.event.EventScopeProperty import net.silkmc.silk.core.event.PlayerEvents +import net.silkmc.silk.paper.conversions.adventureComponent import net.silkmc.silk.paper.conversions.mcPlayer import net.silkmc.silk.paper.events.listenSilk +import org.bukkit.craftbukkit.block.CraftBlock +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.entity.PlayerDeathEvent import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerLoginEvent import org.bukkit.event.player.PlayerQuitEvent @@ -12,10 +17,36 @@ fun PlayerEvents.setupPaper() { listenSilk { preLogin.invoke(PlayerEvents.PlayerEvent(it.player.mcPlayer)) } + listenSilk { postLogin.invoke(PlayerEvents.PlayerEvent(it.player.mcPlayer)) } + listenSilk { preQuit.invoke(PlayerEvents.PlayerQuitEvent(it.player.mcPlayer, AdventureComponent(it.quitMessage()))) } + + listenSilk { + val event = PlayerEvents.PlayerDeathMessageEvent(it.entity.mcPlayer, EventScopeProperty(AdventureComponent(it.deathMessage()))) + deathMessage.invoke(event) + it.deathMessage(event.deathMessage.get().adventureComponent) + } + + listenSilk { paperEvent -> + val craftBlock = paperEvent.block as CraftBlock + val blockPos = craftBlock.position + + val checkEvent = PlayerEvents.PlayerBlockBreakEvent( + paperEvent.player.mcPlayer, + blockPos, + craftBlock.nms, + craftBlock.handle.getBlockEntity(blockPos), + EventScopeProperty(paperEvent.isCancelled) + ) + blockBreak.invoke(checkEvent) + + if (checkEvent.isCancelled.get()) { + paperEvent.isCancelled = true + } + } } diff --git a/silk-testmod/src/main/kotlin/net/silkmc/silk/test/Manager.kt b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/Manager.kt index 2271e9f4..9cfa8c01 100644 --- a/silk-testmod/src/main/kotlin/net/silkmc/silk/test/Manager.kt +++ b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/Manager.kt @@ -8,6 +8,8 @@ import net.silkmc.silk.commands.LiteralCommandBuilder import net.silkmc.silk.commands.clientCommand import net.silkmc.silk.commands.command import net.silkmc.silk.test.commands.* +import net.silkmc.silk.test.events.EntityEventTest +import net.silkmc.silk.test.events.PlayerEventTest import net.silkmc.silk.test.events.ServerEventTest import net.silkmc.silk.test.network.NetworkTest @@ -27,6 +29,8 @@ object Manager : ModInitializer, ClientModInitializer { executeCommandTestCommand ServerEventTest.init() + EntityEventTest.init() + PlayerEventTest.init() NetworkTest.initServer() command("testmod") { diff --git a/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/EntityEventTest.kt b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/EntityEventTest.kt new file mode 100644 index 00000000..29031cdc --- /dev/null +++ b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/EntityEventTest.kt @@ -0,0 +1,19 @@ +package net.silkmc.silk.test.events + +import net.silkmc.silk.core.annotations.ExperimentalSilkApi +import net.silkmc.silk.core.event.Entity +import net.silkmc.silk.core.event.Events +import net.silkmc.silk.core.logging.logInfo + +@OptIn(ExperimentalSilkApi::class) +object EntityEventTest { + fun init() { + Events.Entity.dropItem.listen { event -> + logInfo("received Entity.dropItem event (Item: ${event.item.item.displayName.string})") + } + + Events.Entity.death.listen { event -> + logInfo("received Entity.death event (Source: ${event.source}, Target: ${event.entity.type.description.string})") + } + } +} \ No newline at end of file diff --git a/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt new file mode 100644 index 00000000..d2c0aa37 --- /dev/null +++ b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt @@ -0,0 +1,22 @@ +package net.silkmc.silk.test.events + +import net.silkmc.silk.core.annotations.ExperimentalSilkApi +import net.silkmc.silk.core.event.Entity +import net.silkmc.silk.core.event.Events +import net.silkmc.silk.core.event.Player +import net.silkmc.silk.core.logging.logInfo +import net.silkmc.silk.core.text.literalText + +@OptIn(ExperimentalSilkApi::class) +object PlayerEventTest { + fun init() { + Events.Player.deathMessage.listen { event -> + logInfo("received Player.deathMessage event") + event.deathMessage.set(literalText("This is a test death message")) + } + + Events.Player.blockBreak.listen { event -> + logInfo("received Player.blockBreak event (Pos: ${event.blockPos}, State: ${event.blockState}, Entity: ${event.blockEntity})") + } + } +} \ No newline at end of file