feat(multiplay): support CreateRoom hub method
This commit is contained in:
@@ -15,8 +15,11 @@ from .lazer_user import (
|
||||
User,
|
||||
UserResp,
|
||||
)
|
||||
from .playlist_attempts import ItemAttemptsCount
|
||||
from .playlists import Playlist, PlaylistResp
|
||||
from .pp_best_score import PPBestScore
|
||||
from .relationship import Relationship, RelationshipResp, RelationshipType
|
||||
from .room import Room, RoomResp
|
||||
from .score import (
|
||||
Score,
|
||||
ScoreBase,
|
||||
@@ -43,11 +46,16 @@ __all__ = [
|
||||
"DailyChallengeStats",
|
||||
"DailyChallengeStatsResp",
|
||||
"FavouriteBeatmapset",
|
||||
"ItemAttemptsCount",
|
||||
"OAuthToken",
|
||||
"PPBestScore",
|
||||
"Playlist",
|
||||
"PlaylistResp",
|
||||
"Relationship",
|
||||
"RelationshipResp",
|
||||
"RelationshipType",
|
||||
"Room",
|
||||
"RoomResp",
|
||||
"Score",
|
||||
"ScoreBase",
|
||||
"ScoreResp",
|
||||
|
||||
9
app/database/playlist_attempts.py
Normal file
9
app/database/playlist_attempts.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class ItemAttemptsCount(SQLModel, table=True):
|
||||
__tablename__ = "item_attempts_count" # pyright: ignore[reportAssignmentType]
|
||||
id: int = Field(foreign_key="room_playlists.db_id", primary_key=True, index=True)
|
||||
room_id: int = Field(foreign_key="rooms.id", index=True)
|
||||
attempts: int = Field(default=0)
|
||||
passed: int = Field(default=0)
|
||||
85
app/database/playlists.py
Normal file
85
app/database/playlists.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.models.mods import APIMod, msgpack_to_apimod
|
||||
from app.models.multiplayer_hub import PlaylistItem
|
||||
|
||||
from .beatmap import Beatmap, BeatmapResp
|
||||
|
||||
from sqlmodel import (
|
||||
JSON,
|
||||
BigInteger,
|
||||
Column,
|
||||
DateTime,
|
||||
Field,
|
||||
ForeignKey,
|
||||
Relationship,
|
||||
SQLModel,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .room import Room
|
||||
|
||||
|
||||
class PlaylistBase(SQLModel, UTCBaseModel):
|
||||
id: int = 0
|
||||
owner_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id")))
|
||||
ruleset_id: int = Field(ge=0, le=3)
|
||||
expired: bool = Field(default=False)
|
||||
playlist_order: int = Field(default=0)
|
||||
played_at: datetime | None = Field(
|
||||
sa_column=Column(DateTime(timezone=True)),
|
||||
default=None,
|
||||
)
|
||||
allowed_mods: list[APIMod] = Field(
|
||||
default_factory=list,
|
||||
sa_column=Column(JSON),
|
||||
)
|
||||
required_mods: list[APIMod] = Field(
|
||||
default_factory=list,
|
||||
sa_column=Column(JSON),
|
||||
)
|
||||
beatmap_id: int = Field(
|
||||
foreign_key="beatmaps.id",
|
||||
)
|
||||
freestyle: bool = Field(default=False)
|
||||
|
||||
|
||||
class Playlist(PlaylistBase, table=True):
|
||||
__tablename__ = "room_playlists" # pyright: ignore[reportAssignmentType]
|
||||
db_id: int = Field(default=None, primary_key=True, index=True, exclude=True)
|
||||
room_id: int = Field(foreign_key="rooms.id", exclude=True)
|
||||
|
||||
beatmap: Beatmap = Relationship(
|
||||
sa_relationship_kwargs={
|
||||
"lazy": "joined",
|
||||
}
|
||||
)
|
||||
room: "Room" = Relationship()
|
||||
|
||||
@classmethod
|
||||
async def from_hub(cls, playlist: PlaylistItem, room_id: int) -> "Playlist":
|
||||
return cls(
|
||||
id=playlist.id,
|
||||
owner_id=playlist.owner_id,
|
||||
ruleset_id=playlist.ruleset_id,
|
||||
beatmap_id=playlist.beatmap_id,
|
||||
required_mods=[msgpack_to_apimod(mod) for mod in playlist.required_mods],
|
||||
allowed_mods=[msgpack_to_apimod(mod) for mod in playlist.allowed_mods],
|
||||
expired=playlist.expired,
|
||||
playlist_order=playlist.order,
|
||||
played_at=playlist.played_at,
|
||||
freestyle=playlist.freestyle,
|
||||
room_id=room_id,
|
||||
)
|
||||
|
||||
|
||||
class PlaylistResp(PlaylistBase):
|
||||
beatmap: BeatmapResp | None = None
|
||||
|
||||
@classmethod
|
||||
async def from_db(cls, playlist: Playlist) -> "PlaylistResp":
|
||||
resp = cls.model_validate(playlist)
|
||||
resp.beatmap = await BeatmapResp.from_db(playlist.beatmap)
|
||||
return resp
|
||||
@@ -1,6 +1,135 @@
|
||||
from sqlmodel import Field, SQLModel
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.models.multiplayer_hub import ServerMultiplayerRoom
|
||||
from app.models.room import (
|
||||
MatchType,
|
||||
QueueMode,
|
||||
RoomCategory,
|
||||
RoomDifficultyRange,
|
||||
RoomPlaylistItemStats,
|
||||
RoomStatus,
|
||||
)
|
||||
|
||||
from .lazer_user import User, UserResp
|
||||
from .playlist_attempts import ItemAttemptsCount
|
||||
from .playlists import Playlist, PlaylistResp
|
||||
|
||||
from sqlmodel import (
|
||||
BigInteger,
|
||||
Column,
|
||||
DateTime,
|
||||
Field,
|
||||
ForeignKey,
|
||||
Relationship,
|
||||
SQLModel,
|
||||
)
|
||||
|
||||
|
||||
class RoomIndex(SQLModel, table=True):
|
||||
__tablename__ = "mp_room_index" # pyright: ignore[reportAssignmentType]
|
||||
id: int = Field(default=None, primary_key=True, index=True) # pyright: ignore[reportCallIssue]
|
||||
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(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
),
|
||||
default=datetime.now(UTC),
|
||||
)
|
||||
ended_at: datetime | None = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
),
|
||||
default=None,
|
||||
)
|
||||
participant_count: int = Field(default=0)
|
||||
max_attempts: int | None = Field(default=None) # playlists
|
||||
type: MatchType
|
||||
queue_mode: QueueMode
|
||||
auto_skip: bool
|
||||
auto_start_duration: int
|
||||
status: RoomStatus
|
||||
# TODO: channel_id
|
||||
# recent_participants: list[User]
|
||||
|
||||
|
||||
class Room(RoomBase, table=True):
|
||||
__tablename__ = "rooms" # pyright: ignore[reportAssignmentType]
|
||||
id: int = Field(default=None, primary_key=True, index=True)
|
||||
host_id: int = Field(
|
||||
sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), index=True)
|
||||
)
|
||||
|
||||
host: User = Relationship()
|
||||
playlist: list[Playlist] = Relationship(
|
||||
sa_relationship_kwargs={
|
||||
"lazy": "joined",
|
||||
"cascade": "all, delete-orphan",
|
||||
"overlaps": "room",
|
||||
}
|
||||
)
|
||||
# playlist_item_attempts: list["ItemAttemptsCount"] = Relationship(
|
||||
# sa_relationship_kwargs={
|
||||
# "lazy": "joined",
|
||||
# "cascade": "all, delete-orphan",
|
||||
# "primaryjoin": "ItemAttemptsCount.room_id == Room.id",
|
||||
# }
|
||||
# )
|
||||
|
||||
|
||||
class RoomResp(RoomBase):
|
||||
id: int
|
||||
password: str | None = None
|
||||
host: UserResp | None = None
|
||||
playlist: list[PlaylistResp] = []
|
||||
playlist_item_stats: RoomPlaylistItemStats | None = None
|
||||
difficulty_range: RoomDifficultyRange | None = None
|
||||
current_playlist_item: PlaylistResp | None = None
|
||||
playlist_item_attempts: list[ItemAttemptsCount] = []
|
||||
|
||||
@classmethod
|
||||
async def from_db(cls, room: Room) -> "RoomResp":
|
||||
resp = cls.model_validate(room.model_dump())
|
||||
|
||||
stats = RoomPlaylistItemStats(count_active=0, count_total=0)
|
||||
difficulty_range = RoomDifficultyRange(
|
||||
min=0,
|
||||
max=0,
|
||||
)
|
||||
rulesets = set()
|
||||
for playlist in room.playlist:
|
||||
stats.count_total += 1
|
||||
if not playlist.expired:
|
||||
stats.count_active += 1
|
||||
rulesets.add(playlist.ruleset_id)
|
||||
difficulty_range.min = min(
|
||||
difficulty_range.min, playlist.beatmap.difficulty_rating
|
||||
)
|
||||
difficulty_range.max = max(
|
||||
difficulty_range.max, playlist.beatmap.difficulty_rating
|
||||
)
|
||||
resp.playlist.append(await PlaylistResp.from_db(playlist))
|
||||
stats.ruleset_ids = list(rulesets)
|
||||
resp.playlist_item_stats = stats
|
||||
resp.difficulty_range = difficulty_range
|
||||
resp.current_playlist_item = resp.playlist[-1] if resp.playlist else None
|
||||
# resp.playlist_item_attempts = room.playlist_item_attempts
|
||||
|
||||
return resp
|
||||
|
||||
@classmethod
|
||||
async def from_hub(cls, server_room: ServerMultiplayerRoom) -> "RoomResp":
|
||||
room = server_room.room
|
||||
resp = cls(
|
||||
id=room.room_id,
|
||||
name=room.settings.name,
|
||||
type=room.settings.match_type,
|
||||
queue_mode=room.settings.queue_mode,
|
||||
auto_skip=room.settings.auto_skip,
|
||||
auto_start_duration=room.settings.auto_start_duration,
|
||||
status=server_room.status,
|
||||
category=server_room.category,
|
||||
# duration = room.settings.duration,
|
||||
starts_at=server_room.start_at,
|
||||
participant_count=len(room.users),
|
||||
)
|
||||
return resp
|
||||
|
||||
Reference in New Issue
Block a user