diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 9d53d21..07cb736 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -241,9 +241,27 @@ function makeInstanceMethods() { return _instanceMethods; } +// Создаёт stub-signal который ничего не делает — для unknown свойств Instance +// которые скрипты пытаются использовать как сигнал (script.Parent.Selected:Connect). +function makeStubSignal() { + const sig = makeSignal(); + // Помечаем чтобы знать что это stub (для возможной отладки) + sig.__stub = true; + return sig; +} + +// Эвристика: какие имена свойств вероятно сигналы? +// В Roblox сигналы заканчиваются на: Changed, Added, Removed, Began, Ended, +// Clicked, Activated, Touched, Selected, Deselected, Equipped, Unequipped, и т.д. +function isProbablySignalName(prop) { + if (typeof prop !== 'string') return false; + return /^(Mouse|Touch|Input|Render|Step|Heart|Render|On|Char|Player|Selected|Deselect|Equipped|Unequipped|Activated|Click|Changed|Added|Removed|Began|Ended|Died|Spawned|Reached|Loaded|Hover)/.test(prop) + || /(Changed|Added|Removed|Began|Ended|Clicked|Activated|Touched|Died|Loaded|Hover|Connect|Event|Signal|Reached)$/.test(prop); +} + function newInstance(className, name) { const m = makeInstanceMethods(); - return { + const target = { ClassName: className || 'Instance', Name: name || className || 'Instance', Parent: undefined, @@ -269,6 +287,35 @@ function newInstance(className, name) { SetAttribute: m.SetAttribute, GetPropertyChangedSignal: m.GetPropertyChangedSignal, }; + // Proxy: для unknown свойств возвращаем stub чтобы скрипты не падали. + return new Proxy(target, { + get(t, prop) { + if (prop in t) return t[prop]; + if (typeof prop !== 'string') return undefined; + // Системные/wasmoon-внутренние ключи — undefined чтобы wasmoon не путался + if (prop === 'then' || prop === 'catch' || prop === 'toJSON' || + prop === Symbol.toPrimitive || prop.startsWith('__')) { + return undefined; + } + if (isProbablySignalName(prop)) { + const stub = makeStubSignal(); + t[prop] = stub; + return stub; + } + // Иначе — child stub (Folder) который тоже выживет на чтении свойств + // и может иметь свои дочерние stub'ы. Cache в target чтобы тот же ссылочно. + const childStub = newInstance('Folder', prop); + t[prop] = childStub; + return childStub; + }, + set(t, prop, value) { + t[prop] = value; + return true; + }, + has(t, prop) { + return prop in t || (typeof prop === 'string' && !prop.startsWith('__')); + }, + }); } /**