p2p-lockstep-kit-session 0.1.8 → 0.1.10
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 +483 -119
- package/dist/session/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/serialization-smoke.mjs +371 -106
|
@@ -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,23 @@ 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:
|
|
178
|
-
await waitForBus();
|
|
430
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
179
431
|
await waitForBus();
|
|
180
|
-
|
|
181
|
-
assert.equal(session.state.getPendingAction(), null);
|
|
182
|
-
assert.equal(session.state.getState("local"), "syncing");
|
|
183
|
-
assert.equal(session.state.getState("remote"), "syncing");
|
|
184
|
-
assert.equal(client.sent.at(-1).type, "SYNC_REQUEST");
|
|
185
|
-
|
|
186
|
-
client.inbound({
|
|
187
|
-
type: "SYNC_STATE",
|
|
188
|
-
payload: {
|
|
189
|
-
history: [{ turn: 1, player: "local", move: { step: "peer-move" } }],
|
|
190
|
-
lastStart: "local",
|
|
191
|
-
turn: "local",
|
|
192
|
-
resumeTurn: "local",
|
|
193
|
-
},
|
|
194
|
-
from: "remote",
|
|
195
|
-
});
|
|
196
432
|
await waitForBus();
|
|
197
433
|
|
|
198
434
|
assert.equal(session.state.getPendingAction(), null);
|
|
199
|
-
assert.equal(
|
|
200
|
-
assert.
|
|
201
|
-
assert.
|
|
202
|
-
assert.equal(session.state.getHistory()[0].player, "remote");
|
|
203
|
-
assert.equal(session.state.getLastStart(), "remote");
|
|
435
|
+
assert.equal(client.sent.at(-1).type, 'SYNC_STATE');
|
|
436
|
+
assert.match(session.state.getState('local'), /^(turn|remote_turn)$/);
|
|
437
|
+
assert.match(session.state.getState('remote'), /^(turn|remote_turn)$/);
|
|
204
438
|
}
|
|
205
439
|
|
|
206
440
|
{
|
|
@@ -213,28 +447,28 @@ const startGame = async () => {
|
|
|
213
447
|
onGameEvent() {},
|
|
214
448
|
});
|
|
215
449
|
|
|
216
|
-
client.inbound({ type:
|
|
450
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
217
451
|
await waitForBus();
|
|
218
452
|
|
|
219
|
-
assert.equal(session.state.getPendingAction(),
|
|
220
|
-
assert.equal(session.state.getState(
|
|
221
|
-
assert.equal(session.state.getState(
|
|
453
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
454
|
+
assert.equal(session.state.getState('local'), 'approving');
|
|
455
|
+
assert.equal(session.state.getState('remote'), 'waiting_approval');
|
|
222
456
|
|
|
223
457
|
session.actions.approve();
|
|
224
458
|
await waitForBus();
|
|
225
459
|
|
|
226
460
|
const sent = client.sent.at(-1);
|
|
227
|
-
assert.equal(sent.type,
|
|
228
|
-
assert.equal(sent.payload.action,
|
|
461
|
+
assert.equal(sent.type, 'APPROVE');
|
|
462
|
+
assert.equal(sent.payload.action, 'restart');
|
|
229
463
|
assert.equal(session.state.getPendingAction(), null);
|
|
230
|
-
assert.equal(session.state.getState(
|
|
231
|
-
assert.equal(session.state.getState(
|
|
464
|
+
assert.equal(session.state.getState('local'), 'idle');
|
|
465
|
+
assert.equal(session.state.getState('remote'), 'idle');
|
|
232
466
|
assert.equal(
|
|
233
467
|
snapshots.some(
|
|
234
468
|
(snapshot) =>
|
|
235
|
-
snapshot.localState ===
|
|
236
|
-
snapshot.remoteState ===
|
|
237
|
-
snapshot.pendingAction ===
|
|
469
|
+
snapshot.localState === 'idle' &&
|
|
470
|
+
snapshot.remoteState === 'idle' &&
|
|
471
|
+
snapshot.pendingAction === 'restart',
|
|
238
472
|
),
|
|
239
473
|
false,
|
|
240
474
|
);
|
|
@@ -243,65 +477,96 @@ const startGame = async () => {
|
|
|
243
477
|
{
|
|
244
478
|
const { client, session } = await startGame();
|
|
245
479
|
const beforeRestart = {
|
|
246
|
-
local: session.state.getState(
|
|
247
|
-
remote: session.state.getState(
|
|
480
|
+
local: session.state.getState('local'),
|
|
481
|
+
remote: session.state.getState('remote'),
|
|
248
482
|
};
|
|
249
483
|
|
|
250
|
-
client.inbound({ type:
|
|
484
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
251
485
|
await waitForBus();
|
|
252
486
|
|
|
253
487
|
session.actions.reject();
|
|
254
488
|
await waitForBus();
|
|
255
489
|
|
|
256
490
|
const sent = client.sent.at(-1);
|
|
257
|
-
assert.equal(sent.type,
|
|
258
|
-
assert.equal(sent.payload.action,
|
|
491
|
+
assert.equal(sent.type, 'REJECT');
|
|
492
|
+
assert.equal(sent.payload.action, 'restart');
|
|
259
493
|
assert.equal(session.state.getPendingAction(), null);
|
|
260
|
-
assert.equal(session.state.getState(
|
|
261
|
-
assert.equal(session.state.getState(
|
|
494
|
+
assert.equal(session.state.getState('local'), beforeRestart.local);
|
|
495
|
+
assert.equal(session.state.getState('remote'), beforeRestart.remote);
|
|
262
496
|
}
|
|
263
497
|
|
|
264
498
|
{
|
|
265
499
|
const { client, session } = await startGame();
|
|
266
|
-
client.inbound({ type:
|
|
500
|
+
client.inbound({ type: 'RESTART', from: 'remote' });
|
|
267
501
|
await waitForBus();
|
|
268
502
|
|
|
269
|
-
assert.equal(session.state.getPendingAction(),
|
|
503
|
+
assert.equal(session.state.getPendingAction(), 'restart');
|
|
270
504
|
|
|
271
|
-
session.bus.dispatch({ type:
|
|
505
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
272
506
|
await waitForBus();
|
|
273
507
|
|
|
274
508
|
assert.equal(session.state.getPendingAction(), null);
|
|
275
|
-
assert.equal(session.state.getState(
|
|
276
|
-
assert.equal(session.state.getState(
|
|
509
|
+
assert.equal(session.state.getState('local'), 'syncing');
|
|
510
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
277
511
|
|
|
278
|
-
session.bus.dispatch({ type:
|
|
512
|
+
session.bus.dispatch({ type: 'ONLINE', from: 'local' });
|
|
279
513
|
await waitForBus();
|
|
280
514
|
await waitForBus();
|
|
281
515
|
|
|
282
516
|
assert.equal(session.state.getPendingAction(), null);
|
|
283
|
-
assert.equal(
|
|
284
|
-
assert.
|
|
285
|
-
assert.
|
|
517
|
+
assert.equal(client.sent.at(-1).type, 'SYNC_STATE');
|
|
518
|
+
assert.match(session.state.getState('local'), /^(turn|remote_turn)$/);
|
|
519
|
+
assert.match(session.state.getState('remote'), /^(turn|remote_turn)$/);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
{
|
|
523
|
+
const { client, session } = createConnectedSession();
|
|
524
|
+
await waitForBus();
|
|
286
525
|
|
|
287
526
|
client.inbound({
|
|
288
|
-
type:
|
|
527
|
+
type: 'SYNC_STATE',
|
|
289
528
|
payload: {
|
|
290
|
-
history: [{ turn: 1, player:
|
|
291
|
-
lastStart:
|
|
292
|
-
turn:
|
|
293
|
-
resumeTurn:
|
|
529
|
+
history: [{ turn: 1, player: 'local', move: { step: 'peer-move' } }],
|
|
530
|
+
lastStart: 'local',
|
|
531
|
+
turn: 'remote',
|
|
532
|
+
resumeTurn: 'remote',
|
|
294
533
|
},
|
|
295
|
-
from:
|
|
534
|
+
from: 'remote',
|
|
296
535
|
});
|
|
297
536
|
await waitForBus();
|
|
298
537
|
|
|
299
|
-
assert.equal(session.state.
|
|
300
|
-
assert.equal(session.state.getState(
|
|
301
|
-
assert.equal(session.state.
|
|
538
|
+
assert.equal(session.state.getState('local'), 'turn');
|
|
539
|
+
assert.equal(session.state.getState('remote'), 'remote_turn');
|
|
540
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
541
|
+
assert.equal(session.state.getHistory()[0].player, 'remote');
|
|
542
|
+
assert.equal(session.state.getLastStart(), 'remote');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
{
|
|
546
|
+
const { client, session } = await startGameWithFirstPlayer('local');
|
|
547
|
+
session.actions.move({ step: 'local-before-disconnect' });
|
|
548
|
+
await waitForBus();
|
|
549
|
+
|
|
550
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
551
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
552
|
+
assert.equal(session.state.getHistory().length, 1);
|
|
553
|
+
|
|
554
|
+
session.bus.dispatch({ type: 'OFFLINE', from: 'local' });
|
|
555
|
+
await waitForBus();
|
|
556
|
+
|
|
557
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
558
|
+
assert.equal(session.state.getState('remote'), 'offline');
|
|
559
|
+
|
|
560
|
+
client.inbound({ type: 'SYNC_REQUEST', from: 'remote' });
|
|
561
|
+
await waitForBus();
|
|
562
|
+
|
|
563
|
+
const sent = client.sent.at(-1);
|
|
564
|
+
assert.equal(sent.type, 'SYNC_STATE');
|
|
565
|
+
assert.equal(sent.payload.resumeTurn, 'remote');
|
|
566
|
+
assert.equal(session.state.getState('local'), 'remote_turn');
|
|
567
|
+
assert.equal(session.state.getState('remote'), 'turn');
|
|
302
568
|
assert.equal(session.state.getHistory().length, 1);
|
|
303
|
-
assert.equal(session.state.getHistory()[0].player,
|
|
304
|
-
assert.equal(session.state.getLastStart(), "local");
|
|
569
|
+
assert.equal(session.state.getHistory()[0].player, 'local');
|
|
305
570
|
}
|
|
306
571
|
|
|
307
|
-
console.log(
|
|
572
|
+
console.log('serialization smoke passed');
|