@@ -307,7 +307,7 @@ describe('GameRuntime', (it, beforeEach) => {
307307 assert . equal (
308308 manager . getPlayerActivity ( russell ) . getActivityState ( ) , GameActivity . kStateEngaged ) ;
309309
310- assert . equal ( runtime . stateForTesting , GameRuntime . kStateRunning ) ;
310+ assert . equal ( runtime . state , GameRuntime . kStateRunning ) ;
311311
312312 assert . isNotNull ( vehicles [ 0 ] ) ;
313313 assert . equal ( vehicles [ 0 ] . virtualWorld , runtime . virtualWorld ) ;
@@ -329,7 +329,7 @@ describe('GameRuntime', (it, beforeEach) => {
329329 await runGameLoop ( ) ;
330330
331331 // Both players have been told to leave the game, so it should now have been finalized.
332- assert . equal ( runtime . stateForTesting , GameRuntime . kStateFinalized ) ;
332+ assert . equal ( runtime . state , GameRuntime . kStateFinalized ) ;
333333 assert . equal ( manager . runtimes_ . size , 0 ) ;
334334
335335 assert . equal ( gunther . messages . length , 3 ) ;
@@ -392,6 +392,81 @@ describe('GameRuntime', (it, beforeEach) => {
392392 }
393393 } ) ;
394394
395+ it ( 'should enable players to join continuous games at any time' , async ( assert ) => {
396+ const feature = server . featureManager . loadFeature ( 'games' ) ;
397+ const finance = server . featureManager . loadFeature ( 'finance' ) ;
398+
399+ let activePlayers = 0 ;
400+ let initialized = false ;
401+
402+ feature . registerGame ( class extends Game {
403+ async onInitialized ( settings ) {
404+ if ( initialized )
405+ throw new Error ( 'The game already has been initialized.' ) ;
406+
407+ initialized = true ;
408+ }
409+
410+ async onPlayerAdded ( player ) { ++ activePlayers ; }
411+ async onPlayerRemoved ( player ) { -- activePlayers ; }
412+ } , {
413+ name : 'Bubble' ,
414+ goal : 'Have entities' ,
415+
416+ command : 'bubblegame' ,
417+ price : 500 ,
418+
419+ continuous : true ,
420+ minimumPlayers : 1 ,
421+ maximumPlayers : 4 ,
422+ } ) ;
423+
424+ finance . givePlayerCash ( gunther , 2500 ) ;
425+ finance . givePlayerCash ( russell , 2500 ) ;
426+
427+ assert . isNull ( manager . getPlayerActivity ( gunther ) ) ;
428+ assert . isNull ( manager . getPlayerActivity ( russell ) ) ;
429+
430+ // (1) The game should start immediately for |gunther|, even though other players are
431+ // available. This is because it's been marked as a continuous game.
432+ assert . isTrue ( await gunther . issueCommand ( '/bubblegame' ) ) ;
433+
434+ assert . isNotNull ( manager . getPlayerActivity ( gunther ) ) ;
435+ assert . equal (
436+ manager . getPlayerActivity ( gunther ) . getActivityState ( ) , GameActivity . kStateEngaged ) ;
437+
438+ assert . equal ( activePlayers , 1 ) ;
439+
440+ // (2) Have Russell join the game as well. They should be added to the active game, rather
441+ // than be routed through registration and in a new game instead.
442+ assert . isTrue ( await russell . issueCommand ( '/bubblegame' ) ) ;
443+
444+ assert . isNotNull ( manager . getPlayerActivity ( russell ) ) ;
445+ assert . equal (
446+ manager . getPlayerActivity ( russell ) . getActivityState ( ) , GameActivity . kStateEngaged ) ;
447+
448+ assert . strictEqual ( manager . getPlayerActivity ( gunther ) , manager . getPlayerActivity ( russell ) ) ;
449+ assert . equal ( activePlayers , 2 ) ;
450+
451+ // (3) Have Gunther leave the game. Russell should still be playing.
452+ assert . isTrue ( await gunther . issueCommand ( '/leave' ) ) ;
453+
454+ assert . isNull ( manager . getPlayerActivity ( gunther ) ) ;
455+ assert . equal ( activePlayers , 1 ) ;
456+
457+ assert . isNotNull ( manager . getPlayerActivity ( russell ) ) ;
458+ assert . equal (
459+ manager . getPlayerActivity ( russell ) . getActivityState ( ) , GameActivity . kStateEngaged ) ;
460+
461+ // (4) When Russell leaves the game as well, it should be stopped. Here we mimic that by
462+ // Russell disconnecting, because we're already testing the `/leave` command above.
463+ russell . disconnectForTesting ( ) ;
464+
465+ // Wait until there are no more active runtimes, to verify state is cleaned up.
466+ while ( manager . activeRuntimesForTesting . size )
467+ await server . clock . advance ( 50 ) ;
468+ } ) ;
469+
395470 it ( 'should have a sensible description when casted to a string' , assert => {
396471 const description = new GameDescription ( Game , { name : 'Bubble' , goal : '' } ) ;
397472 const runtime = new GameRuntime ( manager , description , kDefaultSettings ) ;
0 commit comments