From 836bda8294c49abc7003dba29ff53e8bdd5025bc Mon Sep 17 00:00:00 2001 From: Giovani Date: Thu, 15 Jul 2021 23:28:36 -0400 Subject: [PATCH] feat: add ability to send stats to server --- config.yaml | 4 ++++ tetri5/online.py | 60 +++++++++++++++++++++++++++++++++++++++++++----- tetri5/scene.py | 28 ++++++++++++++++++---- 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/config.yaml b/config.yaml index 9001ca7..c0ed94e 100644 --- a/config.yaml +++ b/config.yaml @@ -92,3 +92,7 @@ color: piece-inner-border-1-player-2: "#F83801" piece-outer-border-1: "#000000" piece-ghost: "#9BFCF0" + +online: + server-url: "ws://webapi.tetri5.com" + #server-url: "ws://localhost:5001" diff --git a/tetri5/online.py b/tetri5/online.py index 67b68ca..72cfe24 100644 --- a/tetri5/online.py +++ b/tetri5/online.py @@ -5,6 +5,7 @@ import queue # refer to https://docs.python.org/3/library/queue.html for cross t import uuid from typing import Dict, List from threading import Thread +from tetri5.util import ConfigurationManager class MultiplayerService(): _thread = None @@ -16,6 +17,8 @@ class MultiplayerService(): _send_piece_queue = queue.Queue() _receive_stack_queue = queue.Queue() _send_stack_queue = queue.Queue() + _receive_stats_queue = queue.Queue() + _send_stats_queue = queue.Queue() _receive_message_queue = queue.Queue() _send_message_queue = queue.Queue() @@ -37,6 +40,10 @@ class MultiplayerService(): def send_stack(cls, stack: "StackDto") -> None: cls._send_stack_queue.put(stack) + @classmethod + def send_stats(cls, stats: "StatsDto") -> None: + cls._send_stats_queue.put(stats) + @classmethod def send_message(cls, message: str) -> None: cls._send_message_queue.put(message) @@ -60,6 +67,15 @@ class MultiplayerService(): cls._receive_stack_queue.task_done() return result + @classmethod + def try_receive_stats(cls) -> "StatsDto": + if cls._receive_stats_queue.empty(): + return None + + result = cls._receive_stats_queue.get() + cls._receive_stats_queue.task_done() + return result + @classmethod def try_receive_message(cls) -> str: if cls._receive_message_queue.empty(): @@ -80,7 +96,6 @@ class MultiplayerService(): _NetworkConnectionService.close_connection() class _NetworkConnectionService(): - _URI = "ws://webapi.tetri5.com" # TODO get from config file _websocket = None _is_closed = False _join_game = False @@ -127,7 +142,7 @@ class _NetworkConnectionService(): if MultiplayerService._send_stack_queue.empty(): return - # get next piece to send and send to server + # get next stack to send and send to server stack = MultiplayerService._send_stack_queue.get() # construct json message @@ -139,6 +154,24 @@ class _NetworkConnectionService(): await cls._websocket.send(json_message) MultiplayerService._send_stack_queue.task_done() + @classmethod + async def _try_send_stats(cls) -> None: + # if no messages to proccess, return + if MultiplayerService._send_stats_queue.empty(): + return + + # get next stats to send and send to server + stats = MultiplayerService._send_stats_queue.get() + + # construct json message + json_message = json.dumps({"action": "send_stats",\ + "clientId": MultiplayerService._client_id,\ + "gameId": MultiplayerService._current_game_id,\ + "stats": stats.__dict__}) + + await cls._websocket.send(json_message) + MultiplayerService._send_stats_queue.task_done() + @classmethod async def _try_send_message(cls) -> None: # if no messages to proccess, return @@ -175,6 +208,9 @@ class _NetworkConnectionService(): MultiplayerService._receive_piece_queue.put(PieceDto.create(data["piece"])) if data["type"] == "receive_stack": MultiplayerService._receive_stack_queue.put(StackDto.create(data["stack"])) + if data["type"] == "receive_stats": + MultiplayerService._receive_stats_queue.put(StatsDto.create(data["stats"])) + print(data["stats"]) if data["type"] == "exit_game": print("Exit the game!") cls.close_connection() @@ -194,6 +230,7 @@ class _NetworkConnectionService(): await cls._try_enter_game() await cls._try_send_piece() await cls._try_send_stack() + await cls._try_send_stats() await cls._try_send_message() await cls._try_receive_message() @@ -202,12 +239,13 @@ class _NetworkConnectionService(): await cls._websocket.close() break + # ping_interval=None is important, otherwise the server will disconnect us + # https://stackoverflow.com/a/58993145/11512104 @classmethod async def _connect_to_server(cls) -> None: print("Connecting to server...") # TODO replace with logging - cls._websocket = await websockets.connect(cls._URI, ping_interval=None) - # ping_interval=None is important, otherwise the server will disconnect us - # https://stackoverflow.com/a/58993145/11512104 + url = ConfigurationManager.get("online", "server-url") + cls._websocket = await websockets.connect(url, ping_interval=None) print("Connected to server...") # TODO replace with logging @classmethod @@ -235,4 +273,14 @@ class StackDto(): @staticmethod def create(data: Dict) -> "StackDto": - return StackDto(data["points"], data["square_colors"]) \ No newline at end of file + return StackDto(data["points"], data["square_colors"]) + +class StatsDto(): + def __init__(self, score: int, lines: int, is_well_full: bool) -> None: + self.score = score + self.lines = lines + self.is_well_full = is_well_full + + @staticmethod + def create(data: Dict) -> "StatsDto": + return StatsDto(data["score"], data["lines"], data["is_well_full"]) \ No newline at end of file diff --git a/tetri5/scene.py b/tetri5/scene.py index 0149ac4..c9dbadf 100644 --- a/tetri5/scene.py +++ b/tetri5/scene.py @@ -266,7 +266,12 @@ class MultiplayerScene(Scene): def __init__(self, change_scene: FunctionType) -> None: self._tile_size = ConfigurationManager.get("engine", "tile-size") self._background_color = pygame.Color(ConfigurationManager.get("color", "window-bg")) - self._lines_per_level = ConfigurationManager.get("engine", "lines-per-level") + self._lines_per_level = ConfigurationManager.get("engine", "lines-per-level") + self._points_table = ConfigurationManager.get("engine", "points-table") + + self._score_player_one = 0 + self._score_player_two = 0 + self._total_lines_player_two = 0 # wells init self._well_player_one = Well(ConfigurationManager.get("position", "well-player-1"),\ @@ -328,15 +333,20 @@ class MultiplayerScene(Scene): if self._well_player_two is not None: self._well_player_two.draw(surface) + score_player_one = str(self._score_player_one).zfill(6) + score_player_two = str(self._score_player_two).zfill(6) + lines_player_one = str(self._stack_player_one.total_lines).zfill(4) + lines_player_two = str(self._total_lines_player_two).zfill(4) + # scores TextGenerator.draw("Score", self._score_label_player_one_pos, surface) - TextGenerator.draw("000000", self._score_value_player_one_pos, surface) + TextGenerator.draw(score_player_one, self._score_value_player_one_pos, surface) TextGenerator.draw("Score", self._score_label_player_two_pos, surface) - TextGenerator.draw("000000", self._score_value_player_two_pos, surface) + TextGenerator.draw(score_player_two, self._score_value_player_two_pos, surface) # lines - TextGenerator.draw("Lines 0000", self._lines_label_player_one_pos, surface) - TextGenerator.draw("Lines 0000", self._lines_label_player_two_pos, surface) + TextGenerator.draw("Lines " + lines_player_one, self._lines_label_player_one_pos, surface) + TextGenerator.draw("Lines " + lines_player_two, self._lines_label_player_two_pos, surface) # next TextGenerator.draw("NEXT", self._next_label_player_one_pos, surface) @@ -349,6 +359,13 @@ class MultiplayerScene(Scene): self._stack_player_one.update(elapsed_time) self._update_stack_player_two() + self._score_player_one += self._points_table[self._stack_player_one.lines_completed_last] * (self._get_level_player_one() + 1) + opponent_stats = MultiplayerService.try_receive_stats() + if opponent_stats is not None: + self._score_player_two = opponent_stats.score + self._total_lines_player_two = opponent_stats.lines + + # SEND TO SERVER if self._current_piece_player_one is not None: MultiplayerService.send_piece(PieceDto(self._current_piece_player_one._points,\ self._current_piece_player_one._center,\ @@ -365,6 +382,7 @@ class MultiplayerScene(Scene): ], ) ) + MultiplayerService.send_stats(StatsDto(self._score_player_one, self._stack_player_one.total_lines, False)) def _update_piece_player_one(self, elapsed_time: int) -> None: if self._current_piece_player_one is not None: