diff --git a/src/server/main.py b/src/server/main.py index 1a085d1..3607098 100644 --- a/src/server/main.py +++ b/src/server/main.py @@ -32,7 +32,7 @@ import random game_instance = { "world": None, "sim": None, - "is_paused": False # 新增暂停标记 + "is_paused": True # 默认启动为暂停状态,等待前端连接唤醒 } # 简易的命令行参数检查 (不使用 argparse 以避免冲突和时序问题) @@ -45,9 +45,24 @@ class ConnectionManager: async def connect(self, websocket: WebSocket): await websocket.accept() self.active_connections.append(websocket) + + # 当第一个客户端连接时,自动恢复游戏 + if len(self.active_connections) == 1: + self._set_pause_state(False, "检测到客户端连接,自动恢复游戏运行。") def disconnect(self, websocket: WebSocket): - self.active_connections.remove(websocket) + if websocket in self.active_connections: + self.active_connections.remove(websocket) + + # 当最后一个客户端断开时,自动暂停游戏 + if len(self.active_connections) == 0: + self._set_pause_state(True, "所有客户端已断开,自动暂停游戏以节省资源。") + + def _set_pause_state(self, should_pause: bool, log_msg: str): + """辅助方法:切换暂停状态并打印日志""" + if game_instance.get("is_paused") != should_pause: + game_instance["is_paused"] = should_pause + print(f"[Auto-Control] {log_msg}") async def broadcast(self, message: dict): import json diff --git a/web/src/stores/game.ts b/web/src/stores/game.ts index 6c77440..8c14dc5 100644 --- a/web/src/stores/game.ts +++ b/web/src/stores/game.ts @@ -68,6 +68,14 @@ export const useGameStore = defineStore('game', () => { if (Array.isArray(payload.avatars)) { mergeAvatars(payload.avatars) } + + // Tick means time passed, so cache is stale + hoverCache.clear() + + // If panel is open, silently refresh content to show latest status + if (selectedTarget.value) { + fetchHoverInfo(selectedTarget.value, { force: true, silent: true }) + } } function mergeAvatars(list: Avatar[]) { @@ -117,23 +125,29 @@ export const useGameStore = defineStore('game', () => { } } - async function fetchHoverInfo(target: HoverTarget, forceRefresh = false) { + async function fetchHoverInfo(target: HoverTarget, options: { force?: boolean, silent?: boolean } = {}) { + const { force = false, silent = false } = options const key = cacheKey(target) - if (!forceRefresh) { + + if (!force) { const cached = hoverCache.get(key) if (cached) { if (selectedTarget.value && cacheKey(selectedTarget.value) === key) { hoverInfo.value = cached } - infoLoading.value = false - infoError.value = null + if (!silent) { + infoLoading.value = false + infoError.value = null + } return } } - infoLoading.value = true - infoError.value = null - if (!forceRefresh) hoverInfo.value = [] + if (!silent) { + infoLoading.value = true + infoError.value = null + if (!force) hoverInfo.value = [] + } try { const data = await gameApi.getHoverInfo(target) @@ -144,12 +158,16 @@ export const useGameStore = defineStore('game', () => { } } catch (error) { if (selectedTarget.value && cacheKey(selectedTarget.value) === key) { - infoError.value = error instanceof Error ? error.message : String(error) - hoverInfo.value = [] + if (!silent) { + infoError.value = error instanceof Error ? error.message : String(error) + hoverInfo.value = [] + } } } finally { if (selectedTarget.value && cacheKey(selectedTarget.value) === key) { - infoLoading.value = false + if (!silent) { + infoLoading.value = false + } } } } @@ -158,7 +176,7 @@ export const useGameStore = defineStore('game', () => { await gameApi.setLongTermObjective(avatarId, content) // 成功后刷新 info panel if (selectedTarget.value && selectedTarget.value.id === avatarId && selectedTarget.value.type === 'avatar') { - await fetchHoverInfo(selectedTarget.value, true) + await fetchHoverInfo(selectedTarget.value, { force: true }) } } @@ -166,7 +184,7 @@ export const useGameStore = defineStore('game', () => { await gameApi.clearLongTermObjective(avatarId) // 成功后刷新 info panel if (selectedTarget.value && selectedTarget.value.id === avatarId && selectedTarget.value.type === 'avatar') { - await fetchHoverInfo(selectedTarget.value, true) + await fetchHoverInfo(selectedTarget.value, { force: true }) } }