diff --git a/src/controller/client.py b/src/controller/client.py index 5a8cd87..117b71b 100644 --- a/src/controller/client.py +++ b/src/controller/client.py @@ -1,8 +1,9 @@ from pydantic import ValidationError -from controller import GameController from websockets.server import WebSocketServerProtocol -from netcode.models import Message, LoginMessage, MessageData +from controller import GameController + +from netcode.models import Message, LoginMessage from game import Player @@ -40,7 +41,13 @@ class ClientController: async def listen(self): """Initializes the client controller""" - await self.send(Message(type="context", data=MessageData(message="login"))) + await self.send( + Message( + type="context", + data_type="game_context_id", + data="login", + ) + ) async for message in self.websocket: await self.on_message(message) @@ -49,7 +56,7 @@ class ClientController: 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))) + await self.send(Message(type="error", data_type="error", data=error)) @vibe_check(Message) async def game_context(self, message: Message): @@ -59,8 +66,8 @@ class ClientController: @vibe_check(LoginMessage) async def login_context(self, message: LoginMessage): """Handles messages when the client isn't logged in""" + username = message.data if message.type == "register": - username = message.data.username if len(username) < 3: await self.send_error("username must be longer than 3 characters") @@ -70,9 +77,8 @@ class ClientController: await self.send_error("username already exists") return - player = self.game_controller.create_player(username) + player = await 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: @@ -86,4 +92,6 @@ class ClientController: self.player = player self.on_message = self.game_context print("user logged in : " + username) - await self.send(Message(type="context", data=MessageData(message="game"))) + await self.send( + Message(type="context", data_type="game_context_id", data="game") + ) diff --git a/src/controller/game.py b/src/controller/game.py index 6208d82..0535c15 100644 --- a/src/controller/game.py +++ b/src/controller/game.py @@ -1,6 +1,7 @@ -from typing import Optional +import json from netcode import WSServer -from game import Game, Player +from netcode.models import Message +from game import Game, Player, Team class GameController: @@ -11,14 +12,35 @@ class GameController: self.server = server self.game = game - def get_player(self, username: str) -> Optional[Player]: + async def broadcast(self, message: Message): + await self.server.broadcast(message.model_dump_json()) + + def get_player(self, username: str) -> Player | None: 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) + async def create_player(self, username: str, team: Team | None = None) -> Player: + if team is None: + team = await self.create_team() + player = self.game.create_player(username, team) + + await self.broadcast( + Message( + type="create", + data_type="player", + data=player.serialize_limited(), + ) + ) + return player + + async def create_team(self): + team = self.game.create_team() + await self.broadcast( + Message(type="create", data_type="team", data=team.serialize()) + ) + return team def start_game(self): pass diff --git a/src/game/__init__.py b/src/game/__init__.py index 777a568..009ad0d 100644 --- a/src/game/__init__.py +++ b/src/game/__init__.py @@ -1,3 +1,4 @@ +from game.serializable import Serializable from game.card import Card from game.team import Team from game.player import Player diff --git a/src/game/card.py b/src/game/card.py index 71c1af6..57e33f5 100644 --- a/src/game/card.py +++ b/src/game/card.py @@ -1,4 +1,5 @@ from enum import Enum +from game import Serializable class CardType(str, Enum): @@ -21,7 +22,7 @@ class CardFaction(str, Enum): YELLOW = "yellow" -class Card: +class Card(Serializable): """Represents any playable card""" sprite: str @@ -34,6 +35,9 @@ class Card: def __init__(self): pass + def serialize(self) -> dict: + return self.__dict__ + class CardChampion(Card): """Represents any champion card""" diff --git a/src/game/game.py b/src/game/game.py index 88a9361..6ddc78f 100644 --- a/src/game/game.py +++ b/src/game/game.py @@ -1,16 +1,16 @@ #!/usr/bin/env python from typing import List -from game import Card, Player, Team +from game import Card, Player, Team, Serializable -class Game: +class Game(Serializable): """Instance of the game, with players and a market""" market_stack: List[Card] market: List[Card] gem_stack: List[Card] - players: List[Player] + _players: List[Player] started: bool @@ -26,7 +26,13 @@ class Game: def start_game(self): pass - def create_player(self, username: str, team=Team()) -> Player: + def create_player(self, username: str, team: Team) -> Player: player = Player(username, team) self.players.append(player) return player + + def create_team(self) -> Team: + return Team() + + def serialize(): + return super().serialize() diff --git a/src/game/player.py b/src/game/player.py index 1bb6ec6..2306dab 100644 --- a/src/game/player.py +++ b/src/game/player.py @@ -1,8 +1,8 @@ from typing import List -from game import Card, Team +from game import Card, Team, Serializable -class Player: +class Player(Serializable): """Game player, controller by a Client""" username: str @@ -21,3 +21,13 @@ class Player: self.board = [] self.team = team self.username = username + + def serialize_limited(self) -> dict: + return { + "username": self.username, + "stack_pile_amount": len(self.stack_pile), + "discard_pile": [card.serialize() for card in self.discard_pile], + "team": self.team.id, + "hand_amount": len(self.hand), + "board": [card.serialize() for card in self.board], + } diff --git a/src/game/serializable.py b/src/game/serializable.py new file mode 100644 index 0000000..0e1bd0c --- /dev/null +++ b/src/game/serializable.py @@ -0,0 +1,3 @@ +class Serializable: + def serialize(self): + return self.__dict__ diff --git a/src/game/team.py b/src/game/team.py index ddccd9b..b192007 100644 --- a/src/game/team.py +++ b/src/game/team.py @@ -1,15 +1,20 @@ -class Team: +from game import Serializable + + +class Team(Serializable): """Shared object for health storage""" - _team_id = 1 + id = 1 health: int color: str name: str - def __init__(self, health=80, color="red", name="Team " + str(_team_id)): - self._team_id = Team._team_id - Team._team_id += 1 + def __init__(self, health=80, color="red", name="Team " + str(id)): + self.id = Team.id + print(self.id, Team.id) + Team.id += 1 + print(self.id, Team.id) self.health = health self.color = color self.name = name diff --git a/src/netcode/models.py b/src/netcode/models.py index dfec94a..518d090 100644 --- a/src/netcode/models.py +++ b/src/netcode/models.py @@ -1,19 +1,12 @@ -from typing import Optional, Any +from typing import Any from pydantic import BaseModel -class MessageData(BaseModel): - message: Optional[Any] = None - - class Message(BaseModel): type: str - data: MessageData - - -class LoginData(MessageData): - username: str + data: Any | str | int | None = None + data_type: str class LoginMessage(Message): - data: LoginData + data: str diff --git a/src/netcode/ws_server.py b/src/netcode/ws_server.py index cd2cd4d..7d36366 100644 --- a/src/netcode/ws_server.py +++ b/src/netcode/ws_server.py @@ -2,6 +2,7 @@ import asyncio from typing import Set, Callable, Any, List from websockets.server import serve, WebSocketServerProtocol from websockets import broadcast +import time class WSServer: @@ -11,6 +12,8 @@ class WSServer: message_callbacks: List[Callable[[WebSocketServerProtocol], Any]] + port: 0 + def __init__(self): self.connections = set() self.message_callbacks = [] @@ -27,7 +30,7 @@ class WSServer: async def track_connection(self, websocket: WebSocketServerProtocol): """Keeps track of connected clients in the connections property""" - + self.connections.add(websocket) try: await websocket.wait_closed() finally: @@ -36,6 +39,8 @@ class WSServer: async def serve(self, address="0.0.0.0", port=8765): """Call to start the websocket""" + self.port = port + async with serve(self.handler, address, port): print("Started server on ws://" + address + ":" + str(port)) await asyncio.Future() # run forever