العودة إلى المدونة

هندسة الـ Hybrid Matchmaking: كيف يكشف تقسيم الـ Queue في Call of Duty عن Game Matchmaking Architecture الحديثة

نُشر في 24 يوليو 2026
هندسة الـ Hybrid Matchmaking: كيف يكشف تقسيم الـ Queue في Call of Duty عن Game Matchmaking Architecture الحديثة

باختصار

يتناول المقال الهندسة الخلفية لـ Matchmaking الألعاب الحديثة وتأثير تقسيم الـ Queues كما في Call of Duty: Black Ops 7. يشرح المقال التوازنات التقنية بين تقليل الـ Latency وعدالة الـ SBMM واستخدام خوارزميات التوسع الديناميكي للـ Rules. كما يقدم تطبيقاً برمجياً بـ C# لمواجهة مشاكل التزامن وتخصيص الـ Dedicated Servers، مع توضيح كيف يسهل استخدام horizOn بناء بنية تحتية سحابية موثوقة للألعاب الجماعية.

عندما أعلنت Activision أن لعبة Call of Duty: Black Ops 7 ستقسم قاعدة اللاعبين لديها عبر ثلاثة أسئلة Matchmaking مختلفة — Skill-Based Matchmaking (SBMM)، وClassic Connection-Based، وHybrid — أعادت نقاشاً هندسياً طويلاً في الـ Backend إلى الواجهة مباشرةً. بالنسبة للعناوين التنافسية، فإن تحديد كيفية الجمع بين اللاعبين ليس مجرد تفضيل في تصميم اللعبة؛ بل هو مسألة game matchmaking architecture معقدة توازن بين قيود الشبكة ذات زمن التأخير المتناهي في الصغر (sub-millisecond)، والتباين الرياضي للمهارات، وتجزئة تجمع اللاعبين (pool fragmentation)، وتكاليف Cloud compute.

تقسيم نظام الـ Matchmaking لديك إلى عدة أسئلة (queues) منفصلة يبدو في الظاهر ميزة بسيطة تتعلق بتفضيلات اللاعبين. ولكن في الواقع، فإنه يضاعف أو يضاعف ثلاثة أضعاف العبء الهندسي (engineering overhead) على البنية التحتية للـ Backend. عندما تقوم بتقسيم عدد اللاعبين المتزامنين (CCU) إلى queues منفصلة، تنخفض كثافة الـ tickets بشكل حاد، وتتزايد أوقات الـ queue بشكل أُسي في المناطق ذات الكثافة المنخفضة، وتواجه خوارزميات تخصيص السيرفرات (server allocation algorithms) اضطراباً شديداً (heightened churn).

في هذا المقال، سنحلل المفاضلات التقنية (technical trade-offs) بين SBMM والـ matchmaking القائم على الاتصال أولاً (connection-first)، ونفكك الرياضيات وراء التوسع الديناميكي للـ hybrid queue، ونفحص كود C# حقيقي للـ Backend لمعالجة الـ tickets، ونستكشف كيفية بناء pools متينة للـ matchmaking تتوسع بسلاسة.


The Immutable Matchmaking Trilemma

تجب على كل game matchmaking architecture حديثة حل مشكلة تحسين القيود (constraint optimization problem) المحكومة بصلب ثلاثة متغيرات متنافسة:

  1. Latency (RTT): زمن الذهاب والإياب بين العميل (player client) ومثيل الـ Dedicated Server المخصص (بالميلي ثانية).
  2. Skill Delta ($\Delta$MMR): الفجوة الرياضية في تمثيل المهارة (MMR أو Elo أو TrueSkill) بين اللاعبين في lobby معين.
  3. Queue Duration ($T_{queue}$): إجمالي الوقت الذي يقضيه اللاعب في الانتظار مع احتكاك الـ queue الخامل قبل أن تتحول ticket الـ matchmaking الصالحة إلى تخصيص سيرفر نشط.
                    Latency (RTT)
                      /       \
                     /         \
                    /   Ideal   \
                   /    Match    \
                  /               \
