@@ -45,6 +45,10 @@ def __init__(self):
4545 self ._build_game_class_dict ()
4646 self ._start_clean_up ()
4747 self ._player_joins = threading .Event ()
48+ self ._auto_join_tokens = {} # game name -> auto-join token
49+ self ._next_auto_join_token = 0
50+ self ._AUTO = 'auto'
51+ self ._lock = threading .Lock ()
4852
4953 def _build_game_class_dict (self ):
5054 """
@@ -87,7 +91,9 @@ def _join(self, request):
8791 join. If this is the case, the request is passed to the function for
8892 joining a session. In all other cases, the function for starting a new
8993 game session is called. This implies that an already running session
90- will be terminated.
94+ will be terminated. When a client makes use of the auto-join
95+ functionality, a unique token is generated. In this case, sessions are
96+ never terminated. A new session is created instead.
9197
9298 Parameters:
9399 request (dict): request containing game name and token
@@ -108,14 +114,23 @@ def _join(self, request):
108114 if game_name not in self ._game_classes :
109115 return utility .framework_error ('no such game' )
110116
117+ if token .startswith (self ._AUTO ) and len (token ) > len (self ._AUTO ):
118+ return utility .framework_error ("token must not start with reserved prefix 'auto'" )
119+
120+ # generate token for auto-joining clients:
121+ if token == self ._AUTO :
122+ token = self ._generate_auto_join_token (game_name , players )
123+ name = '' # no observer mode for auto-join sessions
124+
111125 # retrieve game session, if it exists:
112126 session , _ = self ._retrieve_session (game_name , token )
113127
114128 # start or join a session:
115129 if session and not session .full ():
116- return self ._join_session (session , name )
130+ return self ._join_session (session , name , token )
117131 elif not session and not players :
118- return utility .framework_error ('no such game session' )
132+ return utility .framework_error (
133+ 'no such game session; provide the number of players to start one' )
119134 elif session and session .full () and not players :
120135 return utility .framework_error ('game session already full' )
121136 else :
@@ -169,20 +184,22 @@ def _start_session(self, game_name, token, players, name):
169184
170185 # wait for others to join:
171186 self ._await_game_start (session )
187+ self ._remove_auto_join_token (game_name , token )
172188
173189 if not session .full (): # timeout reached
174190 if (game_name , token ) in self ._game_sessions :
175- del self ._game_sessions [(game_name , token )] # remove game session
191+ del self ._game_sessions [(game_name , token )]
176192 return utility .framework_error ('timeout while waiting for others to join' )
177193
178194 log .info (f'Starting session { game_name } :{ token } ' )
179195
180196 return self ._return_data ({
181197 'player_id' :player_id ,
182198 'key' :key ,
199+ 'token' :token ,
183200 'request_size_max' :config .request_size_max })
184201
185- def _join_session (self , session , name ):
202+ def _join_session (self , session , name , token ):
186203 """
187204 Joining a game session.
188205
@@ -199,6 +216,7 @@ def _join_session(self, session, name):
199216 Parameters:
200217 session (GameSession): game session
201218 name (str): player name, can be an empty string
219+ token (str): name of the game session
202220
203221 Returns:
204222 dict: containing the player's ID and key
@@ -217,6 +235,7 @@ def _join_session(self, session, name):
217235 return self ._return_data ({
218236 'player_id' :player_id ,
219237 'key' :key ,
238+ 'token' :token ,
220239 'request_size_max' :config .request_size_max })
221240
222241 def _move (self , request ):
@@ -322,7 +341,8 @@ def _observe(self, request):
322341 needs to know the ID of that player. This function retrieves that ID
323342 based on the player's name. This only works, if the player has supplied
324343 a name when joining the game session. The observed player's key is sent
325- to the client as well.
344+ to the client as well. The observer mode is not available for auto-join
345+ sessions.
326346
327347 Parameters:
328348 request (dict): request containing game name, token and player to be observed
@@ -338,6 +358,9 @@ def _observe(self, request):
338358 token = request ['token' ]
339359 player_name = request ['name' ]
340360
361+ if token == self ._AUTO :
362+ return utility .framework_error ('observer mode not available for auto-join sessions' )
363+
341364 # retrieve game session:
342365 session , err = self ._retrieve_session (game_name , token )
343366 if err : # no such game or game session
@@ -429,6 +452,40 @@ def _retrieve_session(self, game_name, token):
429452
430453 return self ._game_sessions [(game_name , token )], None
431454
455+ def _generate_auto_join_token (self , game_name , players ):
456+ """
457+ Generate a unique token for an auto-join session.
458+
459+ Parameters:
460+ game_name (str): name of the game
461+ players (int): total number of players
462+
463+ Returns:
464+ str: a unique token for an auto-join session, None in case of an error
465+ """
466+ with self ._lock :
467+ if game_name not in self ._auto_join_tokens :
468+ if not players : return None
469+ token = f'{ self ._AUTO } -{ self ._next_auto_join_token } '
470+ self ._next_auto_join_token += 1
471+ self ._auto_join_tokens [game_name ] = token
472+ else :
473+ token = self ._auto_join_tokens [game_name ]
474+
475+ return token
476+
477+ def _remove_auto_join_token (self , game_name , token ):
478+ """
479+ This function is used to remove a token from the auto-join dictionary
480+ after the session has been started.
481+
482+ Parameters:
483+ game_name (str): name of the game
484+ token (str): name of the game session
485+ """
486+ if token .startswith (self ._AUTO ):
487+ del self ._auto_join_tokens [game_name ]
488+
432489 def _return_data (self , data ):
433490 """
434491 Adds data to a dictionary to be sent back to the client. This function
0 commit comments