@babylonjs-toolkit/server 0.0.0-stage → 9.35.1

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
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
1
  {
2
2
  "name": "@babylonjs-toolkit/server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "9.35.1",
4
+ "description": "Babylon Game Server — the multiplayer session server for Babylon Toolkit games",
5
+ "author": "MackeyK24 <mackeyk24@gmail.com>",
6
+ "type": "module",
7
+ "main": "lib/index.js",
8
+ "types": "lib/index.d.ts",
9
+ "exports": {
10
+ ".": "./lib/index.js"
11
+ },
12
+ "bin": {
13
+ "babylon-game-server": "lib/cli.js"
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "templates",
18
+ "README.md",
19
+ "DEPLOY.md",
20
+ "CUSTOM.md"
21
+ ],
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "scripts": {
26
+ "build": "npm run build:server && npm run build:console",
27
+ "build:server": "tsc -p tsconfig.json",
28
+ "build:console": "vite build --config console/vite.config.ts",
29
+ "check:console": "tsc -p console/tsconfig.json",
30
+ "start": "node lib/cli.js",
31
+ "test": "npm run build && node scripts/run-server-tests.mjs",
32
+ "prepack": "npm run build && node scripts/release-manifest.mjs apply",
33
+ "postpack": "node scripts/release-manifest.mjs restore",
34
+ "release:publish": "npm publish --access public",
35
+ "release:image": "cd .. && docker buildx build --platform linux/amd64,linux/arm64 -f SVR/Dockerfile -t ghcr.io/mackeyk24/babylon-game-server:$npm_package_version -t mackeyk24/babylon-game-server:$npm_package_version --push ."
36
+ },
37
+ "dependencies": {
38
+ "@babylonjs/havok": "1.3.14",
39
+ "@colyseus/ws-transport": "0.18.4",
40
+ "babylonjs": "9.35.0",
41
+ "babylonjs-gui": "9.35.0",
42
+ "babylonjs-loaders": "9.35.0",
43
+ "babylonjs-toolkit": "9.35.1",
44
+ "colyseus": "0.18.9",
45
+ "express": "5.3.0",
46
+ "ioredis": "5.11.1",
47
+ "jose": "6.2.12",
48
+ "prom-client": "15.1.3"
49
+ },
50
+ "devDependencies": {
51
+ "@colyseus/sdk": "0.18.5",
52
+ "@fails-components/webtransport": "1.6.2",
53
+ "@types/node": "22.15.3",
54
+ "@types/react": "19.3.0",
55
+ "@types/react-dom": "19.3.0",
56
+ "@vitejs/plugin-react": "6.1.2",
57
+ "qrcode-generator": "1.4.4",
58
+ "react": "19.3.0",
59
+ "react-dom": "19.3.0",
60
+ "typescript": "6.0.3",
61
+ "vite": "8.3.1"
62
+ },
63
+ "optionalDependencies": {
64
+ "@colyseus/h3-transport": "0.18.4"
65
+ }
66
+ }
@@ -0,0 +1,6 @@
1
+ # Caddy in front of the Babylon Game Server {{version}} (docker-compose.websocket.yml).
2
+ # CADDY_SITE is your domain (Caddy gets and renews its certificate automatically) and defaults to :443.
3
+ # CADDY_SITE=:8080 serves plain HTTP on 8080 for a local check.
4
+ {$CADDY_SITE::443} {
5
+ reverse_proxy game-server:2567
6
+ }
@@ -0,0 +1,233 @@
1
+ # Deploying the Babylon Game Server {{version}}
2
+
3
+ This folder was written by `babylon-game-server init`. It holds your server configuration and a ready recipe for each
4
+ way of hosting it. Every recipe uses the published image `ghcr.io/mackeyk24/babylon-game-server:{{version}}` (also
5
+ on Docker Hub as `mackeyk24/babylon-game-server:{{version}}`), either directly or through the `Dockerfile` here,
6
+ which adds your `babylon-game-server.config.mjs`.
7
+
8
+ TLS, UDP (HTTP/3 WebTransport) and player authentication are **off** in every file until you turn them on. Each
9
+ recipe's header states where TLS ends (`edge`: the proxy or platform, `server`: the game server itself), whether its
10
+ host can carry UDP, and how to enable auth.
11
+
12
+ | File | What it is |
13
+ |---|---|
14
+ | `babylon-game-server.config.mjs` | Every setting at its default, with notes on enabling TLS, UDP and auth |
15
+ | `.env.example` | Every setting as a `GAME_SERVER_*` environment variable |
16
+ | `Dockerfile` | Your image: the published image plus your config |
17
+ | `docker-compose.websocket.yml`, `Caddyfile` | The server behind Caddy (HTTPS at Caddy, WebSocket only) |
18
+ | `docker-compose.full.yml` | TLS on the server, WebSocket + HTTP/3 on 443 TCP + UDP, and Redis |
19
+ | `aws/`, `fly.toml`, `railway.json`, `render.yaml`, `digitalocean-app.yaml`, `heroku.yml`, `app.json`, `colyseus-cloud/`, `kubernetes/` | Cloud and platform recipes (see [Platforms](#platforms)) |
20
+
21
+ **Recommended host: Colyseus Cloud (`colyseus-cloud/`).** The server is built on Colyseus 0.18, and Colyseus Cloud is
22
+ Colyseus's own hosting, at a fixed monthly price: `wss://` provided, one process per CPU sharing Redis, and rolling
23
+ deploys that let running sessions finish. Its folder installs the published package instead of using the image; its
24
+ `README.md` has every step. The other recipes run the same server anywhere else.
25
+
26
+ ## Production checklist
27
+
28
+ 1. **Point the game at the server.** Set the game's `serverUrl` (the Unity `NetworkSettings` component, or
29
+ `StartGameArgs`) to `wss://<your domain>`. Browsers block `ws://` from an `https://` page.
30
+ 2. **Decide on authentication.** With auth off, anyone who can reach the server can join (the server logs a warning
31
+ in production). To require players to sign in, set `GAME_SERVER_AUTH_ENABLED=true` and a mode
32
+ (`GAME_SERVER_AUTH_MODE`: `shared-secret`, `jwt` or `webhook`) with its fields, and have the game pass
33
+ `StartGameArgs.authToken`.
34
+ - **Discord sign-in (optional, works beside any mode):** register `https://<your domain>/game-server/discord/callback`
35
+ under OAuth2 → Redirects in your Discord application, set it as `GAME_SERVER_DISCORD_REDIRECT_URI`, and put the client
36
+ secret in `GAME_SERVER_DISCORD_CLIENT_SECRET` in your platform's secret store (with
37
+ `GAME_SERVER_DISCORD_SIGN_IN_ENABLED=true`, `GAME_SERVER_DISCORD_CLIENT_ID` and `GAME_SERVER_DISCORD_RETURN_ORIGINS`).
38
+ 3. **Restrict origins.** Set `GAME_SERVER_ALLOWED_ORIGINS` to your game's origins, e.g. `https://game.example.com`.
39
+ 4. **Protect the admin API.** The four original admin endpoints stay off until `GAME_SERVER_ADMIN_TOKEN` is set; use a
40
+ long random value from your platform's secret store. For scripts, prefer the console's API keys, which carry only
41
+ the permissions they need (`babylon-game-server admin create-api-key`).
42
+ 5. **Size instances for dedicated sessions.** Each Server-mode session runs the scene's simulation on the server
43
+ (`limits.maxDedicatedSessions` per process, default 4). Shared and Host sessions only relay, and are light.
44
+ 6. **When scaling out**, run Redis, set `GAME_SERVER_REDIS_URL` on every instance, and set
45
+ `GAME_SERVER_PUBLIC_ADDRESS` per instance to an address players can reach that instance on directly (seat
46
+ reservations send each player to the instance that holds the session).
47
+ 7. **Health checks:** liveness `GET /game-server/health`, readiness `GET /game-server/ready` (503 while draining or
48
+ when Redis is unreachable). On `SIGTERM` the server drains for `GAME_SERVER_DRAIN_SECONDS` (default 30): give the
49
+ platform at least that long before it kills the container.
50
+ 8. **Set up the admin console** (`/game-server/admin/`; README, "Admin console"):
51
+ - **HTTPS.** The console refuses plain HTTP except from a loopback address. Serve it on your TLS address (an edge
52
+ with `GAME_SERVER_TRUST_PROXY=true` that sends `x-forwarded-proto: https`, or TLS on the server).
53
+ - **Keep its data.** Accounts, bans, the audit log and the leaderboards live in Redis when `GAME_SERVER_REDIS_URL` is set, otherwise in
54
+ `GAME_SERVER_ADMIN_DATA_DIR` (`/data` in the image). Mount a volume at `/data` (the compose files and `fly.toml`
55
+ do), or use Redis with persistence (`docker-compose.full.yml`, `kubernetes/redis.yaml`). Platforms without
56
+ volumes (Railway, Render, DigitalOcean App Platform, Heroku, ECS Fargate) need Redis, or the console forgets its
57
+ accounts on every deploy.
58
+ - **Restrict who can reach it.** Set `GAME_SERVER_ADMIN_ALLOWED_NETWORKS` to your office or VPN ranges (CIDR), or
59
+ block `/game-server/admin/` at the edge. Requests from other addresses answer 404.
60
+ - **Create the first Owner.** The first start logs `admin setup link` (valid 30 minutes, used once): open it on your
61
+ HTTPS address, or run `babylon-game-server admin create-owner --username <name>` in the container. Every account
62
+ signs in with a password and an authenticator code or passkey.
63
+ - **Back up the data.** Copy the data folder (or snapshot the volume), or turn on Redis persistence and backups; it
64
+ holds the bans, the audit log and the leaderboards. The console's Leaderboards page warns when they are not
65
+ durable.
66
+
67
+ ## Docker Compose
68
+
69
+ Both compose files read `GAME_SERVER_IMAGE` when it is set, so you can try a locally built image first
70
+ (`GAME_SERVER_IMAGE=my-game-server docker compose …`).
71
+
72
+ ### WebSocket behind Caddy (`docker-compose.websocket.yml`)
73
+
74
+ Caddy terminates HTTPS (it obtains and renews a certificate for your domain automatically) and proxies to the server,
75
+ which publishes no ports of its own. Point your domain's DNS at the host and open TCP 443.
76
+
77
+ ```sh
78
+ CADDY_SITE=game.example.com docker compose -f docker-compose.websocket.yml up -d
79
+ # players use wss://game.example.com
80
+ ```
81
+
82
+ Local check without a domain (plain HTTP on 8080):
83
+
84
+ ```sh
85
+ CADDY_SITE=:8080 docker compose -f docker-compose.websocket.yml up -d
86
+ curl -s http://localhost:8080/game-server/health # {"status":"ok"}
87
+ docker compose -f docker-compose.websocket.yml down
88
+ ```
89
+
90
+ ### TLS, HTTP/3 and Redis (`docker-compose.full.yml`)
91
+
92
+ The server terminates TLS itself, so it can also offer HTTP/3 WebTransport over UDP on the same port 443.
93
+
94
+ 1. Get a certificate into `./certs` (`fullchain.pem`, `privkey.pem`), for example with certbot:
95
+ `certbot certonly --standalone -d game.example.com`, then copy the files from `/etc/letsencrypt/live/<domain>/`
96
+ (a certbot `--deploy-hook` can copy them on every renewal; the server reloads them without a restart, and HTTP/3
97
+ picks them up at the next restart).
98
+ 2. Open TCP **and** UDP 443 on the host and its firewall.
99
+ 3. Start it:
100
+
101
+ ```sh
102
+ docker compose -f docker-compose.full.yml up -d
103
+ ```
104
+
105
+ Players use `wss://game.example.com`; browsers that support WebTransport switch to HTTP/3 datagrams automatically and
106
+ fall back to the WebSocket when UDP is blocked.
107
+
108
+ ## Platforms
109
+
110
+ Which transports each host can carry. WebSocket works everywhere; HTTP/3 WebTransport (UDP datagrams) needs a host
111
+ that forwards inbound UDP to the server **and** TLS on the server itself. Players whose network or browser cannot use
112
+ HTTP/3 fall back to the WebSocket automatically, so a WebSocket-only host is fully playable.
113
+
114
+ | Host | WebSocket | UDP (HTTP/3) | TLS |
115
+ |---|---|---|---|
116
+ | Docker Compose + Caddy (`docker-compose.websocket.yml`) | yes | no (Caddy proxies TCP only) | edge (Caddy) |
117
+ | Docker Compose full (`docker-compose.full.yml`) | yes | yes (443/udp published) | server |
118
+ | AWS ECS Fargate + ALB (`aws/ecs-fargate-alb.yml`) | yes | no: [ALB listeners are HTTP/HTTPS only](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html) | edge (ALB) |
119
+ | AWS ECS Fargate + NLB QUIC (`aws/ecs-fargate-nlb-quic.yml`) | yes | yes (`TCP_QUIC` listener) | server |
120
+ | AWS single EC2 + Elastic IP (`aws/README.md`) | yes | yes (open UDP 443) | server |
121
+ | AWS Elastic Beanstalk (`aws/README.md`) | yes | no: [ALB listeners are HTTP/HTTPS only](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html) | edge (ALB) |
122
+ | Fly.io (`fly.toml`) | yes | no: [UDP must bind `fly-global-services`](https://docs.fly.io/networking/udp-and-tcp) | edge (Fly proxy) |
123
+ | Railway (`railway.json`) | yes | no: [no inbound UDP](https://docs.railway.com/networking/tcp-proxy) | edge (Railway) |
124
+ | Render (`render.yaml`) | yes | no: [HTTP and WebSocket only](https://render.com/docs/websocket) | edge (Render) |
125
+ | DigitalOcean App Platform (`digitalocean-app.yaml`) | yes | no: [HTTP only](https://docs.digitalocean.com/products/app-platform/details/limits/) | edge (App Platform) |
126
+ | Heroku (`heroku.yml`, `app.json`) | yes | no: [HTTP routing only](https://devcenter.heroku.com/articles/http-routing) | edge (Heroku router) |
127
+ | Colyseus Cloud (`colyseus-cloud/`) | yes | no: its NGINX carries HTTP and WebSocket only | edge (Colyseus Cloud) |
128
+ | Kubernetes (`kubernetes/`) | yes | only with server TLS and a UDP-capable LoadBalancer Service | edge (Ingress) or server |
129
+
130
+ Every recipe below except Colyseus Cloud builds from the `Dockerfile` in this folder (the published image plus your
131
+ config) or runs the published image directly, keeps server TLS, UDP and auth off unless the table says otherwise, and honours the
132
+ platform's `PORT` when `GAME_SERVER_PORT` is not set.
133
+
134
+ ### AWS
135
+
136
+ `aws/README.md` walks through three setups: ECS Fargate behind an Application Load Balancer (`aws/ecs-fargate-alb.yml`,
137
+ WebSocket, TLS at the ALB, health `/game-server/ready`, idle timeout 3600 s), ECS Fargate behind a Network Load
138
+ Balancer with a `TCP_QUIC` listener (`aws/ecs-fargate-nlb-quic.yml`, WebSocket + HTTP/3, TLS on the server from a
139
+ Secrets Manager PEM secret), and one EC2 instance with an Elastic IP, plus Elastic Beanstalk notes.
140
+
141
+ ```sh
142
+ aws cloudformation deploy --template-file aws/ecs-fargate-alb.yml --stack-name babylon-game-server \
143
+ --capabilities CAPABILITY_IAM \
144
+ --parameter-overrides VpcId=vpc-... SubnetIds=subnet-a,subnet-b CertificateArn=arn:aws:acm:...
145
+ ```
146
+
147
+ ### Fly.io (`fly.toml`)
148
+
149
+ WebSocket only, TLS at Fly's edge on 443, readiness check on `/game-server/ready`. Fly hands UDP only to sockets bound
150
+ to `fly-global-services`, while the server's HTTP/3 transport binds one address for TCP and UDP, so HTTP/3 is not
151
+ offered ([Fly: UDP and TCP](https://docs.fly.io/networking/udp-and-tcp)).
152
+
153
+ ```sh
154
+ fly launch --copy-config --no-deploy
155
+ fly deploy
156
+ # players use wss://<app>.fly.dev
157
+ ```
158
+
159
+ ### Railway (`railway.json`)
160
+
161
+ TLS: edge. UDP: not supported on this host. Auth: set `GAME_SERVER_AUTH_*` in the service's variables to enable.
162
+ (JSON has no comments, so this recipe's header lives here.) Railway builds the `Dockerfile`, checks
163
+ `/game-server/ready`, and sets `PORT`. Its public networking is HTTP, plus a TCP proxy; there is no inbound UDP
164
+ ([Railway: TCP proxy](https://docs.railway.com/networking/tcp-proxy)).
165
+
166
+ ```sh
167
+ railway init
168
+ railway up
169
+ railway domain # players use wss://<generated domain>
170
+ ```
171
+
172
+ ### Render (`render.yaml`)
173
+
174
+ A Docker web service with `healthCheckPath: /game-server/ready`; Render terminates TLS and supports WebSockets, but
175
+ not inbound UDP ([Render: WebSockets](https://render.com/docs/websocket)). Commit this folder to a repository and
176
+ create a Blueprint from it in the Render dashboard.
177
+
178
+ ### DigitalOcean App Platform (`digitalocean-app.yaml`)
179
+
180
+ A service built from the `Dockerfile` with `http_port: 2567` and a health check on `/game-server/ready`. App Platform
181
+ routes HTTP (including WebSockets) only ([App Platform limits](https://docs.digitalocean.com/products/app-platform/details/limits/)).
182
+ Set `github.repo`, then:
183
+
184
+ ```sh
185
+ doctl apps create --spec digitalocean-app.yaml
186
+ ```
187
+
188
+ ### Heroku (`heroku.yml`, `app.json`)
189
+
190
+ The container stack builds the `Dockerfile`; `app.json` sets the stack, trusts the router's forwarding header, and
191
+ shortens the drain to 25 s because Heroku stops a dyno 30 s after `SIGTERM`. The router carries HTTP and WebSockets
192
+ only ([Heroku: HTTP routing](https://devcenter.heroku.com/articles/http-routing)).
193
+
194
+ ```sh
195
+ heroku create --stack container
196
+ git push heroku main
197
+ ```
198
+
199
+ ### Colyseus Cloud (`colyseus-cloud/`)
200
+
201
+ The recommended host. WebSocket only, TLS at Colyseus Cloud's NGINX, one process per CPU sharing Redis. This folder is
202
+ a deploy repository that installs the published package, so it uses neither the `Dockerfile` nor the image. Push it as a
203
+ repository of its own, or as a subfolder of another repository with the panel's Root directory set to it. Its
204
+ `README.md` has every step.
205
+
206
+ ```sh
207
+ cd colyseus-cloud
208
+ npm install # keep the package-lock.json this writes
209
+ git init && git add . && git commit -m "Babylon Game Server on Colyseus Cloud"
210
+ npx @colyseus/cloud deploy # or connect the repository on the panel
211
+ # the banner in the application logs shows wss://<your-application-address>
212
+ ```
213
+
214
+ Set `NODE_ENV=production`, `GAME_SERVER_ALLOWED_ORIGINS` and `GAME_SERVER_ADMIN_TOKEN` on the panel. Leave
215
+ `GAME_SERVER_TLS_ENABLED`, `GAME_SERVER_UDP_ENABLED` and `GAME_SERVER_PUBLIC_ADDRESS` unset: the server refuses to start
216
+ with them. The drain time here is 600 s, and a process holding a `boot` dedicated session uses all of it on every
217
+ deploy; `GAME_SERVER_DRAIN_SECONDS` changes it. After changing a boot scene, watch the logs for `the boot dedicated
218
+ sessions could not start; trying again`: a bad scene does not fail a rolling deploy.
219
+
220
+ ### Kubernetes (`kubernetes/`)
221
+
222
+ `deployment.yaml` runs the image with liveness `/game-server/health`, readiness `/game-server/ready`,
223
+ `GAME_SERVER_REDIS_URL`, and a per-pod `GAME_SERVER_PUBLIC_ADDRESS` from the pod IP (downward API). `service.yaml` is
224
+ a ClusterIP Service for an Ingress; `redis.yaml` is a single Redis Deployment and Service.
225
+
226
+ ```sh
227
+ kubectl apply -f kubernetes/redis.yaml -f kubernetes/deployment.yaml -f kubernetes/service.yaml
228
+ ```
229
+
230
+ Put a TLS Ingress in front of the Service for `wss://`. When scaling past one replica, each pod must be reachable on
231
+ its own address, because seat reservations send each player to the pod that holds the session (see the comment in
232
+ `service.yaml`). For HTTP/3, enable server TLS (mount the certificate from a Secret) and UDP, and expose the pods with a
233
+ LoadBalancer Service that forwards both TCP and UDP on the same port.
@@ -0,0 +1,12 @@
1
+ # Babylon Game Server {{version}}: your game's server image, built on the published base image.
2
+ # docker build -t my-game-server .
3
+ # docker run -p 2567:2567 my-game-server
4
+ # The base image runs as user "node", exposes 2567/tcp and 2567/udp, and checks its own /game-server/health.
5
+ # TLS: edge (a proxy or the platform terminates it) unless you enable it in the config.
6
+ # UDP: off unless you enable it in the config (it also needs TLS and a host that forwards UDP).
7
+ # Auth: set GAME_SERVER_AUTH_* to enable.
8
+ FROM ghcr.io/mackeyk24/babylon-game-server:{{version}}
9
+ COPY babylon-game-server.config.mjs /app/config/
10
+ ENV GAME_SERVER_CONFIG=/app/config/babylon-game-server.config.mjs
11
+ # Local dedicated scenes (exported glTF folders) referenced by dedicated.scenes[].sceneRoot:
12
+ # COPY scenes/ /app/scenes/
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "Babylon Game Server",
3
+ "description": "Babylon Game Server {{version}} for Babylon Toolkit multiplayer games. TLS: edge (the Heroku router). UDP: not supported on this host (WebSocket only). Auth: set GAME_SERVER_AUTH_* to enable.",
4
+ "stack": "container",
5
+ "env": {
6
+ "GAME_SERVER_TRUST_PROXY": {
7
+ "description": "The Heroku router sends the player's address in x-forwarded-for.",
8
+ "value": "true"
9
+ },
10
+ "GAME_SERVER_DRAIN_SECONDS": {
11
+ "description": "Seconds running sessions may continue after SIGTERM; Heroku stops a dyno 30 s after SIGTERM.",
12
+ "value": "25"
13
+ }
14
+ },
15
+ "formation": {
16
+ "web": {
17
+ "quantity": 1,
18
+ "size": "basic"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,118 @@
1
+ <!--
2
+ TLS: edge (ecs-fargate-alb.yml, Elastic Beanstalk) or server (ecs-fargate-nlb-quic.yml, single EC2)
3
+ UDP: supported with ecs-fargate-nlb-quic.yml and single EC2; not supported on this host with an ALB or Elastic Beanstalk
4
+ Auth: set GAME_SERVER_AUTH_* to enable
5
+ -->
6
+
7
+ # Babylon Game Server {{version}} on AWS
8
+
9
+ Three ways to run the server on AWS, from simplest to most capable. All of them use the published image
10
+ `ghcr.io/mackeyk24/babylon-game-server:{{version}}`, or your own image built from `../Dockerfile` and pushed to ECR
11
+ (pass it as the `Image` parameter). Nothing here is deployed for you: run the commands with your own AWS account.
12
+
13
+ ## ECS Fargate behind an Application Load Balancer (`ecs-fargate-alb.yml`)
14
+
15
+ WebSocket only. The ALB terminates HTTPS with an ACM certificate for your domain, forwards to the tasks on port 2567,
16
+ checks `/game-server/ready`, and keeps idle WebSockets for 3600 s.
17
+
18
+ 1. Request (or import) a certificate for your domain in ACM, in the stack's region.
19
+ 2. Deploy:
20
+
21
+ ```sh
22
+ aws cloudformation deploy \
23
+ --template-file aws/ecs-fargate-alb.yml \
24
+ --stack-name babylon-game-server \
25
+ --capabilities CAPABILITY_IAM \
26
+ --parameter-overrides \
27
+ VpcId=vpc-0123456789abcdef0 \
28
+ SubnetIds=subnet-aaaa,subnet-bbbb \
29
+ CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/... \
30
+ DesiredCount=1
31
+ ```
32
+
33
+ 3. Point your domain at the `LoadBalancerDnsName` output (CNAME, or a Route 53 alias record), then set the game's
34
+ `serverUrl` to `wss://<your domain>`.
35
+
36
+ ## ECS Fargate behind a Network Load Balancer with QUIC (`ecs-fargate-nlb-quic.yml`)
37
+
38
+ WebSocket **and** HTTP/3 WebTransport. A `TCP_QUIC` listener on 443 passes TCP and QUIC through to the tasks on 2567,
39
+ and the server terminates TLS itself with a PEM certificate from Secrets Manager (`GAME_SERVER_TLS_CERT_PEM` /
40
+ `GAME_SERVER_TLS_KEY_PEM`), with `GAME_SERVER_TLS_ENABLED=true` and `GAME_SERVER_UDP_ENABLED=true`.
41
+
42
+ 1. Store the certificate (for example from certbot) as one JSON secret with the keys `certPem` and `keyPem`:
43
+
44
+ ```sh
45
+ aws secretsmanager create-secret --name babylon-game-server-tls \
46
+ --secret-string "$(jq -n --rawfile certPem fullchain.pem --rawfile keyPem privkey.pem '{certPem: $certPem, keyPem: $keyPem}')"
47
+ ```
48
+
49
+ 2. Deploy:
50
+
51
+ ```sh
52
+ aws cloudformation deploy \
53
+ --template-file aws/ecs-fargate-nlb-quic.yml \
54
+ --stack-name babylon-game-server-quic \
55
+ --capabilities CAPABILITY_IAM \
56
+ --parameter-overrides \
57
+ VpcId=vpc-0123456789abcdef0 \
58
+ SubnetIds=subnet-aaaa,subnet-bbbb \
59
+ TlsSecretArn=arn:aws:secretsmanager:us-east-1:123456789012:secret:babylon-game-server-tls-AbCdEf
60
+ ```
61
+
62
+ 3. Point your domain at the `LoadBalancerDnsName` output.
63
+
64
+ QUIC listeners cannot have security groups, so the task security group admits 2567 TCP and UDP from anywhere. QUIC
65
+ on Network Load Balancers is newer than the rest of this setup: check the current Elastic Load Balancing documentation
66
+ for QUIC listener and target requirements in your region before you depend on it. A renewed certificate reaches
67
+ `wss://` without a restart when it comes from files; from Secrets Manager it is read at task start, so roll the
68
+ service (`aws ecs update-service --force-new-deployment`) after rotating the secret.
69
+
70
+ ## Alternative: one EC2 instance with an Elastic IP (HTTP/3 without a load balancer)
71
+
72
+ The most direct way to offer HTTP/3: the instance's address receives TCP and UDP 443 itself.
73
+
74
+ 1. Launch an instance (for example Amazon Linux 2023, t4g.small) with Docker installed, and associate an Elastic IP.
75
+ Point your domain at the Elastic IP.
76
+ 2. In its security group, open **TCP 443 and UDP 443** to `0.0.0.0/0` (and TCP 80 if certbot uses the HTTP challenge).
77
+ 3. Get a certificate (`certbot certonly --standalone -d game.example.com`) and copy `fullchain.pem` / `privkey.pem`
78
+ into `./certs`, readable by the container user.
79
+ 4. Run the image with TLS and UDP enabled, either with `../docker-compose.full.yml` or directly:
80
+
81
+ ```sh
82
+ docker run -d --restart unless-stopped -p 443:443/tcp -p 443:443/udp \
83
+ -v "$PWD/certs:/certs:ro" \
84
+ -e GAME_SERVER_PORT=443 \
85
+ -e GAME_SERVER_TLS_ENABLED=true \
86
+ -e GAME_SERVER_TLS_CERT_FILE=/certs/fullchain.pem \
87
+ -e GAME_SERVER_TLS_KEY_FILE=/certs/privkey.pem \
88
+ -e GAME_SERVER_UDP_ENABLED=true \
89
+ ghcr.io/mackeyk24/babylon-game-server:{{version}}
90
+ ```
91
+
92
+ ## Elastic Beanstalk notes
93
+
94
+ - Use the **Docker** platform and deploy the folder with `../Dockerfile` (`eb init -p docker`, `eb create`).
95
+ - Beanstalk puts an Application Load Balancer in front: attach your ACM certificate to a 443 HTTPS listener, forward to
96
+ instance port 2567, set the health check path to `/game-server/ready`, and raise the ALB idle timeout to 3600 s so
97
+ WebSockets are not cut.
98
+ - WebSocket only: the ALB does not carry UDP, so HTTP/3 is not available on Beanstalk.
99
+
100
+ ## Scaling
101
+
102
+ One task holds each session (a session never spans processes). To run more than one task, set `GAME_SERVER_REDIS_URL`
103
+ on every task (for example ElastiCache) and give each task a `GAME_SERVER_PUBLIC_ADDRESS` that players can reach it on
104
+ directly, because seat reservations send each player to the task that holds the session. A single load balancer
105
+ address cannot do that on its own, so keep `DesiredCount=1` per stack until per-task addresses are in place.
106
+
107
+ ## Admin console data
108
+
109
+ The admin console (`/game-server/admin/`) keeps its accounts, bans and audit log in Redis when `GAME_SERVER_REDIS_URL`
110
+ is set, and otherwise in files under `GAME_SERVER_ADMIN_DATA_DIR` (`/data` in the templates). Fargate task storage is
111
+ lost whenever a task stops or is replaced, so on ECS use Redis as the console's store: create an ElastiCache for Redis
112
+ (or Valkey) cluster in the tasks' VPC, allow TCP 6379 from the task security group, and add `GAME_SERVER_REDIS_URL`
113
+ (through Secrets Manager when it carries a password) to the container's `Environment` or `Secrets`. Turn on ElastiCache
114
+ backups, because the audit log and the bans live there. Mounting an EFS volume at `/data` also works but is not part of
115
+ these templates. The console needs HTTPS: the ALB template's HTTPS listener and the NLB template's server TLS both
116
+ qualify. The first start logs `admin setup link` in CloudWatch: open its path
117
+ (`/game-server/admin/#/setup?token=…`) on your HTTPS address within 30 minutes to create the first Owner, or run
118
+ `babylon-game-server admin create-owner` against the same Redis.
@@ -0,0 +1,207 @@
1
+ # TLS: edge (the Application Load Balancer terminates HTTPS with your ACM certificate)
2
+ # UDP: not supported on this host (an ALB forwards HTTP and WebSocket only; see aws/ecs-fargate-nlb-quic.yml for HTTP/3)
3
+ # Auth: set GAME_SERVER_AUTH_* to enable (add them to the container Environment below, secrets through Secrets Manager)
4
+ #
5
+ # Babylon Game Server {{version}} on ECS Fargate behind an Application Load Balancer: players connect with
6
+ # wss://<your domain>, which you point (CNAME / alias) at the LoadBalancerDnsName output.
7
+ # Long-form intrinsics only (Ref: / Fn::GetAtt:), so any YAML parser can check the template.
8
+ # aws cloudformation deploy --template-file aws/ecs-fargate-alb.yml --stack-name babylon-game-server \
9
+ # --capabilities CAPABILITY_IAM --parameter-overrides VpcId=vpc-... SubnetIds=subnet-a,subnet-b CertificateArn=arn:aws:acm:...
10
+ # Keep DesiredCount at 1 unless sessions are routed to the task that holds them (see aws/README.md, Scaling).
11
+ AWSTemplateFormatVersion: "2010-09-09"
12
+ Description: Babylon Game Server {{version}} on ECS Fargate behind an Application Load Balancer (WebSocket, TLS at the ALB)
13
+
14
+ Parameters:
15
+ VpcId:
16
+ Type: AWS::EC2::VPC::Id
17
+ Description: The VPC the load balancer and the tasks run in.
18
+ SubnetIds:
19
+ Type: List<AWS::EC2::Subnet::Id>
20
+ Description: Two or more public subnets in different availability zones.
21
+ CertificateArn:
22
+ Type: String
23
+ Description: ARN of the ACM certificate for your game server domain.
24
+ Image:
25
+ Type: String
26
+ Default: ghcr.io/mackeyk24/babylon-game-server:{{version}}
27
+ Description: The server image (or your own image built from the init Dockerfile).
28
+ DesiredCount:
29
+ Type: Number
30
+ Default: 1
31
+ Description: Number of server tasks.
32
+
33
+ Resources:
34
+ Cluster:
35
+ Type: AWS::ECS::Cluster
36
+
37
+ LogGroup:
38
+ Type: AWS::Logs::LogGroup
39
+ Properties:
40
+ RetentionInDays: 14
41
+
42
+ TaskExecutionRole:
43
+ Type: AWS::IAM::Role
44
+ Properties:
45
+ AssumeRolePolicyDocument:
46
+ Version: "2012-10-17"
47
+ Statement:
48
+ - Effect: Allow
49
+ Principal:
50
+ Service: ecs-tasks.amazonaws.com
51
+ Action: sts:AssumeRole
52
+ ManagedPolicyArns:
53
+ - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
54
+
55
+ TaskDefinition:
56
+ Type: AWS::ECS::TaskDefinition
57
+ Properties:
58
+ Family: babylon-game-server
59
+ RequiresCompatibilities:
60
+ - FARGATE
61
+ NetworkMode: awsvpc
62
+ Cpu: "512"
63
+ Memory: "1024"
64
+ ExecutionRoleArn:
65
+ Fn::GetAtt: [TaskExecutionRole, Arn]
66
+ ContainerDefinitions:
67
+ - Name: game-server
68
+ Image:
69
+ Ref: Image
70
+ Essential: true
71
+ # Longer than the 30 s drain, so running sessions finish before the task is stopped.
72
+ StopTimeout: 45
73
+ PortMappings:
74
+ - ContainerPort: 2567
75
+ Protocol: tcp
76
+ Environment:
77
+ - Name: GAME_SERVER_LOG_FORMAT
78
+ Value: json
79
+ # Admin console data (accounts, bans, audit log). Fargate task storage is lost when the task stops: add
80
+ # GAME_SERVER_REDIS_URL (ElastiCache, see aws/README.md) so the console keeps its data in Redis.
81
+ - Name: GAME_SERVER_ADMIN_DATA_DIR
82
+ Value: /data
83
+ # The ALB sends the player's address in x-forwarded-for.
84
+ - Name: GAME_SERVER_TRUST_PROXY
85
+ Value: "true"
86
+ LogConfiguration:
87
+ LogDriver: awslogs
88
+ Options:
89
+ awslogs-group:
90
+ Ref: LogGroup
91
+ awslogs-region:
92
+ Ref: AWS::Region
93
+ awslogs-stream-prefix: game-server
94
+
95
+ LoadBalancerSecurityGroup:
96
+ Type: AWS::EC2::SecurityGroup
97
+ Properties:
98
+ GroupDescription: Babylon Game Server load balancer (HTTPS from anywhere)
99
+ VpcId:
100
+ Ref: VpcId
101
+ SecurityGroupIngress:
102
+ - IpProtocol: tcp
103
+ FromPort: 443
104
+ ToPort: 443
105
+ CidrIp: 0.0.0.0/0
106
+
107
+ ServiceSecurityGroup:
108
+ Type: AWS::EC2::SecurityGroup
109
+ Properties:
110
+ GroupDescription: Babylon Game Server tasks (port 2567 from the load balancer only)
111
+ VpcId:
112
+ Ref: VpcId
113
+ SecurityGroupIngress:
114
+ - IpProtocol: tcp
115
+ FromPort: 2567
116
+ ToPort: 2567
117
+ SourceSecurityGroupId:
118
+ Ref: LoadBalancerSecurityGroup
119
+
120
+ LoadBalancer:
121
+ Type: AWS::ElasticLoadBalancingV2::LoadBalancer
122
+ Properties:
123
+ Type: application
124
+ Scheme: internet-facing
125
+ Subnets:
126
+ Ref: SubnetIds
127
+ SecurityGroups:
128
+ - Ref: LoadBalancerSecurityGroup
129
+ LoadBalancerAttributes:
130
+ # WebSocket sessions stay open; the default 60 s idle timeout would cut quiet players.
131
+ - Key: idle_timeout.timeout_seconds
132
+ Value: "3600"
133
+
134
+ TargetGroup:
135
+ Type: AWS::ElasticLoadBalancingV2::TargetGroup
136
+ Properties:
137
+ VpcId:
138
+ Ref: VpcId
139
+ Protocol: HTTP
140
+ Port: 2567
141
+ TargetType: ip
142
+ HealthCheckProtocol: HTTP
143
+ HealthCheckPath: /game-server/ready
144
+ HealthCheckIntervalSeconds: 15
145
+ HealthyThresholdCount: 2
146
+ UnhealthyThresholdCount: 3
147
+ Matcher:
148
+ HttpCode: "200"
149
+ TargetGroupAttributes:
150
+ - Key: deregistration_delay.timeout_seconds
151
+ Value: "45"
152
+
153
+ HttpsListener:
154
+ Type: AWS::ElasticLoadBalancingV2::Listener
155
+ Properties:
156
+ LoadBalancerArn:
157
+ Ref: LoadBalancer
158
+ Port: 443
159
+ Protocol: HTTPS
160
+ SslPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06
161
+ Certificates:
162
+ - CertificateArn:
163
+ Ref: CertificateArn
164
+ DefaultActions:
165
+ - Type: forward
166
+ TargetGroupArn:
167
+ Ref: TargetGroup
168
+
169
+ Service:
170
+ Type: AWS::ECS::Service
171
+ DependsOn: HttpsListener
172
+ Properties:
173
+ Cluster:
174
+ Ref: Cluster
175
+ LaunchType: FARGATE
176
+ DesiredCount:
177
+ Ref: DesiredCount
178
+ TaskDefinition:
179
+ Ref: TaskDefinition
180
+ HealthCheckGracePeriodSeconds: 30
181
+ NetworkConfiguration:
182
+ AwsvpcConfiguration:
183
+ # A public IP lets the task pull the image from ghcr.io without a NAT gateway.
184
+ AssignPublicIp: ENABLED
185
+ Subnets:
186
+ Ref: SubnetIds
187
+ SecurityGroups:
188
+ - Ref: ServiceSecurityGroup
189
+ LoadBalancers:
190
+ - ContainerName: game-server
191
+ ContainerPort: 2567
192
+ TargetGroupArn:
193
+ Ref: TargetGroup
194
+
195
+ Outputs:
196
+ LoadBalancerDnsName:
197
+ Description: Point your game server domain (CNAME or Route 53 alias) here.
198
+ Value:
199
+ Fn::GetAtt: [LoadBalancer, DNSName]
200
+ HealthUrl:
201
+ Description: The readiness check through the load balancer (use your domain, which the certificate covers).
202
+ Value:
203
+ Fn::Join:
204
+ - ""
205
+ - - "https://"
206
+ - Fn::GetAtt: [LoadBalancer, DNSName]
207
+ - /game-server/ready