diff --git a/src/editor/engine/lua/LuaSharedSandbox.js b/src/editor/engine/lua/LuaSharedSandbox.js index 4906272..4c12ef2 100644 --- a/src/editor/engine/lua/LuaSharedSandbox.js +++ b/src/editor/engine/lua/LuaSharedSandbox.js @@ -175,23 +175,37 @@ export class LuaSharedSandbox { } const wrapped = ` do - local script = { + -- Если parentExpr вернул primitive — у него уже есть :FindFirstChild и пр. + -- Если ничего не вернёт — workspace (всегда валидный). + -- script.Parent.Parent (Tool.Parent = StarterPack / Backpack / workspace). + local _scriptParent = ${parentExpr} + if _scriptParent == nil then _scriptParent = workspace end + if _scriptParent.Parent == nil then _scriptParent.Parent = workspace end + local script = setmetatable({ Name = ${JSON.stringify(scriptName)}, - Parent = ${parentExpr}, + Parent = _scriptParent, ClassName = "Script", Disabled = false, Source = nil, - } + }, { + -- Любой доступ к несуществующему полю → workspace + -- (на случай script.Foo:Bar() в старом коде) + __index = function(t, k) + if k == "FindFirstChild" or k == "WaitForChild" or k == "GetChildren" then + return function() return nil end + end + return workspace[k] + end, + }) local co = coroutine.create(function() - -- WATCHDOG: каждые 50000 инструкций — yield 1 кадр. - -- Защищает от tight-loop типа: - -- while not parent:FindFirstChild(name) do - -- parent.ChildAdded:wait() - -- end - -- где наш stub :wait() возвращает сразу. + -- WATCHDOG: каждые 100000 инструкций — yield 1 кадр. + -- Защищает от tight-loop. yield обёрнут в pcall так как + -- внутри C-call boundary yield бросает ошибку — но в этом + -- случае tight-loop наш hook просто будет вызываться позже + -- (когда Lua вернётся из C-call) и yield сработает. debug.sethook(function() - coroutine.yield(0.016) - end, "", 50000) + pcall(coroutine.yield, 0.016) + end, "", 100000) -- pcall защищает от runtime-ошибок которые иначе крашат -- coroutine и могут повредить WASM-стейт. Возвраты -- handler'а намеренно поглощаются. diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 3c9fbe1..114676e 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -1247,6 +1247,15 @@ export function registerRobloxShim(lua, opts) { inst.FireServer = function (...a) { this.OnServerEvent.Fire(localPlayer, ...a); }; inst.FireClient = function (_p, ...a) { this.OnClientEvent.Fire(...a); }; inst.FireAllClients = function (...a) { this.OnClientEvent.Fire(...a); }; + } else if (className === 'SpecialMesh' || className === 'BlockMesh' + || className === 'CylinderMesh' || className === 'FileMesh') { + inst = newInstance(className, className); + inst.MeshType = { Name: 'Brick', Value: 0 }; + inst.MeshId = ''; + inst.TextureId = ''; + inst.Scale = new RbxVector3(1, 1, 1); + inst.Offset = new RbxVector3(0, 0, 0); + inst.VertexColor = new RbxVector3(1, 1, 1); } else if (className === 'BindableEvent') { inst = newInstance('BindableEvent', 'BindableEvent'); inst.Event = makeSignal(); @@ -1588,6 +1597,29 @@ export function registerRobloxShim(lua, opts) { end wait = rbx_wait + -- Roblox legacy globals + tick = function() return os.time() end -- секунды с epoch + time = function() return os.clock() * 1000 end -- ms аптайм + delay = function(sec, fn) -- delay(sec, fn) — задержка + вызов + if type(fn) ~= 'function' then return end + local co = coroutine.create(function() + rbx_wait(sec or 0) + pcall(fn) + end) + coroutine.resume(co) + end + spawn = function(fn) -- spawn(fn) — запуск в отдельной coroutine + if type(fn) ~= 'function' then return end + local co = coroutine.create(function() pcall(fn) end) + coroutine.resume(co) + end + -- LoadLibrary("RbxStamper"/"RbxUtility") — старый Roblox 2009. + -- Возвращаем пустую таблицу-стаб чтобы скрипт не упал. + LoadLibrary = function(name) + return setmetatable({}, { __index = function() return function() end end }) + end + require = require or function(_) return {} end + function __rbxl_resume_co(co) if not co or coroutine.status(co) ~= 'suspended' then return nil end local ok, ret = coroutine.resume(co) @@ -1609,10 +1641,12 @@ export function registerRobloxShim(lua, opts) { -- что приводит к wasmoon promise-detection crash). pcall возвращает -- (ok, ret1, ret2, ...) — мы их не используем. local co = coroutine.create(function() - -- Тот же watchdog что и в _startSingleScript + -- Тот же watchdog что и в _startSingleScript. + -- yield обёрнут в pcall: внутри C-call boundary yield бросает + -- ошибку, но hook будет вызван позже когда Lua вернётся. debug.sethook(function() - coroutine.yield(0.016) - end, "", 50000) + pcall(coroutine.yield, 0.016) + end, "", 100000) pcall(fn, a1, a2, a3, a4) end) __rbxl_register_coroutine(handlerId, co)