Fix stale restore after game restart

This commit is contained in:
2026-05-20 22:27:36 +02:00
parent 8258ea2321
commit beac5a2be3
12 changed files with 167 additions and 44 deletions
+14 -6
View File
@@ -112,14 +112,20 @@ function getOrCreateEngine(socketId) {
}
return engine;
}
async function handleGameApi(socket, method, args) {
function withClientRequestId(turn, requestId) {
const id = Number(requestId || 0);
return Number.isInteger(id) && id > 0
? { ...turn, clientRequestId: id }
: turn;
}
async function handleGameApi(socket, method, args, requestId) {
const slots = getSlots(socket.id);
switch (method) {
case 'newGame':
case 'newGame()': {
const engine = new ink_engine_1.InkEngine(getStoryPath());
sessions.set(socket.id, engine);
socket.emit('narrativeResponse', engine.newGame());
socket.emit('narrativeResponse', withClientRequestId(engine.newGame(), requestId));
return {
success: true,
result: true,
@@ -138,7 +144,7 @@ async function handleGameApi(socket, method, args) {
if (!Number.isInteger(choiceIndex)) {
return { success: false, error: 'invalid_choice', result: false };
}
socket.emit('narrativeResponse', engine.chooseChoice(choiceIndex));
socket.emit('narrativeResponse', withClientRequestId(engine.chooseChoice(choiceIndex), requestId));
return { success: true, result: true };
}
case 'loadGame':
@@ -149,8 +155,8 @@ async function handleGameApi(socket, method, args) {
return { success: false, error: 'missing_save', result: false };
}
const engine = getOrCreateEngine(socket.id);
socket.emit('narrativeResponse', engine.loadGame(browserSave || slots.get(slot)));
socket.emit('gameLoaded', { slot });
socket.emit('narrativeResponse', withClientRequestId(engine.loadGame(browserSave || slots.get(slot)), requestId));
socket.emit('gameLoaded', { slot, clientRequestId: requestId });
return { success: true, result: true, running: true, slot };
}
case 'resumeGame':
@@ -204,7 +210,9 @@ io.on('connection', (socket) => {
socket.emit('gameConfig', (0, game_config_1.clientGameConfig)(engineConfig));
socket.on('gameApi', async (request, respond) => {
try {
const result = await handleGameApi(socket, String(request?.method ?? ''), Array.isArray(request?.args) ? request.args : []);
const result = await handleGameApi(socket, String(request?.method ?? ''), Array.isArray(request?.args) ? request.args : [], Number.isInteger(Number(request?.requestId)) && Number(request?.requestId) > 0
? Number(request?.requestId)
: undefined);
if (typeof respond === 'function')
respond(result);
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+15 -8
View File
@@ -103,7 +103,13 @@ function normalizeSaveSlot(slot) {
const value = Number(slot);
return Number.isInteger(value) && value > 0 ? value : 1;
}
async function startDemoGameForSocket(socket) {
function withClientRequestId(turn, requestId) {
const id = Number(requestId || 0);
return Number.isInteger(id) && id > 0
? { ...turn, clientRequestId: id }
: turn;
}
async function startDemoGameForSocket(socket, requestId) {
nextTurnIds.set(socket.id, 1);
const gameRunner = new game_runner_1.GameRunner();
const worldFile = (0, game_config_1.projectPath)(process.env.DEFAULT_WORLD_FILE || engineConfig.paths.mainGameFile);
@@ -114,7 +120,7 @@ async function startDemoGameForSocket(socket) {
...(0, turn_result_1.textToParagraphs)(gameState.world.introduction),
...(0, turn_result_1.textToParagraphs)(gameRunner.getCurrentRoomDescription()),
];
socket.emit('narrativeResponse', {
socket.emit('narrativeResponse', withClientRequestId({
turnId: nextTurnId(socket.id),
paragraphs,
choices: [],
@@ -122,16 +128,16 @@ async function startDemoGameForSocket(socket) {
gameState: {
currentRoomId: gameState.currentRoomId,
},
});
}, requestId));
return gameRunner;
}
async function handleGameApi(socket, method, args = []) {
async function handleGameApi(socket, method, args = [], requestId) {
const saveGames = socket.data.saveGames || new Map();
socket.data.saveGames = saveGames;
switch (method) {
case 'newGame':
case 'newGame()':
await startDemoGameForSocket(socket);
await startDemoGameForSocket(socket, requestId);
return { success: true, result: true, running: true, canLoad: saveGames.size > 0 };
case 'loadGame':
case 'loadGame()': {
@@ -139,8 +145,8 @@ async function handleGameApi(socket, method, args = []) {
if (!saveGames.has(slot)) {
return { success: false, error: 'missing_save', result: false };
}
await startDemoGameForSocket(socket);
socket.emit('gameLoaded', { slot });
await startDemoGameForSocket(socket, requestId);
socket.emit('gameLoaded', { slot, clientRequestId: requestId });
return { success: true, result: true, running: true, slot };
}
case 'saveGame':
@@ -176,7 +182,8 @@ io.on('connection', (socket) => {
socket.data.saveGames = new Map();
socket.on('gameApi', async (request, respond) => {
try {
const response = await handleGameApi(socket, String(request?.method || ''), Array.isArray(request?.args) ? request.args : []);
const requestId = Number(request?.requestId || 0);
const response = await handleGameApi(socket, String(request?.method || ''), Array.isArray(request?.args) ? request.args : [], Number.isInteger(requestId) && requestId > 0 ? requestId : undefined);
if (typeof respond === 'function') {
respond(response);
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+13 -5
View File
@@ -127,7 +127,13 @@ function getSlots(socketId) {
}
return slots;
}
async function handleGameApi(socket, method, args) {
function withClientRequestId(turn, requestId) {
const id = Number(requestId || 0);
return Number.isInteger(id) && id > 0
? { ...turn, clientRequestId: id }
: turn;
}
async function handleGameApi(socket, method, args, requestId) {
const slots = getSlots(socket.id);
debugLog(`gameApi request from ${socket.id}: ${method}`, { args });
switch (method) {
@@ -135,7 +141,7 @@ async function handleGameApi(socket, method, args) {
case 'newGame()': {
const engine = getOrCreateEngine(socket.id);
const turn = await engine.newGame();
socket.emit('narrativeResponse', toClientTurn(turn));
socket.emit('narrativeResponse', withClientRequestId(toClientTurn(turn), requestId));
return {
success: true,
result: true,
@@ -151,8 +157,8 @@ async function handleGameApi(socket, method, args) {
}
const engine = getOrCreateEngine(socket.id);
const turn = await engine.loadGame(slots.get(slot));
socket.emit('narrativeResponse', toClientTurn(turn));
socket.emit('gameLoaded', { slot });
socket.emit('narrativeResponse', withClientRequestId(toClientTurn(turn), requestId));
socket.emit('gameLoaded', { slot, clientRequestId: requestId });
return { success: true, result: true, running: true, slot };
}
case 'saveGame':
@@ -229,7 +235,9 @@ io.on('connection', (socket) => {
socket.emit('gameConfig', (0, game_config_1.clientGameConfig)(engineConfig));
socket.on('gameApi', async (request, respond) => {
try {
const result = await handleGameApi(socket, String(request?.method ?? ''), Array.isArray(request?.args) ? request.args : []);
const result = await handleGameApi(socket, String(request?.method ?? ''), Array.isArray(request?.args) ? request.args : [], Number.isInteger(Number(request?.requestId)) && Number(request?.requestId) > 0
? Number(request?.requestId)
: undefined);
debugLog(`gameApi response to ${socket.id}`, result);
if (typeof respond === 'function')
respond(result);
+1 -1
View File
File diff suppressed because one or more lines are too long