diff --git a/packages/serenity/src/events/index.ts b/packages/serenity/src/events/index.ts index d6a30e9c..5fa15cf2 100644 --- a/packages/serenity/src/events/index.ts +++ b/packages/serenity/src/events/index.ts @@ -3,6 +3,7 @@ export * from "./event-signal"; // Concrete player signals export * from "./player-joined"; +export * from "./player-leave"; export * from "./player-spawned"; export * from "./player-chat"; export * from "./player-place-block"; @@ -27,12 +28,14 @@ import { PlayerDroppedItemSignal } from "./player-dropped-item"; import { EntitySpawnedSignal } from "./entity-spawned"; import { CommandExecutedSignal } from "./command-executed"; import { DialogueResponseSignal } from "./dialogue-response"; +import { PlayerLeaveSignal } from "./player-leave"; /** * Contains all the event signals. */ const EVENT_SIGNALS = [ PlayerJoinedSignal, + PlayerLeaveSignal, PlayerSpawnedSignal, PlayerChatSignal, PlayerPlaceBlockSignal, diff --git a/packages/serenity/src/events/player-leave.ts b/packages/serenity/src/events/player-leave.ts new file mode 100644 index 00000000..81268040 --- /dev/null +++ b/packages/serenity/src/events/player-leave.ts @@ -0,0 +1,80 @@ +import { + type DisconnectReason, + Packet, + type DisconnectPacket +} from "@serenityjs/protocol"; + +import { EventSignal } from "./event-signal"; +import { EventPriority } from "./priority"; + +import type { NetworkPacketEvent } from "@serenityjs/network"; +import type { Serenity } from "../serenity"; +import type { Player } from "@serenityjs/world"; + +/** + * The player leave signal. + * @note This signal is emitted after the player has left the game. Before hooking will not trigger any effects. + */ +class PlayerLeaveSignal extends EventSignal { + /** + * The serenity instance. + */ + public static serenity: Serenity; + + /** + * The packet of the event signal. + */ + public static readonly hook = Packet.Disconnect; + + public static readonly priority = EventPriority.Before; + + /** + * The player that joined the game. + */ + public readonly player: Player; + + /** + * The reason for the player disconnect + */ + public readonly reason: DisconnectReason; + + /** + * The message to display when the player disconnects + */ + public readonly message: string; + + /** + * Constructs a new player leave signal instance. + * @param player The player that left the game. + * @param reason The reason for the player disconnect + * @param message The message to display when the player disconnects + */ + public constructor( + player: Player, + reason: DisconnectReason, + message: string + ) { + super(); + this.player = player; + this.reason = reason; + this.message = message; + } + + public static logic(data: NetworkPacketEvent): boolean { + // Separate the data into variables. + const { session, packet } = data; + + // Get the player from the session. + // If there is no player, then ignore the signal. + const player = this.serenity.getPlayer(session); + if (!player) return true; + + // Create a new signal instance + const signal = new this(player, packet.reason, packet.message); + + // Emit the signal + return this.serenity.emit("PlayerLeave", signal); + } +} + +export { PlayerLeaveSignal }; diff --git a/packages/serenity/src/events/signals.ts b/packages/serenity/src/events/signals.ts index c341721c..99573b16 100644 --- a/packages/serenity/src/events/signals.ts +++ b/packages/serenity/src/events/signals.ts @@ -1,3 +1,4 @@ +import type { PlayerLeaveSignal } from "./player-leave"; import type { DialogueResponseSignal } from "./dialogue-response"; import type { CommandExecutedSignal } from "./command-executed"; import type { PlayerStartedBreakingBlockSignal } from "./player-started-breaking-block"; @@ -12,6 +13,7 @@ import type { PlayerPlaceBlockSignal } from "./player-place-block"; interface EventSignals { PlayerJoined: [PlayerJoinedSignal]; + PlayerLeave: [PlayerLeaveSignal]; PlayerSpawned: [PlayerSpawnedSignal]; PlayerChat: [PlayerChatSignal]; PlayerPlaceBlock: [PlayerPlaceBlockSignal];