mcpField
S
scenario-roblox-luau

scenario-roblox-luau

github.com/scenario-labs开发1.2k54 次安装1 天前更新
代码仓库

关于 scenario-roblox-luau

面向 Roblox Studio 开发者的 Luau 工程技能,配合 scenario-roblox-expert toolkit 使用。重点在 Deferred 安全行为、严格类型、资源清理和实测优化,并提供审计、类型检查、探测与基准测试流程。

主要功能

  1. 01审计调度器与事件用法
  2. 02使用两种 Luau 求解器检查类型
  3. 03追踪资源清理与泄漏
  4. 04在游玩测试中探测行为
  5. 05基准测试原生和并行代码
  6. 06采用缓冲区前先测量

适用场景

  1. 01排查 Deferred 事件处理问题
  2. 02跨求解器检查严格类型
  3. 03验证玩家或对象移除后的清理
  4. 04测量优化是否带来收益

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/scenario-labs/skills --skill scenario-roblox-luau

安装后在 Claude Code 中输入/scenario-roblox-luau确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
critique.md
expert-notes.md
gui-paths.md
procedures.md
sources.md
scripts/
rbx_luau.py
scripts/agentkit/luau/
Bench.luau
BufferCodec.luau
Leak.luau
LuauAudit.luau
lune_luau_audit.luau
Parallel.luau
Probe.luau
scripts/agentkit/luau/templates/
Class.luau
ParallelWorker.server.luau

详细介绍

Roblox Luau 工程

在 macOS 上使用 Roblox Studio(2026 年 10 月版本),通过 scenario-roblox-expert 工具包驱动。

此处的专业 Luau,意味着代码在 Deferred 事件和任务调度器下仍能保持正确、通过两种求解器的类型检查、释放创建的所有内容,并且每项优化都有在此版本上测得的数据支撑。请在游戏测试数据模型中验证行为,而不是沿用 Immediate 时代的经验进行推断。

如果你当前可用的技能中缺少此处提到的同级技能,请让用户安装它 (npx skills add scenario-labs/skills --skill <name>);若无人值守,请依据工具架构继续操作,并指出这一缺失。

必备背景知识: scenario-roblox-expert(频道、审查流程、工具包、当前 Roblox 的常见陷阱)。

