権威サーバーと状態レプリケーション
遅延の中でも公平で滑らかなマルチプレイヤーを作りたい人へ。サーバー権威・予測と再調整・デルタ圧縮の原理を押さえれば、チートに強く帯域も抑えた同期を設計できます。
- サーバー権威モデルでは、全ゲーム状態の正本をサーバーだけが持ち、クライアントは入力を送りサーバーが判定した結果を受け取る。クライアントの申告を信用しないことがチート対策の土台になる。
- クライアント予測(自機を即座に動かす)と再調整(サーバー確定値との差を検知したら過去入力を再適用して補正)で、往復遅延を隠しつつ最終的な整合性をサーバーに委ねる。他プレイヤーは補間、超過分は外挿で描く。
- 状態は一定間隔のスナップショットで配り、前回確認済み状態との差分だけを送るデルタ圧縮+量子化+関心領域フィルタで帯域を抑える。UDP前提で信頼性は自前設計する。
なぜ「サーバーが正本を持つ」ことがすべての出発点なのか
リアルタイムマルチプレイヤーの根本課題は、物理的に離れた複数の機械が、光速の制約による遅延の中で、同一のゲーム世界を矛盾なく共有することです。素朴に「各クライアントが自分の状態を計算して他者に通知する」構成にすると、二つの致命的問題が生じます。第一に、遅延で状態がずれたとき「どちらが正しいか」を決められない(分岐した現実が併存する)。第二に、クライアントが送ってくる値を信じるしかなく、改造クライアントが「自分は当たっていない」「ダメージは0だ」と嘘をつけば防げません。
サーバー権威(authoritative server)モデルは、この両方を一手で解きます。ゲーム状態の正本(source of truth)はサーバーだけが保持し、クライアントはプレイヤーの入力(input)——押されたキー、狙った方向、撃った瞬間——だけを送ります。サーバーはその入力をゲームシミュレーションに適用し、当たり判定・ダメージ・アイテム取得などの結果を確定して、各クライアントへ配り返します。クライアントは受け取った状態を描画するだけの存在に近づきます。矛盾は起きえず(正本は一つ)、クライアントの主張はサーバーが検証するため嘘が通りません。
一周期の流れは (1) クライアントが入力を送信、(2) サーバーが全プレイヤーの入力を集めてシミュレーションを1ティック進め状態を更新、(3) サーバーが更新後状態を各クライアントへ配信、(4) クライアントが受信状態を描画、の繰り返しです。クライアントが持つのは「入力の権限」だけで、「結果を決める権限」は一切ありません。「クライアントは結果を提案できるが決定はしない」——この非対称性がチート耐性と一貫性の両方を生みます。
クライアント予測と再調整:往復遅延を隠しつつ整合性はサーバーに委ねる
サーバー権威を素直に実装すると、自機を動かすたびに入力送信→サーバー処理→結果受信の往復(RTT)を待つことになり、100ms級の操作遅延が体感されます。これは許容できません。そこでクライアント予測(client-side prediction)を導入します。クライアントは自分の入力をサーバーへ送ると同時に、同じシミュレーションをローカルでも即座に実行し、自機を先に動かして見せます。多くの場合サーバーも同じ結果を出すので、遅延は体感上消えます。
問題は、サーバーの確定結果がローカル予測とずれたとき(他プレイヤーとの衝突、外乱、モデル差)です。ここで再調整(server reconciliation)が働きます。クライアントは送った入力すべてに連番(シーケンス番号)を付けて保存しておきます。サーバーは状態を返す際「どの入力番号まで処理済みか」を併せて通知します。クライアントはその確定状態を土台に据え、まだサーバーに確認されていない入力を確定状態の上へ再適用(replay)して現在位置を作り直します。予測が正しければ位置は変わらず、ずれていればこの再適用で滑らかに正しい位置へ寄っていきます。
サーバー確定状態は必ず過去の時点のものです(返ってくるまでにRTT分の遅延がある)。その確定状態をそのまま表示すると自機がRTT分だけ過去へ巻き戻り、カクつきます。確定状態は正しい「過去の起点」なので、そこへ未確認入力を順に再適用すれば、サーバーと矛盾しない「現在」を再構築できます。保存する入力はサーバーが確認した番号までを破棄し、それ以降だけを保持すればよく、リングバッファで管理します。予測が当たっている限り再適用結果は前フレームと一致し、ユーザーは補正に気づきません。
他プレイヤーの表示は別の扱いになります。自機は予測しますが、他者の入力は手元にないため予測できません。到着済みの複数スナップショットの間を描くエンティティ補間(interpolation)で滑らかに見せ、意図的に約100〜200ms過去を描画します。パケットが途切れて次のスナップショットが未着のときだけ、速度を延長する外挿(extrapolation/dead reckoning)で当座をしのぎますが、外挿は当たると危険な推測なので短時間に限ります。撃った側と撃たれた側の時間軸のずれは、サーバーが撃った瞬間の過去状態へ巻き戻して判定するラグ補償(lag compensation)で吸収します。
状態スナップショットとデルタ圧縮:帯域を桁で削る
横にスクロール
サーバーは全クライアントへ毎ティック状態を送りますが、ゲーム世界の全エンティティを毎回フルで送れば帯域が破綻します。ここで効くのがスナップショット+デルタ圧縮(delta compression)です。基本発想は「前回そのクライアントが確実に受け取った状態(ベースライン)と、今の状態の差分だけを送る」こと。動いていないオブジェクトや変化のないフィールドは送らず、変わった値だけをビットに載せます。
デルタ圧縮の基本ループ(サーバー側・クライアント別):
1. クライアントごとに「確認済みの最新スナップショット番号」を保持
2. 現在のスナップショット S_now と、その確認済み S_ack を比較
3. フィールド単位で S_now と S_ack が異なる箇所だけをエンコード
→ 変化ビットマスク + 変化した値のみ送信
4. パケットに「基準にした S_ack の番号」を明記して送る
5. クライアントは受信デルタを S_ack へ適用して S_now を復元し、
受信を確認(次回のベースラインになる)
※ UDP前提。デルタは「特定のベースラインからの差」なので、
どのスナップショットを基準にしたか(番号)が一致しないと復元不能。
デルタと直交して効くのが量子化(quantization)です。座標を32bit浮動小数のまま送る必要はなく、マップの範囲と要求精度から必要ビット数を割り出し、整数に丸めて送ります(例:位置を1cm刻みの固定小数に、回転角を10〜12bitに)。値の範囲が既知なら情報量は激減します。さらに関心領域(Area of Interest/relevancy)によるフィルタで、そのプレイヤーから見えない・遠すぎるエンティティは配信対象から外します。これは帯域削減であると同時に、壁の向こうの敵情報をクライアントへ送らないことでウォールハック(wallhack)系チートの成立余地を断つセキュリティ施策でもあります。
低遅延のため通信は基本UDPで、パケットは失われ順序も乱れます。ここで「前フレームとの差」を素朴に連鎖させると、1パケット落ちた瞬間に以降すべてが復元不能になります。堅牢な実装は、各デルタがクライアントが確認応答(ack)した特定のベースラインを基準にします。あるスナップショットのackが届かなければ、サーバーは次も「まだ確認済みの古いベースライン」を基準にデルタを作り続けるため、途中の欠落があっても最新のackから復元できます。TCPを使わないのは、パケットロス時の再送とヘッドオブラインブロッキングが遅延を増やし、リアルタイム性を損なうためです。信頼性が要る一部データ(チャット・確定イベント)だけを選択的に再送する仕組みを自前で載せます。
チート対策:権威モデルは前提であって万能ではない
サーバー権威は最強のチート対策基盤ですが、それだけで完結はしません。「クライアントから来る値は一切信用せず、常に検証する」という原則を各所に貫く必要があります。移動なら、クライアントが申告した新座標をそのまま採用せず、前回位置・経過時間・最大速度から到達可能かをサーバーが検算し、逸脱すればスピードハックとして棄却・巻き戻します。射撃なら、ダメージ量はサーバーが武器データから決め、クライアントの申告は使いません。前述の関心領域フィルタで見えない情報を送らないことは、情報系チートへの構造的な防御になります。
クライアントを信頼する実装は原理的に破られます。代表例は、(1) クライアントが計算したダメージ値をサーバーがそのまま適用する、(2) 当たり判定をクライアント側だけで行い結果だけ送らせる、(3) 全エンティティ状態を無差別に全員へ配りクライアント側で描画を間引く(=壁越しの敵位置が改造クライアントから丸見えになりウォールハックを許す)。いずれも「決定権をクライアントに渡している」点が共通の欠陥です。決定は必ずサーバーで行い、送る情報はそのプレイヤーに必要な分だけに絞ります。
クライアント側の対策(アンチチートによるメモリ改ざん・不正プロセス検知)は補助であり、根拠にはできません。理由は明快で、クライアントは攻撃者の完全な支配下にある機械だからです。サーバー側検証を主、クライアント側検知を従とし、さらに統計的な異常検知(人間には不可能なエイム精度・反応速度)でカバーするのが実務の多層防御です。
| 手法 | 扱う相手 | 目的 | 誤ると起きること |
|---|---|---|---|
| クライアント予測 | 自機 | 自分の操作遅延を隠す | 予測なし=RTT分の操作遅延で重い |
| サーバー再調整 | 自機 | 予測と確定のずれを補正 | 巻き戻り・ワープ・位置の食い違い |
| エンティティ補間 | 他プレイヤー | 受信状態の間を滑らかに描画 | カクつき・瞬間移動して見える |
| 外挿(dead reckoning) | 他プレイヤー | パケット未着時の当座の推測 | 長く使うとゴム紐現象・誤位置 |
| ラグ補償 | 撃つ側と撃たれる側 | 過去へ巻き戻して当たり判定 | 撃たれた側が理不尽に感じる被弾 |
| デルタ圧縮+量子化 | 全状態 | 帯域を差分と精度で削減 | ベースライン不一致で復元不能 |
| 関心領域フィルタ | 全状態 | 不要情報を配らない(帯域+防御) | 壁越し情報漏れでウォールハック |
- サーバー権威の本質: 状態の正本はサーバーのみが持ち、クライアントは入力を送るだけ。決定権をクライアントへ渡さないことが一貫性とチート耐性の両立点。
- 予測と再調整はワンセット: 自機はローカルで即予測し、入力を連番で保存。サーバー確定状態+未確認入力の再適用で、遅延を隠しつつ最終整合をサーバーに委ねる。
- 自機と他機で手法が違う: 自機=予測+再調整、他機=補間(+短時間の外挿)。混同しない。撃ち合いの時間差はラグ補償で吸収。
- 帯域の三段構え: デルタ圧縮(差分)+量子化(精度削減)+関心領域フィルタ(不要分を配らない)。UDP前提で、デルタは連鎖でなくack済みベースライン参照で設計する。
- チート対策の原則: クライアントの申告は常にサーバーで検算(到達可能性・ダメージ・可視性)。クライアント側アンチチートは補助にすぎない。
まとめ
権威サーバーモデルは、ゲーム状態の正本をサーバーだけに持たせ、クライアントを「入力を送り結果を受け取る」存在に限定することで、分岐した現実を排し、クライアントの嘘を無効化します。素直に実装すると操作遅延が問題になるため、自機はクライアント予測で即座に動かし、入力を連番で保存し、サーバー確定状態の上へ未確認入力を再適用する再調整で、遅延を隠しつつ最終的な整合をサーバーへ委ねます。他プレイヤーは過去のスナップショット間を補間で描き、未着時のみ短時間の外挿で補い、撃ち合いの時間差はラグ補償で吸収します。帯域は、ack済みベースラインとの差分だけを送るデルタ圧縮、範囲既知の値を整数に丸める量子化、見えないエンティティを配らない関心領域フィルタの三段で桁単位に削ります。通信はUDPを基盤に信頼性を選択的に自前設計します。そしてチート対策の根幹は「クライアントの申告を一切信用せず、移動・ダメージ・可視性をサーバーで常に検証する」ことにあり、クライアント側アンチチートはあくまで補助です。低遅延・公平性・帯域効率・改ざん耐性という互いに衝突しがちな要求を、これらの技法の組み合わせで同時に満たすのがネットコード設計の核心です。関連する基盤として、UDP/TCPやパケット挙動はネットワークのトピック、改ざん耐性やアンチチートの位置づけはセキュリティのトピック、補間・外挿の描画側の質はグラフィックスのトピックも参照してください。
ゲーム開発の記事ガイド
権威サーバーと状態レプリケーションを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ゲーム開発
比較で見る軸
難易度: advanced / カテゴリ: ゲーム開発 / タグ数: 6
導入後に効く点
クライアント予測(自機を即座に動かす)と再調整(サーバー確定値との差を検知したら過去入力を再適用して補正)で、往復遅延を隠しつつ最終的な整合性をサーバーに委ねる。他プレイヤーは補間、超過分は外挿で描く。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ゲーム開発
- タグ数
- 6
判断チェックリスト
- 自社の用途が「ゲーム開発 / ネットコード」に近いか確認する。
- 強みである「サーバー権威モデルでは、全ゲーム状態の正本をサーバーだけが持ち、クライアントは入力を送りサーバーが判定した結果を受け取る。クライアントの申告を信用しないことがチート対策の土台になる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。