Esempio n. 1
0
    def data_received(self, data):
        if self.data:
            data = self.data + data
            self.data = None

        if self.parse_data(data):
            log.info('[REQUEST] Player: {}, action: {!r}, message:\n{}'.format(
                self.player.idx
                if self.player is not None else self.client_address,
                Action(self.action), self.message),
                     game=self.game)

            try:
                data = json.loads(self.message)
                if not isinstance(data, dict):
                    raise errors.BadCommand(
                        'The command\'s payload is not a dictionary')
                if self.observer:
                    self.write_response(
                        *self.observer.action(self.action, data))
                else:
                    if self.action not in self.ACTION_MAP or self.action in CONFIG.HIDDEN_COMMANDS:
                        raise errors.BadCommand('No such action: {}'.format(
                            self.action))
                    method = self.ACTION_MAP[self.action]
                    result, message = method(self, data)
                    self.write_response(result, message)

                    if not self.observer and self.action in self.REPLAY_ACTIONS:
                        game_db.add_action(self.game_idx,
                                           self.action,
                                           message=data,
                                           player_idx=self.player.idx)

            # Handle errors:
            except (json.decoder.JSONDecodeError, errors.BadCommand) as err:
                self.error_response(Result.BAD_COMMAND, err)
            except errors.AccessDenied as err:
                self.error_response(Result.ACCESS_DENIED, err)
            except errors.InappropriateGameState as err:
                self.error_response(Result.INAPPROPRIATE_GAME_STATE, err)
            except errors.Timeout as err:
                self.error_response(Result.TIMEOUT, err)
            except errors.ResourceNotFound as err:
                self.error_response(Result.RESOURCE_NOT_FOUND, err)
            except Exception:
                log.exception(
                    'Got unhandled exception on client command execution',
                    game=self.game)
                self.error_response(Result.INTERNAL_SERVER_ERROR)
            finally:
                self.action = None
                self.message_len = None
                self.message = None
Esempio n. 2
0
 def on_observer(self, _):
     if self.game or self.observer:
         raise errors.BadCommand('Impossible to connect as observer')
     else:
         self.observer = Observer()
         message = self.observer.games_to_json_str()
         return Result.OKEY, message
Esempio n. 3
0
    def on_login(self, data: dict):
        if self.game is not None or self.player is not None:
            raise errors.BadCommand('You are already logged in')

        self.check_keys(data, ['name'])
        player_name = data['name']
        password = data.get('password', None)

        player = Player.get(player_name, password=password)
        if not player.check_password(password):
            raise errors.AccessDenied('Password mismatch')

        game_name = data.get('game', 'Game of {}'.format(player_name))
        num_players = data.get('num_players', CONFIG.DEFAULT_NUM_PLAYERS)
        num_turns = data.get('num_turns', CONFIG.DEFAULT_NUM_TURNS)

        game = Game.get(game_name,
                        num_players=num_players,
                        num_turns=num_turns)

        game.check_state(GameState.INIT, GameState.RUN)
        player = game.add_player(player)
        self.game = game
        self.game_idx = game.game_idx
        self.player = player

        log.info('Player successfully logged in: {}'.format(player),
                 game=self.game)
        message = self.player.to_json_str()

        return Result.OKEY, message
Esempio n. 4
0
 def check_keys(data: dict, keys, agg_func=all):
     if not agg_func([k in data for k in keys]):
         raise errors.BadCommand(
             'The command\'s payload does not contain all needed keys, '
             'following keys are expected: {}'.format(keys))
     else:
         return True
Esempio n. 5
0
    def action(self, action, data):
        """ Interprets observer's actions.
        """
        if action not in self.ACTION_MAP:
            raise errors.BadCommand('No such action: {}'.format(action))

        method = self.ACTION_MAP[action]
        return method(self, data)
Esempio n. 6
0
 def __init__(self,
              name,
              observed=False,
              map_name=None,
              num_players=CONFIG.DEFAULT_NUM_PLAYERS,
              num_turns=CONFIG.DEFAULT_NUM_TURNS):
     super(Game, self).__init__(name=name)
     log.info('Create game, name: \'{}\''.format(self.name))
     self.name = name
     self.state = GameState.INIT
     self.current_tick = 0
     self.observed = observed
     self.num_players = num_players
     self.num_turns = num_turns
     self.map = Map(use_active=True) if map_name is None else Map(
         name=map_name)
     if self.num_players > len(self.map.towns):
         raise errors.BadCommand(
             'Unable to create game with {} players, maximum players count is {}'
             .format(self.num_players, len(self.map.towns)))
     if self.observed:
         self.game_idx = 0
     else:
         self.game_idx = game_db.add_game(name,
                                          self.map.idx,
                                          num_players=num_players,
                                          num_turns=num_turns)
     self.players = {}
     self.trains = {}
     self.next_train_moves = {}
     self.event_cooldowns = CONFIG.EVENT_COOLDOWNS_ON_START.copy()
     self._lock = Lock()
     self._stop_event = Event()
     self._start_tick_event = Event()
     self._tick_done_condition = Condition()
     random.seed()
