示例#1
0
    async def handle_login(self, client: Client, msg: ProtocolMessage):
        params: Dict[str, Any] = msg.parameters
        answer: Optional[ProtocolMessage] = None

        if client.state is not ClientConnectionState.NOT_CONNECTED:
            answer = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_ALREADY_LOGGED_IN)
        elif len(params["username"]) > ProtocolConfig.USERNAME_MAX_LENGTH:
            answer = ProtocolMessage.create_error(
                ErrorCode.SYNTAX_USERNAME_TOO_LONG)
        else:
            login_successful: bool = self.login_user(params["username"],
                                                     client)
            if not login_successful:
                answer = ProtocolMessage.create_error(
                    ErrorCode.PARAMETER_USERNAME_ALREADY_EXISTS)
            else:
                client.state = ClientConnectionState.GAME_SELECTION
                #self.users[username].state = ClientConnectionState.GAME_SELECTION
                client.username = params["username"]
                self.users[client.username] = client
                self.print_client(
                    client, "Client successfully logged in with '{}'".format(
                        client.username))
                await self.send_games_to_user(client)

        if answer is not None:
            await self.send(client, answer)

        self.print_stats()
示例#2
0
    async def handle_msg(self, client: Client, msg: ProtocolMessage):

        if msg.missing_or_unkown_param:
            answer: ProtocolMessage = ProtocolMessage.create_error(
                ErrorCode.SYNTAX_MISSING_OR_UNKNOWN_PARAMETER)
            await self.send(client, answer)

        # No other command is permitted if the client is not logged in
        elif client.state is ClientConnectionState.NOT_CONNECTED and msg.type is not ProtocolMessageType.LOGIN:
            answer = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_NOT_LOGGED_IN)
            await self.send(client, answer)

        elif msg.type == ProtocolMessageType.LOGIN:
            await self.handle_login(client, msg)

        elif msg.type == ProtocolMessageType.LOGOUT:
            await self.handle_logout(client, msg)

        elif msg.type == ProtocolMessageType.CHAT_SEND:
            await self.handle_chat_send(client, msg)

        elif msg.type == ProtocolMessageType.GET_GAMES:
            await self.handle_get_games(client, msg)

        elif msg.type == ProtocolMessageType.CREATE_GAME:
            await self.handle_create_game(client, msg)

        # handle_cancel handles the case when the user has not created a game
        elif msg.type == ProtocolMessageType.CANCEL:
            await self.handle_cancel(client, msg)

        # handle_join handles the case that they created a game themselves
        elif msg.type == ProtocolMessageType.JOIN:
            await self.handle_join(client, msg)

        # all the following messages are only valid when playing
        elif not client.state == ClientConnectionState.PLAYING:
            answer = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_NOT_IN_GAME)
            await self.send(client, answer)

        elif msg.type == ProtocolMessageType.PLACE:
            await self.handle_place(client, msg)

        elif msg.type == ProtocolMessageType.ABORT:
            await self.handle_abort(client, msg)

        elif msg.type == ProtocolMessageType.MOVE:
            await self.handle_move(client, msg)

        elif msg.type == ProtocolMessageType.SHOOT:
            await self.handle_shoot(client, msg)
示例#3
0
    async def handle_move(self, client: Client, msg: ProtocolMessage):
        our_ctrl: GameController = self.user_game_ctrl[client.username]
        other_ctrl: GameController = self.user_game_ctrl[
            our_ctrl.opponent_name]

        try:
            positions: Positions = our_ctrl.run(msg)
        except BattleshipError as e:
            answer: ProtocolMessage = ProtocolMessage.create_error(
                e.error_code)
            await self.send(client, answer)
            return
        except Exception as e:
            raise e

        our_ctrl.timeout_counter = 0
        our_ctrl.cancel_timeout()

        # notify
        params = {}
        if not len(positions.positions) == 0:
            params["positions"] = positions
        msg_moved: ProtocolMessage = ProtocolMessage.create_single(
            ProtocolMessageType.MOVED, params)

        await self.send(other_ctrl.client, msg_moved)
        await self.send(our_ctrl.client, msg_moved)
        our_ctrl.run(msg_moved)
        other_ctrl.run(msg_moved)

        other_ctrl.start_timeout(self.handle_timeout_wrapper)
