@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.
- package/README.md +507 -0
- package/dist/Phalanx.d.ts +85 -0
- package/dist/Phalanx.d.ts.map +1 -0
- package/dist/Phalanx.js +600 -0
- package/dist/Phalanx.js.map +1 -0
- package/dist/config/defaults.d.ts +10 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +51 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/validation.d.ts +15 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +74 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/services/GameRoom.d.ts +359 -0
- package/dist/services/GameRoom.d.ts.map +1 -0
- package/dist/services/GameRoom.js +1272 -0
- package/dist/services/GameRoom.js.map +1 -0
- package/dist/services/MatchmakingService.d.ts +102 -0
- package/dist/services/MatchmakingService.d.ts.map +1 -0
- package/dist/services/MatchmakingService.js +286 -0
- package/dist/services/MatchmakingService.js.map +1 -0
- package/dist/services/OAuthExchangeService.d.ts +49 -0
- package/dist/services/OAuthExchangeService.d.ts.map +1 -0
- package/dist/services/OAuthExchangeService.js +96 -0
- package/dist/services/OAuthExchangeService.js.map +1 -0
- package/dist/services/PrivateRoomService.d.ts +136 -0
- package/dist/services/PrivateRoomService.d.ts.map +1 -0
- package/dist/services/PrivateRoomService.js +395 -0
- package/dist/services/PrivateRoomService.js.map +1 -0
- package/dist/services/TokenValidator.d.ts +60 -0
- package/dist/services/TokenValidator.d.ts.map +1 -0
- package/dist/services/TokenValidator.js +213 -0
- package/dist/services/TokenValidator.js.map +1 -0
- package/dist/types/index.d.ts +390 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/DeterministicRandom.d.ts +88 -0
- package/dist/utils/DeterministicRandom.d.ts.map +1 -0
- package/dist/utils/DeterministicRandom.js +140 -0
- package/dist/utils/DeterministicRandom.js.map +1 -0
- package/dist/utils/FixedMath.d.ts +22 -0
- package/dist/utils/FixedMath.d.ts.map +1 -0
- package/dist/utils/FixedMath.js +26 -0
- package/dist/utils/FixedMath.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Game mode presets configuration
|
|
3
|
+
*/
|
|
4
|
+
export const GAME_MODES = {
|
|
5
|
+
'1v1': { playersPerMatch: 2, teamsCount: 2 },
|
|
6
|
+
'2v2': { playersPerMatch: 4, teamsCount: 2 },
|
|
7
|
+
'3v3': { playersPerMatch: 6, teamsCount: 2 },
|
|
8
|
+
'4v4': { playersPerMatch: 8, teamsCount: 2 },
|
|
9
|
+
FFA4: { playersPerMatch: 4, teamsCount: 4 },
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Default configuration values
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_CONFIG = {
|
|
15
|
+
// Server
|
|
16
|
+
port: 3000,
|
|
17
|
+
cors: { origin: '*' },
|
|
18
|
+
// Tick System
|
|
19
|
+
tickRate: 20,
|
|
20
|
+
tickDeadlineMs: 50,
|
|
21
|
+
tickMode: 'continuous',
|
|
22
|
+
// Only applies to tickMode: 'event' — ignored in continuous mode
|
|
23
|
+
turnTimeoutMs: 60000,
|
|
24
|
+
gameTypes: [],
|
|
25
|
+
// Matchmaking
|
|
26
|
+
gameMode: '1v1',
|
|
27
|
+
matchmakingIntervalMs: 1000,
|
|
28
|
+
countdownSeconds: 5,
|
|
29
|
+
// Timeouts
|
|
30
|
+
timeoutTicks: 40,
|
|
31
|
+
disconnectTicks: 100,
|
|
32
|
+
reconnectGracePeriodMs: 30000,
|
|
33
|
+
// Command Validation
|
|
34
|
+
maxTickBehind: 10,
|
|
35
|
+
maxTickAhead: 5,
|
|
36
|
+
// Command History (for reconnection)
|
|
37
|
+
commandHistoryTicks: 200, // 10 seconds at 20 TPS
|
|
38
|
+
// Determinism Features (Phase 2.1) - disabled by default for backward compatibility
|
|
39
|
+
validateInputSequence: false,
|
|
40
|
+
enableStateHashing: false,
|
|
41
|
+
stateHashInterval: 60, // 3 seconds at 20 TPS
|
|
42
|
+
// Ready Handshake
|
|
43
|
+
readyTimeoutMs: 30000,
|
|
44
|
+
// Desync Detection Behavior
|
|
45
|
+
desync: {
|
|
46
|
+
enabled: true,
|
|
47
|
+
action: 'end-match', // 'log-only' | 'end-match'
|
|
48
|
+
gracePeriodTicks: 1, // Consecutive desyncs before action
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAA2C;IAChE,KAAK,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IAC5C,KAAK,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IAC5C,KAAK,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IAC5C,KAAK,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;IAC5C,IAAI,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;CAC5C,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,SAAS;IACT,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;IAErB,cAAc;IACd,QAAQ,EAAE,EAAE;IACZ,cAAc,EAAE,EAAE;IAClB,QAAQ,EAAE,YAAY;IACtB,iEAAiE;IACjE,aAAa,EAAE,KAAK;IACpB,SAAS,EAAE,EAAE;IAEb,cAAc;IACd,QAAQ,EAAE,KAAK;IACf,qBAAqB,EAAE,IAAI;IAC3B,gBAAgB,EAAE,CAAC;IAEnB,WAAW;IACX,YAAY,EAAE,EAAE;IAChB,eAAe,EAAE,GAAG;IACpB,sBAAsB,EAAE,KAAK;IAE7B,qBAAqB;IACrB,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,CAAC;IAEf,qCAAqC;IACrC,mBAAmB,EAAE,GAAG,EAAE,uBAAuB;IAEjD,oFAAoF;IACpF,qBAAqB,EAAE,KAAK;IAC5B,kBAAkB,EAAE,KAAK;IACzB,iBAAiB,EAAE,EAAE,EAAE,sBAAsB;IAE7C,kBAAkB;IAClB,cAAc,EAAE,KAAK;IAErB,4BAA4B;IAC5B,MAAM,EAAE;QACN,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,WAAW,EAAE,2BAA2B;QAChD,gBAAgB,EAAE,CAAC,EAAE,oCAAoC;KAC1D;CACF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PhalanxConfig } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates and merges user configuration with defaults
|
|
4
|
+
* @param userConfig - Partial user configuration
|
|
5
|
+
* @returns Complete validated configuration
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateConfig(userConfig?: Partial<PhalanxConfig>): PhalanxConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Gets the resolved game mode (players per match and teams count)
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveGameMode(gameMode: PhalanxConfig['gameMode']): {
|
|
12
|
+
playersPerMatch: number;
|
|
13
|
+
teamsCount: number;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/config/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,UAAU,GAAE,OAAO,CAAC,aAAa,CAAM,GACtC,aAAa,CAuEf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB,CAKA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { DEFAULT_CONFIG, GAME_MODES } from './defaults.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates and merges user configuration with defaults
|
|
4
|
+
* @param userConfig - Partial user configuration
|
|
5
|
+
* @returns Complete validated configuration
|
|
6
|
+
*/
|
|
7
|
+
export function validateConfig(userConfig = {}) {
|
|
8
|
+
const config = {
|
|
9
|
+
...DEFAULT_CONFIG,
|
|
10
|
+
...userConfig,
|
|
11
|
+
cors: {
|
|
12
|
+
...DEFAULT_CONFIG.cors,
|
|
13
|
+
...(userConfig.cors || {}),
|
|
14
|
+
},
|
|
15
|
+
desync: {
|
|
16
|
+
...DEFAULT_CONFIG.desync,
|
|
17
|
+
...(userConfig.desync || {}),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
// Validate port
|
|
21
|
+
if (config.port < 1 || config.port > 65535) {
|
|
22
|
+
throw new Error(`Invalid port: ${config.port}. Must be between 1 and 65535.`);
|
|
23
|
+
}
|
|
24
|
+
// Validate tickRate
|
|
25
|
+
if (config.tickRate < 1 || config.tickRate > 128) {
|
|
26
|
+
throw new Error(`Invalid tickRate: ${config.tickRate}. Must be between 1 and 128.`);
|
|
27
|
+
}
|
|
28
|
+
// Validate tickDeadlineMs
|
|
29
|
+
if (config.tickDeadlineMs < 1) {
|
|
30
|
+
throw new Error(`Invalid tickDeadlineMs: ${config.tickDeadlineMs}. Must be positive.`);
|
|
31
|
+
}
|
|
32
|
+
// Validate gameMode
|
|
33
|
+
if (typeof config.gameMode === 'string') {
|
|
34
|
+
if (!GAME_MODES[config.gameMode]) {
|
|
35
|
+
throw new Error(`Invalid gameMode: ${config.gameMode}. Valid presets: ${Object.keys(GAME_MODES).join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else if (typeof config.gameMode === 'object') {
|
|
39
|
+
if (config.gameMode.playersPerMatch < 2) {
|
|
40
|
+
throw new Error('playersPerMatch must be at least 2');
|
|
41
|
+
}
|
|
42
|
+
if (config.gameMode.teamsCount < 1) {
|
|
43
|
+
throw new Error('teamsCount must be at least 1');
|
|
44
|
+
}
|
|
45
|
+
if (config.gameMode.teamsCount > config.gameMode.playersPerMatch) {
|
|
46
|
+
throw new Error('teamsCount cannot exceed playersPerMatch');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Validate timeout values
|
|
50
|
+
if (config.timeoutTicks < 1) {
|
|
51
|
+
throw new Error('timeoutTicks must be positive');
|
|
52
|
+
}
|
|
53
|
+
if (config.disconnectTicks < config.timeoutTicks) {
|
|
54
|
+
throw new Error('disconnectTicks must be >= timeoutTicks');
|
|
55
|
+
}
|
|
56
|
+
// Validate tick range limits
|
|
57
|
+
if (config.maxTickBehind < 1) {
|
|
58
|
+
throw new Error('maxTickBehind must be positive');
|
|
59
|
+
}
|
|
60
|
+
if (config.maxTickAhead < 1) {
|
|
61
|
+
throw new Error('maxTickAhead must be positive');
|
|
62
|
+
}
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gets the resolved game mode (players per match and teams count)
|
|
67
|
+
*/
|
|
68
|
+
export function resolveGameMode(gameMode) {
|
|
69
|
+
if (typeof gameMode === 'string') {
|
|
70
|
+
return GAME_MODES[gameMode];
|
|
71
|
+
}
|
|
72
|
+
return gameMode;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/config/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3D;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,aAAqC,EAAE;IAEvC,MAAM,MAAM,GAAkB;QAC5B,GAAG,cAAc;QACjB,GAAG,UAAU;QACb,IAAI,EAAE;YACJ,GAAG,cAAc,CAAC,IAAI;YACtB,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC;SAC3B;QACD,MAAM,EAAE;YACN,GAAG,cAAc,CAAC,MAAM;YACxB,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC;SAC7B;KACF,CAAC;IAEF,gBAAgB;IAChB,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,iBAAiB,MAAM,CAAC,IAAI,gCAAgC,CAC7D,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,CAAC,QAAQ,8BAA8B,CACnE,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,2BAA2B,MAAM,CAAC,cAAc,qBAAqB,CACtE,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,CAAC,QAAQ,oBAAoB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,IAAI,MAAM,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,6BAA6B;IAC7B,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAmC;IAIjE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phalanx Engine
|
|
3
|
+
* A game-agnostic deterministic lockstep multiplayer engine
|
|
4
|
+
*/
|
|
5
|
+
export { Phalanx } from './Phalanx.js';
|
|
6
|
+
export { TokenValidatorService, createDevValidator, createEndpointValidator, } from './services/TokenValidator.js';
|
|
7
|
+
export { DeterministicRandom } from './utils/index.js';
|
|
8
|
+
export { FP, FPVector2, FPVector3, FixedPoint } from './utils/index.js';
|
|
9
|
+
export type { FPVector2Interface, FPVector3Interface } from './utils/index.js';
|
|
10
|
+
export type { PhalanxConfig, PlayerCommand, MatchFoundEvent, GameStartEvent, TickSyncEvent, CommandsBatchEvent, TickCommands, SubmitCommandsEvent, PlayerInfo, MatchInfo, GameMode, GameModePreset, CustomGameMode, CorsConfig, TlsConfig, QueuedPlayer, QueueStatusEvent, StateHashEvent, DesyncDetectedEvent, PhalanxEventType, PhalanxEventHandlers, AuthConfig, TokenValidator, TokenValidationResult, DesyncConfig, DesyncAction, PauseConfig, TickMode, GameTypeConfig, RoomCreatedEvent, RoomRecoveredEvent, RoomErrorEvent, } from './types/index.js';
|
|
11
|
+
export { GAME_MODES } from './config/defaults.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG/E,YAAY,EACV,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EAEpB,UAAU,EACV,cAAc,EACd,qBAAqB,EAErB,YAAY,EACZ,YAAY,EAEZ,WAAW,EAEX,QAAQ,EACR,cAAc,EAEd,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phalanx Engine
|
|
3
|
+
* A game-agnostic deterministic lockstep multiplayer engine
|
|
4
|
+
*/
|
|
5
|
+
// Main class
|
|
6
|
+
export { Phalanx } from './Phalanx.js';
|
|
7
|
+
// Authentication
|
|
8
|
+
export { TokenValidatorService, createDevValidator, createEndpointValidator, } from './services/TokenValidator.js';
|
|
9
|
+
// Utilities
|
|
10
|
+
export { DeterministicRandom } from './utils/index.js';
|
|
11
|
+
export { FP, FPVector2, FPVector3, FixedPoint } from './utils/index.js';
|
|
12
|
+
// Constants
|
|
13
|
+
export { GAME_MODES } from './config/defaults.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,iBAAiB;AACjB,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAEtC,YAAY;AACZ,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA4CxE,YAAY;AACZ,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import type { Server as SocketIOServer } from 'socket.io';
|
|
2
|
+
import type { PhalanxConfig, QueuedPlayer, MatchInfo, PlayerCommand, TickCommands } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Game Room
|
|
5
|
+
* Handles a single match with tick synchronization and command broadcasting
|
|
6
|
+
*/
|
|
7
|
+
export declare class GameRoom {
|
|
8
|
+
private readonly id;
|
|
9
|
+
private readonly roomId;
|
|
10
|
+
private readonly io;
|
|
11
|
+
private readonly config;
|
|
12
|
+
private readonly players;
|
|
13
|
+
private readonly socketToPlayer;
|
|
14
|
+
private readonly teams;
|
|
15
|
+
private readonly eventEmitter;
|
|
16
|
+
private currentTick;
|
|
17
|
+
private state;
|
|
18
|
+
private createdAt;
|
|
19
|
+
private tickInterval;
|
|
20
|
+
private countdownTimer;
|
|
21
|
+
private countdownInterval;
|
|
22
|
+
/**
|
|
23
|
+
* Set when the match enters `'waiting-for-players'` (i.e. start()
|
|
24
|
+
* was called but at least one participant's socket was offline).
|
|
25
|
+
* Fires after `playersConnectTimeoutMs` and ends the match with
|
|
26
|
+
* `match-end: 'players-not-connected'` so the connected players
|
|
27
|
+
* aren't stranded indefinitely waiting for someone who's never
|
|
28
|
+
* coming back.
|
|
29
|
+
*
|
|
30
|
+
* Cleared in `transitionFromWaitingForPlayers` when everyone's
|
|
31
|
+
* online and we proceed to the countdown, or in `stop()` on shutdown.
|
|
32
|
+
*/
|
|
33
|
+
private playersConnectTimeout;
|
|
34
|
+
/**
|
|
35
|
+
* How long we'll hold the deferred-start state before giving up on
|
|
36
|
+
* absent participants. Long enough to cover a typical mobile
|
|
37
|
+
* "switched to messenger" round-trip, short enough that the
|
|
38
|
+
* connected player isn't staring at a frozen lobby. Pulled from
|
|
39
|
+
* config when available so games can tune it.
|
|
40
|
+
*/
|
|
41
|
+
private readonly playersConnectTimeoutMs;
|
|
42
|
+
/**
|
|
43
|
+
* Absolute epoch-ms deadline for the countdown, set when the countdown
|
|
44
|
+
* starts and cleared when `game-start` is emitted. Used to compute the
|
|
45
|
+
* remaining seconds for a late-joining socket (e.g. a private-room host
|
|
46
|
+
* whose mobile browser killed the WebSocket while they were sharing the
|
|
47
|
+
* invite link) so the client can render the correct number instead of
|
|
48
|
+
* staying stuck on the last value it saw before disconnecting.
|
|
49
|
+
*/
|
|
50
|
+
private countdownDeadline;
|
|
51
|
+
/**
|
|
52
|
+
* Set to true once `game-start` has been broadcast. A host who recovers
|
|
53
|
+
* after this point needs to synthesize the event locally — we signal
|
|
54
|
+
* that via this flag in the `reconnect-state` payload so the client can
|
|
55
|
+
* transition out of the countdown UI without waiting for a second
|
|
56
|
+
* `game-start` it will never receive.
|
|
57
|
+
*/
|
|
58
|
+
private gameStartEmitted;
|
|
59
|
+
private pendingCommands;
|
|
60
|
+
private readyPlayers;
|
|
61
|
+
private readyTimeout;
|
|
62
|
+
private readonly readyTimeoutMs;
|
|
63
|
+
private commandBuffer;
|
|
64
|
+
private tickSubmissions;
|
|
65
|
+
private lastMessageTime;
|
|
66
|
+
private commandHistory;
|
|
67
|
+
private laggingPlayers;
|
|
68
|
+
private readonly randomSeed;
|
|
69
|
+
private lastSequence;
|
|
70
|
+
private stateHashes;
|
|
71
|
+
private consecutiveDesyncs;
|
|
72
|
+
private readonly desyncConfig;
|
|
73
|
+
private readonly pauseConfig;
|
|
74
|
+
private pauseCount;
|
|
75
|
+
private pausedByPlayerId;
|
|
76
|
+
private readonly gameType;
|
|
77
|
+
private readonly tickMode;
|
|
78
|
+
private turnTimeout;
|
|
79
|
+
constructor(id: string, io: SocketIOServer, config: PhalanxConfig, teams: QueuedPlayer[][], eventEmitter: (event: string, ...args: unknown[]) => boolean | void, gameType?: string);
|
|
80
|
+
/**
|
|
81
|
+
* Start the game room.
|
|
82
|
+
*
|
|
83
|
+
* If every participant's socket is currently live we proceed straight
|
|
84
|
+
* to the countdown. Otherwise the room enters
|
|
85
|
+
* `'waiting-for-players'` — `match-found` and
|
|
86
|
+
* `match-waiting-for-players` are emitted to whoever is online, and
|
|
87
|
+
* the countdown is held back until either:
|
|
88
|
+
*
|
|
89
|
+
* - every player has reconnected (via `GameRoom.handleReconnect`,
|
|
90
|
+
* typically driven by the engine's room/match-recover handlers
|
|
91
|
+
* for the private-room case), at which point we transition to
|
|
92
|
+
* the countdown; OR
|
|
93
|
+
*
|
|
94
|
+
* - `playersConnectTimeoutMs` elapses and we end the match with
|
|
95
|
+
* `match-end: 'players-not-connected'`, freeing the participants
|
|
96
|
+
* who DID show up.
|
|
97
|
+
*
|
|
98
|
+
* Putting this gate inside `GameRoom` (rather than at the
|
|
99
|
+
* matchmaking / private-room layer) means every flow that builds a
|
|
100
|
+
* match — public matchmaking, private invites, future custom
|
|
101
|
+
* lobbies — gets the same "don't drop a single player into a
|
|
102
|
+
* countdown alone" guarantee for free.
|
|
103
|
+
*/
|
|
104
|
+
start(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Drive the deferred-start state machine forward: invoked from
|
|
107
|
+
* `handleReconnect` once a previously-missing player has rebound
|
|
108
|
+
* their socket. If everyone is now present, cancels the players-
|
|
109
|
+
* connect timeout and transitions into the countdown; otherwise
|
|
110
|
+
* just re-broadcasts the updated waiting-for-players list.
|
|
111
|
+
*/
|
|
112
|
+
private maybeBeginCountdownAfterReconnect;
|
|
113
|
+
/** True iff every participant's `connected` flag is set. */
|
|
114
|
+
private areAllPlayersConnected;
|
|
115
|
+
/** PlayerIds whose `connected` flag is currently false. */
|
|
116
|
+
private getDisconnectedPlayerIds;
|
|
117
|
+
/**
|
|
118
|
+
* Broadcast `match-waiting-for-players` to every currently-connected
|
|
119
|
+
* socket in the room, listing which playerIds we're still waiting on.
|
|
120
|
+
* Uses `this.io.to(this.roomId).emit(...)` so socket.io handles room
|
|
121
|
+
* routing to currently connected sockets while we include the canonical
|
|
122
|
+
* `matchId` in the payload, consistent with `notifyMatchFound`.
|
|
123
|
+
*/
|
|
124
|
+
private notifyWaitingForPlayers;
|
|
125
|
+
/**
|
|
126
|
+
* Emit a lifecycle event directly to every currently-connected player.
|
|
127
|
+
*
|
|
128
|
+
* `match-found` is already sent as a direct socket event, while the
|
|
129
|
+
* countdown used to depend on Socket.IO room membership established a
|
|
130
|
+
* few lines earlier in `start()`. Mobile transports can race around
|
|
131
|
+
* polling/websocket upgrade or a brief reconnect: the host still sees
|
|
132
|
+
* `match-found` and switches to the countdown screen, but misses the
|
|
133
|
+
* room broadcast that should advance it. Direct delivery keeps the
|
|
134
|
+
* pre-game lifecycle level with the personalized match-found path.
|
|
135
|
+
*/
|
|
136
|
+
private emitLifecycleToConnectedPlayers;
|
|
137
|
+
/**
|
|
138
|
+
* Start the game countdown
|
|
139
|
+
* Emits countdown events (5, 4, 3, 2, 1, 0) every second, then game-start
|
|
140
|
+
*/
|
|
141
|
+
private startGameCountdown;
|
|
142
|
+
/**
|
|
143
|
+
* Transition to waiting-for-ready state.
|
|
144
|
+
* The tick loop will not begin until all clients send 'client-ready'.
|
|
145
|
+
*/
|
|
146
|
+
private enterWaitingForReady;
|
|
147
|
+
private armReadyTimeout;
|
|
148
|
+
private clearReadyTimeout;
|
|
149
|
+
private armPlayersReconnectTimeout;
|
|
150
|
+
private clearPlayersReconnectTimeout;
|
|
151
|
+
/**
|
|
152
|
+
* Notify all players that a match has been found
|
|
153
|
+
* Each player receives personalized data about their teammates and opponents
|
|
154
|
+
*/
|
|
155
|
+
private notifyMatchFound;
|
|
156
|
+
/**
|
|
157
|
+
* Build the personalized `match-found` payload for `playerId`.
|
|
158
|
+
*
|
|
159
|
+
* Used by the initial broadcast in {@link notifyMatchFound} and by
|
|
160
|
+
* retroactive delivery when a host who was offline at `joinRoom`
|
|
161
|
+
* time finally reconnects via `room-recover`. Returns `null` if
|
|
162
|
+
* the player is not in this match.
|
|
163
|
+
*/
|
|
164
|
+
buildMatchFoundPayload(playerId: string): {
|
|
165
|
+
matchId: string;
|
|
166
|
+
playerId: string;
|
|
167
|
+
teamId: number;
|
|
168
|
+
teammates: {
|
|
169
|
+
playerId: string;
|
|
170
|
+
username: string;
|
|
171
|
+
}[];
|
|
172
|
+
opponents: {
|
|
173
|
+
playerId: string;
|
|
174
|
+
username: string;
|
|
175
|
+
}[];
|
|
176
|
+
} | null;
|
|
177
|
+
private buildMatchFoundPayloadForTeam;
|
|
178
|
+
/**
|
|
179
|
+
* Handle a player reporting ready after asset loading.
|
|
180
|
+
*
|
|
181
|
+
* The match only starts once *every* player in the room is both
|
|
182
|
+
* connected and has explicitly reported `client-ready`
|
|
183
|
+
* (see `areAllPlayersConnectedAndReady`). If any player is
|
|
184
|
+
* disconnected (e.g. mid-reconnect during `waiting-for-ready`),
|
|
185
|
+
* the gate stays closed even if all currently-connected players
|
|
186
|
+
* are ready.
|
|
187
|
+
*
|
|
188
|
+
* Once the gate opens:
|
|
189
|
+
* - In `continuous` tick mode the tick loop is started.
|
|
190
|
+
* - In `event` tick mode no tick loop is created; the turn
|
|
191
|
+
* timeout is armed via `resetTurnTimeout()` instead.
|
|
192
|
+
*
|
|
193
|
+
* @param playerId - The player reporting ready
|
|
194
|
+
*/
|
|
195
|
+
handlePlayerReady(playerId: string): void;
|
|
196
|
+
/**
|
|
197
|
+
* End the match because not all clients reported ready within the timeout.
|
|
198
|
+
*/
|
|
199
|
+
private endMatchDueToReadyTimeout;
|
|
200
|
+
/**
|
|
201
|
+
* Start the actual game (after countdown)
|
|
202
|
+
*/
|
|
203
|
+
private startGame;
|
|
204
|
+
/**
|
|
205
|
+
* Process a single tick
|
|
206
|
+
*/
|
|
207
|
+
private processTick;
|
|
208
|
+
/**
|
|
209
|
+
* Handle a player command
|
|
210
|
+
*/
|
|
211
|
+
handleCommand(playerId: string, command: PlayerCommand): boolean;
|
|
212
|
+
/**
|
|
213
|
+
* Validate command sequence number (2.1.4)
|
|
214
|
+
* Returns true if sequence is valid, false otherwise
|
|
215
|
+
*/
|
|
216
|
+
private validateCommandSequence;
|
|
217
|
+
/**
|
|
218
|
+
* Receive commands from a player for a specific tick (LOCKSTEP-2)
|
|
219
|
+
* Commands can be empty if player has no actions for this tick.
|
|
220
|
+
* This is normal - units may be moving/idle and player doesn't need to input anything.
|
|
221
|
+
*/
|
|
222
|
+
receivePlayerCommands(playerId: string, tick: number, commands: PlayerCommand[]): {
|
|
223
|
+
accepted: boolean;
|
|
224
|
+
invalidCommands?: PlayerCommand[];
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Get all commands for a specific tick
|
|
228
|
+
*/
|
|
229
|
+
getCommandsForTick(tick: number): TickCommands | null;
|
|
230
|
+
/**
|
|
231
|
+
* Check if all players have submitted for a specific tick
|
|
232
|
+
*/
|
|
233
|
+
allPlayersSubmittedForTick(tick: number): boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Get which players have submitted for a specific tick
|
|
236
|
+
*/
|
|
237
|
+
getSubmissionsForTick(tick: number): Set<string>;
|
|
238
|
+
/**
|
|
239
|
+
* Clean up old ticks after they've been processed
|
|
240
|
+
*/
|
|
241
|
+
clearOldTicks(beforeTick: number): void;
|
|
242
|
+
/**
|
|
243
|
+
* Update player activity timestamp (called on any message from player)
|
|
244
|
+
* Uses real time instead of ticks - more reliable with Socket.IO ping/pong
|
|
245
|
+
*/
|
|
246
|
+
updatePlayerActivity(playerId: string): void;
|
|
247
|
+
/**
|
|
248
|
+
* Check for lagging/disconnected players (LOCKSTEP-5)
|
|
249
|
+
* Uses real time (ms) instead of ticks for more reliable detection.
|
|
250
|
+
* Skipped entirely in event mode (turn timeout handles inactivity).
|
|
251
|
+
*/
|
|
252
|
+
private checkPlayerTimeouts;
|
|
253
|
+
/**
|
|
254
|
+
* Reset (or start) the turn timeout for event mode.
|
|
255
|
+
* If no commands arrive within turnTimeoutMs the match ends.
|
|
256
|
+
*/
|
|
257
|
+
private resetTurnTimeout;
|
|
258
|
+
/**
|
|
259
|
+
* End the match because the turn timeout expired (event mode only).
|
|
260
|
+
*/
|
|
261
|
+
private endMatchDueToTurnTimeout;
|
|
262
|
+
/**
|
|
263
|
+
* Store command history for reconnection support (NET-2)
|
|
264
|
+
*/
|
|
265
|
+
private storeCommandHistory;
|
|
266
|
+
/**
|
|
267
|
+
* Get recent command history for reconnecting player (NET-2)
|
|
268
|
+
*/
|
|
269
|
+
getRecentCommandHistory(fromTick: number): {
|
|
270
|
+
tick: number;
|
|
271
|
+
commands: PlayerCommand[];
|
|
272
|
+
}[];
|
|
273
|
+
/**
|
|
274
|
+
* Pause the game.
|
|
275
|
+
* Stops the tick loop and broadcasts 'game-paused' to all clients.
|
|
276
|
+
* All clients freeze deterministically because the pause only takes effect
|
|
277
|
+
* when they receive this broadcast (after the last completed tick).
|
|
278
|
+
*
|
|
279
|
+
* @param requestedBy - Player ID who requested the pause
|
|
280
|
+
* @returns true if the game was paused, false if not in a pauseable state or player exceeded pause limit
|
|
281
|
+
*/
|
|
282
|
+
pause(requestedBy: string): boolean;
|
|
283
|
+
/**
|
|
284
|
+
* Resume the game after a pause.
|
|
285
|
+
* Restarts the tick loop and broadcasts 'game-resumed' to all clients.
|
|
286
|
+
*
|
|
287
|
+
* @param requestedBy - Player ID who requested the resume
|
|
288
|
+
* @returns true if the game was resumed, false if not currently paused or player not allowed to resume
|
|
289
|
+
*/
|
|
290
|
+
resume(requestedBy: string): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Get current tick number
|
|
293
|
+
*/
|
|
294
|
+
getCurrentTick(): number;
|
|
295
|
+
/**
|
|
296
|
+
* Stop the game room
|
|
297
|
+
* @param skipNotify - If true, skip emitting match-ended events (used when already handled)
|
|
298
|
+
*/
|
|
299
|
+
stop(skipNotify?: boolean): void;
|
|
300
|
+
/**
|
|
301
|
+
* Handle player disconnection (NET-2)
|
|
302
|
+
*/
|
|
303
|
+
handleDisconnect(socketId: string): void;
|
|
304
|
+
/**
|
|
305
|
+
* Handle player reconnection (NET-2)
|
|
306
|
+
*/
|
|
307
|
+
handleReconnect(playerId: string, socketId: string): boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Returns true only when every player in the room is currently
|
|
310
|
+
* connected *and* has reported `client-ready`. This is the
|
|
311
|
+
* authoritative gate for leaving the `waiting-for-ready` state
|
|
312
|
+
* and starting the match (continuous or event tick mode).
|
|
313
|
+
*
|
|
314
|
+
* A disconnected player keeps the gate closed even if all other
|
|
315
|
+
* players are ready, which is required for correct reconnect
|
|
316
|
+
* behavior during `waiting-for-ready`.
|
|
317
|
+
*/
|
|
318
|
+
private areAllPlayersConnectedAndReady;
|
|
319
|
+
/**
|
|
320
|
+
* Whether the given playerId is a participant of this match.
|
|
321
|
+
* Used by `PrivateRoomService.recoverRoom` to authenticate a
|
|
322
|
+
* `room-recover` against a deferred / running match without
|
|
323
|
+
* exposing the players map.
|
|
324
|
+
*/
|
|
325
|
+
hasPlayer(playerId: string): boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Get match information
|
|
328
|
+
*/
|
|
329
|
+
getMatchInfo(): MatchInfo;
|
|
330
|
+
/**
|
|
331
|
+
* Get the room ID
|
|
332
|
+
*/
|
|
333
|
+
getId(): string;
|
|
334
|
+
/**
|
|
335
|
+
* Get the random seed for this match
|
|
336
|
+
* Clients use this to initialize their deterministic RNG
|
|
337
|
+
*/
|
|
338
|
+
getRandomSeed(): number;
|
|
339
|
+
/**
|
|
340
|
+
* Receive state hash from a player for a specific tick
|
|
341
|
+
* @param playerId - The player sending the hash
|
|
342
|
+
* @param tick - The tick this hash is for
|
|
343
|
+
* @param hash - The state hash string
|
|
344
|
+
*/
|
|
345
|
+
receiveStateHash(playerId: string, tick: number, hash: string): void;
|
|
346
|
+
/**
|
|
347
|
+
* Check if there's a desync at a given tick
|
|
348
|
+
*/
|
|
349
|
+
private checkForDesync;
|
|
350
|
+
/**
|
|
351
|
+
* End the match due to confirmed desync
|
|
352
|
+
*/
|
|
353
|
+
private endMatchDueToDesync;
|
|
354
|
+
/**
|
|
355
|
+
* Clean up state hashes older than the specified tick
|
|
356
|
+
*/
|
|
357
|
+
private cleanupOldStateHashes;
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=GameRoom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameRoom.d.ts","sourceRoot":"","sources":["../../src/services/GameRoom.ts"],"names":[],"mappings":"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"}
|