# HG changeset patch # User souliane # Date 1389823283 -3600 # Node ID e3f4d80f987d5838a8426e461cf75e20915eccaa # Parent c304ce32042ba892ed10dcb6f772cd4813dc845b plugins room_games, radiocol: better synchronization after a user joins a running game diff -r c304ce32042b -r e3f4d80f987d src/plugins/plugin_misc_radiocol.py --- a/src/plugins/plugin_misc_radiocol.py Mon Feb 17 18:57:53 2014 +0100 +++ b/src/plugins/plugin_misc_radiocol.py Wed Jan 15 23:01:23 2014 +0100 @@ -224,8 +224,16 @@ else: error(_('Unmanaged game element: %s') % elt.name) - def getSyncData(self, room_jid_s): - data = self.games[room_jid_s]['queue'] - if len(data) == QUEUE_LIMIT: - data.append(domish.Element((None, 'no_upload'))) + def getSyncData(self, room_jid_s, force_nicks=[]): + data = {} + status = self.games[room_jid_s]['status'] + nicks = [nick for nick in status if status[nick] == 'desync'] + for nick in force_nicks: + if nick not in nicks: + nicks.append(nick) + for nick in nicks: + if len(self.games[room_jid_s]['queue']) > 0: + data[nick] = copy.deepcopy(self.games[room_jid_s]['queue']) + if len(self.games[room_jid_s]['queue']) == QUEUE_LIMIT: + data[nick].append(domish.Element(('', 'no_upload'))) return data diff -r c304ce32042b -r e3f4d80f987d src/plugins/plugin_misc_room_game.py --- a/src/plugins/plugin_misc_room_game.py Mon Feb 17 18:57:53 2014 +0100 +++ b/src/plugins/plugin_misc_room_game.py Wed Jan 15 23:01:23 2014 +0100 @@ -145,7 +145,7 @@ it is necessary to not modify 'players' outside of _updatePlayers. """ referee = room_jid_s + '/' + referee_nick - self.games[room_jid_s] = {'referee': referee, 'players': [], 'started': False} + self.games[room_jid_s] = {'referee': referee, 'players': [], 'started': False, 'status': {}} self.games[room_jid_s].update(copy.deepcopy(self.game_init)) self.invitations.setdefault(room_jid_s, []) @@ -203,33 +203,61 @@ new_nicks = [nick for nick in nicks if nick not in self.games[room_jid_s]['players']] if len(new_nicks) == 0: return - self.games[room_jid_s]['players'].extend(new_nicks) - self._signalPlayers(room_jid_s, [JID(room_jid_s)], profile) + sync = self._gameExists(room_jid_s, True) and len(self.games[room_jid_s]['players']) > 0 + + def setStatus(status): + for nick in new_nicks: + self.games[room_jid_s]['status'][nick] = status - def _signalPlayers(self, room_jid_s, recipients, profile): - """Let these guys know that we are playing (they may not play themselves). + setStatus('desync' if sync else 'init') + self.games[room_jid_s]['players'].extend(new_nicks) + self._synchronizeRoom(room_jid_s, [JID(room_jid_s)], profile) + if sync: + setStatus('init') + + def _synchronizeRoom(self, room_jid_s, recipients, profile): + """Communicate the list of players to the whole room or only to some users, + also send the synchronization data to the players who recently joined the game. @param room_jid_s: room userhost @recipients: list of JIDs, the recipients of the message could be: - room JID - room JID + "/" + user nick + @param profile """ if self._gameExists(room_jid_s, started=True): element = self._createStartElement(self.games[room_jid_s]['players']) else: element = self._createStartElement(self.games[room_jid_s]['players'], name="players") elements = [(element, None, None)] - for child in self.getSyncData(room_jid_s): - # TODO: sync data may be different and private to each player, - # in that case send a separate message to the new players - elements.append((child, None, None)) + + sync_args = [] + sync_data = self.getSyncData(room_jid_s) + for nick in sync_data: + user_jid = JID(room_jid_s + '/' + nick) + if user_jid in recipients: + user_elements = copy.deepcopy(elements) + for child in sync_data[nick]: + user_elements.append((child, None, None)) + recipients.remove(user_jid) + else: + user_elements = [(child, None, None) for child in sync_data[nick]] + sync_args.append(([user_jid, user_elements], {'profile': profile})) + for recipient in recipients: self._sendElements(recipient, elements, profile=profile) + for args, kwargs in sync_args: + self._sendElements(*args, **kwargs) - def getSyncData(self, room_jid_s): - """This method may be overwritten by any child class. - @return: a list of child elements to be added for the game to be synchronized. + def getSyncData(self, room_jid_s, force_nicks=[]): + """This method may (and should probably) be overwritten by a child class. + The synchronization data are returned for each player who has the state + 'desync' or if he's been contained by force_nicks. + @param room_jid_s: room userhost + @param force_nicks: force the synchronization for this list of the nicks + @return: a mapping between player nicks and a list of child elements + to be sent by self._synchronizeRoom for the game to be synchronized. """ - return [] + return {} def _invitePlayers(self, room, other_players, nick, profile): """Invite players to a room, associated game may exist or not. @@ -380,7 +408,7 @@ return True # profile is not the referee if not self._checkJoinAuth(room_jid_s, user.entity.userhost() if user.entity else None, user.nick): # user not allowed but let him know that we are playing :p - self._signalPlayers(room_jid_s, [JID(room_jid_s + '/' + user.nick)], profile) + self._synchronizeRoom(room_jid_s, [JID(room_jid_s + '/' + user.nick)], profile) return True if self.wait_mode == self.FOR_ALL: # considering the last batch of invitations @@ -466,22 +494,16 @@ if not create: if sync: debug(_('Synchronize game %s in %s for %s') % (self.name, room_jid_s, ', '.join(nicks))) - # TODO: we should call a method to re-send the information to a player who left - # and joined the room again, currently: we may restart a whole new round... self._updatePlayers(room_jid_s, nicks, profile) return self.games[room_jid_s]['started'] = True self._updatePlayers(room_jid_s, nicks, profile) - if self.player_init == {}: - return - # specific data to each player - status = {} - players_data = {} - for nick in nicks: - # The dict must be COPIED otherwise it is shared between all users - players_data[nick] = copy.deepcopy(self.player_init) - status[nick] = "init" - self.games[room_jid_s].update({'status': status, 'players_data': players_data}) + if self.player_init: + # specific data to each player (score, private data) + self.games[room_jid_s].setdefault('players_data', {}) + for nick in nicks: + # The dict must be COPIED otherwise it is shared between all users + self.games[room_jid_s]['players_data'][nick] = copy.deepcopy(self.player_init) def playerReady(self, player, referee, profile_key='@NONE@'): """Must be called when player is ready to start a new game @@ -583,7 +605,6 @@ elem.addContent(content) self.host.profiles[profile].xmlstream.send(msg) - def send(self, to_jid, elem=None, attrs=None, content=None, profile=None): """ @param to_jid: recipient JID diff -r c304ce32042b -r e3f4d80f987d src/plugins/plugin_misc_tarot.py --- a/src/plugins/plugin_misc_tarot.py Mon Feb 17 18:57:53 2014 +0100 +++ b/src/plugins/plugin_misc_tarot.py Wed Jan 15 23:01:23 2014 +0100 @@ -625,3 +625,6 @@ error(_('Unmanaged error type: %s') % elt['type']) else: error(_('Unmanaged card game element: %s') % elt.name) + + def getSyncData(self, room_jid_s): + return {}