comparison sat/plugins/plugin_misc_room_game.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents be6d91572633
children
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
54 54
55 55
56 class RoomGame(object): 56 class RoomGame(object):
57 """This class is used to help launching a MUC game. 57 """This class is used to help launching a MUC game.
58 58
59 Bridge methods callbacks: _prepareRoom, _playerReady, _createGame 59 bridge methods callbacks: _prepare_room, _player_ready, _create_game
60 Triggered methods: userJoinedTrigger, userLeftTrigger 60 Triggered methods: user_joined_trigger, user_left_trigger
61 Also called from subclasses: newRound 61 Also called from subclasses: new_round
62 62
63 For examples of messages sequences, please look in sub-classes. 63 For examples of messages sequences, please look in sub-classes.
64 """ 64 """
65 65
66 # Values for self.invite_mode (who can invite after the game creation) 66 # Values for self.invite_mode (who can invite after the game creation)
79 """For other plugin to dynamically inherit this class, it is necessary to not use __init__ but _init_. 79 """For other plugin to dynamically inherit this class, it is necessary to not use __init__ but _init_.
80 The subclass itself must be initialized this way: 80 The subclass itself must be initialized this way:
81 81
82 class MyGame(object): 82 class MyGame(object):
83 83
84 def inheritFromRoomGame(self, host): 84 def inherit_from_room_game(self, host):
85 global RoomGame 85 global RoomGame
86 RoomGame = host.plugins["ROOM-GAME"].__class__ 86 RoomGame = host.plugins["ROOM-GAME"].__class__
87 self.__class__ = type(self.__class__.__name__, (self.__class__, RoomGame, object), {}) 87 self.__class__ = type(self.__class__.__name__, (self.__class__, RoomGame, object), {})
88 88
89 def __init__(self, host): 89 def __init__(self, host):
90 self.inheritFromRoomGame(host) 90 self.inherit_from_room_game(host)
91 RoomGame._init_(self, host, ...) 91 RoomGame._init_(self, host, ...)
92 92
93 """ 93 """
94 self.host = host 94 self.host = host
95 95
123 # this has been added for testing purpose. It is sometimes needed to remove a dependence 123 # this has been added for testing purpose. It is sometimes needed to remove a dependence
124 # while building the synchronization data, for example to replace a call to time.time() 124 # while building the synchronization data, for example to replace a call to time.time()
125 # by an arbitrary value. If needed, this attribute would be set to True from the testcase. 125 # by an arbitrary value. If needed, this attribute would be set to True from the testcase.
126 self.testing = False 126 self.testing = False
127 127
128 host.trigger.add("MUC user joined", self.userJoinedTrigger) 128 host.trigger.add("MUC user joined", self.user_joined_trigger)
129 host.trigger.add("MUC user left", self.userLeftTrigger) 129 host.trigger.add("MUC user left", self.user_left_trigger)
130 130
131 def _createOrInvite(self, room_jid, other_players, profile): 131 def _create_or_invite(self, room_jid, other_players, profile):
132 """ 132 """
133 This is called only when someone explicitly wants to play. 133 This is called only when someone explicitly wants to play.
134 134
135 The game will not be created if one already exists in the room, 135 The game will not be created if one already exists in the room,
136 also its creation could be postponed until all the expected players 136 also its creation could be postponed until all the expected players
137 join the room (in that case it will be created from userJoinedTrigger). 137 join the room (in that case it will be created from user_joined_trigger).
138 @param room (wokkel.muc.Room): the room 138 @param room (wokkel.muc.Room): the room
139 @param other_players (list[jid.JID]): list of the other players JID (bare) 139 @param other_players (list[jid.JID]): list of the other players JID (bare)
140 """ 140 """
141 # FIXME: broken ! 141 # FIXME: broken !
142 raise NotImplementedError("To be fixed") 142 raise NotImplementedError("To be fixed")
143 client = self.host.getClient(profile) 143 client = self.host.get_client(profile)
144 user_jid = self.host.getJidNStream(profile)[0] 144 user_jid = self.host.get_jid_n_stream(profile)[0]
145 nick = self.host.plugins["XEP-0045"].getRoomNick(client, room_jid) 145 nick = self.host.plugins["XEP-0045"].get_room_nick(client, room_jid)
146 nicks = [nick] 146 nicks = [nick]
147 if self._gameExists(room_jid): 147 if self._game_exists(room_jid):
148 if not self._checkJoinAuth(room_jid, user_jid, nick): 148 if not self._check_join_auth(room_jid, user_jid, nick):
149 return 149 return
150 nicks.extend(self._invitePlayers(room_jid, other_players, nick, profile)) 150 nicks.extend(self._invite_players(room_jid, other_players, nick, profile))
151 self._updatePlayers(room_jid, nicks, True, profile) 151 self._update_players(room_jid, nicks, True, profile)
152 else: 152 else:
153 self._initGame(room_jid, nick) 153 self._init_game(room_jid, nick)
154 (auth, waiting, missing) = self._checkWaitAuth(room_jid, other_players) 154 (auth, waiting, missing) = self._check_wait_auth(room_jid, other_players)
155 nicks.extend(waiting) 155 nicks.extend(waiting)
156 nicks.extend(self._invitePlayers(room_jid, missing, nick, profile)) 156 nicks.extend(self._invite_players(room_jid, missing, nick, profile))
157 if auth: 157 if auth:
158 self.createGame(room_jid, nicks, profile) 158 self.create_game(room_jid, nicks, profile)
159 else: 159 else:
160 self._updatePlayers(room_jid, nicks, False, profile) 160 self._update_players(room_jid, nicks, False, profile)
161 161
162 def _initGame(self, room_jid, referee_nick): 162 def _init_game(self, room_jid, referee_nick):
163 """ 163 """
164 164
165 @param room_jid (jid.JID): JID of the room 165 @param room_jid (jid.JID): JID of the room
166 @param referee_nick (unicode): nickname of the referee 166 @param referee_nick (unicode): nickname of the referee
167 """ 167 """
168 # Important: do not add the referee to 'players' yet. For a 168 # Important: do not add the referee to 'players' yet. For a
169 # <players /> message to be emitted whenever a new player is joining, 169 # <players /> message to be emitted whenever a new player is joining,
170 # it is necessary to not modify 'players' outside of _updatePlayers. 170 # it is necessary to not modify 'players' outside of _update_players.
171 referee_jid = jid.JID(room_jid.userhost() + "/" + referee_nick) 171 referee_jid = jid.JID(room_jid.userhost() + "/" + referee_nick)
172 self.games[room_jid] = { 172 self.games[room_jid] = {
173 "referee": referee_jid, 173 "referee": referee_jid,
174 "players": [], 174 "players": [],
175 "started": False, 175 "started": False,
176 "status": {}, 176 "status": {},
177 } 177 }
178 self.games[room_jid].update(copy.deepcopy(self.game_init)) 178 self.games[room_jid].update(copy.deepcopy(self.game_init))
179 self.invitations.setdefault(room_jid, []) 179 self.invitations.setdefault(room_jid, [])
180 180
181 def _gameExists(self, room_jid, started=False): 181 def _game_exists(self, room_jid, started=False):
182 """Return True if a game has been initialized/started. 182 """Return True if a game has been initialized/started.
183 @param started: if False, the game must be initialized to return True, 183 @param started: if False, the game must be initialized to return True,
184 otherwise it must be initialized and started with createGame. 184 otherwise it must be initialized and started with create_game.
185 @return: True if a game is initialized/started in that room""" 185 @return: True if a game is initialized/started in that room"""
186 return room_jid in self.games and (not started or self.games[room_jid]["started"]) 186 return room_jid in self.games and (not started or self.games[room_jid]["started"])
187 187
188 def _checkJoinAuth(self, room_jid, user_jid=None, nick="", verbose=False): 188 def _check_join_auth(self, room_jid, user_jid=None, nick="", verbose=False):
189 """Checks if this profile is allowed to join the game. 189 """Checks if this profile is allowed to join the game.
190 190
191 The parameter nick is used to check if the user is already 191 The parameter nick is used to check if the user is already
192 a player in that game. When this method is called from 192 a player in that game. When this method is called from
193 userJoinedTrigger, nick is also used to check the user 193 user_joined_trigger, nick is also used to check the user
194 identity instead of user_jid_s (see TODO comment below). 194 identity instead of user_jid_s (see TODO comment below).
195 @param room_jid (jid.JID): the JID of the room hosting the game 195 @param room_jid (jid.JID): the JID of the room hosting the game
196 @param user_jid (jid.JID): JID of the user 196 @param user_jid (jid.JID): JID of the user
197 @param nick (unicode): nick of the user 197 @param nick (unicode): nick of the user
198 @return: True if this profile can join the game 198 @return: True if this profile can join the game
199 """ 199 """
200 auth = False 200 auth = False
201 if not self._gameExists(room_jid): 201 if not self._game_exists(room_jid):
202 auth = False 202 auth = False
203 elif self.join_mode == self.ALL or self.isPlayer(room_jid, nick): 203 elif self.join_mode == self.ALL or self.is_player(room_jid, nick):
204 auth = True 204 auth = True
205 elif self.join_mode == self.INVITED: 205 elif self.join_mode == self.INVITED:
206 # considering all the batches of invitations 206 # considering all the batches of invitations
207 for invitations in self.invitations[room_jid]: 207 for invitations in self.invitations[room_jid]:
208 if user_jid is not None: 208 if user_jid is not None:
225 "room": room_jid.userhost(), 225 "room": room_jid.userhost(),
226 } 226 }
227 ) 227 )
228 return auth 228 return auth
229 229
230 def _updatePlayers(self, room_jid, nicks, sync, profile): 230 def _update_players(self, room_jid, nicks, sync, profile):
231 """Update the list of players and signal to the room that some players joined the game. 231 """Update the list of players and signal to the room that some players joined the game.
232 If sync is True, the news players are synchronized with the game data they have missed. 232 If sync is True, the news players are synchronized with the game data they have missed.
233 Remark: self.games[room_jid]['players'] should not be modified outside this method. 233 Remark: self.games[room_jid]['players'] should not be modified outside this method.
234 @param room_jid (jid.JID): JID of the room 234 @param room_jid (jid.JID): JID of the room
235 @param nicks (list[unicode]): list of players nicks in the room (referee included, in first position) 235 @param nicks (list[unicode]): list of players nicks in the room (referee included, in first position)
249 for nick in new_nicks: 249 for nick in new_nicks:
250 self.games[room_jid]["status"][nick] = status 250 self.games[room_jid]["status"][nick] = status
251 251
252 sync = ( 252 sync = (
253 sync 253 sync
254 and self._gameExists(room_jid, True) 254 and self._game_exists(room_jid, True)
255 and len(self.games[room_jid]["players"]) > 0 255 and len(self.games[room_jid]["players"]) > 0
256 ) 256 )
257 setStatus("desync" if sync else "init") 257 setStatus("desync" if sync else "init")
258 self.games[room_jid]["players"].extend(new_nicks) 258 self.games[room_jid]["players"].extend(new_nicks)
259 self._synchronizeRoom(room_jid, [room_jid], profile) 259 self._synchronize_room(room_jid, [room_jid], profile)
260 if sync: 260 if sync:
261 setStatus("init") 261 setStatus("init")
262 262
263 def _synchronizeRoom(self, room_jid, recipients, profile): 263 def _synchronize_room(self, room_jid, recipients, profile):
264 """Communicate the list of players to the whole room or only to some users, 264 """Communicate the list of players to the whole room or only to some users,
265 also send the synchronization data to the players who recently joined the game. 265 also send the synchronization data to the players who recently joined the game.
266 @param room_jid (jid.JID): JID of the room 266 @param room_jid (jid.JID): JID of the room
267 @recipients (list[jid.JID]): list of JIDs, the recipients of the message could be: 267 @recipients (list[jid.JID]): list of JIDs, the recipients of the message could be:
268 - room JID 268 - room JID
269 - room JID + "/" + user nick 269 - room JID + "/" + user nick
270 @param profile (unicode): %(doc_profile)s 270 @param profile (unicode): %(doc_profile)s
271 """ 271 """
272 if self._gameExists(room_jid, started=True): 272 if self._game_exists(room_jid, started=True):
273 element = self._createStartElement(self.games[room_jid]["players"]) 273 element = self._create_start_element(self.games[room_jid]["players"])
274 else: 274 else:
275 element = self._createStartElement( 275 element = self._create_start_element(
276 self.games[room_jid]["players"], name="players" 276 self.games[room_jid]["players"], name="players"
277 ) 277 )
278 elements = [(element, None, None)] 278 elements = [(element, None, None)]
279 279
280 sync_args = [] 280 sync_args = []
281 sync_data = self._getSyncData(room_jid) 281 sync_data = self._get_sync_data(room_jid)
282 for nick in sync_data: 282 for nick in sync_data:
283 user_jid = jid.JID(room_jid.userhost() + "/" + nick) 283 user_jid = jid.JID(room_jid.userhost() + "/" + nick)
284 if user_jid in recipients: 284 if user_jid in recipients:
285 user_elements = copy.deepcopy(elements) 285 user_elements = copy.deepcopy(elements)
286 for child in sync_data[nick]: 286 for child in sync_data[nick]:
289 else: 289 else:
290 user_elements = [(child, None, None) for child in sync_data[nick]] 290 user_elements = [(child, None, None) for child in sync_data[nick]]
291 sync_args.append(([user_jid, user_elements], {"profile": profile})) 291 sync_args.append(([user_jid, user_elements], {"profile": profile}))
292 292
293 for recipient in recipients: 293 for recipient in recipients:
294 self._sendElements(recipient, elements, profile=profile) 294 self._send_elements(recipient, elements, profile=profile)
295 for args, kwargs in sync_args: 295 for args, kwargs in sync_args:
296 self._sendElements(*args, **kwargs) 296 self._send_elements(*args, **kwargs)
297 297
298 def _getSyncData(self, room_jid, force_nicks=None): 298 def _get_sync_data(self, room_jid, force_nicks=None):
299 """The synchronization data are returned for each player who 299 """The synchronization data are returned for each player who
300 has the state 'desync' or if he's been contained by force_nicks. 300 has the state 'desync' or if he's been contained by force_nicks.
301 @param room_jid (jid.JID): JID of the room 301 @param room_jid (jid.JID): JID of the room
302 @param force_nicks: force the synchronization for this list of the nicks 302 @param force_nicks: force the synchronization for this list of the nicks
303 @return: a mapping between player nicks and a list of elements to 303 @return: a mapping between player nicks and a list of elements to
304 be sent by self._synchronizeRoom for the game to be synchronized. 304 be sent by self._synchronize_room for the game to be synchronized.
305 """ 305 """
306 if not self._gameExists(room_jid): 306 if not self._game_exists(room_jid):
307 return {} 307 return {}
308 data = {} 308 data = {}
309 status = self.games[room_jid]["status"] 309 status = self.games[room_jid]["status"]
310 nicks = [nick for nick in status if status[nick] == "desync"] 310 nicks = [nick for nick in status if status[nick] == "desync"]
311 if force_nicks is None: 311 if force_nicks is None:
312 force_nicks = [] 312 force_nicks = []
313 for nick in force_nicks: 313 for nick in force_nicks:
314 if nick not in nicks: 314 if nick not in nicks:
315 nicks.append(nick) 315 nicks.append(nick)
316 for nick in nicks: 316 for nick in nicks:
317 elements = self.getSyncDataForPlayer(room_jid, nick) 317 elements = self.get_sync_data_for_player(room_jid, nick)
318 if elements: 318 if elements:
319 data[nick] = elements 319 data[nick] = elements
320 return data 320 return data
321 321
322 def getSyncDataForPlayer(self, room_jid, nick): 322 def get_sync_data_for_player(self, room_jid, nick):
323 """This method may (and should probably) be overwritten by a child class. 323 """This method may (and should probably) be overwritten by a child class.
324 @param room_jid (jid.JID): JID of the room 324 @param room_jid (jid.JID): JID of the room
325 @param nick: the nick of the player to be synchronized 325 @param nick: the nick of the player to be synchronized
326 @return: a list of elements to synchronize this player with the game. 326 @return: a list of elements to synchronize this player with the game.
327 """ 327 """
328 return [] 328 return []
329 329
330 def _invitePlayers(self, room_jid, other_players, nick, profile): 330 def _invite_players(self, room_jid, other_players, nick, profile):
331 """Invite players to a room, associated game may exist or not. 331 """Invite players to a room, associated game may exist or not.
332 332
333 @param other_players (list[jid.JID]): list of the players to invite 333 @param other_players (list[jid.JID]): list of the players to invite
334 @param nick (unicode): nick of the user who send the invitation 334 @param nick (unicode): nick of the user who send the invitation
335 @return: list[unicode] of room nicks for invited players who are already in the room 335 @return: list[unicode] of room nicks for invited players who are already in the room
336 """ 336 """
337 raise NotImplementedError("Need to be fixed !") 337 raise NotImplementedError("Need to be fixed !")
338 # FIXME: this is broken and unsecure ! 338 # FIXME: this is broken and unsecure !
339 if not self._checkInviteAuth(room_jid, nick): 339 if not self._check_invite_auth(room_jid, nick):
340 return [] 340 return []
341 # TODO: remove invitation waiting for too long, using the time data 341 # TODO: remove invitation waiting for too long, using the time data
342 self.invitations[room_jid].append( 342 self.invitations[room_jid].append(
343 (time(), [player.userhostJID() for player in other_players]) 343 (time(), [player.userhostJID() for player in other_players])
344 ) 344 )
354 ) 354 )
355 else: 355 else:
356 nicks.append(other_nick) 356 nicks.append(other_nick)
357 return nicks 357 return nicks
358 358
359 def _checkInviteAuth(self, room_jid, nick, verbose=False): 359 def _check_invite_auth(self, room_jid, nick, verbose=False):
360 """Checks if this user is allowed to invite players 360 """Checks if this user is allowed to invite players
361 361
362 @param room_jid (jid.JID): JID of the room 362 @param room_jid (jid.JID): JID of the room
363 @param nick: user nick in the room 363 @param nick: user nick in the room
364 @param verbose: display debug message 364 @param verbose: display debug message
365 @return: True if the user is allowed to invite other players 365 @return: True if the user is allowed to invite other players
366 """ 366 """
367 auth = False 367 auth = False
368 if self.invite_mode == self.FROM_ALL or not self._gameExists(room_jid): 368 if self.invite_mode == self.FROM_ALL or not self._game_exists(room_jid):
369 auth = True 369 auth = True
370 elif self.invite_mode == self.FROM_NONE: 370 elif self.invite_mode == self.FROM_NONE:
371 auth = not self._gameExists(room_jid, started=True) and self.isReferee( 371 auth = not self._game_exists(room_jid, started=True) and self.is_referee(
372 room_jid, nick 372 room_jid, nick
373 ) 373 )
374 elif self.invite_mode == self.FROM_REFEREE: 374 elif self.invite_mode == self.FROM_REFEREE:
375 auth = self.isReferee(room_jid, nick) 375 auth = self.is_referee(room_jid, nick)
376 elif self.invite_mode == self.FROM_PLAYERS: 376 elif self.invite_mode == self.FROM_PLAYERS:
377 auth = self.isPlayer(room_jid, nick) 377 auth = self.is_player(room_jid, nick)
378 if not auth and (verbose or _DEBUG): 378 if not auth and (verbose or _DEBUG):
379 log.debug( 379 log.debug(
380 _("%(user)s not allowed to invite for the game %(game)s in %(room)s") 380 _("%(user)s not allowed to invite for the game %(game)s in %(room)s")
381 % {"user": nick, "game": self.name, "room": room_jid.userhost()} 381 % {"user": nick, "game": self.name, "room": room_jid.userhost()}
382 ) 382 )
383 return auth 383 return auth
384 384
385 def isReferee(self, room_jid, nick): 385 def is_referee(self, room_jid, nick):
386 """Checks if the player with this nick is the referee for the game in this room" 386 """Checks if the player with this nick is the referee for the game in this room"
387 @param room_jid (jid.JID): room JID 387 @param room_jid (jid.JID): room JID
388 @param nick: user nick in the room 388 @param nick: user nick in the room
389 @return: True if the user is the referee of the game in this room 389 @return: True if the user is the referee of the game in this room
390 """ 390 """
391 if not self._gameExists(room_jid): 391 if not self._game_exists(room_jid):
392 return False 392 return False
393 return ( 393 return (
394 jid.JID(room_jid.userhost() + "/" + nick) == self.games[room_jid]["referee"] 394 jid.JID(room_jid.userhost() + "/" + nick) == self.games[room_jid]["referee"]
395 ) 395 )
396 396
397 def isPlayer(self, room_jid, nick): 397 def is_player(self, room_jid, nick):
398 """Checks if the user with this nick is a player for the game in this room. 398 """Checks if the user with this nick is a player for the game in this room.
399 @param room_jid (jid.JID): JID of the room 399 @param room_jid (jid.JID): JID of the room
400 @param nick: user nick in the room 400 @param nick: user nick in the room
401 @return: True if the user is a player of the game in this room 401 @return: True if the user is a player of the game in this room
402 """ 402 """
403 if not self._gameExists(room_jid): 403 if not self._game_exists(room_jid):
404 return False 404 return False
405 # Important: the referee is not in the 'players' list right after 405 # Important: the referee is not in the 'players' list right after
406 # the game initialization, that's why we do also check with isReferee 406 # the game initialization, that's why we do also check with is_referee
407 return nick in self.games[room_jid]["players"] or self.isReferee(room_jid, nick) 407 return nick in self.games[room_jid]["players"] or self.is_referee(room_jid, nick)
408 408
409 def _checkWaitAuth(self, room, other_players, verbose=False): 409 def _check_wait_auth(self, room, other_players, verbose=False):
410 """Check if we must wait for other players before starting the game. 410 """Check if we must wait for other players before starting the game.
411 411
412 @param room (wokkel.muc.Room): the room 412 @param room (wokkel.muc.Room): the room
413 @param other_players (list[jid.JID]): list of the players without the referee 413 @param other_players (list[jid.JID]): list of the players without the referee
414 @param verbose (bool): display debug message 414 @param verbose (bool): display debug message
439 "room": room.occupantJID.userhost(), 439 "room": room.occupantJID.userhost(),
440 } 440 }
441 ) 441 )
442 return result 442 return result
443 443
444 def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE): 444 def get_unique_name(self, muc_service=None, profile_key=C.PROF_KEY_NONE):
445 """Generate unique room name 445 """Generate unique room name
446 446
447 @param muc_service (jid.JID): you can leave empty to autofind the muc service 447 @param muc_service (jid.JID): you can leave empty to autofind the muc service
448 @param profile_key (unicode): %(doc_profile_key)s 448 @param profile_key (unicode): %(doc_profile_key)s
449 @return: jid.JID (unique name for a new room to be created) 449 @return: jid.JID (unique name for a new room to be created)
450 """ 450 """
451 client = self.host.getClient(profile_key) 451 client = self.host.get_client(profile_key)
452 # FIXME: jid.JID must be used instead of strings 452 # FIXME: jid.JID must be used instead of strings
453 room = self.host.plugins["XEP-0045"].getUniqueName(client, muc_service) 453 room = self.host.plugins["XEP-0045"].get_unique_name(client, muc_service)
454 return jid.JID("sat_%s_%s" % (self.name.lower(), room.userhost())) 454 return jid.JID("sat_%s_%s" % (self.name.lower(), room.userhost()))
455 455
456 def _prepareRoom( 456 def _prepare_room(
457 self, other_players=None, room_jid_s="", profile_key=C.PROF_KEY_NONE 457 self, other_players=None, room_jid_s="", profile_key=C.PROF_KEY_NONE
458 ): 458 ):
459 room_jid = jid.JID(room_jid_s) if room_jid_s else None 459 room_jid = jid.JID(room_jid_s) if room_jid_s else None
460 other_players = [jid.JID(player).userhostJID() for player in other_players] 460 other_players = [jid.JID(player).userhostJID() for player in other_players]
461 return self.prepareRoom(other_players, room_jid, profile_key) 461 return self.prepare_room(other_players, room_jid, profile_key)
462 462
463 def prepareRoom(self, other_players=None, room_jid=None, profile_key=C.PROF_KEY_NONE): 463 def prepare_room(self, other_players=None, room_jid=None, profile_key=C.PROF_KEY_NONE):
464 """Prepare the room for a game: create it if it doesn't exist and invite players. 464 """Prepare the room for a game: create it if it doesn't exist and invite players.
465 465
466 @param other_players (list[JID]): list of other players JID (bare) 466 @param other_players (list[JID]): list of other players JID (bare)
467 @param room_jid (jid.JID): JID of the room, or None to generate a unique name 467 @param room_jid (jid.JID): JID of the room, or None to generate a unique name
468 @param profile_key (unicode): %(doc_profile_key)s 468 @param profile_key (unicode): %(doc_profile_key)s
469 """ 469 """
470 # FIXME: need to be refactored 470 # FIXME: need to be refactored
471 client = self.host.getClient(profile_key) 471 client = self.host.get_client(profile_key)
472 log.debug(_("Preparing room for %s game") % self.name) 472 log.debug(_("Preparing room for %s game") % self.name)
473 profile = self.host.memory.getProfileName(profile_key) 473 profile = self.host.memory.get_profile_name(profile_key)
474 if not profile: 474 if not profile:
475 log.error(_("Unknown profile")) 475 log.error(_("Unknown profile"))
476 return defer.succeed(None) 476 return defer.succeed(None)
477 if other_players is None: 477 if other_players is None:
478 other_players = [] 478 other_players = []
479 479
480 # Create/join the given room, or a unique generated one if no room is specified. 480 # Create/join the given room, or a unique generated one if no room is specified.
481 if room_jid is None: 481 if room_jid is None:
482 room_jid = self.getUniqueName(profile_key=profile_key) 482 room_jid = self.get_unique_name(profile_key=profile_key)
483 else: 483 else:
484 self.host.plugins["XEP-0045"].checkRoomJoined(client, room_jid) 484 self.host.plugins["XEP-0045"].check_room_joined(client, room_jid)
485 self._createOrInvite(client, room_jid, other_players) 485 self._create_or_invite(client, room_jid, other_players)
486 return defer.succeed(None) 486 return defer.succeed(None)
487 487
488 user_jid = self.host.getJidNStream(profile)[0] 488 user_jid = self.host.get_jid_n_stream(profile)[0]
489 d = self.host.plugins["XEP-0045"].join(room_jid, user_jid.user, {}, profile) 489 d = self.host.plugins["XEP-0045"].join(room_jid, user_jid.user, {}, profile)
490 return d.addCallback( 490 return d.addCallback(
491 lambda __: self._createOrInvite(client, room_jid, other_players) 491 lambda __: self._create_or_invite(client, room_jid, other_players)
492 ) 492 )
493 493
494 def userJoinedTrigger(self, room, user, profile): 494 def user_joined_trigger(self, room, user, profile):
495 """This trigger is used to check if the new user can take part of a game, create the game if we were waiting for him or just update the players list. 495 """This trigger is used to check if the new user can take part of a game, create the game if we were waiting for him or just update the players list.
496 496
497 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} 497 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User}
498 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID 498 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID
499 @return: True to not interrupt the main process. 499 @return: True to not interrupt the main process.
500 """ 500 """
501 room_jid = room.occupantJID.userhostJID() 501 room_jid = room.occupantJID.userhostJID()
502 profile_nick = room.occupantJID.resource 502 profile_nick = room.occupantJID.resource
503 if not self.isReferee(room_jid, profile_nick): 503 if not self.is_referee(room_jid, profile_nick):
504 return True # profile is not the referee 504 return True # profile is not the referee
505 if not self._checkJoinAuth( 505 if not self._check_join_auth(
506 room_jid, user.entity if user.entity else None, user.nick 506 room_jid, user.entity if user.entity else None, user.nick
507 ): 507 ):
508 # user not allowed but let him know that we are playing :p 508 # user not allowed but let him know that we are playing :p
509 self._synchronizeRoom( 509 self._synchronize_room(
510 room_jid, [jid.JID(room_jid.userhost() + "/" + user.nick)], profile 510 room_jid, [jid.JID(room_jid.userhost() + "/" + user.nick)], profile
511 ) 511 )
512 return True 512 return True
513 if self.wait_mode == self.FOR_ALL: 513 if self.wait_mode == self.FOR_ALL:
514 # considering the last batch of invitations 514 # considering the last batch of invitations
518 "Invitations from %s to play %s in %s have been lost!" 518 "Invitations from %s to play %s in %s have been lost!"
519 % (profile_nick, self.name, room_jid.userhost()) 519 % (profile_nick, self.name, room_jid.userhost())
520 ) 520 )
521 return True 521 return True
522 other_players = self.invitations[room_jid][batch][1] 522 other_players = self.invitations[room_jid][batch][1]
523 (auth, nicks, __) = self._checkWaitAuth(room, other_players) 523 (auth, nicks, __) = self._check_wait_auth(room, other_players)
524 if auth: 524 if auth:
525 del self.invitations[room_jid][batch] 525 del self.invitations[room_jid][batch]
526 nicks.insert(0, profile_nick) # add the referee 526 nicks.insert(0, profile_nick) # add the referee
527 self.createGame(room_jid, nicks, profile_key=profile) 527 self.create_game(room_jid, nicks, profile_key=profile)
528 return True 528 return True
529 # let the room know that a new player joined 529 # let the room know that a new player joined
530 self._updatePlayers(room_jid, [user.nick], True, profile) 530 self._update_players(room_jid, [user.nick], True, profile)
531 return True 531 return True
532 532
533 def userLeftTrigger(self, room, user, profile): 533 def user_left_trigger(self, room, user, profile):
534 """This trigger is used to update or stop the game when a user leaves. 534 """This trigger is used to update or stop the game when a user leaves.
535 535
536 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} 536 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User}
537 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID 537 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID
538 @return: True to not interrupt the main process. 538 @return: True to not interrupt the main process.
539 """ 539 """
540 room_jid = room.occupantJID.userhostJID() 540 room_jid = room.occupantJID.userhostJID()
541 profile_nick = room.occupantJID.resource 541 profile_nick = room.occupantJID.resource
542 if not self.isReferee(room_jid, profile_nick): 542 if not self.is_referee(room_jid, profile_nick):
543 return True # profile is not the referee 543 return True # profile is not the referee
544 if self.isPlayer(room_jid, user.nick): 544 if self.is_player(room_jid, user.nick):
545 try: 545 try:
546 self.games[room_jid]["players"].remove(user.nick) 546 self.games[room_jid]["players"].remove(user.nick)
547 except ValueError: 547 except ValueError:
548 pass 548 pass
549 if len(self.games[room_jid]["players"]) == 0: 549 if len(self.games[room_jid]["players"]) == 0:
557 batch = 0 # add to the first batch of invitations 557 batch = 0 # add to the first batch of invitations
558 if user_jid not in self.invitations[room_jid][batch][1]: 558 if user_jid not in self.invitations[room_jid][batch][1]:
559 self.invitations[room_jid][batch][1].append(user_jid) 559 self.invitations[room_jid][batch][1].append(user_jid)
560 return True 560 return True
561 561
562 def _checkCreateGameAndInit(self, room_jid, profile): 562 def _check_create_game_and_init(self, room_jid, profile):
563 """Check if that profile can create the game. If the game can be created 563 """Check if that profile can create the game. If the game can be created
564 but is not initialized yet, this method will also do the initialization. 564 but is not initialized yet, this method will also do the initialization.
565 565
566 @param room_jid (jid.JID): JID of the room 566 @param room_jid (jid.JID): JID of the room
567 @param profile 567 @param profile
568 @return: a couple (create, sync) with: 568 @return: a couple (create, sync) with:
569 - create: set to True to allow the game creation 569 - create: set to True to allow the game creation
570 - sync: set to True to advice a game synchronization 570 - sync: set to True to advice a game synchronization
571 """ 571 """
572 user_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid, profile) 572 user_nick = self.host.plugins["XEP-0045"].get_room_nick(room_jid, profile)
573 if not user_nick: 573 if not user_nick:
574 log.error( 574 log.error(
575 "Internal error: profile %s has not joined the room %s" 575 "Internal error: profile %s has not joined the room %s"
576 % (profile, room_jid.userhost()) 576 % (profile, room_jid.userhost())
577 ) 577 )
578 return False, False 578 return False, False
579 if self._gameExists(room_jid): 579 if self._game_exists(room_jid):
580 is_referee = self.isReferee(room_jid, user_nick) 580 is_referee = self.is_referee(room_jid, user_nick)
581 if self._gameExists(room_jid, started=True): 581 if self._game_exists(room_jid, started=True):
582 log.info( 582 log.info(
583 _("%(game)s game already created in room %(room)s") 583 _("%(game)s game already created in room %(room)s")
584 % {"game": self.name, "room": room_jid.userhost()} 584 % {"game": self.name, "room": room_jid.userhost()}
585 ) 585 )
586 return False, is_referee 586 return False, is_referee
589 _("%(game)s game in room %(room)s can only be created by %(user)s") 589 _("%(game)s game in room %(room)s can only be created by %(user)s")
590 % {"game": self.name, "room": room_jid.userhost(), "user": user_nick} 590 % {"game": self.name, "room": room_jid.userhost(), "user": user_nick}
591 ) 591 )
592 return False, False 592 return False, False
593 else: 593 else:
594 self._initGame(room_jid, user_nick) 594 self._init_game(room_jid, user_nick)
595 return True, False 595 return True, False
596 596
597 def _createGame(self, room_jid_s, nicks=None, profile_key=C.PROF_KEY_NONE): 597 def _create_game(self, room_jid_s, nicks=None, profile_key=C.PROF_KEY_NONE):
598 self.createGame(jid.JID(room_jid_s), nicks, profile_key) 598 self.create_game(jid.JID(room_jid_s), nicks, profile_key)
599 599
600 def createGame(self, room_jid, nicks=None, profile_key=C.PROF_KEY_NONE): 600 def create_game(self, room_jid, nicks=None, profile_key=C.PROF_KEY_NONE):
601 """Create a new game. 601 """Create a new game.
602 602
603 This can be called directly from a frontend and skips all the checks and invitation system, 603 This can be called directly from a frontend and skips all the checks and invitation system,
604 but the game must not exist and all the players must be in the room already. 604 but the game must not exist and all the players must be in the room already.
605 @param room_jid (jid.JID): JID of the room 605 @param room_jid (jid.JID): JID of the room
608 """ 608 """
609 log.debug( 609 log.debug(
610 _("Creating %(game)s game in room %(room)s") 610 _("Creating %(game)s game in room %(room)s")
611 % {"game": self.name, "room": room_jid} 611 % {"game": self.name, "room": room_jid}
612 ) 612 )
613 profile = self.host.memory.getProfileName(profile_key) 613 profile = self.host.memory.get_profile_name(profile_key)
614 if not profile: 614 if not profile:
615 log.error(_("profile %s is unknown") % profile_key) 615 log.error(_("profile %s is unknown") % profile_key)
616 return 616 return
617 (create, sync) = self._checkCreateGameAndInit(room_jid, profile) 617 (create, sync) = self._check_create_game_and_init(room_jid, profile)
618 if nicks is None: 618 if nicks is None:
619 nicks = [] 619 nicks = []
620 if not create: 620 if not create:
621 if sync: 621 if sync:
622 self._updatePlayers(room_jid, nicks, True, profile) 622 self._update_players(room_jid, nicks, True, profile)
623 return 623 return
624 self.games[room_jid]["started"] = True 624 self.games[room_jid]["started"] = True
625 self._updatePlayers(room_jid, nicks, False, profile) 625 self._update_players(room_jid, nicks, False, profile)
626 if self.player_init: 626 if self.player_init:
627 # specific data to each player (score, private data) 627 # specific data to each player (score, private data)
628 self.games[room_jid].setdefault("players_data", {}) 628 self.games[room_jid].setdefault("players_data", {})
629 for nick in nicks: 629 for nick in nicks:
630 # The dict must be COPIED otherwise it is shared between all users 630 # The dict must be COPIED otherwise it is shared between all users
631 self.games[room_jid]["players_data"][nick] = copy.deepcopy( 631 self.games[room_jid]["players_data"][nick] = copy.deepcopy(
632 self.player_init 632 self.player_init
633 ) 633 )
634 634
635 def _playerReady(self, player_nick, referee_jid_s, profile_key=C.PROF_KEY_NONE): 635 def _player_ready(self, player_nick, referee_jid_s, profile_key=C.PROF_KEY_NONE):
636 self.playerReady(player_nick, jid.JID(referee_jid_s), profile_key) 636 self.player_ready(player_nick, jid.JID(referee_jid_s), profile_key)
637 637
638 def playerReady(self, player_nick, referee_jid, profile_key=C.PROF_KEY_NONE): 638 def player_ready(self, player_nick, referee_jid, profile_key=C.PROF_KEY_NONE):
639 """Must be called when player is ready to start a new game 639 """Must be called when player is ready to start a new game
640 640
641 @param player: the player nick in the room 641 @param player: the player nick in the room
642 @param referee_jid (jid.JID): JID of the referee 642 @param referee_jid (jid.JID): JID of the referee
643 """ 643 """
644 profile = self.host.memory.getProfileName(profile_key) 644 profile = self.host.memory.get_profile_name(profile_key)
645 if not profile: 645 if not profile:
646 log.error(_("profile %s is unknown") % profile_key) 646 log.error(_("profile %s is unknown") % profile_key)
647 return 647 return
648 log.debug("new player ready: %s" % profile) 648 log.debug("new player ready: %s" % profile)
649 # TODO: we probably need to add the game and room names in the sent message 649 # TODO: we probably need to add the game and room names in the sent message
650 self.send(referee_jid, "player_ready", {"player": player_nick}, profile=profile) 650 self.send(referee_jid, "player_ready", {"player": player_nick}, profile=profile)
651 651
652 def newRound(self, room_jid, data, profile): 652 def new_round(self, room_jid, data, profile):
653 """Launch a new round (reinit the user data) 653 """Launch a new round (reinit the user data)
654 654
655 @param room_jid: room userhost 655 @param room_jid: room userhost
656 @param data: a couple (common_data, msg_elts) with: 656 @param data: a couple (common_data, msg_elts) with:
657 - common_data: backend initialization data for the new round 657 - common_data: backend initialization data for the new round
679 self.send(room_jid, msg_elts, profile=profile) 679 self.send(room_jid, msg_elts, profile=profile)
680 if common_data is not None: 680 if common_data is not None:
681 for player in players: 681 for player in players:
682 players_data[player].update(copy.deepcopy(common_data)) 682 players_data[player].update(copy.deepcopy(common_data))
683 683
684 def _createGameElt(self, to_jid): 684 def _create_game_elt(self, to_jid):
685 """Create a generic domish Element for the game messages 685 """Create a generic domish Element for the game messages
686 686
687 @param to_jid: JID of the recipient 687 @param to_jid: JID of the recipient
688 @return: the created element 688 @return: the created element
689 """ 689 """
692 elt["to"] = to_jid.full() 692 elt["to"] = to_jid.full()
693 elt["type"] = type_ 693 elt["type"] = type_
694 elt.addElement(self.ns_tag) 694 elt.addElement(self.ns_tag)
695 return elt 695 return elt
696 696
697 def _createStartElement(self, players=None, name="started"): 697 def _create_start_element(self, players=None, name="started"):
698 """Create a domish Element listing the game users 698 """Create a domish Element listing the game users
699 699
700 @param players: list of the players 700 @param players: list of the players
701 @param name: element name: 701 @param name: element name:
702 - "started" to signal the players that the game has been started 702 - "started" to signal the players that the game has been started
713 player_elt["index"] = str(idx) 713 player_elt["index"] = str(idx)
714 idx += 1 714 idx += 1
715 started_elt.addChild(player_elt) 715 started_elt.addChild(player_elt)
716 return started_elt 716 return started_elt
717 717
718 def _sendElements(self, to_jid, data, profile=None): 718 def _send_elements(self, to_jid, data, profile=None):
719 """ TODO 719 """ TODO
720 720
721 @param to_jid: recipient JID 721 @param to_jid: recipient JID
722 @param data: list of (elem, attr, content) with: 722 @param data: list of (elem, attr, content) with:
723 - elem: domish.Element, unicode or a couple: 723 - elem: domish.Element, unicode or a couple:
727 - attrs: dictionary of attributes for the new child 727 - attrs: dictionary of attributes for the new child
728 - content: unicode that is appended to the child content 728 - content: unicode that is appended to the child content
729 @param profile: the profile from which the message is sent 729 @param profile: the profile from which the message is sent
730 @return: a Deferred instance 730 @return: a Deferred instance
731 """ 731 """
732 client = self.host.getClient(profile) 732 client = self.host.get_client(profile)
733 msg = self._createGameElt(to_jid) 733 msg = self._create_game_elt(to_jid)
734 for elem, attrs, content in data: 734 for elem, attrs, content in data:
735 if elem is not None: 735 if elem is not None:
736 if isinstance(elem, domish.Element): 736 if isinstance(elem, domish.Element):
737 msg.firstChildElement().addChild(elem) 737 msg.firstChildElement().addChild(elem)
738 else: 738 else:
755 @param attrs: dictionary of attributes for the new child 755 @param attrs: dictionary of attributes for the new child
756 @param content: unicode that is appended to the child content 756 @param content: unicode that is appended to the child content
757 @param profile: the profile from which the message is sent 757 @param profile: the profile from which the message is sent
758 @return: a Deferred instance 758 @return: a Deferred instance
759 """ 759 """
760 return self._sendElements(to_jid, [(elem, attrs, content)], profile) 760 return self._send_elements(to_jid, [(elem, attrs, content)], profile)
761 761
762 def getHandler(self, client): 762 def get_handler(self, client):
763 return RoomGameHandler(self) 763 return RoomGameHandler(self)
764 764
765 765
766 @implementer(iwokkel.IDisco) 766 @implementer(iwokkel.IDisco)
767 class RoomGameHandler(XMPPHandler): 767 class RoomGameHandler(XMPPHandler):