Terug naar Blog

Hybride Matchmaking Engineeren: Hoe Call of Duty's Queue-splitsing Moderne Game Matchmaking-architectuur Blootlegt

Gepubliceerd op 24 juli 2026
Hybride Matchmaking Engineeren: Hoe Call of Duty's Queue-splitsing Moderne Game Matchmaking-architectuur Blootlegt

Kort samengevat

Het splitsen van matchmaking queues in games zoals Call of Duty: Black Ops 7 vereist een schaalbare backend-architectuur die een balans zoekt tussen latency, skill variance en queue-tijden. Door gebruik te maken van dynamische rule expansion en atomaire ticket handling kunnen ontwikkelaars poolfragmentatie en hoge latency voorkomen. Dit artikel analyseert de wiskunde, C#-implementatie en backend-infrastructuur die nodig zijn voor ultra-low-latency matchmaking en efficiënte server orchestration.

Toen Activision aankondigde dat Call of Duty: Black Ops 7 zijn player base zou splitsen over drie afzonderlijke matchmaking queues—Skill-Based Matchmaking (SBMM), Classic Connection-Based en Hybrid—bracht dat een langlopende backend engineering-discussie direct in de spotlight. Voor competitieve games is het matchen van spelers niet zomaar een game design-voorkeur; het is een complex game matchmaking architecture-probleem dat balanceert tussen sub-milliseconde netwerkrestricties, mathematische skill variance, poolfragmentatie en cloud compute-kosten.

Het opsplitsen van je matchmaking-systeem in meerdere afzonderlijke queues klinkt op het eerste gezicht als een eenvoudige feature voor spelersvoorkeuren. In werkelijkheid verdubbelt of verdrievoudigt het de engineering overhead op je backend-infrastructuur. Wanneer je een concurrent player base verdeelt over gescheiden queues, daalt de ticket-dichtheid drastisch, exploderen de queue times exponentieel in regio's met lage dichtheid en krijgen server allocation-algoritmes te maken met verhoogde churn.

In dit artikel analyseren we de technische trade-offs van SBMM versus connection-first matchmaking, ontleden we de wiskunde achter dynamische hybride queue-expansie, inspecteren we echte backend C#-code voor ticket processing en onderzoeken we hoe je veerkrachtige matchmaking pools bouwt die strak schalen.


Het Onvermijdelijke Matchmaking-trilemma

Elke moderne game matchmaking-architectuur moet een constraint optimization-probleem oplossen dat gebonden is aan drie concurrerende variabelen:

  1. Latency (RTT): De round-trip time tussen de player client en de toegewezen dedicated server-instantie (gemeten in milliseconden).
  2. Skill Delta ($\Delta$MMR): Het wiskundige verschil in skill-representatie (MMR, Elo of TrueSkill) tussen spelers in een gegeven lobby.
  3. Queue Duration ($T_{queue}$): De totale tijd die een speler wacht in een status van idle queue-frictie voordat een geldig match ticket overgaat in een actieve server allocation.
                    Latency (RTT)
                      /       \
                     /         \
                    /   Ideale  \
                   /    Match    \
                  /               \
Skill Delta (ΔMMR) --------------- Queue Duration (T_queue)

Je kunt eenvoudig optimaliseren voor twee van deze variabelen, wat absoluut ten koste gaat van de derde:

  • Lage Latency + Lage Skill Delta: Resulteert in lange queue times omdat de engine moet zoeken naar zeldzame, perfect gekwalificeerde spelers die zich ook nog eens vlak bij hetzelfde cloud datacenter bevinden.
  • Korte Queue Time + Lage Skill Delta: Resulteert in hoge latency omdat de matchmaker zijn geografische zoekradius wereldwijd moet vergroten om tegenstanders van gelijke skill te vinden.
  • Korte Queue Time + Lage Latency: Resulteert in een hoge skill variance (de klassieke "connection-first" public match-ervaring) omdat de matchmaker direct de dichtstbijzijnde beschikbare clients pakt, ongeacht performance metrics.

Wanneer een titel als Black Ops 7 drie afzonderlijke queue modes introduceert, dwingt het de onderliggende matchmaking-architectuur om drie parallelle rule evaluation loops te onderhouden over gefractioneerde memory pools.

Als je aantal Concurrent Users (CCU) in een specifieke regio—bijvoorbeeld Zuid-Amerika om 04:00 uur 's nachts—onder de 2.000 actieve spelers zakt, verlaagt het verdelen van die spelers over drie afzonderlijke pools de lokale dichtheid per queue tot slechts enkele honderden spelers. Het gevolg: connection-first queues slagen er niet in dedicated servers met lage ping te vinden, en SBMM-queues lopen oneindig vast.


