diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 3fd24df..097402a 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -738,11 +738,16 @@ export function registerRobloxShim(lua, opts) { // Главное: __rbxl_resume_co определена в Lua и вызывается из JS через // lua.global.get('__rbxl_resume_co') — это безопаснее чем doStringSync // потому что не парсит код заново и не создаёт re-entrant проблем. + // Lua-side helper для логов (используется в task.wait/resume для отладки) + global.set('__log', (level, text) => { + send('log', { level: String(level || 'info'), text: String(text || '') }); + }); + lua.doStringSync(` local function rbx_wait(sec) sec = sec or 0 - coroutine.yield(sec) - return sec + local ret = coroutine.yield(sec) + return ret or sec end if type(task) == 'table' then task.wait = rbx_wait @@ -751,20 +756,19 @@ export function registerRobloxShim(lua, opts) { end wait = rbx_wait - -- Вызывается из JS-tickScheduler: - -- возвращает next-delay (number) если co yield'нулся ещё раз, - -- или nil если co завершился / умер. function __rbxl_resume_co(co) if not co or coroutine.status(co) ~= 'suspended' then return nil end local ok, ret = coroutine.resume(co) - if not ok then - return false, tostring(ret) - end + if not ok then return false, tostring(ret) end if coroutine.status(co) == 'dead' then return nil end if type(ret) == 'number' then return ret end return 0 end `); + // Добавим Lua-side helper для лога + global.set('__log', (level, text) => { + send('log', { level: String(level || 'info'), text: String(text || '') }); + }); // Достаём ссылку на Lua-функцию один раз; вызовы безопасны (не doStringSync) const luaResumeCo = lua.global.get('__rbxl_resume_co'); @@ -822,8 +826,6 @@ export function registerRobloxShim(lua, opts) { } } // 2. Резюм coroutine'ов которые task.wait() - // Используем lua.global.get-кешированную __rbxl_resume_co функцию — - // безопаснее чем doStringSync (не re-entrant в WASM). const dueCoros = []; for (let i = waitingCoros.length - 1; i >= 0; i--) { if (waitingCoros[i].wakeAt <= now) { @@ -836,7 +838,6 @@ export function registerRobloxShim(lua, opts) { if (!co) continue; try { const result = luaResumeCo(co); - // result: number (next delay), nil (done), false+errStr (failed) if (result === null || result === undefined) { coroutines.delete(entry.coId); } else if (typeof result === 'number') { @@ -844,6 +845,8 @@ export function registerRobloxShim(lua, opts) { coId: entry.coId, wakeAt: SCHEDULER.now() + result * 1000, }); + } else if (result === false) { + coroutines.delete(entry.coId); } } catch (e) { send('log', { level: 'error', text: `[coroutine ${entry.coId}] ${e?.message || e}` }); diff --git a/src/editor/engine/rbxl-lua-integration.js b/src/editor/engine/rbxl-lua-integration.js index fe0c7f4..4d8c24d 100644 --- a/src/editor/engine/rbxl-lua-integration.js +++ b/src/editor/engine/rbxl-lua-integration.js @@ -141,7 +141,8 @@ export function handleLuaCommand(_scriptId, cmd, payload, runtime) { else if (prop === 'anchored') patch.anchored = value; else if (prop === 'canCollide') patch.canCollide = value; else if (prop === 'opacity') patch.opacity = value; - if (typeof pm.applyPatch === 'function') pm.applyPatch(primId, patch); + if (typeof pm.updateInstance === 'function') pm.updateInstance(primId, patch); + else if (typeof pm.applyPatch === 'function') pm.applyPatch(primId, patch); else if (typeof pm.update === 'function') pm.update(primId, patch); } catch (e) {} return;