roblox-remote-events について
Roblox のクライアント・サーバー通信を実装する開発者向けの Remote Events と Functions のスキルです。通信パターンや配置に加え、サーバー側の検証とセキュリティ対策を説明します。
主な機能
- 01RemoteEvent、RemoteFunction、UnreliableRemoteEvent の比較
- 02Remote の配置場所と作成順序の説明
- 03クライアントとサーバーの呼び出し例
- 04検証、レート制限、リプレイ対策の解説
活用シーン
- 01通知、戻り値、高頻度同期に適した Remote の選択
- 02クライアント・サーバー間メッセージングの構築
- 03Remote ハンドラーのセキュリティリスク確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ sentinelcore/ roblox-skills --skill roblox-remote-eventsインストール後、Claude Code で/roblox-remote-eventsを入力して読み込まれたか確認します。
詳細
Roblox リモートイベント & リモート関数
RemoteEvent と RemoteFunction の比較
| 種類 | 方向 | 値を返すか? | 使用すべき場面 |
|---|---|---|---|
RemoteEvent | 任意の方向 | いいえ(送信して終了) | プレイヤーの行動をサーバーに通知する、状態をブロードキャストする |
RemoteFunction | クライアント→サーバー | はい(呼び出し元を待機させる) | クライアントが結果を受け取る必要がある場合(例: インベントリの取得) |
UnreliableRemoteEvent | 任意の方向 | いいえ | パケットが失われても問題ない高頻度の更新 |
基本的には RemoteEvent を使用してください。 サーバー→クライアントの RemoteFunction は避けてください。エクスプロイターがコールバックをフリーズさせると、サーバースレッドが無期限に停止してしまいます。
リモートを配置する場所
リモートは常に ReplicatedStorage に保存してください。どの LocalScript よりも先に実行されるサーバーの Script から作成します。
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")
発火パターン
クライアント → サーバー(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)
サーバー → 特定の 1 クライアント
local Notify = game:GetService("ReplicatedStorage").Remotes:WaitForChild("Notify")
Notify:FireClient(player, { message = "Welcome!" })
-- LocalScript
Notify.OnClientEvent:Connect(function(data)
print(data.message)
end)
サーバー → すべてのクライアント
AnnounceEvent:FireAllClients({ text = "Game starting in 10 seconds!" })
RemoteFunction(クライアントが呼び出し、サーバーが返す)
-- Script (server)
GetInventory.OnServerInvoke = function(player)
return getPlayerInventory(player.UserId)
end
-- LocalScript
local inventory = GetInventory:InvokeServer() -- yields until server returns
UnreliableRemoteEvent(高頻度の同期)
-- 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)
重要: サーバー側のセキュリティ
クライアントは敵対的です。すべての引数を信頼できない入力として扱ってください。
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)
エクスプロイトのパターンと防御策
| エクスプロイト | 攻撃者が行うこと | 防御策 |
|---|---|---|
| 引数の注入 | 予期しない型を送信してハンドラーをクラッシュさせる | すべての引数を型チェックする |
| ダメージの増幅 | amount = math.huge を送信する | 妥当な最大値にクランプする |
| リモートのスパム | 1 秒間に数千回発火する | プレイヤーごとのクールダウン |
| なりすましターゲット | 別のプレイヤーの UserId を送信する | サーバー自身の状態から解決する |
| 無限の待機 | OnClientEvent コールバックから決して戻らない | サーバー→クライアントの RemoteFunction を避ける |
| 行動の重複 | 有効な発火を再送して 2 回購入する | 行動する前に状態を確認する / トークンを消費する |
クイックリファレンス
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
よくある間違い
| 間違い | 修正方法 |
|---|---|
LocalScript で OnServerEvent を使う | クライアントでは OnClientEvent を使う。OnServerEvent はサーバー専用です |
| リモートを ServerStorage に置く | ReplicatedStorage に移動する |
| プレイヤーの身元以外のペイロードを信頼する | ペイロードのすべてのフィールドを検証する |
| サーバー→クライアントの RemoteFunction | RemoteEvent を使う。フリーズしたクライアントがサーバースレッドを停止させる |
LocalScript に WaitForChild がない | リモートがまだ存在しない可能性があります。常に WaitForChild を使う |
複数の OnServerInvoke の割り当て | 最後の割り当てだけが有効になります。1 か所にまとめる |
| スロットルなしでタイトなループ内で発火する | UnreliableRemoteEvent を使う、またはデルタタイムを蓄積する |
コメント
Markdown 対応