252 lines
11 KiB
Python
252 lines
11 KiB
Python
"""
|
|
SpectatorHub增强补丁
|
|
与MultiplayerHub集成以支持多人游戏观战
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
import asyncio
|
|
import json
|
|
from datetime import datetime, UTC
|
|
from typing import Dict, Optional
|
|
from collections import defaultdict
|
|
|
|
from app.dependencies.database import get_redis
|
|
from app.log import logger
|
|
|
|
|
|
class SpectatorMultiplayerIntegration:
|
|
"""SpectatorHub的多人游戏集成扩展"""
|
|
|
|
def __init__(self, spectator_hub_instance):
|
|
self.hub = spectator_hub_instance
|
|
self.redis = None
|
|
self.multiplayer_subscribers = defaultdict(set) # room_id -> set of connection_ids
|
|
self.leaderboard_cache = {} # room_id -> leaderboard data
|
|
|
|
async def initialize(self):
|
|
"""初始化多人游戏集成"""
|
|
self.redis = get_redis()
|
|
|
|
# 启动Redis订阅任务
|
|
asyncio.create_task(self._subscribe_to_multiplayer_events())
|
|
asyncio.create_task(self._subscribe_to_leaderboard_updates())
|
|
asyncio.create_task(self._subscribe_to_spectator_sync())
|
|
|
|
async def _subscribe_to_multiplayer_events(self):
|
|
"""订阅多人游戏事件"""
|
|
try:
|
|
pubsub = self.redis.pubsub()
|
|
await pubsub.psubscribe("multiplayer_spectator:*")
|
|
|
|
async for message in pubsub.listen():
|
|
if message['type'] == 'pmessage':
|
|
try:
|
|
data = json.loads(message['data'])
|
|
await self._handle_multiplayer_event(message['channel'], data)
|
|
except Exception as e:
|
|
logger.error(f"Error processing multiplayer event: {e}")
|
|
except Exception as e:
|
|
logger.error(f"Error in multiplayer events subscription: {e}")
|
|
|
|
async def _subscribe_to_leaderboard_updates(self):
|
|
"""订阅排行榜更新"""
|
|
try:
|
|
pubsub = self.redis.pubsub()
|
|
await pubsub.psubscribe("leaderboard_update:*")
|
|
|
|
async for message in pubsub.listen():
|
|
if message['type'] == 'pmessage':
|
|
try:
|
|
data = json.loads(message['data'])
|
|
user_id = message['channel'].split(':')[-1]
|
|
await self._send_leaderboard_to_user(int(user_id), data['leaderboard'])
|
|
except Exception as e:
|
|
logger.error(f"Error processing leaderboard update: {e}")
|
|
except Exception as e:
|
|
logger.error(f"Error in leaderboard updates subscription: {e}")
|
|
|
|
async def _subscribe_to_spectator_sync(self):
|
|
"""订阅观战同步事件"""
|
|
try:
|
|
pubsub = self.redis.pubsub()
|
|
await pubsub.psubscribe("spectator_sync:*")
|
|
|
|
async for message in pubsub.listen():
|
|
if message['type'] == 'pmessage':
|
|
try:
|
|
data = json.loads(message['data'])
|
|
room_id = message['channel'].split(':')[-1]
|
|
await self._handle_spectator_sync(int(room_id), data)
|
|
except Exception as e:
|
|
logger.error(f"Error processing spectator sync: {e}")
|
|
except Exception as e:
|
|
logger.error(f"Error in spectator sync subscription: {e}")
|
|
|
|
async def _handle_multiplayer_event(self, channel: str, data: Dict):
|
|
"""处理多人游戏事件"""
|
|
room_id = data.get('room_id')
|
|
event_type = data.get('event_type')
|
|
event_data = data.get('data', {})
|
|
|
|
if not room_id or not event_type:
|
|
return
|
|
|
|
if event_type == "gameplay_started":
|
|
await self._handle_gameplay_started(room_id, event_data)
|
|
elif event_type == "gameplay_ended":
|
|
await self._handle_gameplay_ended(room_id, event_data)
|
|
elif event_type == "user_state_changed":
|
|
await self._handle_user_state_changed(room_id, event_data)
|
|
|
|
async def _handle_gameplay_started(self, room_id: int, game_data: Dict):
|
|
"""处理游戏开始事件"""
|
|
logger.info(f"[SpectatorHub] Multiplayer game started in room {room_id}")
|
|
|
|
# 通知所有观战该房间的用户
|
|
if room_id in self.multiplayer_subscribers:
|
|
for connection_id in self.multiplayer_subscribers[room_id]:
|
|
await self._send_to_connection(connection_id, "MultiplayerGameStarted", game_data)
|
|
|
|
async def _handle_gameplay_ended(self, room_id: int, results_data: Dict):
|
|
"""处理游戏结束事件"""
|
|
logger.info(f"[SpectatorHub] Multiplayer game ended in room {room_id}")
|
|
|
|
# 发送最终结果给观战者
|
|
if room_id in self.multiplayer_subscribers:
|
|
for connection_id in self.multiplayer_subscribers[room_id]:
|
|
await self._send_to_connection(connection_id, "MultiplayerGameEnded", results_data)
|
|
|
|
async def _handle_user_state_changed(self, room_id: int, state_data: Dict):
|
|
"""处理用户状态变化"""
|
|
user_id = state_data.get('user_id')
|
|
new_state = state_data.get('new_state')
|
|
|
|
if room_id in self.multiplayer_subscribers:
|
|
for connection_id in self.multiplayer_subscribers[room_id]:
|
|
await self._send_to_connection(connection_id, "MultiplayerUserStateChanged", {
|
|
'user_id': user_id,
|
|
'state': new_state
|
|
})
|
|
|
|
async def _handle_spectator_sync(self, room_id: int, sync_data: Dict):
|
|
"""处理观战同步请求"""
|
|
target_user = sync_data.get('target_user')
|
|
snapshot = sync_data.get('snapshot')
|
|
|
|
if target_user and snapshot:
|
|
# 找到目标用户的连接并发送快照
|
|
client = self.hub.get_client_by_id(str(target_user))
|
|
if client:
|
|
await self._send_to_connection(client.connection_id, "MultiplayerSnapshot", snapshot)
|
|
|
|
async def _send_leaderboard_to_user(self, user_id: int, leaderboard: list):
|
|
"""发送排行榜给指定用户"""
|
|
client = self.hub.get_client_by_id(str(user_id))
|
|
if client:
|
|
await self._send_to_connection(client.connection_id, "MultiplayerLeaderboard", leaderboard)
|
|
|
|
async def _send_to_connection(self, connection_id: str, method: str, data):
|
|
"""发送数据到指定连接"""
|
|
try:
|
|
await self.hub.broadcast_call(connection_id, method, data)
|
|
except Exception as e:
|
|
logger.error(f"Error sending {method} to connection {connection_id}: {e}")
|
|
|
|
async def subscribe_to_multiplayer_room(self, connection_id: str, room_id: int):
|
|
"""订阅多人游戏房间的观战"""
|
|
self.multiplayer_subscribers[room_id].add(connection_id)
|
|
|
|
# 通知MultiplayerHub有新的观战者
|
|
await self.redis.publish(f"multiplayer_spectator:room:{room_id}", json.dumps({
|
|
'event_type': 'spectator_joined',
|
|
'data': {'user_id': self._get_user_id_from_connection(connection_id)},
|
|
'room_id': room_id,
|
|
'timestamp': datetime.now(UTC).isoformat()
|
|
}))
|
|
|
|
logger.info(f"[SpectatorHub] Connection {connection_id} subscribed to multiplayer room {room_id}")
|
|
|
|
async def unsubscribe_from_multiplayer_room(self, connection_id: str, room_id: int):
|
|
"""取消订阅多人游戏房间"""
|
|
if room_id in self.multiplayer_subscribers:
|
|
self.multiplayer_subscribers[room_id].discard(connection_id)
|
|
if not self.multiplayer_subscribers[room_id]:
|
|
del self.multiplayer_subscribers[room_id]
|
|
|
|
logger.info(f"[SpectatorHub] Connection {connection_id} unsubscribed from multiplayer room {room_id}")
|
|
|
|
async def request_multiplayer_leaderboard(self, connection_id: str, room_id: int):
|
|
"""请求多人游戏排行榜"""
|
|
user_id = self._get_user_id_from_connection(connection_id)
|
|
if user_id:
|
|
await self.redis.publish(f"multiplayer_spectator:room:{room_id}", json.dumps({
|
|
'event_type': 'request_leaderboard',
|
|
'data': {'user_id': user_id},
|
|
'room_id': room_id,
|
|
'timestamp': datetime.now(UTC).isoformat()
|
|
}))
|
|
|
|
def _get_user_id_from_connection(self, connection_id: str) -> Optional[int]:
|
|
"""从连接ID获取用户ID"""
|
|
# 这需要根据实际的SpectatorHub实现来调整
|
|
for client in self.hub.clients.values():
|
|
if client.connection_id == connection_id:
|
|
return client.user_id
|
|
return None
|
|
|
|
|
|
# 在SpectatorHub类中添加的方法
|
|
async def init_multiplayer_integration(self):
|
|
"""初始化多人游戏集成"""
|
|
if not hasattr(self, 'multiplayer_integration'):
|
|
self.multiplayer_integration = SpectatorMultiplayerIntegration(self)
|
|
await self.multiplayer_integration.initialize()
|
|
|
|
async def WatchMultiplayerRoom(self, client, room_id: int):
|
|
"""开始观战多人游戏房间"""
|
|
try:
|
|
if not hasattr(self, 'multiplayer_integration'):
|
|
await self.init_multiplayer_integration()
|
|
|
|
await self.multiplayer_integration.subscribe_to_multiplayer_room(
|
|
client.connection_id, room_id
|
|
)
|
|
|
|
# 请求当前状态同步
|
|
await self.multiplayer_integration.request_multiplayer_leaderboard(
|
|
client.connection_id, room_id
|
|
)
|
|
|
|
return {"success": True, "message": f"Now watching multiplayer room {room_id}"}
|
|
except Exception as e:
|
|
logger.error(f"Error starting multiplayer room watch: {e}")
|
|
raise InvokeException(f"Failed to watch multiplayer room: {e}")
|
|
|
|
async def StopWatchingMultiplayerRoom(self, client, room_id: int):
|
|
"""停止观战多人游戏房间"""
|
|
try:
|
|
if hasattr(self, 'multiplayer_integration'):
|
|
await self.multiplayer_integration.unsubscribe_from_multiplayer_room(
|
|
client.connection_id, room_id
|
|
)
|
|
|
|
return {"success": True, "message": f"Stopped watching multiplayer room {room_id}"}
|
|
except Exception as e:
|
|
logger.error(f"Error stopping multiplayer room watch: {e}")
|
|
raise InvokeException(f"Failed to stop watching multiplayer room: {e}")
|
|
|
|
async def RequestMultiplayerLeaderboard(self, client, room_id: int):
|
|
"""请求多人游戏实时排行榜"""
|
|
try:
|
|
if not hasattr(self, 'multiplayer_integration'):
|
|
await self.init_multiplayer_integration()
|
|
|
|
await self.multiplayer_integration.request_multiplayer_leaderboard(
|
|
client.connection_id, room_id
|
|
)
|
|
|
|
return {"success": True, "message": "Leaderboard request sent"}
|
|
except Exception as e:
|
|
logger.error(f"Error requesting multiplayer leaderboard: {e}")
|
|
raise InvokeException(f"Failed to request leaderboard: {e}")
|