@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
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Manual Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to manually test the `@gamerstake/game-core` package with a live server and clients.
|
|
4
|
+
|
|
5
|
+
## Why Manual Testing?
|
|
6
|
+
|
|
7
|
+
While automated tests verify individual components, **manual testing lets you**:
|
|
8
|
+
|
|
9
|
+
See the game engine running in real-time
|
|
10
|
+
Test networking and state synchronization
|
|
11
|
+
Monitor performance metrics
|
|
12
|
+
Simulate real player behavior
|
|
13
|
+
Debug issues interactively
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### Step 1: Build the Package
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd /Users/nbjekovic/GAMESTAKES/gamerstake/packages/game-core
|
|
21
|
+
pnpm build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Step 2: Start the Test Server
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node examples/simple-game/server.js
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Expected output:**
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Game server started on port 3000
|
|
34
|
+
Room: test-room
|
|
35
|
+
Tick rate: 20 TPS
|
|
36
|
+
|
|
37
|
+
Connect with: node examples/simple-game/client.js
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The server:
|
|
41
|
+
|
|
42
|
+
- Runs a game loop at **20 TPS** (50ms per tick)
|
|
43
|
+
- Creates a persistent world (1000x1000 units)
|
|
44
|
+
- Spawns 5 random obstacle entities
|
|
45
|
+
- Logs player activity and metrics
|
|
46
|
+
|
|
47
|
+
### Step 3: Connect Clients
|
|
48
|
+
|
|
49
|
+
**In a new terminal:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
node examples/simple-game/client.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Expected output:**
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Connecting to server: http://localhost:3000
|
|
59
|
+
Connected! Socket ID: abc123
|
|
60
|
+
Initialized! Player ID: abc123
|
|
61
|
+
Initial entities: 6
|
|
62
|
+
You are at (542, 789)
|
|
63
|
+
|
|
64
|
+
Starting movement test...
|
|
65
|
+
|
|
66
|
+
Moving right
|
|
67
|
+
Position: (592, 789) | Velocity: (200, 0) | Total entities: 6
|
|
68
|
+
Moving down
|
|
69
|
+
Position: (592, 839) | Velocity: (0, 200) | Total entities: 6
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The client:
|
|
73
|
+
|
|
74
|
+
- Connects to the server
|
|
75
|
+
- Receives initial game state
|
|
76
|
+
- **Automatically moves in patterns** (right, down, left, up, diagonal)
|
|
77
|
+
- Logs position updates every 20 ticks
|
|
78
|
+
- Demonstrates all command types (move, stop, action)
|
|
79
|
+
|
|
80
|
+
### Step 4: Test with Multiple Clients
|
|
81
|
+
|
|
82
|
+
**Open 3-4 more terminals and run:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node examples/simple-game/client.js # Terminal 3
|
|
86
|
+
node examples/simple-game/client.js # Terminal 4
|
|
87
|
+
node examples/simple-game/client.js # Terminal 5
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**On the server, you'll see:**
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Socket connected: def456
|
|
94
|
+
Player def456 joined at (234, 567)
|
|
95
|
+
Player def456 joined at (234, 567)
|
|
96
|
+
|
|
97
|
+
Socket connected: ghi789
|
|
98
|
+
Player ghi789 joined at (890, 123)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## What to Observe
|
|
102
|
+
|
|
103
|
+
### On the Server Side
|
|
104
|
+
|
|
105
|
+
**Player Lifecycle**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Socket connected: <id>
|
|
109
|
+
Player <id> joined at (x, y)
|
|
110
|
+
Player <id> moving: (dx, dy)
|
|
111
|
+
⏸ Player <id> stopped
|
|
112
|
+
Player <id> performed action: {"action":"jump","height":100}
|
|
113
|
+
Player <id> left
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Performance Metrics** (every 10 seconds)
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Room: 3 players, 8 entities, Avg tick: 12.34ms
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Good tick time:** < 40ms (80% of 50ms budget)
|
|
123
|
+
**Warning:** > 40ms means optimization needed
|
|
124
|
+
**Critical:** > 50ms means dropped frames
|
|
125
|
+
|
|
126
|
+
### On the Client Side
|
|
127
|
+
|
|
128
|
+
**State Synchronization**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Position: (100, 200) | Velocity: (200, 0) | Total entities: 8
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- Position updates smoothly as player moves
|
|
135
|
+
- Total entities increases as more players join
|
|
136
|
+
- Other players' positions are received in updates
|
|
137
|
+
|
|
138
|
+
**Multiplayer Events**
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Player def456 joined at (234, 567)
|
|
142
|
+
Player ghi789 performed action: jump
|
|
143
|
+
Player def456 left
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Testing Scenarios
|
|
147
|
+
|
|
148
|
+
### Scenario 1: Basic Functionality
|
|
149
|
+
|
|
150
|
+
**Goal:** Verify core features work
|
|
151
|
+
|
|
152
|
+
1. Start server
|
|
153
|
+
2. Connect 1 client
|
|
154
|
+
3. Watch client move automatically
|
|
155
|
+
4. Verify position updates on both sides
|
|
156
|
+
|
|
157
|
+
**Expected:**
|
|
158
|
+
|
|
159
|
+
- Player spawns at random position
|
|
160
|
+
- Movement commands processed correctly
|
|
161
|
+
- State broadcasted every tick (50ms)
|
|
162
|
+
|
|
163
|
+
### Scenario 2: Multiplayer
|
|
164
|
+
|
|
165
|
+
**Goal:** Test multiple players
|
|
166
|
+
|
|
167
|
+
1. Start server
|
|
168
|
+
2. Connect 3-5 clients simultaneously
|
|
169
|
+
3. Watch all clients move in different patterns
|
|
170
|
+
4. Stop one client (Ctrl+C), verify others continue
|
|
171
|
+
|
|
172
|
+
**Expected:**
|
|
173
|
+
|
|
174
|
+
- All players see each other
|
|
175
|
+
- State synchronized across clients
|
|
176
|
+
- Server handles disconnections gracefully
|
|
177
|
+
- No crashes or errors
|
|
178
|
+
|
|
179
|
+
### Scenario 3: Performance
|
|
180
|
+
|
|
181
|
+
**Goal:** Verify performance under load
|
|
182
|
+
|
|
183
|
+
1. Start server
|
|
184
|
+
2. Connect 10+ clients
|
|
185
|
+
3. Watch server metrics logs
|
|
186
|
+
4. Monitor tick time
|
|
187
|
+
|
|
188
|
+
**Expected:**
|
|
189
|
+
|
|
190
|
+
- Tick time stays < 40ms
|
|
191
|
+
- No memory leaks over time
|
|
192
|
+
- Smooth updates for all clients
|
|
193
|
+
|
|
194
|
+
### Scenario 4: Connection Stability
|
|
195
|
+
|
|
196
|
+
**Goal:** Test network reliability
|
|
197
|
+
|
|
198
|
+
1. Start server with 2-3 clients
|
|
199
|
+
2. Stop and restart a client
|
|
200
|
+
3. Stop and restart the server
|
|
201
|
+
4. Simulate poor network (if possible)
|
|
202
|
+
|
|
203
|
+
**Expected:**
|
|
204
|
+
|
|
205
|
+
- Clients reconnect successfully
|
|
206
|
+
- State resynchronized on reconnect
|
|
207
|
+
- Graceful error handling
|
|
208
|
+
|
|
209
|
+
### Scenario 5: Commands
|
|
210
|
+
|
|
211
|
+
**Goal:** Verify input processing
|
|
212
|
+
|
|
213
|
+
The automated client cycles through:
|
|
214
|
+
|
|
215
|
+
- **Move commands** (right, down, left, up, diagonal)
|
|
216
|
+
- **Stop command** (velocity = 0)
|
|
217
|
+
- **Action command** (custom game event)
|
|
218
|
+
|
|
219
|
+
**Expected:**
|
|
220
|
+
|
|
221
|
+
- All command types processed
|
|
222
|
+
- Server logs show correct commands
|
|
223
|
+
- State updated accordingly
|
|
224
|
+
|
|
225
|
+
## Customizing Tests
|
|
226
|
+
|
|
227
|
+
### Change Server Port
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
PORT=4000 node examples/simple-game/server.js
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
SERVER_URL=http://localhost:4000 node examples/simple-game/client.js
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Modify Client Behavior
|
|
238
|
+
|
|
239
|
+
Edit `examples/simple-game/client.ts` to:
|
|
240
|
+
|
|
241
|
+
- Change movement patterns
|
|
242
|
+
- Add custom commands
|
|
243
|
+
- Test specific scenarios
|
|
244
|
+
- Simulate player behavior
|
|
245
|
+
|
|
246
|
+
### Modify Server Rules
|
|
247
|
+
|
|
248
|
+
Edit `examples/simple-game/server.ts` to:
|
|
249
|
+
|
|
250
|
+
- Change world size or boundaries
|
|
251
|
+
- Add collision detection
|
|
252
|
+
- Implement game mechanics
|
|
253
|
+
- Test custom logic
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
### Server won't start
|
|
258
|
+
|
|
259
|
+
**"Port 3000 already in use"**
|
|
260
|
+
|
|
261
|
+
**Solution:**
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
PORT=4000 node examples/simple-game/server.js
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Or kill the process using port 3000:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
lsof -ti:3000 | xargs kill
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
**"Cannot find module './dist/index.js'"**
|
|
276
|
+
|
|
277
|
+
**Solution:** Build the package first:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
pnpm build
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Client can't connect
|
|
284
|
+
|
|
285
|
+
**"Connection error: xhr poll error"**
|
|
286
|
+
|
|
287
|
+
**Check:**
|
|
288
|
+
|
|
289
|
+
1. Is the server running?
|
|
290
|
+
2. Is the port correct? (default: 3000)
|
|
291
|
+
3. Is there a firewall blocking?
|
|
292
|
+
4. Try `localhost` vs `127.0.0.1`
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
**Client connects but no updates**
|
|
297
|
+
|
|
298
|
+
**Check:**
|
|
299
|
+
|
|
300
|
+
1. Are you seeing `S_INIT` event?
|
|
301
|
+
2. Check server logs for errors
|
|
302
|
+
3. Verify client is sending commands
|
|
303
|
+
|
|
304
|
+
### Performance Issues
|
|
305
|
+
|
|
306
|
+
**Tick time > 50ms**
|
|
307
|
+
|
|
308
|
+
**Causes:**
|
|
309
|
+
|
|
310
|
+
- Too many entities
|
|
311
|
+
- Expensive operations in `onTick()`
|
|
312
|
+
- Not using spatial grid for queries
|
|
313
|
+
- Memory leak (growing entity count)
|
|
314
|
+
|
|
315
|
+
**Solutions:**
|
|
316
|
+
|
|
317
|
+
- Profile with console.time()
|
|
318
|
+
- Use grid for spatial queries
|
|
319
|
+
- Limit entity count
|
|
320
|
+
- Check for memory leaks
|
|
321
|
+
|
|
322
|
+
## Interpreting Results
|
|
323
|
+
|
|
324
|
+
### Success Indicators
|
|
325
|
+
|
|
326
|
+
- Server starts without errors
|
|
327
|
+
- Clients connect successfully
|
|
328
|
+
- Position updates smoothly
|
|
329
|
+
- Tick time < 40ms consistently
|
|
330
|
+
- Multiple clients work simultaneously
|
|
331
|
+
- No crashes or disconnects
|
|
332
|
+
|
|
333
|
+
### Warning Signs
|
|
334
|
+
|
|
335
|
+
- Tick time 40-50ms (optimization recommended)
|
|
336
|
+
- Occasional disconnects
|
|
337
|
+
- Delayed state updates
|
|
338
|
+
- Growing memory usage
|
|
339
|
+
|
|
340
|
+
### Failure Indicators
|
|
341
|
+
|
|
342
|
+
- Tick time > 50ms (dropping frames)
|
|
343
|
+
- Clients can't connect
|
|
344
|
+
- Frequent crashes
|
|
345
|
+
- State desync between clients
|
|
346
|
+
- Memory leaks
|
|
347
|
+
|
|
348
|
+
## Next Steps
|
|
349
|
+
|
|
350
|
+
After manual testing works:
|
|
351
|
+
|
|
352
|
+
1. **Build your game** - Use this as a template
|
|
353
|
+
2. **Add game mechanics** - Combat, items, scoring
|
|
354
|
+
3. **Create a UI client** - Web-based with Canvas/Phaser
|
|
355
|
+
4. **Add automated tests** - For your game rules
|
|
356
|
+
5. **Deploy** - Docker, cloud hosting
|
|
357
|
+
|
|
358
|
+
See the **Developer Guide** for building complete games!
|
|
359
|
+
|
|
360
|
+
## Additional Resources
|
|
361
|
+
|
|
362
|
+
- **[Quick Start](./QUICK_START.md)** - Get started in 5 minutes
|
|
363
|
+
- **[Developer Guide](./DEVELOPER_GUIDE.md)** - Complete game development guide
|
|
364
|
+
- **[Example Code](./examples/simple-game/)** - Server and client source code
|
|
365
|
+
- **[Automated Tests](./tests/)** - Unit test examples
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
**Happy testing! **
|
package/QUICK_START.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# Quick Start Guide
|
|
2
|
+
|
|
3
|
+
**Get a multiplayer game running in 5 minutes!**
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
node --version # Should be 20+
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 1. Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @gamerstake/game-core socket.io
|
|
15
|
+
pnpm add -D socket.io-client typescript @types/node
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 2. Create Game Rules
|
|
19
|
+
|
|
20
|
+
Create `server.ts`:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Server } from 'socket.io';
|
|
24
|
+
import { GameServer, GameRules, Room, Entity, Command, MoveCommand } from '@gamerstake/game-core';
|
|
25
|
+
|
|
26
|
+
// Step 1: Define your game logic
|
|
27
|
+
class MyGameRules implements GameRules {
|
|
28
|
+
onRoomCreated(room: Room) {
|
|
29
|
+
console.log(' Room created!');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onPlayerJoin(room: Room, player: Entity) {
|
|
33
|
+
// Spawn player at random position
|
|
34
|
+
player.setPosition(Math.random() * 1000, Math.random() * 1000);
|
|
35
|
+
console.log(` Player ${player.id} joined`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onPlayerLeave(room: Room, playerId: string) {
|
|
39
|
+
console.log(` Player ${playerId} left`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onTick(room: Room, delta: number) {
|
|
43
|
+
// Update all moving entities
|
|
44
|
+
room.getRegistry().forEach((entity) => {
|
|
45
|
+
if (entity.vx !== 0 || entity.vy !== 0) {
|
|
46
|
+
entity.updatePosition(delta);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onCommand(room: Room, playerId: string, command: Command) {
|
|
52
|
+
const player = room.getRegistry().get(playerId);
|
|
53
|
+
if (!player) return;
|
|
54
|
+
|
|
55
|
+
if (command.type === 'move') {
|
|
56
|
+
const moveCmd = command as MoveCommand;
|
|
57
|
+
const speed = 200;
|
|
58
|
+
|
|
59
|
+
// Normalize direction and apply speed
|
|
60
|
+
const len = Math.hypot(moveCmd.dir.x, moveCmd.dir.y);
|
|
61
|
+
if (len > 0) {
|
|
62
|
+
player.setVelocity((moveCmd.dir.x / len) * speed, (moveCmd.dir.y / len) * speed);
|
|
63
|
+
}
|
|
64
|
+
} else if (command.type === 'stop') {
|
|
65
|
+
player.setVelocity(0, 0);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
shouldEndRoom(room: Room) {
|
|
70
|
+
return false; // Persistent world
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Step 2: Create server and room
|
|
75
|
+
const io = new Server(3000, { cors: { origin: '*' } });
|
|
76
|
+
const gameServer = new GameServer();
|
|
77
|
+
gameServer.setServer(io);
|
|
78
|
+
|
|
79
|
+
const room = gameServer.createRoom('my-room', new MyGameRules(), {
|
|
80
|
+
tickRate: 20,
|
|
81
|
+
cellSize: 512,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Step 3: Handle connections
|
|
85
|
+
io.on('connection', (socket) => {
|
|
86
|
+
const playerId = socket.id;
|
|
87
|
+
|
|
88
|
+
// Create player entity
|
|
89
|
+
const player = new Entity(playerId, 0, 0);
|
|
90
|
+
room.addPlayer(player);
|
|
91
|
+
room.getNetwork().registerSocket(playerId, socket);
|
|
92
|
+
|
|
93
|
+
// Send initial state
|
|
94
|
+
socket.emit('S_INIT', {
|
|
95
|
+
playerId,
|
|
96
|
+
...room.getSnapshot(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Handle player inputs
|
|
100
|
+
socket.on('C_MOVE', (data) => {
|
|
101
|
+
room.queueInput(playerId, {
|
|
102
|
+
seq: data.seq,
|
|
103
|
+
type: 'move',
|
|
104
|
+
dir: data.dir,
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
socket.on('C_STOP', (data) => {
|
|
110
|
+
room.queueInput(playerId, {
|
|
111
|
+
seq: data.seq,
|
|
112
|
+
type: 'stop',
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
socket.on('disconnect', () => {
|
|
118
|
+
room.removePlayer(playerId);
|
|
119
|
+
room.getNetwork().unregisterSocket(playerId);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
console.log(' Server running on http://localhost:3000');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 3. Run the Server
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npx tsx server.ts
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
You should see:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Server running on http://localhost:3000
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 4. Create a Test Client
|
|
139
|
+
|
|
140
|
+
Create `client.ts`:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { io } from 'socket.io-client';
|
|
144
|
+
|
|
145
|
+
const socket = io('http://localhost:3000');
|
|
146
|
+
let seq = 0;
|
|
147
|
+
|
|
148
|
+
socket.on('S_INIT', (data) => {
|
|
149
|
+
console.log(' Connected! Player ID:', data.playerId);
|
|
150
|
+
console.log(' Entities:', data.entities.length);
|
|
151
|
+
|
|
152
|
+
// Start moving right after 1 second
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
console.log(' Moving right...');
|
|
155
|
+
socket.emit('C_MOVE', {
|
|
156
|
+
seq: ++seq,
|
|
157
|
+
dir: { x: 1, y: 0 },
|
|
158
|
+
});
|
|
159
|
+
}, 1000);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
socket.on('S_UPDATE', (data) => {
|
|
163
|
+
const player = data.entities?.find((e) => e.id === socket.id);
|
|
164
|
+
if (player && seq % 20 === 0) {
|
|
165
|
+
console.log(` Position: (${player.x.toFixed(0)}, ${player.y.toFixed(0)})`);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
socket.on('connect_error', (err) => {
|
|
170
|
+
console.error(' Connection failed:', err.message);
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 5. Run the Client
|
|
175
|
+
|
|
176
|
+
In a new terminal:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx tsx client.ts
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
You should see:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
Connected! Player ID: abc123
|
|
186
|
+
Entities: 1
|
|
187
|
+
Moving right...
|
|
188
|
+
Position: (50, 0)
|
|
189
|
+
Position: (100, 0)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 6. Test with Multiple Clients
|
|
193
|
+
|
|
194
|
+
Open more terminals and run more clients:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx tsx client.ts # Terminal 3
|
|
198
|
+
npx tsx client.ts # Terminal 4
|
|
199
|
+
npx tsx client.ts # Terminal 5
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Watch the server logs to see all players connecting!
|
|
203
|
+
|
|
204
|
+
## What Just Happened?
|
|
205
|
+
|
|
206
|
+
You created a **multiplayer game server** with game-core
|
|
207
|
+
The server runs a **game loop at 20 TPS** (20 ticks per second)
|
|
208
|
+
Players can **join and move around** in real-time
|
|
209
|
+
The server **broadcasts state updates** to all clients
|
|
210
|
+
All inputs are **processed server-side** (authoritative)
|
|
211
|
+
|
|
212
|
+
## Next Steps
|
|
213
|
+
|
|
214
|
+
### 1. Manual Testing
|
|
215
|
+
|
|
216
|
+
Use the built-in example:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Build the package
|
|
220
|
+
pnpm build
|
|
221
|
+
|
|
222
|
+
# Run the example server
|
|
223
|
+
node examples/simple-game/server.js
|
|
224
|
+
|
|
225
|
+
# In another terminal, run a client
|
|
226
|
+
node examples/simple-game/client.js
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 2. Build a Real Game
|
|
230
|
+
|
|
231
|
+
Check out the **Developer Guide** (`DEVELOPER_GUIDE.md`) for:
|
|
232
|
+
|
|
233
|
+
- Complete tag game tutorial
|
|
234
|
+
- Custom entity types
|
|
235
|
+
- Collision detection
|
|
236
|
+
- Advanced patterns
|
|
237
|
+
- Performance optimization
|
|
238
|
+
|
|
239
|
+
### 3. Add a Client UI
|
|
240
|
+
|
|
241
|
+
Create a web-based client with:
|
|
242
|
+
|
|
243
|
+
- **Canvas** for 2D rendering
|
|
244
|
+
- **Phaser** for game development
|
|
245
|
+
- **Three.js** for 3D graphics
|
|
246
|
+
|
|
247
|
+
### 4. Add Features
|
|
248
|
+
|
|
249
|
+
- **Combat system** - Health, damage, abilities
|
|
250
|
+
- **Inventory** - Items, equipment, crafting
|
|
251
|
+
- **Persistence** - Save/load game state
|
|
252
|
+
- **Matchmaking** - Create/join rooms
|
|
253
|
+
- **Chat** - In-game messaging
|
|
254
|
+
|
|
255
|
+
## Common Issues
|
|
256
|
+
|
|
257
|
+
### "Cannot find module '@gamerstake/game-core'"
|
|
258
|
+
|
|
259
|
+
If developing locally in a monorepo, build the package first:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pnpm build
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### "Port 3000 already in use"
|
|
266
|
+
|
|
267
|
+
Change the port:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const io = new Server(4000, { cors: { origin: '*' } });
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Client can't connect
|
|
274
|
+
|
|
275
|
+
Check:
|
|
276
|
+
|
|
277
|
+
1. Server is running
|
|
278
|
+
2. Port matches (default 3000)
|
|
279
|
+
3. No firewall blocking
|
|
280
|
+
|
|
281
|
+
## Architecture Quick Reference
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
Client Server
|
|
285
|
+
|
|
286
|
+
C_MOVE > [InputQueue]
|
|
287
|
+
|
|
288
|
+
[Game Tick]
|
|
289
|
+
- onCommand()
|
|
290
|
+
- onTick()
|
|
291
|
+
|
|
292
|
+
< S_UPDATE [Network]
|
|
293
|
+
|
|
294
|
+
[Render]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## API Cheat Sheet
|
|
298
|
+
|
|
299
|
+
### Server Side
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Create room
|
|
303
|
+
const room = gameServer.createRoom(id, rules, config);
|
|
304
|
+
|
|
305
|
+
// Add/remove players
|
|
306
|
+
room.addPlayer(entity);
|
|
307
|
+
room.removePlayer(playerId);
|
|
308
|
+
|
|
309
|
+
// Spawn/destroy entities
|
|
310
|
+
room.spawnEntity(entity);
|
|
311
|
+
room.destroyEntity(entityId);
|
|
312
|
+
|
|
313
|
+
// Networking
|
|
314
|
+
room.broadcast(event);
|
|
315
|
+
room.sendTo(playerId, event);
|
|
316
|
+
|
|
317
|
+
// Get state
|
|
318
|
+
const snapshot = room.getSnapshot();
|
|
319
|
+
const entity = room.getRegistry().get(id);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Client Side
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// Connect
|
|
326
|
+
const socket = io('http://localhost:3000');
|
|
327
|
+
|
|
328
|
+
// Receive events
|
|
329
|
+
socket.on('S_INIT', (data) => {
|
|
330
|
+
/* initial state */
|
|
331
|
+
});
|
|
332
|
+
socket.on('S_UPDATE', (data) => {
|
|
333
|
+
/* delta update */
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Send inputs
|
|
337
|
+
socket.emit('C_MOVE', { seq: 1, dir: { x: 1, y: 0 } });
|
|
338
|
+
socket.emit('C_STOP', { seq: 2 });
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Testing
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
# Run unit tests
|
|
345
|
+
pnpm test
|
|
346
|
+
|
|
347
|
+
# Run with coverage
|
|
348
|
+
pnpm test:coverage
|
|
349
|
+
|
|
350
|
+
# Type check
|
|
351
|
+
pnpm typecheck
|
|
352
|
+
|
|
353
|
+
# Build
|
|
354
|
+
pnpm build
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Resources
|
|
358
|
+
|
|
359
|
+
- **Developer Guide** - `DEVELOPER_GUIDE.md` - Complete guide with examples
|
|
360
|
+
- **Examples** - `examples/` - Working game examples
|
|
361
|
+
- **Tests** - `tests/` - Usage patterns
|
|
362
|
+
- **API Docs** - `README.md` - Full API reference
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
**You're ready to build multiplayer games! **
|
|
367
|
+
|
|
368
|
+
For more advanced usage, see the **Developer Guide**.
|