wip: add stack and piece communication

This commit is contained in:
Giovani
2021-07-15 18:58:27 -04:00
parent 4d23aa3439
commit af301c0582
4 changed files with 160 additions and 85 deletions

View File

@@ -3,18 +3,22 @@ import websockets
import json
import queue # refer to https://docs.python.org/3/library/queue.html for cross threading communication
import uuid
from typing import Dict
from typing import Dict, List
from threading import Thread
class MultiplayerService():
_thread = None
_receive_piece_queue = queue.Queue()
_send_piece_queue = queue.Queue()
_receive_message_queue = queue.Queue()
_send_message_queue = queue.Queue()
_current_game_id = None
_client_id = str(uuid.uuid4())
""" QUEUES """
_receive_piece_queue = queue.Queue()
_send_piece_queue = queue.Queue()
_receive_stack_queue = queue.Queue()
_send_stack_queue = queue.Queue()
_receive_message_queue = queue.Queue()
_send_message_queue = queue.Queue()
WAIT_FOR_OPPONENT = "wait_for_opponent"
START_GAME = "start_game"
@@ -24,19 +28,20 @@ class MultiplayerService():
args=(_NetworkConnectionService.init(),))
thread.start()
@classmethod
def enter_game(cls, game_id: str) -> None:
cls._current_game_id = game_id
_NetworkConnectionService._join_game = True # TODO: change this to a function
""" SEND """
@classmethod
def send_piece(cls, piece: "PieceDto") -> None:
cls._send_piece_queue.put(piece)
@classmethod
def send_stack(cls, stack: "StackDto") -> None:
cls._send_stack_queue.put(stack)
@classmethod
def send_message(cls, message: str) -> None:
cls._send_message_queue.put(message)
""" RECEIVE """
@classmethod
def try_receive_piece(cls) -> "PieceDto":
if cls._receive_piece_queue.empty():
@@ -46,6 +51,15 @@ class MultiplayerService():
cls._receive_piece_queue.task_done()
return result
@classmethod
def try_receive_stack(cls) -> "StackDto":
if cls._receive_stack_queue.empty():
return None
result = cls._receive_stack_queue.get()
cls._receive_stack_queue.task_done()
return result
@classmethod
def try_receive_message(cls) -> str:
if cls._receive_message_queue.empty():
@@ -55,6 +69,12 @@ class MultiplayerService():
cls._receive_message_queue.task_done()
return result
""" MISC """
@classmethod
def enter_game(cls, game_id: str) -> None:
cls._current_game_id = game_id
_NetworkConnectionService._join_game = True # TODO: change this to a function
@classmethod
def quit(cls) -> None:
_NetworkConnectionService.close_connection()
@@ -70,26 +90,8 @@ class _NetworkConnectionService():
async def init(cls) -> None:
await cls._connect_to_server()
await cls._run_network_loop()
@classmethod
def close_connection(cls) -> None:
cls._is_closed = True
@classmethod
async def _run_network_loop(cls) -> None:
while True:
await asyncio.sleep(16e-3) # TODO add clock tick instead
await cls._try_enter_game()
await cls._try_send_piece()
await cls._try_send_message()
await cls._try_receive_message()
# if conection is closed, exit loop
if cls._is_closed:
await cls._websocket.close()
break
""" NETWORK SEND """
@classmethod
async def _try_enter_game(cls) -> None:
if not cls._join_game:
@@ -119,6 +121,24 @@ class _NetworkConnectionService():
await cls._websocket.send(json_message)
MultiplayerService._send_piece_queue.task_done()
@classmethod
async def _try_send_stack(cls) -> None:
# if no messages to proccess, return
if MultiplayerService._send_stack_queue.empty():
return
# get next piece to send and send to server
stack = MultiplayerService._send_stack_queue.get()
# construct json message
json_message = json.dumps({"action": "send_stack",\
"clientId": MultiplayerService._client_id,\
"gameId": MultiplayerService._current_game_id,\
"stack": stack.__dict__})
await cls._websocket.send(json_message)
MultiplayerService._send_stack_queue.task_done()
@classmethod
async def _try_send_message(cls) -> None:
# if no messages to proccess, return
@@ -131,11 +151,13 @@ class _NetworkConnectionService():
await cls._websocket.send(message)
MultiplayerService._send_message_queue.task_done()
""" NETWORK RECEIVE """
# todo refactor
@classmethod
async def _try_receive_message(cls) -> None:
try:
task = cls._pending_receive_task or asyncio.create_task(cls._websocket.recv())
done, pending = await asyncio.wait({task}, timeout=8e-3) # TODO experiment with the timeout
done, pending = await asyncio.wait({task}, timeout=4e-3) # TODO experiment with the timeout
if task in done:
json_str = await task
@@ -145,15 +167,14 @@ class _NetworkConnectionService():
return
data = json.loads(json_str)
print(data)
if data["type"] == "wait_for_opponent":
MultiplayerService._receive_message_queue.put(MultiplayerService.WAIT_FOR_OPPONENT)
if data["type"] == "start_game":
MultiplayerService._receive_message_queue.put(MultiplayerService.START_GAME)
if data["type"] == "receive_piece":
print("Receive a piece!")
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"] == "exit_game":
print("Exit the game!")
cls.close_connection()
@@ -162,7 +183,24 @@ class _NetworkConnectionService():
elif len(pending):
cls._pending_receive_task = pending.pop()
finally:
pass # TODO handle connection closed exception
pass # TODO handle connection closed exception and attempt to reconnect
""" MISC """
@classmethod
async def _run_network_loop(cls) -> None:
while True:
await asyncio.sleep(2e-3) # TODO add clock tick instead
await cls._try_enter_game()
await cls._try_send_piece()
await cls._try_send_stack()
await cls._try_send_message()
await cls._try_receive_message()
# if conection is closed, exit loop
if cls._is_closed:
await cls._websocket.close()
break
@classmethod
async def _connect_to_server(cls) -> None:
@@ -172,14 +210,25 @@ class _NetworkConnectionService():
# https://stackoverflow.com/a/58993145/11512104
print("Connected to server...") # TODO replace with logging
@classmethod
def close_connection(cls) -> None:
cls._is_closed = True
# DTOs
class PieceDto():
def __init__(self, x: int, y: int, type_: str) -> None:
self.x = x
self.y = y
self.type = type_
def __init__(self, points: List, center: List) -> None:
self.points = points
self.center = center
@staticmethod
def create(data: Dict) -> "PieceDto":
return PieceDto(data["x"], data["y"], data["type"])
return PieceDto(data["points"], data["center"])
class StackDto():
def __init__(self, points: List) -> None:
self.points = points
@staticmethod
def create(data: Dict) -> "StackDto":
return StackDto(data["points"])