Mercurial > libervia-backend
comparison src/tools/plugins/games.py @ 683:75e4f5e2cc65
plugins radiocol, card_game, quiz: code factorization
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 23 Oct 2013 12:45:13 +0200 |
parents | |
children | c9792d0be499 |
comparison
equal
deleted
inserted
replaced
682:2805fa3f4bdf | 683:75e4f5e2cc65 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT: a jabber client | |
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 from logging import debug, warning, error | |
21 from twisted.words.protocols.jabber.jid import JID | |
22 from twisted.words.xish import domish | |
23 from time import time | |
24 """This library help manage general games (e.g. card games) and it can be used by plugins""" | |
25 | |
26 | |
27 class RoomGame(object): | |
28 """This class is used to help launching a MUC game.""" | |
29 | |
30 def __init__(self, host, plugin_info, ns_tag, player_init_data={}, options={}): | |
31 """ | |
32 @param host | |
33 @param name: the name of this name | |
34 @param player_init_data: dictionary for initialization data, applicable to each player | |
35 @param options: dictionary for game options | |
36 """ | |
37 self.host = host | |
38 self.name = plugin_info["import_name"] | |
39 self.ns_tag = ns_tag | |
40 self.player_init_data = player_init_data | |
41 self.collectiveGame = self.player_init_data is {} | |
42 self.options = options | |
43 self.games = {} | |
44 self.waiting_inv = {} # Invitation waiting for people to join to launch a game | |
45 | |
46 # args: room_jid, referee, players, profile | |
47 host.bridge.addSignal("%sGo" % self.name, ".plugin", signature='ssass') | |
48 | |
49 def prepareRoom(self, other_players, profile_key='@NONE@'): | |
50 """Prepare the room for a game: create it and invite players. | |
51 @param other_players: list for other players JID userhosts | |
52 """ | |
53 debug(_('Preparing room for %s game') % self.name) | |
54 profile = self.host.memory.getProfileName(profile_key) | |
55 if not profile: | |
56 error(_("Unknown profile")) | |
57 return | |
58 _jid, xmlstream = self.host.getJidNStream(profile) | |
59 if other_players is None: | |
60 other_players = [] | |
61 players = other_players[:] | |
62 players.append(_jid.userhost()) | |
63 | |
64 def roomJoined(room): | |
65 _room = room.occupantJID.userhostJID() | |
66 if self.collectiveGame is True or other_players == [] and _jid in [user.entity for user in room.roster.values()]: | |
67 self.createGame(_room.userhost(), None if self.collectiveGame is True else players, profile_key=profile) | |
68 else: | |
69 self.waiting_inv[_room] = (time(), players) # TODO: remove invitation waiting for too long, using the time data | |
70 for player in other_players: | |
71 self.host.plugins["XEP-0249"].invite(JID(player), room.occupantJID.userhostJID(), {"game": self.name}, profile) | |
72 | |
73 def after_init(ignore): | |
74 room_name = "sat_%s_%s" % (self.name.lower(), self.host.plugins["XEP-0045"].getUniqueName(profile_key)) | |
75 print "\n\n===> room_name:", room_name | |
76 muc_service = None | |
77 for service in self.host.memory.getServerServiceEntities("conference", "text", profile): | |
78 if not ".irc." in service.userhost(): | |
79 #FIXME: | |
80 #This awfull ugly hack is here to avoid an issue with openfire: the irc gateway | |
81 #use "conference/text" identity (instead of "conference/irc"), there is certainly a better way | |
82 #to manage this, but this hack fill do it for test purpose | |
83 muc_service = service | |
84 break | |
85 if not muc_service: | |
86 error(_("Can't find a MUC service")) | |
87 return | |
88 | |
89 d = self.host.plugins["XEP-0045"].join(JID("%s@%s" % (room_name, muc_service.userhost())), _jid.user, {}, profile) | |
90 d.addCallback(roomJoined) | |
91 | |
92 client = self.host.getClient(profile) | |
93 if not client: | |
94 error(_('No client for this profile key: %s') % profile_key) | |
95 return | |
96 client.client_initialized.addCallback(after_init) | |
97 | |
98 def userJoinedTrigger(self, room, user, profile): | |
99 """This trigger is used to check if we are waiting for people in this room, | |
100 and to create a game if everybody is here. | |
101 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} | |
102 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID | |
103 """ | |
104 _room_jid = room.occupantJID.userhostJID() | |
105 if self.collectiveGame is True: | |
106 if _room_jid in self.games and self.games[_room_jid]["referee"] == room.occupantJID.full(): | |
107 #we are in a radiocol room, let's start the party ! | |
108 mess = self.createGameElt(JID(_room_jid + '/' + user.nick)) | |
109 mess.firstChildElement().addChild(self.__create_started_elt()) | |
110 self.host.profiles[profile].xmlstream.send(mess) | |
111 return True | |
112 if _room_jid in self.waiting_inv and len(room.roster) >= len(self.waiting_inv[_room_jid][1]): | |
113 expected_players = self.waiting_inv[_room_jid][1] | |
114 players = [] | |
115 for player in expected_players: | |
116 for user in room.roster.values(): | |
117 if user.entity is not None: | |
118 # check people identity | |
119 if user.entity.userhost() == player: | |
120 players.append(user.nick) | |
121 continue | |
122 else: | |
123 # TODO: how to check the identity with only a nickname?? | |
124 if user.nick == JID(player).user: | |
125 players.append(user.nick) | |
126 if len(players) < len(expected_players): | |
127 # Someone here was not invited! He can stay but he doesn't play :p | |
128 return True | |
129 # When we have all people in the room, we create the game | |
130 del self.waiting_inv[_room_jid] | |
131 self.createGame(_room_jid.userhost(), players, profile_key=profile) | |
132 return True | |
133 | |
134 def createGame(self, room_jid, players=None, profile_key='@NONE@'): | |
135 """Create a new game | |
136 @param room_jid: jid of the room | |
137 @param players: list of players nick (nick must exist in the room) | |
138 @param profile_key: %(doc_profile_key)s""" | |
139 debug(_("Creating %s game") % self.name) | |
140 room = JID(room_jid).userhost() | |
141 profile = self.host.memory.getProfileName(profile_key) | |
142 if not profile: | |
143 error(_("profile %s is unknown") % profile_key) | |
144 return | |
145 if room in self.games: | |
146 warning(_("%s game already started in room %s") % (self.name, room)) | |
147 return | |
148 room_nick = self.host.plugins["XEP-0045"].getRoomNick(room, profile) | |
149 if not room_nick: | |
150 error('Internal error') | |
151 return | |
152 referee = room + '/' + room_nick | |
153 self.games[room] = {'referee': referee} | |
154 self.games[room].update(self.options) | |
155 if self.collectiveGame is True: | |
156 mess = self.createGameElt(JID(room)) | |
157 mess.firstChildElement().addChild(self.__create_started_elt()) | |
158 self.host.profiles[profile].xmlstream.send(mess) | |
159 return | |
160 # non collaborative game = individual data and messages | |
161 status = {} | |
162 players_data = {} | |
163 for player in players: | |
164 # The dict must be COPIED otherwise it is shared between all users | |
165 players_data[player] = self.player_init_data.copy() | |
166 status[player] = "init" | |
167 # each player send a message to all the others | |
168 mess = self.createGameElt(JID(room + '/' + player)) | |
169 mess.firstChildElement().addChild(self.__create_started_elt(players)) | |
170 self.host.profiles[profile].xmlstream.send(mess) | |
171 # specific data to each player | |
172 self.games[room].update({'players': players, 'status': status, 'players_data': players_data}) | |
173 | |
174 def createCollectiveGame(self, room_jid, profile_key='@NONE@'): | |
175 return self.createGame(self, room_jid, players=None, profile_key=profile_key) | |
176 | |
177 def playerReady(self, player, referee, profile_key='@NONE@'): | |
178 """Must be called when player is ready to start a new game""" | |
179 profile = self.host.memory.getProfileName(profile_key) | |
180 if not profile: | |
181 error(_("profile %s is unknown") % profile_key) | |
182 return | |
183 debug('new player ready: %s' % profile) | |
184 mess = self.createGameElt(JID(referee)) | |
185 ready_elt = mess.firstChildElement().addElement('player_ready') | |
186 ready_elt['player'] = player | |
187 self.host.profiles[profile].xmlstream.send(mess) | |
188 | |
189 def newRound(self, room_jid, data, profile): | |
190 """Launch a new round (reinit the user data)""" | |
191 debug(_('new round for %s game') % self.name) | |
192 game_data = self.games[room_jid.userhost()] | |
193 players = game_data['players'] | |
194 players_data = game_data['players_data'] | |
195 game_data['stage'] = "init" | |
196 | |
197 common_data, msg_elts = data if data is not None else (None, None) | |
198 | |
199 if isinstance(msg_elts, dict): | |
200 for player in players: | |
201 to_jid = JID(room_jid.userhost() + "/" + player) # FIXME: gof: | |
202 mess = self.createGameElt(to_jid) | |
203 if isinstance(msg_elts[player], domish.Element): | |
204 mess.firstChildElement().addChild(msg_elts[player]) | |
205 self.host.profiles[profile].xmlstream.send(mess) | |
206 elif isinstance(msg_elts, domish.Element): | |
207 mess = self.createGameElt(room_jid) | |
208 mess.firstChildElement().addChild(msg_elts) | |
209 self.host.profiles[profile].xmlstream.send(mess) | |
210 if common_data is not None: | |
211 for player in players: | |
212 players_data[player].update(common_data) | |
213 | |
214 def createGameElt(self, to_jid, type_="normal"): | |
215 """Create a generic domish Element for the game""" | |
216 type_ = "normal" if to_jid.resource else "groupchat" | |
217 elt = domish.Element((None, 'message')) | |
218 elt["to"] = to_jid.full() | |
219 elt["type"] = type_ | |
220 elt.addElement(self.ns_tag) | |
221 return elt | |
222 | |
223 def __create_started_elt(self, players=None): | |
224 """Create a game "started" domish Element""" | |
225 started_elt = domish.Element((None, 'started')) | |
226 if players is None: | |
227 return started_elt | |
228 idx = 0 | |
229 for player in players: | |
230 player_elt = domish.Element((None, 'player')) | |
231 player_elt.addContent(player) | |
232 player_elt['index'] = str(idx) | |
233 idx += 1 | |
234 started_elt.addChild(player_elt) | |
235 return started_elt |