summaryrefslogtreecommitdiff
path: root/swiftstory/client.py
blob: 28fd6c0fd451abac2ab37bf123df65783a700f01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import asyncio
import logging
from typing import Optional

import websockets.exceptions
from websockets.server import WebSocketServerProtocol

from swiftstory.game import Game
from swiftstory.game_manager import GameManager
from swiftstory.exception import WrongAction, UnsupportedLanguage, JoinError
from swiftstory.player import Player


class Client:
    """ Represent a client.
    A client manages a (web)socket to communicate with the outside world.
    It also manages the associated player when in a game. """
    def __init__(self, socket: WebSocketServerProtocol, game_manager: GameManager) -> None:
        self.game: Optional[Game] = None
        self.game_manager: GameManager = game_manager

        self.socket = socket
        self.player: Optional[Player] = None

    def join_game(self, game_name: str, lang: Optional[str]) -> str:
        if self.game is not None:
            raise WrongAction('You are already in a game')

        if lang is None:
            lang = 'en'

        try:
            game = self.game_manager.find_by_name(game_name, lang)
        except UnsupportedLanguage as e:
            raise JoinError(f"unsupported language: {str(e)}") from e

        self.player = Player()
        status = game.try_join(self.player)
        self.game = game
        self.monitor_player()

        return status

    def play_white_card(self, card_id: int) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_play_card(self.player, card_id)

    def pick_black_card(self) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_become_judge(self.player)

    def collect_cards(self) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_collect_cards(self.player)

    def designate_card(self, card_id: Optional[int]) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_designate_card(self.player, card_id)

    def view_player_cards(self) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_view_player_cards(self.player)

    def view_played_cards(self) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_view_played_cards(self.player)

    def view_black_card(self) -> str:
        if self.game is None:
            raise WrongAction('You have to join a game first')
        if self.player is None:
            raise ValueError("Player is None")
        return self.game.try_view_black_card(self.player)

    def monitor_player(self) -> None:
        """ Start monitoring the player for notifications and send them. """
        async def f() -> None:
            assert self.player is not None
            while True:
                try:
                    notif = await self.player.notifications.get()
                    await self.socket.send(notif)
                except websockets.exceptions.ConnectionClosed:
                    logging.warning("Recipient has disconnected.")

        asyncio.create_task(f())

    def disconnect(self) -> None:
        if self.player is not None:
            if self.game is None:
                raise ValueError("Disconnect from inexistent game.")
            self.game.disconnect(self.player)