Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3ba1ecb
add player death event
kxmpxtxnt Jan 19, 2025
6f9c9af
update paper implementation for death event
kxmpxtxnt Jan 19, 2025
337a506
changed Player to ServerPlayer
kxmpxtxnt Jan 20, 2025
07f4ede
set correct death message in the paper impl
kxmpxtxnt Jan 20, 2025
3fdd33a
implement PlayerEvent in PlayerDeathEvent
kxmpxtxnt Jan 20, 2025
7674ea3
add entity death event
kxmpxtxnt Jan 20, 2025
1d389dd
removed toVector + updated docs for ServerPlayer
kxmpxtxnt Jan 20, 2025
fddf2e5
moved comment to the right place
kxmpxtxnt Jan 20, 2025
567a7ae
moved player onDeath comment
kxmpxtxnt Jan 20, 2025
ff4be40
adding Player/Entity-EventTests to testmod
kxmpxtxnt Jan 20, 2025
37206ae
add conversion from paper DamageSource to mc DamageSource
kxmpxtxnt Jan 21, 2025
e520331
update usages of DamageSource conversion
kxmpxtxnt Jan 21, 2025
980c6bb
add mutable joinMessage to PostLoginEvent
kxmpxtxnt Jan 24, 2025
2d9cade
remove unused PlayerEvents imports
kxmpxtxnt Jan 28, 2025
45f7a7d
add chat event, remove unsued pos
kxmpxtxnt Jun 9, 2025
cf360b8
add paper implementation for chat event
kxmpxtxnt Jun 12, 2025
a85b90a
refine chat event (paper)
kxmpxtxnt Jun 12, 2025
ea1c662
add nullable message signature to chat event
kxmpxtxnt Jun 12, 2025
c1a6a89
removed unused pos from EntityDeathEvent, corrected to EntityDeathEve…
kxmpxtxnt Jun 12, 2025
90afef7
add modifiable messages to PlayerChatEvent
kxmpxtxnt Jun 22, 2025
9c38a74
removed debug messages
kxmpxtxnt Jun 22, 2025
5cb4628
add events to test events
kxmpxtxnt Jun 22, 2025
8fa8073
removed shadowed variables added by accident
kxmpxtxnt Jun 23, 2025
b6371fb
add EntitySpawnEvent
kxmpxtxnt Jun 23, 2025
615eeed
add native block conversion
kxmpxtxnt Jun 24, 2025
7ec20b8
add documentation for conversion
kxmpxtxnt Jun 24, 2025
18b2346
add PlayerInteractEvent, still misses entity on fabric side
kxmpxtxnt Jun 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,21 @@ private void onActuallyHurt(ServerLevel serverLevel,
EntityEvents.INSTANCE.getDamageLivingEntity()
.invoke(new EntityEvents.EntityDamageEvent((LivingEntity) (Object) this, amount, damageSource));
}

