plugins room_games, radiocol: better synchronization after a user joins a running game
This commit is contained in:
parent
2b51a340d4
commit
9e6845472d
3 changed files with 62 additions and 30 deletions
|
|
@ -224,8 +224,16 @@ class Radiocol(object):
|
|||
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
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class RoomGame(object):
|
|||
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 @@ class RoomGame(object):
|
|||
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 _signalPlayers(self, room_jid_s, recipients, profile):
|
||||
"""Let these guys know that we are playing (they may not play themselves).
|
||||
def setStatus(status):
|
||||
for nick in new_nicks:
|
||||
self.games[room_jid_s]['status'][nick] = status
|
||||
|
||||
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 @@ class RoomGame(object):
|
|||
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 @@ class RoomGame(object):
|
|||
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 @@ class RoomGame(object):
|
|||
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
|
||||
|
|
|
|||
|
|
@ -625,3 +625,6 @@ class Tarot(object):
|
|||
error(_('Unmanaged error type: %s') % elt['type'])
|
||||
else:
|
||||
error(_('Unmanaged card game element: %s') % elt.name)
|
||||
|
||||
def getSyncData(self, room_jid_s):
|
||||
return {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue