@phalanx-engine/server 0.1.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 (54) hide show
  1. package/README.md +507 -0
  2. package/dist/Phalanx.d.ts +85 -0
  3. package/dist/Phalanx.d.ts.map +1 -0
  4. package/dist/Phalanx.js +600 -0
  5. package/dist/Phalanx.js.map +1 -0
  6. package/dist/config/defaults.d.ts +10 -0
  7. package/dist/config/defaults.d.ts.map +1 -0
  8. package/dist/config/defaults.js +51 -0
  9. package/dist/config/defaults.js.map +1 -0
  10. package/dist/config/validation.d.ts +15 -0
  11. package/dist/config/validation.d.ts.map +1 -0
  12. package/dist/config/validation.js +74 -0
  13. package/dist/config/validation.js.map +1 -0
  14. package/dist/index.d.ts +12 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +14 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/services/GameRoom.d.ts +359 -0
  19. package/dist/services/GameRoom.d.ts.map +1 -0
  20. package/dist/services/GameRoom.js +1272 -0
  21. package/dist/services/GameRoom.js.map +1 -0
  22. package/dist/services/MatchmakingService.d.ts +102 -0
  23. package/dist/services/MatchmakingService.d.ts.map +1 -0
  24. package/dist/services/MatchmakingService.js +286 -0
  25. package/dist/services/MatchmakingService.js.map +1 -0
  26. package/dist/services/OAuthExchangeService.d.ts +49 -0
  27. package/dist/services/OAuthExchangeService.d.ts.map +1 -0
  28. package/dist/services/OAuthExchangeService.js +96 -0
  29. package/dist/services/OAuthExchangeService.js.map +1 -0
  30. package/dist/services/PrivateRoomService.d.ts +136 -0
  31. package/dist/services/PrivateRoomService.d.ts.map +1 -0
  32. package/dist/services/PrivateRoomService.js +395 -0
  33. package/dist/services/PrivateRoomService.js.map +1 -0
  34. package/dist/services/TokenValidator.d.ts +60 -0
  35. package/dist/services/TokenValidator.d.ts.map +1 -0
  36. package/dist/services/TokenValidator.js +213 -0
  37. package/dist/services/TokenValidator.js.map +1 -0
  38. package/dist/types/index.d.ts +390 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +6 -0
  41. package/dist/types/index.js.map +1 -0
  42. package/dist/utils/DeterministicRandom.d.ts +88 -0
  43. package/dist/utils/DeterministicRandom.d.ts.map +1 -0
  44. package/dist/utils/DeterministicRandom.js +140 -0
  45. package/dist/utils/DeterministicRandom.js.map +1 -0
  46. package/dist/utils/FixedMath.d.ts +22 -0
  47. package/dist/utils/FixedMath.d.ts.map +1 -0
  48. package/dist/utils/FixedMath.js +26 -0
  49. package/dist/utils/FixedMath.js.map +1 -0
  50. package/dist/utils/index.d.ts +7 -0
  51. package/dist/utils/index.d.ts.map +1 -0
  52. package/dist/utils/index.js +6 -0
  53. package/dist/utils/index.js.map +1 -0
  54. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,507 @@
