Начало реализации игрового цикла и закрытие некоторых гештальтов

This commit is contained in:
Евгений Титаренко 2023-07-11 19:35:15 +03:00
parent a5e3234568
commit 4275036531
3 changed files with 76 additions and 31 deletions

29
game.py
View file

@ -23,7 +23,7 @@ STREETS = []
class GameState(Enum): class GameState(Enum):
WAITING = 0 START = 0
IN_GAME = 1 IN_GAME = 1
FINISHED = 2 FINISHED = 2
@ -174,13 +174,15 @@ def get_dict_user_or_none(user: User | None):
class Room: class Room:
def __init__(self, password: str = ""): def __init__(self, socket, password: str = ""):
self.socket = socket
self.room_id = str(uuid4()) self.room_id = str(uuid4())
self.users = [] self.users = []
self.state = GameState.WAITING self.state = GameState.START
self.password = password self.password = password
self.current_player = None self.current_player = None
self.streets = copy_streets() self.streets = copy_streets()
self.ready_users = []
def get_dict(self): def get_dict(self):
return { return {
@ -194,12 +196,14 @@ class Room:
def check_color(self, color: Color): def check_color(self, color: Color):
return color not in [user.color for user in self.users] return color not in [user.color for user in self.users]
def add_user(self, user: User): async def send_room_info(self):
if len(self.users) == MAX_PLAYERS: await self.socket.emit('roomInfo', self.get_dict(), room=self.room_id)
return # TODO
async def add_user(self, user: User):
available_colors = list(filter(self.check_color, list(DefaultColors))) available_colors = list(filter(self.check_color, list(DefaultColors)))
user.color = choice_color_from_list(available_colors) user.color = choice_color_from_list(available_colors)
self.users.append(user) self.users.append(user)
await self.send_room_info()
def sort_players_by_initiative(self): def sort_players_by_initiative(self):
self.users.sort(key=lambda user: user.initiative, reverse=True) self.users.sort(key=lambda user: user.initiative, reverse=True)
@ -219,9 +223,20 @@ class Room:
self.current_player = self.users[0] self.current_player = self.users[0]
self.state = GameState.IN_GAME self.state = GameState.IN_GAME
def remove_user(self, user: User): async def remove_user(self, user: User):
if user in self.users: if user in self.users:
self.users.remove(user) self.users.remove(user)
await self.send_room_info()
def is_empty(self): def is_empty(self):
return not len(self.users) > 0 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()

74
main.py
View file

@ -18,11 +18,12 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi_socketio import SocketManager from fastapi_socketio import SocketManager
from cachetools import TTLCache 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 objects import ErrCode, UserCheck
from threading import Timer 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") logging.basicConfig(level=logging.DEBUG, format="%(levelname)s:\t%(asctime)s\t%(message)s")
load_streets() load_streets()
@ -69,13 +70,13 @@ def check_user(username, password=None, token=None):
async def sio_create_room(sid, username, token, password): async def sio_create_room(sid, username, token, password):
match check_user(username, token=token): match check_user(username, token=token):
case User(username=username) as user: 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}") logging.info(f"User {username} created room {new_room.room_id}")
new_room.add_user(user)
rooms[new_room.room_id] = new_room rooms[new_room.room_id] = new_room
await sio.emit("roomCreated", {"room_id": new_room.room_id, "room_password": new_room.password}, room=sid) await sio.emit("roomCreated", {"room_id": new_room.room_id, "room_password": new_room.password}, room=sid)
case _: case UserCheck.USER_DOESNT_EXISTS | UserCheck.INVALID_CREDENTIALS:
return False # TODO: some errors await sio_throw_error(sid, ErrCode.INVALID_CREDENTIALS)
return False
async def sio_send_user_info(sid, user): async def sio_send_user_info(sid, user):
@ -88,7 +89,7 @@ async def sio_send_user_info(sid, user):
@sio.on("joinRoom") @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): match check_user(username, token=token):
case User(username=username) as user: case User(username=username) as user:
global rooms global rooms
@ -98,12 +99,23 @@ async def sio_join_room(sid, username, token, room_id, room_password): # TODO:
else: else:
room = rooms[room_id] room = rooms[room_id]
if room.password == room_password: if room.password == room_password:
user.in_room = room.room_id
user.room_password = room.password
room_data = room.get_dict() 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_send_user_info(sid, user)
await sio.emit("roomInfo", room_data, room=sid) await sio.emit("roomInfo", room_data, room=sid)
else:
await sio_throw_error(sid, ErrCode.INVALID_ROOM_PASSWORD)
case _: case _:
await sio_throw_error(sid, ErrCode.INVALID_CREDENTIALS) await sio_throw_error(sid, ErrCode.INVALID_CREDENTIALS)
return False return False
@ -162,23 +174,37 @@ async def sio_disconnect(sid):
users_to_disconnect[user.username] = t users_to_disconnect[user.username] = t
t.start() t.start()
def disconnect_user(user): def disconnect_user(user):
global rooms async def async_disconnect():
room = rooms[user.in_room] global rooms
room.remove_user(user) room = rooms[user.in_room]
logging.info(f"User {user.username} disconnected from room {room.room_id}") await room.remove_user(user)
if room.is_empty(): logging.info(f"User {user.username} disconnected from room {room.room_id}")
logging.info(f"Room {room.room_id} is closed") user.in_room = False
del rooms[room.room_id] user.room_password = ""
del room if room.is_empty():
t = users_to_disconnect[user.username] logging.info(f"Room {room.room_id} is closed")
t.cancel() del rooms[room.room_id]
del t del room
del users_to_disconnect[user.username] t = users_to_disconnect[user.username]
t.cancel()
del t
del users_to_disconnect[user.username]
asyncio.run(async_disconnect())
@sio.on("rollDices") @sio.on("rollDices")
async def sio_roll_dices(sid, token: str = ""): async def sio_roll_dices(sid, token: str = ""):
print("test") 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)

View file

@ -13,3 +13,7 @@ class ErrCode(Enum):
INVALID_CREDENTIALS = 3 INVALID_CREDENTIALS = 3
INVALID_TOKEN = 4 INVALID_TOKEN = 4
ROOM_DOESNT_EXIST = 10 ROOM_DOESNT_EXIST = 10
INVALID_ROOM_PASSWORD = 11
USER_ALREADY_IN_OTHER_ROOM = 12
ROOM_IS_FULL = 13
GAME_IS_ALREADY_STARTED = 20