Skill Delta (ΔMMR) --------------- Queue Duration (T_queue)

يمكنك بسهولة التحسين لأي اثنين من هذه المتغيرات على حساب المتغير الثالث تماماً:

  • Low Latency + Low Skill Delta: يؤدي إلى أوقات queue طويلة لأن المحرك يجب أن يبحث عن لاعبين نادرين بذات مستوى المهارة والذين يعيشون أيضاً بالقرب من نفس الـ cloud datacenter بالظبط.
  • Low Queue Time + Low Skill Delta: يؤدي إلى latency عالية لأن الـ matchmaker يجب أن يوسع نطاق البحث الجغرافي عالمياً للعثور على خصوم بنفس مستوى المهارة.
  • Low Queue Time + Low Latency: يؤدي إلى تباين عالٍ في المهارة (تجربة المباريات العامة الكلاسيكية القائمة على "الاتصال أولاً") لأن الـ matchmaker يلتقط فوراً أقرب العملاء المتاحين بغض النظر عن مقاييس الأداء.

عندما تقدم لعبة مثل Black Ops 7 ثلاثة أنماط queue منفصلة، فإنها تجبر الـ matchmaking architecture التحتية على تقييم ثلاث حلقات من القواعد (rule evaluation loops) الموازية عبر memory pools مجزأة.

إذا انخفض عدد اللاعبين المتزامنين (CCU) في منطقة محددة — مثل أمريكا الجنوبية الساعة 4 صباحاً — عن 2,000 لاعب نشط، فإن تقسيم هؤلاء اللاعبين على ثلاثة pools منفصلة يقلل الكثافة المحلية لكل queue إلى بضع مئات من اللاعبين فقط. ونتيجة لذلك، تفشل queues الاتصال أولاً في العثور على Dedicated Servers ذات ping منخفض، وتتوقف queues الـ SBMM لأجل غير مسمى.


Deconstructing SBMM vs. Ping-First vs. Hybrid Queues

لبناء Backend قادر على التعامل مع ملايين الـ match tickets، يجب عليك أولاً فهم كيفية عمل كل نموذج معماري تحت الغطاء (under the hood).

1. Connection-Based (Ping-First) Architecture

في محرك قائم على الاتصال أولاً، تكون مصفوفات المهارة ثانوية تماماً أو يتم تجاهلها. الهدف الأساسي هو تقليل تدهور الشبكة (jitter، وpacket loss، وRTT العالي).

  • Client Ping Probing: عند فتح الـ queue، يرسل جاري اللعبة إشارات ping عبر ICMP أو UDP إلى سلسلة من الـ regional edge gateways (مثل us-east-1، eu-central-1، ap-southeast-1).
  • Ping Vector Generation: ينشئ العميل متجه latency: [ us-east: 24ms, us-west: 78ms, eu-central: 142ms ] ويرفقه مع حمولة (payload) الـ matchmaking.
  • Spatial Indexing: يضع الـ matchmaker اللاعبين حصرياً في region hashes بناءً على حدود latency المقبولة (مثل $RTT < 50ms$).

نظرًا لأن بنية الشبكة (network topology) هي التي تحدد إنشاء المباريات، فإن مجموعات البحث عن اللاعبين تكون متوقعة، مما يسمح بحل الـ tickets في زمن $O(1)$ باستخدام أسئلة مكانية بسيطة تعمل بمبدأ FIFO (First-In, First-Out).

2. Skill-Based Matchmaking (SBMM) Architecture

