feat: migrate runtime backend to node server
This commit is contained in:
@@ -12,7 +12,12 @@ import { normalizeNpcPersistentState } from '../data/npcInteractions';
|
||||
import { normalizeQuestLogEntries } from '../data/questFlow';
|
||||
import { normalizeGameRuntimeStats } from '../data/runtimeStats';
|
||||
import { ensureSceneEncounterPreview, TREASURE_ENCOUNTERS_ENABLED } from '../data/sceneEncounterPreviews';
|
||||
import {clearSavedSnapshot, readSavedSnapshot, writeSavedSnapshot} from '../persistence/gameSaveStorage';
|
||||
import type { SavedGameSnapshot } from '../persistence/gameSaveStorage';
|
||||
import {
|
||||
deleteSaveSnapshot,
|
||||
getSaveSnapshot,
|
||||
putSaveSnapshot,
|
||||
} from '../services/storageService';
|
||||
import {
|
||||
applyStoryEngineMigration,
|
||||
buildSaveMigrationManifest,
|
||||
@@ -169,9 +174,24 @@ export function useGamePersistence({
|
||||
resetStoryState: () => void;
|
||||
}) {
|
||||
const [hasSavedGame, setHasSavedGame] = useState(false);
|
||||
const [savedSnapshot, setSavedSnapshot] = useState<SavedGameSnapshot | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setHasSavedGame(Boolean(readSavedSnapshot()));
|
||||
let isActive = true;
|
||||
|
||||
void getSaveSnapshot()
|
||||
.then((snapshot) => {
|
||||
if (!isActive) return;
|
||||
setSavedSnapshot(snapshot);
|
||||
setHasSavedGame(Boolean(snapshot));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('[useGamePersistence] failed to load remote snapshot', error);
|
||||
});
|
||||
|
||||
return () => {
|
||||
isActive = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -180,21 +200,24 @@ export function useGamePersistence({
|
||||
if (!canPersist) return;
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
const didSave = writeSavedSnapshot({
|
||||
void putSaveSnapshot({
|
||||
gameState,
|
||||
bottomTab,
|
||||
currentStory,
|
||||
});
|
||||
|
||||
if (didSave) {
|
||||
setHasSavedGame(true);
|
||||
}
|
||||
})
|
||||
.then((snapshot) => {
|
||||
setSavedSnapshot(snapshot);
|
||||
setHasSavedGame(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('[useGamePersistence] failed to autosave remote snapshot', error);
|
||||
});
|
||||
}, AUTO_SAVE_DELAY_MS);
|
||||
|
||||
return () => window.clearTimeout(timeoutId);
|
||||
}, [bottomTab, currentStory, gameState, isLoading]);
|
||||
|
||||
const saveCurrentGame = useCallback((override?: {
|
||||
const saveCurrentGame = useCallback(async (override?: {
|
||||
gameState?: GameState;
|
||||
bottomTab?: BottomTab;
|
||||
currentStory?: StoryMoment | null;
|
||||
@@ -207,28 +230,40 @@ export function useGamePersistence({
|
||||
return false;
|
||||
}
|
||||
|
||||
const didSave = writeSavedSnapshot({
|
||||
gameState: nextGameState,
|
||||
bottomTab: nextBottomTab,
|
||||
currentStory: nextStory,
|
||||
});
|
||||
|
||||
if (didSave) {
|
||||
try {
|
||||
const snapshot = await putSaveSnapshot({
|
||||
gameState: nextGameState,
|
||||
bottomTab: nextBottomTab,
|
||||
currentStory: nextStory,
|
||||
});
|
||||
setSavedSnapshot(snapshot);
|
||||
setHasSavedGame(true);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn('[useGamePersistence] failed to save remote snapshot', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return didSave;
|
||||
}, [bottomTab, currentStory, gameState]);
|
||||
|
||||
const clearSavedGame = useCallback(() => {
|
||||
clearSavedSnapshot();
|
||||
const clearSavedGame = useCallback(async () => {
|
||||
try {
|
||||
await deleteSaveSnapshot();
|
||||
} catch (error) {
|
||||
console.warn('[useGamePersistence] failed to delete remote snapshot', error);
|
||||
}
|
||||
|
||||
setSavedSnapshot(null);
|
||||
setHasSavedGame(false);
|
||||
}, []);
|
||||
|
||||
const continueSavedGame = useCallback(() => {
|
||||
const snapshot = readSavedSnapshot();
|
||||
const continueSavedGame = useCallback(async () => {
|
||||
const snapshot = savedSnapshot ?? await getSaveSnapshot().catch((error) => {
|
||||
console.warn('[useGamePersistence] failed to refetch remote snapshot', error);
|
||||
return null;
|
||||
});
|
||||
if (!snapshot) {
|
||||
clearSavedGame();
|
||||
setSavedSnapshot(null);
|
||||
setHasSavedGame(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -236,9 +271,10 @@ export function useGamePersistence({
|
||||
setGameState(normalizeSavedGameState(snapshot.gameState));
|
||||
setBottomTab(snapshot.bottomTab ?? 'adventure');
|
||||
hydrateStoryState(normalizeSavedStory(snapshot.currentStory));
|
||||
setSavedSnapshot(snapshot);
|
||||
setHasSavedGame(true);
|
||||
return true;
|
||||
}, [clearSavedGame, hydrateStoryState, resetStoryState, setBottomTab, setGameState]);
|
||||
}, [hydrateStoryState, resetStoryState, savedSnapshot, setBottomTab, setGameState]);
|
||||
|
||||
return {
|
||||
hasSavedGame,
|
||||
|
||||
Reference in New Issue
Block a user