@gamerstake/game-core 0.1.0 → 0.2.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/CHANGELOG.md +142 -0
- package/CLIENT_SDK_README.md +634 -0
- package/README.md +58 -5
- package/dist/client/index.d.ts +574 -0
- package/dist/client/index.js +970 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +90 -1
- package/dist/index.js +356 -1
- package/dist/index.js.map +1 -1
- package/docs/N1-package-overview.md +303 -0
- package/docs/N2-api-reference.md +500 -0
- package/docs/N3-implementation-guide.md +550 -0
- package/docs/N4-testing-validation.md +390 -0
- package/docs/N5-integration-checklist.md +90 -0
- package/docs/README.md +137 -0
- package/examples/simple-game/README.md +17 -6
- package/examples/simple-game/client.ts +25 -14
- package/examples/simple-game/package.json +5 -1
- package/examples/simple-game/server.ts +21 -13
- package/package.json +17 -13
- package/src/adapters/SocketIOAdapter.ts +496 -0
- package/src/client/GameClient.ts +466 -0
- package/src/client/InputBuffer.ts +148 -0
- package/src/client/Interpolator.ts +242 -0
- package/src/client/Reconciler.ts +233 -0
- package/src/client/TimeSync.ts +182 -0
- package/src/client/index.ts +32 -0
- package/src/client/types.ts +247 -0
- package/src/index.ts +8 -0
- package/tests/client/InputBuffer.test.ts +118 -0
- package/tests/client/TimeSync.test.ts +116 -0
- package/tsup.config.ts +4 -1
- package/TESTING_OVERVIEW.md +0 -378
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# N4: @gamerstake/game-core - Testing & Validation Guide
|
|
2
|
+
|
|
3
|
+
> **Version:** 0.1.0
|
|
4
|
+
> **Last Updated:** January 2026
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
1. [Testing Overview](#1-testing-overview)
|
|
11
|
+
2. [Unit Testing](#2-unit-testing)
|
|
12
|
+
3. [Integration Testing](#3-integration-testing)
|
|
13
|
+
4. [Manual Testing](#4-manual-testing)
|
|
14
|
+
5. [Performance Testing](#5-performance-testing)
|
|
15
|
+
6. [Coverage Requirements](#6-coverage-requirements)
|
|
16
|
+
7. [Validation Checklist](#7-validation-checklist)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Testing Overview
|
|
21
|
+
|
|
22
|
+
### 1.1 Testing Stack
|
|
23
|
+
|
|
24
|
+
| Tool | Purpose |
|
|
25
|
+
| ---------------- | ------------------------ |
|
|
26
|
+
| Jest | Test runner & assertions |
|
|
27
|
+
| ts-jest | TypeScript support |
|
|
28
|
+
| socket.io-client | Client mocking |
|
|
29
|
+
|
|
30
|
+
### 1.2 Running Tests
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Run all tests
|
|
34
|
+
pnpm test
|
|
35
|
+
|
|
36
|
+
# Run with coverage
|
|
37
|
+
pnpm test:coverage
|
|
38
|
+
|
|
39
|
+
# Run in watch mode
|
|
40
|
+
pnpm test:watch
|
|
41
|
+
|
|
42
|
+
# Type checking
|
|
43
|
+
pnpm typecheck
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. Unit Testing
|
|
49
|
+
|
|
50
|
+
### 2.1 Entity Tests
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { describe, it, expect } from '@jest/globals';
|
|
54
|
+
import { Entity } from '@gamerstake/game-core';
|
|
55
|
+
|
|
56
|
+
describe('Entity', () => {
|
|
57
|
+
it('creates entity with initial position', () => {
|
|
58
|
+
const entity = new Entity('test-1', 100, 200);
|
|
59
|
+
|
|
60
|
+
expect(entity.id).toBe('test-1');
|
|
61
|
+
expect(entity.x).toBe(100);
|
|
62
|
+
expect(entity.y).toBe(200);
|
|
63
|
+
expect(entity.dirty).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('updates position based on velocity', () => {
|
|
67
|
+
const entity = new Entity('test-1', 0, 0);
|
|
68
|
+
entity.setVelocity(100, 50);
|
|
69
|
+
|
|
70
|
+
entity.updatePosition(1000); // 1 second
|
|
71
|
+
|
|
72
|
+
expect(entity.x).toBe(100);
|
|
73
|
+
expect(entity.y).toBe(50);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('marks dirty when position changes', () => {
|
|
77
|
+
const entity = new Entity('test-1', 0, 0);
|
|
78
|
+
entity.markClean();
|
|
79
|
+
|
|
80
|
+
entity.setPosition(10, 20);
|
|
81
|
+
|
|
82
|
+
expect(entity.dirty).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2.2 Room Tests
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { describe, it, expect, jest } from '@jest/globals';
|
|
91
|
+
import { Room, Entity, GameRules } from '@gamerstake/game-core';
|
|
92
|
+
|
|
93
|
+
const createMockRules = (): GameRules<Entity> => ({
|
|
94
|
+
onRoomCreated: jest.fn(),
|
|
95
|
+
onPlayerJoin: jest.fn(),
|
|
96
|
+
onPlayerLeave: jest.fn(),
|
|
97
|
+
onTick: jest.fn(),
|
|
98
|
+
onCommand: jest.fn(),
|
|
99
|
+
shouldEndRoom: jest.fn(() => false),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('Room', () => {
|
|
103
|
+
it('adds and removes players', () => {
|
|
104
|
+
const rules = createMockRules();
|
|
105
|
+
const room = new Room('room-1', rules);
|
|
106
|
+
const player = new Entity('p1', 0, 0);
|
|
107
|
+
|
|
108
|
+
room.addPlayer(player);
|
|
109
|
+
expect(rules.onPlayerJoin).toHaveBeenCalledWith(room, player);
|
|
110
|
+
expect(room.getRegistry().has('p1')).toBe(true);
|
|
111
|
+
|
|
112
|
+
room.removePlayer('p1');
|
|
113
|
+
expect(rules.onPlayerLeave).toHaveBeenCalledWith(room, 'p1');
|
|
114
|
+
expect(room.getRegistry().has('p1')).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('processes input commands on tick', () => {
|
|
118
|
+
const rules = createMockRules();
|
|
119
|
+
const room = new Room('room-1', rules);
|
|
120
|
+
const player = new Entity('p1', 0, 0);
|
|
121
|
+
room.addPlayer(player);
|
|
122
|
+
|
|
123
|
+
room.queueInput('p1', {
|
|
124
|
+
seq: 1,
|
|
125
|
+
type: 'move',
|
|
126
|
+
dir: { x: 1, y: 0 },
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
room.onTick(1, 50);
|
|
131
|
+
|
|
132
|
+
expect(rules.onCommand).toHaveBeenCalledTimes(1);
|
|
133
|
+
expect(rules.onTick).toHaveBeenCalledTimes(1);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 2.3 Custom Entity Tests
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
describe('Player Entity', () => {
|
|
142
|
+
it('handles damage correctly', () => {
|
|
143
|
+
const player = new Player('p1', 0, 0, 'blue');
|
|
144
|
+
player.health = 100;
|
|
145
|
+
|
|
146
|
+
const died = player.takeDamage(30);
|
|
147
|
+
|
|
148
|
+
expect(player.health).toBe(70);
|
|
149
|
+
expect(died).toBe(false);
|
|
150
|
+
expect(player.dirty).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('detects death', () => {
|
|
154
|
+
const player = new Player('p1', 0, 0, 'blue');
|
|
155
|
+
player.health = 20;
|
|
156
|
+
|
|
157
|
+
const died = player.takeDamage(50);
|
|
158
|
+
|
|
159
|
+
expect(player.health).toBe(0);
|
|
160
|
+
expect(died).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 3. Integration Testing
|
|
168
|
+
|
|
169
|
+
### 3.1 GameServer Integration
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
describe('GameServer Integration', () => {
|
|
173
|
+
it('manages multiple rooms', () => {
|
|
174
|
+
const server = new GameServer();
|
|
175
|
+
const rules = createMockRules();
|
|
176
|
+
|
|
177
|
+
const room1 = server.createRoom('room-1', rules);
|
|
178
|
+
const room2 = server.createRoom('room-2', rules);
|
|
179
|
+
|
|
180
|
+
expect(server.getRoomCount()).toBe(2);
|
|
181
|
+
expect(server.getRoom('room-1')).toBe(room1);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('prevents duplicate room IDs', () => {
|
|
185
|
+
const server = new GameServer();
|
|
186
|
+
const rules = createMockRules();
|
|
187
|
+
|
|
188
|
+
server.createRoom('room-1', rules);
|
|
189
|
+
|
|
190
|
+
expect(() => server.createRoom('room-1', rules)).toThrow('already exists');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('provides aggregated metrics', () => {
|
|
194
|
+
const server = new GameServer();
|
|
195
|
+
const rules = createMockRules();
|
|
196
|
+
|
|
197
|
+
const room1 = server.createRoom('room-1', rules);
|
|
198
|
+
room1.addPlayer(new Entity('p1', 0, 0));
|
|
199
|
+
room1.addPlayer(new Entity('p2', 0, 0));
|
|
200
|
+
|
|
201
|
+
const metrics = server.getMetrics();
|
|
202
|
+
|
|
203
|
+
expect(metrics.roomCount).toBe(1);
|
|
204
|
+
expect(metrics.totalPlayers).toBe(2);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 4. Manual Testing
|
|
212
|
+
|
|
213
|
+
### 4.1 Setup
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Build the package
|
|
217
|
+
cd packages/game-core
|
|
218
|
+
pnpm build
|
|
219
|
+
|
|
220
|
+
# Start test server
|
|
221
|
+
node examples/simple-game/server.js
|
|
222
|
+
|
|
223
|
+
# In another terminal, start client
|
|
224
|
+
node examples/simple-game/client.js
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 4.2 Test Scenarios
|
|
228
|
+
|
|
229
|
+
| Scenario | Steps | Expected Result |
|
|
230
|
+
| ------------ | -------------------------- | ----------------------------- |
|
|
231
|
+
| Connection | Start server, start client | "Connected! Player ID: xxx" |
|
|
232
|
+
| Movement | Connect, wait 1s | Position updates logged |
|
|
233
|
+
| Multi-player | Start 3+ clients | All clients see each other |
|
|
234
|
+
| Disconnect | Kill client (Ctrl+C) | Server logs "Player xxx left" |
|
|
235
|
+
|
|
236
|
+
### 4.3 Manual Testing Checklist
|
|
237
|
+
|
|
238
|
+
```markdown
|
|
239
|
+
## Connection Tests
|
|
240
|
+
|
|
241
|
+
- [ ] Single client connects successfully
|
|
242
|
+
- [ ] Multiple clients connect simultaneously
|
|
243
|
+
- [ ] Client receives S_INIT with player ID
|
|
244
|
+
|
|
245
|
+
## Movement Tests
|
|
246
|
+
|
|
247
|
+
- [ ] Player moves when C_MOVE sent
|
|
248
|
+
- [ ] Player stops when C_STOP sent
|
|
249
|
+
- [ ] Position updates broadcast to all clients
|
|
250
|
+
|
|
251
|
+
## Disconnection Tests
|
|
252
|
+
|
|
253
|
+
- [ ] Client disconnect handled gracefully
|
|
254
|
+
- [ ] Other clients notified of disconnect
|
|
255
|
+
- [ ] Server continues running after disconnect
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 5. Performance Testing
|
|
261
|
+
|
|
262
|
+
### 5.1 Benchmark Setup
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
async function runBenchmark(entityCount: number, iterations: number) {
|
|
266
|
+
const room = new Room('benchmark', new BenchmarkRules(), { cellSize: 512 });
|
|
267
|
+
|
|
268
|
+
// Spawn entities
|
|
269
|
+
for (let i = 0; i < entityCount; i++) {
|
|
270
|
+
const entity = new Entity(`e${i}`, Math.random() * 1000, Math.random() * 1000);
|
|
271
|
+
entity.setVelocity(Math.random() * 100, Math.random() * 100);
|
|
272
|
+
room.spawnEntity(entity);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Benchmark
|
|
276
|
+
const times: number[] = [];
|
|
277
|
+
for (let i = 0; i < iterations; i++) {
|
|
278
|
+
const start = performance.now();
|
|
279
|
+
room.onTick(i, 50);
|
|
280
|
+
times.push(performance.now() - start);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
|
284
|
+
console.log(`Entities: ${entityCount}, Avg tick: ${avg.toFixed(2)}ms`);
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 5.2 Performance Criteria
|
|
289
|
+
|
|
290
|
+
| Metric | Pass | Warn | Fail |
|
|
291
|
+
| ----------------------- | ------ | -------- | ------- |
|
|
292
|
+
| Avg tick (100 entities) | < 10ms | 10-30ms | > 30ms |
|
|
293
|
+
| Avg tick (500 entities) | < 25ms | 25-40ms | > 40ms |
|
|
294
|
+
| Max tick | < 50ms | 50-100ms | > 100ms |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## 6. Coverage Requirements
|
|
299
|
+
|
|
300
|
+
### 6.1 Minimum Coverage
|
|
301
|
+
|
|
302
|
+
```javascript
|
|
303
|
+
// jest.config.ts
|
|
304
|
+
coverageThreshold: {
|
|
305
|
+
global: {
|
|
306
|
+
branches: 80,
|
|
307
|
+
functions: 80,
|
|
308
|
+
lines: 80,
|
|
309
|
+
statements: 80,
|
|
310
|
+
},
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 6.2 Coverage by Module
|
|
315
|
+
|
|
316
|
+
| Module | Target |
|
|
317
|
+
| ---------- | ------ |
|
|
318
|
+
| Entity | 90% |
|
|
319
|
+
| Registry | 85% |
|
|
320
|
+
| Room | 85% |
|
|
321
|
+
| GameServer | 80% |
|
|
322
|
+
| Grid | 80% |
|
|
323
|
+
| InputQueue | 85% |
|
|
324
|
+
| Network | 75% |
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 7. Validation Checklist
|
|
329
|
+
|
|
330
|
+
### 7.1 Pre-Release Validation
|
|
331
|
+
|
|
332
|
+
```markdown
|
|
333
|
+
## Code Quality
|
|
334
|
+
|
|
335
|
+
- [ ] All unit tests passing
|
|
336
|
+
- [ ] Coverage >= 80%
|
|
337
|
+
- [ ] No TypeScript errors
|
|
338
|
+
- [ ] No lint errors
|
|
339
|
+
- [ ] Build succeeds
|
|
340
|
+
|
|
341
|
+
## Integration Tests
|
|
342
|
+
|
|
343
|
+
- [ ] GameServer manages rooms correctly
|
|
344
|
+
- [ ] Room tick cycle works
|
|
345
|
+
- [ ] Player join/leave handled
|
|
346
|
+
- [ ] Input processing works
|
|
347
|
+
- [ ] State broadcasting works
|
|
348
|
+
|
|
349
|
+
## Manual Testing
|
|
350
|
+
|
|
351
|
+
- [ ] Connection test passed
|
|
352
|
+
- [ ] Movement test passed
|
|
353
|
+
- [ ] Multi-player test passed
|
|
354
|
+
- [ ] Disconnection test passed
|
|
355
|
+
|
|
356
|
+
## Performance
|
|
357
|
+
|
|
358
|
+
- [ ] Avg tick < 40ms (100 entities)
|
|
359
|
+
- [ ] Max tick < 50ms
|
|
360
|
+
- [ ] No memory leaks
|
|
361
|
+
- [ ] 20 TPS maintained under load
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 7.2 Integration Validation
|
|
365
|
+
|
|
366
|
+
```markdown
|
|
367
|
+
## Setup Validation
|
|
368
|
+
|
|
369
|
+
- [ ] Package installed correctly
|
|
370
|
+
- [ ] Imports working
|
|
371
|
+
- [ ] TypeScript types available
|
|
372
|
+
|
|
373
|
+
## Implementation Validation
|
|
374
|
+
|
|
375
|
+
- [ ] GameRules implemented completely
|
|
376
|
+
- [ ] All callbacks implemented
|
|
377
|
+
- [ ] Custom entities serialize correctly
|
|
378
|
+
|
|
379
|
+
## Runtime Validation
|
|
380
|
+
|
|
381
|
+
- [ ] Room starts and ticks
|
|
382
|
+
- [ ] Players can join
|
|
383
|
+
- [ ] Inputs processed correctly
|
|
384
|
+
- [ ] State synchronized
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
**Previous:** [N3 - Implementation Guide](./N3-implementation-guide.md)
|
|
390
|
+
**Next:** [N5 - Integration Checklist](./N5-integration-checklist.md)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# N5: Integration Checklist
|
|
2
|
+
|
|
3
|
+
> Version: 0.1.0
|
|
4
|
+
|
|
5
|
+
## Phase 1: Environment Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- [ ] Node.js 20+ installed
|
|
10
|
+
- [ ] pnpm available
|
|
11
|
+
- [ ] TypeScript installed
|
|
12
|
+
|
|
13
|
+
### Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @gamerstake/game-core socket.io
|
|
17
|
+
pnpm add -D typescript @types/node tsx
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Phase 2: Implementation
|
|
21
|
+
|
|
22
|
+
### GameRules Checklist
|
|
23
|
+
|
|
24
|
+
- [ ] onRoomCreated implemented
|
|
25
|
+
- [ ] onPlayerJoin implemented
|
|
26
|
+
- [ ] onPlayerLeave implemented
|
|
27
|
+
- [ ] onTick implemented
|
|
28
|
+
- [ ] onCommand implemented
|
|
29
|
+
- [ ] shouldEndRoom implemented
|
|
30
|
+
|
|
31
|
+
### Server Checklist
|
|
32
|
+
|
|
33
|
+
- [ ] Socket.io server created
|
|
34
|
+
- [ ] GameServer instance created
|
|
35
|
+
- [ ] Room created with config
|
|
36
|
+
- [ ] Connection handler with addPlayer
|
|
37
|
+
- [ ] C_MOVE and C_STOP handlers
|
|
38
|
+
- [ ] Disconnect handler with removePlayer
|
|
39
|
+
|
|
40
|
+
## Phase 3: Testing
|
|
41
|
+
|
|
42
|
+
### Unit Tests
|
|
43
|
+
|
|
44
|
+
- [ ] Entity tests passing
|
|
45
|
+
- [ ] Room tests passing
|
|
46
|
+
- [ ] GameRules tests passing
|
|
47
|
+
|
|
48
|
+
### Manual Tests
|
|
49
|
+
|
|
50
|
+
- [ ] Server starts
|
|
51
|
+
- [ ] Client connects
|
|
52
|
+
- [ ] Movement works
|
|
53
|
+
- [ ] Multiple players work
|
|
54
|
+
- [ ] Disconnect handled
|
|
55
|
+
|
|
56
|
+
## Phase 4: Verification
|
|
57
|
+
|
|
58
|
+
| Feature | Pass |
|
|
59
|
+
| --------------------- | ---- |
|
|
60
|
+
| S_INIT received | [ ] |
|
|
61
|
+
| S_UPDATE received | [ ] |
|
|
62
|
+
| Movement works | [ ] |
|
|
63
|
+
| Other players visible | [ ] |
|
|
64
|
+
|
|
65
|
+
### Performance
|
|
66
|
+
|
|
67
|
+
- [ ] Avg tick < 40ms
|
|
68
|
+
- [ ] Max tick < 50ms
|
|
69
|
+
- [ ] 20 TPS maintained
|
|
70
|
+
|
|
71
|
+
## Phase 5: Production
|
|
72
|
+
|
|
73
|
+
- [ ] Error handling added
|
|
74
|
+
- [ ] Graceful shutdown
|
|
75
|
+
- [ ] Input validation
|
|
76
|
+
- [ ] Logging configured
|
|
77
|
+
|
|
78
|
+
## Sign-Off
|
|
79
|
+
|
|
80
|
+
| Phase | Complete |
|
|
81
|
+
| -------------- | -------- |
|
|
82
|
+
| Environment | [ ] |
|
|
83
|
+
| Implementation | [ ] |
|
|
84
|
+
| Testing | [ ] |
|
|
85
|
+
| Verification | [ ] |
|
|
86
|
+
| Production | [ ] |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
**Previous:** [N4 - Testing](./N4-testing-validation.md)
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# @gamerstake/game-core - Package Documentation
|
|
2
|
+
|
|
3
|
+
> **For External Teams**
|
|
4
|
+
> **Version:** 0.1.0
|
|
5
|
+
> **Last Updated:** January 2026
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Welcome
|
|
10
|
+
|
|
11
|
+
This documentation package provides everything you need to integrate `@gamerstake/game-core` into your multiplayer game. The package is a battle-tested, high-performance multiplayer game engine that handles real-time networking, game loops, entity management, and state synchronization.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Document Index
|
|
16
|
+
|
|
17
|
+
| Document | Description | Audience |
|
|
18
|
+
| ----------------------------------------------------------- | ---------------------------------------------- | ------------- |
|
|
19
|
+
| [N1 - Package Overview](./N1-package-overview.md) | Executive summary, requirements, core concepts | Everyone |
|
|
20
|
+
| [N2 - API Reference](./N2-api-reference.md) | Complete API documentation | Developers |
|
|
21
|
+
| [N3 - Implementation Guide](./N3-implementation-guide.md) | Step-by-step implementation tutorial | Developers |
|
|
22
|
+
| [N4 - Testing & Validation](./N4-testing-validation.md) | Testing requirements and patterns | QA/Developers |
|
|
23
|
+
| [N5 - Integration Checklist](./N5-integration-checklist.md) | Verification checklist for integration | All Teams |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Navigation
|
|
28
|
+
|
|
29
|
+
### Just Getting Started?
|
|
30
|
+
|
|
31
|
+
1. **Read** [N1 - Package Overview](./N1-package-overview.md) to understand what game-core provides
|
|
32
|
+
2. **Follow** [N3 - Implementation Guide](./N3-implementation-guide.md) Quick Start section
|
|
33
|
+
3. **Use** [N5 - Integration Checklist](./N5-integration-checklist.md) to verify your setup
|
|
34
|
+
|
|
35
|
+
### Need API Details?
|
|
36
|
+
|
|
37
|
+
- [N2 - API Reference](./N2-api-reference.md) has complete documentation for all exports
|
|
38
|
+
|
|
39
|
+
### Setting Up Testing?
|
|
40
|
+
|
|
41
|
+
- [N4 - Testing & Validation](./N4-testing-validation.md) covers unit tests, integration tests, and performance testing
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## What game-core Provides
|
|
46
|
+
|
|
47
|
+
- **Fixed tick-rate game loop** (20 TPS with drift compensation)
|
|
48
|
+
- **Authoritative server architecture** (server is source of truth)
|
|
49
|
+
- **Client-server networking** (via Socket.io)
|
|
50
|
+
- **State synchronization** (full snapshots + delta updates)
|
|
51
|
+
- **Input buffering** (reliable command processing)
|
|
52
|
+
- **Entity management** (ECS-lite with dirty tracking)
|
|
53
|
+
- **Spatial partitioning** (grid-based O(1) queries)
|
|
54
|
+
- **Basic 2D physics** (velocity, position, AABB collision)
|
|
55
|
+
|
|
56
|
+
## What You Provide
|
|
57
|
+
|
|
58
|
+
- **Game rules** (implement the `GameRules` interface)
|
|
59
|
+
- **Game client** (render game state, send inputs)
|
|
60
|
+
- **Persistence** (optional - save/load game state)
|
|
61
|
+
- **Authentication** (optional - player identity)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Minimum Example
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// 1. Implement GameRules
|
|
69
|
+
import { GameRules, Room, Entity, Command, MoveCommand } from '@gamerstake/game-core';
|
|
70
|
+
|
|
71
|
+
class MyGameRules implements GameRules {
|
|
72
|
+
onRoomCreated(room: Room): void {}
|
|
73
|
+
onPlayerJoin(room: Room, player: Entity): void {
|
|
74
|
+
player.setPosition(Math.random() * 800, Math.random() * 600);
|
|
75
|
+
}
|
|
76
|
+
onPlayerLeave(room: Room, playerId: string): void {}
|
|
77
|
+
onTick(room: Room, delta: number): void {
|
|
78
|
+
room.getRegistry().forEach((e) => e.updatePosition(delta));
|
|
79
|
+
}
|
|
80
|
+
onCommand(room: Room, playerId: string, command: Command): void {
|
|
81
|
+
const player = room.getRegistry().get(playerId);
|
|
82
|
+
if (!player) return;
|
|
83
|
+
if (command.type === 'move') {
|
|
84
|
+
const { dir } = command as MoveCommand;
|
|
85
|
+
player.setVelocity(dir.x * 200, dir.y * 200);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
shouldEndRoom(room: Room): boolean {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 2. Create server
|
|
94
|
+
import { Server } from 'socket.io';
|
|
95
|
+
import { GameServer, Entity } from '@gamerstake/game-core';
|
|
96
|
+
|
|
97
|
+
const io = new Server(3000);
|
|
98
|
+
const gameServer = new GameServer();
|
|
99
|
+
gameServer.setServer(io);
|
|
100
|
+
|
|
101
|
+
const room = gameServer.createRoom('main', new MyGameRules());
|
|
102
|
+
|
|
103
|
+
io.on('connection', (socket) => {
|
|
104
|
+
const player = new Entity(socket.id, 0, 0);
|
|
105
|
+
room.addPlayer(player);
|
|
106
|
+
room.getNetwork().registerSocket(socket.id, socket);
|
|
107
|
+
|
|
108
|
+
socket.emit('S_INIT', { playerId: socket.id, ...room.getSnapshot() });
|
|
109
|
+
|
|
110
|
+
socket.on('C_MOVE', (data) => {
|
|
111
|
+
room.queueInput(socket.id, { ...data, type: 'move', timestamp: Date.now() });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
socket.on('disconnect', () => {
|
|
115
|
+
room.removePlayer(socket.id);
|
|
116
|
+
room.getNetwork().unregisterSocket(socket.id);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Version History
|
|
124
|
+
|
|
125
|
+
| Version | Date | Changes |
|
|
126
|
+
| ------- | ------------ | --------------- |
|
|
127
|
+
| 0.1.0 | January 2026 | Initial release |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Support
|
|
132
|
+
|
|
133
|
+
For questions or issues, contact the GamerStake engineering team.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
**Start with:** [N1 - Package Overview](./N1-package-overview.md)
|
|
@@ -27,6 +27,7 @@ node examples/simple-game/server.js
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
You should see:
|
|
30
|
+
|
|
30
31
|
```
|
|
31
32
|
Game server started on port 3000
|
|
32
33
|
Room: test-room
|
|
@@ -40,6 +41,7 @@ node examples/simple-game/client.js
|
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
The client will:
|
|
44
|
+
|
|
43
45
|
- Connect to the server
|
|
44
46
|
- Receive initial game state
|
|
45
47
|
- Automatically move around in different patterns
|
|
@@ -75,6 +77,7 @@ Each client is a separate player. You'll see them join in the server logs!
|
|
|
75
77
|
### Test 1: Movement
|
|
76
78
|
|
|
77
79
|
The client automatically cycles through movement patterns:
|
|
80
|
+
|
|
78
81
|
- Right, Down, Left, Up
|
|
79
82
|
- Diagonal movement
|
|
80
83
|
- Stop command
|
|
@@ -83,6 +86,7 @@ The client automatically cycles through movement patterns:
|
|
|
83
86
|
### Test 2: Multiple Players
|
|
84
87
|
|
|
85
88
|
Run 3-5 clients simultaneously to test:
|
|
89
|
+
|
|
86
90
|
- Multiple players in the same room
|
|
87
91
|
- State synchronization
|
|
88
92
|
- Network broadcasting
|
|
@@ -91,6 +95,7 @@ Run 3-5 clients simultaneously to test:
|
|
|
91
95
|
### Test 3: Performance
|
|
92
96
|
|
|
93
97
|
Monitor the server metrics logs:
|
|
98
|
+
|
|
94
99
|
```
|
|
95
100
|
Room: X players, Y entities, Avg tick: Zms
|
|
96
101
|
```
|
|
@@ -100,6 +105,7 @@ Tick time should stay well below 50ms (the tick budget for 20 TPS).
|
|
|
100
105
|
### Test 4: Connection Stability
|
|
101
106
|
|
|
102
107
|
Try:
|
|
108
|
+
|
|
103
109
|
- Stopping clients (Ctrl+C) and reconnecting
|
|
104
110
|
- Stopping the server and restarting
|
|
105
111
|
- Network interruptions
|
|
@@ -119,6 +125,7 @@ SERVER_URL=http://localhost:4000 node examples/simple-game/client.js
|
|
|
119
125
|
### Modify Game Rules
|
|
120
126
|
|
|
121
127
|
Edit `server.ts` and change the `SimpleGameRules` class:
|
|
128
|
+
|
|
122
129
|
- Spawn different entities
|
|
123
130
|
- Change movement speed
|
|
124
131
|
- Add collision detection
|
|
@@ -127,14 +134,15 @@ Edit `server.ts` and change the `SimpleGameRules` class:
|
|
|
127
134
|
### Add More Commands
|
|
128
135
|
|
|
129
136
|
In `server.ts`, add new socket event handlers:
|
|
137
|
+
|
|
130
138
|
```typescript
|
|
131
139
|
socket.on('C_CUSTOM', (data) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
room.queueInput(playerId, {
|
|
141
|
+
seq: data.seq,
|
|
142
|
+
type: 'custom',
|
|
143
|
+
// ...custom data
|
|
144
|
+
timestamp: Date.now(),
|
|
145
|
+
});
|
|
138
146
|
});
|
|
139
147
|
```
|
|
140
148
|
|
|
@@ -145,6 +153,7 @@ Then handle it in `onCommand()`.
|
|
|
145
153
|
### "Cannot find module './dist/index.js'"
|
|
146
154
|
|
|
147
155
|
You need to build the package first:
|
|
156
|
+
|
|
148
157
|
```bash
|
|
149
158
|
pnpm build
|
|
150
159
|
```
|
|
@@ -152,6 +161,7 @@ pnpm build
|
|
|
152
161
|
### "Port 3000 already in use"
|
|
153
162
|
|
|
154
163
|
Change the port:
|
|
164
|
+
|
|
155
165
|
```bash
|
|
156
166
|
PORT=4000 node examples/simple-game/server.js
|
|
157
167
|
```
|
|
@@ -159,6 +169,7 @@ PORT=4000 node examples/simple-game/server.js
|
|
|
159
169
|
### Client can't connect
|
|
160
170
|
|
|
161
171
|
Make sure:
|
|
172
|
+
|
|
162
173
|
1. Server is running
|
|
163
174
|
2. Port matches (default 3000)
|
|
164
175
|
3. No firewall blocking
|