feat(lua): включил импортированные скрипты + защита от подвисания
1. TweenInfo.new(time, easing, direction, repeat, reverses, delay) — глобальный конструктор. Был причиной 'attempt to index nil (TweenInfo)'. 2. Расширенный Enum: InfoType, SortOrder, FillDirection, Font, TextXAlignment, ScaleType, PartType, SurfaceType, UserInputState и др. — типичные для Roblox-туториалов. 3. NumberSequence/ColorSequence/NumberRange/Rect — заглушки конструкторов. 4. _kickoff() теперь батчами по 20 скриптов через setTimeout(0). 742 скрипта инициализируются за ~37 фреймов вместо одного синхронного блока, UI не подвисает. 5. Импортированные .rbxl-скрипты ВКЛЮЧЕНЫ по умолчанию (window.__RBXL_SKIP_IMPORTED=true чтобы выключить). Падения отдельных скриптов изолированы — tickScheduler ловит ошибки и удаляет битые coroutines, остальные продолжают работать.
This commit is contained in:
parent
c5b713fd1f
commit
5342c079d1
@ -121,12 +121,11 @@ export class GameRuntime {
|
|||||||
// скрипты (с маркером // @roblox-lua) теперь идут через ОДИН LuaSharedSandbox.
|
// скрипты (с маркером // @roblox-lua) теперь идут через ОДИН LuaSharedSandbox.
|
||||||
// .rbxl-скрипты распаковываем из JS-комментария-обёртки в чистый Lua.
|
// .rbxl-скрипты распаковываем из JS-комментария-обёртки в чистый Lua.
|
||||||
const luaUserBatch = [];
|
const luaUserBatch = [];
|
||||||
// По умолчанию импортированные .rbxl-скрипты НЕ выполняются:
|
// Импортированные .rbxl-скрипты выполняются по умолчанию (батчами по 20
|
||||||
// - типичная Roblox-карта = 500-2000 скриптов, многие используют
|
// через setTimeout, чтобы не подвешивать UI). Падения скриптов изолированы
|
||||||
// DataStore/Tool/PlayerGui/UserInputService, которых у нас нет;
|
// pcall'ом в shim — один битый скрипт не валит остальных.
|
||||||
// - даже с stub'ами сотни падений → tab подвисает;
|
// Выключить можно через window.__RBXL_SKIP_IMPORTED = true.
|
||||||
// Юзер может включить через window.__RBXL_RUN_IMPORTED = true в консоли.
|
const runImportedRbxl = !(typeof window !== 'undefined' && window.__RBXL_SKIP_IMPORTED === true);
|
||||||
const runImportedRbxl = typeof window !== 'undefined' && window.__RBXL_RUN_IMPORTED === true;
|
|
||||||
let rbxlSkipped = 0;
|
let rbxlSkipped = 0;
|
||||||
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')) {
|
||||||
@ -221,7 +220,7 @@ export class GameRuntime {
|
|||||||
this._log('info', `Запущено Roblox-Lua скриптов (импортированных): ${rbxlImported}`);
|
this._log('info', `Запущено Roblox-Lua скриптов (импортированных): ${rbxlImported}`);
|
||||||
}
|
}
|
||||||
if (rbxlSkipped > 0) {
|
if (rbxlSkipped > 0) {
|
||||||
this._log('info', `Импортированных .rbxl-скриптов пропущено: ${rbxlSkipped}. Включить: window.__RBXL_RUN_IMPORTED = true`);
|
this._log('info', `Импортированных .rbxl-скриптов пропущено: ${rbxlSkipped}. Вернуть: убрать window.__RBXL_SKIP_IMPORTED`);
|
||||||
}
|
}
|
||||||
if (luaWritten > 0) {
|
if (luaWritten > 0) {
|
||||||
this._log('info', `Запущено Lua-скриптов юзера: ${luaWritten}`);
|
this._log('info', `Запущено Lua-скриптов юзера: ${luaWritten}`);
|
||||||
|
|||||||
@ -109,10 +109,33 @@ export class LuaSharedSandbox {
|
|||||||
this._isKickedOff = true;
|
this._isKickedOff = true;
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`[LuaSharedSandbox] kickoff: starting ${this._pendingScripts.length} scripts`);
|
console.log(`[LuaSharedSandbox] kickoff: starting ${this._pendingScripts.length} scripts`);
|
||||||
for (const entry of this._pendingScripts) this._startSingleScript(entry);
|
const pending = this._pendingScripts;
|
||||||
this._pendingScripts = [];
|
this._pendingScripts = [];
|
||||||
|
// Запускаем main-loop сразу — он начнёт tick'ать как только будут coroutines.
|
||||||
this._lastTickAt = performance.now();
|
this._lastTickAt = performance.now();
|
||||||
this._startMainLoop();
|
this._startMainLoop();
|
||||||
|
// Init батчами по 20 с yield между ними, чтобы UI не подвисал на 700+ скриптах.
|
||||||
|
const BATCH_SIZE = 20;
|
||||||
|
let idx = 0;
|
||||||
|
const initBatch = () => {
|
||||||
|
if (this._isStopped) return;
|
||||||
|
const end = Math.min(idx + BATCH_SIZE, pending.length);
|
||||||
|
for (let i = idx; i < end; i++) {
|
||||||
|
try { this._startSingleScript(pending[i]); }
|
||||||
|
catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('[LuaSharedSandbox] init batch err:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx = end;
|
||||||
|
if (idx < pending.length) {
|
||||||
|
setTimeout(initBatch, 0);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`[LuaSharedSandbox] all ${pending.length} scripts kicked off`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setTimeout(initBatch, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_startSingleScript(entry) {
|
_startSingleScript(entry) {
|
||||||
|
|||||||
@ -541,6 +541,53 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
HumanoidStateType: mkE(['Running','Jumping','Freefall','Landed','Dead','Climbing','Swimming','Seated']),
|
HumanoidStateType: mkE(['Running','Jumping','Freefall','Landed','Dead','Climbing','Swimming','Seated']),
|
||||||
EasingStyle: mkE(['Linear','Sine','Quad','Cubic','Quart','Quint','Bounce','Elastic']),
|
EasingStyle: mkE(['Linear','Sine','Quad','Cubic','Quart','Quint','Bounce','Elastic']),
|
||||||
EasingDirection: mkE(['In','Out','InOut']),
|
EasingDirection: mkE(['In','Out','InOut']),
|
||||||
|
// Часто используемые в туториалах
|
||||||
|
InfoType: mkE(['Asset','BundleDetails','Subscription','GamePass','UserProductsInExperience']),
|
||||||
|
SortOrder: mkE(['Name','Custom','LayoutOrder']),
|
||||||
|
FillDirection: mkE(['Horizontal','Vertical']),
|
||||||
|
HorizontalAlignment: mkE(['Left','Center','Right']),
|
||||||
|
VerticalAlignment: mkE(['Top','Center','Bottom']),
|
||||||
|
Font: mkE(['Legacy','Arial','SourceSans','Code','Highway','SciFi','Cartoon','Gotham','GothamBold']),
|
||||||
|
TextXAlignment: mkE(['Left','Center','Right']),
|
||||||
|
TextYAlignment: mkE(['Top','Center','Bottom']),
|
||||||
|
ScaleType: mkE(['Stretch','Slice','Tile','Fit','Crop']),
|
||||||
|
AspectType: mkE(['FitWithinMaxSize','ScaleWithParentSize']),
|
||||||
|
DominantAxis: mkE(['Width','Height']),
|
||||||
|
BorderMode: mkE(['Outline','Middle','Inset']),
|
||||||
|
FormFactor: mkE(['Symmetric','Brick','Plate','Custom']),
|
||||||
|
PartType: mkE(['Ball','Block','Cylinder','Wedge','CornerWedge']),
|
||||||
|
SurfaceType: mkE(['Smooth','Glue','Weld','Studs','Inlet','Universal']),
|
||||||
|
ContextActionResult: mkE(['Pass','Sink']),
|
||||||
|
UserInputState: mkE(['Begin','Change','End','Cancel','None']),
|
||||||
|
});
|
||||||
|
|
||||||
|
// TweenInfo — конструктор объекта с параметрами анимации
|
||||||
|
// Сигнатура: TweenInfo.new(time, easingStyle, easingDirection, repeatCount, reverses, delayTime)
|
||||||
|
global.set('TweenInfo', {
|
||||||
|
new(time, easingStyle, easingDirection, repeatCount, reverses, delayTime) {
|
||||||
|
return {
|
||||||
|
Time: time || 1,
|
||||||
|
EasingStyle: easingStyle,
|
||||||
|
EasingDirection: easingDirection,
|
||||||
|
RepeatCount: repeatCount || 0,
|
||||||
|
Reverses: !!reverses,
|
||||||
|
DelayTime: delayTime || 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// NumberSequence, ColorSequence — упрощённые конструкторы для GUI-эффектов
|
||||||
|
global.set('NumberSequence', {
|
||||||
|
new(...args) { return { Keypoints: [], __ns: true }; },
|
||||||
|
});
|
||||||
|
global.set('ColorSequence', {
|
||||||
|
new(...args) { return { Keypoints: [], __cs: true }; },
|
||||||
|
});
|
||||||
|
global.set('NumberRange', {
|
||||||
|
new(min, max) { return { Min: min, Max: max == null ? min : max }; },
|
||||||
|
});
|
||||||
|
global.set('Rect', {
|
||||||
|
new(minX, minY, maxX, maxY) { return { Min: { X: minX, Y: minY }, Max: { X: maxX, Y: maxY } }; },
|
||||||
});
|
});
|
||||||
|
|
||||||
// === print / warn ===
|
// === print / warn ===
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user