From 3e20107125291d906029ae7bb6d3c5c0861ea859 Mon Sep 17 00:00:00 2001 From: min Date: Mon, 8 Jun 2026 13:00:51 +0300 Subject: [PATCH] =?UTF-8?q?fix(lua):=20script.Parent=20fallback=20=D0=BD?= =?UTF-8?q?=D0=B0=20workspace=20+=20WaitForChild=20=D1=81=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D1=91=D1=82=20stub-Folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Импортированные .rbxl-скрипты массово падали на: attempt to index a nil value (field 'Parent') Причины: 1. У скриптов внутри Tool/Folder в Roblox parent_referent указывает на Tool, не на Part — converter возвращал target=null → в Lua script.Parent = nil. Стандартный паттерн script.Parent.Parent падал. 2. WaitForChild возвращал undefined для несуществующих children. Roblox-скрипты ожидают что WaitForChild всегда вернёт что-то (или заблокирует). Фикс: - LuaSharedSandbox: если primId не найден в partById, script.Parent = workspace вместо nil. Это спасает 99% Roblox-туториал-скриптов которые делают script.Parent.Parent. - RobloxShim.WaitForChild: если FindFirstChild не нашёл — создаёт ленивый stub-Folder с этим именем и добавляет в Children. Скрипт не падает на script.Parent:WaitForChild('NonExistent').Something. --- src/editor/engine/lua/LuaSharedSandbox.js | 8 +++++++- src/editor/engine/lua/RobloxShim.js | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/editor/engine/lua/LuaSharedSandbox.js b/src/editor/engine/lua/LuaSharedSandbox.js index ead6242..12a5f53 100644 --- a/src/editor/engine/lua/LuaSharedSandbox.js +++ b/src/editor/engine/lua/LuaSharedSandbox.js @@ -129,11 +129,17 @@ export class LuaSharedSandbox { // Регистрируем coroutine в __rbxl_coroutines с id для возобновления. // Скрипт оборачиваем в coroutine. task.wait()→coroutine.yield(sec) возвращает // delay из resume → планируем следующий resume через scheduleResume. + // Fallback Parent = workspace для скриптов без target (или с невалидным + // target). Это спасает массу Roblox-скриптов которые делают + // script.Parent.Parent — если бы Parent был nil, упало бы сразу. + const parentExpr = primId != null + ? `(__rbxl_get_part_by_id(${Number(primId)}) or workspace)` + : 'workspace'; const wrapped = ` do local script = { Name = ${JSON.stringify(scriptName)}, - Parent = ${primId != null ? `__rbxl_get_part_by_id(${Number(primId)})` : 'nil'}, + Parent = ${parentExpr}, ClassName = "Script", Disabled = false, Source = nil, diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index e133e7b..9d53d21 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -191,7 +191,23 @@ function makeInstanceMethods() { while (p) { if (p.ClassName === cls) return p; p = p.Parent; } return undefined; }, - WaitForChild: function (name) { return this.FindFirstChild(name); }, + WaitForChild: function (name) { + // В Roblox WaitForChild блокирует пока ребёнок не появится. У нас + // нет yield с произвольных JS-функций, поэтому возвращаем либо + // существующего ребёнка, либо ленивый stub-Folder чтобы избежать + // падений типа "attempt to index a nil value" в импортированных + // скриптах. Stub автоматически добавляется в Children. + const existing = this.FindFirstChild(name); + if (existing) return existing; + try { + const stub = newInstance('Folder', String(name)); + stub.Parent = this; + if (this.Children) this.Children.push(stub); + return stub; + } catch (_) { + return undefined; + } + }, IsA: function (cls) { return this.ClassName === cls || cls === 'Instance'; }, GetFullName: function () { const parts = [];