fix(web): decouple manual pause state from system pause behavior (#37)

The pause indicator was showing 'paused' while the game was still running
because isManualPaused was being modified by both user actions (clicking
pause button) and system actions (opening menu).

Changes:
- systemStore: pause()/resume() no longer modify isManualPaused, only
  togglePause() does (with optimistic update + rollback on failure)
- useGameControl: consolidate 3 overlapping watches into 1 clean watch
  that only handles menu open/close without polluting manual pause state
- App.vue: explicitly call resumeGame() API when game initializes
This commit is contained in:
Zihao Xu
2026-01-18 00:59:44 -08:00
committed by GitHub
parent 47ad330b35
commit ce64c6b048
3 changed files with 27 additions and 44 deletions

View File

@@ -69,7 +69,9 @@ watch(gameInitialized, (val) => {
showSplash.value = false
}
// 设置前端状态并恢复后端
isManualPaused.value = false
systemApi.resumeGame().catch(console.error)
openedFromSplash.value = false // 游戏开始,清除 Splash 来源标记
}
})

View File

@@ -14,50 +14,23 @@ export function useGameControl(gameInitialized: Ref<boolean>) {
const menuDefaultTab = ref<'save' | 'load' | 'create' | 'delete' | 'llm' | 'start'>('load')
const canCloseMenu = ref(true)
// 监听菜单状态和手动暂停状态,控制游戏暂停/继续
watch([showMenu, isManualPaused], ([menuVisible, manualPaused]) => {
// 只在游戏已准备好时控制暂停
// 统一的暂停控制逻辑:
// - 菜单打开时:暂停后端(不影响 isManualPaused
// - 菜单关闭时:如果没有手动暂停,恢复后端
watch(showMenu, (menuVisible) => {
if (!gameInitialized.value) return
if (menuVisible || manualPaused) {
// 如果不是因为菜单打开而暂停(即用户手动暂停),则不需额外操作,因为状态已经在 store 里了
// 但如果是菜单打开导致需要暂停,我们需要调用 pause
if (menuVisible && !manualPaused) {
systemStore.pause().catch(console.error)
} else if (!menuVisible && !manualPaused) {
// 菜单关闭且非手动暂停 -> 恢复
systemStore.resume().catch(console.error)
} else if (manualPaused) {
// 只要是手动暂停,就确保是暂停状态
systemStore.pause().catch(console.error)
}
} else {
systemStore.resume().catch(console.error)
}
})
// 优化:简化 Watch 逻辑
// 核心规则:菜单打开 OR 手动暂停 => 必须暂停
// 菜单关闭 AND 手动播放 => 恢复
watch([showMenu], ([menuVisible]) => {
if (!gameInitialized.value) return
if (menuVisible) {
// 菜单打开,暂停后端
systemStore.pause().catch(console.error)
} else {
// 关闭菜单时,如果不是手动暂停状态,则恢复
// 菜单关闭,只有在非手动暂停时才恢复
if (!isManualPaused.value) {
systemStore.resume().catch(console.error)
}
}
})
// Watch 手动暂停状态的变化,同步到后端
watch(isManualPaused, (val) => {
if (!gameInitialized.value) return
if (val) systemStore.pause().catch(console.error)
else if (!showMenu.value) systemStore.resume().catch(console.error)
})
// 快捷键处理
function handleKeydown(e: KeyboardEvent) {
if (e.key === 'Escape') {

View File

@@ -46,18 +46,27 @@ export const useSystemStore = defineStore('system', () => {
isInitialized.value = val;
}
// 切换手动暂停状态(用户点击暂停按钮时调用)
async function togglePause() {
if (isManualPaused.value) {
await resume();
const newState = !isManualPaused.value;
isManualPaused.value = newState;
try {
if (newState) {
await systemApi.pauseGame();
} else {
await pause();
await systemApi.resumeGame();
}
} catch (e) {
// API 失败时回滚状态
isManualPaused.value = !newState;
console.error(e);
}
}
// 仅调用后端 API不修改 isManualPaused用于菜单打开/关闭等系统行为)
async function pause() {
try {
await systemApi.pauseGame();
isManualPaused.value = true;
} catch (e) {
console.error(e);
}
@@ -66,7 +75,6 @@ export const useSystemStore = defineStore('system', () => {
async function resume() {
try {
await systemApi.resumeGame();
isManualPaused.value = false;
} catch (e) {
console.error(e);
}