@babylonjs-toolkit/server 0.0.0-stage → 9.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/CUSTOM.md +1690 -0
  2. package/DEPLOY.md +2012 -0
  3. package/README.md +1081 -2
  4. package/lib/AbuseLimits.d.ts +64 -0
  5. package/lib/AbuseLimits.js +121 -0
  6. package/lib/Authentication.d.ts +86 -0
  7. package/lib/Authentication.js +272 -0
  8. package/lib/BootSessionOwner.d.ts +86 -0
  9. package/lib/BootSessionOwner.js +116 -0
  10. package/lib/ColyseusLoggerAdapter.d.ts +32 -0
  11. package/lib/ColyseusLoggerAdapter.js +73 -0
  12. package/lib/DedicatedSceneRegistry.d.ts +52 -0
  13. package/lib/DedicatedSceneRegistry.js +74 -0
  14. package/lib/DedicatedSession.d.ts +152 -0
  15. package/lib/DedicatedSession.js +173 -0
  16. package/lib/DualTransport.d.ts +112 -0
  17. package/lib/DualTransport.js +197 -0
  18. package/lib/GameServer.d.ts +64 -0
  19. package/lib/GameServer.js +647 -0
  20. package/lib/GameServerConfig.d.ts +387 -0
  21. package/lib/GameServerConfig.js +1059 -0
  22. package/lib/GameServerLobbyRoom.d.ts +33 -0
  23. package/lib/GameServerLobbyRoom.js +56 -0
  24. package/lib/GameServerLogger.d.ts +111 -0
  25. package/lib/GameServerLogger.js +190 -0
  26. package/lib/GameServerMetrics.d.ts +158 -0
  27. package/lib/GameServerMetrics.js +276 -0
  28. package/lib/GameServerState.d.ts +164 -0
  29. package/lib/GameServerState.js +26 -0
  30. package/lib/HeadlessRuntime.d.ts +316 -0
  31. package/lib/HeadlessRuntime.js +380 -0
  32. package/lib/JoinGate.d.ts +34 -0
  33. package/lib/JoinGate.js +221 -0
  34. package/lib/NetworkAddresses.d.ts +33 -0
  35. package/lib/NetworkAddresses.js +90 -0
  36. package/lib/PlayerTokens.d.ts +17 -0
  37. package/lib/PlayerTokens.js +24 -0
  38. package/lib/RoomServerTransport.d.ts +73 -0
  39. package/lib/RoomServerTransport.js +90 -0
  40. package/lib/ServerEndpoints.d.ts +133 -0
  41. package/lib/ServerEndpoints.js +247 -0
  42. package/lib/ServerInit.d.ts +28 -0
  43. package/lib/ServerInit.js +113 -0
  44. package/lib/SessionRoom.d.ts +391 -0
  45. package/lib/SessionRoom.js +930 -0
  46. package/lib/TlsCertificates.d.ts +35 -0
  47. package/lib/TlsCertificates.js +130 -0
  48. package/lib/admin/AccountRoutes.d.ts +8 -0
  49. package/lib/admin/AccountRoutes.js +502 -0
  50. package/lib/admin/AdminAccounts.d.ts +329 -0
  51. package/lib/admin/AdminAccounts.js +607 -0
  52. package/lib/admin/AdminApiRouter.d.ts +202 -0
  53. package/lib/admin/AdminApiRouter.js +465 -0
  54. package/lib/admin/AdminApiTypes.d.ts +1782 -0
  55. package/lib/admin/AdminApiTypes.js +4 -0
  56. package/lib/admin/AdminAudit.d.ts +113 -0
  57. package/lib/admin/AdminAudit.js +266 -0
  58. package/lib/admin/AdminCli.d.ts +7 -0
  59. package/lib/admin/AdminCli.js +351 -0
  60. package/lib/admin/AdminClock.d.ts +51 -0
  61. package/lib/admin/AdminClock.js +10 -0
  62. package/lib/admin/AdminConsole.d.ts +39 -0
  63. package/lib/admin/AdminConsole.js +325 -0
  64. package/lib/admin/AdminConstants.d.ts +147 -0
  65. package/lib/admin/AdminConstants.js +167 -0
  66. package/lib/admin/AdminCrypto.d.ts +19 -0
  67. package/lib/admin/AdminCrypto.js +29 -0
  68. package/lib/admin/AdminExtensionsHost.d.ts +176 -0
  69. package/lib/admin/AdminExtensionsHost.js +307 -0
  70. package/lib/admin/AdminPasswords.d.ts +33 -0
  71. package/lib/admin/AdminPasswords.js +92 -0
  72. package/lib/admin/AdminPermissions.d.ts +28 -0
  73. package/lib/admin/AdminPermissions.js +74 -0
  74. package/lib/admin/AdminRecords.d.ts +196 -0
  75. package/lib/admin/AdminRecords.js +3 -0
  76. package/lib/admin/AdminRequestBodies.d.ts +47 -0
  77. package/lib/admin/AdminRequestBodies.js +84 -0
  78. package/lib/admin/AdminRequestGate.d.ts +38 -0
  79. package/lib/admin/AdminRequestGate.js +59 -0
  80. package/lib/admin/AdminResponses.d.ts +103 -0
  81. package/lib/admin/AdminResponses.js +164 -0
  82. package/lib/admin/AdminServices.d.ts +99 -0
  83. package/lib/admin/AdminServices.js +4 -0
  84. package/lib/admin/AdminStaticFiles.d.ts +29 -0
  85. package/lib/admin/AdminStaticFiles.js +97 -0
  86. package/lib/admin/AdminStore.d.ts +152 -0
  87. package/lib/admin/AdminStore.js +46 -0
  88. package/lib/admin/AlertChannels.d.ts +43 -0
  89. package/lib/admin/AlertChannels.js +120 -0
  90. package/lib/admin/AlertEngine.d.ts +231 -0
  91. package/lib/admin/AlertEngine.js +681 -0
  92. package/lib/admin/AlertRoutes.d.ts +8 -0
  93. package/lib/admin/AlertRoutes.js +180 -0
  94. package/lib/admin/ApiKeyRoutes.d.ts +8 -0
  95. package/lib/admin/ApiKeyRoutes.js +53 -0
  96. package/lib/admin/ApiKeys.d.ts +63 -0
  97. package/lib/admin/ApiKeys.js +167 -0
  98. package/lib/admin/AuditRoutes.d.ts +8 -0
  99. package/lib/admin/AuditRoutes.js +106 -0
  100. package/lib/admin/AuthToolRoutes.d.ts +13 -0
  101. package/lib/admin/AuthToolRoutes.js +164 -0
  102. package/lib/admin/BanList.d.ts +87 -0
  103. package/lib/admin/BanList.js +152 -0
  104. package/lib/admin/CborDecoder.d.ts +18 -0
  105. package/lib/admin/CborDecoder.js +201 -0
  106. package/lib/admin/ColyseusCloudClient.d.ts +313 -0
  107. package/lib/admin/ColyseusCloudClient.js +272 -0
  108. package/lib/admin/ColyseusCloudRoutes.d.ts +8 -0
  109. package/lib/admin/ColyseusCloudRoutes.js +91 -0
  110. package/lib/admin/ColyseusCloudService.d.ts +180 -0
  111. package/lib/admin/ColyseusCloudService.js +476 -0
  112. package/lib/admin/ColyseusCloudSettings.d.ts +16 -0
  113. package/lib/admin/ColyseusCloudSettings.js +29 -0
  114. package/lib/admin/ColyseusCloudViews.d.ts +105 -0
  115. package/lib/admin/ColyseusCloudViews.js +371 -0
  116. package/lib/admin/CommonPasswords.d.ts +6 -0
  117. package/lib/admin/CommonPasswords.js +19 -0
  118. package/lib/admin/ConfigOverrides.d.ts +174 -0
  119. package/lib/admin/ConfigOverrides.js +406 -0
  120. package/lib/admin/ConfigRoutes.d.ts +8 -0
  121. package/lib/admin/ConfigRoutes.js +91 -0
  122. package/lib/admin/ConsoleSessions.d.ts +112 -0
  123. package/lib/admin/ConsoleSessions.js +201 -0
  124. package/lib/admin/DedicatedRoutes.d.ts +21 -0
  125. package/lib/admin/DedicatedRoutes.js +254 -0
  126. package/lib/admin/ExtensionRoutes.d.ts +8 -0
  127. package/lib/admin/ExtensionRoutes.js +145 -0
  128. package/lib/admin/FileAdminStore.d.ts +157 -0
  129. package/lib/admin/FileAdminStore.js +349 -0
  130. package/lib/admin/FleetDirectory.d.ts +323 -0
  131. package/lib/admin/FleetDirectory.js +489 -0
  132. package/lib/admin/FleetLeader.d.ts +43 -0
  133. package/lib/admin/FleetLeader.js +89 -0
  134. package/lib/admin/FleetRoutes.d.ts +55 -0
  135. package/lib/admin/FleetRoutes.js +465 -0
  136. package/lib/admin/HealthWarnings.d.ts +10 -0
  137. package/lib/admin/HealthWarnings.js +95 -0
  138. package/lib/admin/LeaderboardRoutes.d.ts +8 -0
  139. package/lib/admin/LeaderboardRoutes.js +569 -0
  140. package/lib/admin/LogBuffer.d.ts +60 -0
  141. package/lib/admin/LogBuffer.js +92 -0
  142. package/lib/admin/LogRedaction.d.ts +15 -0
  143. package/lib/admin/LogRedaction.js +72 -0
  144. package/lib/admin/LogRoutes.d.ts +45 -0
  145. package/lib/admin/LogRoutes.js +307 -0
  146. package/lib/admin/MaintenanceMode.d.ts +41 -0
  147. package/lib/admin/MaintenanceMode.js +74 -0
  148. package/lib/admin/MaintenanceWindows.d.ts +91 -0
  149. package/lib/admin/MaintenanceWindows.js +207 -0
  150. package/lib/admin/MetricsRoutes.d.ts +8 -0
  151. package/lib/admin/MetricsRoutes.js +209 -0
  152. package/lib/admin/MetricsSampler.d.ts +124 -0
  153. package/lib/admin/MetricsSampler.js +211 -0
  154. package/lib/admin/OneTimeCodes.d.ts +60 -0
  155. package/lib/admin/OneTimeCodes.js +162 -0
  156. package/lib/admin/OpenIdConnect.d.ts +146 -0
  157. package/lib/admin/OpenIdConnect.js +335 -0
  158. package/lib/admin/Passkeys.d.ts +130 -0
  159. package/lib/admin/Passkeys.js +432 -0
  160. package/lib/admin/PlayerRoutes.d.ts +20 -0
  161. package/lib/admin/PlayerRoutes.js +676 -0
  162. package/lib/admin/RedisAdminStore.d.ts +114 -0
  163. package/lib/admin/RedisAdminStore.js +258 -0
  164. package/lib/admin/RolloutCoordinator.d.ts +99 -0
  165. package/lib/admin/RolloutCoordinator.js +321 -0
  166. package/lib/admin/ServerEvents.d.ts +57 -0
  167. package/lib/admin/ServerEvents.js +89 -0
  168. package/lib/admin/SessionRoutes.d.ts +113 -0
  169. package/lib/admin/SessionRoutes.js +386 -0
  170. package/lib/admin/UserRoutes.d.ts +8 -0
  171. package/lib/admin/UserRoutes.js +214 -0
  172. package/lib/cli.d.ts +2 -0
  173. package/lib/cli.js +221 -0
  174. package/lib/console/assets/index-CI5N7c-T.js +21 -0
  175. package/lib/console/assets/index-DPitvzLp.css +1 -0
  176. package/lib/console/favicon.svg +1 -0
  177. package/lib/console/index.html +16 -0
  178. package/lib/discord/DiscordOAuth.d.ts +57 -0
  179. package/lib/discord/DiscordOAuth.js +127 -0
  180. package/lib/discord/DiscordSessions.d.ts +79 -0
  181. package/lib/discord/DiscordSessions.js +135 -0
  182. package/lib/discord/DiscordSignInConstants.d.ts +54 -0
  183. package/lib/discord/DiscordSignInConstants.js +55 -0
  184. package/lib/discord/DiscordSignInRoutes.d.ts +51 -0
  185. package/lib/discord/DiscordSignInRoutes.js +359 -0
  186. package/lib/index.d.ts +24 -0
  187. package/lib/index.js +18 -0
  188. package/lib/leaderboards/FileLeaderboardRankStore.d.ts +293 -0
  189. package/lib/leaderboards/FileLeaderboardRankStore.js +808 -0
  190. package/lib/leaderboards/LeaderboardConstants.d.ts +63 -0
  191. package/lib/leaderboards/LeaderboardConstants.js +70 -0
  192. package/lib/leaderboards/LeaderboardError.d.ts +15 -0
  193. package/lib/leaderboards/LeaderboardError.js +20 -0
  194. package/lib/leaderboards/LeaderboardIdentity.d.ts +44 -0
  195. package/lib/leaderboards/LeaderboardIdentity.js +72 -0
  196. package/lib/leaderboards/LeaderboardPublicApi.d.ts +17 -0
  197. package/lib/leaderboards/LeaderboardPublicApi.js +389 -0
  198. package/lib/leaderboards/LeaderboardRankStore.d.ts +296 -0
  199. package/lib/leaderboards/LeaderboardRankStore.js +18 -0
  200. package/lib/leaderboards/LeaderboardRules.d.ts +122 -0
  201. package/lib/leaderboards/LeaderboardRules.js +472 -0
  202. package/lib/leaderboards/LeaderboardScheduler.d.ts +43 -0
  203. package/lib/leaderboards/LeaderboardScheduler.js +103 -0
  204. package/lib/leaderboards/LeaderboardService.d.ts +669 -0
  205. package/lib/leaderboards/LeaderboardService.js +1667 -0
  206. package/lib/leaderboards/LeaderboardTransfer.d.ts +35 -0
  207. package/lib/leaderboards/LeaderboardTransfer.js +580 -0
  208. package/lib/leaderboards/RedisLeaderboardRankStore.d.ts +282 -0
  209. package/lib/leaderboards/RedisLeaderboardRankStore.js +691 -0
  210. package/package.json +64 -4
  211. package/templates/init/Caddyfile +6 -0
  212. package/templates/init/DEPLOY.md +233 -0
  213. package/templates/init/Dockerfile +12 -0
  214. package/templates/init/app.json +21 -0
  215. package/templates/init/aws/README.md +118 -0
  216. package/templates/init/aws/ecs-fargate-alb.yml +207 -0
  217. package/templates/init/aws/ecs-fargate-nlb-quic.yml +210 -0
  218. package/templates/init/babylon-game-server.config.mjs +158 -0
  219. package/templates/init/colyseus-cloud/README.md +133 -0
  220. package/templates/init/colyseus-cloud/babylon-game-server.config.mjs +36 -0
  221. package/templates/init/colyseus-cloud/ecosystem.config.cjs +35 -0
  222. package/templates/init/colyseus-cloud/gitignore +6 -0
  223. package/templates/init/colyseus-cloud/package.json +15 -0
  224. package/templates/init/digitalocean-app.yaml +30 -0
  225. package/templates/init/docker-compose.full.yml +45 -0
  226. package/templates/init/docker-compose.websocket.yml +41 -0
  227. package/templates/init/fly.toml +56 -0
  228. package/templates/init/heroku.yml +11 -0
  229. package/templates/init/kubernetes/deployment.yaml +71 -0
  230. package/templates/init/kubernetes/redis.yaml +73 -0
  231. package/templates/init/kubernetes/service.yaml +24 -0
  232. package/templates/init/monitoring/grafana-dashboard.json +539 -0
  233. package/templates/init/monitoring/prometheus-alerts.yml +51 -0
  234. package/templates/init/monitoring/prometheus-scrape.yml +17 -0
  235. package/templates/init/railway.json +13 -0
  236. package/templates/init/render.yaml +19 -0