1
+ # Phalanx Server
2
+
3
+ Server component of [Phalanx Engine](../README.md) - a game-agnostic deterministic lockstep multiplayer engine with authentication, matchmaking, and command synchronization.
4
+
5
+ ## Features
6
+
7
+ - **Deterministic Lockstep**: Synchronizes only player commands, game logic runs deterministically on each client
8
+ - **Matchmaking**: Built-in support for various game modes (1v1, 2v2, 3v3, 4v4, FFA)
9
+ - **Tick System**: Configurable tick rate with command batching
10
+ - **Reconnection Support**: Players can rejoin matches after disconnection
11
+ - **TypeScript**: Full TypeScript support with exported types
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @phalanx-engine/server
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { Phalanx } from '@phalanx-engine/server';
23
+
24
+ const app = new Phalanx({
25
+ port: 3000,
26
+ tickRate: 20,
27
+ gameMode: '3v3',
28
+ });
29
+
30
+ app.start().then(() => {
31
+ console.log('Phalanx server running on port 3000');
32
+ });
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ ```typescript
38
+ import { Phalanx, PhalanxConfig } from '@phalanx-engine/server';
39
+
40
+ const config: Partial<PhalanxConfig> = {
41
+ // === Server ===
42
+ port: 3000, // Server port (default: 3000)
43
+ cors: { origin: '*' }, // CORS configuration
44
+ extraRequestHandler: async (req, res) => {
45
+ // Optional: mount trusted webhooks or custom HTTP endpoints on the
46
+ // same HTTP server. Return true when the request was fully handled.
47
+ if (req.method === 'POST' && req.url === '/telegram/webhook') {
48
+ res.writeHead(204);
49
+ res.end();
50
+ return true;
51
+ }
52
+ return false;
53
+ },
54
+
55
+ // === TLS/SSL (optional) ===
56
+ // See "TLS/WSS Configuration" section below for details
57
+ tls: {
58
+ enabled: true,
59
+ keyPath: '/path/to/privkey.pem',
60
+ certPath: '/path/to/fullchain.pem',
61
+ },
62
+
63
+ // === Authentication (optional) ===
64
+ auth: {
65
+ enabled: false, // Set true to require auth tokens
66
+ },
67
+
68
+ // === Tick System ===
69
+ tickRate: 20, // Ticks per second (default: 20)
70
+ tickDeadlineMs: 50, // Max wait for commands per tick
71
+
72
+ // === Matchmaking ===
73
+ gameMode: '3v3', // Preset: '1v1' | '2v2' | '3v3' | '4v4' | 'FFA4'
74
+ // OR custom:
75
+ // gameMode: { playersPerMatch: 6, teamsCount: 2 },
76
+ matchmakingIntervalMs: 1000,
77
+ countdownSeconds: 5,
78
+
79
+ // === Timeouts ===
80
+ timeoutTicks: 40, // Ticks before "lagging" warning
81
+ disconnectTicks: 100, // Ticks before disconnect
82
+ reconnectGracePeriodMs: 30000,
83
+
84
+ // === Command Validation ===
85
+ maxTickBehind: 10,
86
+ maxTickAhead: 5,
87
+
88
+ // === Command History (for reconnection) ===
89
+ commandHistoryTicks: 200, // Ticks of command history to keep
90
+
91
+ // === Ready Handshake ===
92
+ readyTimeoutMs: 30000, // Max time for clients to send ready (default: 30000)
93
+
94
+ // === Determinism / Desync Detection ===
95
+ enableStateHashing: true, // Enable server-side hash comparison
96
+ stateHashInterval: 60, // Hint interval for hash submission (default: 60)
97
+ desync: {
98
+ enabled: true,
99
+ action: 'end-match', // 'log-only' | 'end-match'
100
+ gracePeriodTicks: 1, // Consecutive desyncs before taking action
101
+ },
102
+
103
+ // === Pause/Resume ===
104
+ pause: {
105
+ maxPausesPerPlayer: 3,
106
+ requireSamePlayerToResume: true,
107
+ },
108
+
109
+ // === Game Types (multi-game-type routing) ===
110
+ tickMode: 'continuous', // Default tick mode: 'continuous' | 'event'
111
+ turnTimeoutMs: 60000, // Turn timeout for event mode (default: 60000)
112
+ gameTypes: [
113
+ {
114
+ gameType: 'moba',
115
+ tickMode: 'continuous',
116
+ tickRate: 20,
117
+ gameMode: '1v1',
118
+ countdownSeconds: 3,
119
+ },
120
+ {
121
+ gameType: 'tactics',
122
+ tickMode: 'event',
123
+ gameMode: '1v1',
124
+ countdownSeconds: 3,
125
+ turnTimeoutMs: 90000,
126
+ },
127
+ ],
128
+ };
129
+
130
+ const app = new Phalanx(config);
131
+ ```
132
+
133
+ ## Event Hooks
134
+
135
+ ```typescript
136
+ app.on('match-created', (match) => {
137
+ console.log(`Match ${match.id} created with ${match.players.length} players`);
138
+ });
139
+
140
+ app.on('match-started', (match) => {
141
+ console.log(`Match ${match.id} started!`);
142
+ });
143
+
144
+ app.on('player-command', (playerId, command) => {
145
+ // Custom command validation
146
+ return true; // or false to reject
147
+ });
148
+
149
+ app.on('player-timeout', (playerId, matchId) => {
150
+ console.log(`Player ${playerId} timed out`);
151
+ });
152
+
153
+ app.on('player-disconnected', (playerId, matchId) => {
154
+ console.log(`Player ${playerId} disconnected from match ${matchId}`);
155
+ });
156
+
157
+ app.on('player-reconnected', (playerId, matchId) => {
158
+ console.log(`Player ${playerId} reconnected to match ${matchId}`);
159
+ });
160
+ ```
161
+
162
+ ## HTTP Extension Hooks
163
+
164
+ Use `extraRequestHandler` when an embedding server needs a small HTTP surface on the same port as Phalanx, such as a Telegram webhook, an internal health probe, or a signed partner callback.
165
+
166
+ ```typescript
167
+ import { Phalanx } from '@phalanx-engine/server';
168
+
169
+ const app = new Phalanx({
170
+ port: 3000,
171
+ extraRequestHandler: async (req, res) => {
172
+ if (req.method !== 'POST' || req.url !== '/telegram/webhook') {
173
+ return false; // fall through to Phalanx built-in routes
174
+ }
175
+
176
+ if (req.headers['x-telegram-bot-api-secret-token'] !== process.env.WEBHOOK_SECRET) {
177
+ res.writeHead(401);
178
+ res.end();
179
+ return true;
180
+ }
181
+
182
+ // Parse and handle the request body here.
183
+ res.writeHead(200);
184
+ res.end();
185
+ return true;
186
+ },
187
+ });
188
+ ```
189
+
190
+ Request routing order is:
191
+
192
+ 1. CORS preflight (`OPTIONS`) is answered by Phalanx.
193
+ 2. `extraRequestHandler` is called.
194
+ 3. Built-in routes run (`/`, `/health`, `/auth/token`).
195
+ 4. Unknown routes receive `404`.
196
+
197
+ Handler contract:
198
+
199
+ - Return `true` only after fully handling the request, including writing headers/body and ending the response.
200
+ - Return `false` to let Phalanx continue with its built-in routing.
201
+ - Set any route-specific CORS or content headers yourself for handled custom routes.
202
+ - Parse and size-limit request bodies yourself; Phalanx only parses bodies for its own built-in routes.
203
+ - If the handler throws, Phalanx logs the error and sends a generic `500` response when headers have not already been sent.
204
+
205
+ Treat custom HTTP routes as public internet-facing endpoints. Validate webhook secrets or signatures, avoid exposing trusted administrative operations to browsers, and apply rate limiting where appropriate.
206
+
207
+ ## API
208
+
209
+ ### Phalanx Class
210
+
211
+ ```typescript
212
+ class Phalanx {
213
+ constructor(config?: Partial<PhalanxConfig>);
214
+
215
+ // Lifecycle
216
+ start(): Promise<void>;
217
+ stop(): Promise<void>;
218
+
219
+ // Trusted server-side integrations
220
+ createPrivateRoomForHost(params: {
221
+ playerId: string;
222
+ username?: string;
223
+ gameType?: string;
224
+ }): RoomCreatedEvent;
225
+
226
+ // Events
227
+ on(event: PhalanxEvent, handler: Function): this;
228
+ off(event: PhalanxEvent, handler: Function): this;
229
+
230
+ // Runtime info
231
+ getActiveMatches(): MatchInfo[];
232
+ getQueueSize(): number;
233
+ getConfig(): PhalanxConfig;
234
+ }
235
+ ```
236
+
237
+ ### Client Events
238
+
239
+ | Event | Emit | Receive | Description |
240
+ | --------------------- | ---- | ------- | --------------------------------- |
241
+ | `queue-join` | ✅ | | Join matchmaking queue |
242
+ | `queue-leave` | ✅ | | Leave matchmaking queue |
243
+ | `queue-status` | | ✅ | Queue join/leave confirmation |
244
+ | `match-found` | | ✅ | Match created, countdown starting |
245
+ | `match-waiting-for-players` | | ✅ | Match is waiting for disconnected participants before countdown |
246
+ | `game-start` | | ✅ | Countdown finished, load assets |
247
+ | `client-ready` | ✅ | | Client finished loading, ready for ticks |
248
+ | `player-ready` | | ✅ | A player reported ready |
249
+ | `match-end` | | ✅ | Match has ended |
250
+ | `submit-commands` | ✅ | | Send game commands |
251
+ | `submit-commands-ack` | | ✅ | Command acknowledgment |
252
+ | `commands-batch` | | ✅ | All commands for a tick |
253
+ | `tick-sync` | | ✅ | Periodic tick synchronization |
254
+ | `countdown` | | ✅ | Countdown before game starts |
255
+ | `submit-state-hash` | ✅ | | Submit state hash for desync detection |
256
+ | `hash-comparison` | | ✅ | Server hash comparison result |
257
+ | `pause-game` | ✅ | | Request game pause |
258
+ | `resume-game` | ✅ | | Request game resume |
259
+ | `game-paused` | | ✅ | Game was paused |
260
+ | `game-resumed` | | ✅ | Game was resumed |
261
+ | `reconnect-match` | ✅ | | Attempt to rejoin a match |
262
+ | `reconnect-status` | | ✅ | Reconnection result |
263
+ | `reconnect-state` | | ✅ | Game state for reconnection |
264
+ | `player-disconnected` | | ✅ | Another player disconnected |
265
+ | `player-reconnected` | | ✅ | Another player reconnected |
266
+ | `room-create` | ✅ | | Create a private room |
267
+ | `room-join` | ✅ | | Join a private room by invite code |
268
+ | `room-cancel` | ✅ | | Cancel a previously created room |
269
+ | `room-recover` | ✅ | | Reclaim a room after socket drop (see Private Room Recovery) |
270
+ | `room-created` | | ✅ | Room created; contains invite `code` |
271
+ | `room-error` | | ✅ | Room operation failed; contains `message` |
272
+ | `room-expired` | | ✅ | Room TTL exceeded; server evicted the room |
273
+ | `room-cancelled` | | ✅ | Host cancelled the room |
274
+ | `room-recovered` | | ✅ | Room successfully reclaimed after socket drop |
275
+
276
+ ### Game Start Synchronization
277
+
278
+ After the countdown completes, the server emits `game-start` and enters a `waiting-for-ready` state. The tick loop does **not** start until all connected clients send `client-ready`. This prevents desync caused by clients with different asset loading times missing early ticks.
279
+
280
+ If a client does not send `client-ready` within 30 seconds, the match ends with reason `'ready-timeout'`.
281
+
282
+ ### Private Room Recovery
283
+
284
+ Private rooms support a `room-recover` handshake so a participant whose socket was dropped (e.g. mobile OS suspended the WebSocket while the user was sharing the invite link) can seamlessly reclaim either a waiting room or a private-room match without losing the pending join or countdown state.
285
+
286
+ `room-recover` requires both the deterministic `playerId` and the original room `code`. Treat the room code as a bearer recovery secret: possession of the code is what proves the caller is allowed to recover that waiting room or match.
287
+
288
+ #### Recovery protocol
289
+
290
+ 1. Client calls `room-recover` with `{ playerId, code }` after reconnecting.
291
+ 2. If the code still maps to a waiting room, only the original host can recover it. The server rebinds the host socket and emits `room-recovered`.
292
+ 3. If the code has already been consumed into a match, any participant in that match can recover with the original code. The server emits `room-recovered` and delegates to the match reconnect flow.
293
+ 4. If the match is still waiting for disconnected participants, reconnecting everyone starts the countdown. If the match is already in progress, the normal reconnect state is delivered.
294
+ 5. Server responds with `room-error: { message: 'Room expired' }` on terminal failure. The same generic message is used for missing, expired, wrong-player, and already-finished cases so callers cannot probe room ownership.
295
+
296
+ #### Server-side room TTL
297
+
298
+ Waiting rooms are kept alive for `ROOM_TTL_MS` (default 5 minutes) from creation. If the host disconnects, the room remains recoverable until that original TTL expires; if no host socket is connected at expiry time, there is no `room-expired` recipient to notify. Clients should mirror this TTL in their local persistence layer so they do not attempt `room-recover` for a room the server has certainly already evicted.
299
+
300
+ The client-side [`RoomRecoveryController`](../phalanx-client/README.md#mobile-friendly-room-recovery) handles the full recovery lifecycle automatically when `roomRecovery.enabled: true` is passed to `PhalanxClient`.
301
+
302
+ ### Trusted Server-Side Private Room Creation
303
+
304
+ Trusted integrations can create a private room for a host that is not currently connected over Socket.IO. This is intended for server-side entry points such as bot commands: the bot creates a room, sends the invite link containing the room code, and the host later opens the game and recovers the room with the same deterministic `playerId`.
305
+
306
+ ```typescript
307
+ const room = app.createPrivateRoomForHost({
308
+ playerId: `telegram:${telegramUserId}`,
309
+ username: telegramUsername,
310
+ gameType: 'chapayev',
311
+ });
312
+
313
+ const inviteUrl = `https://game.example.com/?room=${room.code}`;
314
+ ```
315
+
316
+ `createPrivateRoomForHost`:
317
+
318
+ - Requires `playerId` and accepts optional `username` and `gameType`.
319
+ - Defaults `username` to `playerId` and `gameType` to the default game type when omitted.
320
+ - Returns `{ code }`. If the same host already has an active waiting room, the existing room code is reused rather than creating a duplicate room.
321
+ - Throws if Phalanx is not running, if the host is already queued for matchmaking, or if the host is already in an active private-room match.
322
+ - Does not emit `room-created`, because there is no host socket yet.
323
+
324
+ Typical bot/webhook flow:
325
+
326
+ 1. A trusted HTTP handler validates the webhook request.
327
+ 2. The handler derives a stable server-trusted `playerId` from the external identity, such as `telegram:123456`.
328
+ 3. The handler calls `createPrivateRoomForHost` and sends the invite URL/code back through the external channel.
329
+ 4. The host opens the game and emits `room-recover` with the same `playerId` and code.
330
+ 5. The guest opens the invite and emits `room-join` with the code.
331
+
332
+ Do not expose `createPrivateRoomForHost` as an unauthenticated browser endpoint. Browser clients should continue using the Socket.IO `room-create` flow, while trusted server integrations should authenticate the external request before creating rooms.
333
+
334
+ ## Game Types
335
+
336
+ Phalanx supports running multiple game types on a single server instance. Each game type can override base config values like `tickMode`, `tickRate`, `gameMode`, `countdownSeconds`, and `turnTimeoutMs`.
337
+
338
+ ### Configuration
339
+
340
+ ```typescript
341
+ const app = new Phalanx({
342
+ tickRate: 20,
343
+ gameMode: '1v1',
344
+ gameTypes: [
345
+ {
346
+ gameType: 'direct-strike',
347
+ tickMode: 'continuous',
348
+ tickRate: 20,
349
+ gameMode: '1v1',
350
+ },
351
+ {
352
+ gameType: 'chapayev',
353
+ tickMode: 'event',
354
+ gameMode: '1v1',
355
+ turnTimeoutMs: 90000,
356
+ },
357
+ ],
358
+ });
359
+ ```
360
+
361
+ ### Client: Joining a Game Type Queue
362
+
363
+ Clients declare which game type they want to join via the `queue-join` event:
364
+
365
+ ```typescript
366
+ socket.emit('queue-join', {
367
+ playerId: 'player-123',
368
+ username: 'Alice',
369
+ gameType: 'chapayev', // optional — omit for default queue
370
+ });
371
+ ```
372
+
373
+ Clients that omit `gameType` are placed in a `'default'` queue and use the base config.
374
+
375
+ ### `tickMode: 'event'` (Turn-Based / Tickless)
376
+
377
+ When a game type uses `tickMode: 'event'`, no server tick loop runs. Instead:
378
+
379
+ - On `receivePlayerCommands`, the server immediately broadcasts `commands-batch` to all players in the room.
380
+ - `currentTick` increments by 1 per broadcast.
381
+ - A turn timeout (`turnTimeoutMs`, default 60000ms) starts on game start and resets on each received batch. If the timeout fires, the match ends with reason `'turn-timeout'`.
382
+ - `timeoutTicks` / `disconnectTicks` activity tracking is skipped in event mode.
383
+ - Pause/resume works identically in both modes.
384
+
385
+ This is ideal for turn-based physics games (e.g. Chapayev checkers) where game state transitions are driven by player commands only.
386
+
387
+ ### `turnTimeoutMs`
388
+
389
+ Maximum time (in milliseconds) allowed between command batches in event mode. If no commands arrive within this window, the match ends automatically. Default: `60000` (1 minute).
390
+
391
+ ## Game Modes
392
+
393
+ ```typescript
394
+ import { GAME_MODES } from '@phalanx-engine/server';
395
+
396
+ // Available presets:
397
+ // '1v1' - 2 players, 2 teams
398
+ // '2v2' - 4 players, 2 teams
399
+ // '3v3' - 6 players, 2 teams
400
+ // '4v4' - 8 players, 2 teams
401
+ // 'FFA4' - 4 players, 4 teams (Free For All)
402
+ ```
403
+
404
+ ## TLS/WSS Configuration
405
+
406
+ Phalanx supports secure WebSocket connections (WSS) for production environments.
407
+
408
+ ### Basic TLS Setup
409
+
410
+ ```typescript
411
+ import { Phalanx } from '@phalanx-engine/server';
412
+
413
+ const app = new Phalanx({
414
+ port: 443,
415
+ tls: {
416
+ enabled: true,
417
+ keyPath: '/etc/letsencrypt/live/game.example.com/privkey.pem',
418
+ certPath: '/etc/letsencrypt/live/game.example.com/fullchain.pem',
419
+ },
420
+ });
421
+
422
+ await app.start();
423
+ console.log('Phalanx server running with TLS on port 443');
424
+ ```
425
+
426
+ ### TLS Configuration Options
427
+
428
+ ```typescript
429
+ interface TlsConfig {
430
+ /** Enable TLS/SSL encryption */
431
+ enabled: boolean;
432
+ /** Path to the private key file (PEM format) */
433
+ keyPath: string;
434
+ /** Path to the certificate file (PEM format) */
435
+ certPath: string;
436
+ /** Optional path to CA certificate chain (for Let's Encrypt) */
437
+ caPath?: string;
438
+ }
439
+ ```
440
+
441
+ ### Development Mode (No TLS)
442
+
443
+ When TLS is not configured, the server runs in HTTP/WS mode:
444
+
445
+ ```typescript
446
+ const app = new Phalanx({
447
+ port: 3000,
448
+ // No tls config = development mode
449
+ });
450
+ ```
451
+
452
+ ### Let's Encrypt Setup
453
+
454
+ 1. Install certbot:
455
+ ```bash
456
+ sudo apt install certbot
457
+ ```
458
+
459
+ 2. Obtain certificates:
460
+ ```bash
461
+ sudo certbot certonly --standalone -d game.example.com
462
+ ```
463
+
464
+ 3. Configure Phalanx:
465
+ ```typescript
466
+ const app = new Phalanx({
467
+ port: 443,
468
+ tls: {
469
+ enabled: true,
470
+ keyPath: '/etc/letsencrypt/live/game.example.com/privkey.pem',
471
+ certPath: '/etc/letsencrypt/live/game.example.com/fullchain.pem',
472
+ },
473
+ });
474
+ ```
475
+
476
+ 4. Set up certificate auto-renewal:
477
+ ```bash
478
+ sudo certbot renew --dry-run
479
+ ```
480
+
481
+ > **Note**: You may need to run the server with elevated privileges for port 443, or use a reverse proxy like nginx.
482
+
483
+ ### Client Connection (WSS)
484
+
485
+ When connecting to a TLS-enabled server from the client:
486
+
487
+ ```typescript
488
+ import { PhalanxClient } from '@phalanx-engine/client';
489
+
490
+ const client = await PhalanxClient.create({
491
+ serverUrl: 'https://game.example.com', // Use https:// for TLS
492
+ playerId: 'player-123',
493
+ username: 'MyPlayer',
494
+ });
495
+ ```
496
+
497
+ ## Related Packages
498
+
499
+ - [@phalanx-engine/client](../phalanx-client) - Client library for connecting to Phalanx servers
500
+
501
+ ## Requirements
502
+
503
+ - Node.js 18+
504
+
505
+ ## License
506
+
507
+ MIT
@@ -0,0 +1,85 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { PhalanxConfig, MatchInfo, RoomCreatedEvent, PhalanxEventType, PhalanxEventHandlers } from './types/index.js';
3
+ /**
4
+ * Phalanx Engine
5
+ * A game-agnostic deterministic lockstep multiplayer engine
6
+ */
7
+ export declare class Phalanx extends EventEmitter {
8
+ private readonly config;
9
+ private httpServer;
10
+ private io;
11
+ private matchmaking;
12
+ private privateRooms;
13
+ private tokenValidator;
14
+ private oauthExchange;
15
+ private isRunning;
16
+ constructor(config?: Partial<PhalanxConfig>);
17
+ /**
18
+ * Create HTTP or HTTPS server based on TLS configuration
19
+ */
20
+ private createServer;
21
+ /**
22
+ * Set CORS headers on response
23
+ */
24
+ private setCorsHeaders;
25
+ /**
26
+ * Handle OAuth token exchange request
27
+ */
28
+ private handleTokenExchange;
29
+ /**
30
+ * Read the full request body as a string
31
+ */
32
+ private readRequestBody;
33
+ /**
34
+ * Start the Phalanx server
35
+ */
36
+ start(): Promise<void>;
37
+ /**
38
+ * Create or reuse a private room for a host that will connect later.
39
+ * Intended for trusted server-side integrations such as Telegram bot
40
+ * commands; browser clients should continue using the Socket.IO flow.
41
+ */
42
+ createPrivateRoomForHost(params: {
43
+ playerId: string;
44
+ username?: string;
45
+ gameType?: string;
46
+ }): RoomCreatedEvent;
47
+ /**
48
+ * Stop the Phalanx server
49
+ */
50
+ stop(): Promise<void>;
51
+ /**
52
+ * Setup authentication middleware for Socket.IO connections.
53
+ * Validates tokens before allowing connections.
54
+ */
55
+ private setupAuthMiddleware;
56
+ /**
57
+ * Setup socket event handlers
58
+ */
59
+ private setupSocketHandlers;
60
+ /**
61
+ * Find a GameRoom by matchId across both matchmaking and private room services.
62
+ */
63
+ private findMatch;
64
+ /**
65
+ * Register an event handler
66
+ */
67
+ on<E extends PhalanxEventType>(event: E, handler: PhalanxEventHandlers[E]): this;
68
+ /**
69
+ * Remove an event handler
70
+ */
71
+ off<E extends PhalanxEventType>(event: E, handler: PhalanxEventHandlers[E]): this;
72
+ /**
73
+ * Get all active matches (from both matchmaking and private rooms)
74
+ */
75
+ getActiveMatches(): MatchInfo[];
76
+ /**
77
+ * Get current matchmaking queue size
78
+ */
79
+ getQueueSize(): number;
80
+ /**
81
+ * Get the current configuration
82
+ */
83
+ getConfig(): PhalanxConfig;
84
+ }
85
+ //# sourceMappingURL=Phalanx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Phalanx.d.ts","sourceRoot":"","sources":["../src/Phalanx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAatC,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,gBAAgB,EAEhB,gBAAgB,EAChB,oBAAoB,EAErB,MAAM,kBAAkB,CAAC;AAyB1B;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,YAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,EAAE,CAA+B;IACzC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAK3C;;OAEG;IACH,OAAO,CAAC,YAAY;IA0EpB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2BtB;;OAEG;YACW,mBAAmB;IA6CjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAevB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmE5B;;;;OAIG;IACH,wBAAwB,CAAC,MAAM,EAAE;QAC/B,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,gBAAgB;IAYpB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC3B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA2C3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqS3B;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACM,EAAE,CAAC,CAAC,SAAS,gBAAgB,EACpC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,IAAI;IAIP;;OAEG;IACM,GAAG,CAAC,CAAC,SAAS,gBAAgB,EACrC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,IAAI;IAIP;;OAEG;IACH,gBAAgB,IAAI,SAAS,EAAE;IAM/B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,SAAS,IAAI,aAAa;CAG3B"}