mcpField
R
roblox-remote-events

roblox-remote-events

github.com/sentinelcore開発29584 回インストール35 週間前に更新
リポジトリ

roblox-remote-events について

Roblox のクライアント・サーバー通信を実装する開発者向けの Remote Events と Functions のスキルです。通信パターンや配置に加え、サーバー側の検証とセキュリティ対策を説明します。

主な機能

  1. 01RemoteEvent、RemoteFunction、UnreliableRemoteEvent の比較
  2. 02Remote の配置場所と作成順序の説明
  3. 03クライアントとサーバーの呼び出し例
  4. 04検証、レート制限、リプレイ対策の解説

活用シーン

  1. 01通知、戻り値、高頻度同期に適した Remote の選択
  2. 02クライアント・サーバー間メッセージングの構築
  3. 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 に移動する
プレイヤーの身元以外のペイロードを信頼するペイロードのすべてのフィールドを検証する
サーバー→クライアントの RemoteFunctionRemoteEvent を使う。フリーズしたクライアントがサーバースレッドを停止させる
LocalScript に WaitForChild がないリモートがまだ存在しない可能性があります。常に WaitForChild を使う
複数の OnServerInvoke の割り当て最後の割り当てだけが有効になります。1 か所にまとめる
スロットルなしでタイトなループ内で発火するUnreliableRemoteEvent を使う、またはデルタタイムを蓄積する

コメント