From 59d0d8681175c3eb9f13b0a5657ce25d55226be0 Mon Sep 17 00:00:00 2001 From: min Date: Mon, 8 Jun 2026 13:04:38 +0300 Subject: [PATCH] =?UTF-8?q?fix(lua):=20Proxy=20=D0=B4=D0=BB=D1=8F=20Instan?= =?UTF-8?q?ce=20=E2=80=94=20unknown=20=D1=81=D0=B2=D0=BE=D0=B9=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0=D1=89=D0=B0?= =?UTF-8?q?=D1=8E=D1=82=20stub=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20ni?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Импортированные Roblox-скрипты массово падали на доступе к свойствам которых у нас нет (.Selected, .Equipped, .MouseEnter и т.д.). В Roblox это сигналы которые скрипты подключают через :Connect(). Фикс: оборачиваю newInstance() в Proxy: - isProbablySignalName(prop) → возвращает makeStubSignal() (наш Signal с Connect/Fire) - иначе → возвращает stub-Folder (тоже Instance с потенциальными children) - системные ключи (then, __*, Symbol) → undefined чтобы wasmoon не путался Эвристика покрывает основные Roblox-паттерны: - *.Changed, *.Added, *.Removed, *.Began, *.Ended, *.Touched, *.Died - Mouse*, Touch*, Input*, Render*, Step*, Heart*, On*, Char*, Player* - Selected, Deselected, Equipped, Unequipped, Activated, Reached, Loaded Это позволяет проходить инициализацию массе туториал-скриптов вместо падения на первой же строке. --- src/editor/engine/lua/RobloxShim.js | 49 ++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) 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('__')); + }, + }); } /**