p2p-lockstep-kit-session 0.1.8 → 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 +473 -119
- package/dist/session/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/serialization-smoke.mjs +376 -96
|
@@ -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,276 @@ 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',
|
|
155
408
|
});
|
|
156
409
|
await waitForBus();
|
|
157
410
|
|
|
158
411
|
assert.equal(session.state.getPendingAction(), null);
|
|
159
|
-
assert.equal(session.state.getState(
|
|
160
|
-
assert.equal(session.state.getState(
|
|
412
|
+
assert.equal(session.state.getState('local'), beforeRestart.local);
|
|
413
|
+
assert.equal(session.state.getState('remote'), beforeRestart.remote);
|
|
161
414
|
}
|
|
162
415
|
|
|
163
416
|
{
|
|
@@ -165,42 +418,42 @@ const startGame = async () => {
|
|
|
165
418
|
session.actions.restart();
|
|
166
419
|
await waitForBus();
|
|
167
420
|
|
|
168
|
-
assert.equal(session.state.getPendingAction(),
|
|
421
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
169
422
|
|
|
170
|
-
session.bus.dispatch({ type:
|
|
423
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
171
424
|
await waitForBus();
|
|
172
425
|
|
|
173
426
|
assert.equal(session.state.getPendingAction(), null);
|
|
174
|
-
assert.equal(session.state.getState(
|
|
175
|
-
assert.equal(session.state.getState(
|
|
427
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
428
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
176
429
|
|
|
177
|
-
session.bus.dispatch({ type:
|
|
430
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
178
431
|
await waitForBus();
|
|
179
432
|
await waitForBus();
|
|
180
433
|
|
|
181
434
|
assert.equal(session.state.getPendingAction(), null);
|
|
182
|
-
assert.equal(session.state.getState(
|
|
183
|
-
assert.equal(session.state.getState(
|
|
184
|
-
assert.equal(client.sent.at(-1).type,
|
|
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');
|
|
185
438
|
|
|
186
439
|
client.inbound({
|
|
187
|
-
type:
|
|
440
|
+
type: 'SYNC_STATE',
|
|
188
441
|
payload: {
|
|
189
|
-
history: [{ turn: 1, player:
|
|
190
|
-
lastStart:
|
|
191
|
-
turn:
|
|
192
|
-
resumeTurn:
|
|
442
|
+
history: [{ turn: 1, player: 'local', move: { step: 'peer-move' } }],
|
|
443
|
+
lastStart: 'local',
|
|
444
|
+
turn: 'local',
|
|
445
|
+
resumeTurn: 'local',
|
|
193
446
|
},
|
|
194
|
-
from:
|
|
447
|
+
from: 'remote',
|
|
195
448
|
});
|
|
196
449
|
await waitForBus();
|
|
197
450
|
|
|
198
451
|
assert.equal(session.state.getPendingAction(), null);
|
|
199
|
-
assert.equal(session.state.getState(
|
|
200
|
-
assert.equal(session.state.getState(
|
|
452
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
453
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
201
454
|
assert.equal(session.state.getHistory().length, 1);
|
|
202
|
-
assert.equal(session.state.getHistory()[0].player,
|
|
203
|
-
assert.equal(session.state.getLastStart(),
|
|
455
|
+
assert.equal(session.state.getHistory()[0].player, 'remote');
|
|
456
|
+
assert.equal(session.state.getLastStart(), 'remote');
|
|
204
457
|
}
|
|
205
458
|
|
|
206
459
|
{
|
|
@@ -213,28 +466,28 @@ const startGame = async () => {
|
|
|
213
466
|
onGameEvent() {},
|
|
214
467
|
});
|
|
215
468
|
|
|
216
|
-
client.inbound({ type:
|
|
469
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
217
470
|
await waitForBus();
|
|
218
471
|
|
|
219
|
-
assert.equal(session.state.getPendingAction(),
|
|
220
|
-
assert.equal(session.state.getState(
|
|
221
|
-
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');
|
|
222
475
|
|
|
223
476
|
session.actions.approve();
|
|
224
477
|
await waitForBus();
|
|
225
478
|
|
|
226
479
|
const sent = client.sent.at(-1);
|
|
227
|
-
assert.equal(sent.type,
|
|
228
|
-
assert.equal(sent.payload.action,
|
|
480
|
+
assert.equal(sent.type, 'APPROVE');
|
|
481
|
+
assert.equal(sent.payload.action, 'restart');
|
|
229
482
|
assert.equal(session.state.getPendingAction(), null);
|
|
230
|
-
assert.equal(session.state.getState(
|
|
231
|
-
assert.equal(session.state.getState(
|
|
483
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
484
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
232
485
|
assert.equal(
|
|
233
486
|
snapshots.some(
|
|
234
487
|
(snapshot) =>
|
|
235
|
-
snapshot.localState ===
|
|
236
|
-
snapshot.remoteState ===
|
|
237
|
-
snapshot.pendingAction ===
|
|
488
|
+
snapshot.localState === 'idle' &&
|
|
489
|
+
snapshot.remoteState === 'idle' &&
|
|
490
|
+
snapshot.pendingAction === 'restart',
|
|
238
491
|
),
|
|
239
492
|
false,
|
|
240
493
|
);
|
|
@@ -243,65 +496,92 @@ const startGame = async () => {
|
|
|
243
496
|
{
|
|
244
497
|
const { client, session } = await startGame();
|
|
245
498
|
const beforeRestart = {
|
|
246
|
-
local: session.state.getState(
|
|
247
|
-
remote: session.state.getState(
|
|
499
|
+
local: session.state.getState('local'),
|
|
500
|
+
remote: session.state.getState('remote'),
|
|
248
501
|
};
|
|
249
502
|
|
|
250
|
-
client.inbound({ type:
|
|
503
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
251
504
|
await waitForBus();
|
|
252
505
|
|
|
253
506
|
session.actions.reject();
|
|
254
507
|
await waitForBus();
|
|
255
508
|
|
|
256
509
|
const sent = client.sent.at(-1);
|
|
257
|
-
assert.equal(sent.type,
|
|
258
|
-
assert.equal(sent.payload.action,
|
|
510
|
+
assert.equal(sent.type, 'REJECT');
|
|
511
|
+
assert.equal(sent.payload.action, 'restart');
|
|
259
512
|
assert.equal(session.state.getPendingAction(), null);
|
|
260
|
-
assert.equal(session.state.getState(
|
|
261
|
-
assert.equal(session.state.getState(
|
|
513
|
+
assert.equal(session.state.getState('local'), beforeRestart.local);
|
|
514
|
+
assert.equal(session.state.getState('remote'), beforeRestart.remote);
|
|
262
515
|
}
|
|
263
516
|
|
|
264
517
|
{
|
|
265
518
|
const { client, session } = await startGame();
|
|
266
|
-
client.inbound({ type:
|
|
519
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
267
520
|
await waitForBus();
|
|
268
521
|
|
|
269
|
-
assert.equal(session.state.getPendingAction(),
|
|
522
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
270
523
|
|
|
271
|
-
session.bus.dispatch({ type:
|
|
524
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
272
525
|
await waitForBus();
|
|
273
526
|
|
|
274
527
|
assert.equal(session.state.getPendingAction(), null);
|
|
275
|
-
assert.equal(session.state.getState(
|
|
276
|
-
assert.equal(session.state.getState(
|
|
528
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
529
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
277
530
|
|
|
278
|
-
session.bus.dispatch({ type:
|
|
531
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
279
532
|
await waitForBus();
|
|
280
533
|
await waitForBus();
|
|
281
534
|
|
|
282
535
|
assert.equal(session.state.getPendingAction(), null);
|
|
283
|
-
assert.equal(session.state.getState(
|
|
284
|
-
assert.equal(session.state.getState(
|
|
285
|
-
assert.equal(client.sent.at(-1).type,
|
|
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');
|
|
286
539
|
|
|
287
540
|
client.inbound({
|
|
288
|
-
type:
|
|
541
|
+
type: 'SYNC_STATE',
|
|
289
542
|
payload: {
|
|
290
|
-
history: [{ turn: 1, player:
|
|
291
|
-
lastStart:
|
|
292
|
-
turn:
|
|
293
|
-
resumeTurn:
|
|
543
|
+
history: [{ turn: 1, player: 'remote', move: { step: 'local-move' } }],
|
|
544
|
+
lastStart: 'remote',
|
|
545
|
+
turn: 'remote',
|
|
546
|
+
resumeTurn: 'remote',
|
|
294
547
|
},
|
|
295
|
-
from:
|
|
548
|
+
from: 'remote',
|
|
296
549
|
});
|
|
297
550
|
await waitForBus();
|
|
298
551
|
|
|
299
552
|
assert.equal(session.state.getPendingAction(), null);
|
|
300
|
-
assert.equal(session.state.getState(
|
|
301
|
-
assert.equal(session.state.getState(
|
|
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');
|
|
302
583
|
assert.equal(session.state.getHistory().length, 1);
|
|
303
|
-
assert.equal(session.state.getHistory()[0].player,
|
|
304
|
-
assert.equal(session.state.getLastStart(), "local");
|
|
584
|
+
assert.equal(session.state.getHistory()[0].player, 'local');
|
|
305
585
|
}
|
|
306
586
|
|
|
307
|
-
console.log(
|
|
587
|
+
console.log('serialization smoke passed');
|