roblox-luau
gamedev-skills/awesome-gamedev-agent-skills
Script Roblox experiences in Luau: services, instances, events, and secure client/server communication.
What is roblox-luau?
Build and debug Roblox Studio scripts using Luau. Covers getting services, creating and parenting instances, connecting events, distinguishing server Scripts from client LocalScripts, and wiring RemoteEvent/RemoteFunction communication with server-side validation. Use this when working on Roblox game logic and architecture.
- Get and use Roblox services (Players, Workspace, RunService, UserInputService, etc.)
- Create, configure, and parent instances with proper replication order
- Connect and manage event handlers (PlayerAdded, Touched, Heartbeat) without leaks
- Implement server-authoritative client/server communication via RemoteEvent and RemoteFunction
- Write shared code in ModuleScripts and server-only code in Scripts vs client-only LocalScripts
- Build per-frame loops and react to player lifecycle events
How to install roblox-luau
npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill roblox-luau- Roblox Studio installed
- Basic Lua or Luau familiarity
- Understanding of server vs. client architecture concepts
How to use roblox-luau
- 1.Identify whether your code runs on the server (Script) or client (LocalScript)
- 2.Get the service you need with game:GetService("ServiceName")
- 3.Create instances with Instance.new(), set properties, then set Parent last
- 4.Connect to events with :Connect() and store the connection to disconnect later
- 5.For client→server requests, use RemoteEvent:FireServer() and validate all arguments on the server
- 6.Test in Studio using Play or Play Here, checking the Output window and server/client view toggle
Use cases
- Creating leaderstats and player progression systems
- Building purchasable items with server-side validation to prevent exploits
- Implementing damage, health, and game-state changes that only the server can authorize
- Spawning and configuring parts, NPCs, and interactive objects in the workspace
- Handling user input on the client and securely communicating requests to the server
- Roblox game developers building experiences in Studio
- Scripters implementing game mechanics and player interactions
- Developers securing client/server boundaries against exploits
- Anyone building multiplayer game logic in Roblox
roblox-luau FAQ
Use RemoteEvent for one-way requests (client → server or server → client). RemoteFunction blocks waiting for a return, which can stall the server if a client is slow or malicious; only use it if you genuinely need a synchronous reply.
Never trust the client. Validate every argument's type and range on the server, keep the server authoritative over health/currency/inventory, and always apply game-state changes server-side only.
LocalScripts only run in StarterPlayerScripts, StarterCharacterScripts, StarterGui, or tools. Server Scripts belong in ServerScriptService or Workspace. Check the script's location and type.
task.wait() is the modern, recommended function with better scheduling and throttling. The old wait() is deprecated and should not be used in new code.
Write a ModuleScript in ReplicatedStorage, return a table of functions and values, and require() it from both server and client code.
Full instructions (SKILL.md)
Source of truth, from gamedev-skills/awesome-gamedev-agent-skills.
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, Instances, 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/RemoteFunctioncommunication. - Use when the project has
Script/LocalScript/ModuleScriptobjects,.rbxl(x)places, or a Rojo*.project.json, and code callsgame: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
- Get services with
game:GetService("Name"). Common ones:Players,Workspace,ReplicatedStorage(shared client+server),ServerScriptService(server-only code),ServerStorage,RunService,UserInputService(client). - Know where code runs. A
Scriptruns on the server; aLocalScriptruns on a client (inStarterPlayerScripts,StarterGui, or the player's character). AModuleScriptis shared code yourequire. - Create instances deliberately.
local p = Instance.new("Part"), set its properties, then setp.Parentlast (parenting triggers replication). - React with events.
:Connectto signals likePlayers.PlayerAdded,part.Touched, orRunService.Heartbeat. Disconnect when done to avoid leaks. - 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. - 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.
Patterns
1. Server Script: react to players joining (leaderstats)
-- 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
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)
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!)
-- 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)
-- 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
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
-- 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
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. LocalScriptdoesn't run where you put it → LocalScripts run inStarterPlayerScripts,StarterCharacterScripts,StarterGui, or tools — not inWorkspaceorServerScriptService. ServerScripts belong inServerScriptService/Workspace.- Deprecated globals → use
task.wait/task.spawn/task.delay, not the oldwait()/spawn()/delay()(worse scheduling and throttling). - Parenting first, then setting properties → set properties first and
Parentlast so the instance replicates once in its final state. nilon the client right after join → objects stream/replicate over time; useparent:WaitForChild("Name")instead of indexing directly on the client.- Connections never disconnected → long-lived
:Connecthandlers leak and can fire on destroyed objects; store the connection and:Disconnect()(or useInstance:GetAttributeChangedSignal/:Oncewhere appropriate). - Using a RemoteFunction where a RemoteEvent fits →
RemoteFunctionblocks waiting for a return and a malicious/slow client can stall the server; prefer one-wayRemoteEvents 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 theUseNewLuauTypeSolverworkspace property, andLuauTypeCheckModesets 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,
RemoteFunctionvsRemoteEvent,:WaitForChildtiming,BindableEventfor same-context messaging, attributes,CollectionServicetags, and:Once/connection cleanup), readreferences/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.
Related skills
More from gamedev-skills/awesome-gamedev-agent-skills and the wider catalog.

roguelike
Build turn-based grid dungeons with procedural generation, permadeath, and field-of-view.

router
Routes game-dev requests to the right specialized skill based on engine and task.

rpg
Build RPGs with stats, leveling, inventory, quests, dialogue, and combat systems.

save-systems
Design crash-safe, versioned save/load for game state across sessions and patches.

shader-programming
Write portable game shaders—vertex/fragment pipeline, coordinate spaces, UV math, and common effects (dissolve, outline, rim light) in GLSL with HLSL equivalents.

steam-publish
Publish and update games on Steam using Steamworks, SteamPipe, and steamcmd.