mcpField
R
roblox-networking

roblox-networking

github.com/gamedev-skills開発5.4k4.2k 回インストール1 日前に更新
リポジトリ

roblox-networking について

roblox-networkingは、クライアントとサーバー間の通信を設計・保護するRoblox開発者向けスキルです。サーバー権威の状態、ペイロード検証、レプリケーションの選択、マルチプレイヤーでの不正テストを扱います。

主な機能

  1. 01既存のリモートプロトコルの整理
  2. 02メッセージの意味に応じた転送方式の選択
  3. 03ペイロードとプレイヤー状態の検証
  4. 04サーバーでのゲーム状態変更
  5. 05リクエスト頻度の制限
  6. 06マルチプレイヤーと不正ケースのテスト

活用シーン

  1. 01クライアントによるダメージや価格の指定防止
  2. 02Instanceを対象とするリモートの保護
  3. 03過剰なリモート通信の削減
  4. 04リスポーン後の古いリクエストへの対処

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/roblox-networkingを入力して読み込まれたか確認します。

同梱ファイルagents/openai.yamlreferences/validation-and-testing.md

詳細

Roblox ネットワーク

サーバーがゲーム状態の権限を持ち、クライアントが入力や意図を提供する、明確なリクエストとレプリケーションの契約を構築します。Roblox のローリングプラットフォーム API を対象としています。このスキルでは、roblox-luau のネットワーク入門よりも踏み込んで解説します。

使用する場面

  • Roblox の境界を越える通信を設計、実装、デバッグ、または保護するときに使用します。
  • リモートがクライアントの値を信頼している場合、悪意のあるユーザーが任意の Instance を対象にできる場合、メッセージがサービスに大量送信される場合、ストリーミングされたオブジェクトが見つからない場合、またはクライアントとサーバーの状態が一致しない場合に使用します。

使用しない場面: 基本的な Luau やサービスについては roblox-luau を、永続状態については roblox-datastores を使用してください。物理的な所有権の仕組みには roblox-physics も組み合わせてください。

ワークフロー

  1. 既存のプロトコルを調べます。 すべてのリモートと両方のエンドポイントを探し、方向、送信者、ペイロード、頻度、権限、検証、利用側を記録します。正規のリモートフォルダーを再利用してください。調査を省略したために重複を作らないでください。
  2. 各メッセージを分類します。 クライアントリクエスト、サーバーの事実、または一時的な見た目用サンプルのいずれかです。利便性ではなく意味に基づいて、信頼性のあるイベント、信頼性のないイベント、リクエスト/レスポンスを選択します。
  3. ペイロードを最小限にします。 安定した識別子と意図を送信します。サーバーが導き出せる価格、ダメージ、所有権の結果、任意のパス、計算済みの結果は送信しないでください。
  4. 段階的に検証します。 高コストな処理を行う前に、型/構造/有限値、許可リストに含まれる値、Instance のクラスと祖先、プレイヤーの権限/状態、必要に応じて距離と視線、サーバー側のクールダウン、レート制限を確認します。
  5. サーバーで適用します。 サーバーが対象を特定し、体力、インベントリ、通貨、クールダウン、進行状況を変更します。クライアント側のチェックは UX を向上させますが、信頼性を保証するものではありません。
  6. 必要な範囲だけレプリケーションします。 非公開またはローカルな事実には FireClient を使用し、共有される事実のみをブロードキャストします。クライアントが別個の表示イベントを必要としない限り、レプリケーション済みのプロパティを再度送信しないでください。
  7. 時間とライフサイクルを考慮します。 リクエストは、死亡、リスポーン、ストリーミングの変更、切断の後に届くことがあります。処理時に現在のキャラクター/状態を取得し、プレイヤーごとのレート制限データをクリーンアップします。
  8. Server & Clients で検証します。 通常、形式不正、大量送信、範囲外、古いキャラクター、高速なリスポーン、退出、複数プレイヤーの同時操作、個別指定、ブロードキャストの各ケースを試します。サーバーと各クライアントの Output を個別に確認します。

通信方式を選択する

プリミティブ用途使用しない用途
RemoteEvent順序が保証された信頼性のある一方向のリクエスト/事実新しいデータが古いデータに置き換わる連続サンプル
UnreliableRemoteEvent損失や順序の入れ替わりを許容する、一時的な見た目用/連続状態購入、ダメージの決定、インベントリ、一度限りの状態遷移
RemoteFunction即時の応答が本当に必要な、範囲を限定したクライアントからサーバーへの問い合わせサーバーからクライアントへの呼び出し、長時間かかる/所要時間が不確かな処理、通常のコマンド

サーバーからクライアントを同期的に呼び出してはいけません。クライアントが切断したり、エラーを起こしたり、応答しなかったりする可能性があります。必要に応じて、サーバー側の RemoteEvent:FireClient() と個別の応答イベントを使用してください。

パターン:ゲームプレイの対象を特定する前に検証する

-- ServerScriptService/CombatRequests.server.luau
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

local attack = ReplicatedStorage.Remotes.Attack
local lastRequest: {[Player]: number} = {}
local RANGE = 12
local COOLDOWN = 0.25

