comparison src/plugins/plugin_misc_room_game.py @ 828:8f335c03eebb

plugins room_games, radiocol, libervia: small changes like refactorization to ease the maintenance
author souliane <souliane@mailoo.org>
date Fri, 17 Jan 2014 15:02:46 +0100
parents e3f4d80f987d
children f3513c8cc2e6
comparison
equal deleted inserted replaced
827:215a2cb15e2d 828:8f335c03eebb
106 self.invite_mode = self.FROM_PLAYERS if self.player_init == {} else self.FROM_NONE 106 self.invite_mode = self.FROM_PLAYERS if self.player_init == {} else self.FROM_NONE
107 self.wait_mode = self.FOR_NONE if self.player_init == {} else self.FOR_ALL 107 self.wait_mode = self.FOR_NONE if self.player_init == {} else self.FOR_ALL
108 self.join_mode = self.INVITED 108 self.join_mode = self.INVITED
109 self.ready_mode = self.FORCE # TODO: asking for confirmation is not implemented 109 self.ready_mode = self.FORCE # TODO: asking for confirmation is not implemented
110 110
111 # this has been added for testing purpose. It is sometimes needed to remove a dependence
112 # while building the synchronization data, for example to replace a call to time.time()
113 # by an arbitrary value. If needed, this attribute would be set to True from the testcase.
114 self.testing = False
115
111 host.trigger.add("MUC user joined", self.userJoinedTrigger) 116 host.trigger.add("MUC user joined", self.userJoinedTrigger)
112 host.trigger.add("MUC user left", self.userLeftTrigger) 117 host.trigger.add("MUC user left", self.userLeftTrigger)
113 118
114 def _createOrInvite(self, room, other_players, profile): 119 def _createOrInvite(self, room, other_players, profile):
115 """ 120 """
116 This is called only when someone explicitly wants to play. 121 This is called only when someone explicitly wants to play.
117 The game must not be created if one already exists in the room, 122 The game will not be created if one already exists in the room,
118 and its creation could be postponed until all the expected players 123 also its creation could be postponed until all the expected players
119 join the room (in that case it will be created from userJoinedTrigger). 124 join the room (in that case it will be created from userJoinedTrigger).
120 @param room: instance of wokkel.muc.Room 125 @param room: instance of wokkel.muc.Room
121 @param other_players: list for other players JID userhosts 126 @param other_players: list for other players JID userhosts
122 """ 127 """
123 user_jid = self.host.getJidNStream(profile)[0] 128 user_jid = self.host.getJidNStream(profile)[0]
126 nicks = [nick] 131 nicks = [nick]
127 if self._gameExists(room_jid_s): 132 if self._gameExists(room_jid_s):
128 if not self._checkJoinAuth(room_jid_s, user_jid.userhost(), nick): 133 if not self._checkJoinAuth(room_jid_s, user_jid.userhost(), nick):
129 return 134 return
130 nicks.extend(self._invitePlayers(room, other_players, nick, profile)) 135 nicks.extend(self._invitePlayers(room, other_players, nick, profile))
131 self._updatePlayers(room_jid_s, nicks, profile) 136 self._updatePlayers(room_jid_s, nicks, True, profile)
132 else: 137 else:
133 self._initGame(room_jid_s, nick) 138 self._initGame(room_jid_s, nick)
134 (auth, waiting, missing) = self._checkWaitAuth(room, other_players) 139 (auth, waiting, missing) = self._checkWaitAuth(room, other_players)
135 nicks.extend(waiting) 140 nicks.extend(waiting)
136 nicks.extend(self._invitePlayers(room, missing, nick, profile)) 141 nicks.extend(self._invitePlayers(room, missing, nick, profile))
137 if auth: 142 if auth:
138 self.createGame(room_jid_s, nicks, profile) 143 self.createGame(room_jid_s, nicks, profile)
139 else: 144 else:
140 self._updatePlayers(room_jid_s, nicks, profile) 145 self._updatePlayers(room_jid_s, nicks, False, profile)
141 146
142 def _initGame(self, room_jid_s, referee_nick): 147 def _initGame(self, room_jid_s, referee_nick):
143 """Important: do not add the referee to 'players' yet. For a 148 """Important: do not add the referee to 'players' yet. For a
144 <players /> message to be emitted whenever a new player is joining, 149 <players /> message to be emitted whenever a new player is joining,
145 it is necessary to not modify 'players' outside of _updatePlayers. 150 it is necessary to not modify 'players' outside of _updatePlayers.
189 194
190 if not auth and (verbose or _DEBUG): 195 if not auth and (verbose or _DEBUG):
191 debug(_("%s not allowed to join the game %s in %s") % (user_jid_s or nick, self.name, room_jid_s)) 196 debug(_("%s not allowed to join the game %s in %s") % (user_jid_s or nick, self.name, room_jid_s))
192 return auth 197 return auth
193 198
194 def _updatePlayers(self, room_jid_s, nicks, profile): 199 def _updatePlayers(self, room_jid_s, nicks, sync, profile):
195 """Update the list of players and signal to the room that some players joined the game. 200 """Update the list of players and signal to the room that some players joined the game.
201 If sync is True, the news players are synchronized with the game data they have missed.
196 Remark: self.games[room_jid_s]['players'] should not be modified outside this method. 202 Remark: self.games[room_jid_s]['players'] should not be modified outside this method.
197 @param room_jid_s: room userhost 203 @param room_jid_s: room userhost
198 @param nicks: list of players nicks in the room (referee included, in first position) 204 @param nicks: list of players nicks in the room (referee included, in first position)
205 @param sync: set to True to send synchronization data to the new players
206 @param profile
199 """ 207 """
200 if nicks == []: 208 if nicks == []:
201 return 209 return
202 # this is better than set(nicks).difference(...) as it keeps the order 210 # this is better than set(nicks).difference(...) as it keeps the order
203 new_nicks = [nick for nick in nicks if nick not in self.games[room_jid_s]['players']] 211 new_nicks = [nick for nick in nicks if nick not in self.games[room_jid_s]['players']]
204 if len(new_nicks) == 0: 212 if len(new_nicks) == 0:
205 return 213 return
206 sync = self._gameExists(room_jid_s, True) and len(self.games[room_jid_s]['players']) > 0
207 214
208 def setStatus(status): 215 def setStatus(status):
209 for nick in new_nicks: 216 for nick in new_nicks:
210 self.games[room_jid_s]['status'][nick] = status 217 self.games[room_jid_s]['status'][nick] = status
211 218
219 sync = sync and self._gameExists(room_jid_s, True) and len(self.games[room_jid_s]['players']) > 0
212 setStatus('desync' if sync else 'init') 220 setStatus('desync' if sync else 'init')
213 self.games[room_jid_s]['players'].extend(new_nicks) 221 self.games[room_jid_s]['players'].extend(new_nicks)
214 self._synchronizeRoom(room_jid_s, [JID(room_jid_s)], profile) 222 self._synchronizeRoom(room_jid_s, [JID(room_jid_s)], profile)
215 if sync: 223 if sync:
216 setStatus('init') 224 setStatus('init')
229 else: 237 else:
230 element = self._createStartElement(self.games[room_jid_s]['players'], name="players") 238 element = self._createStartElement(self.games[room_jid_s]['players'], name="players")
231 elements = [(element, None, None)] 239 elements = [(element, None, None)]
232 240
233 sync_args = [] 241 sync_args = []
234 sync_data = self.getSyncData(room_jid_s) 242 sync_data = self._getSyncData(room_jid_s)
235 for nick in sync_data: 243 for nick in sync_data:
236 user_jid = JID(room_jid_s + '/' + nick) 244 user_jid = JID(room_jid_s + '/' + nick)
237 if user_jid in recipients: 245 if user_jid in recipients:
238 user_elements = copy.deepcopy(elements) 246 user_elements = copy.deepcopy(elements)
239 for child in sync_data[nick]: 247 for child in sync_data[nick]:
246 for recipient in recipients: 254 for recipient in recipients:
247 self._sendElements(recipient, elements, profile=profile) 255 self._sendElements(recipient, elements, profile=profile)
248 for args, kwargs in sync_args: 256 for args, kwargs in sync_args:
249 self._sendElements(*args, **kwargs) 257 self._sendElements(*args, **kwargs)
250 258
251 def getSyncData(self, room_jid_s, force_nicks=[]): 259 def _getSyncData(self, room_jid_s, force_nicks=[]):
260 """The synchronization data are returned for each player who
261 has the state 'desync' or if he's been contained by force_nicks.
262 @param room_jid_s: room userhost
263 @param force_nicks: force the synchronization for this list of the nicks
264 @return: a mapping between player nicks and a list of elements to
265 be sent by self._synchronizeRoom for the game to be synchronized.
266 """
267 if not self._gameExists(room_jid_s):
268 return {}
269 data = {}
270 status = self.games[room_jid_s]['status']
271 nicks = [nick for nick in status if status[nick] == 'desync']
272 for nick in force_nicks:
273 if nick not in nicks:
274 nicks.append(nick)
275 for nick in nicks:
276 elements = self.getSyncDataForPlayer(room_jid_s, nick)
277 if elements:
278 data[nick] = elements
279 return data
280
281 def getSyncDataForPlayer(self, room_jid_s, nick):
252 """This method may (and should probably) be overwritten by a child class. 282 """This method may (and should probably) be overwritten by a child class.
253 The synchronization data are returned for each player who has the state 283 @param room_jid_s: room userhost
254 'desync' or if he's been contained by force_nicks. 284 @param nick: the nick of the player to be synchronized
255 @param room_jid_s: room userhost 285 @return: a list of elements to synchronize this player with the game.
256 @param force_nicks: force the synchronization for this list of the nicks 286 """
257 @return: a mapping between player nicks and a list of child elements 287 return []
258 to be sent by self._synchronizeRoom for the game to be synchronized.
259 """
260 return {}
261 288
262 def _invitePlayers(self, room, other_players, nick, profile): 289 def _invitePlayers(self, room, other_players, nick, profile):
263 """Invite players to a room, associated game may exist or not. 290 """Invite players to a room, associated game may exist or not.
264 @param room: wokkel.muc.Room instance 291 @param room: wokkel.muc.Room instance
265 @param other_players: list of JID userhosts to invite 292 @param other_players: list of JID userhosts to invite
422 del self.invitations[room_jid_s][batch] 449 del self.invitations[room_jid_s][batch]
423 nicks.insert(0, profile_nick) # add the referee 450 nicks.insert(0, profile_nick) # add the referee
424 self.createGame(room_jid_s, nicks, profile_key=profile) 451 self.createGame(room_jid_s, nicks, profile_key=profile)
425 return True 452 return True
426 # let the room know that a new player joined 453 # let the room know that a new player joined
427 self._updatePlayers(room_jid_s, [user.nick], profile) 454 self._updatePlayers(room_jid_s, [user.nick], True, profile)
428 return True 455 return True
429 456
430 def userLeftTrigger(self, room, user, profile): 457 def userLeftTrigger(self, room, user, profile):
431 """This trigger is used to update or stop the game when a user leaves. 458 """This trigger is used to update or stop the game when a user leaves.
432 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} 459 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User}
440 if self.isPlayer(room_jid_s, user.nick): 467 if self.isPlayer(room_jid_s, user.nick):
441 try: 468 try:
442 self.games[room_jid_s]['players'].remove(user.nick) 469 self.games[room_jid_s]['players'].remove(user.nick)
443 except ValueError: 470 except ValueError:
444 pass 471 pass
472 if len(self.games[room_jid_s]['players']) == 0:
473 del self.games[room_jid_s] # finish the game
474 return True
445 if self.wait_mode == self.FOR_ALL: 475 if self.wait_mode == self.FOR_ALL:
446 # allow this user to join the game again 476 # allow this user to join the game again
447 user_jid_s = user.entity.userhost() 477 user_jid_s = user.entity.userhost()
448 if len(self.invitations[room_jid_s]) == 0: 478 if len(self.invitations[room_jid_s]) == 0:
449 self.invitations[room_jid_s].append((time(), [user_jid_s])) 479 self.invitations[room_jid_s].append((time(), [user_jid_s]))
491 error(_("profile %s is unknown") % profile_key) 521 error(_("profile %s is unknown") % profile_key)
492 return 522 return
493 (create, sync) = self._checkCreateGameAndInit(room_jid_s, profile) 523 (create, sync) = self._checkCreateGameAndInit(room_jid_s, profile)
494 if not create: 524 if not create:
495 if sync: 525 if sync:
496 debug(_('Synchronize game %s in %s for %s') % (self.name, room_jid_s, ', '.join(nicks))) 526 self._updatePlayers(room_jid_s, nicks, True, profile)
497 self._updatePlayers(room_jid_s, nicks, profile)
498 return 527 return
499 self.games[room_jid_s]['started'] = True 528 self.games[room_jid_s]['started'] = True
500 self._updatePlayers(room_jid_s, nicks, profile) 529 self._updatePlayers(room_jid_s, nicks, False, profile)
501 if self.player_init: 530 if self.player_init:
502 # specific data to each player (score, private data) 531 # specific data to each player (score, private data)
503 self.games[room_jid_s].setdefault('players_data', {}) 532 self.games[room_jid_s].setdefault('players_data', {})
504 for nick in nicks: 533 for nick in nicks:
505 # The dict must be COPIED otherwise it is shared between all users 534 # The dict must be COPIED otherwise it is shared between all users