Update variables v2 state handling

This commit is contained in:
2026-02-01 02:49:35 +08:00
parent 4b0541610b
commit bcf664e9a0
6 changed files with 1008 additions and 128 deletions

View File

@@ -28,7 +28,7 @@ import {
applyXbGetVarForMessage,
parseValueForSet,
} from "./var-commands.js";
import { applyStateForMessage, clearStateAppliedFrom } from "./state2/index.js";
import { applyStateForMessage } from "./state2/index.js";
import {
preprocessBumpAliases,
executeQueuedVareventJsAfterTurn,
@@ -1624,16 +1624,10 @@ function rollbackToPreviousOf(messageId) {
const id = Number(messageId);
if (Number.isNaN(id)) return;
clearStateAppliedFrom(id);
if (typeof globalThis.LWB_StateRollbackHook === 'function') {
Promise.resolve(globalThis.LWB_StateRollbackHook(id)).catch((e) => {
console.error('[variablesCore] LWB_StateRollbackHook failed:', e);
});
}
const prevId = id - 1;
if (prevId < 0) return;
// ???? 1.0 ???????
const snap = getSnapshot(prevId);
if (snap) {
const normalized = normalizeSnapshotRecord(snap);
@@ -1647,12 +1641,52 @@ function rollbackToPreviousOf(messageId) {
}
}
function rebuildVariablesFromScratch() {
async function rollbackToPreviousOfAsync(messageId) {
const id = Number(messageId);
if (Number.isNaN(id)) return;
// ???????? floor>=id ? L0
if (typeof globalThis.LWB_StateRollbackHook === 'function') {
try {
await globalThis.LWB_StateRollbackHook(id);
} catch (e) {
console.error('[variablesCore] LWB_StateRollbackHook failed:', e);
}
}
const prevId = id - 1;
const mode = getVariablesMode();
if (mode === '2.0') {
try {
const mod = await import('./state2/index.js');
await mod.restoreStateV2ToFloor(prevId); // prevId<0 ???
} catch (e) {
console.error('[variablesCore][2.0] restoreStateV2ToFloor failed:', e);
}
return;
}
// mode === '1.0'
rollbackToPreviousOf(id);
}
async function rebuildVariablesFromScratch() {
try {
const mode = getVariablesMode();
if (mode === '2.0') {
const mod = await import('./state2/index.js');
const chat = getContext()?.chat || [];
const lastId = chat.length ? chat.length - 1 : -1;
await mod.restoreStateV2ToFloor(lastId);
return;
}
// 1.0 旧逻辑
setVarDict({});
const chat = getContext()?.chat || [];
for (let i = 0; i < chat.length; i++) {
applyVariablesForMessage(i);
await applyVariablesForMessage(i);
}
} catch {}
}
@@ -1842,7 +1876,7 @@ async function applyVariablesForMessage(messageId) {
} catch (e) {
parseErrors++;
if (debugOn) {
try { xbLog.error(MODULE_ID, `plot-log è§£æž<EFBFBD>失败:楼å±?${messageId} å<EFBFBD>?${idx + 1} 预览=${preview(b)}`, e); } catch {}
try { xbLog.error(MODULE_ID, `plot-log 解析失败<EFBFBD>?${messageId} <20>?${idx + 1} 预览=${preview(b)}`, e); } catch {}
}
return;
}
@@ -1873,7 +1907,7 @@ async function applyVariablesForMessage(messageId) {
try {
xbLog.warn(
MODULE_ID,
`plot-log 未产生å<EFBFBD>¯æ‰§è¡ŒæŒ‡ä»¤ï¼šæ¥¼å±?${messageId} å<EFBFBD>—æ•°=${blocks.length} è§£æž<EFBFBD>æ<EFBFBD>¡ç®=${parsedPartsTotal} è§£æž<EFBFBD>失败=${parseErrors} 预览=${preview(blocks[0])}`
`plot-log 未产生可执行指令<EFBFBD>?${messageId} 块数=${blocks.length} 解析条目=${parsedPartsTotal} 解析失败=${parseErrors} 预览=${preview(blocks[0])}`
);
} catch {}
}
@@ -2149,7 +2183,7 @@ async function applyVariablesForMessage(messageId) {
const denied = guardDenied ? `,被规则拦截=${guardDenied}` : '';
xbLog.warn(
MODULE_ID,
`plot-log 指令执行å<EFBFBD>Žæ— å<EFBFBD>˜åŒï¼šæ¥¼å±?${messageId} 指令æ•?${ops.length}${denied} 示例=${preview(JSON.stringify(guardDeniedSamples))}`
`plot-log 指令执行后无变化<EFBFBD>?${messageId} 指令<EFBFBD>?${ops.length}${denied} 示例=${preview(JSON.stringify(guardDeniedSamples))}`
);
} catch {}
}
@@ -2218,7 +2252,7 @@ function bindEvents() {
events?.on(event_types.MESSAGE_SENT, async () => {
try {
snapshotCurrentLastFloor();
if (getVariablesMode() !== '2.0') snapshotCurrentLastFloor();
const chat = getContext()?.chat || [];
const id = chat.length ? chat.length - 1 : undefined;
if (typeof id === 'number') {
@@ -2247,7 +2281,7 @@ function bindEvents() {
if (typeof id === 'number') {
await applyVarsForMessage(id);
applyXbGetVarForMessage(id, true);
snapshotForMessageId(id);
if (getVariablesMode() !== '2.0') snapshotForMessageId(id);
}
} catch {}
});
@@ -2259,7 +2293,7 @@ function bindEvents() {
if (typeof id === 'number') {
await applyVarsForMessage(id);
applyXbGetVarForMessage(id, true);
snapshotForMessageId(id);
if (getVariablesMode() !== '2.0') snapshotForMessageId(id);
}
} catch {}
});
@@ -2283,33 +2317,35 @@ function bindEvents() {
events?.on(event_types.MESSAGE_EDITED, async (data) => {
try {
const id = getMsgIdLoose(data);
if (typeof id === 'number') {
clearAppliedFor(id);
rollbackToPreviousOf(id);
if (typeof id !== 'number') return;
setTimeout(async () => {
await applyVarsForMessage(id);
applyXbGetVarForMessage(id, true);
if (getVariablesMode() !== '2.0') clearAppliedFor(id);
try {
const ctx = getContext();
const msg = ctx?.chat?.[id];
if (msg) updateMessageBlock(id, msg, { rerenderMessage: true });
} catch {}
// ? ?? await????? apply ????????????
await rollbackToPreviousOfAsync(id);
try {
const ctx = getContext();
const es = ctx?.eventSource;
const et = ctx?.event_types;
if (es?.emit && et?.MESSAGE_UPDATED) {
suppressUpdatedOnce.add(id);
await es.emit(et.MESSAGE_UPDATED, id);
}
} catch {}
setTimeout(async () => {
await applyVarsForMessage(id);
applyXbGetVarForMessage(id, true);
await executeQueuedVareventJsAfterTurn();
}, 10);
}
try {
const ctx = getContext();
const msg = ctx?.chat?.[id];
if (msg) updateMessageBlock(id, msg, { rerenderMessage: true });
} catch {}
try {
const ctx = getContext();
const es = ctx?.eventSource;
const et = ctx?.event_types;
if (es?.emit && et?.MESSAGE_UPDATED) {
suppressUpdatedOnce.add(id);
await es.emit(et.MESSAGE_UPDATED, id);
}
} catch {}
await executeQueuedVareventJsAfterTurn();
}, 10);
} catch {}
});
@@ -2317,28 +2353,44 @@ function bindEvents() {
events?.on(event_types.MESSAGE_SWIPED, async (data) => {
try {
const id = getMsgIdLoose(data);
if (typeof id === 'number') {
lastSwipedId = id;
clearAppliedFor(id);
rollbackToPreviousOf(id);
if (typeof id !== 'number') return;
const tId = setTimeout(async () => {
pendingSwipeApply.delete(id);
await applyVarsForMessage(id);
await executeQueuedVareventJsAfterTurn();
}, 10);
lastSwipedId = id;
if (getVariablesMode() !== '2.0') clearAppliedFor(id);
pendingSwipeApply.set(id, tId);
}
// ? ?? await???????????????
await rollbackToPreviousOfAsync(id);
const tId = setTimeout(async () => {
pendingSwipeApply.delete(id);
await applyVarsForMessage(id);
await executeQueuedVareventJsAfterTurn();
}, 10);
pendingSwipeApply.set(id, tId);
} catch {}
});
// message deleted
events?.on(event_types.MESSAGE_DELETED, (data) => {
events?.on(event_types.MESSAGE_DELETED, async (data) => {
try {
const id = getMsgIdStrict(data);
if (typeof id === 'number') {
rollbackToPreviousOf(id);
if (typeof id !== 'number') return;
// ? ????????await ???????
await rollbackToPreviousOfAsync(id);
// ✅ 2.0:物理删除消息 => 同步清理 WAL/ckpt避免膨胀
if (getVariablesMode() === '2.0') {
try {
const mod = await import('./state2/index.js');
await mod.trimStateV2FromFloor(id);
} catch (e) {
console.error('[variablesCore][2.0] trimStateV2FromFloor failed:', e);
}
}
if (getVariablesMode() !== '2.0') {
clearSnapshotsFrom(id);
clearAppliedFrom(id);
}
@@ -2349,7 +2401,7 @@ function bindEvents() {
events?.on(event_types.GENERATION_STARTED, (data) => {
try {
snapshotPreviousFloor();
if (getVariablesMode() !== '2.0') snapshotPreviousFloor();
// cancel swipe delay
const t = (typeof data === 'string' ? data : (data?.type || '')).toLowerCase();
@@ -2364,7 +2416,7 @@ function bindEvents() {
});
// chat changed
events?.on(event_types.CHAT_CHANGED, () => {
events?.on(event_types.CHAT_CHANGED, async () => {
try {
rulesClearCache();
rulesLoadFromMeta();
@@ -2372,6 +2424,13 @@ function bindEvents() {
const meta = getContext()?.chatMetadata || {};
meta[LWB_PLOT_APPLIED_KEY] = {};
getContext()?.saveMetadataDebounced?.();
if (getVariablesMode() === '2.0') {
try {
const mod = await import('./state2/index.js');
mod.clearStateAppliedFrom(0);
} catch {}
}
} catch {}
});
}
@@ -2408,6 +2467,33 @@ export function initVariablesCore() {
applyDeltaTable: applyRulesDeltaToTable,
save: rulesSaveToMeta,
};
globalThis.LWB_StateV2 = {
/**
* @param {string} text - 包含 <state>...</state> 的文本
* @param {{ floor?: number, silent?: boolean }} [options]
* - floor: 指定写入/记录用楼层(默认:最后一楼)
* - silent: true 时不触发 stateAtomsGenerated初始化用
*/
applyText: async (text, options = {}) => {
const { applyStateForMessage } = await import('./state2/index.js');
const ctx = getContext();
const floor =
Number.isFinite(options.floor)
? Number(options.floor)
: Math.max(0, (ctx?.chat?.length || 1) - 1);
const result = applyStateForMessage(floor, String(text || ''));
// ✅ 默认会触发(当作事件)
// ✅ 初始化时 silent=true不触发当作基线写入
if (!options.silent && result?.atoms?.length) {
$(document).trigger('xiaobaix:variables:stateAtomsGenerated', {
messageId: floor,
atoms: result.atoms,
});
}
return result;
},
};
}
/**
@@ -2429,6 +2515,7 @@ export function cleanupVariablesCore() {
// clear global hooks
delete globalThis.LWB_Guard;
delete globalThis.LWB_StateV2;
// clear guard state
guardBypass(false);
@@ -2454,4 +2541,4 @@ export {
rulesSetTable,
rulesLoadFromMeta,
rulesSaveToMeta,
};
};