diff --git a/packages/serenity/src/handlers/index.ts b/packages/serenity/src/handlers/index.ts index 53f7927f..32e7c3d6 100644 --- a/packages/serenity/src/handlers/index.ts +++ b/packages/serenity/src/handlers/index.ts @@ -21,6 +21,7 @@ import { NpcRequest } from "./npc-request"; import { BlockActorData } from "./block-actor-data"; import { BookEdit } from "./book-edit"; import { RequestChunkRadius } from "./request-chunk-radius"; +import { ServerboundDiagnostics } from "./serverbound-diagnostics"; const HANDLERS = [ RequestNetworkSettings, @@ -45,7 +46,8 @@ const HANDLERS = [ NpcRequest, BlockActorData, BookEdit, - RequestChunkRadius + RequestChunkRadius, + ServerboundDiagnostics ]; export { HANDLERS }; @@ -73,3 +75,5 @@ export * from "./respawn"; export * from "./npc-request"; export * from "./block-actor-data"; export * from "./book-edit"; +export * from "./request-chunk-radius"; +export * from "./serverbound-diagnostics"; diff --git a/packages/serenity/src/handlers/serverbound-diagnostics.ts b/packages/serenity/src/handlers/serverbound-diagnostics.ts new file mode 100644 index 00000000..2157e433 --- /dev/null +++ b/packages/serenity/src/handlers/serverbound-diagnostics.ts @@ -0,0 +1,45 @@ +import { + DisconnectReason, + ServerboundDiagnosticsPacket +} from "@serenityjs/protocol"; + +import { SerenityHandler } from "./serenity-handler"; + +import type { NetworkSession } from "@serenityjs/network"; + +class ServerboundDiagnostics extends SerenityHandler { + public static readonly packet = ServerboundDiagnosticsPacket.id; + + public static handle( + packet: ServerboundDiagnosticsPacket, + session: NetworkSession + ): void { + // Get the player from the session + // If there is no player, then disconnect the session. + const player = this.serenity.getPlayer(session); + if (!player) + return session.disconnect( + "Failed to connect due to an invalid player. Please try again.", + DisconnectReason.InvalidPlayer + ); + + // Get the diagnostics from the player + const diagnostics = player.diagnostics; + + // Set the diagnostics enabled property to true + if (!diagnostics.enabled) diagnostics.enabled = true; + + // Set the diagnostics properties + diagnostics.fps = packet.fps; + diagnostics.serverSimTickTime = packet.serverSimTickTime; + diagnostics.clientSimTickTime = packet.clientSimTickTime; + diagnostics.beginFrameTime = packet.beginFrameTime; + diagnostics.inputTime = packet.inputTime; + diagnostics.renderTime = packet.renderTime; + diagnostics.endFrameTime = packet.endFrameTime; + diagnostics.remainderTimePercent = packet.remainderTimePercent; + diagnostics.unaccountedTimePercent = packet.unaccountedTimePercent; + } +} + +export { ServerboundDiagnostics }; diff --git a/packages/world/src/player/diagnostics.ts b/packages/world/src/player/diagnostics.ts new file mode 100644 index 00000000..f1ed397b --- /dev/null +++ b/packages/world/src/player/diagnostics.ts @@ -0,0 +1,53 @@ +class PlayerDiagnostic { + /** + * Whether the diagnostic is enabled client-side. + */ + public enabled = false; + + /** + * The client's frames per second. + */ + public fps = 0; + + /** + * The server simulation tick time. + */ + public serverSimTickTime = 0; + + /** + * The client simulation tick time. + */ + public clientSimTickTime = 0; + + /** + * The time the frame began. + */ + public beginFrameTime = 0; + + /** + * The time the input was received. + */ + public inputTime = 0; + + /** + * The time the frame was rendered + */ + public renderTime = 0; + + /** + * The time the frame ended. + */ + public endFrameTime = 0; + + /** + * The remainder time percentage. + */ + public remainderTimePercent = 0; + + /** + * The percentage of time that was unaccounted for in the frame. + */ + public unaccountedTimePercent = 0; +} + +export { PlayerDiagnostic }; diff --git a/packages/world/src/player/index.ts b/packages/world/src/player/index.ts index 36df5cf3..e378b136 100644 --- a/packages/world/src/player/index.ts +++ b/packages/world/src/player/index.ts @@ -2,3 +2,4 @@ export * from "./player"; export * from "./device"; export * from "./status"; export * from "./options"; +export * from "./diagnostics"; diff --git a/packages/world/src/player/player.ts b/packages/world/src/player/player.ts index e1369283..16cb6b34 100644 --- a/packages/world/src/player/player.ts +++ b/packages/world/src/player/player.ts @@ -37,6 +37,7 @@ import { EntitySpawnedSignal, PlayerMissSwingSignal } from "../events"; import { PlayerStatus } from "./status"; import { Device } from "./device"; +import { PlayerDiagnostic } from "./diagnostics"; import type { ItemStack } from "../item"; import type { PlayerOptions } from "./options"; @@ -84,6 +85,12 @@ class Player extends Entity { */ public readonly device: Device; + /** + * The player's diagnostics. This is used to track the player's network latency, and other diagnostic information. + * This is only available when the client has `Enable Client Diagnostics` enabled in the creator settings. + */ + public readonly diagnostics = new PlayerDiagnostic(); + /** * The current status of the player's connection. */