attack.OnServerEvent:Connect(function(player: Player, target: unknown)
    local now = Workspace:GetServerTimeNow()
    if now - (lastRequest[player] or -math.huge) < COOLDOWN then return end
    lastRequest[player] = now

    if typeof(target) ~= "Instance" or not target:IsA("Model") then return end
    if not target:IsDescendantOf(Workspace.Characters) then return end
    local targetHumanoid = target:FindFirstChildOfClass("Humanoid")
    local targetRoot = target:FindFirstChild("HumanoidRootPart")
    local character = player.Character
    local root = character and character:FindFirstChild("HumanoidRootPart")
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not targetHumanoid or not targetRoot or not root or not humanoid then return end
    if humanoid.Health <= 0 or targetHumanoid.Health <= 0 then return end
    if (root.Position - targetRoot.Position).Magnitude > RANGE then return end
    if not serverCombatStateAllowsAttack(player, now) then return end

    targetHumanoid:TakeDamage(serverDamageFor(player))
end)

Players.PlayerRemoving:Connect(function(player)
    lastRequest[player] = nil
end)

これはあくまで簡潔な例です。実際の近接攻撃システムでは、サーバーが把握する攻撃可能時間、視線/形状チェック、チームルール、遅延への対処が必要になる場合があります。距離チェックを 1 つ行うだけで安全になると考えないでください。

パターン:境界でのトークンバケット

type Bucket = {tokens: number, updatedAt: number}
local buckets: {[Player]: Bucket} = {}
local CAPACITY, REFILL_PER_SECOND = 6, 3

local function consume(player: Player, cost: number): boolean
    local now = os.clock()
    local bucket = buckets[player] or {tokens = CAPACITY, updatedAt = now}
    bucket.tokens = math.min(CAPACITY,
        bucket.tokens + (now - bucket.updatedAt) * REFILL_PER_SECOND)
    bucket.updatedAt = now
    if bucket.tokens < cost then buckets[player] = bucket; return false end
    bucket.tokens -= cost
    buckets[player] = bucket
    return true
end

コストはサーバーへの影響に応じて設定します。データストアの呼び出し、クローン作成、レイキャスト、広範囲へのレプリケーションを行う前に、低コストでリクエストを拒否してください。拒否したパケットごとに警告を出すのではなく、不正利用の兆候を集計して記録します。

レプリケーション、ストリーミング、予測

  • レプリケーションされたインスタンス/プロパティは、すでに状態チャネルです。リモートは、意図、プライベートな状態、または表示上の合図に使用し、DataModel の無条件な並列コピーには使用しないでください。
  • インスタンスのストリーミングでは、有効なサーバー Instance がクライアントに存在しないことがあります。安定した ID を送信して不在を許容し、オプションのストリーミングコンテンツをいつまでも待たないでください。
  • 高頻度の見た目だけのデータには UnreliableRemoteEvent を使用できます。配信と順序は保証されないため、各サンプルを自己完結させてください。1000 bytes を超えるペイロードは破棄されます(Studio Output に超過分が表示されます)。RemoteEvent と UnreliableRemoteEvent には、すべての同種リモートを合算してクライアントあたり約 500 calls/second のスロットルも適用されます。これは、攻撃者より先に正規のプレイヤーが上限に達する値です。
  • レイテンシに敏感で、取り消し可能な表示だけを予測してください。クライアントのシーケンス/コマンド ID を含め、サーバーは権威ある状態と確認応答を返し、クライアントは滑らかに修正します。予測によってダメージ、通貨、インベントリ、または進行状況を付与してはいけません。
  • ネットワーク所有権は応答性を高めますが、そのクライアントが物理シミュレーションに影響を与えられるようになります。ゲームプレイ上の結果はサーバーで検証してください。所有権は認可ではありません。

よくある失敗

症状考えられる原因対処法
エクスプロイターがダメージ/価格を選択できるクライアントからの結果を受け入れている意図/ID を送信し、サーバーで導出して適用する
任意のオブジェクトを削除できるtypeof(Instance) しか確認していないクラス、階層、所有権、状態、許可リストに登録された操作を検証する
サーバーがプレイヤーへの処理で停止するサーバーがクライアントの RemoteFunction を呼び出している非同期イベントに置き換える
正規のプレイヤーでスロットルが発生するフレームごとに信頼性の高いメッセージを送信している頻度を下げる、状態レプリケーションを使う、バッチ処理する、または見た目だけのデータには信頼性の低いイベントを使う
古いパケットで新しい効果が取り消される順序が保証されないサンプルをコマンドとして扱っているサンプルを置き換え可能/バージョン管理されたものにする。状態遷移には信頼性の高いイベントを使う
リスポーン後にリモートが機能しなくなるキャラクター/ルートをキャッシュしている処理時に現在のキャラクターを取得し、古い状態を拒否する
プライベートデータが漏れるデフォルトで FireAllClients を使用しているFireClient と最小限のペイロードを使用する
距離チェックを回避されるクライアント所有のオブジェクトがターゲットの近くに移動された重要なオブジェクトをアンカーする/サーバー所有にし、サーバー側の完全なコンテキストを検証する

リソース

  • ペイロードのルール、Instance/有限値チェック、レプリケーション設計、必須の複数クライアント悪用テストマトリックスについては、references/validation-and-testing.md をお読みください。

関連スキル

  • roblox-luau — 実行場所と RemoteEvent の基本的な仕組み。
  • roblox-characters — リスポーンに対応したキャラクターの取得。
  • roblox-physics — ネットワーク所有権、レイ/オーバーラップ検証、物理的な結果。
  • roblox-studio-workflow — Server & Clients によるテストと Output の確認。

主な参考資料

  • https://create.roblox.com/docs/scripting/events/remote
  • https://create.roblox.com/docs/scripting/security/client-server-boundary
  • https://create.roblox.com/docs/physics/network-ownership
  • https://create.roblox.com/docs/studio/testing-modes

コメント