تمنح SBMM الأولوية لعدالة المباراة من خلال نمذجة قدرات اللاعب باستخدام توزيعات غاوس متعددة الأبعاد (مثل TrueSkill 2) أو تنويعات Elo المخصصة. تشمل المدخلات الرئيسية نسب الفوز/الخسارة، ونسب القتل/الموت، والضرر في الدقيقة، ومسار الأداء الحديث.

  • Distance Evaluation: يحسب الـ matchmaker المسافة الإقليدية (Euclidean) أو مسافة ماهالانوبيس (Mahalanobis) بين متجهات اللاعبين المرشحين في فضاء مهارة مكون من $N$ أبعاد.
  • Sorting Cost: لا يمكن للـ matchmakers الاعتماد على أسئلة FIFO البسيطة. يجب عليهم صيانة مجموعات مرتبة (sorted sets) أو أشكال شجرية مكانية (مثل KD-trees) لملفات الـ tickets للعثور بسرعة على المرشحين ضمن تباين مهارة مقبول $\sigma$.
  • Search Space Contraction: مع زيادة المهارة (مثل أعلى 0.5% من اللاعبين)، ينكمش تجمع المرشحين المؤهلين بشدة. هذا يجبر النظام إما على احتجاز الـ tickets لأجل غير مسمى أو إضعاف معايير صرامة المهارة تدريجياً.

3. Dynamic Hybrid Architecture

بدلاً من إجبار اللاعبين على خيارات queue محددة مسبقاً في الكود، تطبق الـ production backends الحديثة غالباً نموذج Dynamic Hybrid. في هذا الإعداد، تبدأ كل match ticket بحدود SBMM صارمة وlatency منخفضة. ومع زيادة $T_{queue}$، تقوم دالة تراجع القواعد (rule decay function) بتوسيع تباين المهارة المقبول ($\Delta MMR$) وحد الـ latency الأقصى ($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$ هو وقت الـ queue المنقضي بالثواني.

من خلال الاستفادة من التوسع الديناميكي، تمنع تعليق الـ queue النهائي مع الحفاظ على جودة مباراة عالية خلال فترات ذروة الـ CCU.


Code Implementation: Dynamic Rule Expansion Engine

فيما يلي تطبيق بلغة C# مُختبر في بيئات الإنتاج لمحرك توسيع قواعد الـ matchmaking الديناميكي. تقوم هذه الخدمة بتقييم الـ tickets الواردة مقابل pools الـ queue النشطة، وتحسب مصفوفات توافق الـ 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;
        }
    }
}

Key Technical Aspects of This Implementation:

  • Asymmetric Rule Satisfaction (تلبية القواعد غير المتماثلة): تتحقق الطريقة من احترام القيود المتوسعة لكلا اللاعبين (pingA <= rulesA.MaxAllowedPingMs && pingB <= rulesB.MaxAllowedPingMs). اللاعب الجديد الموجود في الـ queue لمدة ثانيتين لن يتم سحبه إلى سيرفر بـ ping يبلغ 150ms لمجرد أن اللاعب الآخر كان ينتظر لمدة 90 ثانية.
  • Discrete Ping Stepping (التوسع المتقطع للـ Ping): تتوسع حدود الـ latency باستخدام خطوات زمنية منفصلة (PingStepIntervalSeconds) بدلاً من المنحنيات المستمرة. هذا يتجنب إعادة تعيين الـ edge routers دون داعٍ في كل دورة tick.
  • Spatial Intersection (التقاطع المكاني): يعتمد التوافق على تقاطعات مفاتيح الـ Dictionary عبر الخرائط الإقليمية، واختيار الـ datacenter الذي يسجل أقل إجمالي زمن للذهاب والإياب (round-trip latency).

Backend Infrastructure Challenges: Concurrency, Lock Contention, and Dedicated Server Provisioning

كتابة خوارزميات الـ matchmaking بشكل معزول أمر بسيط. لكن الصعوبة الهندسية الحقيقية تظهر عندما تقوم بتشغيل هذا النظام عبر clusters من العقد الموزعة (distributed node clusters) التي تتعامل مع مئات الآلاف من الـ tickets المتزامنة.