示例#4
0
    async def handle_create_game(self, client: Client, msg: ProtocolMessage):

        if client.state in [
                ClientConnectionState.GAME_CREATED,
                ClientConnectionState.PLAYING
        ]:
            # TODO: in the case of GAME_CREATED, this is more or less in line with the RFC
            # TODO: in the case of PLAYING, a better error code would be nice
            msg_error = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_NUMBER_OF_GAMES_LIMIT_EXCEEDED)
            await self.send(client, msg_error)
            return

        game_id: int = ServerLobbyController.next_game_id
        ServerLobbyController.next_game_id += 1

        game_controller: GameController = await GameController.create_from_msg(
            game_id, client, self.loop, msg, client.username)

        if game_controller is not None:
            client.state = ClientConnectionState.GAME_CREATED
            self.user_gid[client.username] = game_id
            self.games[game_id] = (game_controller, None)
            self.user_game_ctrl[client.username] = game_controller
            # and send the game to all users
            game_msg: ProtocolMessage = game_controller.to_game_msg()
            await self.msg_to_all(game_msg)

            self.print_stats()
        # the game controller already sends the error messages, so this is fine.
        # TODO: move this here to be consistent.
        else:
            pass
示例#5
0
    async def handle_chat_send(self, client: Client, msg: ProtocolMessage):
        params: Dict[str, Any] = msg.parameters
        answer: Optional[ProtocolMessage] = None
        forward: ProtocolMessage

        text: str = params["text"]
        recipient: str = params["username"]

        if len(text) > ProtocolConfig.CHAT_MAX_TEXT_LENGTH:
            answer = ProtocolMessage.create_error(
                ErrorCode.SYNTAX_MESSAGE_TEXT_TOO_LONG)

        # check if the message is for all users
        elif recipient == "":
            forward = ProtocolMessage.create_single(
                ProtocolMessageType.CHAT_RECV, {
                    "sender": client.username,
                    "recipient": "",
                    "text": text
                })
            await self.msg_to_all_but_one(forward, client.username)
            self.print_client(
                client,
                "Forwarding chat message to all logged in users but {}".format(
                    client.username))

        elif recipient not in self.users:
            answer = ProtocolMessage.create_error(
                ErrorCode.PARAMETER_USERNAME_DOES_NOT_EXIST)

        else:
            forward = ProtocolMessage.create_single(
                ProtocolMessageType.CHAT_RECV, {
                    "sender": client.username,
                    "recipient": recipient,
                    "text": text
                })
            await self.msg_to_user(forward, recipient)
            self.print_client(
                client, "Forwarding chat message to '{}'".format(recipient))

        if answer is not None:
            await self.send(client, answer)
示例#6
0
 async def handle_cancel(self, client: Client, msg: ProtocolMessage):
     if client.state == ClientConnectionState.GAME_CREATED:
         game_id: int = self.user_gid[client.username]
         del self.user_gid[client.username]
         del self.user_game_ctrl[client.username]
         del self.games[game_id]
         client.state = ClientConnectionState.GAME_SELECTION
         await self.send_delete_game(game_id)
     else:
         msg_error: ProtocolMessage = ProtocolMessage.create_error(
             ErrorCode.PARAMETER_UNKNOWN_GAME_ID)
         await self.send(client, msg_error)
