From a9a2cae149e347d966070f31577b05b7f9748609 Mon Sep 17 00:00:00 2001 From: Paul-Louis NECH Date: Sun, 19 Apr 2020 16:13:21 +0200 Subject: [PATCH] server: Refactor async, works until givehand --- server/app.py | 3 +-- server/game/lobby.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------- server/game/manager.py | 2 +- server/model/game.py | 23 ++++++++++++----------- server/ws.py | 23 +++++++++++++---------- 5 files changed, 97 insertions(+), 65 deletions(-) diff --git a/server/app.py b/server/app.py index 91831ce..80ef5c3 100644 --- a/server/app.py +++ b/server/app.py @@ -36,8 +36,7 @@ async def hello_world(): @router.get("/reset") async def reset( ): - lobby.reset() - return "Done." + return lobby.reset() app.include_router(router) diff --git a/server/game/lobby.py b/server/game/lobby.py index ebe833f..f25bda9 100644 --- a/server/game/lobby.py +++ b/server/game/lobby.py @@ -1,3 +1,4 @@ +import asyncio import json from typing import List, Dict, Optional @@ -37,30 +38,48 @@ class LobbyManager(ClientManager): def players_ready(self): return [self.lobby[k] for k, m in self.metadata.items() if m.ready] - async def new_player(self, player: Player, sid: str) -> str: + def new_player(self, player: Player, sid: str) -> str: self.lobby[player.name] = player + self.players.append(player) self.metadata[player.name] = Metadata(sid=sid) print(f"Added {player} to a lobby with {len(self.lobby)} players.") return f"Bienvenu, {player}! Il y a {len(self.lobby)} joueurs en ligne." def wants_to_play(self, player: Player): + """ + Notes that a player wants to play, starting a game if others too. + """ self.metadata[player.name].ready = True - print(f"{player} ready to play! ({self.nb_ready} people ready)") + print(f"Lobby | {player} wants to play!") - async def announces(self, player: Player, announce: Announce): - # FIXME: Call this message on incoming "ANNOUNCE" + ready = self.players_ready + return ready + + async def maybe_start_game(self): + print("Should we start a game?") + ready = self.players_ready + if len(ready) > 1: + await self.start_game(ready) + print(f"Lobby: Game started. with {ready}.") + + def just_announced(self, player: Player, announce: Announce): meta = self.metadata[player.name] meta.last_announce = announce meta.fresh_announce = True - print(f"{player} ready to play! ({self.nb_ready} people ready)") + print(f"{player} announced {announce}!") + + async def start_game(self, players: Optional[List[Player]] = None, max_players=2): + if not players: + players = self.players_ready[:max_players] + + game = Game(players, manager=self) + self.games.append(game) - def start_game(self, max_players=2): - players = self.players_ready[:max_players] - self.games.append(Game(players, manager=self)) for p in players: self.metadata[p.name].ready = False print(f"Game started : {' vs '.join([p.name for p in players])}") + await game.new_game() def reset(self): players = len(self.players) @@ -72,27 +91,27 @@ class LobbyManager(ClientManager): self.send(p, MessageToPlayer.Reset) self.sio.disconnect(m.sid) self.lobby.clear() + self.players.clear() self.metadata.clear() self.games.clear() print(f"Reset done, {players} players, {games} games.") return msg - def send(self, to: Player, message: MessageToPlayer, extra=None): - data = {message: message.name} - if extra: - data["extra"] = extra - self.sio.send(message) - pass + async def send(self, to: Player, message: MessageToPlayer, extras=None): + sid = self.metadata[to.name].sid + data = {"message": str(message.name)} + if extras: + data["extra"] = extras + + print(f"MSGOUT|{sid} ({to.name}), {data}") + await self.sio.emit('messageChannel', json.dumps(data), room=sid) - def handle_message(self, sid, data): + async def handle_message(self, sid, data): message = None extras = {"players": [p.name for p in self.players]} - - print(f"Lobby| Received message from {sid}: {data}.") - sender = self.which_player(sid) - if sender: - print(f"Lobby| Found sender: {sender.name}") + + print(f"MSGIN| {sid} ({sender.name}): {data}.") for option in MessageFromPlayer: if option.value in data: @@ -100,34 +119,44 @@ class LobbyManager(ClientManager): if option == MessageFromPlayer.Waiting: self.metadata[sender.name].ready = False - print(f"MSG|Player {sender.name} waiting, while {extras} ready.") + print(f"Lobby| Player {sender.name} waiting") - if len(self.players_ready) > 1: - message = MessageToPlayer.ReadyToStart + message = MessageToPlayer.ReadyToStart \ + if len(self.players_ready) > 1 else MessageToPlayer.Waiting elif option == MessageFromPlayer.Ready: - self.wants_to_play(player=sender) + print(f"Lobby|Player {sender.name} ready") - print(f"MSG|Players ready: {extras}.") + gamers = self.wants_to_play(player=sender) + if gamers: + print(f"Game ready! gamers={gamers}") + message = MessageToPlayer.NewGame + extras["playersPlaying"] = [p.name for p in gamers] + + else: + print(f"Still alone ready!") + message = MessageToPlayer.ReadyToStart + extras["playersReady"] = [p.name for p in self.players_ready] - message = MessageToPlayer.ReadyToStart \ - if len(self.players_ready) > 1 else MessageToPlayer.Waiting - extras["playersReady"] = [p.name for p in self.players_ready] elif option == MessageFromPlayer.Bet: # FIXME vraie annonce, pas juste carre d'as lol - self.announces(player=sender, - announce=Announce(bet=CARRE_ACE)) - game: Game = self.game_with(sender) - # TODO: connect with current game, return appropriate message + announce = Announce(bet=CARRE_ACE) + self.just_announced(player=sender, + announce=announce) + + print(f"Lobby| Player {sender.name} just announced {announce}.") + + extras["youBet"] = announce.bet + elif option == MessageFromPlayer.Menteur: - self.announces(player=sender, announce=Announce()) - # TODO: connect with current game, return appropriate message + self.just_announced(player=sender, announce=Announce()) + extras["youBet"] = False - body = {"message": message.name if message else "none"} - if extras: - body["extras"] = extras - return json.dumps(body) + if message: + game = asyncio.create_task(self.maybe_start_game()) + await self.send(sender, message, extras) + await game def which_player(self, sid) -> Player: sender: Optional[Player] = None @@ -139,8 +168,8 @@ class LobbyManager(ClientManager): def game_with(self, player: Player) -> Game: return [g for g in self.games if player in g.players][0] - def send_waiting_for(self, player: Player): + async def send_waiting_for(self, player: Player): game = self.game_with(player) - self.send(player, MessageToPlayer.YourTurn, extra={"bet": game.current_bet}) + await self.send(player, MessageToPlayer.YourTurn, extras={"bet": game.current_bet}) for p in [p for p in game.players if p != player]: - self.send(p, MessageToPlayer.Waiting, extra={"waitingFor": p.name}) + await self.send(p, MessageToPlayer.Waiting, extras={"waitingFor": p.name}) diff --git a/server/game/manager.py b/server/game/manager.py index 7e8d2c1..4eb7964 100644 --- a/server/game/manager.py +++ b/server/game/manager.py @@ -19,7 +19,7 @@ class ClientManager(ABC): self.send(p, message) @abstractmethod - def send(self, + async def send(self, to: Player, message: MessageToPlayer, extra=None): diff --git a/server/model/game.py b/server/model/game.py index a68e687..42001ac 100644 --- a/server/model/game.py +++ b/server/model/game.py @@ -25,7 +25,7 @@ class Game: self.current_bet: Optional[Hand] = None self.manager = manager - def message(self, message: MessageToPlayer, + async def message(self, message: MessageToPlayer, *to: Player, extra=None ) -> None: @@ -33,7 +33,7 @@ class Game: if not to: to = self.players for player in to: - self.manager.send(player, message, extra) + await self.manager.send(player, message, extra) @property def global_hand(self) -> Hand: @@ -41,6 +41,7 @@ class Game: return Hand(cards=all_cards) async def new_game(self): + print(f"Game starting with {self.players}!") self.deck.reset() while len(self.players) > 1: @@ -48,14 +49,14 @@ class Game: if self.defeats[loser] == 5: print(f"{loser} is eliminated!") - self.message(MessageToPlayer.Lose, loser) + await self.message(MessageToPlayer.Lose, loser) self.players.remove(loser) else: print(f"{loser} lost the round, now playing with {self.defeats[loser] + 1} cards!") winner = self.players[0] - self.message(MessageToPlayer.Win, winner) - self.message(MessageToPlayer.WinnerIs, extra=winner) + await self.message(MessageToPlayer.Win, winner) + await self.message(MessageToPlayer.WinnerIs, extra=winner) print(f"Game over - {winner.name} wins with {len(winner.hand)} cards!") async def new_turn(self) -> Player: @@ -67,7 +68,7 @@ class Game: # Distribution self.deck.reset() - self.message(MessageToPlayer.WaitTurn) + await self.message(MessageToPlayer.WaitTurn) for current_player in self.players: current_player.clear() count_player_cards = self.defeats[current_player] + 1 @@ -77,7 +78,7 @@ class Game: current_player.give(card) print(f"{card}") - self.message(MessageToPlayer.GiveHand, current_player, extra=current_player.hand) + await self.message(MessageToPlayer.GiveHand, current_player, extra=current_player.hand) print(f"Cards sent.") # Tour @@ -88,7 +89,7 @@ class Game: if loser is not None: self.players.remove(loser) self.players.insert(0, loser) - self.message(MessageToPlayer.LoseRound, extra=loser.name) + await self.message(MessageToPlayer.LoseRound, extra=loser.name) return loser last_player = current_player @@ -142,14 +143,14 @@ class Game: print(f"| {current_player}'s turn >") if not self.current_bet: # First player, has to bet something - self.message(MessageToPlayer.YourTurn, current_player, extra=self.current_bet) + await self.message(MessageToPlayer.YourTurn, current_player, extra=self.current_bet) while not self.current_bet: # Ask a valid bet # FIXME: Wait for player announce? Maybe just sleep 10? announce = await current_player.announce(self.current_bet) if announce.bet: self.current_bet = announce.bet print(f"{current_player} starts the round: {self.current_bet}") - self.message(MessageToPlayer.Announce, extra={"player": current_player, "announce": announce}) + await self.message(MessageToPlayer.Announce, extra={"player": current_player, "announce": announce}) else: print(f"You cannot say Menteur on first round, {current_player}!") @@ -160,7 +161,7 @@ class Game: print("CARRE D'AS!") announce = Announce() # MENTEUR obligatoire else: - self.message(MessageToPlayer.YourTurn, current_player, extra=self.current_bet) + await self.message(MessageToPlayer.YourTurn, current_player, extra=self.current_bet) announce = current_player.announce(self.current_bet) if announce.bet: diff --git a/server/ws.py b/server/ws.py index 80e84a4..23963d4 100644 --- a/server/ws.py +++ b/server/ws.py @@ -3,8 +3,7 @@ from typing import Optional import socketio -from server.game.lobby import LobbyManager -from server.game.message import MessageToPlayer +from server.game.lobby import LobbyManager, Metadata from server.model.hand import Hand from server.model.players import Player, Announce @@ -23,34 +22,38 @@ class ClientPlayer(Player): self.ready = False async def announce(self, current_bet: Optional[Hand]) -> Announce: - announce = None - while not self.lobby.metadata[self.name].fresh_announce: - lobby.send_waiting_for(self) + metadata: Metadata = self.lobby.metadata[self.name] + print(f"Asking Client {self.name} for announce...", end="") + + while not metadata.last_announce and metadata.fresh_announce: + await lobby.send_waiting_for(self) + print(".", end="") sleep(2) - return lobby.last_announces[self] + + print(f" {metadata.last_announce.bet}!") + return metadata.last_announce @sio.event async def connect(sid, environ): print("[WS] Connect ", sid, environ) player = ClientPlayer(lobby) - reply: str = await lobby.new_player(player, sid) + reply: str = lobby.new_player(player, sid) await sio.emit('messageChannel', reply, room=sid) @sio.event async def message(sid, data): print("[WS] Message ", data) - await sio.emit('messageChannel', lobby.handle_message(sid, data), room=sid) + await lobby.handle_message(sid, data) @sio.on("pingServer") async def ping_server(sid, data): print("[WS] Ping received:", data) - await sio.emit('messageChannel', "PONG") + await sio.emit('messageChannel', "PONG", room=sid) @sio.event def disconnect(sid): print('[WS] Disconnect ', sid) - -- libgit2 0.27.0