立场(专家补充)

  1. task.spawn 不等于并发。 它会先运行主体代码,直到首次让出执行权,然后调用方才继续(Crusherfire)。要实现“此步骤之后”,请使用 task.defer:它会按调用顺序排队。task.wait() 和 task.delay(0) 会在下一个 Heartbeat 恢复:从 PreSimulation 开始时是同一帧,从 Heartbeat 开始时则是下一帧(Crusherfire;实测分别为 0.07 ms 和 16.4 ms)。
  2. 编写可安全用于 Deferred 的代码。 处理程序会在下一个恢复点运行,绝不会在 :Fire() 或设置属性时运行(Deferred events 文档)。嵌套触发会按队列顺序运行;在处理程序内连接的监听器会错过同一步中的触发事件;Destroying 或 CharacterRemoving 处理程序看到的 Parent 为 nil(Crusherfire;均已实测)。在处理程序内读取状态,使用 :Once(),并在 Destroy 前保存清理所需的信息。仅使用 Deferred,就将某款游戏的帧耗时从 6 到 8 ms 降至 4 到 5 ms(Drew Hamilton,RDC 2026)。
  3. 只在允许且代价低的地方让出执行权。 绝不要在 __index 等元方法、table.sort 比较器或 gsub 回调中让出执行权:会出现“attempt to yield across metamethod/C-call boundary”。绝不要在逐帧处理程序中让出执行权:在 Heartbeat 中使用 task.wait(0.25),1 s 内就累积了 16 个线程[新增,实测]。pcall 无法解决问题:在 pcall 下失败的 task.wait 仍会让其线程注册在调度器中[新增,实测]。
  4. 兼顾两种求解器进行类型标注。 新模块以 --!strict 开头。方法定义为 function Class.name(self: Class, ...):冒号方法会让字段拼写错误在两种求解器下都漏过检查[新增,已测试]。优先使用组合:带类型的元表继承在两种求解器下都会产生误报。使用 IsA 或 FindFirstChildOfClass 收窄类型,而不是 :: any。同时在旧版和新版求解器下进行测试,因为严格脚本会遵循 Workspace.UseNewLuauTypeSolver,而脚本无法读取该属性。
  5. 每个对象都负责自己的清理。 Destroy 是幂等的,并会断开它创建的所有连接(Roblox Learn,OOP)。以玩家为键的表应在 PlayerRemoving 时清空。旧角色应销毁:在 Default 设置下,重生后的角色会脱离父级,但其连接仍然存在(实测)。内存泄漏指内存始终无法回落;仅仅增长并不算泄漏(Roblox Learn,memory leaks)。
  6. Parallel Luau 用于计算;串行代码负责写入。 每个独立单元使用一个 Actor,在并行阶段之前执行 require,并在 task.synchronize() 后写入(文档;Suphi Kaner;Stewiepfing)。每个 Actor 都有自己的 Luau VM,因此模块状态不会共享。对 SharedTable 执行读-改-写时,应使用 SharedTable.increment 或 update:8 个 Actor 执行普通的 += 1 会导致 79% 的更新丢失(实测)。
  7. 测量后再使用原生代码和缓冲区。 --!native 对带类型的数值代码效果显著:数字循环快 3.3 倍,带类型的 Vector3 循环快 4.9 倍,而 Vector3 参数未标注类型时则没有提升(实测;文档建议添加类型标注)。到处启用原生代码会增加启动开销,并占用全局内存预算(Roblox Learn,native)。缓冲区在持续吞吐量超过约 50 KB/s 后才有优势(Crusherfire);整数写入会静默回绕,因此需要进行范围检查。
  8. 注入的代码块不等于游戏本身。 Server 上的 execute_luau 代码块有自己的模块缓存:它读取到的 Counter.n = 0,而游戏 Scripts 读取到的是 41[新增,实测]。请通过属性或 BindableFunction 钩子读取游戏状态。

先确认

  • **Signal 模式:**Workspace > SignalBehavior。脚本无法读取该属性;请检查 Properties 面板、Rojo 项目文件或 rbx_luau.audit(place)。Default 表示 Immediate。未设置该属性的 Rojo 构建场景使用 Default。
  • 服务器权威:Workspace.AuthorityMode = Server 会强制使用 Deferred 和固定步长模拟;此时游戏逻辑应归属 scenario-roblox-gameplay。
  • 类型设置:Workspace.LuauTypeCheckMode、UseNewLuauTypeSolver,以及 Rojo 项目是否提供 sourcemap。
  • **代码运行位置:**Server、Client 或 Actor 下。原生代码只对服务器脚本有帮助。
  • **优化前的基准数值:**热点路径每帧耗时(ms)、网络传输速率(KB/s)、每个周期的堆增长斜率。
  • PlayerCharacterDestroyBehavior(Properties 面板中设置;不可通过脚本设置)。
  • 未知时的默认值:Deferred、--!strict、两个类型求解器均设为门控、无原生代码、无 Actor、无缓冲区。

