mcpField
L
luau-best-practices

luau-best-practices

github.com/dig1t开发5592 次安装4 周前更新
代码仓库

关于 luau-best-practices

面向使用 Luau 开发 Roblox 游戏的开发者的参考技能,涵盖代码组织、服务端验证、错误处理、内存清理和常见模式。

主要功能

  1. 01Luau 命名与文件组织指南
  2. 02服务端和客户端模块模式
  3. 03外部调用的错误处理
  4. 04连接清理与内存管理
  5. 05输入验证和反作弊实践
  6. 06现代语法与反模式指南

适用场景

  1. 01组织 Roblox 游戏模块和服务
  2. 02检查服务端与客户端边界及输入验证
  3. 03处理 API 故障并清理资源

安装

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

$npx skills add https://github.com/dig1t/skills --skill luau-best-practices

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

包含的文件

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

references/
code-style.md
error-handling.md
memory.md
patterns.md
security.md

详细介绍

Luau 最佳实践

面向 Roblox 游戏开发的生产级模式。

核心原则

  1. 服务器权威 - 游戏状态由服务器掌控;客户端负责呈现
  2. 尽早失败 - 尽早验证,在开发阶段明确报错
  3. 明确优于隐式 - 清晰表达意图胜过巧妙代码
  4. 尽量缩小接口 - 只公开必要内容

代码风格

命名约定
-- 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 tfor _, v in pairs(t)
在服务器上验证信任客户端数据
使用类型到处使用 any
断开事件连接留下未断开的连接
使用常量使用魔法数字/字符串
尽早返回深层嵌套
编写小函数编写 200+ 行的函数

参考资料

评论

支持 Markdown