@@ -0,0 +1,33 @@
1
+ import { LobbyRoom, type AuthContext } from "colyseus";
2
+ import { type JoinGateResult } from "./JoinGate.js";
3
+ import type { FusionConnectRequest } from "./HeadlessRuntime.js";
4
+ /**
5
+ * The lobby room ("fusion_lobby"): Colyseus' LobbyRoom behind the same join gate as sessions (D10), so origin, rate and
6
+ * auth rules apply to session listing too. Live lobby rooms are kept in the server state, so the admin console can
7
+ * remove a player from the lobby (game-server-admin-console R15).
8
+ */
9
+ export declare class GameServerLobbyRoom extends LobbyRoom {
10
+ /**
11
+ * The join gate of the lobby (kind "lobby": no protocol-version or session-name checks).
12
+ * @param token The bearer token the client sent.
13
+ * @param options The join options.
14
+ * @param context Colyseus' matchmaking context.
15
+ * @returns The identity and client address, stored as client.auth.
16
+ * @throws ServerError when the gate refuses the join.
17
+ */
18
+ static onAuth(token: string, options: Partial<FusionConnectRequest>, context: AuthContext): Promise<JoinGateResult>;
19
+ /**
20
+ * Starts the lobby listing and joins the server's live lobby rooms.
21
+ * @param options The creation options.
22
+ */
23
+ onCreate(options: unknown): Promise<void>;
24
+ /** Stops the lobby listing and leaves the server's live lobby rooms. */
25
+ onDispose(): void;
26
+ /**
27
+ * Removes the lobby clients that match a user id or an address (lobby kick): each leaves with CloseCode.CONSENTED.
28
+ * @param userIds User ids to remove.
29
+ * @param addresses Addresses to remove.
30
+ * @returns How many clients were removed.
31
+ */
32
+ adminKickMatching(userIds: string[], addresses: string[]): number;
33
+ }
@@ -0,0 +1,56 @@
1
+ import { CloseCode, LobbyRoom } from "colyseus";
2
+ import { findGameServerState, getGameServerState } from "./GameServerState.js";
3
+ import { authorizeJoin } from "./JoinGate.js";
4
+ import { normalizeAddress } from "./NetworkAddresses.js";
5
+ /**
6
+ * The lobby room ("fusion_lobby"): Colyseus' LobbyRoom behind the same join gate as sessions (D10), so origin, rate and
7
+ * auth rules apply to session listing too. Live lobby rooms are kept in the server state, so the admin console can
8
+ * remove a player from the lobby (game-server-admin-console R15).
9
+ */
10
+ export class GameServerLobbyRoom extends LobbyRoom {
11
+ /**
12
+ * The join gate of the lobby (kind "lobby": no protocol-version or session-name checks).
13
+ * @param token The bearer token the client sent.
14
+ * @param options The join options.
15
+ * @param context Colyseus' matchmaking context.
16
+ * @returns The identity and client address, stored as client.auth.
17
+ * @throws ServerError when the gate refuses the join.
18
+ */
19
+ static async onAuth(token, options, context) {
20
+ return authorizeJoin(token, options, context, "lobby");
21
+ }
22
+ /**
23
+ * Starts the lobby listing and joins the server's live lobby rooms.
24
+ * @param options The creation options.
25
+ */
26
+ async onCreate(options) {
27
+ await super.onCreate(options);
28
+ getGameServerState().lobbyRooms.add(this);
29
+ }
30
+ /** Stops the lobby listing and leaves the server's live lobby rooms. */
31
+ onDispose() {
32
+ super.onDispose();
33
+ findGameServerState()?.lobbyRooms.delete(this);
34
+ }
35
+ /**
36
+ * Removes the lobby clients that match a user id or an address (lobby kick): each leaves with CloseCode.CONSENTED.
37
+ * @param userIds User ids to remove.
38
+ * @param addresses Addresses to remove.
39
+ * @returns How many clients were removed.
40
+ */
41
+ adminKickMatching(userIds, addresses) {
42
+ const kickedAddresses = new Set(addresses.map(normalizeAddress));
43
+ let disconnected = 0;
44
+ for (const client of Array.from(this.clients)) {
45
+ const gateResult = client.auth;
46
+ const userId = gateResult?.identity?.userId ?? null;
47
+ const address = normalizeAddress(gateResult?.clientAddress ?? "");
48
+ const matches = (userId != null && userIds.includes(userId)) || kickedAddresses.has(address);
49
+ if (!matches)
50
+ continue;
51
+ client.leave(CloseCode.CONSENTED);
52
+ disconnected += 1;
53
+ }
54
+ return disconnected;
55
+ }
56
+ }
@@ -0,0 +1,111 @@
1
+ import type { GameServerLogLevel } from "./GameServerConfig.js";
2
+ /** Context fields of a log entry; the known ones are added whenever the caller has them (D4). */
3
+ export interface LogFields {
4
+ /** The session name. */
5
+ sessionName?: string;
6
+ /** The Colyseus room id. */
7
+ roomId?: string;
8
+ /** The player id in the session. */
9
+ playerId?: number;
10
+ /** The verified user id, or null when the player is anonymous. */
11
+ userId?: string | null;
12
+ /** The client's address (D11). */
13
+ clientAddress?: string;
14
+ /** Any other field. Never put tokens, secrets or PEM text here. */
15
+ [field: string]: unknown;
16
+ }
17
+ /** The format of the lines a logger writes. */
18
+ export type LogLineFormat = "json" | "text";
19
+ /**
20
+ * Receives every entry that passes the logger's level, before it is written (the admin console's log buffer, D21).
21
+ * @param level The entry's level.
22
+ * @param message What happened.
23
+ * @param fields The entry's fields, undefined ones dropped and errors turned into their messages.
24
+ */
25
+ export type LogSink = (level: GameServerLogLevel, message: string, fields: Record<string, unknown>) => void;
26
+ /**
27
+ * Structured logs without a dependency (D4): one JSON object per line in production, else one readable line per entry,
28
+ * `<ISO time> <LEVEL> [GameServer] <message> key=value ...`.
29
+ */
30
+ export declare class GameServerLogger {
31
+ /** The line format. */
32
+ private readonly format;
33
+ /** The least severe level written; the console can change it while the server runs (D21). */
34
+ private currentLevelValue;
35
+ /** Where lines go, or null for the console (errors and warnings to stderr, the rest to stdout). */
36
+ private readonly write;
37
+ /** Listeners of every logged entry. */
38
+ private readonly sinks;
39
+ /**
40
+ * Creates a logger.
41
+ * @param format "json" or "text".
42
+ * @param level The least severe level written.
43
+ * @param write Receives each finished line (no trailing newline); the console when omitted.
44
+ */
45
+ constructor(format: LogLineFormat, level: GameServerLogLevel, write?: (line: string) => void);
46
+ /** The least severe level logged now. */
47
+ get currentLevel(): GameServerLogLevel;
48
+ /**
49
+ * Changes the least severe level logged, from the next entry on.
50
+ * @param level The new level.
51
+ */
52
+ setLevel(level: GameServerLogLevel): void;
53
+ /**
54
+ * Adds a listener that receives every entry that passes the level, before it is written.
55
+ * @param sink The listener; one that throws is ignored, so it can never stop a log line.
56
+ */
57
+ addSink(sink: LogSink): void;
58
+ /**
59
+ * Removes a listener added with addSink.
60
+ * @param sink The listener.
61
+ */
62
+ removeSink(sink: LogSink): void;
63
+ /**
64
+ * Logs a failure that needs attention.
65
+ * @param message What happened.
66
+ * @param fields Context fields.
67
+ */
68
+ error(message: string, fields?: LogFields): void;
69
+ /**
70
+ * Logs something unexpected that the server worked around.
71
+ * @param message What happened.
72
+ * @param fields Context fields.
73
+ */
74
+ warn(message: string, fields?: LogFields): void;
75
+ /**
76
+ * Logs a normal lifecycle event.
77
+ * @param message What happened.
78
+ * @param fields Context fields.
79
+ */
80
+ info(message: string, fields?: LogFields): void;
81
+ /**
82
+ * Logs detail useful when diagnosing.
83
+ * @param message What happened.
84
+ * @param fields Context fields.
85
+ */
86
+ debug(message: string, fields?: LogFields): void;
87
+ /**
88
+ * Hands one entry to the sinks and writes it, when its level is enabled.
89
+ * @param level The entry's level.
90
+ * @param message What happened.
91
+ * @param fields Context fields.
92
+ */
93
+ private log;
94
+ /**
95
+ * Calls every sink with a copy of the fields; a sink that throws is ignored.
96
+ * @param level The entry's level.
97
+ * @param message What happened.
98
+ * @param fields The cleaned fields.
99
+ */
100
+ private notifySinks;
101
+ }
102
+ /**
103
+ * The process logger.
104
+ * @returns The logger createGameServer configured, or a text/info logger before that.
105
+ */
106
+ export declare function getLogger(): GameServerLogger;
107
+ /**
108
+ * Replaces the process logger (createGameServer calls it with the configured format and level).
109
+ * @param logger The new logger.
110
+ */
111
+ export declare function setLogger(logger: GameServerLogger): void;
@@ -0,0 +1,190 @@
1
+ /** Rank of each level: an entry is written when its rank is at most the logger's (error 0 … debug 3). */
2
+ const LOG_LEVEL_RANK = { error: 0, warn: 1, info: 2, debug: 3 };
3
+ /** Width the level is padded to in text lines, so messages line up. */
4
+ const TEXT_LEVEL_WIDTH = 5;
5
+ /** Tag of every text line. */
6
+ const TEXT_LINE_TAG = "[GameServer]";
7
+ /**
8
+ * Structured logs without a dependency (D4): one JSON object per line in production, else one readable line per entry,
9
+ * `<ISO time> <LEVEL> [GameServer] <message> key=value ...`.
10
+ */
11
+ export class GameServerLogger {
12
+ /** The line format. */
13
+ format;
14
+ /** The least severe level written; the console can change it while the server runs (D21). */
15
+ currentLevelValue;
16
+ /** Where lines go, or null for the console (errors and warnings to stderr, the rest to stdout). */
17
+ write;
18
+ /** Listeners of every logged entry. */
19
+ sinks = [];
20
+ /**
21
+ * Creates a logger.
22
+ * @param format "json" or "text".
23
+ * @param level The least severe level written.
24
+ * @param write Receives each finished line (no trailing newline); the console when omitted.
25
+ */
26
+ constructor(format, level, write) {
27
+ this.format = format;
28
+ this.currentLevelValue = level;
29
+ this.write = write ?? null;
30
+ }
31
+ /** The least severe level logged now. */
32
+ get currentLevel() {
33
+ return this.currentLevelValue;
34
+ }
35
+ /**
36
+ * Changes the least severe level logged, from the next entry on.
37
+ * @param level The new level.
38
+ */
39
+ setLevel(level) {
40
+ this.currentLevelValue = level;
41
+ }
42
+ /**
43
+ * Adds a listener that receives every entry that passes the level, before it is written.
44
+ * @param sink The listener; one that throws is ignored, so it can never stop a log line.
45
+ */
46
+ addSink(sink) {
47
+ this.sinks.push(sink);
48
+ }
49
+ /**
50
+ * Removes a listener added with addSink.
51
+ * @param sink The listener.
52
+ */
53
+ removeSink(sink) {
54
+ const index = this.sinks.indexOf(sink);
55
+ if (index >= 0)
56
+ this.sinks.splice(index, 1);
57
+ }
58
+ /**
59
+ * Logs a failure that needs attention.
60
+ * @param message What happened.
61
+ * @param fields Context fields.
62
+ */
63
+ error(message, fields) {
64
+ this.log("error", message, fields);
65
+ }
66
+ /**
67
+ * Logs something unexpected that the server worked around.
68
+ * @param message What happened.
69
+ * @param fields Context fields.
70
+ */
71
+ warn(message, fields) {
72
+ this.log("warn", message, fields);
73
+ }
74
+ /**
75
+ * Logs a normal lifecycle event.
76
+ * @param message What happened.
77
+ * @param fields Context fields.
78
+ */
79
+ info(message, fields) {
80
+ this.log("info", message, fields);
81
+ }
82
+ /**
83
+ * Logs detail useful when diagnosing.
84
+ * @param message What happened.
85
+ * @param fields Context fields.
86
+ */
87
+ debug(message, fields) {
88
+ this.log("debug", message, fields);
89
+ }
90
+ /**
91
+ * Hands one entry to the sinks and writes it, when its level is enabled.
92
+ * @param level The entry's level.
93
+ * @param message What happened.
94
+ * @param fields Context fields.
95
+ */
96
+ log(level, message, fields) {
97
+ if (LOG_LEVEL_RANK[level] > LOG_LEVEL_RANK[this.currentLevelValue])
98
+ return;
99
+ const entryFields = cleanFields(fields);
100
+ this.notifySinks(level, message, entryFields);
101
+ const line = (this.format === "json") ? jsonLine(level, message, entryFields) : textLine(level, message, entryFields);
102
+ if (this.write != null)
103
+ this.write(line);
104
+ else if (LOG_LEVEL_RANK[level] <= LOG_LEVEL_RANK.warn)
105
+ process.stderr.write(line + "\n");
106
+ else
107
+ process.stdout.write(line + "\n");
108
+ }
109
+ /**
110
+ * Calls every sink with a copy of the fields; a sink that throws is ignored.
111
+ * @param level The entry's level.
112
+ * @param message What happened.
113
+ * @param fields The cleaned fields.
114
+ */
115
+ notifySinks(level, message, fields) {
116
+ for (const sink of this.sinks) {
117
+ try {
118
+ sink(level, message, Object.assign({}, fields));
119
+ }
120
+ catch {
121
+ // A failing listener must never stop the line itself from being written.
122
+ }
123
+ }
124
+ }
125
+ }
126
+ /** The process logger (text/info until createGameServer configures it). */
127
+ let processLogger = new GameServerLogger("text", "info");
128
+ /**
129
+ * The process logger.
130
+ * @returns The logger createGameServer configured, or a text/info logger before that.
131
+ */
132
+ export function getLogger() {
133
+ return processLogger;
134
+ }
135
+ /**
136
+ * Replaces the process logger (createGameServer calls it with the configured format and level).
137
+ * @param logger The new logger.
138
+ */
139
+ export function setLogger(logger) {
140
+ processLogger = logger;
141
+ }
142
+ /**
143
+ * Drops undefined fields and turns errors into their messages (JSON.stringify writes an Error as {}).
144
+ * @param fields The caller's fields.
145
+ * @returns Fields ready to write.
146
+ */
147
+ function cleanFields(fields) {
148
+ const cleaned = {};
149
+ if (fields == null)
150
+ return cleaned;
151
+ for (const [name, value] of Object.entries(fields)) {
152
+ if (value === undefined)
153
+ continue;
154
+ cleaned[name] = (value instanceof Error) ? value.message : value;
155
+ }
156
+ return cleaned;
157
+ }
158
+ /**
159
+ * A JSON log line.
160
+ * @param level The level.
161
+ * @param message The message.
162
+ * @param fields The fields.
163
+ * @returns `{"time":…,"level":…,"message":…,…fields}`.
164
+ */
165
+ function jsonLine(level, message, fields) {
166
+ return JSON.stringify(Object.assign({ time: new Date().toISOString(), level: level, message: message }, fields));
167
+ }
168
+ /**
169
+ * A readable log line.
170
+ * @param level The level.
171
+ * @param message The message.
172
+ * @param fields The fields.
173
+ * @returns `<ISO time> <LEVEL> [GameServer] <message> key=value ...`.
174
+ */
175
+ function textLine(level, message, fields) {
176
+ const fieldText = Object.entries(fields).map(([name, value]) => " " + name + "=" + textFieldValue(value)).join("");
177
+ return new Date().toISOString() + " " + level.toUpperCase().padEnd(TEXT_LEVEL_WIDTH) + " " + TEXT_LINE_TAG + " " + message + fieldText;
178
+ }
179
+ /**
180
+ * How a field value reads in a text line: strings with spaces quoted, objects as JSON.
181
+ * @param value The value.
182
+ * @returns The text.
183
+ */
184
+ function textFieldValue(value) {
185
+ if (typeof value === "string")
186
+ return /\s/.test(value) ? JSON.stringify(value) : value;
187
+ if (value !== null && typeof value === "object")
188
+ return JSON.stringify(value);
189
+ return String(value);
190
+ }
@@ -0,0 +1,158 @@
1
+ import type { AuthMode } from "./GameServerConfig.js";
2
+ import type { GameServerMetricsSink, GameServerStateValues } from "./GameServerState.js";
3
+ /** Counter totals since start, summed over their labels (game-server-admin-console R22, DR §6.7). */
4
+ export interface MetricsCounterSnapshot {
5
+ /** Accepted joins. */
6
+ joinsAccepted: number;
7
+ /** Refused joins. */
8
+ joinsRefused: number;
9
+ /** Refused joins by ShutdownReason name. */
10
+ refusalsByReason: Record<string, number>;
11
+ /** Failed token checks. */
12
+ authFailures: number;
13
+ /** Frames from clients. */
14
+ framesIn: number;
15
+ /** Frames to clients. */
16
+ framesOut: number;
17
+ /** Frame bytes from clients. */
18
+ bytesIn: number;
19
+ /** Frame bytes to clients. */
20
+ bytesOut: number;
21
+ /** Frames (both directions) by the client's transport. */
22
+ framesByTransport: {
23
+ websocket: number;
24
+ webtransport: number;
25
+ };
26
+ /** Room messages (frames and controls) from clients. */
27
+ messagesIn: number;
28
+ /** Room messages (frames and controls) to clients. */
29
+ messagesOut: number;
30
+ /** Dedicated frames longer than one tick. */
31
+ dedicatedTickOverruns: number;
32
+ /** Sum of dedicated frame durations, in seconds. */
33
+ dedicatedFrameSecondsSum: number;
34
+ /** Dedicated frames observed. */
35
+ dedicatedFrameCount: number;
36
+ /** Process CPU time, in seconds. */
37
+ cpuSecondsTotal: number;
38
+ /** Resident memory, in bytes. */
39
+ residentMemoryBytes: number;
40
+ /** Used V8 heap, in bytes. */
41
+ heapUsedBytes: number;
42
+ /** Time spent in garbage collection, in seconds. */
43
+ gcSecondsTotal: number;
44
+ /** Accepted leaderboard scores (game-server-leaderboards D21). */
45
+ leaderboardScoresAccepted: number;
46
+ /** Refused leaderboard scores (game-server-leaderboards D21). */
47
+ leaderboardScoresRefused: number;
48
+ }
49
+ /**
50
+ * The Prometheus metrics of a game server (D16), on a private registry with the process defaults (event-loop lag,
51
+ * memory). The player, session and dedicated-session gauges are computed when Prometheus scrapes, from the live session
52
+ * rooms, so nothing polls; the counters and the frame histogram are fed by the rooms and the join gate through
53
+ * GameServerMetricsSink.
54
+ */
55
+ export declare class GameServerMetrics implements GameServerMetricsSink {
56
+ /** The registry every metric of this server is on. */
57
+ private readonly registry;
58
+ /** Reads the running server's state at scrape time. */
59
+ private readonly readState;
60
+ /** Join decisions by result. */
61
+ private readonly joinsTotal;
62
+ /** Refused joins by ShutdownReason name. */
63
+ private readonly refusalsTotal;
64
+ /** Failed token checks by auth mode. */
65
+ private readonly authFailuresTotal;
66
+ /** Simulation frames by direction, transport and channel. */
67
+ private readonly framesTotal;
68
+ /** Simulation frame bytes by direction and transport. */
69
+ private readonly bytesTotal;
70
+ /** Room messages (frames and controls) by direction: the room message rate. */
71
+ private readonly messagesTotal;
72
+ /** Leaderboard scores by result (game-server-leaderboards D21). */
73
+ private readonly leaderboardScoresTotal;
74
+ /** Refused leaderboard scores and requests by reason (game-server-leaderboards D19). */
75
+ private readonly leaderboardRefusalsTotal;
76
+ /** Dedicated frames that took longer than one simulation tick. */
77
+ private readonly dedicatedTickOverrunsTotal;
78
+ /** How long dedicated runners' frames take. */
79
+ private readonly dedicatedFrameSeconds;
80
+ /** The last counter snapshot, reused for SNAPSHOT_CACHE_MS. */
81
+ private cachedSnapshot;
82
+ /**
83
+ * Creates the metrics and registers the process defaults.
84
+ * @param state Reads the running server's state (the gauges read its session rooms at scrape time).
85
+ */
86
+ constructor(state: () => GameServerStateValues);
87
+ /** The Content-Type of the Prometheus text the metrics endpoint answers with. */
88
+ get contentType(): string;
89
+ /**
90
+ * Counts a join decision; a refusal is also counted by its reason.
91
+ * @param result "accepted" or "refused".
92
+ * @param reasonName The ShutdownReason name of a refusal, or null.
93
+ */
94
+ countJoin(result: "accepted" | "refused", reasonName: string | null): void;
95
+ /**
96
+ * Counts a failed token check.
97
+ * @param mode The auth mode that refused it.
98
+ */
99
+ countAuthFailure(mode: AuthMode): void;
100
+ /**
101
+ * Counts one simulation frame and its bytes.
102
+ * @param direction "in" from a client, "out" to a client.
103
+ * @param transport The client's connection.
104
+ * @param channel The delivery channel.
105
+ * @param byteLength The frame size in bytes.
106
+ */
107
+ countFrame(direction: "in" | "out", transport: "websocket" | "webtransport", channel: "reliable" | "unreliable", byteLength: number): void;
108
+ /**
109
+ * Counts one room message (a simulation frame or a control).
110
+ * @param direction "in" from a client, "out" to a client.
111
+ */
112
+ countMessage(direction: "in" | "out"): void;
113
+ /**
114
+ * Counts one leaderboard score; a refused score is also counted by its reason (game-server-leaderboards D19).
115
+ * @param result What happened to the score.
116
+ * @param reason The refusal reason, or null.
117
+ */
118
+ countLeaderboardScore(result: "accepted" | "duplicate" | "refused", reason: string | null): void;
119
+ /**
120
+ * Counts one refused leaderboard request (rate limit, auth, ban, bad request; game-server-leaderboards D19).
121
+ * @param reason The refusal reason.
122
+ */
123
+ countLeaderboardRequestRefusal(reason: string): void;
124
+ /**
125
+ * Records how long a dedicated runner's frame took, counting an overrun when it did not fit in one tick.
126
+ * @param seconds The frame's duration, in seconds.
127
+ * @param tickSeconds The simulation tick length, in seconds.
128
+ */
129
+ observeDedicatedFrame(seconds: number, tickSeconds: number): void;
130
+ /**
131
+ * Renders every metric as Prometheus text (computing the gauges from the live session rooms).
132
+ * @returns The exposition text.
133
+ */
134
+ render(): Promise<string>;
135
+ /**
136
+ * The counter totals the admin console reads (game-server-admin-console R22): a snapshot younger than the allowed
137
+ * age is returned as it is, so the registry's JSON is built at most about once per metrics sample (building it also
138
+ * restarts the event-loop histogram of the process defaults).
139
+ * @param maxAgeMilliseconds The oldest cached snapshot accepted (default SNAPSHOT_CACHE_MS; the metrics sampler
140
+ * passes less, so each of its samples reads fresh counters).
141
+ * @returns The totals since start.
142
+ */
143
+ snapshot(maxAgeMilliseconds?: number): Promise<MetricsCounterSnapshot>;
144
+ /**
145
+ * Registers the scrape-time gauges: players and sessions by game mode, and dedicated sessions.
146
+ */
147
+ private registerGauges;
148
+ /**
149
+ * Sums players and sessions of the live session rooms by game mode name.
150
+ * @returns The totals, keyed by game mode name ("Pending" before a session's first join).
151
+ */
152
+ private totalsByGameMode;
153
+ /**
154
+ * The live session rooms of the running server.
155
+ * @returns The rooms.
156
+ */
157
+ private liveRooms;
158
+ }