Mercurial > libervia-backend
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 """ |