From a0ffa1deb7f750f78c60b116f4d538d22678526c Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Tue, 27 Aug 2024 19:25:13 +0200 Subject: [PATCH 1/5] Implement Silk entity events in paper module --- .../paper/conversions/NativeMcConversions.kt | 9 +++++++++ .../paper/events/internal/EntityEvents.kt | 19 +++++++++++++++++++ .../paper/internal/SilkPaperEntrypoint.kt | 2 ++ 3 files changed, 30 insertions(+) create mode 100644 silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt 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 34348c81..71c3ac09 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 @@ -2,10 +2,13 @@ package net.silkmc.silk.paper.conversions import net.minecraft.server.MinecraftServer import net.minecraft.server.dedicated.DedicatedServer +import net.minecraft.server.level.ServerEntity import net.minecraft.server.level.ServerPlayer import org.bukkit.Server import org.bukkit.craftbukkit.CraftServer +import org.bukkit.craftbukkit.entity.CraftEntity import org.bukkit.craftbukkit.entity.CraftPlayer +import org.bukkit.entity.Entity import org.bukkit.entity.Player /** @@ -20,3 +23,9 @@ val Server.mcServer: MinecraftServer */ val Player.mcPlayer: ServerPlayer get() = (this as CraftPlayer).handle + +/** + * Converts a Paper [Entity] to a native [net.minecraft.world.entity.Entity]. The instance should be a [ServerEntity], but this api does not guarantee that. + */ +val Entity.mcEntity: net.minecraft.world.entity.Entity + get() = (this as CraftEntity).handle 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 new file mode 100644 index 00000000..04b8fe80 --- /dev/null +++ b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/EntityEvents.kt @@ -0,0 +1,19 @@ +package net.silkmc.silk.paper.events.internal + +import net.minecraft.world.entity.LivingEntity +import net.silkmc.silk.core.event.EntityEvents +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.event.entity.EntityDamageEvent + +fun EntityEvents.setupPaper() { + listenSilk { + val mcEntity = it.entity.mcEntity + checkInvulnerability.invoke(EntityEvents.EntityCheckInvulnerabilityEvent(mcEntity, (it.damageSource as CraftDamageSource).handle, EventScopeProperty(it.isCancelled))) + if (mcEntity is LivingEntity) { + damageLivingEntity.invoke(EntityEvents.EntityDamageEvent(mcEntity, it.finalDamage.toFloat(), (it.damageSource as CraftDamageSource).handle)) + } + } +} \ No newline at end of file diff --git a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/internal/SilkPaperEntrypoint.kt b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/internal/SilkPaperEntrypoint.kt index 88e3e883..29c3d18c 100644 --- a/silk-paper/src/main/kotlin/net/silkmc/silk/paper/internal/SilkPaperEntrypoint.kt +++ b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/internal/SilkPaperEntrypoint.kt @@ -1,6 +1,7 @@ package net.silkmc.silk.paper.internal import net.silkmc.silk.core.annotations.InternalSilkApi +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.event.Server @@ -21,6 +22,7 @@ class SilkPaperEntrypoint : JavaPlugin(), Listener { Events.Server.setupPaper() Events.Player.setupPaper() + Events.Entity.setupPaper() } override fun onEnable() { From 8d35a094dc8dcf4ff79e10b592ac1b5462dbaf61 Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Sun, 1 Sep 2024 20:34:09 +0200 Subject: [PATCH 2/5] Adding multiple player and entity events PlayerEvents: deathMessage EntityEvents: dropItem, death --- .../core/mixin/entity/MixinBehaviorUtils.java | 41 +++++++++ .../silk/core/mixin/entity/MixinEntity.java | 28 ++++++ .../core/mixin/entity/MixinLivingEntity.java | 88 ++++++++++++++++++- .../core/mixin/entity/MixinServerPlayer.java | 36 ++++++++ .../silkmc/silk/core/event/EntityEvents.kt | 33 +++++++ .../silkmc/silk/core/event/PlayerEvents.kt | 11 +++ .../src/main/resources/silk-core.mixins.json | 2 + 7 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinBehaviorUtils.java create mode 100644 silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayer.java 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/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..6a7220cd 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 @@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile 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.silkmc.silk.core.annotations.ExperimentalSilkApi @ExperimentalSilkApi @@ -60,4 +61,14 @@ 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() } diff --git a/silk-core/src/main/resources/silk-core.mixins.json b/silk-core/src/main/resources/silk-core.mixins.json index ec54db7e..18a2270f 100644 --- a/silk-core/src/main/resources/silk-core.mixins.json +++ b/silk-core/src/main/resources/silk-core.mixins.json @@ -4,8 +4,10 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "block.AbstractBlockAccessor", + "entity.MixinBehaviorUtils", "entity.MixinEntity", "entity.MixinLivingEntity", + "entity.MixinServerPlayer", "server.MixinMinecraftServer", "server.MixinPlayerList", "server.MixinServerConfigurationPacketListenerImpl", From 30cd9d4c4485d9b25e96d7540ffbc254e5722ce7 Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Sun, 1 Sep 2024 20:35:14 +0200 Subject: [PATCH 3/5] Adding paper implementation for new events --- .../paper/conversions/NativeMcConversions.kt | 10 ++++ .../paper/events/internal/EntityEvents.kt | 56 ++++++++++++++++++- .../paper/events/internal/PlayerEvents.kt | 12 ++++ 3 files changed, 77 insertions(+), 1 deletion(-) 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 04b8fe80..a9bb3024 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,14 +6,68 @@ 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 { val mcEntity = it.entity.mcEntity - checkInvulnerability.invoke(EntityEvents.EntityCheckInvulnerabilityEvent(mcEntity, (it.damageSource as CraftDamageSource).handle, EventScopeProperty(it.isCancelled))) + + // Check invulnerability event - start + val checkInvulnerabilityEvent = EntityEvents.EntityCheckInvulnerabilityEvent( + mcEntity, + (it.damageSource as CraftDamageSource).handle, + EventScopeProperty(false) + ) + + checkInvulnerability.invoke(checkInvulnerabilityEvent) + + if (checkInvulnerabilityEvent.isInvulnerable.get()) { + it.isCancelled = true + return@listenSilk + } + // Check invulnerability event - end + + // Damage event - start if (mcEntity is LivingEntity) { damageLivingEntity.invoke(EntityEvents.EntityDamageEvent(mcEntity, it.finalDamage.toFloat(), (it.damageSource as CraftDamageSource).handle)) } + // 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 + } } } \ No newline at end of file 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..562fd782 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,13 @@ 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.core.text.LiteralTextBuilder +import net.silkmc.silk.paper.conversions.adventureComponent import net.silkmc.silk.paper.conversions.mcPlayer import net.silkmc.silk.paper.events.listenSilk +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 +16,18 @@ 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) + } } From a8c017e7c96acf7388e7deae8d17613354923946 Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Sun, 1 Sep 2024 20:36:05 +0200 Subject: [PATCH 4/5] Add new event testing --- .../kotlin/net/silkmc/silk/test/Manager.kt | 4 ++++ .../silk/test/events/EntityEventTest.kt | 19 +++++++++++++++++++ .../silk/test/events/PlayerEventTest.kt | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/EntityEventTest.kt create mode 100644 silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt 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..057b96e0 --- /dev/null +++ b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt @@ -0,0 +1,18 @@ +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")) + } + } +} \ No newline at end of file From 80629ffcc896557856f3a2ab93ee857ac6def211 Mon Sep 17 00:00:00 2001 From: Miraculixx Date: Sat, 7 Sep 2024 15:00:41 +0200 Subject: [PATCH 5/5] Add player block break event Added on fabric and paper side --- .../entity/MixinServerPlayerGameMode.java | 43 +++++++++++++++++++ .../silkmc/silk/core/event/PlayerEvents.kt | 16 +++++++ .../src/main/resources/silk-core.mixins.json | 1 + .../paper/events/internal/PlayerEvents.kt | 21 ++++++++- .../silk/test/events/PlayerEventTest.kt | 4 ++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 silk-core/src/main/java/net/silkmc/silk/core/mixin/entity/MixinServerPlayerGameMode.java 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/PlayerEvents.kt b/silk-core/src/main/kotlin/net/silkmc/silk/core/event/PlayerEvents.kt index 6a7220cd..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,10 +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 @@ -71,4 +74,17 @@ object PlayerEvents { * 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 18a2270f..1cc9b324 100644 --- a/silk-core/src/main/resources/silk-core.mixins.json +++ b/silk-core/src/main/resources/silk-core.mixins.json @@ -8,6 +8,7 @@ "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/events/internal/PlayerEvents.kt b/silk-paper/src/main/kotlin/net/silkmc/silk/paper/events/internal/PlayerEvents.kt index 562fd782..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 @@ -3,10 +3,11 @@ 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.core.text.LiteralTextBuilder 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 @@ -30,4 +31,22 @@ fun PlayerEvents.setupPaper() { 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/events/PlayerEventTest.kt b/silk-testmod/src/main/kotlin/net/silkmc/silk/test/events/PlayerEventTest.kt index 057b96e0..d2c0aa37 100644 --- 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 @@ -14,5 +14,9 @@ object PlayerEventTest { 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