关于 luau-best-practices
面向使用 Luau 开发 Roblox 游戏的开发者的参考技能,涵盖代码组织、服务端验证、错误处理、内存清理和常见模式。
主要功能
- 01Luau 命名与文件组织指南
- 02服务端和客户端模块模式
- 03外部调用的错误处理
- 04连接清理与内存管理
- 05输入验证和反作弊实践
- 06现代语法与反模式指南
适用场景
- 01组织 Roblox 游戏模块和服务
- 02检查服务端与客户端边界及输入验证
- 03处理 API 故障并清理资源
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dig1t/ skills --skill luau-best-practices安装后在 Claude Code 中输入/luau-best-practices确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/code-style.mderror-handling.mdmemory.mdpatterns.mdsecurity.md详细介绍
Luau 最佳实践
面向 Roblox 游戏开发的生产级模式。
核心原则
- 服务器权威 - 游戏状态由服务器掌控;客户端负责呈现
- 尽早失败 - 尽早验证,在开发阶段明确报错
- 明确优于隐式 - 清晰表达意图胜过巧妙代码
- 尽量缩小接口 - 只公开必要内容
代码风格
命名约定
-- PascalCase: Types, Classes, Services, Modules
type PlayerData = { ... }
local ShopService = {}
local PlayerController = require(...)
-- camelCase: Variables, functions, methods
local playerCount = 0
local function getPlayerData() end
function ShopService:purchaseItem() end
-- SCREAMING_SNAKE_CASE: Constants
local MAX_PLAYERS = 50
local DEFAULT_HEALTH = 100
-- Private with underscore prefix
local function _validateInput() end
local _cache = {}
文件组织
文件内的顺序:--!strict → 服务/导入 → 常量 → 类型 → 模块表 → 私有状态 → 私有函数 → 公共 API → return。完整模板见 references/code-style.md。
模块模式
服务模式(服务器)
--!strict
local MyService = {}
local _started = false
function MyService:Start()
assert(not _started, "MyService already started")
_started = true
-- Initialize connections, load data
end
function MyService:Stop()
-- Cleanup for hot-reloading
end
return MyService
控制器模式(客户端)
--!strict
local MyController = {}
local _player = game:GetService("Players").LocalPlayer
function MyController:Init()
-- Setup without yielding
end
function MyController:Start()
-- Connect events, start loops
end
return MyController
更多模式(信号、状态管理、延迟初始化)见 references/patterns.md。
错误处理
对外部调用使用 pcall
-- DataStore, HTTP, any Roblox API that can fail
local success, result = pcall(function()
return dataStore:GetAsync(key)
end)
if not success then
warn("DataStore failed:", result)
return nil
end
return result
对于编程错误(不应发生的情况)使用 assert;对于可能正常失败的外部调用,使用 pcall + warn。结果类型和重试模式见 references/error-handling.md。
内存管理
始终断开连接
local connection: RBXScriptConnection
connection = event:Connect(function()
-- handler
end)
-- Later, cleanup:
connection:Disconnect()
使用 Maids/Janitors
local Maid = require(Packages.Maid)
local maid = Maid.new()
maid:GiveTask(event:Connect(handler))
maid:GiveTask(instance)
maid:GiveTask(function()
-- Custom cleanup
end)
-- Cleanup everything at once
maid:Destroy()
用于缓存的弱表和其他防止泄漏的模式见 references/memory.md。
安全最佳实践
服务器权威
-- BAD: Client tells server what happened
RemoteEvent.OnServerEvent:Connect(function(player, damage)
target.Health -= damage -- Client controls damage!
end)
-- GOOD: Server calculates everything
RemoteEvent.OnServerEvent:Connect(function(player, targetId)
local target = getValidTarget(player, targetId)
if not target then return end
local damage = calculateDamage(player) -- Server calculates
target.Health -= damage
end)
验证所有输入
RemoteFunction.OnServerInvoke = function(player, itemId, quantity)
-- Type validation
if typeof(itemId) ~= "string" then return end
if typeof(quantity) ~= "number" then return end
-- Range validation
if quantity < 1 or quantity > 99 then return end
if quantity ~= math.floor(quantity) then return end
-- Business logic validation
if not Items[itemId] then return end
if not canAfford(player, itemId, quantity) then return end
-- Now safe to process
return purchaseItem(player, itemId, quantity)
end
速率限制和反作弊模式见 references/security.md。
常见反模式
避免
-- Using wait() - use task.wait()
wait(1) -- BAD
task.wait(1) -- GOOD
-- spawn() - use task.spawn()
spawn(fn) -- BAD
task.spawn(fn) -- GOOD
-- delay() - use task.delay()
delay(1, fn) -- BAD
task.delay(1, fn) -- GOOD
-- Polling when events exist
while true do
if something then break end
task.wait()
end
-- GOOD: Use events/signals instead
-- String concatenation in loops
local s = ""
for i = 1, 1000 do
s = s .. tostring(i) -- O(n²)
end
-- GOOD: Use table.concat
-- Dot-index chains on instances
workspace.Folder.SubFolder.Part -- Errors if missing
-- GOOD: Safe navigation
local folder = workspace:FindFirstChild("Folder")
local subFolder = folder and folder:FindFirstChild("SubFolder")
local part = subFolder and subFolder:FindFirstChild("Part")
推荐
-- Generalized iteration
for _, v in ipairs(array) do end -- OLD
for _, v in array do end -- MODERN (Luau)
-- If expressions
local x = if condition then a else b -- Clean ternary
-- Continue in loops
for _, item in items do
if not item.valid then continue end
process(item)
end
-- Optional chaining with and
local name = player and player.Character and player.Character.Name
项目结构
src/
├── Server/
│ ├── init.server.luau # Bootstrap
│ ├── Services/ # Game services
│ │ ├── DataService.luau
│ │ └── CombatService.luau
│ └── Components/ # Server components
├── Client/
│ ├── init.client.luau # Bootstrap
│ ├── Controllers/ # Client controllers
│ └── UI/ # UI components
├── Shared/
│ ├── Types.luau # Shared type definitions
│ ├── Constants.luau # Shared constants
│ └── Util/ # Shared utilities
└── Packages/ # Wally packages
快速参考
| 应该 | 不应该 |
|---|---|
task.wait() | wait() |
task.spawn() | spawn() |
task.delay() | delay() |
for _, v in t | for _, v in pairs(t) |
| 在服务器上验证 | 信任客户端数据 |
| 使用类型 | 到处使用 any |
| 断开事件连接 | 留下未断开的连接 |
| 使用常量 | 使用魔法数字/字符串 |
| 尽早返回 | 深层嵌套 |
| 编写小函数 | 编写 200+ 行的函数 |
参考资料
评论
支持 Markdown