This commit is contained in:
Eden Kirin
2023-03-26 23:59:15 +02:00
parent f74bc9b52e
commit fa2aee881d
6 changed files with 31 additions and 13 deletions

View File

@ -53,7 +53,9 @@ async def get_game_info(
)
@router.post("/game", response_model=StartGameResponseDto)
@router.post(
"/game", response_model=StartGameResponseDto, status_code=status.HTTP_201_CREATED
)
async def start_game(
body: StartGameRequestDto,
engine: GameEngine = Depends(get_game_engine),
@ -72,7 +74,6 @@ async def start_game(
@router.get(
"/player/{uuid}",
response_model=PlayerInfoResponseDto,
status_code=status.HTTP_201_CREATED,
responses={
status.HTTP_403_FORBIDDEN: {
"model": ErrorResponseDto,

View File

@ -5,6 +5,7 @@ from typing import Optional
from hopper.enums import Direction, PlayerMoveResult
from hopper.errors import Collision, PositionOutOfBounds
from hopper.interfaces import SendGameStateInterface
from hopper.models.board import (
BOARD_DUMP_CHARS,
BoardLayout,
@ -17,12 +18,13 @@ from hopper.models.board import (
)
from hopper.models.player import Player, PlayerList, Position
from hopper.watchdog import InactivityWatchdog
from hopper.ws_server import WSServer
from settings import settings
class GameEngine:
def __init__(self, board: GameBoard, ws_server: Optional[WSServer] = None) -> None:
def __init__(
self, board: GameBoard, ws_server: Optional[SendGameStateInterface] = None
) -> None:
self.board = board
self.ws_server = ws_server
self.players = PlayerList()
@ -163,7 +165,7 @@ class GameEngineFactory:
board_width: int,
board_height: int,
obstacle_count: int = 0,
ws_server: Optional[WSServer] = None,
ws_server: Optional[SendGameStateInterface] = None,
) -> GameEngine:
board = GameBoard(
width=board_width,
@ -188,7 +190,9 @@ class GameEngineFactory:
return game
@staticmethod
def create_default(ws_server: Optional[WSServer] = None) -> GameEngine:
def create_default(
ws_server: Optional[SendGameStateInterface] = None,
) -> GameEngine:
return GameEngineFactory.create(
board_width=settings.board.WIDTH,
board_height=settings.board.HEIGHT,

6
hopper/interfaces.py Normal file
View File

@ -0,0 +1,6 @@
from typing import Protocol
class SendGameStateInterface(Protocol):
async def send_game_state(self) -> None:
...

View File

@ -5,14 +5,18 @@ import time
from threading import Thread
from typing import Optional
from hopper.interfaces import SendGameStateInterface
from hopper.models.player import PlayerList
from hopper.ws_server import WSServer
from settings import settings
class InactivityWatchdog(Thread):
def __init__(
self, players: PlayerList, ws_server: Optional[WSServer] = None, *args, **kwargs
self,
players: PlayerList,
ws_server: Optional[SendGameStateInterface] = None,
*args,
**kwargs,
) -> None:
self.players = players
self.ws_server = ws_server

View File

@ -12,20 +12,20 @@ from settings import settings
class WSServer(Thread):
def __init__(self, *args, **kwargs) -> None:
self.connected_clients = set[WebSocketServerProtocol]()
super().__init__(*args, **kwargs)
async def handler(self, websocket: WebSocketServerProtocol) -> None:
"""New handler instance spawns for each connected client"""
self.connected_clients.add(websocket)
logging.info(f"Add client: {websocket.id}")
try:
# send initial game state to connected client
await self.send_game_state_to_client(websocket)
# loop and do nothing while client is connected
connected = True
while connected:
try:
message = await websocket.recv()
# we're expecting nothing from client, but read if client sends a message
await websocket.recv()
except ConnectionClosedOK:
connected = False
finally:
@ -49,11 +49,13 @@ class WSServer(Thread):
async def send_game_state_to_client(
self, websocket: WebSocketServerProtocol
) -> None:
"""Send game state to the client"""
message = self._create_game_state_message()
logging.debug(f"Sending game state to client: {websocket.id}")
await websocket.send(message)
async def send_game_state(self) -> None:
"""Broadcast game state to all connected clients"""
if not self.connected_clients:
return
@ -77,4 +79,5 @@ class WSServer(Thread):
await asyncio.Future() # run forever
def run(self) -> None:
self.connected_clients = set[WebSocketServerProtocol]()
asyncio.run(self.run_async())