feat(multiplayer): support add/edit/remove playlist item
This commit is contained in:
@@ -16,7 +16,10 @@ from sqlmodel import (
|
|||||||
ForeignKey,
|
ForeignKey,
|
||||||
Relationship,
|
Relationship,
|
||||||
SQLModel,
|
SQLModel,
|
||||||
|
func,
|
||||||
|
select,
|
||||||
)
|
)
|
||||||
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .room import Room
|
from .room import Room
|
||||||
@@ -59,9 +62,20 @@ class Playlist(PlaylistBase, table=True):
|
|||||||
room: "Room" = Relationship()
|
room: "Room" = Relationship()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def from_hub(cls, playlist: PlaylistItem, room_id: int) -> "Playlist":
|
async def get_next_id_for_room(cls, room_id: int, session: AsyncSession) -> int:
|
||||||
|
stmt = select(func.coalesce(func.max(cls.id), -1) + 1).where(
|
||||||
|
cls.room_id == room_id
|
||||||
|
)
|
||||||
|
result = await session.exec(stmt)
|
||||||
|
return result.one()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def from_hub(
|
||||||
|
cls, playlist: PlaylistItem, room_id: int, session: AsyncSession
|
||||||
|
) -> "Playlist":
|
||||||
|
next_id = await cls.get_next_id_for_room(room_id, session=session)
|
||||||
return cls(
|
return cls(
|
||||||
id=playlist.id,
|
id=next_id,
|
||||||
owner_id=playlist.owner_id,
|
owner_id=playlist.owner_id,
|
||||||
ruleset_id=playlist.ruleset_id,
|
ruleset_id=playlist.ruleset_id,
|
||||||
beatmap_id=playlist.beatmap_id,
|
beatmap_id=playlist.beatmap_id,
|
||||||
@@ -74,6 +88,50 @@ class Playlist(PlaylistBase, table=True):
|
|||||||
room_id=room_id,
|
room_id=room_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def update(cls, playlist: PlaylistItem, room_id: int, session: AsyncSession):
|
||||||
|
db_playlist = await session.exec(
|
||||||
|
select(cls).where(cls.id == playlist.id, cls.room_id == room_id)
|
||||||
|
)
|
||||||
|
db_playlist = db_playlist.first()
|
||||||
|
if db_playlist is None:
|
||||||
|
raise ValueError("Playlist item not found")
|
||||||
|
db_playlist.owner_id = playlist.owner_id
|
||||||
|
db_playlist.ruleset_id = playlist.ruleset_id
|
||||||
|
db_playlist.beatmap_id = playlist.beatmap_id
|
||||||
|
db_playlist.required_mods = [
|
||||||
|
msgpack_to_apimod(mod) for mod in playlist.required_mods
|
||||||
|
]
|
||||||
|
db_playlist.allowed_mods = [
|
||||||
|
msgpack_to_apimod(mod) for mod in playlist.allowed_mods
|
||||||
|
]
|
||||||
|
db_playlist.expired = playlist.expired
|
||||||
|
db_playlist.playlist_order = playlist.order
|
||||||
|
db_playlist.played_at = playlist.played_at
|
||||||
|
db_playlist.freestyle = playlist.freestyle
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def add_to_db(
|
||||||
|
cls, playlist: PlaylistItem, room_id: int, session: AsyncSession
|
||||||
|
):
|
||||||
|
db_playlist = await cls.from_hub(playlist, room_id, session)
|
||||||
|
session.add(db_playlist)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(db_playlist)
|
||||||
|
playlist.id = db_playlist.id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def delete_item(cls, item_id: int, room_id: int, session: AsyncSession):
|
||||||
|
db_playlist = await session.exec(
|
||||||
|
select(cls).where(cls.id == item_id, cls.room_id == room_id)
|
||||||
|
)
|
||||||
|
db_playlist = db_playlist.first()
|
||||||
|
if db_playlist is None:
|
||||||
|
raise ValueError("Playlist item not found")
|
||||||
|
await session.delete(db_playlist)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
|
||||||
class PlaylistResp(PlaylistBase):
|
class PlaylistResp(PlaylistBase):
|
||||||
beatmap: BeatmapResp | None = None
|
beatmap: BeatmapResp | None = None
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
import datetime
|
import datetime
|
||||||
from typing import Annotated, Any, Literal
|
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
||||||
|
|
||||||
|
from app.database.beatmap import Beatmap
|
||||||
|
from app.dependencies.database import engine
|
||||||
|
from app.exception import InvokeException
|
||||||
|
|
||||||
from .room import (
|
from .room import (
|
||||||
DownloadState,
|
DownloadState,
|
||||||
@@ -21,7 +26,14 @@ from .signalr import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from msgpack_lazer_api import APIMod
|
from msgpack_lazer_api import APIMod
|
||||||
from pydantic import BaseModel, Field, field_serializer, field_validator
|
from pydantic import Field, field_serializer, field_validator
|
||||||
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from app.signalr.hub import MultiplayerHub
|
||||||
|
|
||||||
|
HOST_LIMIT = 50
|
||||||
|
PER_USER_LIMIT = 3
|
||||||
|
|
||||||
|
|
||||||
class MultiplayerClientState(UserState):
|
class MultiplayerClientState(UserState):
|
||||||
@@ -161,8 +173,246 @@ class MultiplayerRoom(MessagePackArrayModel):
|
|||||||
return msgpack_union_dump(v)
|
return msgpack_union_dump(v)
|
||||||
|
|
||||||
|
|
||||||
class ServerMultiplayerRoom(BaseModel):
|
class MultiplayerQueue:
|
||||||
|
def __init__(self, room: "ServerMultiplayerRoom", hub: "MultiplayerHub"):
|
||||||
|
self.server_room = room
|
||||||
|
self.hub = hub
|
||||||
|
self.current_index = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def upcoming_items(self):
|
||||||
|
return sorted(
|
||||||
|
(item for item in self.room.playlist if not item.expired),
|
||||||
|
key=lambda i: i.order,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def room(self):
|
||||||
|
return self.server_room.room
|
||||||
|
|
||||||
|
async def update_order(self):
|
||||||
|
from app.database import Playlist
|
||||||
|
|
||||||
|
match self.room.settings.queue_mode:
|
||||||
|
case QueueMode.ALL_PLAYERS_ROUND_ROBIN:
|
||||||
|
ordered_active_items = []
|
||||||
|
|
||||||
|
is_first_set = True
|
||||||
|
first_set_order_by_user_id = {}
|
||||||
|
|
||||||
|
active_items = [item for item in self.room.playlist if not item.expired]
|
||||||
|
active_items.sort(key=lambda x: x.id)
|
||||||
|
|
||||||
|
user_item_groups = {}
|
||||||
|
for item in active_items:
|
||||||
|
if item.owner_id not in user_item_groups:
|
||||||
|
user_item_groups[item.owner_id] = []
|
||||||
|
user_item_groups[item.owner_id].append(item)
|
||||||
|
|
||||||
|
max_items = max(
|
||||||
|
(len(items) for items in user_item_groups.values()), default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(max_items):
|
||||||
|
current_set = []
|
||||||
|
for user_id, items in user_item_groups.items():
|
||||||
|
if i < len(items):
|
||||||
|
current_set.append(items[i])
|
||||||
|
|
||||||
|
if is_first_set:
|
||||||
|
current_set.sort(key=lambda item: (item.order, item.id))
|
||||||
|
ordered_active_items.extend(current_set)
|
||||||
|
first_set_order_by_user_id = {
|
||||||
|
item.owner_id: idx
|
||||||
|
for idx, item in enumerate(ordered_active_items)
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
current_set.sort(
|
||||||
|
key=lambda item: first_set_order_by_user_id.get(
|
||||||
|
item.owner_id, 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ordered_active_items.extend(current_set)
|
||||||
|
|
||||||
|
is_first_set = False
|
||||||
|
|
||||||
|
for idx, item in enumerate(ordered_active_items):
|
||||||
|
item.order = idx
|
||||||
|
case _:
|
||||||
|
ordered_active_items = sorted(
|
||||||
|
(item for item in self.room.playlist if not item.expired),
|
||||||
|
key=lambda x: x.id,
|
||||||
|
)
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
for idx, item in enumerate(ordered_active_items):
|
||||||
|
if item.order == idx:
|
||||||
|
continue
|
||||||
|
item.order = idx
|
||||||
|
await Playlist.update(item, self.room.room_id, session)
|
||||||
|
await self.hub.playlist_changed(
|
||||||
|
self.server_room, item, beatmap_changed=False
|
||||||
|
)
|
||||||
|
|
||||||
|
async def update_current_item(self):
|
||||||
|
upcoming_items = self.upcoming_items
|
||||||
|
next_item = (
|
||||||
|
upcoming_items[0]
|
||||||
|
if upcoming_items
|
||||||
|
else max(
|
||||||
|
self.room.playlist,
|
||||||
|
key=lambda i: i.played_at or datetime.datetime.min,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.current_index = self.room.playlist.index(next_item)
|
||||||
|
last_id = self.room.settings.playlist_item_id
|
||||||
|
self.room.settings.playlist_item_id = next_item.id
|
||||||
|
if last_id != next_item.id:
|
||||||
|
await self.hub.setting_changed(self.server_room, True)
|
||||||
|
|
||||||
|
async def add_item(self, item: PlaylistItem, user: MultiplayerRoomUser):
|
||||||
|
from app.database import Playlist
|
||||||
|
|
||||||
|
is_host = self.room.host and self.room.host.user_id == user.user_id
|
||||||
|
if self.room.settings.queue_mode == QueueMode.HOST_ONLY and not is_host:
|
||||||
|
raise InvokeException("You are not the host")
|
||||||
|
|
||||||
|
limit = HOST_LIMIT if is_host else PER_USER_LIMIT
|
||||||
|
if (
|
||||||
|
len(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda x: x.owner_id == user.user_id,
|
||||||
|
self.room.playlist,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
>= limit
|
||||||
|
):
|
||||||
|
raise InvokeException(f"You can only have {limit} items in the queue")
|
||||||
|
|
||||||
|
if item.freestyle and len(item.allowed_mods) > 0:
|
||||||
|
raise InvokeException("Freestyle items cannot have allowed mods")
|
||||||
|
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
async with session:
|
||||||
|
beatmap = await session.get(Beatmap, item.beatmap_id)
|
||||||
|
if beatmap is None:
|
||||||
|
raise InvokeException("Beatmap not found")
|
||||||
|
if item.checksum != beatmap.checksum:
|
||||||
|
raise InvokeException("Checksum mismatch")
|
||||||
|
# TODO: mods validation
|
||||||
|
item.owner_id = user.user_id
|
||||||
|
item.star = float(
|
||||||
|
beatmap.difficulty_rating
|
||||||
|
) # FIXME: beatmap use decimal
|
||||||
|
await Playlist.add_to_db(item, self.room.room_id, session)
|
||||||
|
self.room.playlist.append(item)
|
||||||
|
await self.hub.playlist_added(self.server_room, item)
|
||||||
|
await self.update_order()
|
||||||
|
await self.update_current_item()
|
||||||
|
|
||||||
|
async def edit_item(self, item: PlaylistItem, user: MultiplayerRoomUser):
|
||||||
|
from app.database import Playlist
|
||||||
|
|
||||||
|
if item.freestyle and len(item.allowed_mods) > 0:
|
||||||
|
raise InvokeException("Freestyle items cannot have allowed mods")
|
||||||
|
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
async with session:
|
||||||
|
beatmap = await session.get(Beatmap, item.beatmap_id)
|
||||||
|
if beatmap is None:
|
||||||
|
raise InvokeException("Beatmap not found")
|
||||||
|
if item.checksum != beatmap.checksum:
|
||||||
|
raise InvokeException("Checksum mismatch")
|
||||||
|
|
||||||
|
existing_item = next(
|
||||||
|
(i for i in self.room.playlist if i.id == item.id), None
|
||||||
|
)
|
||||||
|
if existing_item is None:
|
||||||
|
raise InvokeException(
|
||||||
|
"Attempted to change an item that doesn't exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_item.owner_id != user.user_id and self.room.host != user:
|
||||||
|
raise InvokeException(
|
||||||
|
"Attempted to change an item which is not owned by the user"
|
||||||
|
)
|
||||||
|
|
||||||
|
if existing_item.expired:
|
||||||
|
raise InvokeException(
|
||||||
|
"Attempted to change an item which has already been played"
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: mods validation
|
||||||
|
item.owner_id = user.user_id
|
||||||
|
item.star = float(beatmap.difficulty_rating)
|
||||||
|
item.order = existing_item.order
|
||||||
|
|
||||||
|
await Playlist.update(item, self.room.room_id, session)
|
||||||
|
|
||||||
|
# Update item in playlist
|
||||||
|
for idx, playlist_item in enumerate(self.room.playlist):
|
||||||
|
if playlist_item.id == item.id:
|
||||||
|
self.room.playlist[idx] = item
|
||||||
|
break
|
||||||
|
|
||||||
|
await self.hub.playlist_changed(
|
||||||
|
self.server_room,
|
||||||
|
item,
|
||||||
|
beatmap_changed=item.checksum != existing_item.checksum,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def remove_item(self, playlist_item_id: int, user: MultiplayerRoomUser):
|
||||||
|
from app.database import Playlist
|
||||||
|
|
||||||
|
item = next(
|
||||||
|
(i for i in self.room.playlist if i.id == playlist_item_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if item is None:
|
||||||
|
raise InvokeException("Item does not exist in the room")
|
||||||
|
|
||||||
|
# Check if it's the only item and current item
|
||||||
|
if item == self.current_item:
|
||||||
|
upcoming_items = [i for i in self.room.playlist if not i.expired]
|
||||||
|
if len(upcoming_items) == 1:
|
||||||
|
raise InvokeException("The only item in the room cannot be removed")
|
||||||
|
|
||||||
|
if item.owner_id != user.user_id and self.room.host != user:
|
||||||
|
raise InvokeException(
|
||||||
|
"Attempted to remove an item which is not owned by the user"
|
||||||
|
)
|
||||||
|
|
||||||
|
if item.expired:
|
||||||
|
raise InvokeException(
|
||||||
|
"Attempted to remove an item which has already been played"
|
||||||
|
)
|
||||||
|
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
await Playlist.delete_item(item.id, self.room.room_id, session)
|
||||||
|
|
||||||
|
self.room.playlist.remove(item)
|
||||||
|
self.current_index = self.room.playlist.index(self.upcoming_items[0])
|
||||||
|
|
||||||
|
await self.update_order()
|
||||||
|
await self.update_current_item()
|
||||||
|
await self.hub.playlist_removed(self.server_room, item.id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_item(self):
|
||||||
|
"""Get the current playlist item"""
|
||||||
|
current_id = self.room.settings.playlist_item_id
|
||||||
|
return next(
|
||||||
|
(item for item in self.room.playlist if item.id == current_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ServerMultiplayerRoom:
|
||||||
room: MultiplayerRoom
|
room: MultiplayerRoom
|
||||||
category: RoomCategory
|
category: RoomCategory
|
||||||
status: RoomStatus
|
status: RoomStatus
|
||||||
start_at: datetime.datetime
|
start_at: datetime.datetime
|
||||||
|
queue: MultiplayerQueue | None = None
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import time
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
|
from app.exception import InvokeException
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.models.signalr import UserState
|
from app.models.signalr import UserState
|
||||||
from app.signalr.exception import InvokeException
|
|
||||||
from app.signalr.packet import (
|
from app.signalr.packet import (
|
||||||
ClosePacket,
|
ClosePacket,
|
||||||
CompletionPacket,
|
CompletionPacket,
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ from typing import override
|
|||||||
from app.database import Room
|
from app.database import Room
|
||||||
from app.database.playlists import Playlist
|
from app.database.playlists import Playlist
|
||||||
from app.dependencies.database import engine
|
from app.dependencies.database import engine
|
||||||
|
from app.exception import InvokeException
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.models.multiplayer_hub import (
|
from app.models.multiplayer_hub import (
|
||||||
|
BeatmapAvailability,
|
||||||
MultiplayerClientState,
|
MultiplayerClientState,
|
||||||
|
MultiplayerQueue,
|
||||||
MultiplayerRoom,
|
MultiplayerRoom,
|
||||||
MultiplayerRoomUser,
|
MultiplayerRoomUser,
|
||||||
|
PlaylistItem,
|
||||||
ServerMultiplayerRoom,
|
ServerMultiplayerRoom,
|
||||||
)
|
)
|
||||||
from app.models.room import RoomCategory, RoomStatus
|
from app.models.room import RoomCategory, RoomStatus
|
||||||
from app.models.signalr import serialize_to_list
|
from app.models.signalr import serialize_to_list
|
||||||
from app.signalr.exception import InvokeException
|
|
||||||
|
|
||||||
from .hub import Client, Hub
|
from .hub import Client, Hub
|
||||||
|
|
||||||
@@ -40,6 +43,9 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
|||||||
|
|
||||||
async def CreateRoom(self, client: Client, room: MultiplayerRoom):
|
async def CreateRoom(self, client: Client, room: MultiplayerRoom):
|
||||||
logger.info(f"[MultiplayerHub] {client.user_id} creating room")
|
logger.info(f"[MultiplayerHub] {client.user_id} creating room")
|
||||||
|
store = self.get_or_create_state(client)
|
||||||
|
if store.room_id != 0:
|
||||||
|
raise InvokeException("You are already in a room")
|
||||||
async with AsyncSession(engine) as session:
|
async with AsyncSession(engine) as session:
|
||||||
async with session:
|
async with session:
|
||||||
db_room = Room(
|
db_room = Room(
|
||||||
@@ -55,22 +61,22 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
|||||||
session.add(db_room)
|
session.add(db_room)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(db_room)
|
await session.refresh(db_room)
|
||||||
playitem = room.playlist[0]
|
item = room.playlist[0]
|
||||||
playitem.owner_id = client.user_id
|
item.owner_id = client.user_id
|
||||||
playitem.order = 1
|
|
||||||
db_playlist = await Playlist.from_hub(playitem, db_room.id)
|
|
||||||
session.add(db_playlist)
|
|
||||||
room.room_id = db_room.id
|
room.room_id = db_room.id
|
||||||
starts_at = db_room.starts_at
|
starts_at = db_room.starts_at
|
||||||
await session.commit()
|
await Playlist.add_to_db(item, db_room.id, session)
|
||||||
await session.refresh(db_playlist)
|
|
||||||
# room.playlist.append()
|
|
||||||
server_room = ServerMultiplayerRoom(
|
server_room = ServerMultiplayerRoom(
|
||||||
room=room,
|
room=room,
|
||||||
category=RoomCategory.NORMAL,
|
category=RoomCategory.NORMAL,
|
||||||
status=RoomStatus.IDLE,
|
status=RoomStatus.IDLE,
|
||||||
start_at=starts_at,
|
start_at=starts_at,
|
||||||
)
|
)
|
||||||
|
queue = MultiplayerQueue(
|
||||||
|
room=server_room,
|
||||||
|
hub=self,
|
||||||
|
)
|
||||||
|
server_room.queue = queue
|
||||||
self.rooms[room.room_id] = server_room
|
self.rooms[room.room_id] = server_room
|
||||||
return await self.JoinRoomWithPassword(
|
return await self.JoinRoomWithPassword(
|
||||||
client, room.room_id, room.settings.password
|
client, room.room_id, room.settings.password
|
||||||
@@ -101,3 +107,115 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
|||||||
room.users.append(user)
|
room.users.append(user)
|
||||||
self.add_to_group(client, self.group_id(room_id))
|
self.add_to_group(client, self.group_id(room_id))
|
||||||
return serialize_to_list(room)
|
return serialize_to_list(room)
|
||||||
|
|
||||||
|
async def ChangeBeatmapAvailability(
|
||||||
|
self, client: Client, beatmap_availability: BeatmapAvailability
|
||||||
|
):
|
||||||
|
store = self.get_or_create_state(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
|
||||||
|
user = next((u for u in room.users if u.user_id == client.user_id), None)
|
||||||
|
if user is None:
|
||||||
|
raise InvokeException("You are not in this room")
|
||||||
|
|
||||||
|
availability = user.availability
|
||||||
|
if (
|
||||||
|
availability.state == beatmap_availability.state
|
||||||
|
and availability.progress == beatmap_availability.progress
|
||||||
|
):
|
||||||
|
return
|
||||||
|
user.availability = availability
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(store.room_id),
|
||||||
|
"UserBeatmapAvailabilityChanged",
|
||||||
|
user.user_id,
|
||||||
|
serialize_to_list(beatmap_availability),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def AddPlaylistItem(self, client: Client, item: PlaylistItem):
|
||||||
|
store = self.get_or_create_state(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
|
||||||
|
assert server_room.queue
|
||||||
|
user = next((u for u in room.users if u.user_id == client.user_id), None)
|
||||||
|
if user is None:
|
||||||
|
raise InvokeException("You are not in this room")
|
||||||
|
|
||||||
|
await server_room.queue.add_item(
|
||||||
|
item,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def EditPlaylistItem(self, client: Client, item: PlaylistItem):
|
||||||
|
store = self.get_or_create_state(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
|
||||||
|
assert server_room.queue
|
||||||
|
user = next((u for u in room.users if u.user_id == client.user_id), None)
|
||||||
|
if user is None:
|
||||||
|
raise InvokeException("You are not in this room")
|
||||||
|
|
||||||
|
await server_room.queue.edit_item(
|
||||||
|
item,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def RemovePlaylistItem(self, client: Client, item_id: int):
|
||||||
|
store = self.get_or_create_state(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
|
||||||
|
assert server_room.queue
|
||||||
|
user = next((u for u in room.users if u.user_id == client.user_id), None)
|
||||||
|
if user is None:
|
||||||
|
raise InvokeException("You are not in this room")
|
||||||
|
|
||||||
|
await server_room.queue.remove_item(
|
||||||
|
item_id,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def setting_changed(self, room: ServerMultiplayerRoom, beatmap_changed: bool):
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(room.room.room_id),
|
||||||
|
"SettingsChanged",
|
||||||
|
serialize_to_list(room.room.settings),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def playlist_added(self, room: ServerMultiplayerRoom, item: PlaylistItem):
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(room.room.room_id),
|
||||||
|
"PlaylistItemAdded",
|
||||||
|
serialize_to_list(item),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def playlist_removed(self, room: ServerMultiplayerRoom, item_id: int):
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(room.room.room_id),
|
||||||
|
"PlaylistItemRemoved",
|
||||||
|
item_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def playlist_changed(
|
||||||
|
self, room: ServerMultiplayerRoom, item: PlaylistItem, beatmap_changed: bool
|
||||||
|
):
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(room.room.room_id),
|
||||||
|
"PlaylistItemChanged",
|
||||||
|
serialize_to_list(item),
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user