feat(multiplayer): supoort manage user (kick, transfer host, leave)

This commit is contained in:
MingxuanGame
2025-08-03 13:50:59 +00:00
parent 2600fa499f
commit c2579e86eb

View File

@@ -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)