@gamerstake/game-core 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/.eslintrc.json +22 -0
- package/.testing-guide-summary.md +261 -0
- package/DEVELOPER_GUIDE.md +996 -0
- package/MANUAL_TESTING.md +369 -0
- package/QUICK_START.md +368 -0
- package/README.md +379 -0
- package/TESTING_OVERVIEW.md +378 -0
- package/dist/index.d.ts +1266 -0
- package/dist/index.js +1632 -0
- package/dist/index.js.map +1 -0
- package/examples/simple-game/README.md +176 -0
- package/examples/simple-game/client.ts +201 -0
- package/examples/simple-game/package.json +14 -0
- package/examples/simple-game/server.ts +233 -0
- package/jest.config.ts +39 -0
- package/package.json +54 -0
- package/src/core/GameLoop.ts +214 -0
- package/src/core/GameRules.ts +103 -0
- package/src/core/GameServer.ts +200 -0
- package/src/core/Room.ts +368 -0
- package/src/entities/Entity.ts +118 -0
- package/src/entities/Registry.ts +161 -0
- package/src/index.ts +51 -0
- package/src/input/Command.ts +41 -0
- package/src/input/InputQueue.ts +130 -0
- package/src/network/Network.ts +112 -0
- package/src/network/Snapshot.ts +59 -0
- package/src/physics/AABB.ts +104 -0
- package/src/physics/Movement.ts +124 -0
- package/src/spatial/Grid.ts +202 -0
- package/src/types/index.ts +117 -0
- package/src/types/protocol.ts +161 -0
- package/src/utils/Logger.ts +112 -0
- package/src/utils/RingBuffer.ts +116 -0
- package/tests/AABB.test.ts +38 -0
- package/tests/Entity.test.ts +35 -0
- package/tests/GameLoop.test.ts +58 -0
- package/tests/GameServer.test.ts +64 -0
- package/tests/Grid.test.ts +28 -0
- package/tests/InputQueue.test.ts +42 -0
- package/tests/Movement.test.ts +37 -0
- package/tests/Network.test.ts +39 -0
- package/tests/Registry.test.ts +36 -0
- package/tests/RingBuffer.test.ts +38 -0
- package/tests/Room.test.ts +80 -0
- package/tests/Snapshot.test.ts +19 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +14 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1266 @@
|
|
|
1
|
+
import { Server, Socket } from 'socket.io';
|
|
2
|
+
import pino from 'pino';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Core type definitions for game-core package.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Room configuration options.
|
|
9
|
+
*/
|
|
10
|
+
interface RoomConfig {
|
|
11
|
+
/** Ticks per second (default: 20) */
|
|
12
|
+
tickRate?: number;
|
|
13
|
+
/** Grid cell size in world units (default: 512) */
|
|
14
|
+
cellSize?: number;
|
|
15
|
+
/** Maximum buffered inputs per player (default: 100) */
|
|
16
|
+
maxInputQueueSize?: number;
|
|
17
|
+
/** Maximum entities per room (default: 1000) */
|
|
18
|
+
maxEntities?: number;
|
|
19
|
+
/** Visibility range in grid cells (default: 1 = 3x3 area) */
|
|
20
|
+
visibilityRange?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Server metrics for monitoring.
|
|
24
|
+
*/
|
|
25
|
+
interface ServerMetrics {
|
|
26
|
+
/** Total number of active rooms */
|
|
27
|
+
roomCount: number;
|
|
28
|
+
/** Total players across all rooms */
|
|
29
|
+
totalPlayers: number;
|
|
30
|
+
/** Per-room metrics */
|
|
31
|
+
rooms: RoomMetrics[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Per-room metrics.
|
|
35
|
+
*/
|
|
36
|
+
interface RoomMetrics {
|
|
37
|
+
/** Room ID */
|
|
38
|
+
id: string;
|
|
39
|
+
/** Number of players in room */
|
|
40
|
+
playerCount: number;
|
|
41
|
+
/** Total entities in room */
|
|
42
|
+
entityCount: number;
|
|
43
|
+
/** Current tick number */
|
|
44
|
+
tickCount: number;
|
|
45
|
+
/** Is the room running */
|
|
46
|
+
isRunning: boolean;
|
|
47
|
+
/** Average tick time (ms) */
|
|
48
|
+
avgTickTime?: number;
|
|
49
|
+
/** Actual ticks per second */
|
|
50
|
+
ticksPerSecond?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Game loop performance metrics.
|
|
54
|
+
*/
|
|
55
|
+
interface LoopMetrics {
|
|
56
|
+
/** Average tick processing time (ms) */
|
|
57
|
+
avgTickTime: number;
|
|
58
|
+
/** Maximum tick time (ms) */
|
|
59
|
+
maxTickTime: number;
|
|
60
|
+
/** Minimum tick time (ms) */
|
|
61
|
+
minTickTime: number;
|
|
62
|
+
/** Actual ticks per second */
|
|
63
|
+
ticksPerSecond: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Network event base type.
|
|
67
|
+
*/
|
|
68
|
+
interface NetworkEvent {
|
|
69
|
+
/** Opcode identifying event type */
|
|
70
|
+
op: string;
|
|
71
|
+
/** Additional event data */
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Snapshot of entity state.
|
|
76
|
+
*/
|
|
77
|
+
interface EntitySnapshot {
|
|
78
|
+
id: string;
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
vx: number;
|
|
82
|
+
vy: number;
|
|
83
|
+
[key: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Full room state snapshot.
|
|
87
|
+
*/
|
|
88
|
+
interface StateSnapshot {
|
|
89
|
+
/** Current tick number */
|
|
90
|
+
tick: number;
|
|
91
|
+
/** All entities in room */
|
|
92
|
+
entities: EntitySnapshot[];
|
|
93
|
+
/** Timestamp */
|
|
94
|
+
timestamp: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Game Loop
|
|
99
|
+
*
|
|
100
|
+
* Fixed tick rate server loop with drift compensation.
|
|
101
|
+
* Processes inputs, updates state, broadcasts changes.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Tick handler interface.
|
|
106
|
+
* Implement this to receive tick callbacks.
|
|
107
|
+
*/
|
|
108
|
+
interface TickHandler {
|
|
109
|
+
onTick(tickNumber: number, deltaMs: number): void;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* High-precision game loop using setTimeout with drift compensation.
|
|
113
|
+
*/
|
|
114
|
+
declare class GameLoop {
|
|
115
|
+
private readonly tickRate;
|
|
116
|
+
private readonly tickMs;
|
|
117
|
+
private tickNumber;
|
|
118
|
+
private lastTickTime;
|
|
119
|
+
private running;
|
|
120
|
+
private timeout;
|
|
121
|
+
private readonly handlers;
|
|
122
|
+
private tickTimes;
|
|
123
|
+
private readonly metricsWindowSize;
|
|
124
|
+
/**
|
|
125
|
+
* Create a new game loop.
|
|
126
|
+
*
|
|
127
|
+
* @param tickRate - Ticks per second (default: 20)
|
|
128
|
+
*/
|
|
129
|
+
constructor(tickRate?: number);
|
|
130
|
+
/**
|
|
131
|
+
* Register a tick handler.
|
|
132
|
+
*/
|
|
133
|
+
addHandler(handler: TickHandler): void;
|
|
134
|
+
/**
|
|
135
|
+
* Remove a tick handler.
|
|
136
|
+
*/
|
|
137
|
+
removeHandler(handler: TickHandler): void;
|
|
138
|
+
/**
|
|
139
|
+
* Start the game loop.
|
|
140
|
+
*/
|
|
141
|
+
start(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Stop the game loop.
|
|
144
|
+
*/
|
|
145
|
+
stop(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Check if loop is running.
|
|
148
|
+
*/
|
|
149
|
+
isRunning(): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Get current tick number.
|
|
152
|
+
*/
|
|
153
|
+
getCurrentTick(): number;
|
|
154
|
+
/**
|
|
155
|
+
* Get tick rate (TPS).
|
|
156
|
+
*/
|
|
157
|
+
getTickRate(): number;
|
|
158
|
+
/**
|
|
159
|
+
* Get tick duration (ms).
|
|
160
|
+
*/
|
|
161
|
+
getTickMs(): number;
|
|
162
|
+
/**
|
|
163
|
+
* Get performance metrics.
|
|
164
|
+
*/
|
|
165
|
+
getMetrics(): LoopMetrics;
|
|
166
|
+
/**
|
|
167
|
+
* Schedule the next tick with drift compensation.
|
|
168
|
+
*/
|
|
169
|
+
private scheduleNextTick;
|
|
170
|
+
/**
|
|
171
|
+
* Execute one tick.
|
|
172
|
+
*/
|
|
173
|
+
private tick;
|
|
174
|
+
/**
|
|
175
|
+
* Record tick time for metrics.
|
|
176
|
+
*/
|
|
177
|
+
private recordTickTime;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Entity System
|
|
182
|
+
*
|
|
183
|
+
* Base entity class for all game objects.
|
|
184
|
+
*/
|
|
185
|
+
/**
|
|
186
|
+
* Base entity class.
|
|
187
|
+
* All game objects (players, NPCs, items) extend this.
|
|
188
|
+
*
|
|
189
|
+
* Includes position, velocity, and dirty flag for
|
|
190
|
+
* efficient network synchronization.
|
|
191
|
+
*/
|
|
192
|
+
declare class Entity {
|
|
193
|
+
/** Unique entity identifier */
|
|
194
|
+
readonly id: string;
|
|
195
|
+
/** World X position */
|
|
196
|
+
x: number;
|
|
197
|
+
/** World Y position */
|
|
198
|
+
y: number;
|
|
199
|
+
/** Velocity X (units/second) */
|
|
200
|
+
vx: number;
|
|
201
|
+
/** Velocity Y (units/second) */
|
|
202
|
+
vy: number;
|
|
203
|
+
/** Dirty flag - entity needs to be broadcast */
|
|
204
|
+
dirty: boolean;
|
|
205
|
+
/** Last update timestamp */
|
|
206
|
+
lastUpdate: number;
|
|
207
|
+
/**
|
|
208
|
+
* Create a new entity.
|
|
209
|
+
*
|
|
210
|
+
* @param id - Unique identifier
|
|
211
|
+
* @param x - Initial X position
|
|
212
|
+
* @param y - Initial Y position
|
|
213
|
+
*/
|
|
214
|
+
constructor(id: string, x: number, y: number);
|
|
215
|
+
/**
|
|
216
|
+
* Update position based on velocity and delta time.
|
|
217
|
+
*
|
|
218
|
+
* @param deltaMs - Time since last update (milliseconds)
|
|
219
|
+
*/
|
|
220
|
+
updatePosition(deltaMs: number): void;
|
|
221
|
+
/**
|
|
222
|
+
* Set velocity.
|
|
223
|
+
*/
|
|
224
|
+
setVelocity(vx: number, vy: number): void;
|
|
225
|
+
/**
|
|
226
|
+
* Teleport to position (no velocity integration).
|
|
227
|
+
*/
|
|
228
|
+
setPosition(x: number, y: number): void;
|
|
229
|
+
/**
|
|
230
|
+
* Mark entity as clean (after broadcast).
|
|
231
|
+
*/
|
|
232
|
+
markClean(): void;
|
|
233
|
+
/**
|
|
234
|
+
* Mark entity as dirty (needs broadcast).
|
|
235
|
+
*/
|
|
236
|
+
markDirty(): void;
|
|
237
|
+
/**
|
|
238
|
+
* Get entity as plain object for serialization.
|
|
239
|
+
*/
|
|
240
|
+
toJSON(): Record<string, unknown>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Command types for player input.
|
|
245
|
+
*/
|
|
246
|
+
/**
|
|
247
|
+
* Base command interface.
|
|
248
|
+
*/
|
|
249
|
+
interface Command {
|
|
250
|
+
/** Client sequence number for reconciliation */
|
|
251
|
+
readonly seq: number;
|
|
252
|
+
/** Command type */
|
|
253
|
+
readonly type: string;
|
|
254
|
+
/** Timestamp when command was created */
|
|
255
|
+
readonly timestamp: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Movement command.
|
|
259
|
+
*/
|
|
260
|
+
interface MoveCommand extends Command {
|
|
261
|
+
type: 'move';
|
|
262
|
+
dir: {
|
|
263
|
+
x: number;
|
|
264
|
+
y: number;
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Stop command.
|
|
269
|
+
*/
|
|
270
|
+
interface StopCommand extends Command {
|
|
271
|
+
type: 'stop';
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Generic action command.
|
|
275
|
+
*/
|
|
276
|
+
interface ActionCommand extends Command {
|
|
277
|
+
type: 'action';
|
|
278
|
+
action: string;
|
|
279
|
+
data?: Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Game Rules Interface
|
|
284
|
+
*
|
|
285
|
+
* Pluggable game logic interface.
|
|
286
|
+
* Implement this to create custom multiplayer games.
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Game-specific logic interface.
|
|
291
|
+
*
|
|
292
|
+
* This is the core abstraction that makes game-core reusable.
|
|
293
|
+
* All game-specific logic is implemented through this interface,
|
|
294
|
+
* keeping the engine generic.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* class MyGameRules implements GameRules {
|
|
299
|
+
* onRoomCreated(room: Room): void {
|
|
300
|
+
* // Spawn initial entities
|
|
301
|
+
* }
|
|
302
|
+
*
|
|
303
|
+
* onPlayerJoin(room: Room, player: Entity): void {
|
|
304
|
+
* // Setup player
|
|
305
|
+
* }
|
|
306
|
+
*
|
|
307
|
+
* onTick(room: Room, delta: number): void {
|
|
308
|
+
* // Update game state
|
|
309
|
+
* }
|
|
310
|
+
*
|
|
311
|
+
* onCommand(room: Room, playerId: string, command: Command): void {
|
|
312
|
+
* // Handle player input
|
|
313
|
+
* }
|
|
314
|
+
*
|
|
315
|
+
* onPlayerLeave(room: Room, playerId: string): void {
|
|
316
|
+
* // Cleanup player
|
|
317
|
+
* }
|
|
318
|
+
*
|
|
319
|
+
* shouldEndRoom(room: Room): boolean {
|
|
320
|
+
* // For match-based games, return true when match ends
|
|
321
|
+
* // For persistent worlds, return false
|
|
322
|
+
* return false;
|
|
323
|
+
* }
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
interface GameRules<TEntity extends Entity = Entity> {
|
|
328
|
+
/**
|
|
329
|
+
* Called once when room is created.
|
|
330
|
+
* Use for initialization (spawn items, set boundaries, etc).
|
|
331
|
+
*
|
|
332
|
+
* @param room - The newly created room
|
|
333
|
+
*/
|
|
334
|
+
onRoomCreated(room: Room<TEntity>): void;
|
|
335
|
+
/**
|
|
336
|
+
* Called when a player joins the room.
|
|
337
|
+
*
|
|
338
|
+
* @param room - The room
|
|
339
|
+
* @param player - The entity representing the player
|
|
340
|
+
*/
|
|
341
|
+
onPlayerJoin(room: Room<TEntity>, player: TEntity): void;
|
|
342
|
+
/**
|
|
343
|
+
* Called when a player leaves the room.
|
|
344
|
+
*
|
|
345
|
+
* @param room - The room
|
|
346
|
+
* @param playerId - The ID of the leaving player
|
|
347
|
+
*/
|
|
348
|
+
onPlayerLeave(room: Room<TEntity>, playerId: string): void;
|
|
349
|
+
/**
|
|
350
|
+
* Called every tick (20 TPS by default).
|
|
351
|
+
* Process inputs, update game state, check win conditions.
|
|
352
|
+
*
|
|
353
|
+
* @param room - The room
|
|
354
|
+
* @param delta - Time since last tick (milliseconds)
|
|
355
|
+
*/
|
|
356
|
+
onTick(room: Room<TEntity>, delta: number): void;
|
|
357
|
+
/**
|
|
358
|
+
* Called when a player sends a command.
|
|
359
|
+
*
|
|
360
|
+
* @param room - The room
|
|
361
|
+
* @param playerId - The player who sent the command
|
|
362
|
+
* @param command - The input command from the player
|
|
363
|
+
*/
|
|
364
|
+
onCommand(room: Room<TEntity>, playerId: string, command: Command): void;
|
|
365
|
+
/**
|
|
366
|
+
* Check if the room should be destroyed.
|
|
367
|
+
*
|
|
368
|
+
* For match-based games, return true when match ends.
|
|
369
|
+
* For persistent worlds, return false.
|
|
370
|
+
*
|
|
371
|
+
* @param room - The room
|
|
372
|
+
* @returns True if room should end
|
|
373
|
+
*/
|
|
374
|
+
shouldEndRoom(room: Room<TEntity>): boolean;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Entity Registry
|
|
379
|
+
*
|
|
380
|
+
* Manages entity lifecycle and provides efficient queries.
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Entity registry with lifecycle management.
|
|
385
|
+
*
|
|
386
|
+
* Provides zero-allocation queries using pre-allocated buffers
|
|
387
|
+
* to minimize GC pressure in hot paths.
|
|
388
|
+
*/
|
|
389
|
+
declare class Registry<TEntity extends Entity = Entity> {
|
|
390
|
+
private readonly entities;
|
|
391
|
+
private readonly dirtyEntities;
|
|
392
|
+
private readonly dirtyBuffer;
|
|
393
|
+
private readonly allBuffer;
|
|
394
|
+
/**
|
|
395
|
+
* Add an entity to the registry.
|
|
396
|
+
*/
|
|
397
|
+
add(entity: TEntity): void;
|
|
398
|
+
/**
|
|
399
|
+
* Remove an entity from the registry.
|
|
400
|
+
*/
|
|
401
|
+
remove(entityId: string): boolean;
|
|
402
|
+
/**
|
|
403
|
+
* Get an entity by ID.
|
|
404
|
+
*/
|
|
405
|
+
get(entityId: string): TEntity | undefined;
|
|
406
|
+
/**
|
|
407
|
+
* Check if entity exists.
|
|
408
|
+
*/
|
|
409
|
+
has(entityId: string): boolean;
|
|
410
|
+
/**
|
|
411
|
+
* Mark an entity as dirty (needs broadcast).
|
|
412
|
+
*/
|
|
413
|
+
markDirty(entityId: string): void;
|
|
414
|
+
/**
|
|
415
|
+
* Get all dirty entities and clear dirty flags.
|
|
416
|
+
* OPTIMIZED: Reuses buffer array to avoid allocations.
|
|
417
|
+
*
|
|
418
|
+
* @returns Array of dirty entities (reused buffer - don't store reference!)
|
|
419
|
+
*/
|
|
420
|
+
getDirtyEntities(): TEntity[];
|
|
421
|
+
/**
|
|
422
|
+
* Get all entities.
|
|
423
|
+
* OPTIMIZED: Reuses buffer array to avoid allocations.
|
|
424
|
+
*
|
|
425
|
+
* @returns Array of all entities (reused buffer - don't store reference!)
|
|
426
|
+
*/
|
|
427
|
+
getAll(): TEntity[];
|
|
428
|
+
/**
|
|
429
|
+
* Get entity count.
|
|
430
|
+
*/
|
|
431
|
+
size(): number;
|
|
432
|
+
/**
|
|
433
|
+
* Get dirty entity count.
|
|
434
|
+
*/
|
|
435
|
+
dirtyCount(): number;
|
|
436
|
+
/**
|
|
437
|
+
* Clear all entities.
|
|
438
|
+
*/
|
|
439
|
+
clear(): void;
|
|
440
|
+
/**
|
|
441
|
+
* Execute callback for each entity.
|
|
442
|
+
*/
|
|
443
|
+
forEach(callback: (entity: TEntity) => void): void;
|
|
444
|
+
/**
|
|
445
|
+
* Filter entities by predicate.
|
|
446
|
+
*
|
|
447
|
+
* @param predicate - Filter function
|
|
448
|
+
* @returns New array of matching entities
|
|
449
|
+
*/
|
|
450
|
+
filter(predicate: (entity: TEntity) => boolean): TEntity[];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Grid System
|
|
455
|
+
*
|
|
456
|
+
* Spatial partitioning using a fixed-size grid.
|
|
457
|
+
* Enables O(1) lookup for nearby entities.
|
|
458
|
+
*/
|
|
459
|
+
/**
|
|
460
|
+
* Grid-based spatial partitioning system.
|
|
461
|
+
*
|
|
462
|
+
* Uses a hash map of cells for O(1) entity tracking.
|
|
463
|
+
* Automatically cleans up empty cells to prevent memory leaks.
|
|
464
|
+
*/
|
|
465
|
+
declare class Grid {
|
|
466
|
+
private readonly cellSize;
|
|
467
|
+
private readonly cells;
|
|
468
|
+
private readonly entityCells;
|
|
469
|
+
/**
|
|
470
|
+
* Create a new spatial grid.
|
|
471
|
+
*
|
|
472
|
+
* @param cellSize - Size of each cell in world units (default: 512)
|
|
473
|
+
*/
|
|
474
|
+
constructor(cellSize?: number);
|
|
475
|
+
/**
|
|
476
|
+
* Convert world coordinates to cell key.
|
|
477
|
+
*/
|
|
478
|
+
getCellKey(worldX: number, worldY: number): string;
|
|
479
|
+
/**
|
|
480
|
+
* Get or create a cell at world coordinates.
|
|
481
|
+
*/
|
|
482
|
+
private getCell;
|
|
483
|
+
/**
|
|
484
|
+
* Add an entity to the grid.
|
|
485
|
+
*/
|
|
486
|
+
addEntity(entityId: string, x: number, y: number): void;
|
|
487
|
+
/**
|
|
488
|
+
* Remove an entity from the grid.
|
|
489
|
+
*/
|
|
490
|
+
removeEntity(entityId: string): void;
|
|
491
|
+
/**
|
|
492
|
+
* Move an entity to a new position.
|
|
493
|
+
* Returns true if the entity changed cells.
|
|
494
|
+
*/
|
|
495
|
+
moveEntity(entityId: string, oldX: number, oldY: number, newX: number, newY: number): boolean;
|
|
496
|
+
/**
|
|
497
|
+
* Get all entities in nearby cells.
|
|
498
|
+
*
|
|
499
|
+
* @param worldX - World X coordinate
|
|
500
|
+
* @param worldY - World Y coordinate
|
|
501
|
+
* @param range - Range in grid cells (default: 1 = 3x3 area)
|
|
502
|
+
*/
|
|
503
|
+
getNearbyEntities(worldX: number, worldY: number, range?: number): string[];
|
|
504
|
+
/**
|
|
505
|
+
* Get entities in a specific cell.
|
|
506
|
+
*/
|
|
507
|
+
getEntitiesInCell(worldX: number, worldY: number): string[];
|
|
508
|
+
/**
|
|
509
|
+
* Get the cell an entity is in.
|
|
510
|
+
*/
|
|
511
|
+
getEntityCell(entityId: string): string | undefined;
|
|
512
|
+
/**
|
|
513
|
+
* Get total number of active cells.
|
|
514
|
+
*/
|
|
515
|
+
getCellCount(): number;
|
|
516
|
+
/**
|
|
517
|
+
* Get total entities in grid.
|
|
518
|
+
*/
|
|
519
|
+
getEntityCount(): number;
|
|
520
|
+
/**
|
|
521
|
+
* Clear all cells and entities.
|
|
522
|
+
*/
|
|
523
|
+
clear(): void;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Input Queue
|
|
528
|
+
*
|
|
529
|
+
* Buffers player inputs for processing in the game loop.
|
|
530
|
+
* Uses RingBuffer for O(1) push/pop operations.
|
|
531
|
+
*/
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Input queue manager using RingBuffer for O(1) operations.
|
|
535
|
+
*
|
|
536
|
+
* Manages input queues for multiple players, with automatic
|
|
537
|
+
* overflow handling (drops oldest inputs when full).
|
|
538
|
+
*/
|
|
539
|
+
declare class InputQueue {
|
|
540
|
+
private readonly queues;
|
|
541
|
+
private readonly maxQueueSize;
|
|
542
|
+
/**
|
|
543
|
+
* Create a new input queue manager.
|
|
544
|
+
*
|
|
545
|
+
* @param maxQueueSize - Maximum inputs per player (default: 100)
|
|
546
|
+
*/
|
|
547
|
+
constructor(maxQueueSize?: number);
|
|
548
|
+
/**
|
|
549
|
+
* Get or create a player's queue.
|
|
550
|
+
*/
|
|
551
|
+
private getQueue;
|
|
552
|
+
/**
|
|
553
|
+
* Add an input to a player's queue. O(1)
|
|
554
|
+
*/
|
|
555
|
+
push(playerId: string, input: Command): void;
|
|
556
|
+
/**
|
|
557
|
+
* Get and remove the next input for a player. O(1)
|
|
558
|
+
*/
|
|
559
|
+
pop(playerId: string): Command | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* Peek at the next input without removing. O(1)
|
|
562
|
+
*/
|
|
563
|
+
peek(playerId: string): Command | undefined;
|
|
564
|
+
/**
|
|
565
|
+
* Get all inputs for a player and clear.
|
|
566
|
+
*/
|
|
567
|
+
drain(playerId: string): Command[];
|
|
568
|
+
/**
|
|
569
|
+
* Get queue size for a player.
|
|
570
|
+
*/
|
|
571
|
+
size(playerId: string): number;
|
|
572
|
+
/**
|
|
573
|
+
* Clear a player's queue.
|
|
574
|
+
*/
|
|
575
|
+
clear(playerId: string): void;
|
|
576
|
+
/**
|
|
577
|
+
* Remove a player's queue entirely.
|
|
578
|
+
*/
|
|
579
|
+
remove(playerId: string): void;
|
|
580
|
+
/**
|
|
581
|
+
* Clear all queues.
|
|
582
|
+
*/
|
|
583
|
+
clearAll(): void;
|
|
584
|
+
/**
|
|
585
|
+
* Get total queued inputs across all players.
|
|
586
|
+
*/
|
|
587
|
+
getTotalSize(): number;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Network Layer
|
|
592
|
+
*
|
|
593
|
+
* Broadcast and send utilities for game events.
|
|
594
|
+
*/
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Network layer for broadcasting game events.
|
|
598
|
+
*
|
|
599
|
+
* Wraps Socket.io with game-specific utilities.
|
|
600
|
+
*/
|
|
601
|
+
declare class Network {
|
|
602
|
+
private io;
|
|
603
|
+
private readonly sockets;
|
|
604
|
+
/**
|
|
605
|
+
* Set the Socket.io server instance.
|
|
606
|
+
*/
|
|
607
|
+
setServer(io: Server): void;
|
|
608
|
+
/**
|
|
609
|
+
* Register a socket connection.
|
|
610
|
+
*/
|
|
611
|
+
registerSocket(playerId: string, socket: Socket): void;
|
|
612
|
+
/**
|
|
613
|
+
* Unregister a socket connection.
|
|
614
|
+
*/
|
|
615
|
+
unregisterSocket(playerId: string): void;
|
|
616
|
+
/**
|
|
617
|
+
* Get a socket by player ID.
|
|
618
|
+
*/
|
|
619
|
+
getSocket(playerId: string): Socket | undefined;
|
|
620
|
+
/**
|
|
621
|
+
* Broadcast event to all connected players.
|
|
622
|
+
*/
|
|
623
|
+
broadcast(event: NetworkEvent): void;
|
|
624
|
+
/**
|
|
625
|
+
* Broadcast event to all players in a room.
|
|
626
|
+
*/
|
|
627
|
+
broadcastToRoom(roomId: string, event: NetworkEvent): void;
|
|
628
|
+
/**
|
|
629
|
+
* Send event to a specific player.
|
|
630
|
+
*/
|
|
631
|
+
sendTo(playerId: string, event: NetworkEvent): void;
|
|
632
|
+
/**
|
|
633
|
+
* Send event to multiple players.
|
|
634
|
+
*/
|
|
635
|
+
sendToMany(playerIds: string[], event: NetworkEvent): void;
|
|
636
|
+
/**
|
|
637
|
+
* Get connected player count.
|
|
638
|
+
*/
|
|
639
|
+
getPlayerCount(): number;
|
|
640
|
+
/**
|
|
641
|
+
* Clear all sockets.
|
|
642
|
+
*/
|
|
643
|
+
clear(): void;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Room
|
|
648
|
+
*
|
|
649
|
+
* A single game room/match instance.
|
|
650
|
+
* Manages entities, tick loop, and networking.
|
|
651
|
+
*/
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* A single game room/match.
|
|
655
|
+
*
|
|
656
|
+
* Orchestrates all game systems: tick loop, entities, spatial partitioning,
|
|
657
|
+
* input processing, and network synchronization.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```typescript
|
|
661
|
+
* const room = new Room('room-1', new MyGameRules(), {
|
|
662
|
+
* tickRate: 20,
|
|
663
|
+
* cellSize: 512,
|
|
664
|
+
* });
|
|
665
|
+
*
|
|
666
|
+
* room.start();
|
|
667
|
+
*
|
|
668
|
+
* // Add players
|
|
669
|
+
* const player = new Entity('player-1', 100, 100);
|
|
670
|
+
* room.addPlayer(player);
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
declare class Room<TEntity extends Entity = Entity> implements TickHandler {
|
|
674
|
+
/** Room identifier */
|
|
675
|
+
readonly id: string;
|
|
676
|
+
/** Game-specific logic */
|
|
677
|
+
readonly rules: GameRules<TEntity>;
|
|
678
|
+
private readonly registry;
|
|
679
|
+
private readonly grid;
|
|
680
|
+
private readonly loop;
|
|
681
|
+
private readonly inputQueue;
|
|
682
|
+
private readonly network;
|
|
683
|
+
private readonly players;
|
|
684
|
+
private tickCount;
|
|
685
|
+
private startTime;
|
|
686
|
+
private readonly config;
|
|
687
|
+
/**
|
|
688
|
+
* Create a new room.
|
|
689
|
+
*
|
|
690
|
+
* @param id - Unique room identifier
|
|
691
|
+
* @param rules - Game-specific logic
|
|
692
|
+
* @param config - Room configuration
|
|
693
|
+
*/
|
|
694
|
+
constructor(id: string, rules: GameRules<TEntity>, config?: RoomConfig);
|
|
695
|
+
/**
|
|
696
|
+
* Start the room (begins tick loop).
|
|
697
|
+
*/
|
|
698
|
+
start(): void;
|
|
699
|
+
/**
|
|
700
|
+
* Stop the room (ends tick loop).
|
|
701
|
+
*/
|
|
702
|
+
stop(): void;
|
|
703
|
+
/**
|
|
704
|
+
* Add a player to the room.
|
|
705
|
+
*/
|
|
706
|
+
addPlayer(player: TEntity): void;
|
|
707
|
+
/**
|
|
708
|
+
* Remove a player from the room.
|
|
709
|
+
*/
|
|
710
|
+
removePlayer(playerId: string): void;
|
|
711
|
+
/**
|
|
712
|
+
* Spawn a non-player entity.
|
|
713
|
+
*/
|
|
714
|
+
spawnEntity(entity: TEntity): void;
|
|
715
|
+
/**
|
|
716
|
+
* Destroy an entity.
|
|
717
|
+
*/
|
|
718
|
+
destroyEntity(entityId: string): void;
|
|
719
|
+
/**
|
|
720
|
+
* Queue a player input.
|
|
721
|
+
*/
|
|
722
|
+
queueInput(playerId: string, command: Command): void;
|
|
723
|
+
/**
|
|
724
|
+
* Broadcast event to all players.
|
|
725
|
+
*/
|
|
726
|
+
broadcast(event: NetworkEvent): void;
|
|
727
|
+
/**
|
|
728
|
+
* Send event to specific player.
|
|
729
|
+
*/
|
|
730
|
+
sendTo(playerId: string, event: NetworkEvent): void;
|
|
731
|
+
/**
|
|
732
|
+
* Get full state snapshot.
|
|
733
|
+
*/
|
|
734
|
+
getSnapshot(): StateSnapshot;
|
|
735
|
+
/**
|
|
736
|
+
* Get delta snapshot (only dirty entities).
|
|
737
|
+
*/
|
|
738
|
+
getDeltaSnapshot(): StateSnapshot;
|
|
739
|
+
/**
|
|
740
|
+
* Main tick function (called by GameLoop).
|
|
741
|
+
*/
|
|
742
|
+
onTick(tickNumber: number, deltaMs: number): void;
|
|
743
|
+
/**
|
|
744
|
+
* Get all players.
|
|
745
|
+
*/
|
|
746
|
+
getPlayers(): Map<string, TEntity>;
|
|
747
|
+
/**
|
|
748
|
+
* Get entity registry.
|
|
749
|
+
*/
|
|
750
|
+
getRegistry(): Registry<TEntity>;
|
|
751
|
+
/**
|
|
752
|
+
* Get spatial grid.
|
|
753
|
+
*/
|
|
754
|
+
getGrid(): Grid;
|
|
755
|
+
/**
|
|
756
|
+
* Get network layer.
|
|
757
|
+
*/
|
|
758
|
+
getNetwork(): Network;
|
|
759
|
+
/**
|
|
760
|
+
* Get input queue.
|
|
761
|
+
*/
|
|
762
|
+
getInputQueue(): InputQueue;
|
|
763
|
+
/**
|
|
764
|
+
* Get current tick number.
|
|
765
|
+
*/
|
|
766
|
+
getTickCount(): number;
|
|
767
|
+
/**
|
|
768
|
+
* Get uptime in milliseconds.
|
|
769
|
+
*/
|
|
770
|
+
getUptime(): number;
|
|
771
|
+
/**
|
|
772
|
+
* Check if room is running.
|
|
773
|
+
*/
|
|
774
|
+
isRunning(): boolean;
|
|
775
|
+
/**
|
|
776
|
+
* Get room configuration.
|
|
777
|
+
*/
|
|
778
|
+
getConfig(): Required<RoomConfig>;
|
|
779
|
+
/**
|
|
780
|
+
* Get room metrics.
|
|
781
|
+
*/
|
|
782
|
+
getMetrics(): {
|
|
783
|
+
avgTickTime: number;
|
|
784
|
+
maxTickTime: number;
|
|
785
|
+
minTickTime: number;
|
|
786
|
+
ticksPerSecond: number;
|
|
787
|
+
roomId: string;
|
|
788
|
+
tickCount: number;
|
|
789
|
+
uptime: number;
|
|
790
|
+
playerCount: number;
|
|
791
|
+
entityCount: number;
|
|
792
|
+
cellCount: number;
|
|
793
|
+
queuedInputs: number;
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Game Server
|
|
799
|
+
*
|
|
800
|
+
* Multi-room game server.
|
|
801
|
+
* Manages room lifecycle and routing.
|
|
802
|
+
*/
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Multi-room game server.
|
|
806
|
+
*
|
|
807
|
+
* Orchestrates multiple game rooms and provides routing
|
|
808
|
+
* for Socket.io connections.
|
|
809
|
+
*
|
|
810
|
+
* @example
|
|
811
|
+
* ```typescript
|
|
812
|
+
* const server = new GameServer();
|
|
813
|
+
* server.setServer(io);
|
|
814
|
+
*
|
|
815
|
+
* const room = server.createRoom('room-1', new MyGameRules());
|
|
816
|
+
*
|
|
817
|
+
* // Later...
|
|
818
|
+
* server.destroyRoom('room-1');
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
declare class GameServer {
|
|
822
|
+
private readonly rooms;
|
|
823
|
+
private io;
|
|
824
|
+
/**
|
|
825
|
+
* Set the Socket.io server instance.
|
|
826
|
+
*
|
|
827
|
+
* This should be called before creating rooms to enable
|
|
828
|
+
* network functionality.
|
|
829
|
+
*/
|
|
830
|
+
setServer(io: Server): void;
|
|
831
|
+
/**
|
|
832
|
+
* Create a new room.
|
|
833
|
+
*
|
|
834
|
+
* @param id - Unique room identifier
|
|
835
|
+
* @param rules - Game-specific logic
|
|
836
|
+
* @param config - Room configuration
|
|
837
|
+
* @returns The created room
|
|
838
|
+
* @throws If room with same ID already exists
|
|
839
|
+
*/
|
|
840
|
+
createRoom<TEntity extends Entity = Entity>(id: string, rules: GameRules<TEntity>, config?: RoomConfig): Room<TEntity>;
|
|
841
|
+
/**
|
|
842
|
+
* Destroy a room.
|
|
843
|
+
*
|
|
844
|
+
* Stops the room's tick loop and removes it from the server.
|
|
845
|
+
*
|
|
846
|
+
* @param id - Room identifier
|
|
847
|
+
* @returns True if room was destroyed, false if not found
|
|
848
|
+
*/
|
|
849
|
+
destroyRoom(id: string): boolean;
|
|
850
|
+
/**
|
|
851
|
+
* Get a room by ID.
|
|
852
|
+
*/
|
|
853
|
+
getRoom(id: string): Room | undefined;
|
|
854
|
+
/**
|
|
855
|
+
* Get a room with specific entity type.
|
|
856
|
+
*
|
|
857
|
+
* @param id - Room identifier
|
|
858
|
+
* @returns Room cast to specific entity type, or undefined
|
|
859
|
+
*/
|
|
860
|
+
getRoomAs<TEntity extends Entity>(id: string): Room<TEntity> | undefined;
|
|
861
|
+
/**
|
|
862
|
+
* Check if room exists.
|
|
863
|
+
*/
|
|
864
|
+
hasRoom(id: string): boolean;
|
|
865
|
+
/**
|
|
866
|
+
* Get all rooms.
|
|
867
|
+
*/
|
|
868
|
+
getRooms(): Map<string, Room>;
|
|
869
|
+
/**
|
|
870
|
+
* Get room count.
|
|
871
|
+
*/
|
|
872
|
+
getRoomCount(): number;
|
|
873
|
+
/**
|
|
874
|
+
* Get total player count across all rooms.
|
|
875
|
+
*/
|
|
876
|
+
getTotalPlayerCount(): number;
|
|
877
|
+
/**
|
|
878
|
+
* Get server health metrics.
|
|
879
|
+
*/
|
|
880
|
+
getMetrics(): ServerMetrics;
|
|
881
|
+
/**
|
|
882
|
+
* Stop all rooms.
|
|
883
|
+
*/
|
|
884
|
+
stopAll(): void;
|
|
885
|
+
/**
|
|
886
|
+
* Destroy all rooms.
|
|
887
|
+
*/
|
|
888
|
+
destroyAll(): void;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* AABB Collision Detection
|
|
893
|
+
*
|
|
894
|
+
* Axis-Aligned Bounding Box collision detection.
|
|
895
|
+
*/
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Axis-Aligned Bounding Box.
|
|
899
|
+
*/
|
|
900
|
+
interface AABB {
|
|
901
|
+
x: number;
|
|
902
|
+
y: number;
|
|
903
|
+
width: number;
|
|
904
|
+
height: number;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* AABB collision detection utilities.
|
|
908
|
+
*/
|
|
909
|
+
declare class AABBCollision {
|
|
910
|
+
/**
|
|
911
|
+
* Check if two AABBs overlap.
|
|
912
|
+
*/
|
|
913
|
+
static overlaps(a: AABB, b: AABB): boolean;
|
|
914
|
+
/**
|
|
915
|
+
* Check if a point is inside an AABB.
|
|
916
|
+
*/
|
|
917
|
+
static containsPoint(aabb: AABB, x: number, y: number): boolean;
|
|
918
|
+
/**
|
|
919
|
+
* Create AABB from entity.
|
|
920
|
+
*/
|
|
921
|
+
static fromEntity(entity: Entity, width: number, height: number): AABB;
|
|
922
|
+
/**
|
|
923
|
+
* Get the distance between two AABBs.
|
|
924
|
+
* Returns 0 if overlapping.
|
|
925
|
+
*/
|
|
926
|
+
static distance(a: AABB, b: AABB): number;
|
|
927
|
+
/**
|
|
928
|
+
* Compute the overlap amount between two AABBs.
|
|
929
|
+
* Returns { x: 0, y: 0 } if not overlapping.
|
|
930
|
+
*/
|
|
931
|
+
static overlap(a: AABB, b: AABB): {
|
|
932
|
+
x: number;
|
|
933
|
+
y: number;
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Movement Physics
|
|
939
|
+
*
|
|
940
|
+
* Velocity integration and boundary constraints.
|
|
941
|
+
*/
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Boundary constraints for movement.
|
|
945
|
+
*/
|
|
946
|
+
interface Boundary {
|
|
947
|
+
minX: number;
|
|
948
|
+
maxX: number;
|
|
949
|
+
minY: number;
|
|
950
|
+
maxY: number;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Movement physics utilities.
|
|
954
|
+
*/
|
|
955
|
+
declare class Movement {
|
|
956
|
+
/**
|
|
957
|
+
* Update entity position based on velocity.
|
|
958
|
+
*
|
|
959
|
+
* @param entity - Entity to update
|
|
960
|
+
* @param deltaMs - Time delta in milliseconds
|
|
961
|
+
*/
|
|
962
|
+
static integrate(entity: Entity, deltaMs: number): void;
|
|
963
|
+
/**
|
|
964
|
+
* Apply velocity to entity.
|
|
965
|
+
*/
|
|
966
|
+
static applyVelocity(entity: Entity, vx: number, vy: number): void;
|
|
967
|
+
/**
|
|
968
|
+
* Stop entity movement.
|
|
969
|
+
*/
|
|
970
|
+
static stop(entity: Entity): void;
|
|
971
|
+
/**
|
|
972
|
+
* Constrain entity to boundary.
|
|
973
|
+
*
|
|
974
|
+
* @param entity - Entity to constrain
|
|
975
|
+
* @param boundary - Boundary constraints
|
|
976
|
+
* @returns True if entity was clamped
|
|
977
|
+
*/
|
|
978
|
+
static constrainToBoundary(entity: Entity, boundary: Boundary): boolean;
|
|
979
|
+
/**
|
|
980
|
+
* Calculate distance between two entities.
|
|
981
|
+
*/
|
|
982
|
+
static distance(a: Entity, b: Entity): number;
|
|
983
|
+
/**
|
|
984
|
+
* Normalize a direction vector.
|
|
985
|
+
*/
|
|
986
|
+
static normalize(x: number, y: number): {
|
|
987
|
+
x: number;
|
|
988
|
+
y: number;
|
|
989
|
+
};
|
|
990
|
+
/**
|
|
991
|
+
* Calculate velocity from direction and speed.
|
|
992
|
+
*/
|
|
993
|
+
static velocityFromDirection(dirX: number, dirY: number, speed: number): {
|
|
994
|
+
vx: number;
|
|
995
|
+
vy: number;
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Snapshot System
|
|
1001
|
+
*
|
|
1002
|
+
* Full state snapshots for network synchronization.
|
|
1003
|
+
*/
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Snapshot generator.
|
|
1007
|
+
*
|
|
1008
|
+
* Creates full state snapshots for sending to clients.
|
|
1009
|
+
* Used for initial sync and periodic full updates.
|
|
1010
|
+
*/
|
|
1011
|
+
declare class Snapshot {
|
|
1012
|
+
/**
|
|
1013
|
+
* Create a full state snapshot from entities.
|
|
1014
|
+
*
|
|
1015
|
+
* @param tick - Current tick number
|
|
1016
|
+
* @param entities - Array of entities to snapshot
|
|
1017
|
+
* @returns Full state snapshot
|
|
1018
|
+
*/
|
|
1019
|
+
static create(tick: number, entities: Entity[]): StateSnapshot;
|
|
1020
|
+
/**
|
|
1021
|
+
* Convert an entity to a snapshot.
|
|
1022
|
+
*/
|
|
1023
|
+
static entityToSnapshot(entity: Entity): EntitySnapshot;
|
|
1024
|
+
/**
|
|
1025
|
+
* Create a delta snapshot (only dirty entities).
|
|
1026
|
+
*
|
|
1027
|
+
* @param tick - Current tick number
|
|
1028
|
+
* @param dirtyEntities - Array of dirty entities
|
|
1029
|
+
* @returns Delta snapshot
|
|
1030
|
+
*/
|
|
1031
|
+
static createDelta(tick: number, dirtyEntities: Entity[]): StateSnapshot;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Ring Buffer
|
|
1036
|
+
*
|
|
1037
|
+
* O(1) insert and remove, fixed capacity, no allocations in hot path.
|
|
1038
|
+
* Used for input queues, position history, etc.
|
|
1039
|
+
*/
|
|
1040
|
+
declare class RingBuffer<T> {
|
|
1041
|
+
private readonly capacity;
|
|
1042
|
+
private readonly buffer;
|
|
1043
|
+
private head;
|
|
1044
|
+
private tail;
|
|
1045
|
+
private count;
|
|
1046
|
+
constructor(capacity: number);
|
|
1047
|
+
/**
|
|
1048
|
+
* Add item to buffer. O(1)
|
|
1049
|
+
* Returns false if buffer is full.
|
|
1050
|
+
*/
|
|
1051
|
+
push(item: T): boolean;
|
|
1052
|
+
/**
|
|
1053
|
+
* Add item, overwriting oldest if full. O(1)
|
|
1054
|
+
*/
|
|
1055
|
+
pushOverwrite(item: T): T | undefined;
|
|
1056
|
+
/**
|
|
1057
|
+
* Remove and return oldest item. O(1)
|
|
1058
|
+
*/
|
|
1059
|
+
shift(): T | undefined;
|
|
1060
|
+
/**
|
|
1061
|
+
* Peek at oldest item without removing. O(1)
|
|
1062
|
+
*/
|
|
1063
|
+
peek(): T | undefined;
|
|
1064
|
+
/**
|
|
1065
|
+
* Get current size.
|
|
1066
|
+
*/
|
|
1067
|
+
size(): number;
|
|
1068
|
+
/**
|
|
1069
|
+
* Check if empty.
|
|
1070
|
+
*/
|
|
1071
|
+
isEmpty(): boolean;
|
|
1072
|
+
/**
|
|
1073
|
+
* Check if full.
|
|
1074
|
+
*/
|
|
1075
|
+
isFull(): boolean;
|
|
1076
|
+
/**
|
|
1077
|
+
* Clear all items. O(1)
|
|
1078
|
+
*/
|
|
1079
|
+
clear(): void;
|
|
1080
|
+
/**
|
|
1081
|
+
* Get capacity.
|
|
1082
|
+
*/
|
|
1083
|
+
getCapacity(): number;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Logger utility
|
|
1088
|
+
*
|
|
1089
|
+
* Wrapper around pino for structured logging.
|
|
1090
|
+
* Consumers can override this with their own logger.
|
|
1091
|
+
*/
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Logger instance.
|
|
1095
|
+
* Can be replaced by consumers.
|
|
1096
|
+
*/
|
|
1097
|
+
declare let logger: pino.Logger<never, boolean>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Set a custom logger instance.
|
|
1100
|
+
*
|
|
1101
|
+
* @param customLogger - Custom pino logger
|
|
1102
|
+
*/
|
|
1103
|
+
declare function setLogger(customLogger: pino.Logger): void;
|
|
1104
|
+
/**
|
|
1105
|
+
* Create a child logger with additional context.
|
|
1106
|
+
*
|
|
1107
|
+
* @param bindings - Additional context fields
|
|
1108
|
+
*/
|
|
1109
|
+
declare function createChildLogger(bindings: Record<string, unknown>): pino.Logger;
|
|
1110
|
+
/**
|
|
1111
|
+
* Logger class for dependency injection.
|
|
1112
|
+
*/
|
|
1113
|
+
declare class Logger {
|
|
1114
|
+
private logger;
|
|
1115
|
+
constructor(bindings?: Record<string, unknown>);
|
|
1116
|
+
debug(obj: object, msg?: string): void;
|
|
1117
|
+
debug(msg: string): void;
|
|
1118
|
+
info(obj: object, msg?: string): void;
|
|
1119
|
+
info(msg: string): void;
|
|
1120
|
+
warn(obj: object, msg?: string): void;
|
|
1121
|
+
warn(msg: string): void;
|
|
1122
|
+
error(obj: object, msg?: string): void;
|
|
1123
|
+
error(msg: string): void;
|
|
1124
|
+
child(bindings: Record<string, unknown>): Logger;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* Network protocol definitions.
|
|
1129
|
+
*
|
|
1130
|
+
* Opcodes and message formats for client-server communication.
|
|
1131
|
+
*/
|
|
1132
|
+
/**
|
|
1133
|
+
* Client → Server opcodes.
|
|
1134
|
+
*/
|
|
1135
|
+
declare enum ClientOpcode {
|
|
1136
|
+
/** Player movement input */
|
|
1137
|
+
C_MOVE = "C_MOVE",
|
|
1138
|
+
/** Player stop input */
|
|
1139
|
+
C_STOP = "C_STOP",
|
|
1140
|
+
/** Generic action */
|
|
1141
|
+
C_ACTION = "C_ACTION"
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Server → Client opcodes.
|
|
1145
|
+
*/
|
|
1146
|
+
declare enum ServerOpcode {
|
|
1147
|
+
/** Initial state on join */
|
|
1148
|
+
S_INIT = "S_INIT",
|
|
1149
|
+
/** Delta state update */
|
|
1150
|
+
S_UPDATE = "S_UPDATE",
|
|
1151
|
+
/** Entity spawned */
|
|
1152
|
+
S_SPAWN = "S_SPAWN",
|
|
1153
|
+
/** Entity destroyed */
|
|
1154
|
+
S_DESPAWN = "S_DESPAWN",
|
|
1155
|
+
/** Custom game event */
|
|
1156
|
+
S_EVENT = "S_EVENT",
|
|
1157
|
+
/** Error message */
|
|
1158
|
+
S_ERROR = "S_ERROR"
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Client movement input.
|
|
1162
|
+
*/
|
|
1163
|
+
interface C_Move {
|
|
1164
|
+
op: ClientOpcode.C_MOVE;
|
|
1165
|
+
seq: number;
|
|
1166
|
+
dir: {
|
|
1167
|
+
x: number;
|
|
1168
|
+
y: number;
|
|
1169
|
+
};
|
|
1170
|
+
timestamp: number;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Client stop input.
|
|
1174
|
+
*/
|
|
1175
|
+
interface C_Stop {
|
|
1176
|
+
op: ClientOpcode.C_STOP;
|
|
1177
|
+
seq: number;
|
|
1178
|
+
timestamp: number;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Client action input.
|
|
1182
|
+
*/
|
|
1183
|
+
interface C_Action {
|
|
1184
|
+
op: ClientOpcode.C_ACTION;
|
|
1185
|
+
seq: number;
|
|
1186
|
+
action: string;
|
|
1187
|
+
data?: Record<string, unknown>;
|
|
1188
|
+
timestamp: number;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Server initial state.
|
|
1192
|
+
*/
|
|
1193
|
+
interface S_Init {
|
|
1194
|
+
op: ServerOpcode.S_INIT;
|
|
1195
|
+
playerId: string;
|
|
1196
|
+
tick: number;
|
|
1197
|
+
entities: Array<{
|
|
1198
|
+
id: string;
|
|
1199
|
+
x: number;
|
|
1200
|
+
y: number;
|
|
1201
|
+
vx: number;
|
|
1202
|
+
vy: number;
|
|
1203
|
+
}>;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Server delta update.
|
|
1207
|
+
*/
|
|
1208
|
+
interface S_Update {
|
|
1209
|
+
op: ServerOpcode.S_UPDATE;
|
|
1210
|
+
tick: number;
|
|
1211
|
+
entities: Array<{
|
|
1212
|
+
id: string;
|
|
1213
|
+
x: number;
|
|
1214
|
+
y: number;
|
|
1215
|
+
vx: number;
|
|
1216
|
+
vy: number;
|
|
1217
|
+
seq?: number;
|
|
1218
|
+
}>;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Server entity spawn.
|
|
1222
|
+
*/
|
|
1223
|
+
interface S_Spawn {
|
|
1224
|
+
op: ServerOpcode.S_SPAWN;
|
|
1225
|
+
entity: {
|
|
1226
|
+
id: string;
|
|
1227
|
+
x: number;
|
|
1228
|
+
y: number;
|
|
1229
|
+
vx: number;
|
|
1230
|
+
vy: number;
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Server entity despawn.
|
|
1235
|
+
*/
|
|
1236
|
+
interface S_Despawn {
|
|
1237
|
+
op: ServerOpcode.S_DESPAWN;
|
|
1238
|
+
entityId: string;
|
|
1239
|
+
reason?: string;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Server custom event.
|
|
1243
|
+
*/
|
|
1244
|
+
interface S_Event {
|
|
1245
|
+
op: ServerOpcode.S_EVENT;
|
|
1246
|
+
event: string;
|
|
1247
|
+
data?: Record<string, unknown>;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Server error message.
|
|
1251
|
+
*/
|
|
1252
|
+
interface S_Error {
|
|
1253
|
+
op: ServerOpcode.S_ERROR;
|
|
1254
|
+
code: string;
|
|
1255
|
+
message: string;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Union of all client messages.
|
|
1259
|
+
*/
|
|
1260
|
+
type ClientMessage = C_Move | C_Stop | C_Action;
|
|
1261
|
+
/**
|
|
1262
|
+
* Union of all server messages.
|
|
1263
|
+
*/
|
|
1264
|
+
type ServerMessage = S_Init | S_Update | S_Spawn | S_Despawn | S_Event | S_Error;
|
|
1265
|
+
|
|
1266
|
+
export { type AABB, AABBCollision, type ActionCommand, type Boundary, type C_Action, type C_Move, type C_Stop, type ClientMessage, ClientOpcode, type Command, Entity, type EntitySnapshot, GameLoop, type GameRules, GameServer, Grid, InputQueue, Logger, type LoopMetrics, type MoveCommand, Movement, Network, type NetworkEvent, Registry, RingBuffer, Room, type RoomConfig, type RoomMetrics, type S_Despawn, type S_Error, type S_Event, type S_Init, type S_Spawn, type S_Update, type ServerMessage, type ServerMetrics, ServerOpcode, Snapshot, type StateSnapshot, type StopCommand, type TickHandler, createChildLogger, logger, setLogger };
|