-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix(ui-client): TTTK-52 Moved UI Client to new file
- Loading branch information
Showing
3 changed files
with
141 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
from __future__ import annotations | ||
import asyncio | ||
from websockets.client import connect | ||
from Server.player import Player | ||
from threading import Thread | ||
from queue import Queue, Empty | ||
import tkinter as tk | ||
from Client.client import GameClient | ||
import logging | ||
|
||
# Set up logging | ||
logging.basicConfig(level=logging.INFO) | ||
logger = logging.getLogger(__name__) | ||
|
||
class GameClientUI(GameClient): | ||
"""A class to represent a game client that connects to a server and sends and receives messages. This class is specifically designed to be used with a tkinter UI. | ||
Attributes: | ||
_tk_root (tk.Tk): The root of the tkinter application. | ||
_in_queue (Queue): The queue to receive messages from the server. | ||
_out_queue (Queue): The queue to send messages to the server. | ||
For the rest of the attributes and methods see the `GameClient` class. | ||
""" | ||
def __init__(self, ip:str, port:int, player:Player, tk_root:tk.Tk, in_queue:Queue, out_queue:Queue) -> None: | ||
self._tk_root = tk_root | ||
self._in_queue = in_queue | ||
self._out_queue = out_queue | ||
super().__init__(ip, port, player) | ||
|
||
@classmethod | ||
async def join_game(cls, player: Player, ip: str, tk_root:tk.Tk, in_queue:Queue, out_queue:Queue, port: int = 8765) -> tuple[GameClientUI, asyncio.Task]: | ||
|
||
client = cls(ip, port, player, tk_root, in_queue, out_queue) | ||
|
||
await client.connect() | ||
listening_task = asyncio.create_task(client.listen()) | ||
await asyncio.create_task(client.join_lobby()) | ||
|
||
return client, listening_task | ||
|
||
async def _message_handler(self, message_type: str): | ||
# Receive messages from the server | ||
match message_type: | ||
case "lobby/status": | ||
self._in_queue.put({ | ||
"message_type": "lobby/status", | ||
"player": self._lobby_status | ||
}) | ||
self._tk_root.event_generate("<<lobby/status>>", when="tail") | ||
case "game/start": | ||
self._in_queue.put({ | ||
"message_type": "game/start", | ||
"starting_player": self._current_player, | ||
"starting_player_symbol": self._symbol == "X", | ||
"opponent": self._opponent, | ||
"opponent_symbol": self._symbol != "X" | ||
}) | ||
self._tk_root.event_generate("<<game/start>>", when="tail") | ||
case "game/end": | ||
self._in_queue.put({ | ||
"message_type": "game/end", | ||
"winner": self._winner, | ||
"win": self._winner == self._player, | ||
"final_playfield": self._game_status | ||
}) | ||
self._tk_root.event_generate("<<game/end>>", when="tail") | ||
self.client.close() | ||
case "game/turn": | ||
self._in_queue.put({ | ||
"message_type": "game/turn", | ||
"next_player": int(self._current_player == self._opponent), | ||
"playfield": self._game_status | ||
}) | ||
self._tk_root.event_generate("<<game/turn>>", when="tail") | ||
case "statistics/statistics": | ||
pass | ||
case "game/error": | ||
self._in_queue.put({ | ||
"message_type": "game/error", | ||
"error_message": self._error_history[-1] | ||
}) | ||
self._tk_root.event_generate("<<game/error>>", when="tail") | ||
case "chat/receive": | ||
self._in_queue.put({ | ||
"message_type": "chat/receive", | ||
"sender": self._chat_history[-1][0], | ||
"message": self._chat_history[-1][1] | ||
}) | ||
self._tk_root.event_generate("<<chat/receive>>", when="tail") | ||
|
||
return | ||
|
||
async def client_thread_function(tk_root:tk.Tk, in_queue:Queue, out_queue:Queue, player: Player, ip:str, port:int) -> None: | ||
"""The function that is run in the client thread. It connects to the server. It sends and receives messages.""" | ||
|
||
client, listening_task = await GameClientUI.join_game(player=player, ip=ip, tk_root=tk_root, in_queue=in_queue, out_queue=out_queue, port=port) | ||
|
||
while client._websocket.open: | ||
# Send messages to the server | ||
try: | ||
message: dict = out_queue.get_nowait() | ||
except Empty: | ||
await asyncio.sleep(0.1) | ||
logger.warning("Out queue is empty") | ||
continue | ||
|
||
logger.warning(f"Sending: {message}") | ||
if message: | ||
match message["message_type"]: | ||
case "lobby/join": | ||
pass | ||
case "lobby/ready": | ||
await client.lobby_ready(message["args"]) | ||
case "lobby/kick": | ||
await client.lobby_kick(message["args"]) | ||
case "game/make_move": | ||
await client.game_make_move(message["args"]) | ||
case "chat/message": | ||
pass | ||
case _: | ||
logger.error(f"Unknown message type received from UI in out_queue: {message['message_type']}") | ||
continue | ||
|
||
out_queue.task_done() | ||
|
||
await listening_task | ||
|
||
return | ||
|
||
def asyncio_thread_wrapper(tk_root:tk.Tk, in_queue:Queue, out_queue:Queue, player: Player, ip:str, port:int): | ||
"""Wrapper function to run the client thread function in an asyncio event loop.""" | ||
asyncio.run(client_thread_function(tk_root, in_queue, out_queue, player, ip, port)) | ||
|
||
def client_thread(tk_root:tk.Tk, in_queue:Queue, out_queue:Queue, player: Player, ip:str, port:int = 8765) -> Thread: | ||
"""Start a new client thread that connects to the server and sends and receives messages.""" | ||
thread = Thread(target=asyncio_thread_wrapper, args=(tk_root, in_queue, out_queue, player, ip , port), daemon=True) | ||
thread.start() | ||
return thread |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters