Merge branch 'main' into geoip

This commit is contained in:
咕谷酱
2025-08-18 00:49:00 +08:00
committed by GitHub
7 changed files with 143 additions and 138 deletions

5
.gitignore vendored
View File

@@ -52,7 +52,7 @@ coverage.xml
.pytest_cache/ .pytest_cache/
cover/ cover/
# 测试用的证书 # 测试用的证书
test-cert/ test-cert/
# Translations # Translations
*.mo *.mo
@@ -217,4 +217,5 @@ replays/
osu-master/* osu-master/*
geoip/* geoip/*
newrelic.ini newrelic.ini
logs/

View File

@@ -127,6 +127,16 @@ logger.add(
level=settings.log_level, level=settings.log_level,
diagnose=settings.debug, diagnose=settings.debug,
) )
logger.add(
"logs/{time:YYYY-MM-DD}.log",
rotation="00:00",
retention="30 days",
colorize=False,
format="{time:YYYY-MM-DD HH:mm:ss} {level} | {message}",
level=settings.log_level,
diagnose=settings.debug,
encoding="utf8",
)
logging.basicConfig(handlers=[InterceptHandler()], level=settings.log_level, force=True) logging.basicConfig(handlers=[InterceptHandler()], level=settings.log_level, force=True)
uvicorn_loggers = [ uvicorn_loggers = [
@@ -140,3 +150,5 @@ for logger_name in uvicorn_loggers:
uvicorn_logger = logging.getLogger(logger_name) uvicorn_logger = logging.getLogger(logger_name)
uvicorn_logger.handlers = [InterceptHandler()] uvicorn_logger.handlers = [InterceptHandler()]
uvicorn_logger.propagate = False uvicorn_logger.propagate = False
logging.getLogger("httpx").setLevel("WARNING")

View File

@@ -167,7 +167,8 @@ async def mark_as_read(
if db_channel is None: if db_channel is None:
raise HTTPException(status_code=404, detail="Channel not found") raise HTTPException(status_code=404, detail="Channel not found")
assert db_channel.channel_id assert db_channel.channel_id
await server.mark_as_read(db_channel.channel_id, message) assert current_user.id
await server.mark_as_read(db_channel.channel_id, current_user.id, message)
class PMReq(BaseModel): class PMReq(BaseModel):

View File

@@ -69,8 +69,8 @@ class ChatServer:
if client: if client:
await self.send_event(client, event) await self.send_event(client, event)
async def mark_as_read(self, channel_id: int, message_id: int): async def mark_as_read(self, channel_id: int, user_id: int, message_id: int):
await self.redis.set(f"chat:{channel_id}:last_msg", message_id) await self.redis.set(f"chat:{channel_id}:last_read:{user_id}", message_id)
async def send_message_to_channel( async def send_message_to_channel(
self, message: ChatMessageResp, is_bot_command: bool = False self, message: ChatMessageResp, is_bot_command: bool = False
@@ -91,7 +91,10 @@ class ChatServer:
) )
) )
assert message.message_id assert message.message_id
await self.mark_as_read(message.channel_id, message.message_id) await self.mark_as_read(
message.channel_id, message.sender_id, message.message_id
)
await self.redis.set(f"chat:{message.channel_id}:last_msg", message.message_id)
async def batch_join_channel( async def batch_join_channel(
self, users: list[User], channel: ChatChannel, session: AsyncSession self, users: list[User], channel: ChatChannel, session: AsyncSession

View File

@@ -273,12 +273,16 @@ class Hub[TState: UserState]:
result = await self.invoke_method(client, packet.target, args) result = await self.invoke_method(client, packet.target, args)
except InvokeException as e: except InvokeException as e:
error = e.message error = e.message
except Exception as e: logger.debug(
f"Client {client.connection_token} call {packet.target}"
f" failed: {error}"
)
except Exception:
logger.exception( logger.exception(
f"Error invoking method {packet.target} for " f"Error invoking method {packet.target} for "
f"client {client.connection_id}" f"client {client.connection_id}"
) )
error = str(e) error = "Unknown error occured in server"
if packet.invocation_id is not None: if packet.invocation_id is not None:
await client.send_packet( await client.send_packet(
CompletionPacket( CompletionPacket(

View File

@@ -336,12 +336,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
async def ChangeBeatmapAvailability( async def ChangeBeatmapAvailability(
self, client: Client, beatmap_availability: BeatmapAvailability self, client: Client, beatmap_availability: BeatmapAvailability
): ):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -353,60 +348,74 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
) )
async def AddPlaylistItem(self, client: Client, item: PlaylistItem): async def AddPlaylistItem(self, client: Client, item: PlaylistItem):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
raise InvokeException("You are not in this room") raise InvokeException("You are not in this room")
logger.info(
f"[MultiplayerHub] {client.user_id} adding "
f"beatmap {item.beatmap_id} to room {room.room_id}"
)
await server_room.queue.add_item( await server_room.queue.add_item(
item, item,
user, user,
) )
async def EditPlaylistItem(self, client: Client, item: PlaylistItem): async def EditPlaylistItem(self, client: Client, item: PlaylistItem):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
raise InvokeException("You are not in this room") raise InvokeException("You are not in this room")
logger.info(
f"[MultiplayerHub] {client.user_id} editing "
f"item {item.id} in room {room.room_id}"
)
await server_room.queue.edit_item( await server_room.queue.edit_item(
item, item,
user, user,
) )
async def RemovePlaylistItem(self, client: Client, item_id: int): async def RemovePlaylistItem(self, client: Client, item_id: int):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
raise InvokeException("You are not in this room") raise InvokeException("You are not in this room")
logger.info(
f"[MultiplayerHub] {client.user_id} removing "
f"item {item_id} from room {room.room_id}"
)
await server_room.queue.remove_item( await server_room.queue.remove_item(
item_id, item_id,
user, user,
) )
async def change_db_settings(self, room: ServerMultiplayerRoom):
async with AsyncSession(engine) as session:
await session.execute(
update(Room)
.where(col(Room.id) == room.room.room_id)
.values(
name=room.room.settings.name,
type=room.room.settings.match_type,
queue_mode=room.room.settings.queue_mode,
auto_skip=room.room.settings.auto_skip,
auto_start_duration=int(
room.room.settings.auto_start_duration.total_seconds()
),
host_id=room.room.host.user_id,
)
)
async def setting_changed(self, room: ServerMultiplayerRoom, beatmap_changed: bool): async def setting_changed(self, room: ServerMultiplayerRoom, beatmap_changed: bool):
await self.change_db_settings(room)
await self.validate_styles(room) await self.validate_styles(room)
await self.unready_all_users(room, beatmap_changed) await self.unready_all_users(room, beatmap_changed)
await self.broadcast_group_call( await self.broadcast_group_call(
@@ -444,12 +453,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
async def ChangeUserStyle( async def ChangeUserStyle(
self, client: Client, beatmap_id: int | None, ruleset_id: int | None self, client: Client, beatmap_id: int | None, ruleset_id: int | None
): ):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -572,12 +576,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
) )
async def ChangeUserMods(self, client: Client, new_mods: list[APIMod]): async def ChangeUserMods(self, client: Client, new_mods: list[APIMod]):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -634,7 +633,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
"Cannot ready up while all items have been played." "Cannot ready up while all items have been played."
) )
case MultiplayerUserState.WAITING_FOR_LOAD: case MultiplayerUserState.WAITING_FOR_LOAD:
raise InvokeException("Cannot change state from {old} to {new}") raise InvokeException(f"Cannot change state from {old} to {new}")
case MultiplayerUserState.LOADED: case MultiplayerUserState.LOADED:
if old != MultiplayerUserState.WAITING_FOR_LOAD: if old != MultiplayerUserState.WAITING_FOR_LOAD:
raise InvokeException(f"Cannot change state from {old} to {new}") raise InvokeException(f"Cannot change state from {old} to {new}")
@@ -647,18 +646,13 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
if old != MultiplayerUserState.PLAYING: if old != MultiplayerUserState.PLAYING:
raise InvokeException(f"Cannot change state from {old} to {new}") raise InvokeException(f"Cannot change state from {old} to {new}")
case MultiplayerUserState.RESULTS: case MultiplayerUserState.RESULTS:
raise InvokeException("Cannot change state from {old} to {new}") raise InvokeException(f"Cannot change state from {old} to {new}")
case MultiplayerUserState.SPECTATING: case MultiplayerUserState.SPECTATING:
if old not in (MultiplayerUserState.IDLE, MultiplayerUserState.READY): if old not in (MultiplayerUserState.IDLE, MultiplayerUserState.READY):
raise InvokeException(f"Cannot change state from {old} to {new}") raise InvokeException(f"Cannot change state from {old} to {new}")
async def ChangeState(self, client: Client, state: MultiplayerUserState): async def ChangeState(self, client: Client, state: MultiplayerUserState):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -666,6 +660,10 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
if user.state == state: if user.state == state:
return return
logger.info(
f"[MultiplayerHub] {user.user_id}'s state "
f"changed from {user.state} to {state}"
)
match state: match state:
case MultiplayerUserState.IDLE: case MultiplayerUserState.IDLE:
if user.state.is_playing: if user.state.is_playing:
@@ -684,6 +682,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
or room.state == MultiplayerRoomState.WAITING_FOR_LOAD or room.state == MultiplayerRoomState.WAITING_FOR_LOAD
): ):
await self.call_noblock(client, "LoadRequested") await self.call_noblock(client, "LoadRequested")
await self.update_room_state(server_room) await self.update_room_state(server_room)
async def change_user_state( async def change_user_state(
@@ -767,6 +766,10 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
async def change_room_state( async def change_room_state(
self, room: ServerMultiplayerRoom, state: MultiplayerRoomState self, room: ServerMultiplayerRoom, state: MultiplayerRoomState
): ):
logger.debug(
f"[MultiplayerHub] Room {room.room.room_id} state "
f"changed from {room.room.state} to {state}"
)
room.room.state = state room.room.state = state
await self.broadcast_group_call( await self.broadcast_group_call(
self.group_id(room.room.room_id), self.group_id(room.room.room_id),
@@ -775,18 +778,12 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
) )
async def StartMatch(self, client: Client): async def StartMatch(self, client: Client):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
raise InvokeException("You are not in this room") raise InvokeException("You are not in this room")
if room.host is None or room.host.user_id != client.user_id: self._ensure_host(client, server_room)
raise InvokeException("You are not the host of this room")
# Check host state - host must be ready or spectating # Check host state - host must be ready or spectating
if room.host.state not in ( if room.host.state not in (
@@ -799,6 +796,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
if all(u.state != MultiplayerUserState.READY for u in room.users): if all(u.state != MultiplayerUserState.READY for u in room.users):
raise InvokeException("Can't start match when no users are ready.") raise InvokeException("Can't start match when no users are ready.")
logger.info(f"[MultiplayerHub] Room {room.room_id} match started")
await self.start_match(server_room) await self.start_match(server_room)
async def start_match(self, room: ServerMultiplayerRoom): async def start_match(self, room: ServerMultiplayerRoom):
@@ -894,34 +892,21 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
async def make_user_leave( async def make_user_leave(
self, self,
client: Client, client: Client | None,
room: ServerMultiplayerRoom, room: ServerMultiplayerRoom,
user: MultiplayerRoomUser, user: MultiplayerRoomUser,
kicked: bool = False, kicked: bool = False,
): ):
self.remove_from_group(client, self.group_id(room.room.room_id)) if client:
self.remove_from_group(client, self.group_id(room.room.room_id))
room.room.users.remove(user) room.room.users.remove(user)
if len(room.room.users) == 0: target_store = self.state.get(user.user_id)
await self.end_room(room) if target_store:
await self.update_room_state(room) target_store.room_id = 0
if (
len(room.room.users) != 0
and room.room.host
and room.room.host.user_id == user.user_id
):
next_host = room.room.users[0]
await self.set_host(room, next_host)
if kicked: redis = get_redis()
await self.call_noblock(client, "UserKicked", user) await redis.publish("chat:room:left", f"{room.room.channel_id}:{user.user_id}")
await self.broadcast_group_call(
self.group_id(room.room.room_id), "UserKicked", user
)
else:
await self.broadcast_group_call(
self.group_id(room.room.room_id), "UserLeft", user
)
async with AsyncSession(engine) as session: async with AsyncSession(engine) as session:
async with session.begin(): async with session.begin():
@@ -941,12 +926,28 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
raise InvokeException("Room does not exist in database") raise InvokeException("Room does not exist in database")
db_room.participant_count -= 1 db_room.participant_count -= 1
target_store = self.state.get(user.user_id) if len(room.room.users) == 0:
if target_store: await self.end_room(room)
target_store.room_id = 0 return
await self.update_room_state(room)
if (
len(room.room.users) != 0
and room.room.host
and room.room.host.user_id == user.user_id
):
next_host = room.room.users[0]
await self.set_host(room, next_host)
redis = get_redis() if kicked:
await redis.publish("chat:room:left", f"{room.room.channel_id}:{user.user_id}") if client:
await self.call_noblock(client, "UserKicked", user)
await self.broadcast_group_call(
self.group_id(room.room.room_id), "UserKicked", user
)
else:
await self.broadcast_group_call(
self.group_id(room.room.room_id), "UserLeft", user
)
async def end_room(self, room: ServerMultiplayerRoom): async def end_room(self, room: ServerMultiplayerRoom):
assert room.room.host assert room.room.host
@@ -971,14 +972,13 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
room.room.host.user_id, room.room.host.user_id,
) )
del self.rooms[room.room.room_id] del self.rooms[room.room.room_id]
logger.info(f"[MultiplayerHub] Room {room.room.room_id} ended")
async def LeaveRoom(self, client: Client): async def LeaveRoom(self, client: Client):
store = self.get_or_create_state(client) store = self.get_or_create_state(client)
if store.room_id == 0: if store.room_id == 0:
return return
if store.room_id not in self.rooms: server_room = self._ensure_in_room(client)
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -989,18 +989,12 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
user.user_id, user.user_id,
) )
await self.make_user_leave(client, server_room, user) await self.make_user_leave(client, server_room, user)
logger.info(f"[MultiplayerHub] {client.user_id} left room {room.room_id}")
async def KickUser(self, client: Client, user_id: int): async def KickUser(self, client: Client, user_id: int):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
self._ensure_host(client, server_room)
if room.host is None or room.host.user_id != client.user_id:
raise InvokeException("You are not the host of this room")
if user_id == client.user_id: if user_id == client.user_id:
raise InvokeException("Can't kick self") raise InvokeException("Can't kick self")
@@ -1014,12 +1008,15 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
user.user_id, user.user_id,
) )
target_client = self.get_client_by_id(str(user.user_id)) target_client = self.get_client_by_id(str(user.user_id))
if target_client is None:
return
await self.make_user_leave(target_client, server_room, user, kicked=True) await self.make_user_leave(target_client, server_room, user, kicked=True)
logger.info(
f"[MultiplayerHub] {user.user_id} was kicked from room {room.room_id}"
f"by {client.user_id}"
)
async def set_host(self, room: ServerMultiplayerRoom, user: MultiplayerRoomUser): async def set_host(self, room: ServerMultiplayerRoom, user: MultiplayerRoomUser):
room.room.host = user room.room.host = user
await self.change_db_settings(room)
await self.broadcast_group_call( await self.broadcast_group_call(
self.group_id(room.room.room_id), self.group_id(room.room.room_id),
"HostChanged", "HostChanged",
@@ -1027,16 +1024,9 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
) )
async def TransferHost(self, client: Client, user_id: int): async def TransferHost(self, client: Client, user_id: int):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
self._ensure_host(client, server_room)
if room.host is None or room.host.user_id != client.user_id:
raise InvokeException("You are not the host of this room")
new_host = next((u for u in room.users if u.user_id == user_id), None) new_host = next((u for u in room.users if u.user_id == user_id), None)
if new_host is None: if new_host is None:
@@ -1046,14 +1036,13 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
new_host.user_id, new_host.user_id,
) )
await self.set_host(server_room, new_host) await self.set_host(server_room, new_host)
logger.info(
f"[MultiplayerHub] {client.user_id} transferred host to {new_host.user_id}"
f" in room {room.room_id}"
)
async def AbortGameplay(self, client: Client): async def AbortGameplay(self, client: Client):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -1070,16 +1059,9 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
await self.update_room_state(server_room) await self.update_room_state(server_room)
async def AbortMatch(self, client: Client): async def AbortMatch(self, client: Client):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
self._ensure_host(client, server_room)
if room.host is None or room.host.user_id != client.user_id:
raise InvokeException("You are not the host of this room")
if ( if (
room.state != MultiplayerRoomState.PLAYING room.state != MultiplayerRoomState.PLAYING
@@ -1100,6 +1082,9 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
GameplayAbortReason.HOST_ABORTED, GameplayAbortReason.HOST_ABORTED,
) )
await self.update_room_state(server_room) await self.update_room_state(server_room)
logger.info(
f"[MultiplayerHub] {client.user_id} aborted match in room {room.room_id}"
)
async def change_user_match_state( async def change_user_match_state(
self, room: ServerMultiplayerRoom, user: MultiplayerRoomUser self, room: ServerMultiplayerRoom, user: MultiplayerRoomUser
@@ -1141,12 +1126,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
await self.update_room_state(server_room) await self.update_room_state(server_room)
async def SendMatchRequest(self, client: Client, request: MatchRequest): async def SendMatchRequest(self, client: Client, request: MatchRequest):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:
@@ -1156,7 +1136,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
if room.host and room.host.user_id != user.user_id: if room.host and room.host.user_id != user.user_id:
raise InvokeException("You are not the host of this room") raise InvokeException("You are not the host of this room")
if room.state != MultiplayerRoomState.OPEN: if room.state != MultiplayerRoomState.OPEN:
raise InvokeException("Cannot start a countdown during ongoing play") raise InvokeException("Cannot start match countdown when not open")
await server_room.start_countdown( await server_room.start_countdown(
MatchStartCountdown(time_remaining=request.duration), MatchStartCountdown(time_remaining=request.duration),
self.start_match, self.start_match,
@@ -1181,12 +1161,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
await server_room.match_type_handler.handle_request(user, request) await server_room.match_type_handler.handle_request(user, request)
async def InvitePlayer(self, client: Client, user_id: int): async def InvitePlayer(self, client: Client, user_id: int):
store = self.get_or_create_state(client) server_room = self._ensure_in_room(client)
if store.room_id == 0:
raise InvokeException("You are not in a room")
if store.room_id not in self.rooms:
raise InvokeException("Room does not exist")
server_room = self.rooms[store.room_id]
room = server_room.room room = server_room.room
user = next((u for u in room.users if u.user_id == client.user_id), None) user = next((u for u in room.users if u.user_id == client.user_id), None)
if user is None: if user is None:

View File

@@ -6,6 +6,7 @@ import lzma
import struct import struct
import time import time
from typing import override from typing import override
from venv import logger
from app.config import settings from app.config import settings
from app.database import Beatmap, User from app.database import Beatmap, User
@@ -217,6 +218,7 @@ class SpectatorHub(Hub[StoreClientState]):
maximum_statistics=state.maximum_statistics, maximum_statistics=state.maximum_statistics,
) )
) )
logger.info(f"[SpectatorHub] {client.user_id} began playing {state.beatmap_id}")
await self.broadcast_group_call( await self.broadcast_group_call(
self.group_id(user_id), self.group_id(user_id),
"UserBeganPlaying", "UserBeganPlaying",
@@ -320,6 +322,10 @@ class SpectatorHub(Hub[StoreClientState]):
async def _end_session(self, user_id: int, state: SpectatorState) -> None: async def _end_session(self, user_id: int, state: SpectatorState) -> None:
if state.state == SpectatedUserState.Playing: if state.state == SpectatedUserState.Playing:
state.state = SpectatedUserState.Quit state.state = SpectatedUserState.Quit
logger.info(
f"[SpectatorHub] {user_id} finished playing {state.beatmap_id} "
f"with {state.state}"
)
await self.broadcast_group_call( await self.broadcast_group_call(
self.group_id(user_id), self.group_id(user_id),
"UserFinishedPlaying", "UserFinishedPlaying",
@@ -330,7 +336,9 @@ class SpectatorHub(Hub[StoreClientState]):
async def StartWatchingUser(self, client: Client, target_id: int) -> None: async def StartWatchingUser(self, client: Client, target_id: int) -> None:
user_id = int(client.connection_id) user_id = int(client.connection_id)
target_store = self.state.get(target_id) target_store = self.state.get(target_id)
logger.info(f"[SpectatorHub] {user_id} started watching {target_id}")
if target_store and target_store.state: if target_store and target_store.state:
logger.debug(f"[SpectatorHub] {target_id} is {target_store.state}")
await self.call_noblock( await self.call_noblock(
client, client,
"UserBeganPlaying", "UserBeganPlaying",
@@ -361,3 +369,4 @@ class SpectatorHub(Hub[StoreClientState]):
store.watched_user.discard(target_id) store.watched_user.discard(target_id)
if (target_client := self.get_client_by_id(str(target_id))) is not None: if (target_client := self.get_client_by_id(str(target_id))) is not None:
await self.call_noblock(target_client, "UserEndedWatching", user_id) await self.call_noblock(target_client, "UserEndedWatching", user_id)
logger.info(f"[SpectatorHub] {user_id} ended watching {target_id}")