feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39
@ -175,23 +175,37 @@ export class LuaSharedSandbox {
|
|||||||
}
|
}
|
||||||
const wrapped = `
|
const wrapped = `
|
||||||
do
|
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)},
|
Name = ${JSON.stringify(scriptName)},
|
||||||
Parent = ${parentExpr},
|
Parent = _scriptParent,
|
||||||
ClassName = "Script",
|
ClassName = "Script",
|
||||||
Disabled = false,
|
Disabled = false,
|
||||||
Source = nil,
|
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()
|
local co = coroutine.create(function()
|
||||||
-- WATCHDOG: каждые 50000 инструкций — yield 1 кадр.
|
-- WATCHDOG: каждые 100000 инструкций — yield 1 кадр.
|
||||||
-- Защищает от tight-loop типа:
|
-- Защищает от tight-loop. yield обёрнут в pcall так как
|
||||||
-- while not parent:FindFirstChild(name) do
|
-- внутри C-call boundary yield бросает ошибку — но в этом
|
||||||
-- parent.ChildAdded:wait()
|
-- случае tight-loop наш hook просто будет вызываться позже
|
||||||
-- end
|
-- (когда Lua вернётся из C-call) и yield сработает.
|
||||||
-- где наш stub :wait() возвращает сразу.
|
|
||||||
debug.sethook(function()
|
debug.sethook(function()
|
||||||
coroutine.yield(0.016)
|
pcall(coroutine.yield, 0.016)
|
||||||
end, "", 50000)
|
end, "", 100000)
|
||||||
-- pcall защищает от runtime-ошибок которые иначе крашат
|
-- pcall защищает от runtime-ошибок которые иначе крашат
|
||||||
-- coroutine и могут повредить WASM-стейт. Возвраты
|
-- coroutine и могут повредить WASM-стейт. Возвраты
|
||||||
-- handler'а намеренно поглощаются.
|
-- handler'а намеренно поглощаются.
|
||||||
|
|||||||
@ -1247,6 +1247,15 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
inst.FireServer = function (...a) { this.OnServerEvent.Fire(localPlayer, ...a); };
|
inst.FireServer = function (...a) { this.OnServerEvent.Fire(localPlayer, ...a); };
|
||||||
inst.FireClient = function (_p, ...a) { this.OnClientEvent.Fire(...a); };
|
inst.FireClient = function (_p, ...a) { this.OnClientEvent.Fire(...a); };
|
||||||
inst.FireAllClients = function (...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') {
|
} else if (className === 'BindableEvent') {
|
||||||
inst = newInstance('BindableEvent', 'BindableEvent');
|
inst = newInstance('BindableEvent', 'BindableEvent');
|
||||||
inst.Event = makeSignal();
|
inst.Event = makeSignal();
|
||||||
@ -1588,6 +1597,29 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
end
|
end
|
||||||
wait = rbx_wait
|
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)
|
function __rbxl_resume_co(co)
|
||||||
if not co or coroutine.status(co) ~= 'suspended' then return nil end
|
if not co or coroutine.status(co) ~= 'suspended' then return nil end
|
||||||
local ok, ret = coroutine.resume(co)
|
local ok, ret = coroutine.resume(co)
|
||||||
@ -1609,10 +1641,12 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
-- что приводит к wasmoon promise-detection crash). pcall возвращает
|
-- что приводит к wasmoon promise-detection crash). pcall возвращает
|
||||||
-- (ok, ret1, ret2, ...) — мы их не используем.
|
-- (ok, ret1, ret2, ...) — мы их не используем.
|
||||||
local co = coroutine.create(function()
|
local co = coroutine.create(function()
|
||||||
-- Тот же watchdog что и в _startSingleScript
|
-- Тот же watchdog что и в _startSingleScript.
|
||||||
|
-- yield обёрнут в pcall: внутри C-call boundary yield бросает
|
||||||
|
-- ошибку, но hook будет вызван позже когда Lua вернётся.
|
||||||
debug.sethook(function()
|
debug.sethook(function()
|
||||||
coroutine.yield(0.016)
|
pcall(coroutine.yield, 0.016)
|
||||||
end, "", 50000)
|
end, "", 100000)
|
||||||
pcall(fn, a1, a2, a3, a4)
|
pcall(fn, a1, a2, a3, a4)
|
||||||
end)
|
end)
|
||||||
__rbxl_register_coroutine(handlerId, co)
|
__rbxl_register_coroutine(handlerId, co)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user