diff --git a/app/database/room.py b/app/database/room.py index 80457b6..08f1466 100644 --- a/app/database/room.py +++ b/app/database/room.py @@ -29,7 +29,7 @@ class RoomBase(SQLModel): name: str = Field(index=True) category: RoomCategory = Field(default=RoomCategory.NORMAL, index=True) duration: int | None = Field(default=None) # minutes - starts_at: datetime = Field( + starts_at: datetime | None = Field( sa_column=Column( DateTime(timezone=True), ), diff --git a/app/models/multiplayer_hub.py b/app/models/multiplayer_hub.py index 9d78282..ed37b98 100644 --- a/app/models/multiplayer_hub.py +++ b/app/models/multiplayer_hub.py @@ -335,6 +335,58 @@ class MultiplayerRoom(BaseModel): active_countdowns: list[MultiplayerCountdown] = Field(default_factory=list) channel_id: int + @classmethod + def from_db(cls, room) -> "MultiplayerRoom": + """ + 将 Room (数据库模型) 转换为 MultiplayerRoom (业务模型) + """ + + # 用户列表 + users = [MultiplayerRoomUser(user_id=room.host_id)] + host_user = MultiplayerRoomUser(user_id=room.host_id) + # playlist 转换 + playlist = [] + if hasattr(room, "playlist"): + for item in room.playlist: + playlist.append( + PlaylistItem( + id=item.id, + owner_id=item.owner_id, + beatmap_id=item.beatmap_id, + beatmap_checksum=item.beatmap.checksum if item.beatmap else "", + ruleset_id=item.ruleset_id, + required_mods=item.required_mods, + allowed_mods=item.allowed_mods, + expired=item.expired, + playlist_order=item.playlist_order, + played_at=item.played_at, + star_rating=item.beatmap.difficulty_rating + if item.beatmap is not None + else 0.0, + freestyle=item.freestyle, + ) + ) + + return cls( + room_id=room.id, + state=getattr(room, "state", MultiplayerRoomState.OPEN), + settings=MultiplayerRoomSettings( + name=room.name, + playlist_item_id=playlist[0].id if playlist else 0, + password=getattr(room, "password", ""), + match_type=room.type, + queue_mode=room.queue_mode, + auto_start_duration=timedelta(seconds=room.auto_start_duration), + auto_skip=room.auto_skip, + ), + users=users, + host=host_user, + match_state=None, + playlist=playlist, + active_countdowns=[], + channel_id=getattr(room, "channel_id", 0), + ) + class MultiplayerQueue: def __init__(self, room: "ServerMultiplayerRoom"): diff --git a/app/router/room.py b/app/router/room.py index 476eaf2..800c861 100644 --- a/app/router/room.py +++ b/app/router/room.py @@ -1,13 +1,17 @@ from __future__ import annotations +from datetime import UTC, datetime +from time import timezone from typing import Literal from app.database.lazer_user import User -from app.database.room import RoomResp +from app.database.playlists import Playlist +from app.database.room import Room, RoomBase, RoomResp from app.dependencies.database import get_db, get_redis from app.dependencies.fetcher import get_fetcher from app.dependencies.user import get_current_user from app.fetcher import Fetcher +from app.models.multiplayer_hub import MultiplayerRoom, ServerMultiplayerRoom from app.models.room import RoomStatus from app.signalr.hub import MultiplayerHubs @@ -33,22 +37,68 @@ async def get_all_rooms( rooms = MultiplayerHubs.rooms.values() resp_list: list[RoomResp] = [] for room in rooms: - if category != "realtime": # 歌单模式的处理逻辑 - if room.category == category: - if mode == "owned": - if ( - room.room.host.user_id if room.room.host is not None else 0 - ) != current_user.id: - continue - else: - continue - else: - if mode == "owned": - if ( - room.room.host.user_id if room.room.host is not None else 0 - ) != current_user.id: - continue - if room.status != status: - continue + if category == "realtime" and room.category != "normal": + continue + elif category != room.category: + continue resp_list.append(await RoomResp.from_hub(room)) return resp_list + + +class APICreatedRoom(RoomResp): + error: str = "" + + +class APIUploadedRoom(RoomBase): + def to_room(self) -> Room: + """ + 将 APIUploadedRoom 转换为 Room 对象,playlist 字段需单独处理。 + """ + room_dict = self.model_dump() + room_dict.pop("playlist", None) + # host_id 已在字段中 + return Room(**room_dict) + + id: int | None + host_id: int | None = None + playlist: list[Playlist] + + +@router.post("/rooms", tags=["room"], response_model=APICreatedRoom) +async def create_room( + room: APIUploadedRoom, + db: AsyncSession = Depends(get_db), + current_user: User = Depends(get_current_user), +): + # db_room = Room.from_resp(room) + await db.refresh(current_user) + user_id = current_user.id + db_room = room.to_room() + db_room.host_id = current_user.id if current_user.id else 1 + db.add(db_room) + await db.commit() + await db.refresh(db_room) + + playlist: list[Playlist] = [] + # 处理 APIUploadedRoom 里的 playlist 字段 + for item in room.playlist: + # 确保 room_id 正确赋值 + item.id = await Playlist.get_next_id_for_room(db_room.id, db) + item.room_id = db_room.id + item.owner_id = user_id if user_id else 1 + db.add(item) + await db.commit() + await db.refresh(item) + playlist.append(item) + await db.refresh(db_room) + db_room.playlist = playlist + server_room = ServerMultiplayerRoom( + room=MultiplayerRoom.from_db(db_room), + category=db_room.category, + start_at=datetime.now(UTC), + hub=MultiplayerHubs, + ) + MultiplayerHubs.rooms[db_room.id] = server_room + created_room = APICreatedRoom.model_validate(db_room) + created_room.error = "" + return created_room diff --git a/app/signalr/hub/multiplayer.py b/app/signalr/hub/multiplayer.py index af28d26..fa869b6 100644 --- a/app/signalr/hub/multiplayer.py +++ b/app/signalr/hub/multiplayer.py @@ -103,7 +103,7 @@ class MultiplayerHub(Hub[MultiplayerClientState]): item = room.playlist[0] item.owner_id = client.user_id room.room_id = db_room.id - starts_at = db_room.starts_at + starts_at = db_room.starts_at or datetime.now(UTC) await Playlist.add_to_db(item, db_room.id, session) server_room = ServerMultiplayerRoom( room=room,