feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39
@ -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'а намеренно поглощаются.
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user