ハイブリッドMatchmakingのエンジニアリング:『Call of Duty』のキュー分離から紐解く現代のゲームMatchmaking Architecture
要点まとめ
Activisionが『Call of Duty: Black Ops 7』で導入した3つのMatchmakingキュー分離を題材に、現代のゲームMatchmaking Architectureにおける技術的トレードオフを解説します。SBMMと接続優先の数学的メカニズム、動的なルール拡張アルゴリズムのC#実装、並行処理やDedicated Serverプロビジョニングにおけるインフラ課題について深く考察しています。低レイテンシかつスケーラブルなマルチプレイヤーBackendを構築するための実践的なベストプラクティスを提示します。
Activisionが『Call of Duty: Black Ops 7』において、プレイヤーベースを「Skill-Based Matchmaking(SBMM)」、「クラシック Connection-Based(接続優先)」、「ハイブリッド」という3つの独立したMatchmakingキューに分割することを発表した際、長年議論されてきたBackendエンジニアリングの課題が一躍脚光を浴びることとなりました。対戦型タイトルにおいて、プレイヤーのペアリング方法を決定することは、単なるゲームデザイン上の嗜好にとどまりません。サブミリ秒単位のネットワーク制約、数学的なスキル分散、プール分断、そしてクラウドのコンピュートコストのバランスを取る、複雑なゲームMatchmaking Architectureの課題なのです。
Matchmakingシステムを複数の独立したキューに分離することは、一見するとシンプルなプレイヤーの嗜好機能のように思えます。しかし実際には、Backendインフラストラクチャにおけるエンジニアリングオーバーヘッドを2倍、3倍へと膨れ上がらせます。同時接続プレイヤー(CCU)を複数のキューに分割すると、チケット密度が急激に低下し、低密度リージョンでのキュー待ち時間が指数関数的に増大、サーバー割り当てアルゴリズムのチャーンが高まります。
本記事では、SBMMと接続優先Matchmakingの技術的なトレードオフを分析し、動的なハイブリッドキュー拡張の背景にある数学的メカニズムを紐解きます。さらに、チケット処理を行う実際のBackend C#コードを検証し、クリーンにスケールする堅牢なMatchmakingプールの構築方法を探ります。
避けては通れないMatchmakingのトレードオフ(The Immutable Matchmaking Trilemma)
現代のすべてのゲームMatchmaking Architectureは、相反する3つの変数によって縛られた制約最適化問題を解決する必要があります。
- レイテンシ(RTT): プレイヤーのクライアントと割り当てられたDedicated Serverインスタンス間のラウンドトリップタイム(ミリ秒単位)。
- スキル差($\Delta$MMR): 与えられたロビー内のプレイヤー間におけるスキル表現(MMR、Elo、TrueSkillなど)の数学的ギャップ。
- キュー待ち時間($T_{queue}$): 有効なマッチチケットがアクティブなサーバー割り当てへと遷移するまでに、プレイヤーがアイドル状態で待機する合計時間。
Latency (RTT)
/ \
/ \
/ Ideal \
/ Match \
/ \
Skill Delta (ΔMMR) --------------- Queue Duration (T_queue)
これらの変数のうち任意の2つを最適化することは容易ですが、残る1つを犠牲にすることになります。
- 低レイテンシ + 小さいスキル差: エンジンは、同一のクラウドデータセンターの近くに住み、かつ完璧にスキルが一致する稀有なプレイヤーを探す必要があるため、キュー待ち時間が長くなります。
- 短いキュー待ち時間 + 小さいスキル差: マッチメイカーが同等のスキルの対戦相手を見つけるために地理的な検索半径を世界中に広げる必要があるため、レイテンシが高くなります。
- 短いキュー待ち時間 + 低レイテンシ: パフォーマンス指標に関係なくマッチメイカーが即座に最も近い利用可能なクライアントを確保するため、スキル分散が大きくなります(従来の「接続優先」パブリックマッチの体験)。
『Black Ops 7』のようなタイトルが3つの独立したキューモードを導入すると、基盤となるMatchmaking Architectureは、断片化されたメモリプール上で3つの並列ルール評価ループを維持することを強迫されます。
特定のリージョン(例えば午前4時の南米など)における Concurrent Users(CCU)が2,000名以下に落ち込んだ場合、それらのプレイヤーを3つの分離されたプールに分割すると、各キューのローカル密度はわずか数件から数百名にまで低下します。その結果、接続優先キューは低ピンの Dedicated Server を発見できず、SBMMキューは永久にスタック(停止)することになります。
SBMM vs Ping優先 vs ハイブリッドキューの分解
数百万ものマッチチケットを処理できる Backend を構築するには、まず各アーキテクチャモデルが内部でどのように動作するかを理解する必要があります。
1. Connection-Based(Ping優先)Architecture
接続優先エンジンでは、スキルマトリクスは完全に二次的な扱いとなるか、破棄されます。主な目的は、ネットワークの劣化(ジッター、パケットロス、高RTT)を最小限に抑えることです。
- クライアントPingプロービング: キューを開始すると、ゲームクライアントは一連のリージョナルEdge Gateway(例:
us-east-1、eu-central-1、ap-southeast-1)に対してICMPまたはUDPのPingビーコンを送信します。 - Pingベクトルの生成: クライアントはレイテンシのベクトル(
[ us-east: 24ms, us-west: 78ms, eu-central: 142ms ])を構築し、Matchmakingのペイロードに添付します。 - 空間インデックス作成(Spatial Indexing): マッチメイカーは、許容可能なレイテンシ閾値(例: $RTT < 50ms$)に基づいて、プレイヤーを厳密にリージョンハッシュにバケット分けします。
ネットワークトポロジによってマッチ作成が決定されるため、プレイヤーの検索バケットは予測可能であり、単純なFIFO(First-In, First-Out)空間キューを使用してチケットを $O(1)$ 時間で解決できます。
2. Skill-Based Matchmaking(SBMM)Architecture
SBMMは、多次元ガウス分布(TrueSkill 2など)やカスタムElo変形を用いてプレイヤーの能力をモデリングし、マッチの公平性を優先します。主な入力値には、勝敗比、キル/デス比(K/D)、1分あたりのダメージ量(DPM)、直近のパフォーマンス推移などが含まれます。
- 距離の評価: マッチメイカーは、$N$次元のスキル空間における候補プレイヤーベクトル間のユークリッド距離またはマハラノビス距離を計算します。
- ソートコスト: マッチメイカーは単純なFIFOキューに依存できません。許容可能なスキル分散 $\sigma$ 内の候補を迅速に見つけるために、チケットプロファイルのソート済みセットまたは空間ツリー(KDツリーなど)を維持する必要があります。
- 検索空間の縮小: スキルが上昇するにつれて(例: 上位0.5%のプレイヤー)、適格な候補のプールは急激に縮小します。これにより、システムはチケットを無制限に保持するか、スキル制約の厳密性を徐々に緩和(Decay)せざるを得なくなります。
3. Dynamic Hybrid Architecture
プレイヤーにハードコードされたキューの選択を強要する代わりに、現代のプロダクション Backend ではDynamic Hybridモデルを実装することが多くなっています。このセットアップでは、すべてのマッチチケットは厳格なSBMMと低レイテンシの制限から始まります。$T_{queue}$ が増加するにつれて、ルール減衰関数が許容されるスキル差($\Delta MMR$)とレイテンシ上限($RTT_{max}$)を継続的に拡張していきます...
$$\Delta MMR_{allowed}(t) = \Delta MMR_{base} + \alpha \cdot t^{\gamma}$$
$$RTT_{allowed}(t) = \min\left(RTT_{max_cap}, RTT_{base} + \beta \cdot \lfloor t / \Delta t_{step} \rfloor\right)$$
ここで、$\alpha$ と $\beta$ は拡張係数、$\gamma$ は指数曲線の急峻さを制御し、$t$ は経過したキュー待ち時間(秒)です。
動的な条件緩和を活用することで、ピーク時のCCU期間中に高いマッチクオリティを維持しながら、無限のキュー停滞を防止できます。
コード実装:動的ルール拡張エンジン
以下は、実戦でテスト済みの動的Matchmakingルール拡張エンジンのC#実装です。このサービスは、アクティブなキュープールに対して受信チケットを評価し、リアルタイムのPing互換性マトリクスと動的MMR分散上限を計算します。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Horizon.Matchmaking.Engine
{
public class MatchmakingTicket
{
public string TicketId { get; set; } = Guid.NewGuid().ToString();
public string PlayerId { get; set; }
public double SkillRating { get; set; } // Elo/MMR representation
public Dictionary<string, int> RegionalPingMap { get; set; } = new(); // e.g., "us-east": 28
public DateTime EnqueuedAtUtc { get; set; }
}
public class DynamicMatchRules
{
public double MaxAllowedMmrDelta { get; set; }
public int MaxAllowedPingMs { get; set; }
}
public class MatchmakingEvaluator
{
private const double BaseMmrDelta = 50.0;
private const double MaxMmrCap = 600.0;
private const int BasePingMs = 35;
private const int AbsoluteMaxPingMs = 180;
private const double PingStepIntervalSeconds = 4.0;
/// <summary>
/// Calculates the expanded criteria for a ticket based on elapsed queue time.
/// </summary>
public DynamicMatchRules GetRelaxedRules(MatchmakingTicket ticket, DateTime currentUtc)
{
double elapsedTime = (currentUtc - ticket.EnqueuedAtUtc).TotalSeconds;
// Exponential expansion for skill tolerance to ensure high-skill players eventually match
double mmrExpansion = Math.Pow(elapsedTime, 1.35) * 4.5;
double calculatedMmrDelta = Math.Min(MaxMmrCap, BaseMmrDelta + mmrExpansion);
// Step-wise discrete expansion for latency tolerance (prevents constant server hopping)
int pingSteps = (int)Math.Floor(elapsedTime / PingStepIntervalSeconds);
int calculatedPingLimit = Math.Min(AbsoluteMaxPingMs, BasePingMs + (pingSteps * 15));
return new DynamicMatchRules
{
MaxAllowedMmrDelta = calculatedMmrDelta,
MaxAllowedPingMs = calculatedPingLimit
};
}
/// <summary>
/// Evaluates whether two tickets can be paired into a match session.
/// </summary>
public bool CanMatchTickets(MatchmakingTicket ticketA, MatchmakingTicket ticketB, DateTime currentUtc, out string selectedRegion)
{
selectedRegion = null;
DynamicMatchRules rulesA = GetRelaxedRules(ticketA, currentUtc);
DynamicMatchRules rulesB = GetRelaxedRules(ticketB, currentUtc);
// 1. Evaluate Skill Delta
double actualMmrDelta = Math.Abs(ticketA.SkillRating - ticketB.SkillRating);
if (actualMmrDelta > rulesA.MaxAllowedMmrDelta || actualMmrDelta > rulesB.MaxAllowedMmrDelta)
{
return false; // Skill variance too wide for current queue age
}
// 2. Evaluate Common Datacenter Ping Compatibility
int lowestCombinedPing = int.MaxValue;
foreach (var (region, pingA) in ticketA.RegionalPingMap)
{
if (ticketB.RegionalPingMap.TryGetValue(region, out int pingB))
{
// Match must satisfy BOTH players' dynamic ping limits
if (pingA <= rulesA.MaxAllowedPingMs && pingB <= rulesB.MaxAllowedPingMs)
{
int combinedPing = pingA + pingB;
if (combinedPing < lowestCombinedPing)
{
lowestCombinedPing = combinedPing;
selectedRegion = region;
}
}
}
}
return selectedRegion != null;
}
}
}
この実装の主な技術的ポイント:
- 非対称ルールの充足(Asymmetric Rule Satisfaction): このメソッドは、両方のプレイヤーの拡張する制約が満たされているか検証します(
pingA <= rulesA.MaxAllowedPingMs && pingB <= rulesB.MaxAllowedPingMs)。キューに入って2秒の新しいプレイヤーが、もう一方のプレイヤーが90秒待っているという理由だけで、Ping 150msのサーバーに引きずり込まれることはありません。 - 段階的なPing拡張(Discrete Ping Stepping): レイテンシの上限は、連続的な曲線ではなく離散的なタイムステップ(
PingStepIntervalSeconds)を使って拡張されます。これにより、毎ループTickごとに不要なEdgeルーターの再割り当てが発生するのを防ぎます。 - 空間的交差(Spatial Intersection): ペアリングはリージョンマップ全体のディクショナリキーの交差に依存し、累積ラウンドトリップレイテンシが最も低いデータセンターを選択します。
Backend インフラストラクチャの課題:並行性、ロック競合、Dedicated Server のプロビジョニング
Matchmakingアルゴリズムを単体で記述することは容易です。本当のエンジニアリングの難しさは、数十万件の同時並行チケットを処理する分散ノードクラスタ上でこのシステムを稼働させるときに発生します。
[Player Clients]
│ (WebSockets / Low Latency)
▼
[Ingress Load Balancers]
│
▼
[Distributed Ticket Pool (e.g., Redis Cluster / Memory Grid)]
│
┌────┴────────────────────────┬────────────────────────┐
▼ ▼ ▼
[Worker Node 1] [Worker Node 2] [Worker Node 3]
│ │ │
└────┬────────────────────────┴────────────────────────┘
│ (Atomic Claim / Lua Mutex Lock)
▼
[Server Orchestration API] ──► Spin up Agones / Fleet Instances
1. 分散チケットのロック競合(Distributed Ticket Lock Contention)
複数の並列マッチメイカープロセスが同じ中央チケットプールをスキャンすると、競合状態(Race Condition)が必然的に発生します。2つの別々のワーカースレッドがチケット #1042 を同時に評価し、まったく異なる2つのロビーにペアリングしようとする可能性があります。
これを解決するために、エンジニアはマッチ割り当てを発行する前に、分散プリミティブ(Redis Luaスクリプトやメモリグリッドのアトミック操作など)を使用してアトミックなロック要求を実行する必要があります。チケットが別のマッチワーカーによってロックされている場合、スレッドは即座にその状態を解放し、バックトラックします。
2. 高頻度のリアルタイム通信
Matchmakingのステータス更新は、無駄な接続ハンドシェイクで莫大なコンピュートリソースを消費するため、標準的なHTTPポーリングに依存することはできません。キュー時間の予測や動的なPing検索をクライアントに通知し続けるため、Backend は永続的な双方向 WebSockets または長時間接続の gRPC ストリームを維持する必要があります。
マルチプレイヤーの状態更新に効率の悪いポーリングループを使用している場合は、ぜひ当社のガイドリアルタイム Backend における Unreal Engine WebSockets と HTTP ポーリングの置き換え方法(英語)をご覧ください。
3. サーバー割り当てのハンドシェイクとフリートプロビジョニング
プレイヤーのマッチングは課題の半分に過ぎません。有効なチケットグループが形成された後:
- マッチメイカーはDedicated Serverオーケストレーター(AgonesやカスタムKubernetesコントローラーなど)に通信します。
- 厳密な期限内(通常は $< 1500ms$)に、選択したデータセンター内でクリーンなゲームサーバーインスタンスを確保または割り当てる必要があります。
- サーバーが起動し、リスニングUDPポートをバインドして、IP/Portアドレスのペイロードを返します。
- マッチメイカーは接続の詳細をすべてのクライアントの WebSockets に配信します。
分散チケットプールの構築、ロック競合の処理、リージョンごとのソケットクラスタの管理、そしてDedicated Serverライフサイクルの手動でのオーケストレーションには、数ヶ月のインフラ構築作業が必要です。
ここで horizOn は深刻なエンジニアリングオーバーヘッドを解消します。Redisチケットストアの組み立てやカスタムAgonesクラスタラッパーの作成、サーバーフリートのスケーリングスクリプトの管理を行う代わりに、horizOn は完全マネージドの超低レイテンシMatchmakingキューとサーバーフリートオーケストレーションを箱から出してすぐ使える形で提供します。開発者が行うのはMatchmakingルールセットの記述のみであり、horizOn がグローバル分散、アトミックなチケットロック、自動サーバー割り当てをシームレスに処理します。
現代のゲームMatchmaking Architectureを構築するための5つのベストプラクティス
ハイリスクな競争型シューターを開発する場合でも、インディー向けのカジュアルなアーケードタイトルを開発する場合でも、以下の実践的なアーキテクチャ原則に従ってください。
1. チケット提出前にクライアントのPingベクトル取得を義務付ける
データセンターへの近接性を判断するために、クライアントIPのGeo-IP検索に依存しないでください。Geo-IPデータベースはEdgeルーティングにおいて不正確なことで知られており、リアルタイムのISP混雑を無視します。キュー登録エンドポイントを呼び出す前に、すべてのリージョンエンドポイントへのUDP Pingプローブを介してクライアントエンジンに直接レイテンシを測定させてください。
2. プールの分断を回避する
アクティブなプレイヤーベースによって明確に正当化されない限り、マイナーなゲームモードのために分離されたキューを作成しないでください。ゲームモード、マップの好み、Matchmakingの厳密性キューにプレイヤーベースを分割すると、プールの劣化が複合的に進行します。リージョン内のキューごとのアクティブCCUが1,000人を下回る場合は、動的な単一キューのフォールバックモードに自動的に切り替えてください。
3. サーバープロビジョニングとマッチ評価の切り離し
マッチメイカーのスレッドがフリートオーケストレーション層から非同期で動作することを確認してください。仮想サーバーインスタンスの起動を待つ間、アクティブなマッチメイカーワーカーのループを開いたままにしないでください。ノンブロッキングなPub/Subメッセージキューを利用してサーバー割り当てを要求し、インスタンスが正常状態を報告した際に接続ペイロードを配信します。
4. スマートな休眠機能によるアイドルサーバーコストの最適化
Matchmakingの需要はピーク時には予期せぬ急増を見せ、オフピーク時には急激に低下します。数百のゲームサーバーインスタンスを空のままアイドル状態で放置することは、多大な運用収益の無駄となります。動的なフリートウォームアップとサーバーの休眠パターン(Hibernation)を実装してください。アイドルサーバー利用の最適化に関する深い技術分析については、当社の記事ゼロウェイストサーバーの構築とFortniteサーバー最適化提案の分析(英語)をお読みください。
5. 高レイテンシ環境下での Netcode とリプリケーションのベンチマーク
どれほど高度なMatchmaking Architectureであっても、オフピーク時には中程度のレイテンシギャップ($100-120ms$)を挟んでプレイヤーをペアリングすることがあります。サーバーの Netcode が厳密なクライアント予測、ラグ補正、ステート調停(State Reconciliation)を利用して、パケット遅延を自然に乗り越えられるようにしてください。高Pingマッチ中にクライアントの位置情報が破綻したり跳ねたりする場合は、当社のガイドUnreal Engine マルチプレイヤーにおけるプレイヤー位置の同期ズレ修正方法(英語)を参照してください。
まとめと次のステップ
Activisionが『Call of Duty: Black Ops 7』において、明示的なSBMM、接続優先、ハイブリッドキューの提供を決断したことは、Matchmaking Architectureがプレイヤーの満足度にどれほど重要であるかを示しています。しかし、キューの分割には莫大なプレイヤー密度、低レイテンシのソケットネットワーキング、動的なルール減衰アルゴリズム、アトミックなチケット処理が必要不可欠です。
次のマルチプレイヤーゲームを構築する際は、Backend インフラ、ソケットサーバー、フリート割り当てロジックをゼロから書き起こすのに何ヶ月も無駄にしないでください。horizOn を活用することで、スケーラブルなマルチプレイヤーゲーム Backend、自動Matchmaking、サーバーオーケストレーションをわずか数分で導入できる方法をご確認ください。今すぐ horizOn を無料でお試しいただくか、horizOn ドキュメント を参照して Backend 開発のワークフローを加速させましょう。