roblox-remote-events
SKILL.md
Roblox Remote Events & Functions
RemoteEvent vs RemoteFunction
| Type | Direction | Returns value? | Use when |
|---|---|---|---|
RemoteEvent |
Any direction | No (fire-and-forget) | Notifying server of player action, broadcasting state |
RemoteFunction |
Client→Server | Yes (yields caller) | Client needs a result back (e.g. fetch inventory) |
UnreliableRemoteEvent |
Any direction | No | High-frequency updates where dropped packets are fine |
Default to RemoteEvent. Avoid server→client RemoteFunction — an exploiter's frozen callback stalls your server thread indefinitely.
Where to Put Remotes
Always store Remotes in ReplicatedStorage. Create them from a server Script that runs before any LocalScript.
ReplicatedStorage/
Remotes/
DealDamage (RemoteEvent)
GetInventory (RemoteFunction)
SyncPosition (UnreliableRemoteEvent)
-- Script in ServerScriptService
local folder = Instance.new("Folder")
folder.Name = "Remotes"
folder.Parent = game:GetService("ReplicatedStorage")
local function make(class, name)
local r = Instance.new(class)
r.Name = name
r.Parent = folder
return r
end
make("RemoteEvent", "DealDamage")
make("RemoteFunction", "GetInventory")
make("UnreliableRemoteEvent", "SyncPosition")
Firing Patterns
Client → Server (FireServer)
-- LocalScript
local DealDamage = game:GetService("ReplicatedStorage").Remotes:WaitForChild("DealDamage")
DealDamage:FireServer({ targetId = 12345, amount = 50 })
-- First arg on server is always the firing Player (injected automatically, cannot be spoofed)
-- Script (server) — VALIDATE everything in the payload
DealDamage.OnServerEvent:Connect(function(player, data)
-- player identity is trustworthy; data contents are not
end)
Server → One Client
local Notify = game:GetService("ReplicatedStorage").Remotes:WaitForChild("Notify")
Notify:FireClient(player, { message = "Welcome!" })
-- LocalScript
Notify.OnClientEvent:Connect(function(data)
print(data.message)
end)
Server → All Clients
AnnounceEvent:FireAllClients({ text = "Game starting in 10 seconds!" })
RemoteFunction (Client Calls, Server Returns)
-- Script (server)
GetInventory.OnServerInvoke = function(player)
return getPlayerInventory(player.UserId)
end
-- LocalScript
local inventory = GetInventory:InvokeServer() -- yields until server returns
UnreliableRemoteEvent (High-Frequency Sync)
-- LocalScript
RunService.Heartbeat:Connect(function()
SyncPosition:FireServer(character.HumanoidRootPart.CFrame)
end)
-- Script (server) — still validate
SyncPosition.OnServerEvent:Connect(function(player, cframe)
if typeof(cframe) ~= "CFrame" then return end
-- apply with sanity bounds check
end)
CRITICAL: Server-Side Security
The client is hostile. Treat every argument as untrusted input.
local MAX_DAMAGE = 100
local COOLDOWNS = {}
local COOLDOWN_SECONDS = 0.5
DealDamage.OnServerEvent:Connect(function(player, data)
-- 1. Rate limit
local now = tick()
if COOLDOWNS[player.UserId] and now - COOLDOWNS[player.UserId] < COOLDOWN_SECONDS then
return
end
COOLDOWNS[player.UserId] = now
-- 2. Type checks
if type(data) ~= "table" then return end
if type(data.targetId) ~= "number" then return end
if type(data.amount) ~= "number" then return end
-- 3. Range clamp
local amount = math.clamp(data.amount, 0, MAX_DAMAGE)
-- 4. Server-side weapon lookup — never trust client-provided Instance
local weapon = getEquippedWeapon(player)
if not weapon then return end
-- 5. Server-side target lookup
local target = getPlayerByUserId(data.targetId)
if not target then return end
applyDamage(target, amount, player)
end)
Exploit Patterns & Defenses
| Exploit | What the attacker does | Defense |
|---|---|---|
| Argument injection | Sends unexpected types to crash handler | Type-check all arguments |
| Damage amplification | Sends amount = math.huge |
Clamp to sane maximum |
| Remote spam | Fires thousands of times per second | Per-player cooldown |
| Spoofed target | Sends another player's UserId | Server resolves from its own state |
| Infinite yield | Never returns from OnClientEvent callback |
Avoid server→client RemoteFunction |
| Duplicate action | Replays a valid fire to buy twice | Check state / consume token before acting |
Quick Reference
FireServer(args) LocalScript → server
FireClient(player, args) server → one client
FireAllClients(args) server → every client
InvokeServer(args) LocalScript → server, waits for return
OnServerEvent server-side listener for FireServer
OnClientEvent client-side listener for FireClient/FireAllClients
OnServerInvoke server-side function assigned for InvokeServer
Common Mistakes
| Mistake | Fix |
|---|---|
OnServerEvent in a LocalScript |
Use OnClientEvent on client; OnServerEvent is server-only |
| Remotes in ServerStorage | Move to ReplicatedStorage |
| Trusting payload beyond player identity | Validate every field in the payload |
| Server→client RemoteFunction | Use RemoteEvent; frozen client stalls server thread |
No WaitForChild in LocalScript |
Remotes may not exist yet; always use WaitForChild |
Multiple OnServerInvoke assignments |
Only the last assignment wins; keep it in one place |
| Firing inside tight loop without throttle | Use UnreliableRemoteEvent or accumulate delta time |
Weekly Installs
18
Repository
sentinelcore/ro…x-skillsGitHub Stars
1
First Seen
Feb 23, 2026
Security Audits
Installed on
opencode18
gemini-cli18
github-copilot18
codex18
amp18
kimi-cli18