返回 Skills 目录
gamedev-skills/awesome-gamedev-agent-skills已通过检查

SKILL DETAIL

roblox-luau

gamedev-skills/awesome-gamedev-agent-skills/roblox-luau

Script a Roblox experience in Luau: get services, create and parent Instances, connect events, run server Scripts vs client LocalScripts, and communicate across the client/server boundary with RemoteEvents/RemoteFunctions (server-authoritative). Use when building or debugging Roblox Studio scripts — when the user mentions Roblox, Luau, services, RemoteEvent, Instance.new, PlayerAdded, or client vs server. For saving player data use roblox-datastores.

安装量 · 233查看来源

Installation

npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill roblox-luau

技能文件

SKILL.md

最近同步 · 2026年10月24日

references/client-server.md›
# Roblox client/server model & communication (current engine)

Depth behind the Luau skill: how code is split across machines, the messaging
primitives, replication timing, and tags/attributes.

## Where code runs

| Container | Runs on | Use for |
|-----------|---------|---------|
| `ServerScriptService` | server | authoritative game logic (`Script`) |
| `ServerStorage` | server | assets/data the client must never see |
| `ReplicatedStorage` | both | remotes, `ModuleScript`s, shared assets |
| `StarterPlayerScripts` | client | persistent client logic (`LocalScript`) |
| `StarterCharacterScripts` | client | per-character client logic |
| `StarterGui` | client | UI scripts |

The server simulates the authoritative world; each client renders its own view and
sends input. Roblox always filters client→server replication, so a client cannot
directly change server-owned state — it can only ask via a remote.

## RemoteEvent vs RemoteFunction

Both live in `ReplicatedStorage` so both sides can find them.

- **RemoteEvent** — fire-and-forget, one-way, asynchronous.
  - Client→server: `remote:FireServer(args)` → server handles `remote.OnServerEvent:Connect(function(player, args) ... end)`. The `player` is injected by the engine and is trustworthy; everything after it is not.
  - Server→client: `remote:FireClient(player, args)` or `remote:FireAllClients(args)` → client handles `remote.OnClientEvent:Connect(function(args) ... end)`.

- **RemoteFunction** — request/response, the caller **yields** for a return value.
  - Client→server: `local result = remote:InvokeServer(args)`; server defines `remote.OnServerInvoke = function(player, args) return ... end`.
  - Prefer `RemoteEvent` for most things. A `RemoteFunction` invoked **on a client by the server** is dangerous: a malicious client can never return or can error, hanging the server. Only invoke client→server, validate, and consider timeouts.

### Security checklist for every server handler