[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

عندما تقوم عدة عمليات matchmaker متوازية بمسح نفس الـ ticket pool المركزي، تحدث race conditions بشكل لا مفر منه. قد تقوم خيطان (threads) منفصلتان بتقييم Ticket #1042 في نفس الوقت ومحاولة إقرانها في غرفتين (lobbies) مختلفين تماماً.

لحل هذه المشكلة، يجب على المطورين تنفيذ عمليات مطالبات القفل الذرية (atomic lock claims) باستخدام أشكال أولية موزعة (مثل سكريبتات Redis Lua أو عمليات الـ memory grid الذرية) قبل إصدار تعيين المباراة. إذا كانت الـ ticket مقفولة بواسطة عامل matchmaker آخر، فإن الـ thread يحرر حالته فوراً ويتراجع.

2. High-Frequency Real-Time Communication

لا يمكن لتحديثات حالة الـ Matchmaking الاعتماد على الـ HTTP polling القياسي دون استهلاك موارد معالجة ضخمة في مصافحات اتصالات لا فائدة منها. لإبقاء العملاء على اطلاع بتقديرات مدة الـ queue وعمليات البحث الديناميكية عن الـ ping، يجب أن تحافظ الـ backends على اتصالات WebSockets دائمة ومزدوجة الاتجاه أو gRPC streams طويلة الأمد.

إذا كنت تتبع نهج الـ polling غير الكفء لحالة الـ multiplayer حالياً، فاطلع على دليلنا حول الاستغناء عن HTTP polling لصالح Unreal Engine WebSockets في الـ backends في الوقت الفعلي.

3. Server Allocation Handshakes & Fleet Provisioning

توفيق اللاعبين هو نصف المعركة فقط. بمجرد تشكيل مجموعة tickets صالحة:

  1. يتواصل الـ matchmaker مع منسق سيرفرات مخصص (مثل Agones أو متحكمات Kubernetes المخصصة).
  2. يجب المطالبة بمثيل سيرفر ألعاب نظيف (clean game server instance) أو تخصيصه في الـ datacenter المختار ضمن الموعد النهائي الصارم (عادة $< 1500ms$).
  3. يبدأ السيرفر بالعمل، ويربط منفذ UDP المستمع الخاص به، ويعيد حمولة عنوان IP/Port.
  4. يرسل الـ matchmaker تفاصيل الاتصال إلى جميع WebSockets الخاصة بالعملاء.

إن بناء أسئلة الـ tickets الموزعة، وإدارة تنافس الأقفال، وإدارة socket clusters المعتمدة على المناطق، وتنسيق دورة حياة الـ dedicated servers يدوياً يتطلب شهوراً من العمل على البنية التحتية.

وهنا تأتي منصة horizOn لتلغي هذا العبء الهندسي الضخم. بدلاً من تجميع مخازن tickets على Redis، وكتابة أغطية مخصصة لـ Agones clusters، وإدارة سكريبتات توسيع أسطول السيرفرات، توفر horizOn أسئلة matchmaking فائقة السرعة ومنسقة مسبقاً بالكامل لأسطول السيرفرات بشكل جاهز الاستخدام. أنت تكتب قواعد الـ matchmaking الخاصة بك؛ وتتكفل horizOn بالتوزيع العالمي، وإقفال الـ tickets الذري، وتخصيص السيرفرات التلقائي بسلاسة.


5 Best Practices for Building Modern Game Matchmaking Architecture

سواء كنت تبني لعبة إطلاق نار تنافسية عالية المخاطر أو لعبة أركيد مستقلة خفيفة، اتبع هذه المبادئ المعمارية المعتمدة:

1. Mandate Client Ping Vectors Before Ticket Submission

لا تعتمد أبداً على استعلامات Geo-IP للعميل لتحديد القرب من الـ datacenters لديك. قواعد بيانات Geo-IP غير دقيقة بالمرة للتوجيه عند الأطراف وتتجاهل الازدحام اللحظي عند مزودي الخدمة (ISPs). احرص دائماً على إجبار محرك اللعبة لدى العميل على قياس الـ latency المباشر عبر فحص UDP ping لكافة النقاط الإقليمية قبل استدعاء نقطة النهاية للإضافة للـ queue.

2. Guard Against Pool Fragmentation

تجنب إنشاء أسئلة (queues) منفصلة لأنماط اللعب الثانوية ما لم يكن لديك عدد لاعبين نشط يبرر ذلك بوضوح. إن تقسيم قاعدة اللاعبين على أنماط اللعب، وتفضيلات الخرائط، ودرجة صرامة الـ matchmaking يتسبب في تدهور الـ pool بشكل مضاعف. إذا انخفض الـ CCU النشط لكل queue عن 1,000 لاعب في منطقة ما، انتقل تلقائياً إلى أنماط التراجع الديناميكية بـ queue واحد.

3. Decouple Server Provisioning from Match Evaluation

تأكد من أن خيوط (threads) الـ matchmaker تعمل بشكل غير متزامن (asynchronously) عن طبقة تنسيق الأسطول (fleet orchestration layer). لا تترك حلقة عمل الـ matchmaker مفتوحة أثناء انتظار بدء تشغيل مثيل سيرفر وهمي. استخدم أسئلة رسائل pub/sub غير معطلة (non-blocking) لطلب تخصيصات السيرفرات وتوصيل بيانات الاتصال عندما تبلغ المثيلات عن حالة سليمة.

4. Optimize Idle Server Costs with Smart Hibernation

ترتفع حركة الـ matchmaking بشكل غير متوقع خلال ساعات الذروة وتهبط بشكل حاد خارج أوقات الذروة. ترك المئات من مثيلات سيرفرات الألعاب خاملة وفارغة يهدد أرباح التشغيل بشكل كبير. قم بتطبيق أنماط إحماء الأسطول الديناميكي وسبات السيرفرات (server hibernation). للحصول على تحليل تقني عميق حول تحسين استخدام السيرفرات الخاملة، اقرأ مقالنا حول بناء سيرفرات بدون هدر واقتراحات تحسين سيرفرات Fortnite.

5. Benchmark Netcode and Replication Under High Latency

حتى أكثر معمارية matchmaking تقدماً قد تقرن أحياناً اللاعبين عبر فجوات latency متوسطة ($100-120ms$) خلال أوقات غير الذروة. تأكد من أن الـ netcode الخاص بالسيرفر يستخدم التنبؤ الدقيق من جانب العميل (client prediction)، وتعويض البطء (lag compensation)، وتسوية الحالة (state reconciliation) للتعامل مع تأخير الحزم بسلاسة. إذا تقطعت مواضع اللاعبين أو قفزت أثناء المباريات ذات الـ ping العالي، فراجع دليلنا حول كيفية إصلاح عدم تزامن مواقع اللاعبين في Unreal Engine multiplayer.


Summary & Next Steps

يعكس قرار Activision بتقديم queues صريحة لـ SBMM، وConnection-First، وHybrid في لعبة Call of Duty: Black Ops 7 مدى أهمية الـ matchmaking architecture لرضا اللاعبين. ومع ذلك، فإن تقسيم الـ queues يتطلب كثافة لاعبين هائلة، وشبكات sockets ذات latency منخفضة، وخوارزميات تراجع قواعد ديناميكية، ومعالجة ذرية للـ tickets.

إذا كنت تبني لعبيتك الجماعية التالية، فلا تضيع شهوراً في كتابة البنية التحتية للـ backend، وسيرفرات الـ socket، ومنطق تخصيص الأسطول من الصفر. تعرف على كيف تمكّن horizOn المطورين من نشر backends ألعاب multiplayer قابلة للتوسع، وmatchmaking مؤتمت، وتنسيق سيرفرات في دقائق. جرب horizOn مجاناً اليوم أو استكشف وثائق horizOn لتسريع سير عمل الـ backend لديك.


Source: Call of Duty: Black Ops 7 will very soon offer three different matchmaking systems, and you'd be right to be confused