WS send game state

This commit is contained in:
Eden Kirin
2023-03-25 15:54:23 +01:00
parent 0f0fe68890
commit 4b511c0cb8
5 changed files with 41 additions and 18 deletions

View File

@ -18,14 +18,14 @@ from hopper.api.dto import (
from hopper.engine import GameEngine from hopper.engine import GameEngine
from hopper.enums import Direction, PlayerMoveResult from hopper.enums import Direction, PlayerMoveResult
from hopper.errors import Collision, PositionOutOfBounds from hopper.errors import Collision, PositionOutOfBounds
from hopper.ws_client import send_game_state from hopper.ws_client import ws_send_game_state
router = APIRouter() router = APIRouter()
@router.get("/ping", response_model=PingResponse) @router.get("/ping", response_model=PingResponse)
async def ping() -> PingResponse: async def ping() -> PingResponse:
await send_game_state() await ws_send_game_state()
return PingResponse( return PingResponse(
message="Pong!", message="Pong!",
) )
@ -48,7 +48,7 @@ async def start_game(
body: StartGameRequestDto, body: StartGameRequestDto,
engine: GameEngine = Depends(get_game_engine), engine: GameEngine = Depends(get_game_engine),
) -> StartGameResponseDto: ) -> StartGameResponseDto:
new_player = engine.start_game(player_name=body.player_name) new_player = await engine.start_game(player_name=body.player_name)
return StartGameResponseDto( return StartGameResponseDto(
board=engine.board, board=engine.board,
@ -118,7 +118,7 @@ async def move_player(
) )
try: try:
move_result = engine.move_player(player, direction) move_result = await engine.move_player(player, direction)
except PositionOutOfBounds: except PositionOutOfBounds:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Position out of bounds" status_code=status.HTTP_409_CONFLICT, detail="Position out of bounds"

View File

@ -9,10 +9,11 @@ from hopper.models.board import (
Layer, Layer,
LayerObject, LayerObject,
ObjectType, ObjectType,
create_random_position, create_random_position, BoardLayout,
) )
from hopper.models.player import Player, PlayerList, Position from hopper.models.player import Player, PlayerList, Position
from hopper.watchdog import InactivityWatchdog from hopper.watchdog import InactivityWatchdog
from hopper.ws_client import ws_send_game_state
from settings import settings from settings import settings
@ -46,7 +47,7 @@ class GameEngine:
) )
self._inacivity_watchdog.start() self._inacivity_watchdog.start()
def start_game(self, player_name: str) -> Player: async def start_game(self, player_name: str) -> Player:
self._start_inactivity_watchdog() self._start_inactivity_watchdog()
player = Player( player = Player(
name=player_name, name=player_name,
@ -57,9 +58,11 @@ class GameEngine:
logging.info(f"Starting new game for player: {player}") logging.info(f"Starting new game for player: {player}")
self.__debug_print_board() self.__debug_print_board()
await ws_send_game_state()
return player return player
def move_player(self, player: Player, direction: Direction) -> PlayerMoveResult: async def move_player(self, player: Player, direction: Direction) -> PlayerMoveResult:
player.reset_timeout() player.reset_timeout()
new_position = Position(player.position.x, player.position.y) new_position = Position(player.position.x, player.position.y)
@ -87,6 +90,8 @@ class GameEngine:
player.position = new_position player.position = new_position
player.move_count += 1 player.move_count += 1
await ws_send_game_state()
if self.is_player_on_destination(player): if self.is_player_on_destination(player):
logging.info(f"Player {player} reached destination!") logging.info(f"Player {player} reached destination!")
return PlayerMoveResult.DESTINATION_REACHED return PlayerMoveResult.DESTINATION_REACHED
@ -105,6 +110,8 @@ class GameEngine:
def colided_with_obstacle(self, position: Position) -> bool: def colided_with_obstacle(self, position: Position) -> bool:
return self.board.get_object_at_position(position) is not None return self.board.get_object_at_position(position) is not None
def get_board_layout(self) -> BoardLayout:
return BoardLayout(board=self.board, players=self.players)
class GameEngineFactory: class GameEngineFactory:
@staticmethod @staticmethod
@ -144,6 +151,7 @@ class GameEngineFactory:
def __add_test_player(players: PlayerList) -> None: def __add_test_player(players: PlayerList) -> None:
if not (settings.debug and settings.debug.CREATE_TEST_PLAYER): if not (settings.debug and settings.debug.CREATE_TEST_PLAYER):
return return
player = Player( player = Player(
name="Pero", name="Pero",
uuid="test-player-id", uuid="test-player-id",

View File

@ -1,9 +1,11 @@
import asyncio
import datetime import datetime
import logging import logging
import time import time
from threading import Thread from threading import Thread
from hopper.models.player import PlayerList from hopper.models.player import PlayerList
from hopper.ws_client import ws_send_game_state
from settings import settings from settings import settings
@ -28,6 +30,8 @@ class InactivityWatchdog(Thread):
seconds=settings.inacivity_watchdog.KICK_TIMEOUT seconds=settings.inacivity_watchdog.KICK_TIMEOUT
) )
send_game_state = False
for player in self.players: for player in self.players:
if ( if (
player.can_be_deactivated player.can_be_deactivated
@ -36,6 +40,7 @@ class InactivityWatchdog(Thread):
): ):
player.active = False player.active = False
logging.info(f"Player {player} set as inactive") logging.info(f"Player {player} set as inactive")
send_game_state = True
# safe remove from list # safe remove from list
n = 0 n = 0
@ -44,8 +49,16 @@ class InactivityWatchdog(Thread):
if player.can_be_deactivated and player.last_seen < kick_threshold: if player.can_be_deactivated and player.last_seen < kick_threshold:
self.players.pop(n) self.players.pop(n)
logging.info(f"Player {player} kicked out") logging.info(f"Player {player} kicked out")
send_game_state = True
else: else:
n += 1 n += 1
if send_game_state:
self.send_game_state()
def send_game_state(self):
logging.info("Sending WS game state")
asyncio.run(ws_send_game_state())
def stop(self) -> None: def stop(self) -> None:
self.stopped = True self.stopped = True

View File

@ -3,7 +3,6 @@ from contextlib import asynccontextmanager
import websockets import websockets
from hopper.api.dependencies import get_game_engine
from hopper.models.ws_dto import GameStateDto from hopper.models.ws_dto import GameStateDto
from settings import settings from settings import settings
@ -15,7 +14,10 @@ async def create_ws_client() -> websockets.WebSocketServerProtocol:
yield websocket yield websocket
async def send_game_state() -> None: async def ws_send_game_state() -> None:
# avoid circular imports
from hopper.api.dependencies import get_game_engine
async with create_ws_client() as websocket: async with create_ws_client() as websocket:
engine = get_game_engine() engine = get_game_engine()
@ -23,9 +25,6 @@ async def send_game_state() -> None:
board=engine.board, board=engine.board,
destination=engine.board.destination, destination=engine.board.destination,
players=engine.players, players=engine.players,
layers=engine.board.layers, layers=engine.get_board_layout().layers,
) )
print(json.dumps(game_state.dict(), indent=4))
await websocket.send(json.dumps(game_state.dict())) await websocket.send(json.dumps(game_state.dict()))

View File

@ -6,7 +6,7 @@ from websockets import WebSocketServerProtocol, broadcast
from settings import settings from settings import settings
connected_clients = set() connected_clients = set[WebSocketServerProtocol]()
def setup_logging() -> None: def setup_logging() -> None:
@ -18,15 +18,18 @@ def setup_logging() -> None:
async def ws_handler(websocket: WebSocketServerProtocol): async def ws_handler(websocket: WebSocketServerProtocol):
connected_clients.add(websocket) connected_clients.add(websocket)
logging.info(f"Add client: {websocket}") logging.info(f"Add client: {websocket.id}")
try: try:
async for message in websocket: async for message in websocket:
print(">>>>>>>>>>", message) logging.debug(f"Received message: {message}")
broadcast_clients = [client for client in connected_clients if client.id != websocket.id]
if broadcast_clients:
logging.debug(f"Broadcast message to clients: {broadcast_clients}")
broadcast(connected_clients, message) broadcast(connected_clients, message)
finally: finally:
connected_clients.remove(websocket) connected_clients.remove(websocket)
logging.info(f"Remove client: {websocket}") logging.info(f"Remove client: {websocket.id}")
async def main(): async def main():