From dc753adf1f93bad34544cc7da6e091d53d77ef05 Mon Sep 17 00:00:00 2001 From: bangonicdd <157843588+bangonicdd2@users.noreply.github.com> Date: Wed, 9 Apr 2025 01:45:06 +0900 Subject: [PATCH] fix: Lua factory init failure under concurrent display editing --- src/ts/process/lua.ts | 106 ++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts index bfe71cda..91359d42 100644 --- a/src/ts/process/lua.ts +++ b/src/ts/process/lua.ts @@ -20,23 +20,24 @@ let LuaEditDisplayIds = new Set() let LuaLowLevelIds = new Set() interface LuaEngineState { - code: string; - engine: LuaEngine; + code?: string; + engine?: LuaEngine; mutex: Mutex; - chat: Chat; - setVar: (key:string, value:string) => void, - getVar: (key:string) => string, - getGlobalVar: (key:string) => any, + wasEmpty?: boolean; + chat?: Chat; + setVar?: (key:string, value:string) => void, + getVar?: (key:string) => string, } let LuaEngines = new Map() +let luaFactoryPromise: Promise | null = null; +let pendingEngineCreations = new Map>(); export async function runLua(code:string, arg:{ char?:character|groupChat|simpleCharacterArgument, chat?:Chat setVar?: (key:string, value:string) => void, getVar?: (key:string) => string, - getGlobalVar?: (key:string) => any, lowLevelAccess?: boolean, mode?: string, data?: any @@ -44,40 +45,25 @@ export async function runLua(code:string, arg:{ const char = arg.char ?? getCurrentCharacter() const setVar = arg.setVar ?? setChatVar const getVar = arg.getVar ?? getChatVar - const getGlobalVar = arg.getGlobalVar ?? getGlobalChatVar const mode = arg.mode ?? 'manual' const data = arg.data ?? {} let chat = arg.chat ?? getCurrentChat() let stopSending = false let lowLevelAccess = arg.lowLevelAccess ?? false - if(!luaFactory){ - await makeLuaFactory() - } - let luaEngineState = LuaEngines.get(mode) - let wasEmpty = false - if (!luaEngineState) { - luaEngineState = { - code, - engine: await luaFactory.createEngine({injectObjects: true}), - mutex: new Mutex(), - chat, - setVar, - getVar, - getGlobalVar - } - LuaEngines.set(mode, luaEngineState) - wasEmpty = true - } else { + await ensureLuaFactory() + let luaEngineState = await getOrCreateEngineState(mode); + + return await luaEngineState.mutex.runExclusive(async () => { luaEngineState.chat = chat luaEngineState.setVar = setVar luaEngineState.getVar = getVar - luaEngineState.getGlobalVar = getGlobalVar - } - return await luaEngineState.mutex.runExclusive(async () => { - if (wasEmpty || code !== luaEngineState.code) { - if (!wasEmpty) + if (code !== luaEngineState.code) { + if (!luaEngineState.wasEmpty){ luaEngineState.engine.global.close() + } + luaEngineState.code = code + luaEngineState.wasEmpty = false luaEngineState.engine = await luaFactory.createEngine({injectObjects: true}) const luaEngine = luaEngineState.engine luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => { @@ -96,7 +82,7 @@ export async function runLua(code:string, arg:{ if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.has(id)){ return } - return luaEngineState.getGlobalVar(key) + return getGlobalChatVar(key) }) luaEngine.global.set('stopChat', (id:string) => { if(!LuaSafeIds.has(id)){ @@ -564,7 +550,7 @@ export async function runLua(code:string, arg:{ } async function makeLuaFactory(){ - luaFactory = new LuaFactory() + const _luaFactory = new LuaFactory() async function mountFile(name:string){ let code = '' for(let i = 0; i < 3; i++){ @@ -576,10 +562,61 @@ async function makeLuaFactory(){ } } catch (error) {} } - await luaFactory.mountFile(name,code) + await _luaFactory.mountFile(name,code) } await mountFile('json.lua') + luaFactory = _luaFactory +} + +async function ensureLuaFactory() { + if (luaFactory) return; + + if (luaFactoryPromise) { + try { + await luaFactoryPromise; + } catch (error) { + luaFactoryPromise = null; + } + return; + } + + try { + luaFactoryPromise = makeLuaFactory(); + await luaFactoryPromise; + } finally { + luaFactoryPromise = null; + } +} + +async function getOrCreateEngineState( + mode: string, +): Promise { + let engineState = LuaEngines.get(mode); + if (engineState) { + return engineState; + } + + let pendingCreation = pendingEngineCreations.get(mode); + if (pendingCreation) { + return pendingCreation; + } + + const creationPromise = (async () => { + const engineState: LuaEngineState = { + mutex: new Mutex(), + wasEmpty: true, + }; + LuaEngines.set(mode, engineState); + + pendingEngineCreations.delete(mode); + + return engineState; + })(); + + pendingEngineCreations.set(mode, creationPromise); + + return creationPromise; } function luaCodeWarper(code:string){ @@ -687,7 +724,6 @@ callListenMain = async(function(type, id, value) if type == 'editDisplay' then for _, func in ipairs(editDisplayFuncs) do realValue = func(id, realValue) - print(realValue) end end