示例#7
0
    async def handle_shoot(self, client: Client, msg: ProtocolMessage):
        our_ctrl: GameController = self.user_game_ctrl[client.username]
        other_ctrl: GameController = self.user_game_ctrl[
            our_ctrl.opponent_name]

        try:
            hit: bool = other_ctrl.run(msg)
        except BattleshipError as e:
            answer = ProtocolMessage.create_error(e.error_code)
            await self.send(client, answer)
            return
        except Exception as e:
            raise e

        our_ctrl.timeout_counter = 0
        our_ctrl.cancel_timeout()

        if hit:
            sunk: bool = other_ctrl.ship_sunk_at_pos(
                msg.parameters["position"].horizontal,
                msg.parameters["position"].vertical)
            msg_hit: ProtocolMessage = ProtocolMessage.create_single(
                ProtocolMessageType.HIT, {
                    "sunk": sunk,
                    "position": msg.parameters["position"]
                })

            await self.send(other_ctrl.client, msg_hit)
            await self.send(our_ctrl.client, msg_hit)
            our_ctrl.run(msg_hit)
            other_ctrl.run(msg_hit)

            our_ctrl.start_timeout(self.handle_timeout_wrapper)

            if other_ctrl.all_ships_sunk():
                await self.end_game_with_reason(our_ctrl, other_ctrl,
                                                EndGameReason.YOU_WON,
                                                EndGameReason.OPPONENT_WON)

        else:
            msg_fail: ProtocolMessage = ProtocolMessage.create_single(
                ProtocolMessageType.FAIL,
                {"position": msg.parameters["position"]})

            await self.send(other_ctrl.client, msg_fail)
            await self.send(our_ctrl.client, msg_fail)
            our_ctrl.run(msg_fail)
            other_ctrl.run(msg_fail)

            other_ctrl.start_timeout(self.handle_timeout_wrapper)
示例#8
0
    async def handle_place(self, client: Client, msg: ProtocolMessage):
        our_ctrl: GameController = self.user_game_ctrl[client.username]
        other_ctrl: GameController = self.user_game_ctrl[
            our_ctrl.opponent_name]

        try:
            our_ctrl.run(msg)
        except BattleshipError as e:
            # TODO: maybe check if it's not an internal error
            answer: ProtocolMessage = ProtocolMessage.create_error(
                e.error_code)
            await self.send(client, answer)
            return
        except Exception as e:
            raise e

        # notify the other
        msg_placed: ProtocolMessage = ProtocolMessage.create_single(
            ProtocolMessageType.PLACED)
        await self.send(other_ctrl.client, msg_placed)

        # if both are on waiting, the game can start
        if our_ctrl.state == GameState.WAITING and other_ctrl.state == GameState.WAITING:
            # who starts?
            starting_ctrl: GameController
            waiting_ctrl: GameController
            (starting_ctrl, waiting_ctrl) = (
                our_ctrl, other_ctrl) if randrange(2) == 1 else (other_ctrl,
                                                                 our_ctrl)

            youstart: ProtocolMessage = ProtocolMessage.create_single(
                ProtocolMessageType.YOUSTART)
            await self.send(starting_ctrl.client, youstart)
            starting_ctrl.run(youstart)
            starting_ctrl.timeout_counter = 0
            starting_ctrl.start_timeout(self.handle_timeout_wrapper)

            youwait: ProtocolMessage = ProtocolMessage.create_single(
                ProtocolMessageType.WAIT)
            await self.send(waiting_ctrl.client, youwait)
            waiting_ctrl.run(youwait)

            # TODO: fix this, should be merged with state, is this even used anymore?
            starting_ctrl._game_started = True
            waiting_ctrl._game_started = True