SBMM vs. Ping-First vs. Hybride Queues Geëntleed

Om een backend te bouwen die miljoenen match tickets kan verwerken, moet je eerst begrijpen hoe elk architecturaal model onder de motorkap werkt.

1. Connection-Based (Ping-First) Architectuur

In een connection-first engine zijn skill-matrices volledig secundair of worden ze overboord gegooid. Het hoofddoel is het minimaliseren van netwerkdegradatie (jitter, packet loss, hoge RTT).

  • Client Ping Probing: Bij het openen van de queue verstuurt de game client ICMP- of UDP-pingbeacons naar een reeks regionale edge gateways (bijv. us-east-1, eu-central-1, ap-southeast-1).
  • Ping Vector Generation: De client bouwt een vector van latencies op: [ us-east: 24ms, us-west: 78ms, eu-central: 142ms ] en voegt deze toe aan de matchmaking payload.
  • Spatial Indexing: De matchmaker deelt spelers strikt in regio-hashes in op basis van acceptabele latency-drempels (bijv. $RTT < 50ms$).

Omdat de netwerktopologie de match-creatie dicteert, zijn de zoekbuckets voor spelers voorspelbaar. Hierdoor kunnen tickets in $O(1)$-tijd worden verwerkt met behulp van eenvoudige FIFO (First-In, First-Out) ruimtelijke queues.

2. Skill-Based Matchmaking (SBMM) Architectuur

SBMM geeft prioriteit aan eerlijke matches door de vaardigheden van spelers te modelleren via multidimensionale Gaussische distributies (bijv. TrueSkill 2) of aangepaste Elo-varianten. Belangrijke inputs zijn win/loss ratios, kill/death ratios, damage per minute en recente prestatie-trajecten.

  • Distance Evaluation: De matchmaker berekent de Euclidische of Mahalanobis-afstand tussen kandidaat-spelervectoren in een $N$-dimensionale skill space.
  • Sorting Cost: Matchmakers kunnen niet vertrouwen op eenvoudige FIFO-queues. Ze moeten gesorteerde sets of ruimtelijke bomen (zoals KD-trees) van ticketprofielen onderhouden om snel kandidaten binnen een acceptabele skill variance $\sigma$ te vinden.
  • Search Space Contraction: Naarmate de skill toeneemt (bijv. de beste 0,5% van de spelers), krimpt de pool van geschikte kandidaten drastisch. Dit dwingt het systeem om tickets voor onbepaalde tijd vast te houden of de striktheid van de skill-parameters langzaam te laten vervallen (decay).

3. Dynamische Hybride Architectuur

In plaats van spelers te dwingen tot hardcoded queue-keuzes, implementeren moderne productie-backends vaak een Dynamisch Hybride model. In deze opzet begint elk match ticket met strikte SBMM en lage latency-grenzen. Naarmate $T_{queue}$ toeneemt, vergroot een rule decay-functie continu de acceptabele skill delta ($\Delta MMR$) en latency-limiet ($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)$$

Waar $\alpha$ en $\beta$ expansiecoëfficiënten zijn, $\gamma$ de steilheid van de exponentiële curve regelt en $t$ de verstreken queue duration in seconden is.

Door gebruik te maken van dynamische relaxatie voorkom je dat queues oneindig vasthangen, terwijl je tijdens piek-CCU-periodes een hoge matchkwaliteit behoudt.


Code-implementatie: Dynamic Rule Expansion Engine

