Example #1
0
    def __init__(self, test_mode=False):
        self._logger = logging.getLogger('ecbb.gm')

        # TODO::limit the number of games loaded in memory at the same
        # time to avoid too important memory consumption
        self._games = {}
        self._web_players = {}
        self._db = DBInterface(test_mode)

        # load constants from DB
        (status_ext, self.ext_infos) = self._db.get_extensions_infos()
        (status_tz, self.timezones) = self._db.get_timezones()
        # if a database error occurs so early, just quit
        if status_ext != DB_STATUS.OK or status_tz != DB_STATUS.OK:
            sys.exit()
Example #2
0
class GamesManager(object):
    """
    As we can have multiple games running at the same time we need an
    object to handle them.
    The entry point for the webserver view.
    Save the games in the database after each turn.
    Load running games from the database (after a program stop).
    Allow to browse ended games.

    attributes:
      self._games (dict): the games, accessed by their id
      self._web_players (dict): the players, accessed by their id
      self._db (DBInterface object): the DB interface object
    """
    def __init__(self, test_mode=False):
        self._logger = logging.getLogger('ecbb.gm')

        # TODO::limit the number of games loaded in memory at the same
        # time to avoid too important memory consumption
        self._games = {}
        self._web_players = {}
        self._db = DBInterface(test_mode)

        # load constants from DB
        (status_ext, self.ext_infos) = self._db.get_extensions_infos()
        (status_tz, self.timezones) = self._db.get_timezones()
        # if a database error occurs so early, just quit
        if status_ext != DB_STATUS.OK or status_tz != DB_STATUS.OK:
            sys.exit()

    def debug(self, msg):
        """ called from mako templates to log stuffs """
        self._logger.debug(msg)

    @engine.util.log
    def create_game(self, creator_id, name, level, private, password,
                    num_players, players_ids, extensions):
        """ Instanciate a new game. Save it to the database to get its uniq id.
        Store it in self._games. Do not return the created game nor its id, the
        game will be visible to the player in the 'my games' view.

        args:
         creator_id (int): id of the player creating the game
         name (str): name of the game given by the creator
         level (int): difficulty (1-4)
         private (bool): True if password protected to join
         password (str): the password to join if private
         num_players (int): number of players in the game
         players_ids [int, ..., int]: list of players id already added to the
                                      game, includes the creator_id
         extensions {id (int) -> name (str)}: activated extensions
        return: db_ok (bool)
        """
        game = Game(creator_id, name, level, private, password,
                    num_players, extensions, init_state=True)

        (status, game) = self._db.create_game(game, players_ids)
        if status != DB_STATUS.OK:
            self._logger.error(("Error inserting game {!r} by {} in "
                                "database.").format(name, creator_id))
            return False

        (status, _) = self._db.save_state(game)
        if status != DB_STATUS.OK:
            self._logger.error("Error saving game {} state.".format(game.id_))
            return False

        self._logger.info("Game {!r} successfully created".format(name))

        self._games[game.id_] = game
        return True
        
    @engine.util.log
    def save_game(self, game):
        """ update an existing game into the database.
        first save the current state to get its id, then update the game in db.
        args: game (Game object): the game to save
        return: db_ok (bool), upd_ok (bool)
        """
        (status, _) = self._db.save_state(game)
        if status != DB_STATUS.OK:
            return (False, False)

        (status, _) = self._db.save_game(game)
        return (status != DB_STATUS.ERROR, status != DB_STATUS.NO_ROWS)

    @engine.util.log
    def load_game(self, game_id, force=False):
        """ load a game from the database if not already in memory.
        fully load a game, i.e. also load:
         -players
         -extensions ids
         -states ids
         -current state

        args:
         game_id (int): id of the game to load
         force (bool): to force the loading of the game from db even if the game
                       is already in memory
        return:
         OK: (db_ok (bool): True, fully loaded Game object)
         NO_GAME: (db_ok: True, None)
         ERROR: (db_ok: False, None)
        """
        if not force and game_id in self._games:
            return (True, self._games[game_id])

        # load game
        (status, game) = self._db.load_game(game_id)
        if status == DB_STATUS.ERROR:
            self._logger.error("Error loading game {}".format(game_id))
            return (False, None)
        elif status == DB_STATUS.NO_ROWS:
            self._logger.warning("Game {} not found".format(game_id))
            return (True, None)

        # load players
        (status, players_ids) = self._db.get_game_players_ids(game_id)
        if status != DB_STATUS.OK:
            self._logger.error(("Error loading players ids for game "
                                "{}").format(game_id))
            return (False, None)

        game.players_ids = players_ids
        for player_id in players_ids:
            if self.get_player(player_id) == None:
                self._logger.error(("Error loading player {} for game "
                                    "{}").format(player_id, game_id))
                return (False, None)

        # load extensions
        (status, ext) = self._db.get_game_ext(game_id)
        if status != DB_STATUS.OK:
            self._logger.error(("Error loading extensions for game "
                                "{}").format(game_id))
            return (False, None)
        game.extensions = ext

        # load states
        (status, states_ids) = self._db.get_game_states_ids(game_id)
        if status != DB_STATUS.OK:
            self._logger.error(("Error loading states ids for game {}"
                                "").format(game_id))
            return (False, None)
        game.states_ids = states_ids

        # load current state
        (status, state) = self._db.load_state(game.cur_state_id())
        if status != DB_STATUS.OK:
            self._logger.error(("Error loading state {} for game {}"
                                "").format(game.cur_state_id(), game.id_))
            return (False, None)
        game.cur_state = state

        self._games[game_id] = game

        return (True, self._games[game_id])

    @engine.util.log
    def get_game(self, game_id):
        """
        Returns the game, do NOT load it from database if not already in memory.
        The caller must have call load_game manually before.

        args: game_id (int)
        return:
         OK: Game object whose id is game_id
         ERROR: Raise a KeyError exception
        """
        return self._games[game_id]

    @engine.util.log
    def get_my_games(self, player_id):
        """ return the games the player is currently playing
        even the non started ones.
        args: player_id (int)
        return: (db_ok (bool), [game_1, ..., game_n])
        """
        status, my_games_ids = self._db.get_my_games_ids(player_id)
        if status != DB_STATUS.OK:
            return (False, None)

        my_games = []
        for id_ in my_games_ids:
            db_ok, game = self.load_game(id_)
            if db_ok and game is not None:
                my_games.append(game)

        return (True, my_games)

    @engine.util.log
    def get_pub_priv_games(self):
        """ return the not yet started, not ended games, public and private.
        args: None
        return:
         OK: (db_ok (bool): True,
              pubs ([game_1, ..., game_n]),
              privs ([game_1, ..., game_n]))
         ERROR: (db_ok (bool): False, None, None)
        """
        status, ids = self._db.get_pub_priv_games_ids()
        if status == DB_STATUS.ERROR:
            return (False, None, None)

        pub_ids, priv_ids = ids

        pub_games = []
        for id_ in pub_ids:
            db_ok, game = self.load_game(id_)
            if db_ok and game is not None:
                pub_games.append(game)

        priv_games = []
        for id_ in priv_ids:
            db_ok, game = self.load_game(id_)
            if db_ok and game is not None:
                priv_games.append(game)

        return (True, pub_games, priv_games)

    @engine.util.log
    def _load_player(self, player_id):
        """ load a player from the database.
        args: player_id (int)
        return:
         OK: player_id (int)
         ERROR: None
        """
        (status, player) = self._db.load_player(player_id)
        # TODO::handle NO_ROWS and ERROR
        if status != DB_STATUS.OK:
            return None
        else:
            self._web_players[player_id] = player
            return player_id

    @engine.util.log
    def get_player(self, player_id):
        """ load player if not present then return it.
        args: player_id (int)
        return:
         OK: Player object
         ERROR: None
        """
        if player_id not in self._web_players:
            if self._load_player(player_id) is None:
                return None

        return self._web_players[player_id]

    @engine.util.log
    def get_players_infos(self):
        """ get minimal infos (id and name) on all registered players.
        args: None
        return:
         OK: [(id_1, name_1), ..., (id_n, name_n)] ordered by name
         ERROR: None
        """
        (status, players_infos) = self._db.get_players_infos()
        if status != DB_STATUS.OK:
            return None
        else:
            return players_infos

    @engine.util.log
    def auth_player(self, email, password):
        """ check player password with the one encrypted in database.
        args:
         email (str): user email
         password (str): plain password
        return: (db_ok (bool), auth_ok (bool), player_id (int))
        """
        (status, id_) = self._db.auth_player(email, password)
        if status == DB_STATUS.OK:
            return (True, True, id_)
        elif status == DB_STATUS.NO_ROWS:
            return (True, False, None)
        else:
            return (False, None, None)

    @engine.util.log
    def create_player(self, name, email, password, tz_id):
        """ create a new player in the db.
        args:
         name (str): player name
         email (str): player email
         password (str): player plain password
         tz_id (int): id of the player timezone
        return: (db_ok (bool), dup_ok (bool), player_id (int))
        """
        (status, id_) = self._db.create_player(name, email, password, tz_id)
        if status == DB_STATUS.OK:
            return (True, True, id_)
        elif status == DB_STATUS.DUP_ERROR:
            return (True, False, None)
        else:
            return (False, None, None)

    @engine.util.log
    def update_player(self, player, to_update):
        """ update player email/password/tz_id in the db.
        args:
         player: unmodified Player object to update.
         to_update (dict): possible keys:
          email (str): new player email
          password (str): new player password
          timezone (int): new timezone id
        return: (db_ok (bool), upd_ok (bool))
        """
        (status, dummy) = self._db.update_player(player, to_update)
        if status == DB_STATUS.OK:
            # update player in memory
            if self._load_player(player.id_) is None:
                return (False, True)
            else:
                return (True, True)
        elif status == DB_STATUS.DUP_ERROR or status == DB_STATUS.NO_ROWS:
            return (True, False)
        else:
            return(False, False)