feat(rbxl): XML-������ ������ .rbxl + Day/Night + Tool/Mouse/Backpack flow #38

Closed
min wants to merge 39 commits from feat/rbxl-xml-parser-import into main
Showing only changes of commit d750c94a78 - Show all commits

View File

@ -25,6 +25,11 @@ const SCHEDULER = {
const HEARTBEAT_SIGNAL = makeSignal(); const HEARTBEAT_SIGNAL = makeSignal();
const STEPPED_SIGNAL = makeSignal(); const STEPPED_SIGNAL = makeSignal();
// Глобальный helper для запуска Lua-handler'ов в собственной coroutine.
// Без этого Roblox-обработчики которые внутри делают wait() падают с
// "attempt to yield across a C-call boundary".
let _runHandlerInCoroutine = null;
function makeSignal() { function makeSignal() {
const sig = { const sig = {
__isSignal: true, __isSignal: true,
@ -45,9 +50,16 @@ function makeSignal() {
sig.connect = sig.Connect; sig.connect = sig.Connect;
sig.Fire = function (...args) { sig.Fire = function (...args) {
for (const fn of [...sig.connections]) { for (const fn of [...sig.connections]) {
try { fn(...args); } catch (e) { // Запускаем handler в его собственной coroutine — это позволяет
// eslint-disable-next-line no-console // делать wait() внутри без yield-across-C-boundary ошибки.
console.error('[Signal handler]', e); if (_runHandlerInCoroutine) {
try { _runHandlerInCoroutine(fn, args); } catch (e) {
console.error('[Signal handler]', e);
}
} else {
try { fn(...args); } catch (e) {
console.error('[Signal handler]', e);
}
} }
} }
}; };
@ -1389,7 +1401,29 @@ export function registerRobloxShim(lua, opts) {
if type(ret) == 'number' then return ret end if type(ret) == 'number' then return ret end
return 0 return 0
end end
-- Запуск Lua-handler'а в собственной coroutine.
-- Используется при Fire сигнала из JS иначе wait() внутри handler'а
-- падает с 'attempt to yield across a C-call boundary'.
__rbxl_next_handler_id = 0
function __rbxl_run_in_coroutine(fn, a1, a2, a3, a4)
__rbxl_next_handler_id = __rbxl_next_handler_id + 1
local handlerId = "handler_" .. __rbxl_next_handler_id
local co = coroutine.create(function() fn(a1, a2, a3, a4) end)
__rbxl_register_coroutine(handlerId, co)
local ok, ret = coroutine.resume(co)
if not ok then
__rbxl_send_error(handlerId, tostring(ret))
__rbxl_unregister_coroutine(handlerId)
elseif type(ret) == 'number' then
__rbxl_schedule_resume(handlerId, ret)
elseif coroutine.status(co) == 'dead' then
__rbxl_unregister_coroutine(handlerId)
end
end
`); `);
// Кешируем ссылку на функцию для использования из makeSignal
_runHandlerInCoroutine = lua.global.get('__rbxl_run_in_coroutine');
// Добавим Lua-side helper для лога // Добавим Lua-side helper для лога
global.set('__log', (level, text) => { global.set('__log', (level, text) => {
send('log', { level: String(level || 'info'), text: String(text || '') }); send('log', { level: String(level || 'info'), text: String(text || '') });