Checkpoint Eibenreith ink architecture
This commit is contained in:
@@ -33,6 +33,8 @@ class GameLoopModule extends BaseModule {
|
||||
this.clientResetGeneration = 0;
|
||||
this.restoreGeneration = 0;
|
||||
this.pendingHistoryRestoreCleanup = null;
|
||||
this.gameOperationGeneration = 0;
|
||||
this.autoSaveGeneration = 0;
|
||||
|
||||
// Bind methods using parent's bindMethods utility
|
||||
this.bindMethods([
|
||||
@@ -212,6 +214,8 @@ class GameLoopModule extends BaseModule {
|
||||
async resumeAutosaveIfAvailable() {
|
||||
if (this.resumeAttempted) return false;
|
||||
this.resumeAttempted = true;
|
||||
const operationGeneration = ++this.gameOperationGeneration;
|
||||
const isCurrentOperation = () => operationGeneration === this.gameOperationGeneration;
|
||||
|
||||
const storyHistory = this.getModule('story-history');
|
||||
const socketClient = this.getModule('socket-client');
|
||||
@@ -220,11 +224,13 @@ class GameLoopModule extends BaseModule {
|
||||
}
|
||||
|
||||
const browserSave = await storyHistory.loadSlot(this.autoSaveSlot);
|
||||
if (!isCurrentOperation()) return false;
|
||||
if (!browserSave?.inkState || browserSave.running === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.resetClientPlaybackAndDisplay();
|
||||
if (!isCurrentOperation()) return false;
|
||||
this.currentChoices = [];
|
||||
this.currentInputMode = 'none';
|
||||
document.dispatchEvent(new CustomEvent('story:choices', { detail: [] }));
|
||||
@@ -234,6 +240,7 @@ class GameLoopModule extends BaseModule {
|
||||
}));
|
||||
|
||||
const response = await socketClient.resumeGame(browserSave.inkState);
|
||||
if (!isCurrentOperation()) return false;
|
||||
if (!response?.success) {
|
||||
console.warn('GameLoop: autosave resume failed', response);
|
||||
document.dispatchEvent(new CustomEvent('story:history-restoring', {
|
||||
@@ -249,6 +256,7 @@ class GameLoopModule extends BaseModule {
|
||||
this.gameState.canLoad = true;
|
||||
this.updateUIState();
|
||||
await this.restoreBrowserSave(browserSave, 'autosave-resume', { resetDisplay: false });
|
||||
if (!isCurrentOperation()) return false;
|
||||
this.restoreInputStateFromSave(browserSave, 'autosave-resume');
|
||||
return true;
|
||||
}
|
||||
@@ -297,6 +305,8 @@ class GameLoopModule extends BaseModule {
|
||||
async requestStartGame() {
|
||||
const socketClient = this.getModule('socket-client');
|
||||
if (!socketClient) return;
|
||||
const operationGeneration = ++this.gameOperationGeneration;
|
||||
const isCurrentOperation = () => operationGeneration === this.gameOperationGeneration;
|
||||
|
||||
this.gameState.started = true;
|
||||
this.gameState.startedOnce = true;
|
||||
@@ -304,9 +314,11 @@ class GameLoopModule extends BaseModule {
|
||||
this.gameState.canSave = true;
|
||||
this.updateUIState();
|
||||
await this.resetClientPlaybackAndDisplay();
|
||||
if (!isCurrentOperation()) return;
|
||||
const storyHistory = this.getModule('story-history');
|
||||
if (storyHistory && typeof storyHistory.startNewGame === 'function') {
|
||||
await storyHistory.startNewGame();
|
||||
if (!isCurrentOperation()) return;
|
||||
if (typeof storyHistory.saveSlot === 'function') {
|
||||
await storyHistory.saveSlot(this.autoSaveSlot, {
|
||||
inkState: null,
|
||||
@@ -317,6 +329,7 @@ class GameLoopModule extends BaseModule {
|
||||
}
|
||||
}
|
||||
const response = await socketClient.newGame();
|
||||
if (!isCurrentOperation()) return;
|
||||
if (!response?.success) {
|
||||
console.error('GameLoop: newGame failed', response);
|
||||
this.gameState.started = false;
|
||||
@@ -331,6 +344,7 @@ class GameLoopModule extends BaseModule {
|
||||
this.gameState.canLoad = Boolean(response.canLoad);
|
||||
this.updateUIState();
|
||||
if (response.savedState && storyHistory && typeof storyHistory.saveSlot === 'function') {
|
||||
if (!isCurrentOperation()) return;
|
||||
await storyHistory.saveSlot(this.autoSaveSlot, {
|
||||
inkState: response.savedState,
|
||||
choices: [],
|
||||
@@ -375,12 +389,16 @@ class GameLoopModule extends BaseModule {
|
||||
async requestLoadGame() {
|
||||
const socketClient = this.getModule('socket-client');
|
||||
if (!socketClient) return;
|
||||
const operationGeneration = ++this.gameOperationGeneration;
|
||||
const isCurrentOperation = () => operationGeneration === this.gameOperationGeneration;
|
||||
|
||||
const storyHistory = this.getModule('story-history');
|
||||
const browserSave = storyHistory && typeof storyHistory.loadSlot === 'function'
|
||||
? await storyHistory.loadSlot(1)
|
||||
: null;
|
||||
if (!isCurrentOperation()) return;
|
||||
const hasSave = browserSave ? { result: true } : await socketClient.hasSaveGame(1);
|
||||
if (!isCurrentOperation()) return;
|
||||
if (!hasSave?.result) {
|
||||
this.gameState.canLoad = false;
|
||||
this.updateUIState();
|
||||
@@ -394,7 +412,9 @@ class GameLoopModule extends BaseModule {
|
||||
this.gameState.canLoad = true;
|
||||
this.updateUIState();
|
||||
await this.restoreBrowserSave(browserSave, 'load-game', { resetDisplay: true });
|
||||
if (!isCurrentOperation()) return;
|
||||
const response = await socketClient.loadGame(1, browserSave?.inkState || null);
|
||||
if (!isCurrentOperation()) return;
|
||||
if (response?.success && browserSave && Array.isArray(browserSave.choices)) {
|
||||
this.currentChoices = browserSave.choices;
|
||||
this.currentInputMode = browserSave.inputMode || this.currentInputMode;
|
||||
@@ -523,11 +543,16 @@ class GameLoopModule extends BaseModule {
|
||||
return;
|
||||
}
|
||||
|
||||
const autoSaveGeneration = this.autoSaveGeneration;
|
||||
const gameId = storyHistory.currentGameId || null;
|
||||
this.autoSaveInProgress = true;
|
||||
try {
|
||||
const response = this.gameState.started && typeof socketClient.exportGameState === 'function'
|
||||
? await socketClient.exportGameState()
|
||||
: null;
|
||||
if (autoSaveGeneration !== this.autoSaveGeneration || storyHistory.currentGameId !== gameId) {
|
||||
return;
|
||||
}
|
||||
if (!response?.success || !response.savedState) {
|
||||
return;
|
||||
}
|
||||
@@ -535,6 +560,7 @@ class GameLoopModule extends BaseModule {
|
||||
|
||||
const audioManager = this.getModule('audio-manager');
|
||||
await storyHistory.saveSlot(this.autoSaveSlot, {
|
||||
gameId,
|
||||
inkState: response.savedState,
|
||||
latestRenderedBlockId: storyHistory.latestRenderedBlockId || 0,
|
||||
renderedLineCount: storyHistory.renderedLineCount || 0,
|
||||
@@ -545,9 +571,11 @@ class GameLoopModule extends BaseModule {
|
||||
});
|
||||
} finally {
|
||||
this.autoSaveInProgress = false;
|
||||
if (this.autoSaveQueued) {
|
||||
if (autoSaveGeneration === this.autoSaveGeneration && this.autoSaveQueued) {
|
||||
this.autoSaveQueued = false;
|
||||
this.autoSaveCurrentSession();
|
||||
} else {
|
||||
this.autoSaveQueued = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -568,6 +596,7 @@ class GameLoopModule extends BaseModule {
|
||||
async resetClientPlaybackAndDisplay() {
|
||||
this.clientResetGeneration += 1;
|
||||
this.restoreGeneration += 1;
|
||||
this.autoSaveGeneration += 1;
|
||||
this.clearPendingHistoryRestore('client-reset');
|
||||
|
||||
const playbackCoordinator = this.getModule('playback-coordinator');
|
||||
|
||||
Reference in New Issue
Block a user