流程

  1. **审计现有内容。**目标:在试玩前找出调度器、让出、Deferred、并行、原生代码和泄漏方面的问题。方法:离线运行 rbx_luau.audit("place.rbxl" or "src/", "warn")(Lune);在 Studio 中运行 rbx_luau.run(s, chunk, dm="Edit", kit=["LuauAudit"]) 和 LuauAudit.tree(game);工具包的 Audit 会检查旧版 API。门槛:错误级发现为 0;每条警告都已修复或用一行说明原因。
  2. **修正惯用写法。**目标:使用当前的调度器和事件惯用写法。方法:只用 task.*;用 PreSimulation、PostSimulation、PreRender 替代 Stepped 和 RenderStepped;使用 :Once();帧处理程序中不执行让出操作(使用 busy flag,或一个已生成的工作循环);元方法不得让出(预先计算,或显式使用可能让出的 get)。门槛:审计干净;在 Deferred 下行为不变(步骤 5)。
  3. **类型检查。**目标:让两个类型求解器都能在编辑时捕获错误。方法:使用 --!strict、Fields 类型和 typeof(setmetatable({} :: Fields, Class))、带点号类型的 self、组合,以及 table.create(n, value);然后运行 rbx_luau.typecheck(paths, sourcemap=rbx_luau.sourcemap(project))。门槛:old 和 new 中的错误均为 0。
  4. **所有权与清理。**目标:任何对象的存活时间都不超过其所有者。方法:使用类模板(将 Destroy 连接到 Destroying);清理 PlayerRemoving;销毁角色。在 Server 试玩中用 Leak.retained(make, release, n) 和 Leak.sample(cycle, 20),以及 Leak.slope 验证。门槛:释放后 alive 为 0;20 个或更多周期内堆增长斜率持平;Destroy 后剩余连接数为 0。
  5. **探测依赖执行顺序的行为。**目标:获取此场景中的事实,而非凭空假设。方法:在一次试玩中,通过 rbx_luau.run(..., dm="Server") 和 dm="Client" 运行 Probe.scheduler()、Probe.delayTiming()、Probe.signals()(见流程 P3)。门槛:已记录结果;没有代码依赖探测结果显示会被 Deferred 改变的执行顺序。
  6. **只依据数值进行优化。**目标:只保留经测量有效的优化。方法:在 Server 数据模型中运行 Bench.run({plain=..., native=...}, 7)(交错轮次、预热、中位数);使用 Parallel.workers,对比 1 个与 N 个 Actor,以及一个工作模块;比较 BufferCodec 大小与表载荷。门槛:原生代码比率至少为 1.2 [新增阈值],并行加速比至少为 1.5 [新增阈值],缓冲区超过带宽门槛;否则回退。
  7. **交付时附上证据。**目标:评审者无需重新运行即可信任结果。方法:附上类型检查输出、审计 JSON、探测和基准测试结果,并注明构建、数据模型和日期;如改动可见,则通过 rbx_review 附上试玩录屏。门槛:交付说明中的每项声明都有数值或测试名称作为依据。

数值

数值测量值或说明对比基准
task.delay(0) 恢复执行从 PreSimulation 起 0.07 ms,从 Heartbeat 起 16.42 msStudio 0.743 Server,60 Hz
让出执行权的 Heartbeat 处理程序1 秒内有 16 个并发线程每次调用 task.wait(0.25)
延迟执行带来的帧耗时提升从 6 到 8 ms 降至 4 到 5 ms一款已发布的游戏(RDC 2026)
Native 加速比,数值循环3.3x纯 Luau,Server,n = 200,000
Native 加速比,类型化 Vector3 循环4.9x;非类型化为 0.96x同一内核,但不带 --!native
Native 大小限制每个代码块 64K 条指令,每个函数 32K 个代码块,每个脚本 1M 条指令文档;另有全局上限
并行加速比7.6x(144.5 ms 降至 19.0 ms)1 个与 8 个 Actor,32 个纯计算任务,18 核 Mac
SharedTable 普通 += 1丢失 160,000 次更新中的 127,107 次SharedTable.increment 未丢失更新
Buffer 带宽限制持续入站约 50 KB/sCrusherfire
通过远程传输的数值Number 为 9 字节;Color3 为 13 字节,打包后为 5 字节Crusherfire
SharedTable 键字符串或 0 到 2^32 - 1 的整数SharedTable 参考资料
内存预算Server 约 6.25 GB;手机约 2 GB,其中 700 MB 被操作系统占用Roblox Learn,内存泄漏
重入限制10(文档);自我重新触发的处理程序达到 50 次且未报错Deferred,BindableEvent

质量门槛

