fix(lua): require() no-op + улучшенный Proxy для Instance
1. require(): в Roblox загружает ModuleScript. У нас модулей нет —
возвращаем mod как есть (если объект) или undefined.
2. Proxy улучшения:
- Object.hasOwnProperty + 'in' checks: методы (WaitForChild,
FindFirstChild и т.д.) точно не перехватываются;
- Symbol-ключи всегда undefined;
- System keys (then, catch, toString, constructor) → undefined
чтобы wasmoon не пытался обращаться как с Promise/класс;
- has() возвращает true для всех строковых ключей (избавляет от
падений на 'if obj.SomeField then ...').
This commit is contained in:
parent
59d0d86811
commit
dc7420a61d
@ -250,6 +250,21 @@ function makeStubSignal() {
|
||||
return sig;
|
||||
}
|
||||
|
||||
// Callable proxy: сам вызывается как function (ничего не делает), также имеет
|
||||
// поля Connect/Disconnect и Fire/fire — то есть выглядит и как метод, и как
|
||||
// сигнал, и как объект. Используется для unknown method-like свойств.
|
||||
function makeStubCallable() {
|
||||
const fn = function () { return undefined; };
|
||||
fn.__stub = true;
|
||||
fn.Connect = function () { return { Disconnect: () => {}, disconnect: () => {}, Connected: false }; };
|
||||
fn.connect = fn.Connect;
|
||||
fn.Fire = function () {};
|
||||
fn.fire = fn.Fire;
|
||||
fn.Wait = function () { return null; };
|
||||
fn.wait = fn.Wait;
|
||||
return fn;
|
||||
}
|
||||
|
||||
// Эвристика: какие имена свойств вероятно сигналы?
|
||||
// В Roblox сигналы заканчиваются на: Changed, Added, Removed, Began, Ended,
|
||||
// Clicked, Activated, Touched, Selected, Deselected, Equipped, Unequipped, и т.д.
|
||||
@ -290,30 +305,39 @@ function newInstance(className, name) {
|
||||
// Proxy: для unknown свойств возвращаем stub чтобы скрипты не падали.
|
||||
return new Proxy(target, {
|
||||
get(t, prop) {
|
||||
if (prop in t) return t[prop];
|
||||
// Существующее свойство всегда возвращаем как есть (включая методы)
|
||||
if (Object.prototype.hasOwnProperty.call(t, prop) || prop in t) {
|
||||
return t[prop];
|
||||
}
|
||||
// Не-строки и Symbol.* — undefined чтобы wasmoon не путался
|
||||
if (typeof prop !== 'string') return undefined;
|
||||
// Системные/wasmoon-внутренние ключи — undefined чтобы wasmoon не путался
|
||||
if (prop === 'then' || prop === 'catch' || prop === 'toJSON' ||
|
||||
prop === Symbol.toPrimitive || prop.startsWith('__')) {
|
||||
// wasmoon JS-internal ключи — undefined
|
||||
if (prop === 'then' || prop === 'catch' || prop === 'finally' ||
|
||||
prop === 'toJSON' || prop === 'toString' || prop === 'valueOf' ||
|
||||
prop === 'constructor' || prop === 'prototype' ||
|
||||
prop.startsWith('__') || prop.startsWith('Symbol')) {
|
||||
return undefined;
|
||||
}
|
||||
// Эвристика: имена сигналов → stub-сигнал. Иначе — stub-Folder.
|
||||
// Stub-Folder сам по себе callable (на случай если скрипт его вызовет
|
||||
// как функцию: `foo()` вместо `foo:Connect()` — оба не падают).
|
||||
let stub;
|
||||
if (isProbablySignalName(prop)) {
|
||||
const stub = makeStubSignal();
|
||||
t[prop] = stub;
|
||||
return stub;
|
||||
stub = makeStubSignal();
|
||||
} else {
|
||||
stub = newInstance('Folder', prop);
|
||||
}
|
||||
// Иначе — child stub (Folder) который тоже выживет на чтении свойств
|
||||
// и может иметь свои дочерние stub'ы. Cache в target чтобы тот же ссылочно.
|
||||
const childStub = newInstance('Folder', prop);
|
||||
t[prop] = childStub;
|
||||
return childStub;
|
||||
t[prop] = stub;
|
||||
return stub;
|
||||
},
|
||||
set(t, prop, value) {
|
||||
t[prop] = value;
|
||||
return true;
|
||||
},
|
||||
has(t, prop) {
|
||||
return prop in t || (typeof prop === 'string' && !prop.startsWith('__'));
|
||||
// Для in-проверок отвечаем true почти всегда (чтобы Lua не падал на
|
||||
// условиях вроде if obj.SomeField then ...)
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -485,6 +509,16 @@ export function registerRobloxShim(lua, opts) {
|
||||
send('log', { level: 'warn', text: args.map(stringify).join('\t') });
|
||||
});
|
||||
|
||||
// require(ModuleScript) — в Roblox загружает модуль. У нас модулей нет —
|
||||
// возвращаем undefined (Lua nil) чтобы скрипты типа local mod = require(...)
|
||||
// не падали. require строкой (стандартный Lua) перехватывать не будем.
|
||||
global.set('require', (mod) => {
|
||||
// Если передали Instance-stub — возвращаем сам stub (чтобы хоть
|
||||
// что-то можно было сделать с возвращённым значением).
|
||||
if (mod && typeof mod === 'object') return mod;
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// === task.* + wait ===
|
||||
// task.wait/wait — реальный yield через coroutines. Юзер пишет:
|
||||
// while true do part.Position = ... ; task.wait(0.1) end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user