anear-js-api 0.4.40 → 0.5.2
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/EACH_PARTICIPANT_LIFECYCLE.md +230 -0
- package/RENDER_USAGE_EXAMPLES.md +14 -14
- package/lib/models/AnearEvent.js +12 -0
- package/lib/models/AnearParticipant.js +6 -6
- package/lib/state_machines/AnearEventMachine.js +302 -102
- package/lib/utils/AppMachineTransition.js +70 -13
- package/lib/utils/DisplayEventProcessor.js +148 -37
- package/lib/utils/FontAssetsUploader.js +10 -0
- package/lib/utils/PugHelpers.js +13 -2
- package/lib/utils/RealtimeMessaging.js +1 -1
- package/lib/utils/RenderContextBuilder.js +23 -6
- package/package.json +1 -1
|
@@ -24,15 +24,26 @@ const logger = require('../utils/Logger')
|
|
|
24
24
|
//
|
|
25
25
|
|
|
26
26
|
const { assign, createMachine, interpret } = require('xstate')
|
|
27
|
+
const C = require('../utils/Constants')
|
|
28
|
+
|
|
29
|
+
const getPlayingParticipantIds = (context) => {
|
|
30
|
+
return Object.keys(context.participants).filter(id => !context.participants[id].isHost);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getAllParticipantIds = (context) => {
|
|
34
|
+
return Object.keys(context.participants);
|
|
35
|
+
};
|
|
27
36
|
|
|
28
|
-
const AnearApi = require('../api/AnearApi')
|
|
29
|
-
const AnearParticipantMachine = require('../state_machines/AnearParticipantMachine')
|
|
30
|
-
const AnearParticipant = require('../models/AnearParticipant')
|
|
31
37
|
const RealtimeMessaging = require('../utils/RealtimeMessaging')
|
|
32
38
|
const AppMachineTransition = require('../utils/AppMachineTransition')
|
|
33
39
|
const DisplayEventProcessor = require('../utils/DisplayEventProcessor')
|
|
34
40
|
const PugHelpers = require('../utils/PugHelpers')
|
|
35
|
-
|
|
41
|
+
|
|
42
|
+
const AnearApi = require('../api/AnearApi')
|
|
43
|
+
const AnearParticipantMachine = require('../state_machines/AnearParticipantMachine')
|
|
44
|
+
const AnearParticipant = require('../models/AnearParticipant')
|
|
45
|
+
|
|
46
|
+
const CurrentDateTimestamp = _ => new Date().getTime()
|
|
36
47
|
|
|
37
48
|
const AnearEventMachineContext = (
|
|
38
49
|
anearEvent,
|
|
@@ -54,7 +65,8 @@ const AnearEventMachineContext = (
|
|
|
54
65
|
participantsDisplayChannel: null, // display all participants
|
|
55
66
|
spectatorsDisplayChannel: null, // display all spectators
|
|
56
67
|
participants: {},
|
|
57
|
-
participantMachines: {}
|
|
68
|
+
participantMachines: {},
|
|
69
|
+
participantsActionTimeout: null
|
|
58
70
|
})
|
|
59
71
|
|
|
60
72
|
const DeferredStates = [
|
|
@@ -126,7 +138,7 @@ const ActiveEventStatesConfig = {
|
|
|
126
138
|
target: '#waiting.fetching'
|
|
127
139
|
},
|
|
128
140
|
PARTICIPANT_MACHINE_READY: {
|
|
129
|
-
actions: 'logAPMReady',
|
|
141
|
+
actions: ['logAPMReady', 'sendEnterToAppMachine'],
|
|
130
142
|
target: '#eventCreated'
|
|
131
143
|
}
|
|
132
144
|
}
|
|
@@ -164,13 +176,38 @@ const ActiveEventStatesConfig = {
|
|
|
164
176
|
id: 'eventCreated',
|
|
165
177
|
initial: 'waitingAnnounce',
|
|
166
178
|
entry: [
|
|
167
|
-
'sendParticipantEnterToAppEventMachine',
|
|
168
179
|
'enableSpectatorPresenceEvents'
|
|
169
180
|
],
|
|
170
181
|
on: {
|
|
182
|
+
ACTION: {
|
|
183
|
+
actions: ['processParticipantAction']
|
|
184
|
+
},
|
|
171
185
|
RENDER_DISPLAY: {
|
|
172
186
|
target: '#createdRendering'
|
|
173
|
-
}
|
|
187
|
+
},
|
|
188
|
+
PARTICIPANT_LEAVE: [
|
|
189
|
+
{
|
|
190
|
+
cond: 'isHostLeaving',
|
|
191
|
+
actions: ['sendHostExitToAppMachine', 'sendExitToParticipantMachine']
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
cond: 'isPermanentLeave',
|
|
195
|
+
actions: ['sendExitToAppMachine', 'sendExitToParticipantMachine']
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
actions: ['processDisconnectEvents']
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
PARTICIPANT_ENTER: [
|
|
202
|
+
{
|
|
203
|
+
cond: 'participantExists',
|
|
204
|
+
actions: 'processReconnectEvents'
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
// This shouldn't happen in eventCreated, but good to handle.
|
|
208
|
+
target: '#newParticipantJoining'
|
|
209
|
+
}
|
|
210
|
+
]
|
|
174
211
|
},
|
|
175
212
|
states: {
|
|
176
213
|
waitingAnnounce: {
|
|
@@ -207,8 +244,7 @@ const ActiveEventStatesConfig = {
|
|
|
207
244
|
notifyingRenderComplete: {
|
|
208
245
|
after: {
|
|
209
246
|
timeoutRendered: {
|
|
210
|
-
target: '#waitingAnnounce'
|
|
211
|
-
actions: ['notifyAppMachineRendered']
|
|
247
|
+
target: '#waitingAnnounce'
|
|
212
248
|
}
|
|
213
249
|
}
|
|
214
250
|
}
|
|
@@ -222,16 +258,23 @@ const ActiveEventStatesConfig = {
|
|
|
222
258
|
// existing participant
|
|
223
259
|
initial: 'transitioning',
|
|
224
260
|
on: {
|
|
261
|
+
ACTION: {
|
|
262
|
+
actions: ['processParticipantAction']
|
|
263
|
+
},
|
|
225
264
|
RENDER_DISPLAY: {
|
|
226
265
|
target: '#announceRendering'
|
|
227
266
|
},
|
|
228
267
|
PARTICIPANT_LEAVE: [
|
|
268
|
+
{
|
|
269
|
+
cond: 'isHostLeaving',
|
|
270
|
+
actions: ['sendHostExitToAppMachine', 'sendExitToParticipantMachine']
|
|
271
|
+
},
|
|
229
272
|
{
|
|
230
273
|
cond: 'isPermanentLeave',
|
|
231
|
-
actions: ['
|
|
274
|
+
actions: ['sendExitToAppMachine', 'sendExitToParticipantMachine']
|
|
232
275
|
},
|
|
233
276
|
{
|
|
234
|
-
actions: ['
|
|
277
|
+
actions: ['processDisconnectEvents']
|
|
235
278
|
}
|
|
236
279
|
],
|
|
237
280
|
PARTICIPANT_ENTER: [
|
|
@@ -239,7 +282,7 @@ const ActiveEventStatesConfig = {
|
|
|
239
282
|
// a participant could re-entering the event after above PARTICIPANT_LEAVE (browser refresh).
|
|
240
283
|
// so look for existing participants and send reconnect events to interested machines
|
|
241
284
|
cond: 'participantExists',
|
|
242
|
-
actions: '
|
|
285
|
+
actions: 'processReconnectEvents'
|
|
243
286
|
},
|
|
244
287
|
{
|
|
245
288
|
// spectator clicked JOIN
|
|
@@ -326,7 +369,7 @@ const ActiveEventStatesConfig = {
|
|
|
326
369
|
deferred: DeferredStatesPlus('START'),
|
|
327
370
|
on: {
|
|
328
371
|
PARTICIPANT_MACHINE_READY: {
|
|
329
|
-
actions: ['
|
|
372
|
+
actions: ['sendEnterToAppMachine'],
|
|
330
373
|
target: '#waitingToStart',
|
|
331
374
|
internal: true
|
|
332
375
|
}
|
|
@@ -335,28 +378,38 @@ const ActiveEventStatesConfig = {
|
|
|
335
378
|
}
|
|
336
379
|
},
|
|
337
380
|
live: {
|
|
381
|
+
id: 'live',
|
|
338
382
|
initial: 'transitioning',
|
|
339
383
|
on: {
|
|
340
384
|
RENDER_DISPLAY: {
|
|
341
385
|
target: '#liveRendering'
|
|
342
386
|
},
|
|
343
|
-
SPECTATOR_ENTER: {
|
|
344
|
-
actions: 'sendSpectatorEnterToAppEventMachine'
|
|
345
|
-
},
|
|
346
387
|
PARTICIPANT_LEAVE: [
|
|
388
|
+
{
|
|
389
|
+
cond: 'isHostLeaving',
|
|
390
|
+
actions: ['sendHostExitToAppMachine', 'sendExitToParticipantMachine']
|
|
391
|
+
},
|
|
347
392
|
{
|
|
348
393
|
cond: 'isPermanentLeave',
|
|
349
|
-
actions: ['
|
|
394
|
+
actions: ['sendExitToAppMachine', 'sendExitToParticipantMachine']
|
|
350
395
|
},
|
|
351
396
|
{
|
|
352
|
-
actions: ['
|
|
397
|
+
actions: ['processDisconnectEvents']
|
|
353
398
|
}
|
|
354
399
|
],
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
400
|
+
PARTICIPANT_ENTER: [
|
|
401
|
+
{
|
|
402
|
+
cond: 'participantExists',
|
|
403
|
+
actions: 'processReconnectEvents'
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
// spectator clicked JOIN
|
|
407
|
+
target: '.participantEntering'
|
|
408
|
+
}
|
|
409
|
+
],
|
|
410
|
+
PAUSE: {
|
|
411
|
+
target: 'paused'
|
|
412
|
+
}
|
|
360
413
|
},
|
|
361
414
|
states: {
|
|
362
415
|
transitioning: {
|
|
@@ -373,28 +426,9 @@ const ActiveEventStatesConfig = {
|
|
|
373
426
|
},
|
|
374
427
|
waitingForActions: {
|
|
375
428
|
id: 'waitingForActions',
|
|
429
|
+
entry: () => logger.debug('[AEM] live state...waiting for actions'),
|
|
376
430
|
on: {
|
|
377
|
-
PARTICIPANT_ENTER: [
|
|
378
|
-
{
|
|
379
|
-
// a participant could re-entering the event after above PARTICIPANT_LEAVE (browser refresh).
|
|
380
|
-
// so look for existing participants and send reconnect events to interested machines
|
|
381
|
-
cond: 'participantExists',
|
|
382
|
-
actions: 'sendParticipantReconnectEvents'
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
// in the future, open house events allow spectators to join and leave freely
|
|
386
|
-
// during live.waitingForActions
|
|
387
|
-
cond: 'isOpenHouseEvent',
|
|
388
|
-
target: 'participantEntering'
|
|
389
|
-
},
|
|
390
|
-
{
|
|
391
|
-
// shouldn't receive these so log it and return to current state
|
|
392
|
-
actions: ['logInvalidParticipantEnter'],
|
|
393
|
-
target: '#activeEvent.failure'
|
|
394
|
-
}
|
|
395
|
-
],
|
|
396
431
|
ACTION: {
|
|
397
|
-
// A participant has clicked an anear-data-action
|
|
398
432
|
actions: ['processParticipantAction']
|
|
399
433
|
}
|
|
400
434
|
}
|
|
@@ -404,10 +438,17 @@ const ActiveEventStatesConfig = {
|
|
|
404
438
|
deferred: DeferredStates,
|
|
405
439
|
invoke: {
|
|
406
440
|
src: 'renderDisplay',
|
|
407
|
-
onDone:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
441
|
+
onDone: [
|
|
442
|
+
{
|
|
443
|
+
cond: 'isParticipantsTimeoutActive',
|
|
444
|
+
target: 'notifyingRenderCompleteWithTimeout',
|
|
445
|
+
actions: 'setupParticipantsTimeout'
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
target: 'notifyingRenderComplete',
|
|
449
|
+
internal: true
|
|
450
|
+
}
|
|
451
|
+
],
|
|
411
452
|
onError: {
|
|
412
453
|
target: '#activeEvent.failure'
|
|
413
454
|
}
|
|
@@ -416,11 +457,57 @@ const ActiveEventStatesConfig = {
|
|
|
416
457
|
notifyingRenderComplete: {
|
|
417
458
|
after: {
|
|
418
459
|
timeoutRendered: {
|
|
419
|
-
target: '
|
|
460
|
+
target: 'waitingForActions',
|
|
461
|
+
actions: ['notifyAppMachineRendered']
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
notifyingRenderCompleteWithTimeout: {
|
|
466
|
+
after: {
|
|
467
|
+
timeoutRendered: {
|
|
468
|
+
target: 'waitAllParticipantsResponse',
|
|
420
469
|
actions: ['notifyAppMachineRendered']
|
|
421
470
|
}
|
|
422
471
|
}
|
|
423
472
|
},
|
|
473
|
+
waitAllParticipantsResponse: {
|
|
474
|
+
always: {
|
|
475
|
+
cond: 'allParticipantsResponded',
|
|
476
|
+
target: 'waitingForActions',
|
|
477
|
+
actions: ['clearParticipantsTimeout']
|
|
478
|
+
},
|
|
479
|
+
after: {
|
|
480
|
+
participantsActionTimeout: {
|
|
481
|
+
target: 'handleParticipantsTimeout'
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
on: {
|
|
485
|
+
ACTION: {
|
|
486
|
+
actions: ['processAndForwardAction', 'processParticipantResponse'],
|
|
487
|
+
internal: true
|
|
488
|
+
},
|
|
489
|
+
RENDER_DISPLAY: {
|
|
490
|
+
target: '#liveRendering'
|
|
491
|
+
},
|
|
492
|
+
PARTICIPANT_LEAVE: [
|
|
493
|
+
{
|
|
494
|
+
cond: 'isHostLeaving',
|
|
495
|
+
actions: ['removeLeavingParticipantFromTimeout', 'sendHostExitToAppMachine', 'sendExitToParticipantMachine']
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
cond: 'isPermanentLeave',
|
|
499
|
+
actions: ['removeLeavingParticipantFromTimeout', 'sendExitToAppMachine', 'sendExitToParticipantMachine']
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
actions: ['processDisconnectEvents']
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
handleParticipantsTimeout: {
|
|
508
|
+
entry: ['sendActionsTimeoutToAppM', 'clearParticipantsTimeout'],
|
|
509
|
+
always: 'waitingForActions'
|
|
510
|
+
},
|
|
424
511
|
participantEntering: {
|
|
425
512
|
// a PARTICIPANT_ENTER received from a new user JOIN click. Unless already exists,
|
|
426
513
|
// create an AnearParticipantMachine instance.
|
|
@@ -444,8 +531,8 @@ const ActiveEventStatesConfig = {
|
|
|
444
531
|
deferred: DeferredStates,
|
|
445
532
|
on: {
|
|
446
533
|
PARTICIPANT_MACHINE_READY: {
|
|
447
|
-
actions: ['
|
|
448
|
-
target: '
|
|
534
|
+
actions: ['sendEnterToAppMachine'],
|
|
535
|
+
target: 'waitingForActions',
|
|
449
536
|
internal: true
|
|
450
537
|
}
|
|
451
538
|
}
|
|
@@ -484,10 +571,10 @@ const ActiveEventStatesConfig = {
|
|
|
484
571
|
always: 'waitForParticipantsToExit'
|
|
485
572
|
},
|
|
486
573
|
waitForParticipantsToExit: {
|
|
487
|
-
entry: context => logger.debug(`[AEM] Entering waitForParticipantsToExit with ${
|
|
574
|
+
entry: context => logger.debug(`[AEM] Entering waitForParticipantsToExit with ${getAllParticipantIds(context).length} participants`),
|
|
488
575
|
always: [
|
|
489
576
|
{
|
|
490
|
-
cond: context =>
|
|
577
|
+
cond: context => getAllParticipantIds(context).length === 0,
|
|
491
578
|
target: 'finalizing'
|
|
492
579
|
}
|
|
493
580
|
],
|
|
@@ -530,10 +617,10 @@ const ActiveEventStatesConfig = {
|
|
|
530
617
|
always: 'waitForParticipantsToExit'
|
|
531
618
|
},
|
|
532
619
|
waitForParticipantsToExit: {
|
|
533
|
-
entry: context => logger.debug(`[AEM] Entering waitForParticipantsToExit with ${
|
|
620
|
+
entry: context => logger.debug(`[AEM] Entering waitForParticipantsToExit with ${getAllParticipantIds(context).length} participants`),
|
|
534
621
|
always: [
|
|
535
622
|
{
|
|
536
|
-
cond: context =>
|
|
623
|
+
cond: context => getAllParticipantIds(context).length === 0,
|
|
537
624
|
target: 'finalizing'
|
|
538
625
|
}
|
|
539
626
|
],
|
|
@@ -625,7 +712,7 @@ const CreateEventChannelsAndAppMachineConfig = {
|
|
|
625
712
|
invoke: {
|
|
626
713
|
src: 'attachToEventChannel',
|
|
627
714
|
onDone: {
|
|
628
|
-
target: '
|
|
715
|
+
target: '.',
|
|
629
716
|
internal: true
|
|
630
717
|
},
|
|
631
718
|
onError: {
|
|
@@ -823,15 +910,68 @@ const AnearEventMachineFunctions = ({
|
|
|
823
910
|
userJSON => context.appEventMachine.send('SPECTATOR_ENTER', userJSON)
|
|
824
911
|
)
|
|
825
912
|
},
|
|
826
|
-
|
|
827
|
-
const
|
|
913
|
+
sendEnterToAppMachine: (context, event) => {
|
|
914
|
+
const anearParticipant = event.data?.anearParticipant ?? event.anearParticipant;
|
|
915
|
+
|
|
916
|
+
if (!anearParticipant) {
|
|
917
|
+
logger.error('[AEM] sendEnterToAppMachine was called without an anearParticipant in the event', event);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
828
920
|
const participantInfo = context.participants[anearParticipant.id]
|
|
829
921
|
|
|
830
|
-
|
|
922
|
+
if (!participantInfo) {
|
|
923
|
+
logger.error(`[AEM] participantInfo not found for ${anearParticipant.id} in sendEnterToAppMachine`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const isHost = anearParticipant.isHost;
|
|
928
|
+
const eventName = isHost ? 'HOST_ENTER' : 'PARTICIPANT_ENTER';
|
|
929
|
+
const eventPayload = { participant: participantInfo };
|
|
930
|
+
|
|
931
|
+
logger.debug(`[AEM] Sending ${eventName} for ${anearParticipant.id}`);
|
|
932
|
+
context.appEventMachine.send(eventName, eventPayload);
|
|
933
|
+
},
|
|
934
|
+
processDisconnectEvents: (context, event) => {
|
|
935
|
+
const participantId = event.data.id;
|
|
936
|
+
const participant = context.participants[participantId];
|
|
937
|
+
const isHost = participant && participant.isHost;
|
|
938
|
+
const eventName = isHost ? 'HOST_DISCONNECT' : 'PARTICIPANT_DISCONNECT';
|
|
939
|
+
const participantMachine = context.participantMachines[participantId];
|
|
831
940
|
|
|
832
|
-
|
|
941
|
+
logger.debug(`[AEM] processing disconnect for ${participantId}. Is host? ${isHost}`);
|
|
942
|
+
if (participantMachine) {
|
|
943
|
+
participantMachine.send('PARTICIPANT_DISCONNECT');
|
|
944
|
+
}
|
|
945
|
+
context.appEventMachine.send(eventName, { participantId });
|
|
946
|
+
},
|
|
947
|
+
processReconnectEvents: (context, event) => {
|
|
948
|
+
const participantId = event.data.id;
|
|
949
|
+
const participant = context.participants[participantId];
|
|
950
|
+
const isHost = participant && participant.isHost;
|
|
951
|
+
const eventName = isHost ? 'HOST_RECONNECT' : 'PARTICIPANT_RECONNECT';
|
|
952
|
+
const participantMachine = context.participantMachines[participantId];
|
|
833
953
|
|
|
834
|
-
|
|
954
|
+
logger.debug(`[AEM] processing reconnect for ${participantId}. Is host? ${isHost}`);
|
|
955
|
+
if (participantMachine) {
|
|
956
|
+
participantMachine.send('PARTICIPANT_RECONNECT');
|
|
957
|
+
}
|
|
958
|
+
context.appEventMachine.send(eventName, { participantId });
|
|
959
|
+
},
|
|
960
|
+
sendExitToAppMachine: (context, event) => {
|
|
961
|
+
// coming from an action channel message, event.data.id
|
|
962
|
+
const participantId = event.data.id;
|
|
963
|
+
const participantInfo = context.participants[participantId];
|
|
964
|
+
if (participantInfo) {
|
|
965
|
+
logger.debug(`[AEM] sending PARTICIPANT_EXIT to AppM for participant ${participantId}`);
|
|
966
|
+
context.appEventMachine.send('PARTICIPANT_EXIT', { participantId });
|
|
967
|
+
} else {
|
|
968
|
+
logger.warn(`[AEM] Participant info not found for id ${participantId} during sendExitToAppMachine`);
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
sendHostExitToAppMachine: (context, event) => {
|
|
972
|
+
const participantId = event.data.id;
|
|
973
|
+
logger.debug(`[AEM] sending HOST_EXIT to AppM for host ${participantId}`);
|
|
974
|
+
context.appEventMachine.send('HOST_EXIT', { participantId });
|
|
835
975
|
},
|
|
836
976
|
sendExitToParticipantMachine: (context, event) => {
|
|
837
977
|
// coming from an action channel message, event.data.id
|
|
@@ -843,46 +983,11 @@ const AnearEventMachineFunctions = ({
|
|
|
843
983
|
logger.warn(`[AEM] Participant machine not found for id ${event.data.id} during sendExitToParticipantMachine`)
|
|
844
984
|
}
|
|
845
985
|
},
|
|
846
|
-
sendParticipantExitToAppEventMachine: (context, event) => {
|
|
847
|
-
context.appEventMachine.send('PARTICIPANT_EXIT',
|
|
848
|
-
{ participantId: event.data.id }
|
|
849
|
-
)
|
|
850
|
-
},
|
|
851
986
|
sendParticipantExitEvents: context => {
|
|
852
987
|
Object.values(context.participantMachines).forEach(pm => pm.send('PARTICIPANT_EXIT'))
|
|
853
988
|
},
|
|
854
989
|
|
|
855
990
|
|
|
856
|
-
sendParticipantDisconnectEvents: (context, event) => {
|
|
857
|
-
const participantMachine = context.participantMachines[event.data.id]
|
|
858
|
-
if (participantMachine) {
|
|
859
|
-
logger.debug("[AEM] sending PARTICIPANT_DISCONNECT to APM and AppM")
|
|
860
|
-
// triggers a timer state in APM to timeout a missing Participant
|
|
861
|
-
participantMachine.send('PARTICIPANT_DISCONNECT', event.data)
|
|
862
|
-
// gives the App the opportunity to process the potential early exit
|
|
863
|
-
// of a key player, possibly pausing or altering game behavior until
|
|
864
|
-
// the participant either times out (exits game), or comes back after
|
|
865
|
-
// the momentary outage
|
|
866
|
-
context.appEventMachine.send('PARTICIPANT_DISCONNECT',
|
|
867
|
-
{ participantId: event.data.id }
|
|
868
|
-
)
|
|
869
|
-
}
|
|
870
|
-
},
|
|
871
|
-
sendParticipantReconnectEvents: (context, event) => {
|
|
872
|
-
const participantId = event.data.id
|
|
873
|
-
const participantMachine = context.participantMachines[participantId]
|
|
874
|
-
if (participantMachine) {
|
|
875
|
-
// suspends the disconnect timeout and restores participant presence state in
|
|
876
|
-
// the event
|
|
877
|
-
participantMachine.send('PARTICIPANT_RECONNECT')
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
const participantInfo = context.participants[participantId]
|
|
881
|
-
// send this to the app so they can receive and trigger a meta display for private participants
|
|
882
|
-
if (participantInfo) {
|
|
883
|
-
context.appEventMachine.send('PARTICIPANT_RECONNECT', { participant: participantInfo })
|
|
884
|
-
}
|
|
885
|
-
},
|
|
886
991
|
updateParticipantPresence: (context, event) => {
|
|
887
992
|
// lookup the participantMachine and update its context
|
|
888
993
|
const participantMachine = context.participantMachines[event.data.id]
|
|
@@ -902,6 +1007,9 @@ const AnearEventMachineFunctions = ({
|
|
|
902
1007
|
const participantId = event.data.participantId
|
|
903
1008
|
const eventMessagePayload = JSON.parse(event.data.payload) // { eventName: {eventObject} }
|
|
904
1009
|
const [appEventName, payload] = Object.entries(eventMessagePayload)[0]
|
|
1010
|
+
|
|
1011
|
+
logger.debug(`[AEM] got Event ${appEventName} from payload from participant ${participantId}`)
|
|
1012
|
+
|
|
905
1013
|
const actionEventPayload = {
|
|
906
1014
|
participantId,
|
|
907
1015
|
payload
|
|
@@ -913,10 +1021,90 @@ const AnearEventMachineFunctions = ({
|
|
|
913
1021
|
|
|
914
1022
|
context.appEventMachine.send(appEventName, actionEventPayload)
|
|
915
1023
|
},
|
|
1024
|
+
processAndForwardAction: (context, event) => {
|
|
1025
|
+
const participantId = event.data.participantId;
|
|
1026
|
+
const { nonResponders } = context.participantsActionTimeout;
|
|
1027
|
+
|
|
1028
|
+
// Check if this is the last responder before the context is updated.
|
|
1029
|
+
// This assumes the current participant IS a non-responder.
|
|
1030
|
+
const isFinalAction = nonResponders.size === 1 && nonResponders.has(participantId);
|
|
1031
|
+
|
|
1032
|
+
logger.info(`[AEM] Participants FINAL ACTION is ${isFinalAction}`)
|
|
1033
|
+
|
|
1034
|
+
// Forward to AppM with the finalAction flag
|
|
1035
|
+
const eventMessagePayload = JSON.parse(event.data.payload);
|
|
1036
|
+
const [appEventName, payload] = Object.entries(eventMessagePayload)[0];
|
|
1037
|
+
const appM_Payload = {
|
|
1038
|
+
participantId,
|
|
1039
|
+
payload,
|
|
1040
|
+
finalAction: isFinalAction
|
|
1041
|
+
};
|
|
1042
|
+
context.appEventMachine.send(appEventName, appM_Payload);
|
|
1043
|
+
|
|
1044
|
+
// Forward to APM (without the flag)
|
|
1045
|
+
const participantMachine = context.participantMachines[participantId];
|
|
1046
|
+
if (participantMachine) {
|
|
1047
|
+
const apm_Payload = { participantId, payload };
|
|
1048
|
+
participantMachine.send('ACTION', apm_Payload);
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
916
1051
|
processParticipantTimeout: (context, event) => context.appEventMachine.send(
|
|
917
1052
|
'PARTICIPANT_TIMEOUT',
|
|
918
1053
|
{ participantId: event.participantId }
|
|
919
1054
|
),
|
|
1055
|
+
setupParticipantsTimeout: assign((context, event) => {
|
|
1056
|
+
const timeoutMsecs = event.data.participantsTimeout.msecs
|
|
1057
|
+
const allParticipantIds = getPlayingParticipantIds(context)
|
|
1058
|
+
logger.debug(`[AEM] Starting participants action timeout for ${timeoutMsecs}ms. Responders: ${allParticipantIds.join(', ')}`)
|
|
1059
|
+
|
|
1060
|
+
return {
|
|
1061
|
+
participantsActionTimeout: {
|
|
1062
|
+
msecs: timeoutMsecs,
|
|
1063
|
+
nonResponders: new Set(allParticipantIds)
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}),
|
|
1067
|
+
processParticipantResponse: assign((context, event) => {
|
|
1068
|
+
const participantId = event.data.participantId
|
|
1069
|
+
const { nonResponders, ...rest } = context.participantsActionTimeout
|
|
1070
|
+
const newNonResponders = new Set(nonResponders)
|
|
1071
|
+
newNonResponders.delete(participantId)
|
|
1072
|
+
|
|
1073
|
+
return {
|
|
1074
|
+
participantsActionTimeout: {
|
|
1075
|
+
...rest,
|
|
1076
|
+
nonResponders: newNonResponders
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}),
|
|
1080
|
+
removeLeavingParticipantFromTimeout: assign((context, event) => {
|
|
1081
|
+
const participantId = event.data.id
|
|
1082
|
+
const { nonResponders, ...rest } = context.participantsActionTimeout
|
|
1083
|
+
const newNonResponders = new Set(nonResponders)
|
|
1084
|
+
newNonResponders.delete(participantId)
|
|
1085
|
+
|
|
1086
|
+
return {
|
|
1087
|
+
participantsActionTimeout: {
|
|
1088
|
+
...rest,
|
|
1089
|
+
nonResponders: newNonResponders
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}),
|
|
1093
|
+
sendActionsTimeoutToAppM: (context, _event) => {
|
|
1094
|
+
const { nonResponders, msecs } = context.participantsActionTimeout
|
|
1095
|
+
const nonResponderIds = [...nonResponders]
|
|
1096
|
+
logger.info(`[AEM] Participants action timed out. Non-responders: ${nonResponderIds.join(', ')}`)
|
|
1097
|
+
|
|
1098
|
+
if (context.appEventMachine) {
|
|
1099
|
+
context.appEventMachine.send('ACTIONS_TIMEOUT', {
|
|
1100
|
+
timeout: msecs,
|
|
1101
|
+
nonResponderIds
|
|
1102
|
+
})
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
clearParticipantsTimeout: assign({
|
|
1106
|
+
participantsActionTimeout: null
|
|
1107
|
+
}),
|
|
920
1108
|
cleanupExitingParticipant: assign((context, event) => {
|
|
921
1109
|
const { participantId } = event
|
|
922
1110
|
const participant = context.participants[participantId]
|
|
@@ -951,13 +1139,11 @@ const AnearEventMachineFunctions = ({
|
|
|
951
1139
|
logInvalidParticipantEnter: (c, e) => logger.info("[AEM] Error: Unexepected PARTICIPANT_ENTER with id: ", e.data.id),
|
|
952
1140
|
},
|
|
953
1141
|
services: {
|
|
954
|
-
renderDisplay: (context, event) => {
|
|
1142
|
+
renderDisplay: async (context, event) => {
|
|
955
1143
|
const displayEventProcessor = new DisplayEventProcessor(context)
|
|
956
1144
|
|
|
957
|
-
return displayEventProcessor.processAndPublish(event.displayEvents)
|
|
1145
|
+
return await displayEventProcessor.processAndPublish(event.displayEvents)
|
|
958
1146
|
},
|
|
959
|
-
|
|
960
|
-
|
|
961
1147
|
getAttachedCreatorOrHost: async (context, event) => {
|
|
962
1148
|
logger.debug("[AEM] getAttachedCreatorOrHost() invoked")
|
|
963
1149
|
|
|
@@ -1051,16 +1237,30 @@ const AnearEventMachineFunctions = ({
|
|
|
1051
1237
|
return false
|
|
1052
1238
|
}
|
|
1053
1239
|
},
|
|
1240
|
+
isHostLeaving: (context, event) => {
|
|
1241
|
+
const participantId = event.data.id;
|
|
1242
|
+
const participant = context.participants[participantId];
|
|
1243
|
+
return participant && participant.isHost;
|
|
1244
|
+
},
|
|
1054
1245
|
participantExists: (context, event) => !!context.participants[event.data.id],
|
|
1055
1246
|
eventCreatorIsHost: (context, event) => context.anearEvent.hosted,
|
|
1056
|
-
isOpenHouseEvent: (context, event) => context.anearEvent.openHouse || false
|
|
1247
|
+
isOpenHouseEvent: (context, event) => context.anearEvent.openHouse || false,
|
|
1248
|
+
isParticipantsTimeoutActive: (context, event) => {
|
|
1249
|
+
return event.data && event.data.participantsTimeout && event.data.participantsTimeout.msecs > 0
|
|
1250
|
+
},
|
|
1251
|
+
allParticipantsResponded: (context, event) => {
|
|
1252
|
+
return context.participantsActionTimeout && context.participantsActionTimeout.nonResponders.size === 0
|
|
1253
|
+
}
|
|
1057
1254
|
},
|
|
1058
1255
|
delays: {
|
|
1059
1256
|
// in the future, these delays should be goverened by the type of App and
|
|
1060
1257
|
// if there has been activity.
|
|
1061
1258
|
timeoutEventAnnounce: context => C.TIMEOUT_MSECS.ANNOUNCE,
|
|
1062
1259
|
timeoutEventStart: context => C.TIMEOUT_MSECS.START,
|
|
1063
|
-
timeoutRendered: context => C.TIMEOUT_MSECS.RENDERED_EVENT_DELAY
|
|
1260
|
+
timeoutRendered: context => C.TIMEOUT_MSECS.RENDERED_EVENT_DELAY,
|
|
1261
|
+
participantsActionTimeout: (context, event) => {
|
|
1262
|
+
return context.participantsActionTimeout.msecs
|
|
1263
|
+
}
|
|
1064
1264
|
}
|
|
1065
1265
|
})
|
|
1066
1266
|
|