feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39

Merged
min merged 215 commits from feat/lua-50-games-bundle into main 2026-06-09 21:59:25 +00:00
4 changed files with 41 additions and 9 deletions
Showing only changes of commit dbfd214f42 - Show all commits

View File

@ -113,18 +113,28 @@ class CFrame:
matrix: tuple # (r00, r01, r02, r10, r11, r12, r20, r21, r22) matrix: tuple # (r00, r01, r02, r10, r11, r12, r20, r21, r22)
def to_euler_xyz(self) -> tuple: def to_euler_xyz(self) -> tuple:
"""Конверт 3x3 rotation matrix в Euler XYZ (radians). """Конверт 3x3 rotation matrix в Euler YXZ (Babylon convention).
Использует стандартную intrinsic XYZ rotation extraction: Babylon mesh.rotation = Vector3(rx, ry, rz) применяется в порядке YXZ
Rx = atan2(r21, r22) (rotate Y first, then X, then Z). Чтобы извлечь Euler из матрицы под
Ry = atan2(-r20, sqrt(r21² + r22²)) этот convention, используем формулу YXZ-extraction:
Rz = atan2(r10, r00) Rx = asin(-r12)
Ry = atan2(r02, r22)
Rz = atan2(r10, r11)
(имя метода to_euler_xyz сохраняем для совместимости вызовов.)
""" """
import math import math
r00, r01, r02, r10, r11, r12, r20, r21, r22 = self.matrix r00, r01, r02, r10, r11, r12, r20, r21, r22 = self.matrix
rx = math.atan2(r21, r22) # Edge case: r12 близко к ±1 (gimbal lock на X = ±90°)
ry = math.atan2(-r20, math.sqrt(r21*r21 + r22*r22)) clamped = max(-1.0, min(1.0, -r12))
rz = math.atan2(r10, r00) rx = math.asin(clamped)
if abs(clamped) > 0.99999:
# Gimbal lock — z = 0, y = atan2(-r20, r00)
ry = math.atan2(-r20, r00)
rz = 0.0
else:
ry = math.atan2(r02, r22)
rz = math.atan2(r10, r11)
return (rx, ry, rz) return (rx, ry, rz)

View File

@ -183,6 +183,15 @@ export class LuaSharedSandbox {
Source = nil, Source = nil,
} }
local co = coroutine.create(function() local co = coroutine.create(function()
-- WATCHDOG: каждые 50000 инструкций yield 1 кадр.
-- Защищает от tight-loop типа:
-- while not parent:FindFirstChild(name) do
-- parent.ChildAdded:wait()
-- end
-- где наш stub :wait() возвращает сразу.
debug.sethook(function()
coroutine.yield(0.016)
end, "", 50000)
-- pcall защищает от runtime-ошибок которые иначе крашат -- pcall защищает от runtime-ошибок которые иначе крашат
-- coroutine и могут повредить WASM-стейт. Возвраты -- coroutine и могут повредить WASM-стейт. Возвраты
-- handler'а намеренно поглощаются. -- handler'а намеренно поглощаются.

View File

@ -58,7 +58,10 @@ function makeSignal() {
} }
}; };
sig.fire = sig.Fire; sig.fire = sig.Fire;
sig.Wait = () => undefined; // Wait() возвращает -1 как маркер "yield 1 кадр" — наш Lua-prelude
// оборачивает все Signal:Wait через __rbxl_signal_wait который при
// получении -1 делает rbx_wait(0.05) (yield в coroutine).
sig.Wait = () => -1;
sig.wait = sig.Wait; sig.wait = sig.Wait;
return sig; return sig;
} }
@ -1385,6 +1388,12 @@ export function registerRobloxShim(lua, opts) {
local ret = coroutine.yield(sec) local ret = coroutine.yield(sec)
return ret or sec return ret or sec
end end
-- Глобальный безопасный yield для любых stub-сигналов / любых
-- "ждунов". Используется в Lua-обёртках вокруг WaitForChild и т.п.
function __rbxl_yield_frame()
coroutine.yield(0.05)
end
if type(task) == 'table' then if type(task) == 'table' then
task.wait = rbx_wait task.wait = rbx_wait
else else
@ -1413,6 +1422,10 @@ 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
debug.sethook(function()
coroutine.yield(0.016)
end, "", 50000)
pcall(fn, a1, a2, a3, a4) pcall(fn, a1, a2, a3, a4)
end) end)
__rbxl_register_coroutine(handlerId, co) __rbxl_register_coroutine(handlerId, co)