@Inject(
method = "die",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/damagesource/DamageSource;getEntity()Lnet/minecraft/world/entity/Entity;",
shift = At.Shift.BEFORE
)
)
private void die(DamageSource damageSource, CallbackInfo ci) {
var event = new EntityEvents.EntityDeathEvent(
(LivingEntity) (Object) this,
damageSource
);

EntityEvents.INSTANCE.getOnDeath().invoke(event);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.silkmc.silk.core.mixin.level;

import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.context.UseOnContext;
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.CallbackInfoReturnable;

@Mixin(BlockItem.class)
public class MixinBlockItem {

@Inject(
method = "useOn",
at = @At("HEAD"),
cancellable = true)
private void useOn(UseOnContext useOnContext, CallbackInfoReturnable<InteractionResult> cir) {
var player = useOnContext.getPlayer();

if (player == null) return;

var event = new PlayerEvents.PlayerInteractEvent(
useOnContext.getPlayer(),
useOnContext.getHand(),
useOnContext.getLevel().getBlockState(useOnContext.getClickedPos()).getBlock(),
null
);

PlayerEvents.INSTANCE.getOnInteract().invoke(event);

if (event.isCancelled().get()) {
cir.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.silkmc.silk.core.mixin.level;

import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.silkmc.silk.core.event.EntityEvents;
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;

@Mixin(ServerLevel.class)
public class MixinServerLevel {

@Inject(
method = "addEntity",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;addNewEntity(Lnet/minecraft/world/level/entity/EntityAccess;)Z",
shift = At.Shift.BEFORE
),
cancellable = true
)
private void addEntity(Entity entity, CallbackInfoReturnable<Boolean> cir) {
var event = new EntityEvents.EntitySpawnEvent(
entity
);

EntityEvents.INSTANCE.getOnSpawn().invoke(event);

if (event.isCancelled().get()) {
cir.cancel();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.silkmc.silk.core.mixin.player;

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 die(DamageSource damageSource, CallbackInfo ci, @Local LocalRef<Component> component) {
var event = new PlayerEvents.PlayerDeathEvent(
(ServerPlayer) (Object) this,
damageSource,
new EventScopeProperty<>(component.get()));

PlayerEvents.INSTANCE.getOnDeath().invoke(event);

component.set(event.getDeathMessage().get());
}

}
Original file line number Diff line number Diff line change
@@ -1,35 +1,78 @@
package net.silkmc.silk.core.mixin.server;

import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import net.minecraft.network.Connection;
import net.minecraft.network.chat.*;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.players.PlayerList;
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;

import java.util.function.Predicate;

@Mixin(PlayerList.class)
public class MixinPlayerList {

@Inject(
method = "placeNewPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;<init>(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/network/Connection;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/server/network/CommonListenerCookie;)V",
shift = At.Shift.BEFORE
)
)
private void onPreLogin(Connection connection, ServerPlayer player, CommonListenerCookie commonListenerCookie, CallbackInfo ci) {
PlayerEvents.INSTANCE.getPreLogin().invoke(new PlayerEvents.PlayerEvent<>(player));
}
@Inject(
method = "placeNewPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;<init>(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/network/Connection;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/server/network/CommonListenerCookie;)V",
shift = At.Shift.BEFORE
)
)
private void onPreLogin(Connection connection, ServerPlayer player, CommonListenerCookie commonListenerCookie, CallbackInfo ci) {
PlayerEvents.INSTANCE.getPreLogin().invoke(new PlayerEvents.PlayerEvent<>(player));
}

@Inject(
method = "placeNewPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V",
shift = At.Shift.BEFORE
)
)
private void onPostLogin(Connection connection, ServerPlayer player, CommonListenerCookie commonListenerCookie, CallbackInfo ci, @Local LocalRef<MutableComponent> mutableComponent) {
var event = new PlayerEvents.PostLoginEvent(player, new EventScopeProperty<>(mutableComponent.get()));
PlayerEvents.INSTANCE.getPostLogin().invoke(event);
mutableComponent.set(event.getJoinMessage().get().copy());
}

@Inject(
method = "placeNewPlayer",
at = @At("TAIL")
)
private void onPostLogin(Connection connection, ServerPlayer player, CommonListenerCookie commonListenerCookie, CallbackInfo ci) {
PlayerEvents.INSTANCE.getPostLogin().invoke(new PlayerEvents.PlayerEvent<>(player));
@Inject(
method = "broadcastChatMessage(Lnet/minecraft/network/chat/PlayerChatMessage;Ljava/util/function/Predicate;Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/network/chat/ChatType$Bound;)V",
at = @At(
value = "INVOKE",
target = "Ljava/util/List;iterator()Ljava/util/Iterator;"
),
cancellable = true
)
private void broadcastChatMessage(PlayerChatMessage playerChatMessage, Predicate<ServerPlayer> predicate, ServerPlayer serverPlayer, ChatType.Bound bound, CallbackInfo ci, @Local LocalRef<OutgoingChatMessage> outgoingChatMessage) {
var event = new PlayerEvents.PlayerChatEvent(serverPlayer, playerChatMessage, new EventScopeProperty<>(outgoingChatMessage.get().content()), bound);
PlayerEvents.INSTANCE.getOnChat().invoke(event);
if (event.isCancelled().get()) {
ci.cancel();
}

var message = new PlayerChatMessage(
playerChatMessage.link(),
playerChatMessage.signature(),
new SignedMessageBody(
event.getMessage().get().getString(),
playerChatMessage.signedBody().timeStamp(),
playerChatMessage.salt(),
LastSeenMessages.EMPTY
),
event.getChatMessage().unsignedContent(),
playerChatMessage.filterMask()
);

outgoingChatMessage.set(OutgoingChatMessage.create(message));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.silkmc.silk.core.annotations.ExperimentalSilkApi
import net.silkmc.silk.core.event.EntityEvents.checkInvulnerability

@ExperimentalSilkApi
object EntityEvents {
Expand Down Expand Up @@ -42,4 +43,23 @@ object EntityEvents {
* event, since Minecraft performs its checks more than one time
*/
val checkInvulnerability = Event.onlySync<EntityCheckInvulnerabilityEvent>()

open class EntityDeathEvent(
entity: LivingEntity,
val damageSource: DamageSource,
) : EntityEvent<LivingEntity>(entity)

/**
* Called when a [LivingEntity] dies.
*/
val onDeath = Event.syncAsync<EntityDeathEvent>()

open class EntitySpawnEvent(
entity: Entity,
) : EntityEvent<Entity>(entity), Cancellable {

override val isCancelled: EventScopeProperty<Boolean> = EventScopeProperty(false)
}

val onSpawn = Event.syncAsync<EntitySpawnEvent>()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package net.silkmc.silk.core.event

import com.mojang.authlib.GameProfile
import net.minecraft.network.chat.ChatType
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.PlayerChatMessage
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.block.Block
import net.silkmc.silk.core.annotations.ExperimentalSilkApi
import net.silkmc.silk.core.event.PlayerEvents.preQuit
import net.silkmc.silk.core.event.PlayerEvents.quitDuringConfiguration
import net.silkmc.silk.core.event.PlayerEvents.quitDuringLogin

@ExperimentalSilkApi
object PlayerEvents {
Expand All @@ -16,10 +25,15 @@ object PlayerEvents {
*/
val preLogin = Event.syncAsync<PlayerEvent<ServerPlayer>>()

open class PostLoginEvent(
player: ServerPlayer,
val joinMessage: EventScopeProperty<Component>
) : PlayerEvent<ServerPlayer>(player)

/**
* Called after a player has received all login information from the server.
*/
val postLogin = Event.syncAsync<PlayerEvent<ServerPlayer>>()
val postLogin = Event.syncAsync<PostLoginEvent>()

open class PlayerQuitEvent(
player: ServerPlayer,
Expand Down Expand Up @@ -60,4 +74,43 @@ object PlayerEvents {
* @see preQuit
*/
val quitDuringConfiguration = Event.syncAsync<PlayerQuitDuringLoginEvent>()

open class PlayerDeathEvent(
player: ServerPlayer,
val source: DamageSource,
var deathMessage: EventScopeProperty<Component>,
) : PlayerEvent<ServerPlayer>(player)

/**
* Called when a [ServerPlayer] dies.
*/
val onDeath = Event.syncAsync<PlayerDeathEvent>()

/**
* Called before a message from a player is broadcasted.
*/
open class PlayerChatEvent(
player: ServerPlayer,
val chatMessage: PlayerChatMessage,
var message: EventScopeProperty<Component>,
val bound: ChatType.Bound,
) : PlayerEvent<ServerPlayer>(player), Cancellable {

override val isCancelled: EventScopeProperty<Boolean> = EventScopeProperty(false)
}

val onChat = Event.syncAsync<PlayerChatEvent>()

open class PlayerInteractEvent(
player: Player,
val hand: InteractionHand,
val block: Block?,
val entity: Entity?,
) : PlayerEvent<Player>(player), Cancellable {

override val isCancelled: EventScopeProperty<Boolean> = EventScopeProperty(false)
}

val onInteract = Event.syncAsync<PlayerInteractEvent>()

}
3 changes: 3 additions & 0 deletions silk-core/src/main/resources/silk-core.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"block.AbstractBlockAccessor",
"entity.MixinEntity",
"entity.MixinLivingEntity",
"level.MixinBlockItem",
"level.MixinServerLevel",
"player.MixinServerPlayer",
"server.MixinMinecraftServer",
"server.MixinPlayerList",
"server.MixinServerConfigurationPacketListenerImpl",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ 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.block.Block
import org.bukkit.craftbukkit.CraftServer
import org.bukkit.craftbukkit.block.CraftBlock
import org.bukkit.craftbukkit.damage.CraftDamageSource
import org.bukkit.craftbukkit.entity.CraftEntity
import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.damage.DamageSource
import org.bukkit.entity.Entity
import org.bukkit.entity.Player

Expand All @@ -29,3 +33,16 @@ val Player.mcPlayer: ServerPlayer
*/
val Entity.mcEntity: net.minecraft.world.entity.Entity
get() = (this as CraftEntity).handle

@Suppress("UnstableApiUsage")
/**
* Converts a Paper [DamageSource] to a native [net.minecraft.world.damagesource.DamageSource].
*/
val DamageSource.mcDamageSource: net.minecraft.world.damagesource.DamageSource
get() = (this as CraftDamageSource).handle

/**
* Converts a Paper [Block] to a native [net.minecraft.world.level.block.Block].
*/
val Block.mcBlock: net.minecraft.world.level.block.Block?
get() = (this as CraftBlock).handle.getBlockIfLoaded(this.position)
Loading