Hieronder vind je een in de praktijk beproefde C#-implementatie van een dynamic matchmaking rule expansion engine. Deze service evalueert inkomende tickets ten opzichte van actieve queue pools en berekent realtime ping compatibility-matrices en dynamische MMR variance caps.

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 representatie
        public Dictionary<string, int> RegionalPingMap { get; set; } = new(); // bijv. "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>
        /// Berekent de verruimde criteria voor een ticket op basis van de verstreken queue-tijd.
        /// </summary>
        public DynamicMatchRules GetRelaxedRules(MatchmakingTicket ticket, DateTime currentUtc)
        {
            double elapsedTime = (currentUtc - ticket.EnqueuedAtUtc).TotalSeconds;

            // Exponentiële expansie van skill-tolerantie zodat high-skill spelers uiteindelijk gematcht worden
            double mmrExpansion = Math.Pow(elapsedTime, 1.35) * 4.5;
            double calculatedMmrDelta = Math.Min(MaxMmrCap, BaseMmrDelta + mmrExpansion);

            // Stapsgewijze discrete expansie voor latency-tolerantie (voorkomt 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>
        /// Evalueert of twee tickets gekoppeld kunnen worden in een match-sessie.
        /// </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. Evalueer Skill Delta
            double actualMmrDelta = Math.Abs(ticketA.SkillRating - ticketB.SkillRating);
            if (actualMmrDelta > rulesA.MaxAllowedMmrDelta || actualMmrDelta > rulesB.MaxAllowedMmrDelta)
            {
                return false; // Skill variance te groot voor huidige queue-leeftijd
            }

            // 2. Evalueer Gedeelde Datacenter Ping-compatibiliteit
            int lowestCombinedPing = int.MaxValue;

            foreach (var (region, pingA) in ticketA.RegionalPingMap)
            {
                if (ticketB.RegionalPingMap.TryGetValue(region, out int pingB))
                {
                    // Match moet voldoen aan de dynamische ping-limieten van BEIDE spelers
                    if (pingA <= rulesA.MaxAllowedPingMs && pingB <= rulesB.MaxAllowedPingMs)
                    {
                        int combinedPing = pingA + pingB;
                        if (combinedPing < lowestCombinedPing)
                        {
                            lowestCombinedPing = combinedPing;
                            selectedRegion = region;
                        }
                    }
                }
            }

            return selectedRegion != null;
        }
    }
}

Belangrijkste Technische Aspecten van Deze Implementatie:

  • Asymmetrische Regel-voldoening: De methode controleert of aan de verruimende restricties van beide spelers wordt voldaan (pingA <= rulesA.MaxAllowedPingMs && pingB <= rulesB.MaxAllowedPingMs). Een nieuwe speler die 2 seconden in de queue staat, wordt niet naar een server met 150ms ping gesleept alleen omdat de andere speler al 90 seconden wacht.
  • Discrete Ping Stepping: Latency-grenzen verruimen via discrete tijdsstappen (PingStepIntervalSeconds) in plaats van een continue curve. Dit voorkomt onnodige herentoewijzing door de edge-router bij elke tick loop.
  • Spatiale Intersectie: Het paarsgewijs matchen vertrouwt op dictionary key-doorsnedes van regionale maps, waarbij het datacenter met de laagste cumulatieve round-trip latency wordt geselecteer d.

Uitdagingen in Backend-Infrastructuur: Concurrency, Lock Contention en Dedicated Server Provisioning

Het schrijven van match-algoritmes op zichzelf is overzichtelijk. De echte engineering-uitdaging ontstaat wanneer je dit systeem draait op gedistribueerde node clusters die honderdduizenden gelijktijdige tickets verwerken.

[Player Clients]
       │ (WebSockets / Lage Latency)
       ▼
[Ingress Load Balancers]
       │
       ▼
