From 34c519345efdb7c236586d093e99e36df87d80e4 Mon Sep 17 00:00:00 2001 From: legonzaur Date: Wed, 27 Mar 2024 18:43:58 +0100 Subject: [PATCH] working on clientController : register POC --- requirements.txt | 3 ++ src/controller/__init__.py | 5 ++- src/controller/client.py | 80 ++++++++++++++++++++++++++++++++++++-- src/controller/game.py | 15 ++++++- src/game/game.py | 26 +++++-------- src/game/player.py | 5 ++- src/main.py | 9 +++-- src/netcode/models.py | 19 +++++++++ 8 files changed, 136 insertions(+), 26 deletions(-) create mode 100644 src/netcode/models.py diff --git a/requirements.txt b/requirements.txt index eefc955..7040c69 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,10 @@ +annotated-types==0.6.0 gevent==24.2.1 greenlet==3.0.3 mypy==1.9.0 mypy-extensions==1.0.0 +pydantic==2.6.4 +pydantic_core==2.16.3 typing_extensions==4.10.0 websockets==12.0 zope.event==5.0 diff --git a/src/controller/__init__.py b/src/controller/__init__.py index 9a8f5b7..be139b5 100644 --- a/src/controller/__init__.py +++ b/src/controller/__init__.py @@ -1,2 +1,5 @@ -from controller.client import Client +"""controller handles the translation between +netcode messages and model functions""" + from controller.game import GameController +from controller.client import ClientController diff --git a/src/controller/client.py b/src/controller/client.py index c079866..5a8cd87 100644 --- a/src/controller/client.py +++ b/src/controller/client.py @@ -1,15 +1,89 @@ +from pydantic import ValidationError + +from controller import GameController from websockets.server import WebSocketServerProtocol +from netcode.models import Message, LoginMessage, MessageData + +from game import Player -class Client: +def vibe_check(t): + """Enforces the selected type for the message""" + + def decorator(func): + async def new_f(self, message): + try: + message = t.model_validate_json(message) + return await func(self, message) + except ValidationError as e: + await self.send_error(e.errors()) + + return new_f + + return decorator + + +class ClientController: websocket: WebSocketServerProtocol - def __init__(self, websocket: WebSocketServerProtocol): + game_controller: GameController + + player: Player + + def __init__( + self, game_controller: GameController, websocket: WebSocketServerProtocol + ): self.websocket = websocket + self.on_message = self.login_context + + self.game_controller = game_controller async def listen(self): + """Initializes the client controller""" + await self.send(Message(type="context", data=MessageData(message="login"))) + async for message in self.websocket: await self.on_message(message) - async def on_message(self, message): + async def send(self, message: Message): + await self.websocket.send(message.model_dump_json()) + + async def send_error(self, error: str): + await self.send(Message(type="error", data=MessageData(message=error))) + + @vibe_check(Message) + async def game_context(self, message: Message): + """Handles messages when the client is logged in""" print(message) + + @vibe_check(LoginMessage) + async def login_context(self, message: LoginMessage): + """Handles messages when the client isn't logged in""" + if message.type == "register": + username = message.data.username + + if len(username) < 3: + await self.send_error("username must be longer than 3 characters") + return + + if self.game_controller.get_player(username) is not None: + await self.send_error("username already exists") + return + + player = self.game_controller.create_player(username) + elif message.type == "login": + username = message.data.username + player = self.game_controller.get_player(username) + + if player is None: + await self.send_error("player doesn't exists") + return + + else: + await self.send_error("invalid message type for context") + return + + self.player = player + self.on_message = self.game_context + print("user logged in : " + username) + await self.send(Message(type="context", data=MessageData(message="game"))) diff --git a/src/controller/game.py b/src/controller/game.py index 8d5e4cf..6208d82 100644 --- a/src/controller/game.py +++ b/src/controller/game.py @@ -1,11 +1,24 @@ +from typing import Optional from netcode import WSServer +from game import Game, Player class GameController: server: WSServer + game: Game - def __init__(self, server: WSServer): + def __init__(self, game: Game, server: WSServer): self.server = server + self.game = game + + def get_player(self, username: str) -> Optional[Player]: + players = [p for p in self.game.players if p.username == username] + if players and len(players) > 0: + return players[0] + return None + + def create_player(self, username: str) -> Player: + return self.game.create_player(username) def start_game(self): pass diff --git a/src/game/game.py b/src/game/game.py index 53c1649..88a9361 100644 --- a/src/game/game.py +++ b/src/game/game.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -from typing import List, Callable, Coroutine, Any +from typing import List -from game import Card, Team -from controller import GameController +from game import Card, Player, Team class Game: @@ -11,28 +10,23 @@ class Game: market_stack: List[Card] market: List[Card] gem_stack: List[Card] - teams: List[Team] + players: List[Player] started: bool default_health = 80 - controller: GameController - - def __init__(self, controller): + def __init__(self): self.market_stack = [] self.market = [] self.gem_stack = [] - self.teams = [] + self.players = [] self.started = False - self.controller = controller - - def create_team(self): - if self.started: - return False - team = Team(health=self.default_health) - self.teams.append(team) - def start_game(self): pass + + def create_player(self, username: str, team=Team()) -> Player: + player = Player(username, team) + self.players.append(player) + return player diff --git a/src/game/player.py b/src/game/player.py index adb7496..1bb6ec6 100644 --- a/src/game/player.py +++ b/src/game/player.py @@ -5,6 +5,8 @@ from game import Card, Team class Player: """Game player, controller by a Client""" + username: str + stack_pile: List[Card] discard_pile: List[Card] team: Team @@ -12,9 +14,10 @@ class Player: hand: List[Card] board: List[Card] - def __init__(self, team: Team): + def __init__(self, username: str, team: Team): self.stack_pile = [] self.discard_pile = [] self.hand = [] self.board = [] self.team = team + self.username = username diff --git a/src/main.py b/src/main.py index 03789e7..bc98938 100644 --- a/src/main.py +++ b/src/main.py @@ -4,17 +4,18 @@ from game import Game from netcode.ws_server import WebSocketServerProtocol from netcode import WSServer -from controller import Client, GameController +from controller import ClientController, GameController gameServer = WSServer() -controller = GameController(gameServer) -hero_game = Game(controller) + +hero_game = Game() +game_controller = GameController(hero_game, gameServer) playerServer = WSServer() async def handler(websocket: WebSocketServerProtocol): - client = Client(websocket) + client = ClientController(game_controller, websocket) await client.listen() diff --git a/src/netcode/models.py b/src/netcode/models.py new file mode 100644 index 0000000..dfec94a --- /dev/null +++ b/src/netcode/models.py @@ -0,0 +1,19 @@ +from typing import Optional, Any +from pydantic import BaseModel + + +class MessageData(BaseModel): + message: Optional[Any] = None + + +class Message(BaseModel): + type: str + data: MessageData + + +class LoginData(MessageData): + username: str + + +class LoginMessage(Message): + data: LoginData