可测量项:

  • rbx_luau.typecheck:两个求解器均为 0 个错误,并使用项目的 sourcemap。
  • rbx_luau.audit:0 个错误;警告已解决。
  • Toolkit Audit:未发现旧版调度器或弃用 API。
  • 每次修复清理逻辑后,Leak.retained 的存活对象数为 0,且 Leak.slope 持平。
  • 记录该场景信号模式在 Server 和 Client 上的探测结果。
  • 保留 Native 代码或 Actor 前,记录 Bench 和 Parallel 的比率。
  • BufferCodec 往返转换结果完全一致(或在 f32 精度范围内),并测试拒绝情况。

视觉项(当行为可见时):通过 rbx_review 录制的试玩画面,展示对象正在移动、已清理,或展示你所声称的状态;对于性能分析相关的声明,需手动录制 MicroProfiler 或 Script Profiler 画面(GUI 路径)。

常见错误

错误症状修复
wait()、spawn()、delay()被限流、恢复执行延迟task.wait、task.spawn、task.delay
:Fire() 后的逻辑假定处理程序已运行Deferred 模式下状态过时在处理程序内部操作,或使用 task.defer
Destroying 处理程序读取 Parentnil 错误在 Destroy 前捕获
Heartbeat 处理程序中使用 task.wait线程堆积、工作量翻倍使用忙碌标志,或单个工作循环
在 __index 或排序比较器中让出"attempt to yield across metamethod/C-call boundary"预先计算;使用显式 getter
严格模式代码中使用 function C:m()字段拼写错误未被发现function C.m(self: C)
(x :: any).Child拼写错误未被检查器发现使用 IsA、FindFirstChildOfClass 缩窄类型
类型化继承链对有效调用提示 "does not have key"使用组合
未使用 PlayerRemoving 就执行 players[player] = ...每次加入后服务器内存都会增加在 PlayerRemoving 时清除
在 Actor 外使用 ConnectParallel报错;处理程序从未运行将脚本设为 Actor 的子级
desynchronize 后执行 require 或写入 Instance"not safe to write in parallel"先执行 require;在 synchronize 后写入
在 pcall 内调用 task.synchronize()某次运行中位置发生崩溃(原因未隔离)不要在 pcall 内切换阶段
在未类型化的 Vector3 代码上使用 --!native没有加速为参数添加类型注解
buffer.writeu8(b, 0, 300)存储为 44检查范围,或使用 BufferCodec
collectgarbage("collect")在 Roblox 中报错使用 gcinfo()

交接

  • 来自 scenario-roblox-architecture: 模块布局、清理库选择(Trove、Janitor、 Maid)、信号模块。本技能会检查其中的 Luau 代码。
  • 交接至 scenario-roblox-networking: BufferCodec 模式和实测载荷大小;远程对象、速率 限制和线路格式归该技能负责。
  • 交接至 scenario-roblox-performance: Bench 和 Parallel 结果、原生代码候选项、泄漏趋势; 帧预算和性能分析会话归该技能负责。
  • 与 scenario-roblox-gameplay 双向交接: 固定步长和 Server Authority 代码;本技能 提供调度器和 Deferred 规则。
  • 来自 scenario-roblox-data: 以玩家为键的会话状态;本技能检查 PlayerRemoving 清理。
  • 交接至 scenario-roblox-tooling: 类型门禁和用于 CI 的 lune_luau_audit.luau。

Roblox 当前说明

  • Deferred 是模板默认值;Default 仍映射到 Immediate;Server Authority 会强制使用 Deferred。SignalBehavior 为 NotScriptable。
  • 新类型求解器:于 2025-11-20 面向非 strict 和 nocheck 模式正式发布;strict 模式遵循 UseNewLuauTypeSolver(NotScriptable)。旧求解器将保留至 2026 年。
  • wait、spawn、delay 是已弃用的全局函数;Stepped 和 RenderStepped 已被取代。
  • 原生代码生成在服务器脚本中运行;debug.dumpcodesize() 仅可从命令栏(Server 视图)调用。
  • 在 Roblox 中,vector.create 返回 Vector3;缓冲区序列化为 JSON 时格式为 {"m":null,"t":"buffer","base64":...}。
  • 在 Roblox 脚本中,collectgarbage 仅接受 "count"。

参考资料

评论

支持 Markdown