From 38d135586b70a7b2873bb843fcd9ebab0c7f7d8d Mon Sep 17 00:00:00 2001 From: min Date: Mon, 8 Jun 2026 20:51:24 +0300 Subject: [PATCH] =?UTF-8?q?fix(rbxl):=20watchdog=20100k=E2=86=9220k=20+=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=D1=82=20pcall(yield)=20+=20batch=205+2?= =?UTF-8?q?0ms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Прошлый коммит pcall(coroutine.yield) дал бесконечный цикл: yield внутри C-call падал → pcall ловил → hook возвращался → счётчик не сбросился → срабатывал опять моментально → вис. Новая стратегия: 1. Голый coroutine.yield в watchdog: если внутри C-call упадёт с ошибкой — pcall(fn,...) внутри coroutine её поймает, скрипт завершится. Лучше чем вис. 2. Frequency 100k→20k инструкций — yield чаще, меньше времени на tight-loop перед уступкой управления UI. 3. Batch kickoff 20→5 скриптов с delay 20мс (было 0). 55 скриптов ROBLOX Battle = ~200мс распределено, UI отзывается. Page-hang при init должен исчезнуть. Скрипты с tight-loop типа WaitForChild через ChildAdded:wait() упадут с ошибкой про yield, но не повесят страницу. --- src/editor/engine/lua/LuaSharedSandbox.js | 16 +++++++--------- src/editor/engine/lua/RobloxShim.js | 6 ++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/editor/engine/lua/LuaSharedSandbox.js b/src/editor/engine/lua/LuaSharedSandbox.js index 4c12ef2..7883bbd 100644 --- a/src/editor/engine/lua/LuaSharedSandbox.js +++ b/src/editor/engine/lua/LuaSharedSandbox.js @@ -115,8 +115,8 @@ export class LuaSharedSandbox { // Запускаем main-loop сразу — он начнёт tick'ать как только будут coroutines. this._lastTickAt = performance.now(); this._startMainLoop(); - // Init батчами по 20 с yield между ними, чтобы UI не подвисал на 700+ скриптах. - const BATCH_SIZE = 20; + // Init батчами по 5 с задержкой 20мс между ними, чтобы UI отзывался. + const BATCH_SIZE = 5; let idx = 0; const initBatch = () => { if (this._isStopped) return; @@ -130,7 +130,7 @@ export class LuaSharedSandbox { } idx = end; if (idx < pending.length) { - setTimeout(initBatch, 0); + setTimeout(initBatch, 20); } else { // eslint-disable-next-line no-console console.log(`[LuaSharedSandbox] all ${pending.length} scripts kicked off`); @@ -199,13 +199,11 @@ export class LuaSharedSandbox { }) local co = coroutine.create(function() -- WATCHDOG: каждые 100000 инструкций — yield 1 кадр. - -- Защищает от tight-loop. yield обёрнут в pcall так как - -- внутри C-call boundary yield бросает ошибку — но в этом - -- случае tight-loop наш hook просто будет вызываться позже - -- (когда Lua вернётся из C-call) и yield сработает. + -- НЕ оборачиваем в pcall — внутри C-call boundary yield + -- упадёт ошибкой, что прервёт скрипт. Это лучше чем виснуть. debug.sethook(function() - pcall(coroutine.yield, 0.016) - end, "", 100000) + coroutine.yield(0.016) + end, "", 20000) -- pcall защищает от runtime-ошибок которые иначе крашат -- coroutine и могут повредить WASM-стейт. Возвраты -- handler'а намеренно поглощаются. diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 114676e..5cc004c 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -1642,11 +1642,9 @@ export function registerRobloxShim(lua, opts) { -- (ok, ret1, ret2, ...) — мы их не используем. local co = coroutine.create(function() -- Тот же watchdog что и в _startSingleScript. - -- yield обёрнут в pcall: внутри C-call boundary yield бросает - -- ошибку, но hook будет вызван позже когда Lua вернётся. debug.sethook(function() - pcall(coroutine.yield, 0.016) - end, "", 100000) + coroutine.yield(0.016) + end, "", 20000) pcall(fn, a1, a2, a3, a4) end) __rbxl_register_coroutine(handlerId, co)