p2p-lockstep-kit-session 0.1.7 → 0.1.9
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/dist/session/index.js +492 -119
- package/dist/session/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/serialization-smoke.mjs +425 -59
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import assert from
|
|
2
|
-
import { createSession } from
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createSession } from '../dist/session/index.js';
|
|
3
3
|
|
|
4
4
|
const waitForBus = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ class BoundaryClient {
|
|
|
14
14
|
|
|
15
15
|
onStateChange(handler) {
|
|
16
16
|
this.stateHandler = handler;
|
|
17
|
-
handler(
|
|
17
|
+
handler('passive');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
onRemoteStream() {}
|
|
@@ -24,7 +24,7 @@ class BoundaryClient {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
connect() {
|
|
27
|
-
this.stateHandler?.(
|
|
27
|
+
this.stateHandler?.('connected');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
inbound(data) {
|
|
@@ -34,9 +34,9 @@ class BoundaryClient {
|
|
|
34
34
|
|
|
35
35
|
const createConnectedSession = () => {
|
|
36
36
|
const client = new BoundaryClient();
|
|
37
|
-
const session = createSession(client,
|
|
37
|
+
const session = createSession(client, 'demo-room');
|
|
38
38
|
|
|
39
|
-
session.net.setPeerIds({ local:
|
|
39
|
+
session.net.setPeerIds({ local: 'local', remote: 'remote' });
|
|
40
40
|
client.connect();
|
|
41
41
|
|
|
42
42
|
return { client, session };
|
|
@@ -44,23 +44,51 @@ const createConnectedSession = () => {
|
|
|
44
44
|
|
|
45
45
|
const startGame = async () => {
|
|
46
46
|
const runtime = createConnectedSession();
|
|
47
|
-
runtime.client.inbound({ type:
|
|
47
|
+
runtime.client.inbound({ type: 'READY', sid: 'demo-room', from: 'remote' });
|
|
48
48
|
await waitForBus();
|
|
49
49
|
runtime.session.actions.start();
|
|
50
50
|
await waitForBus();
|
|
51
51
|
|
|
52
|
+
assert.match(runtime.session.state.getState('local'), /^(turn|remote_turn)$/);
|
|
52
53
|
assert.match(
|
|
53
|
-
runtime.session.state.getState(
|
|
54
|
+
runtime.session.state.getState('remote'),
|
|
54
55
|
/^(turn|remote_turn)$/,
|
|
55
56
|
);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
|
|
58
|
+
return runtime;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const startGameWithFirstPlayer = async (firstPlayer) => {
|
|
62
|
+
const runtime = createConnectedSession();
|
|
63
|
+
runtime.client.inbound({ type: 'READY', sid: 'demo-room', from: 'remote' });
|
|
64
|
+
await waitForBus();
|
|
65
|
+
runtime.session.state.setLastStart(
|
|
66
|
+
firstPlayer === 'local' ? 'remote' : 'local',
|
|
67
|
+
);
|
|
68
|
+
runtime.session.actions.start();
|
|
69
|
+
await waitForBus();
|
|
70
|
+
|
|
71
|
+
assert.equal(
|
|
72
|
+
runtime.session.state.getState('local'),
|
|
73
|
+
firstPlayer === 'local' ? 'turn' : 'remote_turn',
|
|
74
|
+
);
|
|
75
|
+
assert.equal(
|
|
76
|
+
runtime.session.state.getState('remote'),
|
|
77
|
+
firstPlayer === 'local' ? 'remote_turn' : 'turn',
|
|
59
78
|
);
|
|
60
79
|
|
|
61
80
|
return runtime;
|
|
62
81
|
};
|
|
63
82
|
|
|
83
|
+
const oneMoveWinPlugin = {
|
|
84
|
+
validateMove() {
|
|
85
|
+
return { valid: true };
|
|
86
|
+
},
|
|
87
|
+
checkWin(_gameState, history) {
|
|
88
|
+
return history.at(-1)?.player ?? null;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
64
92
|
{
|
|
65
93
|
const { client, session } = createConnectedSession();
|
|
66
94
|
await waitForBus();
|
|
@@ -69,22 +97,22 @@ const startGame = async () => {
|
|
|
69
97
|
await waitForBus();
|
|
70
98
|
|
|
71
99
|
const sent = client.sent.at(-1);
|
|
72
|
-
assert.equal(typeof sent,
|
|
73
|
-
assert.equal(sent.type,
|
|
74
|
-
assert.equal(sent.sid,
|
|
75
|
-
assert.equal(session.state.getState(
|
|
76
|
-
assert.equal(session.state.getState(
|
|
100
|
+
assert.equal(typeof sent, 'object');
|
|
101
|
+
assert.equal(sent.type, 'READY');
|
|
102
|
+
assert.equal(sent.sid, 'demo-room');
|
|
103
|
+
assert.equal(session.state.getState('local'), 'ready');
|
|
104
|
+
assert.equal(session.state.getState('remote'), 'could_start');
|
|
77
105
|
}
|
|
78
106
|
|
|
79
107
|
{
|
|
80
108
|
const { client, session } = createConnectedSession();
|
|
81
109
|
await waitForBus();
|
|
82
110
|
|
|
83
|
-
client.inbound({ type:
|
|
111
|
+
client.inbound({ type: 'READY', sid: 'demo-room', from: 'remote' });
|
|
84
112
|
await waitForBus();
|
|
85
113
|
|
|
86
|
-
assert.equal(session.state.getState(
|
|
87
|
-
assert.equal(session.state.getState(
|
|
114
|
+
assert.equal(session.state.getState('local'), 'could_start');
|
|
115
|
+
assert.equal(session.state.getState('remote'), 'ready');
|
|
88
116
|
}
|
|
89
117
|
|
|
90
118
|
{
|
|
@@ -92,12 +120,12 @@ const startGame = async () => {
|
|
|
92
120
|
await waitForBus();
|
|
93
121
|
|
|
94
122
|
client.inbound(
|
|
95
|
-
JSON.stringify({ type:
|
|
123
|
+
JSON.stringify({ type: 'READY', sid: 'demo-room', from: 'remote' }),
|
|
96
124
|
);
|
|
97
125
|
await waitForBus();
|
|
98
126
|
|
|
99
|
-
assert.equal(session.state.getState(
|
|
100
|
-
assert.equal(session.state.getState(
|
|
127
|
+
assert.equal(session.state.getState('local'), 'could_start');
|
|
128
|
+
assert.equal(session.state.getState('remote'), 'ready');
|
|
101
129
|
}
|
|
102
130
|
|
|
103
131
|
{
|
|
@@ -113,51 +141,319 @@ const startGame = async () => {
|
|
|
113
141
|
session.actions.restart();
|
|
114
142
|
await waitForBus();
|
|
115
143
|
|
|
116
|
-
assert.equal(session.state.getPendingAction(),
|
|
117
|
-
assert.equal(session.state.getState(
|
|
118
|
-
assert.equal(session.state.getState(
|
|
144
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
145
|
+
assert.equal(session.state.getState('local'), 'waiting_approval');
|
|
146
|
+
assert.equal(session.state.getState('remote'), 'approving');
|
|
119
147
|
|
|
120
148
|
client.inbound({
|
|
121
|
-
type:
|
|
122
|
-
payload: { action:
|
|
123
|
-
from:
|
|
149
|
+
type: 'APPROVE',
|
|
150
|
+
payload: { action: 'restart' },
|
|
151
|
+
from: 'remote',
|
|
124
152
|
});
|
|
125
153
|
await waitForBus();
|
|
126
154
|
|
|
127
155
|
assert.equal(session.state.getPendingAction(), null);
|
|
128
|
-
assert.equal(session.state.getState(
|
|
129
|
-
assert.equal(session.state.getState(
|
|
156
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
157
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
130
158
|
assert.equal(
|
|
131
159
|
snapshots.some(
|
|
132
160
|
(snapshot) =>
|
|
133
|
-
snapshot.localState ===
|
|
134
|
-
snapshot.remoteState ===
|
|
135
|
-
snapshot.pendingAction ===
|
|
161
|
+
snapshot.localState === 'idle' &&
|
|
162
|
+
snapshot.remoteState === 'idle' &&
|
|
163
|
+
snapshot.pendingAction === 'restart',
|
|
136
164
|
),
|
|
137
165
|
false,
|
|
138
166
|
);
|
|
139
167
|
}
|
|
140
168
|
|
|
169
|
+
{
|
|
170
|
+
const { client, session } = await startGame();
|
|
171
|
+
const snapshots = [];
|
|
172
|
+
session.observer.subscribe({
|
|
173
|
+
onStateChange(snapshot) {
|
|
174
|
+
snapshots.push(snapshot);
|
|
175
|
+
},
|
|
176
|
+
onGameEvent() {},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (session.state.getState('local') !== 'turn') {
|
|
180
|
+
client.inbound({
|
|
181
|
+
type: 'MOVE',
|
|
182
|
+
payload: { step: 'remote-first' },
|
|
183
|
+
from: 'remote',
|
|
184
|
+
});
|
|
185
|
+
await waitForBus();
|
|
186
|
+
snapshots.splice(0, snapshots.length);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const beforeHistory = session.state.getHistory().length;
|
|
190
|
+
session.actions.move({ step: 'local-move' });
|
|
191
|
+
await waitForBus();
|
|
192
|
+
|
|
193
|
+
assert.equal(session.state.getHistory().length, beforeHistory + 1);
|
|
194
|
+
assert.equal(
|
|
195
|
+
snapshots.some((snapshot) => snapshot.history.length === beforeHistory + 1),
|
|
196
|
+
true,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
{
|
|
201
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
202
|
+
session.state.setGamePlugin(oneMoveWinPlugin);
|
|
203
|
+
|
|
204
|
+
session.actions.move({ step: 'winning-local-move' });
|
|
205
|
+
await waitForBus();
|
|
206
|
+
|
|
207
|
+
assert.equal(client.sent.at(-1).type, 'MOVE');
|
|
208
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
209
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
210
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
211
|
+
assert.equal(session.state.getHistory()[0].move.step, 'winning-local-move');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
{
|
|
215
|
+
const { client, session } = await startGameWithFirstPlayer('remote');
|
|
216
|
+
session.state.setGamePlugin(oneMoveWinPlugin);
|
|
217
|
+
|
|
218
|
+
client.inbound({
|
|
219
|
+
type: 'MOVE',
|
|
220
|
+
payload: { step: 'winning-remote-move' },
|
|
221
|
+
from: 'remote',
|
|
222
|
+
});
|
|
223
|
+
await waitForBus();
|
|
224
|
+
|
|
225
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
226
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
227
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
228
|
+
assert.equal(session.state.getHistory()[0].move.step, 'winning-remote-move');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
{
|
|
232
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
233
|
+
session.state.setGamePlugin(oneMoveWinPlugin);
|
|
234
|
+
|
|
235
|
+
session.actions.move({ step: 'winning-local-move' });
|
|
236
|
+
await waitForBus();
|
|
237
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
238
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
239
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
240
|
+
|
|
241
|
+
client.inbound({ type: 'READY', sid: 'demo-room', from: 'remote' });
|
|
242
|
+
await waitForBus();
|
|
243
|
+
session.actions.start();
|
|
244
|
+
await waitForBus();
|
|
245
|
+
|
|
246
|
+
assert.equal(session.state.getHistory().length, 0);
|
|
247
|
+
assert.match(session.state.getState('local'), /^(turn|remote_turn)$/);
|
|
248
|
+
assert.match(session.state.getState('remote'), /^(turn|remote_turn)$/);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
{
|
|
252
|
+
const { client, session } = await startGameWithFirstPlayer('remote');
|
|
253
|
+
session.state.setGamePlugin(oneMoveWinPlugin);
|
|
254
|
+
|
|
255
|
+
client.inbound({
|
|
256
|
+
type: 'MOVE',
|
|
257
|
+
payload: { step: 'winning-remote-move' },
|
|
258
|
+
from: 'remote',
|
|
259
|
+
});
|
|
260
|
+
await waitForBus();
|
|
261
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
262
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
263
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
264
|
+
|
|
265
|
+
session.actions.ready();
|
|
266
|
+
await waitForBus();
|
|
267
|
+
client.inbound({
|
|
268
|
+
type: 'START',
|
|
269
|
+
payload: { starter: 'sender' },
|
|
270
|
+
from: 'remote',
|
|
271
|
+
});
|
|
272
|
+
await waitForBus();
|
|
273
|
+
|
|
274
|
+
assert.equal(session.state.getHistory().length, 0);
|
|
275
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
276
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
{
|
|
280
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
281
|
+
|
|
282
|
+
session.actions.move({ step: 'local-just-moved' });
|
|
283
|
+
await waitForBus();
|
|
284
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
285
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
286
|
+
|
|
287
|
+
session.actions.undo();
|
|
288
|
+
await waitForBus();
|
|
289
|
+
|
|
290
|
+
assert.equal(client.sent.at(-1).type, 'UNDO');
|
|
291
|
+
assert.equal(client.sent.at(-1).payload.count, 1);
|
|
292
|
+
assert.equal(session.state.getPendingUndoCount(), 1);
|
|
293
|
+
|
|
294
|
+
client.inbound({
|
|
295
|
+
type: 'APPROVE',
|
|
296
|
+
payload: { action: 'undo' },
|
|
297
|
+
from: 'remote',
|
|
298
|
+
});
|
|
299
|
+
await waitForBus();
|
|
300
|
+
|
|
301
|
+
assert.equal(session.state.getHistory().length, 0);
|
|
302
|
+
assert.equal(session.state.getState('local'), 'turn');
|
|
303
|
+
assert.equal(session.state.getState('remote'), 'remote_turn');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
{
|
|
307
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
308
|
+
|
|
309
|
+
session.actions.move({ step: 'local-first' });
|
|
310
|
+
await waitForBus();
|
|
311
|
+
client.inbound({
|
|
312
|
+
type: 'MOVE',
|
|
313
|
+
payload: { step: 'remote-reply' },
|
|
314
|
+
from: 'remote',
|
|
315
|
+
});
|
|
316
|
+
await waitForBus();
|
|
317
|
+
assert.equal(session.state.getState('local'), 'turn');
|
|
318
|
+
assert.equal(session.state.getHistory().length, 2);
|
|
319
|
+
|
|
320
|
+
session.actions.undo();
|
|
321
|
+
await waitForBus();
|
|
322
|
+
|
|
323
|
+
assert.equal(client.sent.at(-1).type, 'UNDO');
|
|
324
|
+
assert.equal(client.sent.at(-1).payload.count, 2);
|
|
325
|
+
assert.equal(session.state.getPendingUndoCount(), 2);
|
|
326
|
+
|
|
327
|
+
client.inbound({
|
|
328
|
+
type: 'APPROVE',
|
|
329
|
+
payload: { action: 'undo' },
|
|
330
|
+
from: 'remote',
|
|
331
|
+
});
|
|
332
|
+
await waitForBus();
|
|
333
|
+
|
|
334
|
+
assert.equal(session.state.getHistory().length, 0);
|
|
335
|
+
assert.equal(session.state.getState('local'), 'turn');
|
|
336
|
+
assert.equal(session.state.getState('remote'), 'remote_turn');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
{
|
|
340
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
341
|
+
|
|
342
|
+
session.actions.move({ step: 'local-just-moved' });
|
|
343
|
+
await waitForBus();
|
|
344
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
345
|
+
|
|
346
|
+
session.actions.undo();
|
|
347
|
+
await waitForBus();
|
|
348
|
+
client.inbound({
|
|
349
|
+
type: 'REJECT',
|
|
350
|
+
payload: { action: 'undo' },
|
|
351
|
+
from: 'remote',
|
|
352
|
+
});
|
|
353
|
+
await waitForBus();
|
|
354
|
+
|
|
355
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
356
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
357
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
358
|
+
assert.equal(
|
|
359
|
+
client.sent.some((message) => message.type === 'UNDO'),
|
|
360
|
+
true,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
{
|
|
365
|
+
const { client } = createConnectedSession();
|
|
366
|
+
await waitForBus();
|
|
367
|
+
|
|
368
|
+
client.inbound({ type: 'UNDO', payload: { count: 3 }, from: 'remote' });
|
|
369
|
+
await waitForBus();
|
|
370
|
+
await waitForBus();
|
|
371
|
+
|
|
372
|
+
const sent = client.sent.at(-1);
|
|
373
|
+
assert.equal(sent.type, 'REJECT');
|
|
374
|
+
assert.equal(sent.payload.action, 'undo');
|
|
375
|
+
assert.equal(sent.payload.reason, 'invalid_state');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
{
|
|
379
|
+
const { client, session } = createConnectedSession();
|
|
380
|
+
await waitForBus();
|
|
381
|
+
|
|
382
|
+
session.actions.ready();
|
|
383
|
+
await waitForBus();
|
|
384
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
385
|
+
await waitForBus();
|
|
386
|
+
await waitForBus();
|
|
387
|
+
|
|
388
|
+
const sent = client.sent.at(-1);
|
|
389
|
+
assert.equal(sent.type, 'REJECT');
|
|
390
|
+
assert.equal(sent.payload.action, 'restart');
|
|
391
|
+
assert.equal(sent.payload.reason, 'invalid_state');
|
|
392
|
+
}
|
|
393
|
+
|
|
141
394
|
{
|
|
142
395
|
const { client, session } = await startGame();
|
|
143
396
|
const beforeRestart = {
|
|
144
|
-
local: session.state.getState(
|
|
145
|
-
remote: session.state.getState(
|
|
397
|
+
local: session.state.getState('local'),
|
|
398
|
+
remote: session.state.getState('remote'),
|
|
146
399
|
};
|
|
147
400
|
|
|
148
401
|
session.actions.restart();
|
|
149
402
|
await waitForBus();
|
|
150
403
|
|
|
151
404
|
client.inbound({
|
|
152
|
-
type:
|
|
153
|
-
payload: { action:
|
|
154
|
-
from:
|
|
405
|
+
type: 'REJECT',
|
|
406
|
+
payload: { action: 'restart' },
|
|
407
|
+
from: 'remote',
|
|
408
|
+
});
|
|
409
|
+
await waitForBus();
|
|
410
|
+
|
|
411
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
412
|
+
assert.equal(session.state.getState('local'), beforeRestart.local);
|
|
413
|
+
assert.equal(session.state.getState('remote'), beforeRestart.remote);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
{
|
|
417
|
+
const { client, session } = await startGame();
|
|
418
|
+
session.actions.restart();
|
|
419
|
+
await waitForBus();
|
|
420
|
+
|
|
421
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
422
|
+
|
|
423
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
424
|
+
await waitForBus();
|
|
425
|
+
|
|
426
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
427
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
428
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
429
|
+
|
|
430
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
431
|
+
await waitForBus();
|
|
432
|
+
await waitForBus();
|
|
433
|
+
|
|
434
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
435
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
436
|
+
assert.equal(session.state.getState('remote'), 'syncing');
|
|
437
|
+
assert.equal(client.sent.at(-1).type, 'SYNC_REQUEST');
|
|
438
|
+
|
|
439
|
+
client.inbound({
|
|
440
|
+
type: 'SYNC_STATE',
|
|
441
|
+
payload: {
|
|
442
|
+
history: [{ turn: 1, player: 'local', move: { step: 'peer-move' } }],
|
|
443
|
+
lastStart: 'local',
|
|
444
|
+
turn: 'local',
|
|
445
|
+
resumeTurn: 'local',
|
|
446
|
+
},
|
|
447
|
+
from: 'remote',
|
|
155
448
|
});
|
|
156
449
|
await waitForBus();
|
|
157
450
|
|
|
158
451
|
assert.equal(session.state.getPendingAction(), null);
|
|
159
|
-
assert.equal(session.state.getState(
|
|
160
|
-
assert.equal(session.state.getState(
|
|
452
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
453
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
454
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
455
|
+
assert.equal(session.state.getHistory()[0].player, 'remote');
|
|
456
|
+
assert.equal(session.state.getLastStart(), 'remote');
|
|
161
457
|
}
|
|
162
458
|
|
|
163
459
|
{
|
|
@@ -170,28 +466,28 @@ const startGame = async () => {
|
|
|
170
466
|
onGameEvent() {},
|
|
171
467
|
});
|
|
172
468
|
|
|
173
|
-
client.inbound({ type:
|
|
469
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
174
470
|
await waitForBus();
|
|
175
471
|
|
|
176
|
-
assert.equal(session.state.getPendingAction(),
|
|
177
|
-
assert.equal(session.state.getState(
|
|
178
|
-
assert.equal(session.state.getState(
|
|
472
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
473
|
+
assert.equal(session.state.getState('local'), 'approving');
|
|
474
|
+
assert.equal(session.state.getState('remote'), 'waiting_approval');
|
|
179
475
|
|
|
180
476
|
session.actions.approve();
|
|
181
477
|
await waitForBus();
|
|
182
478
|
|
|
183
479
|
const sent = client.sent.at(-1);
|
|
184
|
-
assert.equal(sent.type,
|
|
185
|
-
assert.equal(sent.payload.action,
|
|
480
|
+
assert.equal(sent.type, 'APPROVE');
|
|
481
|
+
assert.equal(sent.payload.action, 'restart');
|
|
186
482
|
assert.equal(session.state.getPendingAction(), null);
|
|
187
|
-
assert.equal(session.state.getState(
|
|
188
|
-
assert.equal(session.state.getState(
|
|
483
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
484
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
189
485
|
assert.equal(
|
|
190
486
|
snapshots.some(
|
|
191
487
|
(snapshot) =>
|
|
192
|
-
snapshot.localState ===
|
|
193
|
-
snapshot.remoteState ===
|
|
194
|
-
snapshot.pendingAction ===
|
|
488
|
+
snapshot.localState === 'idle' &&
|
|
489
|
+
snapshot.remoteState === 'idle' &&
|
|
490
|
+
snapshot.pendingAction === 'restart',
|
|
195
491
|
),
|
|
196
492
|
false,
|
|
197
493
|
);
|
|
@@ -200,22 +496,92 @@ const startGame = async () => {
|
|
|
200
496
|
{
|
|
201
497
|
const { client, session } = await startGame();
|
|
202
498
|
const beforeRestart = {
|
|
203
|
-
local: session.state.getState(
|
|
204
|
-
remote: session.state.getState(
|
|
499
|
+
local: session.state.getState('local'),
|
|
500
|
+
remote: session.state.getState('remote'),
|
|
205
501
|
};
|
|
206
502
|
|
|
207
|
-
client.inbound({ type:
|
|
503
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
208
504
|
await waitForBus();
|
|
209
505
|
|
|
210
506
|
session.actions.reject();
|
|
211
507
|
await waitForBus();
|
|
212
508
|
|
|
213
509
|
const sent = client.sent.at(-1);
|
|
214
|
-
assert.equal(sent.type,
|
|
215
|
-
assert.equal(sent.payload.action,
|
|
510
|
+
assert.equal(sent.type, 'REJECT');
|
|
511
|
+
assert.equal(sent.payload.action, 'restart');
|
|
512
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
513
|
+
assert.equal(session.state.getState('local'), beforeRestart.local);
|
|
514
|
+
assert.equal(session.state.getState('remote'), beforeRestart.remote);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
{
|
|
518
|
+
const { client, session } = await startGame();
|
|
519
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
520
|
+
await waitForBus();
|
|
521
|
+
|
|
522
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
523
|
+
|
|
524
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
525
|
+
await waitForBus();
|
|
526
|
+
|
|
216
527
|
assert.equal(session.state.getPendingAction(), null);
|
|
217
|
-
assert.equal(session.state.getState(
|
|
218
|
-
assert.equal(session.state.getState(
|
|
528
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
529
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
530
|
+
|
|
531
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
532
|
+
await waitForBus();
|
|
533
|
+
await waitForBus();
|
|
534
|
+
|
|
535
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
536
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
537
|
+
assert.equal(session.state.getState('remote'), 'syncing');
|
|
538
|
+
assert.equal(client.sent.at(-1).type, 'SYNC_REQUEST');
|
|
539
|
+
|
|
540
|
+
client.inbound({
|
|
541
|
+
type: 'SYNC_STATE',
|
|
542
|
+
payload: {
|
|
543
|
+
history: [{ turn: 1, player: 'remote', move: { step: 'local-move' } }],
|
|
544
|
+
lastStart: 'remote',
|
|
545
|
+
turn: 'remote',
|
|
546
|
+
resumeTurn: 'remote',
|
|
547
|
+
},
|
|
548
|
+
from: 'remote',
|
|
549
|
+
});
|
|
550
|
+
await waitForBus();
|
|
551
|
+
|
|
552
|
+
assert.equal(session.state.getPendingAction(), null);
|
|
553
|
+
assert.equal(session.state.getState('local'), 'turn');
|
|
554
|
+
assert.equal(session.state.getState('remote'), 'remote_turn');
|
|
555
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
556
|
+
assert.equal(session.state.getHistory()[0].player, 'local');
|
|
557
|
+
assert.equal(session.state.getLastStart(), 'local');
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
{
|
|
561
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
562
|
+
session.actions.move({ step: 'local-before-disconnect' });
|
|
563
|
+
await waitForBus();
|
|
564
|
+
|
|
565
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
566
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
567
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
568
|
+
|
|
569
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
570
|
+
await waitForBus();
|
|
571
|
+
|
|
572
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
573
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
574
|
+
|
|
575
|
+
client.inbound({ type: 'SYNC_REQUEST', from: 'remote' });
|
|
576
|
+
await waitForBus();
|
|
577
|
+
|
|
578
|
+
const sent = client.sent.at(-1);
|
|
579
|
+
assert.equal(sent.type, 'SYNC_STATE');
|
|
580
|
+
assert.equal(sent.payload.resumeTurn, 'remote');
|
|
581
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
582
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
583
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
584
|
+
assert.equal(session.state.getHistory()[0].player, 'local');
|
|
219
585
|
}
|
|
220
586
|
|
|
221
|
-
console.log(
|
|
587
|
+
console.log('serialization smoke passed');
|