[Distributed Ticket Pool (bijv. 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

Wanneer meerdere parallelle matchmaker-processen dezelfde centrale ticket pool scannen, ontstaan er onvermijdelijk race conditions. Twee afzonderlijke worker threads zouden tegelijkertijd Ticket #1042 kunnen evalueren en proberen dit in twee compleet verschillende lobbies in te delen.

Om dit op te lossen, moeten developers atomaire lock claims uitvoeren met behulp van gedistribueerde primitieven (zoals Redis Lua-scripts of memory grid atomic operations) voordat een match-toewijzing wordt uitgezonden. Als een ticket is vergrendeld door een andere match worker, geeft de thread zijn status direct vrij en keert hij terug.

2. Hoge-frequentie Realtime Communicatie

Matchmaking status-updates kunnen niet vertrouwen op standaard HTTP polling zonder enorme hoeveelheden compute resources te verspillen aan nutteloze connection handshakes. Om clients op de hoogte te houden van geschatte queue-duur en dynamische ping-zoekopdrachten, moeten backends permanente, dual-channel WebSockets of langlopende gRPC-streams onderhouden.

Vertrouw je momenteel nog op onefficiënte polling loops voor je multiplayer-status? Bekijk dan onze gids over het vervangen van HTTP polling door Unreal Engine WebSockets in realtime backends.

3. Server Allocation Handshakes & Fleet Provisioning

Spelers matchen is pas de helft van het werk. Zodra een geldige ticketgroep is gevormd:

  1. Neemt de matchmaker contact op met een dedicated server orchestrator (bijv. Agones, custom Kubernetes controllers).
  2. Moet een schone game server-instantie binnen een strikte deadline (meestal $< 1500ms$) worden geclaimd of toegewezen in het geselecteerde datacenter.
  3. Start de server op, bindt zijn luisterende UDP-poort en retourneert de IP/Poort-adrespayload.
  4. Verstuurt de matchmaker de verbindingsdetails naar alle client WebSockets.

Het bouwen van gedistribueerde ticket pools, het afhandelen van lock contention, het beheren van regio-gebaseerde socket clusters en het handmatig orchestreren van dedicated server-levenscycli vereist maanden aan infrastructuurwerk.

Dit is waar horizOn de zware engineering overhead wegneemt. In plaats van het opzetten van Redis ticket stores, het schrijven van custom Agones cluster wrappers en het beheren van server fleet scaling scripts, biedt horizOn kant-en-klare, volledig beheerde, ultra-low-latency matchmaking queues en server fleet orchestration. Jij schrijft je matchmaking rulesets; horizOn regelt naadloos de globale distributie, atomaire ticket locking en automatische server allocation.


5 Best Practices voor het Bouwen van een Moderne Game Matchmaking-architectuur

Of je nu een hoogwaardige competitieve shooter bouwt of een indie casual arcade-game, volg deze in de praktijk beproefde architectuurprincipes:

1. Eis Client Ping Vectors Vóór Ticket Submission

Vertrouw nooit op GEO-IP lookups van de client om de afstand tot je datacenters te bepalen. GEO-IP databases staan bekend om hun onnauwkeurigheid voor edge routing en houden geen rekening met actieve ISP-congestie. Dwing de game client altijd om rechtstreekse latency te meten via UDP ping probes naar alle regionale endpoints voordat de enqueue-endpoint wordt aangeroepen.

2. Bescherm Tegen Poolfragmentatie

Voorkom het aanmaken van afzonderlijke queues voor secundaire game modes, tenzij je actieve player base dit duidelijk rechtvaardigt. Het verdelen van je spelers over game modes, map-voorkeuren en matchmaking strictness queues verergert pool-degradatie. Als je actieve CCU per queue in een regio onder de 1.000 spelers zakt, schakel dan automatisch over naar dynamische fallback modes met één enkele queue.

3. Ontkoppel Server Provisioning van Match Evaluation

Zorg ervoor dat je matchmaker-threads asynchroon werken ten opzichte van je fleet orchestration-laag. Houd een actieve matchmaker worker loop nooit open terwijl je wacht tot een virtuele server-instantie opstart. Gebruik non-blocking pub/sub message queues om server allocations aan te vragen en verbindingspayloads af te leveren zodra instanties een gezonde status rapporteren.

4. Optimaliseer Kosten voor Inactieve Servers met Slimme Hibernation

Matchmaking piekt onvoorspelbaar tijdens piekuren en daalt sterk buiten de piekuren. Het onbeheerd leeg laten draaien van honderden game server-instanties verspilt aanzienlijke operationele omzet. Implementeer dynamische fleet warm-up en server hibernation-patronen. Lees voor een diepgaande technische analyse over het optimaliseren van ongebruikte servercapaciteit ons artikel over het ontwerpen van zero-waste servers en voorstellen voor Fortnite server-optimalisatie.

5. Benchmark Netcode en Replicatie Onder Hoge Latency

Zelfs de meest geavanceerde matchmaking-architectuur zal buiten de piekuren af en toe spelers koppelen over matige latency-afstanden ($100-120ms$). Zorg ervoor dat je server-netcode gebruikmaakt van strikte client prediction, lag compensation en state reconciliation om netwerkvertraging soepel op te vangen. Als spelersposities haperen of verspringen tijdens matches met een hoge ping, raadpleeg dan onze gids over het oplossen van player location desync in Unreal Engine multiplayer.


Samenvatting & Volgende Stappen

Het besluit van Activision om expliciete SBMM-, Connection-First- en Hybrid-queues aan te bieden in Call of Duty: Black Ops 7 illustreert hoe cruciaal matchmaking-architectuur is voor de tevredenheid van spelers. Het splitsen van queues vereist echter een enorme spelersdichtheid, low-latency socket networking, dynamische rule decay-algoritmes en atomaire ticket-afhandeling.

Als je bouwt aan je volgende multiplayer game, verspil dan geen maanden aan het vanaf de grond opbouwen van backend-infrastructuur, socket servers en fleet allocation logica. Ontdek hoe horizOn developers in staat stelt om schaalbare multiplayer game backends, geautomatiseerde matchmaking en server orchestration binnen enkele minuten te implementeren. Probeer horizOn vandaag nog gratis uit of verken de horizOn documentatie om je backend workflow te versnellen.