feat(multiplayer): supoort manage user (kick, transfer host, leave)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
from app.database import Room
|
from app.database import Room
|
||||||
@@ -33,7 +33,8 @@ from app.models.score import GameMode
|
|||||||
|
|
||||||
from .hub import Client, Hub
|
from .hub import Client, Hub
|
||||||
|
|
||||||
from sqlmodel import select
|
from sqlalchemy import update
|
||||||
|
from sqlmodel import col, select
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
GAMEPLAY_LOAD_TIMEOUT = 30
|
GAMEPLAY_LOAD_TIMEOUT = 30
|
||||||
@@ -627,3 +628,114 @@ class MultiplayerHub(Hub[MultiplayerClientState]):
|
|||||||
"MatchEvent",
|
"MatchEvent",
|
||||||
event,
|
event,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def make_user_leave(
|
||||||
|
self,
|
||||||
|
client: Client,
|
||||||
|
room: ServerMultiplayerRoom,
|
||||||
|
user: MultiplayerRoomUser,
|
||||||
|
kicked: bool = False,
|
||||||
|
):
|
||||||
|
self.remove_from_group(client, self.group_id(room.room.room_id))
|
||||||
|
room.room.users.remove(user)
|
||||||
|
|
||||||
|
if len(room.room.users) == 0:
|
||||||
|
await self.end_room(room)
|
||||||
|
await self.update_room_state(room)
|
||||||
|
if 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:
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
target_store = self.state.get(user.user_id)
|
||||||
|
if target_store:
|
||||||
|
target_store.room_id = 0
|
||||||
|
|
||||||
|
async def end_room(self, room: ServerMultiplayerRoom):
|
||||||
|
assert room.room.host
|
||||||
|
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,
|
||||||
|
ended_at=datetime.now(UTC),
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
del self.rooms[room.room.room_id]
|
||||||
|
|
||||||
|
async def LeaveRoom(self, client: Client):
|
||||||
|
store = self.get_or_create_state(client)
|
||||||
|
if store.room_id == 0:
|
||||||
|
return
|
||||||
|
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")
|
||||||
|
|
||||||
|
await self.make_user_leave(client, server_room, user)
|
||||||
|
|
||||||
|
async def KickUser(self, client: Client, user_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
|
||||||
|
|
||||||
|
if room.host is None or room.host.user_id != client.user_id:
|
||||||
|
raise InvokeException("You are not the host of this room")
|
||||||
|
|
||||||
|
user = next((u for u in room.users if u.user_id == user_id), None)
|
||||||
|
if user is None:
|
||||||
|
raise InvokeException("User not found in this room")
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
async def set_host(self, room: ServerMultiplayerRoom, user: MultiplayerRoomUser):
|
||||||
|
room.room.host = user
|
||||||
|
await self.broadcast_group_call(
|
||||||
|
self.group_id(room.room.room_id),
|
||||||
|
"HostChanged",
|
||||||
|
user.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def TransferHost(self, client: Client, user_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
|
||||||
|
|
||||||
|
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)
|
||||||
|
if new_host is None:
|
||||||
|
raise InvokeException("User not found in this room")
|
||||||
|
await self.set_host(server_room, new_host)
|
||||||
|
|||||||
Reference in New Issue
Block a user