Mercurial > libervia-backend
comparison src/plugins/plugin_misc_room_game.py @ 825:e3f4d80f987d
plugins room_games, radiocol: better synchronization after a user joins a running game
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 15 Jan 2014 23:01:23 +0100 |
parents | 1fe00f0c9a91 |
children | 8f335c03eebb |
comparison
equal
deleted
inserted
replaced
824:c304ce32042b | 825:e3f4d80f987d |
---|---|
143 """Important: do not add the referee to 'players' yet. For a | 143 """Important: do not add the referee to 'players' yet. For a |
144 <players /> message to be emitted whenever a new player is joining, | 144 <players /> message to be emitted whenever a new player is joining, |
145 it is necessary to not modify 'players' outside of _updatePlayers. | 145 it is necessary to not modify 'players' outside of _updatePlayers. |
146 """ | 146 """ |
147 referee = room_jid_s + '/' + referee_nick | 147 referee = room_jid_s + '/' + referee_nick |
148 self.games[room_jid_s] = {'referee': referee, 'players': [], 'started': False} | 148 self.games[room_jid_s] = {'referee': referee, 'players': [], 'started': False, 'status': {}} |
149 self.games[room_jid_s].update(copy.deepcopy(self.game_init)) | 149 self.games[room_jid_s].update(copy.deepcopy(self.game_init)) |
150 self.invitations.setdefault(room_jid_s, []) | 150 self.invitations.setdefault(room_jid_s, []) |
151 | 151 |
152 def _gameExists(self, room_jid_s, started=False): | 152 def _gameExists(self, room_jid_s, started=False): |
153 """Return True if a game has been initialized/started. | 153 """Return True if a game has been initialized/started. |
201 return | 201 return |
202 # this is better than set(nicks).difference(...) as it keeps the order | 202 # 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']] | 203 new_nicks = [nick for nick in nicks if nick not in self.games[room_jid_s]['players']] |
204 if len(new_nicks) == 0: | 204 if len(new_nicks) == 0: |
205 return | 205 return |
206 sync = self._gameExists(room_jid_s, True) and len(self.games[room_jid_s]['players']) > 0 | |
207 | |
208 def setStatus(status): | |
209 for nick in new_nicks: | |
210 self.games[room_jid_s]['status'][nick] = status | |
211 | |
212 setStatus('desync' if sync else 'init') | |
206 self.games[room_jid_s]['players'].extend(new_nicks) | 213 self.games[room_jid_s]['players'].extend(new_nicks) |
207 self._signalPlayers(room_jid_s, [JID(room_jid_s)], profile) | 214 self._synchronizeRoom(room_jid_s, [JID(room_jid_s)], profile) |
208 | 215 if sync: |
209 def _signalPlayers(self, room_jid_s, recipients, profile): | 216 setStatus('init') |
210 """Let these guys know that we are playing (they may not play themselves). | 217 |
218 def _synchronizeRoom(self, room_jid_s, recipients, profile): | |
219 """Communicate the list of players to the whole room or only to some users, | |
220 also send the synchronization data to the players who recently joined the game. | |
211 @param room_jid_s: room userhost | 221 @param room_jid_s: room userhost |
212 @recipients: list of JIDs, the recipients of the message could be: | 222 @recipients: list of JIDs, the recipients of the message could be: |
213 - room JID | 223 - room JID |
214 - room JID + "/" + user nick | 224 - room JID + "/" + user nick |
225 @param profile | |
215 """ | 226 """ |
216 if self._gameExists(room_jid_s, started=True): | 227 if self._gameExists(room_jid_s, started=True): |
217 element = self._createStartElement(self.games[room_jid_s]['players']) | 228 element = self._createStartElement(self.games[room_jid_s]['players']) |
218 else: | 229 else: |
219 element = self._createStartElement(self.games[room_jid_s]['players'], name="players") | 230 element = self._createStartElement(self.games[room_jid_s]['players'], name="players") |
220 elements = [(element, None, None)] | 231 elements = [(element, None, None)] |
221 for child in self.getSyncData(room_jid_s): | 232 |
222 # TODO: sync data may be different and private to each player, | 233 sync_args = [] |
223 # in that case send a separate message to the new players | 234 sync_data = self.getSyncData(room_jid_s) |
224 elements.append((child, None, None)) | 235 for nick in sync_data: |
236 user_jid = JID(room_jid_s + '/' + nick) | |
237 if user_jid in recipients: | |
238 user_elements = copy.deepcopy(elements) | |
239 for child in sync_data[nick]: | |
240 user_elements.append((child, None, None)) | |
241 recipients.remove(user_jid) | |
242 else: | |
243 user_elements = [(child, None, None) for child in sync_data[nick]] | |
244 sync_args.append(([user_jid, user_elements], {'profile': profile})) | |
245 | |
225 for recipient in recipients: | 246 for recipient in recipients: |
226 self._sendElements(recipient, elements, profile=profile) | 247 self._sendElements(recipient, elements, profile=profile) |
227 | 248 for args, kwargs in sync_args: |
228 def getSyncData(self, room_jid_s): | 249 self._sendElements(*args, **kwargs) |
229 """This method may be overwritten by any child class. | 250 |
230 @return: a list of child elements to be added for the game to be synchronized. | 251 def getSyncData(self, room_jid_s, force_nicks=[]): |
231 """ | 252 """This method may (and should probably) be overwritten by a child class. |
232 return [] | 253 The synchronization data are returned for each player who has the state |
254 'desync' or if he's been contained by force_nicks. | |
255 @param room_jid_s: room userhost | |
256 @param force_nicks: force the synchronization for this list of the nicks | |
257 @return: a mapping between player nicks and a list of child elements | |
258 to be sent by self._synchronizeRoom for the game to be synchronized. | |
259 """ | |
260 return {} | |
233 | 261 |
234 def _invitePlayers(self, room, other_players, nick, profile): | 262 def _invitePlayers(self, room, other_players, nick, profile): |
235 """Invite players to a room, associated game may exist or not. | 263 """Invite players to a room, associated game may exist or not. |
236 @param room: wokkel.muc.Room instance | 264 @param room: wokkel.muc.Room instance |
237 @param other_players: list of JID userhosts to invite | 265 @param other_players: list of JID userhosts to invite |
378 profile_nick = room.occupantJID.resource | 406 profile_nick = room.occupantJID.resource |
379 if not self.isReferee(room_jid_s, profile_nick): | 407 if not self.isReferee(room_jid_s, profile_nick): |
380 return True # profile is not the referee | 408 return True # profile is not the referee |
381 if not self._checkJoinAuth(room_jid_s, user.entity.userhost() if user.entity else None, user.nick): | 409 if not self._checkJoinAuth(room_jid_s, user.entity.userhost() if user.entity else None, user.nick): |
382 # user not allowed but let him know that we are playing :p | 410 # user not allowed but let him know that we are playing :p |
383 self._signalPlayers(room_jid_s, [JID(room_jid_s + '/' + user.nick)], profile) | 411 self._synchronizeRoom(room_jid_s, [JID(room_jid_s + '/' + user.nick)], profile) |
384 return True | 412 return True |
385 if self.wait_mode == self.FOR_ALL: | 413 if self.wait_mode == self.FOR_ALL: |
386 # considering the last batch of invitations | 414 # considering the last batch of invitations |
387 batch = len(self.invitations[room_jid_s]) - 1 | 415 batch = len(self.invitations[room_jid_s]) - 1 |
388 if batch < 0: | 416 if batch < 0: |
464 return | 492 return |
465 (create, sync) = self._checkCreateGameAndInit(room_jid_s, profile) | 493 (create, sync) = self._checkCreateGameAndInit(room_jid_s, profile) |
466 if not create: | 494 if not create: |
467 if sync: | 495 if sync: |
468 debug(_('Synchronize game %s in %s for %s') % (self.name, room_jid_s, ', '.join(nicks))) | 496 debug(_('Synchronize game %s in %s for %s') % (self.name, room_jid_s, ', '.join(nicks))) |
469 # TODO: we should call a method to re-send the information to a player who left | |
470 # and joined the room again, currently: we may restart a whole new round... | |
471 self._updatePlayers(room_jid_s, nicks, profile) | 497 self._updatePlayers(room_jid_s, nicks, profile) |
472 return | 498 return |
473 self.games[room_jid_s]['started'] = True | 499 self.games[room_jid_s]['started'] = True |
474 self._updatePlayers(room_jid_s, nicks, profile) | 500 self._updatePlayers(room_jid_s, nicks, profile) |
475 if self.player_init == {}: | 501 if self.player_init: |
476 return | 502 # specific data to each player (score, private data) |
477 # specific data to each player | 503 self.games[room_jid_s].setdefault('players_data', {}) |
478 status = {} | 504 for nick in nicks: |
479 players_data = {} | 505 # The dict must be COPIED otherwise it is shared between all users |
480 for nick in nicks: | 506 self.games[room_jid_s]['players_data'][nick] = copy.deepcopy(self.player_init) |
481 # The dict must be COPIED otherwise it is shared between all users | |
482 players_data[nick] = copy.deepcopy(self.player_init) | |
483 status[nick] = "init" | |
484 self.games[room_jid_s].update({'status': status, 'players_data': players_data}) | |
485 | 507 |
486 def playerReady(self, player, referee, profile_key='@NONE@'): | 508 def playerReady(self, player, referee, profile_key='@NONE@'): |
487 """Must be called when player is ready to start a new game | 509 """Must be called when player is ready to start a new game |
488 @param player: the player nick in the room | 510 @param player: the player nick in the room |
489 @param referee: referee userhost | 511 @param referee: referee userhost |
581 elem.attributes.update(attrs) | 603 elem.attributes.update(attrs) |
582 if content is not None: | 604 if content is not None: |
583 elem.addContent(content) | 605 elem.addContent(content) |
584 self.host.profiles[profile].xmlstream.send(msg) | 606 self.host.profiles[profile].xmlstream.send(msg) |
585 | 607 |
586 | |
587 def send(self, to_jid, elem=None, attrs=None, content=None, profile=None): | 608 def send(self, to_jid, elem=None, attrs=None, content=None, profile=None): |
588 """ | 609 """ |
589 @param to_jid: recipient JID | 610 @param to_jid: recipient JID |
590 @param elem: domish.Element, unicode or a couple: | 611 @param elem: domish.Element, unicode or a couple: |
591 - domish.Element to be directly added as a child to the message | 612 - domish.Element to be directly added as a child to the message |