diff --git a/game.py b/game.py index 0f13702..8598dbf 100644 --- a/game.py +++ b/game.py @@ -23,7 +23,7 @@ STREETS = [] class GameState(Enum): - WAITING = 0 + START = 0 IN_GAME = 1 FINISHED = 2 @@ -174,13 +174,15 @@ def get_dict_user_or_none(user: User | None): class Room: - def __init__(self, password: str = ""): + def __init__(self, socket, password: str = ""): + self.socket = socket self.room_id = str(uuid4()) self.users = [] - self.state = GameState.WAITING + self.state = GameState.START self.password = password self.current_player = None self.streets = copy_streets() + self.ready_users = [] def get_dict(self): return { @@ -194,12 +196,14 @@ class Room: def check_color(self, color: Color): return color not in [user.color for user in self.users] - def add_user(self, user: User): - if len(self.users) == MAX_PLAYERS: - return # TODO + async def send_room_info(self): + await self.socket.emit('roomInfo', self.get_dict(), room=self.room_id) + + async def add_user(self, user: User): available_colors = list(filter(self.check_color, list(DefaultColors))) user.color = choice_color_from_list(available_colors) self.users.append(user) + await self.send_room_info() def sort_players_by_initiative(self): self.users.sort(key=lambda user: user.initiative, reverse=True) @@ -219,9 +223,20 @@ class Room: self.current_player = self.users[0] self.state = GameState.IN_GAME - def remove_user(self, user: User): + async def remove_user(self, user: User): if user in self.users: self.users.remove(user) + await self.send_room_info() def is_empty(self): return not len(self.users) > 0 + + def is_full(self): + return len(self.users) == MAX_PLAYERS + + async def set_user_ready(self, user): + self.ready_users.append(user) + if len(self.ready_users) == len(self.users): + self.start_game() + else: + await self.send_room_info() diff --git a/main.py b/main.py index 38f50bc..c230d82 100644 --- a/main.py +++ b/main.py @@ -18,11 +18,12 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi_socketio import SocketManager from cachetools import TTLCache -from game import Room, User, load_streets +from game import Room, User, load_streets, GameState from objects import ErrCode, UserCheck from threading import Timer +import asyncio -ROOM_DISCONNECT_TIMEOUT = 60*5 # TODO: Сохранить остаток таймера для каждого пользователя +ROOM_DISCONNECT_TIMEOUT = 60*1 # TODO: Сохранить остаток таймера для каждого пользователя logging.basicConfig(level=logging.DEBUG, format="%(levelname)s:\t%(asctime)s\t%(message)s") load_streets() @@ -69,13 +70,13 @@ def check_user(username, password=None, token=None): async def sio_create_room(sid, username, token, password): match check_user(username, token=token): case User(username=username) as user: - new_room = Room(password) + new_room = Room(sio, password) logging.info(f"User {username} created room {new_room.room_id}") - new_room.add_user(user) rooms[new_room.room_id] = new_room await sio.emit("roomCreated", {"room_id": new_room.room_id, "room_password": new_room.password}, room=sid) - case _: - return False # TODO: some errors + case UserCheck.USER_DOESNT_EXISTS | UserCheck.INVALID_CREDENTIALS: + await sio_throw_error(sid, ErrCode.INVALID_CREDENTIALS) + return False async def sio_send_user_info(sid, user): @@ -88,7 +89,7 @@ async def sio_send_user_info(sid, user): @sio.on("joinRoom") -async def sio_join_room(sid, username, token, room_id, room_password): # TODO: Check if user already in room +async def sio_join_room(sid, username, token, room_id, room_password): # TODO: Проверить начата ли игра match check_user(username, token=token): case User(username=username) as user: global rooms @@ -98,12 +99,23 @@ async def sio_join_room(sid, username, token, room_id, room_password): # TODO: else: room = rooms[room_id] if room.password == room_password: - user.in_room = room.room_id - user.room_password = room.password room_data = room.get_dict() - sio.enter_room(sid, room.room_id) + if not user.in_room: + if room.is_full(): + await sio_throw_error(sid, ErrCode.ROOM_IS_FULL) + return + user.in_room = room.room_id + user.room_password = room.password + room_data = room.get_dict() + await room.add_user(user) + sio.enter_room(sid, room.room_id) + elif user.in_room != room.room_id: + await sio_throw_error(sid, ErrCode.USER_ALREADY_IN_OTHER_ROOM) + return await sio_send_user_info(sid, user) await sio.emit("roomInfo", room_data, room=sid) + else: + await sio_throw_error(sid, ErrCode.INVALID_ROOM_PASSWORD) case _: await sio_throw_error(sid, ErrCode.INVALID_CREDENTIALS) return False @@ -162,23 +174,37 @@ async def sio_disconnect(sid): users_to_disconnect[user.username] = t t.start() + def disconnect_user(user): - global rooms - room = rooms[user.in_room] - room.remove_user(user) - logging.info(f"User {user.username} disconnected from room {room.room_id}") - if room.is_empty(): - logging.info(f"Room {room.room_id} is closed") - del rooms[room.room_id] - del room - t = users_to_disconnect[user.username] - t.cancel() - del t - del users_to_disconnect[user.username] - - + async def async_disconnect(): + global rooms + room = rooms[user.in_room] + await room.remove_user(user) + logging.info(f"User {user.username} disconnected from room {room.room_id}") + user.in_room = False + user.room_password = "" + if room.is_empty(): + logging.info(f"Room {room.room_id} is closed") + del rooms[room.room_id] + del room + t = users_to_disconnect[user.username] + t.cancel() + del t + del users_to_disconnect[user.username] + asyncio.run(async_disconnect()) @sio.on("rollDices") async def sio_roll_dices(sid, token: str = ""): print("test") + + +@sio.on("getReady") +async def sio_get_ready(sid): + user = sio_sessions[sid] + if user.in_room: + room = rooms[user.in_room] + if not room.state == GameState.START: + await sio_throw_error(sid, ErrCode.GAME_IS_ALREADY_STARTED) + else: + await room.set_user_ready(user) diff --git a/objects.py b/objects.py index 245aa49..800e166 100644 --- a/objects.py +++ b/objects.py @@ -13,3 +13,7 @@ class ErrCode(Enum): INVALID_CREDENTIALS = 3 INVALID_TOKEN = 4 ROOM_DOESNT_EXIST = 10 + INVALID_ROOM_PASSWORD = 11 + USER_ALREADY_IN_OTHER_ROOM = 12 + ROOM_IS_FULL = 13 + GAME_IS_ALREADY_STARTED = 20