feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39
@ -62,6 +62,11 @@ ContextActionResult, UserInputState, BorderMode, FormFactor.
|
|||||||
аргумента раньше делал tight loop без yield → WASM stack overflow
|
аргумента раньше делал tight loop без yield → WASM stack overflow
|
||||||
("memory access out of bounds").
|
("memory access out of bounds").
|
||||||
|
|
||||||
|
- **Уважаем `enabled: false`** в Roblox-метадате. Roblox-скрипты с
|
||||||
|
`Disabled = true` — это шаблоны для клонирования (`script.Clean:Clone()`),
|
||||||
|
не должны запускаться при старте. `parseRobloxLuaMeta()` парсит JSON
|
||||||
|
из второй строки packed-кода, при `enabled=false` скрипт идёт в `rbxlSkipped`.
|
||||||
|
|
||||||
### Надо ли портировать в JS-движок?
|
### Надо ли портировать в JS-движок?
|
||||||
|
|
||||||
✅ **Да, всё** — это базовый Roblox-совместимый API, который должен работать
|
✅ **Да, всё** — это базовый Roblox-совместимый API, который должен работать
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { ScriptSandbox } from './ScriptSandbox';
|
|||||||
import { STORYS_addres } from '../../api/API';
|
import { STORYS_addres } from '../../api/API';
|
||||||
import { PhysicsWorld } from './PhysicsWorld';
|
import { PhysicsWorld } from './PhysicsWorld';
|
||||||
import { LabelManager } from './LabelManager';
|
import { LabelManager } from './LabelManager';
|
||||||
import { handleLuaCommand, unpackRobloxLuaCode } from './rbxl-lua-integration.js';
|
import { handleLuaCommand, unpackRobloxLuaCode, parseRobloxLuaMeta } from './rbxl-lua-integration.js';
|
||||||
import { LuaSharedSandbox } from './lua/LuaSharedSandbox.js';
|
import { LuaSharedSandbox } from './lua/LuaSharedSandbox.js';
|
||||||
|
|
||||||
export class GameRuntime {
|
export class GameRuntime {
|
||||||
@ -128,6 +128,11 @@ export class GameRuntime {
|
|||||||
for (const s of scripts) {
|
for (const s of scripts) {
|
||||||
if (s && typeof s.code === 'string' && s.code.startsWith('// @roblox-lua')) {
|
if (s && typeof s.code === 'string' && s.code.startsWith('// @roblox-lua')) {
|
||||||
if (!runImportedRbxl) { rbxlSkipped++; continue; }
|
if (!runImportedRbxl) { rbxlSkipped++; continue; }
|
||||||
|
// Уважаем поле enabled=false из Roblox-метадаты: такие скрипты
|
||||||
|
// были disabled-шаблоны (для клонирования через :Clone()), их
|
||||||
|
// запуск немедленно крашит coroutine (WASM access out of bounds).
|
||||||
|
const meta = parseRobloxLuaMeta(s.code);
|
||||||
|
if (meta && meta.enabled === false) { rbxlSkipped++; continue; }
|
||||||
const luaSource = unpackRobloxLuaCode(s.code);
|
const luaSource = unpackRobloxLuaCode(s.code);
|
||||||
if (luaSource && luaSource.trim()) {
|
if (luaSource && luaSource.trim()) {
|
||||||
luaUserBatch.push({
|
luaUserBatch.push({
|
||||||
|
|||||||
@ -20,6 +20,20 @@ export function unpackRobloxLuaCode(code) {
|
|||||||
return code.slice(start, closeIdx);
|
return code.slice(start, closeIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Парсит JSON-метадату из 2-й строки packed-кода (`// {"roblox_class":..., "enabled": true}`). */
|
||||||
|
export function parseRobloxLuaMeta(code) {
|
||||||
|
if (typeof code !== 'string') return null;
|
||||||
|
const lines = code.split('\n');
|
||||||
|
if (lines.length < 2) return null;
|
||||||
|
const metaLine = lines[1];
|
||||||
|
if (!metaLine.startsWith('// ')) return null;
|
||||||
|
try {
|
||||||
|
return JSON.parse(metaLine.slice(3));
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Сцена → snap для shim'а (workspace:GetChildren). */
|
/** Сцена → snap для shim'а (workspace:GetChildren). */
|
||||||
export function buildLuaSceneSnap(primitives) {
|
export function buildLuaSceneSnap(primitives) {
|
||||||
const out = { primitives: {} };
|
const out = { primitives: {} };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user