Fix stale restore after game restart
This commit is contained in:
@@ -30,6 +30,9 @@ class GameLoopModule extends BaseModule {
|
||||
this.autoSaveQueued = false;
|
||||
this.resumeAttempted = false;
|
||||
this.lastInkState = null;
|
||||
this.clientResetGeneration = 0;
|
||||
this.restoreGeneration = 0;
|
||||
this.pendingHistoryRestoreCleanup = null;
|
||||
|
||||
// Bind methods using parent's bindMethods utility
|
||||
this.bindMethods([
|
||||
@@ -52,6 +55,17 @@ class GameLoopModule extends BaseModule {
|
||||
'resetClientPlaybackAndDisplay'
|
||||
]);
|
||||
}
|
||||
|
||||
clearPendingHistoryRestore(reason = 'cancelled') {
|
||||
if (this.pendingHistoryRestoreCleanup) {
|
||||
this.pendingHistoryRestoreCleanup(reason);
|
||||
this.pendingHistoryRestoreCleanup = null;
|
||||
return;
|
||||
}
|
||||
document.dispatchEvent(new CustomEvent('story:history-restoring', {
|
||||
detail: { active: false, reason }
|
||||
}));
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.reportProgress(100, "Game loop initialized");
|
||||
@@ -402,6 +416,12 @@ class GameLoopModule extends BaseModule {
|
||||
if (options.resetDisplay) {
|
||||
await this.resetClientPlaybackAndDisplay();
|
||||
}
|
||||
const restoreGeneration = ++this.restoreGeneration;
|
||||
const resetGeneration = this.clientResetGeneration;
|
||||
const isCurrentRestore = () =>
|
||||
restoreGeneration === this.restoreGeneration &&
|
||||
resetGeneration === this.clientResetGeneration;
|
||||
this.clearPendingHistoryRestore(`${reason}-superseded`);
|
||||
document.dispatchEvent(new CustomEvent('story:history-restoring', {
|
||||
detail: { active: true, reason }
|
||||
}));
|
||||
@@ -416,10 +436,12 @@ class GameLoopModule extends BaseModule {
|
||||
const uiController = this.getModule('ui-controller');
|
||||
if (browserSave && uiController?.displayHandler?.restoreFromHistory) {
|
||||
await uiController.displayHandler.restoreFromHistory(browserSave);
|
||||
if (!isCurrentRestore()) return;
|
||||
}
|
||||
const audioManager = this.getModule('audio-manager');
|
||||
if (browserSave?.musicState && audioManager?.restoreMusicState) {
|
||||
await audioManager.restoreMusicState(browserSave.musicState);
|
||||
if (!isCurrentRestore()) return;
|
||||
}
|
||||
const hasUnrenderedHistory = this.hasUnrenderedHistory(browserSave);
|
||||
if (hasUnrenderedHistory) {
|
||||
@@ -430,19 +452,27 @@ class GameLoopModule extends BaseModule {
|
||||
}));
|
||||
}
|
||||
if (hasUnrenderedHistory) {
|
||||
await this.queueUnrenderedHistoryBlocks(browserSave);
|
||||
await this.queueUnrenderedHistoryBlocks(browserSave, isCurrentRestore);
|
||||
if (!isCurrentRestore()) return;
|
||||
}
|
||||
if (!hasUnrenderedHistory) {
|
||||
document.dispatchEvent(new CustomEvent('story:history-restoring', {
|
||||
detail: { active: false, reason: `${reason}-complete` }
|
||||
}));
|
||||
} else {
|
||||
const clearRestoring = () => {
|
||||
const clearRestoring = (eventOrReason = 'pending-output-drained') => {
|
||||
const clearReason = typeof eventOrReason === 'string'
|
||||
? eventOrReason
|
||||
: 'pending-output-drained';
|
||||
document.dispatchEvent(new CustomEvent('story:history-restoring', {
|
||||
detail: { active: false, reason: 'pending-output-drained' }
|
||||
detail: { active: false, reason: clearReason }
|
||||
}));
|
||||
document.removeEventListener('tts:queue-empty', clearRestoring);
|
||||
if (this.pendingHistoryRestoreCleanup === clearRestoring) {
|
||||
this.pendingHistoryRestoreCleanup = null;
|
||||
}
|
||||
};
|
||||
this.pendingHistoryRestoreCleanup = clearRestoring;
|
||||
document.addEventListener('tts:queue-empty', clearRestoring);
|
||||
}
|
||||
}
|
||||
@@ -522,7 +552,7 @@ class GameLoopModule extends BaseModule {
|
||||
}
|
||||
}
|
||||
|
||||
async queueUnrenderedHistoryBlocks(saveRecord = {}) {
|
||||
async queueUnrenderedHistoryBlocks(saveRecord = {}, isCurrentRestore = null) {
|
||||
const storyHistory = this.getModule('story-history');
|
||||
const textBuffer = this.getModule('text-buffer');
|
||||
if (!storyHistory || !textBuffer || typeof textBuffer.addBlocks !== 'function') return;
|
||||
@@ -530,10 +560,16 @@ class GameLoopModule extends BaseModule {
|
||||
const end = Math.max(0, Number(saveRecord.latestBlockId || 0));
|
||||
if (end < start) return;
|
||||
const blocks = await storyHistory.getBlocksRange(saveRecord.gameId, start, end);
|
||||
if (typeof isCurrentRestore === 'function' && !isCurrentRestore()) return;
|
||||
if (saveRecord.gameId && storyHistory.currentGameId !== saveRecord.gameId) return;
|
||||
textBuffer.addBlocks(blocks);
|
||||
}
|
||||
|
||||
async resetClientPlaybackAndDisplay() {
|
||||
this.clientResetGeneration += 1;
|
||||
this.restoreGeneration += 1;
|
||||
this.clearPendingHistoryRestore('client-reset');
|
||||
|
||||
const playbackCoordinator = this.getModule('playback-coordinator');
|
||||
if (playbackCoordinator && typeof playbackCoordinator.stop === 'function') {
|
||||
await playbackCoordinator.stop();
|
||||
|
||||
Reference in New Issue
Block a user