comparison src/plugins/plugin_misc_room_game.py @ 944:e1842ebcb2f3

core, plugin XEP-0115: discovery refactoring: - hashing algorithm of XEP-0115 has been including in core - our own hash is still calculated by XEP-0115 and can be regenerated with XEP_0115.recalculateHash - old discovery methods have been removed. Now the following methods are used: - hasFeature: tell if a feature is available for an entity - getDiscoInfos: self explaining - getDiscoItems: self explaining - findServiceEntities: return all available items of an entity which given (category, type) - findFeaturesSet: search for a set of features in entity + entity's items all these methods are asynchronous, and manage cache automatically - XEP-0115 manage in a better way hashes, and now use a trigger for presence instead of monkey patch - new FeatureNotFound exception, when we want to do something which is not available - refactored client initialisation sequence, removed client.initialized Deferred - added constant APP_URL - test_plugin_xep_0033.py has been temporarly deactivated, the time to adapt it - lot of cleaning
author Goffi <goffi@goffi.org>
date Fri, 28 Mar 2014 18:07:22 +0100
parents c6d8fc63b1db
children 301b342c697a
comparison
equal deleted inserted replaced
943:71926ec2114d 944:e1842ebcb2f3
360 # the game initialization, that's why we do also check with isReferee 360 # the game initialization, that's why we do also check with isReferee
361 return nick in self.games[room_jid_s]['players'] or self.isReferee(room_jid_s, nick) 361 return nick in self.games[room_jid_s]['players'] or self.isReferee(room_jid_s, nick)
362 362
363 def _checkWaitAuth(self, room, other_players, verbose=False): 363 def _checkWaitAuth(self, room, other_players, verbose=False):
364 """Check if we must wait for other players before starting the game. 364 """Check if we must wait for other players before starting the game.
365
365 @param room: wokkel.muc.Room instance 366 @param room: wokkel.muc.Room instance
366 @param other_players: list of players JID userhosts without the referee 367 @param other_players: list of players JID userhosts without the referee
367 @param verbose: display debug message 368 @param verbose: display debug message
368 @return: (x, y, z) with: 369 @return: (x, y, z) with:
369 x: False if we must wait, True otherwise 370 x: False if we must wait, True otherwise
381 result = (len(nicks) == len(other_players), nicks, missing) 382 result = (len(nicks) == len(other_players), nicks, missing)
382 if not result[0] and (verbose or _DEBUG): 383 if not result[0] and (verbose or _DEBUG):
383 debug(_("Still waiting for %(users)s before starting the game %(game)s in %(room)s") % {'users': result[2], 'game': self.name, 'room': room.occupantJID.userhost()}) 384 debug(_("Still waiting for %(users)s before starting the game %(game)s in %(room)s") % {'users': result[2], 'game': self.name, 'room': room.occupantJID.userhost()})
384 return result 385 return result
385 386
386 def getUniqueName(self, muc_service="", profile_key=C.PROF_KEY_NONE): 387 def getUniqueName(self, muc_service=None, profile_key=C.PROF_KEY_NONE):
387 """ 388 """Generate unique room name
389
388 @param muc_service: you can leave empty to autofind the muc service 390 @param muc_service: you can leave empty to autofind the muc service
389 @param profile_key 391 @param profile_key: %(doc_profile_key)s
390 @return: a unique name for a new room to be created 392 @return: a unique name for a new room to be created
391 """ 393 """
394 # FIXME: jid.JID must be used instead of strings
392 room = self.host.plugins["XEP-0045"].getUniqueName(muc_service, profile_key=profile_key) 395 room = self.host.plugins["XEP-0045"].getUniqueName(muc_service, profile_key=profile_key)
393 return "sat_%s_%s" % (self.name.lower(), room) if room != "" else "" 396 return "sat_%s_%s" % (self.name.lower(), room.full())
394 397
395 def prepareRoom(self, other_players=None, room_jid_s=None, profile_key=C.PROF_KEY_NONE): 398 def prepareRoom(self, other_players=None, room_jid_s=None, profile_key=C.PROF_KEY_NONE):
396 """Prepare the room for a game: create it if it doesn't exist and invite players. 399 """Prepare the room for a game: create it if it doesn't exist and invite players.
400
397 @param other_players: list for other players JID userhosts 401 @param other_players: list for other players JID userhosts
398 @param room_jid_s: JID userhost of the room, or None to generate a unique name 402 @param room_jid_s: JID userhost of the room, or None to generate a unique name
399 @param profile_key 403 @param profile_key
400 """ 404 """
401 debug(_('Preparing room for %s game') % self.name) 405 debug(_('Preparing room for %s game') % self.name)
408 412
409 def roomJoined(room): 413 def roomJoined(room):
410 """@param room: instance of wokkel.muc.Room""" 414 """@param room: instance of wokkel.muc.Room"""
411 self._createOrInvite(room, [JID(player).userhost() for player in other_players], profile) 415 self._createOrInvite(room, [JID(player).userhost() for player in other_players], profile)
412 416
413 def afterClientInit(room_jid_s): 417 # Create/join the given room, or a unique generated one if no room is specified.
414 """Create/join the given room, or a unique generated one if no room is specified. 418 if room_jid_s is not None and room_jid_s != "": # a room name has been specified
415 @param room_jids: userhost of the room to join 419 if room_jid_s in self.host.plugins["XEP-0045"].clients[profile].joined_rooms:
416 """ 420 roomJoined(self.host.plugins["XEP-0045"].clients[profile].joined_rooms[room_jid_s])
417 if room_jid_s is not None and room_jid_s != "": # a room name has been specified 421 return
418 if room_jid_s in self.host.plugins["XEP-0045"].clients[profile].joined_rooms: 422 else:
419 roomJoined(self.host.plugins["XEP-0045"].clients[profile].joined_rooms[room_jid_s]) 423 room_jid_s = self.getUniqueName(profile_key=profile_key)
420 return 424 if room_jid_s == "":
421 else: 425 return
422 room_jid_s = self.getUniqueName(profile_key=profile_key) 426 user_jid = self.host.getJidNStream(profile)[0]
423 if room_jid_s == "": 427 d = self.host.plugins["XEP-0045"].join(JID(room_jid_s), user_jid.user, {}, profile)
424 return 428 return d.addCallback(roomJoined)
425 user_jid = self.host.getJidNStream(profile)[0]
426 d = self.host.plugins["XEP-0045"].join(JID(room_jid_s), user_jid.user, {}, profile)
427 d.addCallback(roomJoined)
428
429 client = self.host.getClient(profile)
430 client.client_initialized.addCallback(lambda ignore: afterClientInit(room_jid_s))
431 429
432 def userJoinedTrigger(self, room, user, profile): 430 def userJoinedTrigger(self, room, user, profile):
433 """This trigger is used to check if the new user can take part of a game, 431 """This trigger is used to check if the new user can take part of a game, create the game if we were waiting for him or just update the players list.
434 create the game if we were waiting for him or just update the players list. 432
435 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} 433 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User}
436 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID 434 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID
437 @return: True to not interrupt the main process. 435 @return: True to not interrupt the main process.
438 """ 436 """
439 room_jid_s = room.occupantJID.userhost() 437 room_jid_s = room.occupantJID.userhost()
461 self._updatePlayers(room_jid_s, [user.nick], True, profile) 459 self._updatePlayers(room_jid_s, [user.nick], True, profile)
462 return True 460 return True
463 461
464 def userLeftTrigger(self, room, user, profile): 462 def userLeftTrigger(self, room, user, profile):
465 """This trigger is used to update or stop the game when a user leaves. 463 """This trigger is used to update or stop the game when a user leaves.
464
466 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User} 465 @room: wokkel.muc.Room object. room.roster is a dict{wokkel.muc.User.nick: wokkel.muc.User}
467 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID 466 @user: wokkel.muc.User object. user.nick is a unicode and user.entity a JID
468 @return: True to not interrupt the main process. 467 @return: True to not interrupt the main process.
469 """ 468 """
470 room_jid_s = room.occupantJID.userhost() 469 room_jid_s = room.occupantJID.userhost()
489 if user_jid_s not in self.invitations[room_jid_s][batch][1]: 488 if user_jid_s not in self.invitations[room_jid_s][batch][1]:
490 self.invitations[room_jid_s][batch][1].append(user_jid_s) 489 self.invitations[room_jid_s][batch][1].append(user_jid_s)
491 return True 490 return True
492 491
493 def _checkCreateGameAndInit(self, room_jid_s, profile): 492 def _checkCreateGameAndInit(self, room_jid_s, profile):
494 """Check if that profile can create the game. If the game can be created 493 """Check if that profile can create the game. If the game can be created but is not initialized yet, this method will also do the initialization.
495 but is not initialized yet, this method will also do the initialization. 494
496 @param room_jid_s: room userhost 495 @param room_jid_s: room userhost
497 @param profile 496 @param profile
498 @return: a couple (create, sync) with: 497 @return: a couple (create, sync) with:
499 - create: set to True to allow the game creation 498 - create: set to True to allow the game creation
500 - sync: set to True to advice a game synchronization 499 - sync: set to True to advice a game synchronization
501 """ 500 """
502 user_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid_s, profile) 501 user_nick = self.host.plugins["XEP-0045"].getRoomNick(room_jid_s, profile)
503 if not user_nick: 502 if not user_nick:
504 error('Internal error: profile %s has not joined the room %s' % (profile, room_jid_s)) 503 error('Internal error: profile %s has not joined the room %s' % (profile, room_jid_s))
505 return False, False 504 return False, False
514 else: 513 else:
515 self._initGame(room_jid_s, user_nick) 514 self._initGame(room_jid_s, user_nick)
516 return True, False 515 return True, False
517 516
518 def createGame(self, room_jid_s, nicks=None, profile_key=C.PROF_KEY_NONE): 517 def createGame(self, room_jid_s, nicks=None, profile_key=C.PROF_KEY_NONE):
519 """Create a new game - this can be called directly from a frontend 518 """Create a new game.
520 and skips all the checks and invitation system, but the game must 519
521 not exist and all the players must be in the room already. 520 This can be called directly from a frontend and skips all the checks and invitation system,
521 but the game must not exist and all the players must be in the room already.
522 @param room_jid: JID userhost of the room 522 @param room_jid: JID userhost of the room
523 @param nicks: list of players nicks in the room (referee included, in first position) 523 @param nicks: list of players nicks in the room (referee included, in first position)
524 @param profile_key: %(doc_profile_key)s""" 524 @param profile_key: %(doc_profile_key)s
525 """
525 debug(_("Creating %(game)s game in room %(room)s") % {'game': self.name, 'room': room_jid_s}) 526 debug(_("Creating %(game)s game in room %(room)s") % {'game': self.name, 'room': room_jid_s})
526 profile = self.host.memory.getProfileName(profile_key) 527 profile = self.host.memory.getProfileName(profile_key)
527 if not profile: 528 if not profile:
528 error(_("profile %s is unknown") % profile_key) 529 error(_("profile %s is unknown") % profile_key)
529 return 530 return
543 # The dict must be COPIED otherwise it is shared between all users 544 # The dict must be COPIED otherwise it is shared between all users
544 self.games[room_jid_s]['players_data'][nick] = copy.deepcopy(self.player_init) 545 self.games[room_jid_s]['players_data'][nick] = copy.deepcopy(self.player_init)
545 546
546 def playerReady(self, player, referee, profile_key=C.PROF_KEY_NONE): 547 def playerReady(self, player, referee, profile_key=C.PROF_KEY_NONE):
547 """Must be called when player is ready to start a new game 548 """Must be called when player is ready to start a new game
549
548 @param player: the player nick in the room 550 @param player: the player nick in the room
549 @param referee: referee userhost 551 @param referee: referee userhost
550 """ 552 """
551 profile = self.host.memory.getProfileName(profile_key) 553 profile = self.host.memory.getProfileName(profile_key)
552 if not profile: 554 if not profile:
556 # TODO: we probably need to add the game and room names in the sent message 558 # TODO: we probably need to add the game and room names in the sent message
557 self.send(JID(referee), 'player_ready', {'player': player}, profile=profile) 559 self.send(JID(referee), 'player_ready', {'player': player}, profile=profile)
558 560
559 def newRound(self, room_jid, data, profile): 561 def newRound(self, room_jid, data, profile):
560 """Launch a new round (reinit the user data) 562 """Launch a new round (reinit the user data)
563
561 @param room_jid: room userhost 564 @param room_jid: room userhost
562 @param data: a couple (common_data, msg_elts) with: 565 @param data: a couple (common_data, msg_elts) with:
563 - common_data: backend initialization data for the new round 566 - common_data: backend initialization data for the new round
564 - msg_elts: dict to map each user to his specific initialization message 567 - msg_elts: dict to map each user to his specific initialization message
565 @param profile 568 @param profile
566 """ 569 """
567 debug(_('new round for %s game') % self.name) 570 debug(_('new round for %s game') % self.name)
568 game_data = self.games[room_jid.userhost()] 571 game_data = self.games[room_jid.userhost()]
569 players = game_data['players'] 572 players = game_data['players']
583 for player in players: 586 for player in players:
584 players_data[player].update(copy.deepcopy(common_data)) 587 players_data[player].update(copy.deepcopy(common_data))
585 588
586 def _createGameElt(self, to_jid): 589 def _createGameElt(self, to_jid):
587 """Create a generic domish Element for the game messages 590 """Create a generic domish Element for the game messages
591
588 @param to_jid: JID of the recipient 592 @param to_jid: JID of the recipient
589 @return: the created element 593 @return: the created element
590 """ 594 """
591 type_ = "normal" if to_jid.resource else "groupchat" 595 type_ = "normal" if to_jid.resource else "groupchat"
592 elt = domish.Element((None, 'message')) 596 elt = domish.Element((None, 'message'))
595 elt.addElement(self.ns_tag) 599 elt.addElement(self.ns_tag)
596 return elt 600 return elt
597 601
598 def _createStartElement(self, players=None, name="started"): 602 def _createStartElement(self, players=None, name="started"):
599 """Create a domish Element listing the game users 603 """Create a domish Element listing the game users
604
600 @param players: list of the players 605 @param players: list of the players
601 @param name: element name: 606 @param name: element name:
602 - "started" to signal the players that the game has been started 607 - "started" to signal the players that the game has been started
603 - "players" to signal the list of players when the game is not started yet 608 - "players" to signal the list of players when the game is not started yet
604 @return the create element 609 @return the create element
605 """ 610 """
606 started_elt = domish.Element((None, name)) 611 started_elt = domish.Element((None, name))
607 if players is None: 612 if players is None:
608 return started_elt 613 return started_elt
614 idx += 1 619 idx += 1
615 started_elt.addChild(player_elt) 620 started_elt.addChild(player_elt)
616 return started_elt 621 return started_elt
617 622
618 def _sendElements(self, to_jid, data, profile=None): 623 def _sendElements(self, to_jid, data, profile=None):
619 """ 624 """ TODO
625
620 @param to_jid: recipient JID 626 @param to_jid: recipient JID
621 @param data: list of (elem, attr, content) with: 627 @param data: list of (elem, attr, content) with:
622 - elem: domish.Element, unicode or a couple: 628 - elem: domish.Element, unicode or a couple:
623 - domish.Element to be directly added as a child to the message 629 - domish.Element to be directly added as a child to the message
624 - unicode name or couple (uri, name) to create a new domish.Element 630 - unicode name or couple (uri, name) to create a new domish.Element
625 and add it as a child to the message (see domish.Element.addElement) 631 and add it as a child to the message (see domish.Element.addElement)
626 - attrs: dictionary of attributes for the new child 632 - attrs: dictionary of attributes for the new child
627 - content: unicode that is appended to the child content 633 - content: unicode that is appended to the child content
628 @param profile: the profile from which the message is sent 634 @param profile: the profile from which the message is sent
629 @return: a Deferred instance 635 @return: a Deferred instance
630 """ 636 """
631 if profile is None: 637 if profile is None:
632 error(_("Message can not be sent without a sender profile")) 638 error(_("Message can not be sent without a sender profile"))
644 elem.addContent(content) 650 elem.addContent(content)
645 self.host.profiles[profile].xmlstream.send(msg) 651 self.host.profiles[profile].xmlstream.send(msg)
646 return defer.succeed(None) 652 return defer.succeed(None)
647 653
648 def send(self, to_jid, elem=None, attrs=None, content=None, profile=None): 654 def send(self, to_jid, elem=None, attrs=None, content=None, profile=None):
649 """ 655 """ TODO
656
650 @param to_jid: recipient JID 657 @param to_jid: recipient JID
651 @param elem: domish.Element, unicode or a couple: 658 @param elem: domish.Element, unicode or a couple:
652 - domish.Element to be directly added as a child to the message 659 - domish.Element to be directly added as a child to the message
653 - unicode name or couple (uri, name) to create a new domish.Element 660 - unicode name or couple (uri, name) to create a new domish.Element
654 and add it as a child to the message (see domish.Element.addElement) 661 and add it as a child to the message (see domish.Element.addElement)
655 @param attrs: dictionary of attributes for the new child 662 @param attrs: dictionary of attributes for the new child
656 @param content: unicode that is appended to the child content 663 @param content: unicode that is appended to the child content
657 @param profile: the profile from which the message is sent 664 @param profile: the profile from which the message is sent
658 @return: a Deferred instance 665 @return: a Deferred instance
659 """ 666 """