fix: Lua factory init failure under concurrent display editing

This commit is contained in:
bangonicdd
2025-04-09 01:45:06 +09:00
parent d91dd1b825
commit dc753adf1f

View File

@@ -20,23 +20,24 @@ let LuaEditDisplayIds = new Set<string>()
let LuaLowLevelIds = new Set<string>() let LuaLowLevelIds = new Set<string>()
interface LuaEngineState { interface LuaEngineState {
code: string; code?: string;
engine: LuaEngine; engine?: LuaEngine;
mutex: Mutex; mutex: Mutex;
chat: Chat; wasEmpty?: boolean;
setVar: (key:string, value:string) => void, chat?: Chat;
getVar: (key:string) => string, setVar?: (key:string, value:string) => void,
getGlobalVar: (key:string) => any, getVar?: (key:string) => string,
} }
let LuaEngines = new Map<string, LuaEngineState>() let LuaEngines = new Map<string, LuaEngineState>()
let luaFactoryPromise: Promise<void> | null = null;
let pendingEngineCreations = new Map<string, Promise<LuaEngineState>>();
export async function runLua(code:string, arg:{ export async function runLua(code:string, arg:{
char?:character|groupChat|simpleCharacterArgument, char?:character|groupChat|simpleCharacterArgument,
chat?:Chat chat?:Chat
setVar?: (key:string, value:string) => void, setVar?: (key:string, value:string) => void,
getVar?: (key:string) => string, getVar?: (key:string) => string,
getGlobalVar?: (key:string) => any,
lowLevelAccess?: boolean, lowLevelAccess?: boolean,
mode?: string, mode?: string,
data?: any data?: any
@@ -44,40 +45,25 @@ export async function runLua(code:string, arg:{
const char = arg.char ?? getCurrentCharacter() const char = arg.char ?? getCurrentCharacter()
const setVar = arg.setVar ?? setChatVar const setVar = arg.setVar ?? setChatVar
const getVar = arg.getVar ?? getChatVar const getVar = arg.getVar ?? getChatVar
const getGlobalVar = arg.getGlobalVar ?? getGlobalChatVar
const mode = arg.mode ?? 'manual' const mode = arg.mode ?? 'manual'
const data = arg.data ?? {} const data = arg.data ?? {}
let chat = arg.chat ?? getCurrentChat() let chat = arg.chat ?? getCurrentChat()
let stopSending = false let stopSending = false
let lowLevelAccess = arg.lowLevelAccess ?? false let lowLevelAccess = arg.lowLevelAccess ?? false
if(!luaFactory){ await ensureLuaFactory()
await makeLuaFactory() let luaEngineState = await getOrCreateEngineState(mode);
}
let luaEngineState = LuaEngines.get(mode) return await luaEngineState.mutex.runExclusive(async () => {
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 {
luaEngineState.chat = chat luaEngineState.chat = chat
luaEngineState.setVar = setVar luaEngineState.setVar = setVar
luaEngineState.getVar = getVar luaEngineState.getVar = getVar
luaEngineState.getGlobalVar = getGlobalVar if (code !== luaEngineState.code) {
} if (!luaEngineState.wasEmpty){
return await luaEngineState.mutex.runExclusive(async () => {
if (wasEmpty || code !== luaEngineState.code) {
if (!wasEmpty)
luaEngineState.engine.global.close() luaEngineState.engine.global.close()
}
luaEngineState.code = code
luaEngineState.wasEmpty = false
luaEngineState.engine = await luaFactory.createEngine({injectObjects: true}) luaEngineState.engine = await luaFactory.createEngine({injectObjects: true})
const luaEngine = luaEngineState.engine const luaEngine = luaEngineState.engine
luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => { 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)){ if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.has(id)){
return return
} }
return luaEngineState.getGlobalVar(key) return getGlobalChatVar(key)
}) })
luaEngine.global.set('stopChat', (id:string) => { luaEngine.global.set('stopChat', (id:string) => {
if(!LuaSafeIds.has(id)){ if(!LuaSafeIds.has(id)){
@@ -564,7 +550,7 @@ export async function runLua(code:string, arg:{
} }
async function makeLuaFactory(){ async function makeLuaFactory(){
luaFactory = new LuaFactory() const _luaFactory = new LuaFactory()
async function mountFile(name:string){ async function mountFile(name:string){
let code = '' let code = ''
for(let i = 0; i < 3; i++){ for(let i = 0; i < 3; i++){
@@ -576,10 +562,61 @@ async function makeLuaFactory(){
} }
} catch (error) {} } catch (error) {}
} }
await luaFactory.mountFile(name,code) await _luaFactory.mountFile(name,code)
} }
await mountFile('json.lua') 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<LuaEngineState> {
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){ function luaCodeWarper(code:string){
@@ -687,7 +724,6 @@ callListenMain = async(function(type, id, value)
if type == 'editDisplay' then if type == 'editDisplay' then
for _, func in ipairs(editDisplayFuncs) do for _, func in ipairs(editDisplayFuncs) do
realValue = func(id, realValue) realValue = func(id, realValue)
print(realValue)
end end
end end