示例#9
0
    async def msg_callback(msg: ProtocolMessage):
        logging.debug("< {}".format(msg))

        # if we receive a non error message, we take this as a hint that we are successfully logged in
        if lobby_controller.state is ClientConnectionState.NOT_CONNECTED and not msg.type == ProtocolMessageType.ERROR:
            lobby_controller.state = ClientConnectionState.CONNECTED

        if msg.type == ProtocolMessageType.ERROR:
            pass
        elif msg.type == ProtocolMessageType.GAMES:
            await lobby_controller.handle_games(msg)
        elif msg.type == ProtocolMessageType.GAME:
            await lobby_controller.handle_game(msg)
        elif msg.type == ProtocolMessageType.DELETE_GAME:
            await lobby_controller.handle_delete_game(msg)
        elif msg.type == ProtocolMessageType.CHAT_RECV:
            if in_lobby_or_battle is True:
                await lobby_controller.handle_chat_recv(msg)
        elif msg.type == ProtocolMessageType.HIT:
            await lobby_controller.handle_hit(msg)
        elif msg.type == ProtocolMessageType.WAIT:
            await lobby_controller.handle_wait(msg)
        elif msg.type == ProtocolMessageType.YOUSTART:
            await lobby_controller.handle_youstart(msg)
        elif msg.type == ProtocolMessageType.TIMEOUT:
            await lobby_controller.handle_timeout(msg)
        elif msg.type == ProtocolMessageType.FAIL:
            await lobby_controller.handle_fail(msg)
        elif msg.type == ProtocolMessageType.ENDGAME:
            await lobby_controller.handle_endgame(msg)
        elif msg.type == ProtocolMessageType.MOVED:
            await lobby_controller.handle_moved(msg)
        elif msg.type == ProtocolMessageType.STARTGAME:
            await lobby_controller.handle_start_game(msg)
        elif msg.type == ProtocolMessageType.PLACED:
            await lobby_controller.handle_placed(msg)
        elif msg.type == ProtocolMessageType.NONE:
            err: ProtocolMessage = ProtocolMessage.create_error(
                ErrorCode.UNKNOWN)
            await lobby_controller.client.send(err)
        # add the other types if needed
        else:
            pass
示例#10
0
    async def handle_join(self, client: Client, msg: ProtocolMessage):
        answer: Optional[ProtocolMessage] = None

        game_id: int = msg.parameters["game_id"]

        if not client.state == ClientConnectionState.GAME_SELECTION:
            # TODO: this is not really the right error message, but… there is no other
            answer = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_GAME_ALREADY_STARTED)

        # there is no available game with the specified game_ID (error code 104)
        elif not game_id in self.games.keys():
            answer = ProtocolMessage.create_error(
                ErrorCode.PARAMETER_UNKNOWN_GAME_ID)

        # the message lacks the password parameter although a password is required (error code 105)
        elif self.games[game_id][
                0].options == GameOptions.PASSWORD and not "password" in msg.parameters:
            answer = ProtocolMessage.create_error(
                ErrorCode.PARAMETER_PASSWORD_REQUIRED)

        # a password is required for the game, but the given password is incorrect (error code 106)
        elif self.games[game_id][
                0].options == GameOptions.PASSWORD and not msg.parameters[
                    "password"] == self.games[game_id][0].password:
            answer = ProtocolMessage.create_error(
                ErrorCode.PARAMETER_INVALID_PASSWORD)

        # the user wants to join his own game (error code 107)
        elif self.games[game_id][0].username == client.username:
            answer = ProtocolMessage.create_error(
                ErrorCode.PARAMETER_ILLEGAL_JOIN)

        # the game has already started (error code 8)
        elif not self.games[game_id][0].state == GameState.IN_LOBBY:
            answer = ProtocolMessage.create_error(
                ErrorCode.ILLEGAL_STATE_GAME_ALREADY_STARTED)

        # Everything ok, let them play
        else:
            # setup a game_controller for the other one
            game_controller1: GameController = self.games[game_id][0]
            game_controller1.opponent_name = client.username
            game_controller1.state = GameState.PLACE_SHIPS

            client1: Client = self.games[game_id][0].client

            game_controller2: GameController = GameController.create_from_existing_for_opponent(
                game_controller1, client)
            game_controller2.state = GameState.PLACE_SHIPS

            self.games[game_id] = (self.games[game_id][0], game_controller2)

            self.user_gid[client.username] = game_id
            # this is already done for the other user

            self.user_game_ctrl[client.username] = game_controller2

            # set client states
            client.state = ClientConnectionState.PLAYING
            client1.state = ClientConnectionState.PLAYING

            # send startgame messages
            await self.send(client, game_controller2.to_start_game_msg())
            await self.send(client1, game_controller1.to_start_game_msg())

            # inform the other users the game is no longer available
            await self.send_delete_game(game_id)

            self.print_stats()

        if answer is not None:
            await self.send(client, answer)