Mercurial > libervia-backend
view src/tools/plugins/games.py @ 703:0c2c1dfb79e4
plugin group blog: renamed options parameter as extra for consistency with sendMessage
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 14 Nov 2013 16:49:57 +0100 |
parents | 75e4f5e2cc65 |
children | c9792d0be499 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # SAT: a jabber client # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from logging import debug, warning, error from twisted.words.protocols.jabber.jid import JID from twisted.words.xish import domish from time import time """This library help manage general games (e.g. card games) and it can be used by plugins""" class RoomGame(object): """This class is used to help launching a MUC game.""" def __init__(self, host, plugin_info, ns_tag, player_init_data={}, options={}): """ @param host @param name: the name of this name @param player_init_data: dictionary for initialization data, applicable to each player @param options: dictionary for game options """ self.host = host self.name = plugin_info["import_name"] self.ns_tag = ns_tag self.player_init_data = player_init_data self.collectiveGame = self.player_init_data is {} self.options = options self.games = {} self.waiting_inv = {} # Invitation waiting for people to join to launch a game # args: room_jid, referee, players, profile host.bridge.addSignal("%sGo" % self.name, ".plugin", signature='ssass') def prepareRoom(self, other_players, profile_key='@NONE@'): """Prepare the room for a game: create it and invite players. @param other_players: list for other players JID userhosts """ debug(_('Preparing room for %s game') % self.name) profile = self.host.memory.getProfileName(profile_key) if not profile: error(_("Unknown profile")) return _jid, xmlstream = self.host.getJidNStream(profile) if other_players is None: other_players = [] players = other_players[:] players.append(_jid.userhost()) def roomJoined(room): _room = room.occupantJID.userhostJID() if self.collectiveGame is True or other_players == [] and _jid in [user.entity for user in room.roster.values()]: self.createGame(_room.userhost(), None if self.collectiveGame is True else players, profile_key=profile) else: self.waiting_inv[_room] = (time(), players) # TODO: remove invitation waiting for too long, using the time data for player in other_players: self.host.plugins["XEP-0249"].invite(JID(player), room.occupantJID.userhostJID(), {"game": self.name}, profile) def after_init(ignore): room_name = "sat_%s_%s" % (self.name.lower(), self.host.plugins["XEP-0045"].getUniqueName(profile_key)) print "\n\n===> room_name:", room_name muc_service = None for service in self.host.memory.getServerServiceEntities("conference", "text", profile): if not ".irc." in service.userhost(): #FIXME: #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way #to manage this, but this hack fill do it for test purpose muc_service = service break if not muc_service: error(_("Can't find a MUC service")) return d = self.host.plugins["XEP-0045"].join(JID("%s@%s" % (room_name, muc_service.userhost())), _jid.user, {}, profile) d.addCallback(roomJoined) client = self.host.getClient(profile) if not client: error(_('No client for this profile key: %s') % profile_key) return client.client_initialized.addCallback(after_init) def userJoinedTrigger(self, room, user, profile): """This trigger is used to check if we are waiting for people in this room, and to create a game if everybody is here. @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID """ _room_jid = room.occupantJID.userhostJID() if self.collectiveGame is True: if _room_jid in self.games and self.games[_room_jid]["referee"] == room.occupantJID.full(): #we are in a radiocol room, let's start the party ! mess = self.createGameElt(JID(_room_jid + '/' + user.nick)) mess.firstChildElement().addChild(self.__create_started_elt()) self.host.profiles[profile].xmlstream.send(mess) return True if _room_jid in self.waiting_inv and len(room.roster) >= len(self.waiting_inv[_room_jid][1]): expected_players = self.waiting_inv[_room_jid][1] players = [] for player in expected_players: for user in room.roster.values(): if user.entity is not None: # check people identity if user.entity.userhost() == player: players.append(user.nick) continue else: # TODO: how to check the identity with only a nickname?? if user.nick == JID(player).user: players.append(user.nick) if len(players) < len(expected_players): # Someone here was not invited! He can stay but he doesn't play :p return True # When we have all people in the room, we create the game del self.waiting_inv[_room_jid] self.createGame(_room_jid.userhost(), players, profile_key=profile) return True def createGame(self, room_jid, players=None, profile_key='@NONE@'): """Create a new game @param room_jid: jid of the room @param players: list of players nick (nick must exist in the room) @param profile_key: %(doc_profile_key)s""" debug(_("Creating %s game") % self.name) room = JID(room_jid).userhost() profile = self.host.memory.getProfileName(profile_key) if not profile: error(_("profile %s is unknown") % profile_key) return if room in self.games: warning(_("%s game already started in room %s") % (self.name, room)) return room_nick = self.host.plugins["XEP-0045"].getRoomNick(room, profile) if not room_nick: error('Internal error') return referee = room + '/' + room_nick self.games[room] = {'referee': referee} self.games[room].update(self.options) if self.collectiveGame is True: mess = self.createGameElt(JID(room)) mess.firstChildElement().addChild(self.__create_started_elt()) self.host.profiles[profile].xmlstream.send(mess) return # non collaborative game = individual data and messages status = {} players_data = {} for player in players: # The dict must be COPIED otherwise it is shared between all users players_data[player] = self.player_init_data.copy() status[player] = "init" # each player send a message to all the others mess = self.createGameElt(JID(room + '/' + player)) mess.firstChildElement().addChild(self.__create_started_elt(players)) self.host.profiles[profile].xmlstream.send(mess) # specific data to each player self.games[room].update({'players': players, 'status': status, 'players_data': players_data}) def createCollectiveGame(self, room_jid, profile_key='@NONE@'): return self.createGame(self, room_jid, players=None, profile_key=profile_key) def playerReady(self, player, referee, profile_key='@NONE@'): """Must be called when player is ready to start a new game""" profile = self.host.memory.getProfileName(profile_key) if not profile: error(_("profile %s is unknown") % profile_key) return debug('new player ready: %s' % profile) mess = self.createGameElt(JID(referee)) ready_elt = mess.firstChildElement().addElement('player_ready') ready_elt['player'] = player self.host.profiles[profile].xmlstream.send(mess) def newRound(self, room_jid, data, profile): """Launch a new round (reinit the user data)""" debug(_('new round for %s game') % self.name) game_data = self.games[room_jid.userhost()] players = game_data['players'] players_data = game_data['players_data'] game_data['stage'] = "init" common_data, msg_elts = data if data is not None else (None, None) if isinstance(msg_elts, dict): for player in players: to_jid = JID(room_jid.userhost() + "/" + player) # FIXME: gof: mess = self.createGameElt(to_jid) if isinstance(msg_elts[player], domish.Element): mess.firstChildElement().addChild(msg_elts[player]) self.host.profiles[profile].xmlstream.send(mess) elif isinstance(msg_elts, domish.Element): mess = self.createGameElt(room_jid) mess.firstChildElement().addChild(msg_elts) self.host.profiles[profile].xmlstream.send(mess) if common_data is not None: for player in players: players_data[player].update(common_data) def createGameElt(self, to_jid, type_="normal"): """Create a generic domish Element for the game""" type_ = "normal" if to_jid.resource else "groupchat" elt = domish.Element((None, 'message')) elt["to"] = to_jid.full() elt["type"] = type_ elt.addElement(self.ns_tag) return elt def __create_started_elt(self, players=None): """Create a game "started" domish Element""" started_elt = domish.Element((None, 'started')) if players is None: return started_elt idx = 0 for player in players: player_elt = domish.Element((None, 'player')) player_elt.addContent(player) player_elt['index'] = str(idx) idx += 1 started_elt.addChild(player_elt) return started_elt