feat: expand network connection implementation
This commit is contained in:
115
tetris/online.py
115
tetris/online.py
@@ -1,49 +1,130 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
from os import stat
|
||||||
import websockets
|
import websockets
|
||||||
|
import json
|
||||||
|
import queue # refer to https://docs.python.org/3/library/queue.html for multi-threading
|
||||||
|
from typing import Dict
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
class MultiplayerService():
|
class MultiplayerService():
|
||||||
_thread = None
|
_thread = None
|
||||||
|
_receive_piece_queue = queue.Queue()
|
||||||
|
_send_piece_queue = queue.Queue()
|
||||||
|
_current_game_id = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def init(cls):
|
def init(cls) -> None:
|
||||||
thread = Thread(target=asyncio.get_event_loop().run_until_complete,\
|
thread = Thread(target=asyncio.get_event_loop().run_until_complete,\
|
||||||
args=(_NetworkConnectionService.init(),))
|
args=(_NetworkConnectionService.init(),))
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def quit(cls):
|
def enter_game(cls, game_id: str) -> None:
|
||||||
_NetworkConnectionService.close_connection()
|
cls._current_game_id = game_id
|
||||||
|
_NetworkConnectionService._join_game = True
|
||||||
class _NetworkConnectionService():
|
pass
|
||||||
_websocket = None
|
|
||||||
_URI = "ws://webapi.tetri5.com"
|
|
||||||
_is_closed = False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def init(cls):
|
def send_piece(cls, piece: "PieceDto") -> None:
|
||||||
|
cls._send_piece_queue.put(piece)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def try_receive_piece(cls) -> "PieceDto":
|
||||||
|
return cls._receive_piece_queue.get() if not cls._receive_piece_queue.empty() else None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def quit(cls) -> None:
|
||||||
|
_NetworkConnectionService.close_connection()
|
||||||
|
|
||||||
|
class _NetworkConnectionService():
|
||||||
|
_URI = "ws://localhost:5001" # TODO get from config file
|
||||||
|
_websocket = None
|
||||||
|
_is_closed = False
|
||||||
|
_join_game = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def init(cls) -> None:
|
||||||
await cls._connect_to_server()
|
await cls._connect_to_server()
|
||||||
await cls._run_network_loop()
|
await cls._run_network_loop()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def close_connection(cls):
|
def close_connection(cls) -> None:
|
||||||
cls._is_closed = True
|
cls._is_closed = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _run_network_loop(cls):
|
async def _run_network_loop(cls) -> None:
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(16e-3)
|
await asyncio.sleep(16e-3) # TODO add clock tick instead
|
||||||
await cls._websocket.send("ping")
|
|
||||||
print(await cls._websocket.recv())
|
|
||||||
|
|
||||||
|
await cls._try_enter_game()
|
||||||
|
await cls._try_send_piece()
|
||||||
|
# await cls._try_receive_message()
|
||||||
|
|
||||||
|
# if conection is closed, exit loop
|
||||||
if cls._is_closed:
|
if cls._is_closed:
|
||||||
await cls._websocket.close()
|
await cls._websocket.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _connect_to_server(cls):
|
async def _try_enter_game(cls) -> None:
|
||||||
print("Connecting to server...")
|
if not cls._join_game:
|
||||||
|
return
|
||||||
|
|
||||||
|
json_message = json.dumps({"action": "enter_game", "gameId": MultiplayerService._current_game_id})
|
||||||
|
await cls._websocket.send(json_message)
|
||||||
|
|
||||||
|
json_response = await cls._websocket.recv()
|
||||||
|
print(json_response)
|
||||||
|
|
||||||
|
cls._join_game = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _try_send_piece(cls) -> None:
|
||||||
|
# if no messages to proccess, return
|
||||||
|
if MultiplayerService._send_piece_queue.empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
# get next piece to send and send to server
|
||||||
|
piece = MultiplayerService._send_piece_queue.get()
|
||||||
|
|
||||||
|
# construct json message
|
||||||
|
message = {}
|
||||||
|
message["action"] = "send_piece"
|
||||||
|
message["gameId"] = MultiplayerService._current_game_id
|
||||||
|
message["piece"] = piece.__dict__()
|
||||||
|
|
||||||
|
await cls._websocket.send(json.dumps(message))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def _try_receive_message(cls) -> None:
|
||||||
|
try:
|
||||||
|
async for message in cls._websocket:
|
||||||
|
data = json.loads(message)
|
||||||
|
print(data)
|
||||||
|
# if message type is receive_piece, put it in the receive queue
|
||||||
|
if data["type"] == "receive_piece":
|
||||||
|
# convert Dict to PieceDto
|
||||||
|
MultiplayerService._receive_piece_queue.put(PieceDto.create(data["piece"]))
|
||||||
|
finally:
|
||||||
|
pass # TODO handle connection closed exception
|
||||||
|
|
||||||
|
@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)
|
cls._websocket = await websockets.connect(cls._URI, ping_interval=None)
|
||||||
# ping_interval=None is important, otherwise the server will disconnect us
|
# ping_interval=None is important, otherwise the server will disconnect us
|
||||||
# https://stackoverflow.com/a/58993145/11512104
|
# https://stackoverflow.com/a/58993145/11512104
|
||||||
print("Connected to server...")
|
print("Connected to server...") # TODO replace with logging
|
||||||
|
|
||||||
|
# DTOs
|
||||||
|
|
||||||
|
class PieceDto():
|
||||||
|
def __init__(self, x: int, y: int, type_: str) -> None:
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.type = type_
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create(data: Dict) -> "PieceDto":
|
||||||
|
return PieceDto(data["x"], data["y"], data["type"])
|
||||||
Reference in New Issue
Block a user