Esempio n. 7
0
    def make_upgrade(self, player: Player, posts_idx=(), trains_idx=()):
        """ Upgrades given Posts and Trains to next level.
        """
        # Get posts from request:
        posts = []
        for post_idx in posts_idx:
            if post_idx not in self.map.posts:
                raise errors.ResourceNotFound(
                    'Post index not found, index: {}'.format(post_idx))
            post = self.map.posts[post_idx]
            if post.type != PostType.TOWN:
                raise errors.BadCommand(
                    'The post is not a Town, post: {}'.format(post))
            if post.player_idx != player.idx:
                raise errors.AccessDenied('Town\'s owner mismatch')
            posts.append(post)

        # Get trains from request:
        trains = []
        for train_idx in trains_idx:
            if train_idx not in self.trains:
                raise errors.ResourceNotFound(
                    'Train index not found, index: {}'.format(train_idx))
            train = self.trains[train_idx]
            if train.player_idx != player.idx:
                raise errors.AccessDenied('Train\'s owner mismatch')
            trains.append(train)

        # Check existence of next level for each entity:
        posts_has_next_lvl = all(
            [p.level + 1 in CONFIG.TOWN_LEVELS for p in posts])
        trains_has_next_lvl = all(
            [t.level + 1 in CONFIG.TRAIN_LEVELS for t in trains])
        if not all([posts_has_next_lvl, trains_has_next_lvl]):
            raise errors.BadCommand(
                'Not all entities requested for upgrade have next levels')

        # Check armor quantity for upgrade:
        armor_to_up_posts = sum([p.next_level_price for p in posts])
        armor_to_up_trains = sum([t.next_level_price for t in trains])
        armor_to_up = sum([armor_to_up_posts, armor_to_up_trains])
        if player.town.armor < armor_to_up:
            raise errors.BadCommand(
                'Not enough armor resource for upgrade, player\'s armor: {}, '
                'armor needed to upgrade: {}'.format(player.town.armor,
                                                     armor_to_up))

        # Check that trains are in town now:
        for train in trains:
            if not self.is_train_at_post(train, post_to_check=player.town):
                raise errors.BadCommand(
                    'The train is not in Town now, train: {}'.format(train))

        # Upgrade entities:
        for post in posts:
            player.town.armor -= post.next_level_price
            post.set_level(post.level + 1)
            log.info('Post has been upgraded, post: {}'.format(post),
                     game=self)
        for train in trains:
            player.town.armor -= train.next_level_price
            train.set_level(train.level + 1)
            log.info('Train has been upgraded, post: {}'.format(train),
                     game=self)
Esempio n. 8
0
    def move_train(self, player, train_idx, speed, line_idx):
        """ Process action MOVE. Changes path or speed of the Train.
        """
        if train_idx not in self.trains:
            raise errors.ResourceNotFound(
                'Train index not found, index: {}'.format(train_idx))
        if line_idx not in self.map.lines:
            raise errors.ResourceNotFound(
                'Line index not found, index: {}'.format(line_idx))
        train = self.trains[train_idx]
        if train.player_idx != player.idx:
            raise errors.AccessDenied('Train\'s owner mismatch')
        if train_idx in self.next_train_moves:
            self.next_train_moves.pop(train_idx)

        # Check cooldown for the train:
        if train.cooldown > 0:
            raise errors.BadCommand(
                'The train is under cooldown, cooldown: {}'.format(
                    train.cooldown))

        # Stop the train; reverse direction on move; continue run the train:
        if speed == 0 or train.line_idx == line_idx:
            train.speed = speed

        # The train is standing:
        elif train.speed == 0:
            # The train is standing at the end of the line:
            if self.map.lines[train.line_idx].length == train.position:
                line_from = self.map.lines[train.line_idx]
                line_to = self.map.lines[line_idx]
                if line_from.points[1] in line_to.points:
                    train.line_idx = line_idx
                    train.speed = speed
                    if line_from.points[1] == line_to.points[0]:
                        train.position = 0
                    else:
                        train.position = line_to.length
                else:
                    raise errors.BadCommand(
                        'The end of the train\'s line is not connected to the next line, '
                        'train\'s line: {}, next line: {}'.format(
                            line_from, line_to))
            # The train is standing at the beginning of the line:
            elif train.position == 0:
                line_from = self.map.lines[train.line_idx]
                line_to = self.map.lines[line_idx]
                if line_from.points[0] in line_to.points:
                    train.line_idx = line_idx
                    train.speed = speed
                    if line_from.points[0] == line_to.points[0]:
                        train.position = 0
                    else:
                        train.position = line_to.length
                else:
                    raise errors.BadCommand(
                        'The beginning of the train\'s line is not connected to the next line, '
                        'train\'s line: {}, next line: {}'.format(
                            line_from, line_to))
            # The train is standing on the line (between line's points), player have to continue run the train.
            else:
                raise errors.BadCommand(
                    'The train is standing on the line (between line\'s points), '
                    'player have to continue run the train')

        # The train is moving on the line (between line's points):
        elif train.speed != 0 and train.line_idx != line_idx:
            switch_line_possible = False
            line_from = self.map.lines[train.line_idx]
            line_to = self.map.lines[line_idx]
            if train.speed > 0 and speed > 0:
                switch_line_possible = (
                    line_from.points[1] == line_to.points[0])
            elif train.speed > 0 and speed < 0:
                switch_line_possible = (
                    line_from.points[1] == line_to.points[1])
            elif train.speed < 0 and speed > 0:
                switch_line_possible = (
                    line_from.points[0] == line_to.points[0])
            elif train.speed < 0 and speed < 0:
                switch_line_possible = (
                    line_from.points[0] == line_to.points[1])

            # This train move request is valid and will be applied later:
            if switch_line_possible:
                self.next_train_moves[train_idx] = {
                    'speed': speed,
                    'line_idx': line_idx
                }
            # This train move request is invalid:
            else:
                raise errors.BadCommand(
                    'The train is not able to switch the current line to the next line, '
                    'or new speed is incorrect, train\'s line: {}, next line: {}, '
                    'train\'s speed: {}, new speed: {}'.format(
                        line_from, line_to, train.speed, speed))
Esempio n. 9
0
 def wrapped(self, *args, **kwargs):
     if self.game is None:
         raise errors.BadCommand('A game is not chosen')
     else:
         return func(self, *args, **kwargs)