1. Validate argument **types** (`type(x) ~= "number"` → reject).
2. Validate argument **ranges/identity** (does this item exist? can this player afford it? do they own the thing they're acting on?).
3. Rate-limit if the action is expensive (track last-call time per player).
4. Never send secret data (prices, loot tables, other players' private data) to the
   client "to be returned"; decide it on the server.

## BindableEvent / BindableFunction

For messaging **within the same machine** (server-to-server scripts, or client-to-
client), use `BindableEvent`/`BindableFunction`. They do not cross the network and
carry no security boundary — don't use them to replace remotes.

## Replication timing & WaitForChild

When a client starts, the data model streams in over time, so a child you expect
may not exist yet. On the client, use:

```lua
local remote = ReplicatedStorage:WaitForChild("BuyItem")        -- yields until it exists
local gui = player:WaitForChild("PlayerGui"):WaitForChild("HUD")
```

`WaitForChild` accepts an optional timeout; without one it warns after ~5 seconds.
On the server, instances you created exist immediately, so direct indexing is fine.

## Connections & cleanup

`:Connect` returns an `RBXScriptConnection`. Long-lived connections that are never
disconnected leak and can fire on destroyed instances:

```lua
local conn = part.Touched:Connect(onTouch)
-- when done:
conn:Disconnect()

-- One-shot: auto-disconnects after the first fire.
part.Touched:Once(onTouchOnce)

-- Clean up everything tied to a player on leave:
Players.PlayerRemoving:Connect(function(player) cleanupFor(player) end)
```

## Attributes & CollectionService tags

- **Attributes** store typed metadata directly on an instance and replicate to
  clients: `part:SetAttribute("Damage", 25)`, `part:GetAttribute("Damage")`, and
  `part:GetAttributeChangedSignal("Damage"):Connect(...)`. Good for designer-tweakable
  values without extra `Value` objects.
- **CollectionService tags** group instances by string tag for data-driven systems:

```lua
local CollectionService = game:GetService("CollectionService")
for _, door in CollectionService:GetTagged("Door") do
    setupDoor(door)
end
CollectionService:GetInstanceAddedSignal("Door"):Connect(setupDoor)
```

Tags + attributes together let you build behavior systems that pick up any tagged
instance (placed by designers in Studio) without hard-coding references.

## Studio testing

- **Play** — your avatar in a combined server+client session.
- **Run** — server simulation with no player.
- **Start (Local Server)** — separate server + N client windows; the only way to
  properly test replication and remotes. Use the Server/Client view toggle and the
  Output window to confirm which side a message came from.
SKILL.md›
---
name: roblox-luau
description: >
  Script a Roblox experience in Luau: get services, create and parent Instances,
  connect events, run server Scripts vs client LocalScripts, and communicate across
  the client/server boundary with RemoteEvents/RemoteFunctions (server-authoritative).
  Use when building or debugging Roblox Studio scripts — when the user mentions
  Roblox, Luau, services, RemoteEvent, Instance.new, PlayerAdded, or client vs
  server. For saving player data use roblox-datastores.
---

# Roblox Luau Scripting

Script a Roblox experience in **Luau**: services, `Instance`s, events, the
server/client split, and secure cross-boundary communication. Targets the current
Roblox engine and Studio.

## When to use

- Use when writing Roblox scripts: getting services, creating/parenting instances,
  connecting events, deciding server vs client, or wiring `RemoteEvent`/
  `RemoteFunction` communication.
- Use when the project has `Script`/`LocalScript`/`ModuleScript` objects, `.rbxl(x)`
  places, or a Rojo `*.project.json`, and code calls `game:GetService(...)`.

**When *not* to use:** persisting data across sessions → `roblox-datastores`.
Remote protocol architecture, exploit hardening, rate limits, high-frequency replication, and
multi-client abuse testing → `roblox-networking`. Generic Lua questions unrelated to the Roblox
API. Engine-agnostic input/save architecture → `input-systems` / `save-systems`.

## Core workflow

1. **Get services with `game:GetService("Name")`.** Common ones: `Players`,
   `Workspace`, `ReplicatedStorage` (shared client+server), `ServerScriptService`
   (server-only code), `ServerStorage`, `RunService`, `UserInputService` (client).
2. **Know where code runs.** A `Script` runs on the **server**; a `LocalScript`
   runs on a **client** (in `StarterPlayerScripts`, `StarterGui`, or the player's
   character). A `ModuleScript` is shared code you `require`.
3. **Create instances deliberately.** `local p = Instance.new("Part")`, set its
   properties, then set `p.Parent` **last** (parenting triggers replication).
4. **React with events.** `:Connect` to signals like `Players.PlayerAdded`,
   `part.Touched`, or `RunService.Heartbeat`. Disconnect when done to avoid leaks.
5. **Cross the client/server boundary with Remotes — and never trust the client.**
   Clients request via `RemoteEvent:FireServer(...)`; the server validates and
   applies. The server is authoritative for all game state.
6. **Test in Studio** with Play / Play Here / server+client Start; use the Output
   window and the server/client view toggle to confirm where code ran.
7. **Run static checks before Studio when the project already has the tools.** Put
   `std = "roblox"` in the root `selene.toml`, then run `selene .`.
   For a Rojo project, write the sourcemap once with
   `rojo sourcemap default.project.json --output sourcemap.json`, then run
   `luau-lsp analyze --sourcemap=sourcemap.json src`, passing the Roblox type definitions
   with `--definitions` (`luau-lsp analyze --help` lists the flags). Studio Script Sync
   projects may not have a sourcemap.

## Patterns

### 1. Server Script: react to players joining (leaderstats)

```lua
-- ServerScriptService/Leaderboard.server.luau  (a Script = runs on the server)
local Players = game:GetService("Players")

local function onPlayerAdded(player: Player)
    local stats = Instance.new("Folder")
    stats.Name = "leaderstats"          -- this name makes it show on the leaderboard

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Value = 0
    coins.Parent = stats

    stats.Parent = player               -- parent LAST
end

Players.PlayerAdded:Connect(onPlayerAdded)
```

### 2. Create and configure an instance

```lua
local Workspace = game:GetService("Workspace")

local part = Instance.new("Part")
part.Size = Vector3.new(4, 1, 4)
part.Position = Vector3.new(0, 10, 0)
part.Anchored = true                    -- won't fall under gravity
part.BrickColor = BrickColor.new("Bright blue")
part.Parent = Workspace                 -- set Parent last so it replicates once, fully
```

### 3. Connect an event (and disconnect to avoid leaks)

```lua
local debounce = false
local connection
connection = part.Touched:Connect(function(hit: BasePart)
    local character = hit.Parent
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not humanoid or debounce then return end
    debounce = true
    humanoid.Health -= 10
    task.wait(1)                        -- task.wait, NOT the deprecated wait()
    debounce = false
end)

-- Later, when the part is removed or the round ends:
-- connection:Disconnect()
```

### 4. Client → server with a RemoteEvent (validate on the server!)

```lua
-- ReplicatedStorage: create a RemoteEvent named "BuyItem" (in Studio or via code).
-- CLIENT (LocalScript): request a purchase. The client can lie — this is only a request.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")  -- wait: may not have replicated yet
buyButton.MouseButton1Click:Connect(function()
    buyItem:FireServer("sword")        -- send the item id only; never the price/result
end)
```

```lua
-- SERVER (Script): the ONLY place the transaction is decided.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local buyItem = ReplicatedStorage:WaitForChild("BuyItem")
local PRICES = { sword = 100, shield = 75 }

buyItem.OnServerEvent:Connect(function(player: Player, itemId)
    -- TRUST NOTHING from the client. Validate types and values.
    if type(itemId) ~= "string" then return end
    local price = PRICES[itemId]
    if not price then return end                         -- unknown item
    local coins = player.leaderstats.Coins
    if coins.Value < price then return end               -- can't afford
    coins.Value -= price                                 -- server applies the change
    grantItem(player, itemId)
end)
```

### 5. A per-frame loop with RunService

```lua
local RunService = game:GetService("RunService")
-- Heartbeat fires every frame AFTER physics; dt is seconds since the last step.
RunService.Heartbeat:Connect(function(dt)
    spinner.CFrame *= CFrame.Angles(0, math.rad(90) * dt, 0)  -- 90deg/sec, frame-independent
end)
```

### 6. Shared code in a ModuleScript

```lua
-- ReplicatedStorage/GameConfig (a ModuleScript) — usable by server and client.
local GameConfig = {}
GameConfig.MaxHealth = 100
function GameConfig.damageFor(weapon: string): number
    return ({ sword = 25, bow = 15 })[weapon] or 0
end
return GameConfig
```

```lua
local GameConfig = require(game:GetService("ReplicatedStorage"):WaitForChild("GameConfig"))
print(GameConfig.MaxHealth)
```

## Pitfalls

- **Trusting the client is an exploit** → clients can send any arguments to a
  `RemoteEvent`/`RemoteFunction`. Validate every argument's type and range on the
  server and keep the server authoritative over health, currency, and inventory.
- **`LocalScript` doesn't run where you put it** → LocalScripts run in
  `StarterPlayerScripts`, `StarterCharacterScripts`, `StarterGui`, or tools — not in
  `Workspace` or `ServerScriptService`. Server `Script`s belong in
  `ServerScriptService`/`Workspace`.
- **Deprecated globals** → use `task.wait`/`task.spawn`/`task.delay`, not the old
  `wait()`/`spawn()`/`delay()` (worse scheduling and throttling).
- **Parenting first, then setting properties** → set properties first and `Parent`
  last so the instance replicates once in its final state.
- **`nil` on the client right after join** → objects stream/replicate over time; use
  `parent:WaitForChild("Name")` instead of indexing directly on the client.
- **Connections never disconnected** → long-lived `:Connect` handlers leak and can
  fire on destroyed objects; store the connection and `:Disconnect()` (or use
  `Instance:GetAttributeChangedSignal`/`:Once` where appropriate).
- **Using a RemoteFunction where a RemoteEvent fits** → `RemoteFunction` blocks
  waiting for a return and a malicious/slow client can stall the server; prefer
  one-way `RemoteEvent`s unless you genuinely need a reply.
- **Assuming the old type checker** -> Luau's New Type Solver reached general release
  (out of Studio Beta) in Nov 2025. `nocheck`/non-strict places get it automatically;
  strict-mode places opt in with the `UseNewLuauTypeSolver` workspace property, and
  `LuauTypeCheckMode` sets a place's default checking mode. It runs only at edit time
  (no runtime cost), so triage type warnings against the solver the place actually has
  enabled, not older behavior.

## References

- For the full client/server model (replication, `RemoteFunction` vs `RemoteEvent`,
  `:WaitForChild` timing, `BindableEvent` for same-context messaging, attributes,
  `CollectionService` tags, and `:Once`/connection cleanup), read
  `references/client-server.md`.

## Related skills

- `roblox-datastores` — persist player data across sessions (server-only).
- `roblox-networking` — production remote contracts, server validation, rate limits, replication,
  streaming, prediction, and multi-client testing.
- `save-systems` — engine-agnostic persistence concepts.
- `game-ai` / `input-systems` — portable AI and input patterns to implement in Luau.