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

كيفية إصلاح فشل تسجيل الدخول وأخطاء Achievements في Epic Online Services باستخدام Unreal Blueprint

نُشر في 9 يوليو 2026
كيفية إصلاح فشل تسجيل الدخول وأخطاء Achievements في Epic Online Services باستخدام Unreal Blueprint

باختصار

يوضح هذا المقال كيفية إصلاح مشاكل تسجيل الدخول وفشل تحديث الـ achievements في Unreal Engine عند استخدام Epic Online Services (EOS). يركز المقال على أهمية معالجة التدفق غير المتزامن (asynchronous flow) وتجنب ربط الـ nodes بشكل خطي مباشر، مع توضيح الفارق الجوهري بين معرفات EAID و PUID. كما يقدم المقال نماذج تهيئة عملية لكل من لغة C++ وملف DefaultEngine.ini لتسهيل عملية الدمج وتخطي مشكلات تعارض الـ overlay.

تقوم بعمل compile لنسخة shipping build من لعبتك المطورة بـ Unreal Engine، وتُشغلها من Epic Games Launcher، ثم تنقر على زر تسجيل الدخول المخصص. لا يحدث شيء، أو يتجمد game client، وتمتلئ الـ logs بإشعارات فشل authentication غامضة. والأسوأ من ذلك، يتم تشغيل الـ achievement unlock node دون أي خطأ ظاهر، ومع ذلك يظل ملف اللاعب في overlay الخاص بـ Epic Games Store فارغًا تمامًا. هذه الأخطاء شائعة للغاية عند دمج storefront APIs. لحسن الحظ، يمكن حلها عن طريق تصحيح تدفق الـ asynchronous execution وفهم كيفية قيام الـ subsystem بعمل mapping لهويات المستخدمين.

فهم EOS Authentication Pipeline: مقارنة بين EAID و PUID

يتطلب دمج Epic Online Services (EOS) عبر Online Subsystem (OSS) الخاص بـ Unreal Engine التعامل مع نظام هوية ثنائي المستويات. تتم إدارة Epic Account ID (EAID) بواسطة Auth Interface. حيث يقوم بالتحقق من هوية المستخدم على مستوى الـ storefront، مما يتيح ميزات مثل social overlay، وقوائم الأصدقاء، وفحوصات مكتبة Epic Games Store.

وعلى العكس من ذلك، تتم إدارة Product User ID (PUID) بواسطة Connect Interface. ويُعد PUID معرفًا خاصًا باللعبة يربط المستخدم بـ backend اللعبة. وتشمل هذه الخدمات الـ matchmaking، والـ stats، والـ cloud saves، والـ achievements.

لماذا يؤدي عدم تطابق EAID و PUID إلى تعطل الـ Achievements

يحدث سيناريو فشل شائع عندما ينجح المطورون في عمل authentication مع Auth Interface (مما يولد EAID) ولكن الـ subsystem يفشل في تسجيل الدخول إلى Connect Interface. نظرًا لأن الـ achievements تتم إدارتها بواسطة Connect Interface باستخدام الـ PUIDs، فإن كتابة تقدم الـ achievement بدون PUID صالح يفشل مع ظهور خطأ EOS_InvalidUser.

في الألعاب التي يتم إطلاقها بدون storefront wrapper، عادةً ما يسجل المستخدمون الدخول باستخدام credentials يتم التحقق منها مقابل جدول مستخدمين مركزي. أما في بيئة storefront حديثة مثل Epic Games Store، فإن الهوية تكون موحدة (federated). عندما تبدأ لعبتك، تتلقى cryptographic ticket من الـ launcher. بعد ذلك، تمرر اللعبة هذه التذكرة إلى سيرفرات authentication الخاصة بالـ storefront. يعيد الـ SDK معرف Epic Account ID، والذي يعمل كجواز سفر للمستخدم عبر جميع الألعاب على المنصة.

ومع ذلك، للحفاظ على الخصوصية وأمان الـ cross-play، لا تسمح Epic للميزات الخاصة باللعبة مثل الـ achievements بقراءة Epic Account ID مباشرة. بدلاً من ذلك، يجب على اللعبة تهيئة (initialize) الـ Connect Interface، والذي يقوم بعملية تبادل ثانوية للحصول على Product User ID. يضمن هذا الفصل أنه في حال تم اختراق لعبتك، لن تتعرض الـ global Epic Account credentials الخاصة باللاعبين للخطر. بدون PUID، تعمل لعبتك فعليًا في حالة ضيف غير موثق (unauthenticated guest state)، مما يمنع الوصول إلى ميزات EGS. إذا كانت إعدادات credentials config أو client policy تفتقر إلى الأذونات الصحيحة في Epic Developer Portal، فستفشل عملية الربط هذه. ستُبلغ واجهة الهوية (identity interface) عن تسجيل دخول ناجح لأن حساب Epic صالح، ولكن طبقة خدمات اللعبة (game services layer) لا يمكنها كتابة بيانات الـ achievement لأنه لم يتم ربط أي PUID على الإطلاق.

تشريح تسلسل تسجيل دخول Blueprint معطوب

يواجه العديد من المطورين مشاكل في تسجيل الدخول بسبب ربط Blueprint execution pins بشكل خطي ومتتالي. سيفشل دائمًا سحب execution pin من node الـ Login مباشرة إلى node الـ Query Achievements أو Write Achievement Progress.

يجب أن تنتقل طلبات الـ Authentication إلى backend الخاص بـ Epic، مما يجعلها asynchronous. يستمر المحرك (engine) في تنفيذ Blueprint graph في الإطار (frame) التالي مباشرة، قبل وقت طويل من إرجاع سيرفرات Epic لـ user credentials. في هذه المرحلة، يظل Unique Net ID الخاص باللاعب null.

مشكلة "Missing Node": حالات السباق (Race Conditions) في Async Execution

إذا قمت باستدعاء nodes الـ achievement مباشرة بعد node تسجيل الدخول، فإن الـ subsystem يطلق خطأً صامتًا (silent error) لأن حالة المستخدم المخزنة مؤقتًا (cached user state) فارغة. يتطلب سير العمل الصحيح ربط custom events بـ asynchronous delegates الخاصة بالـ subsystem.

علاوة على ذلك، غالبًا ما يغفل المطورون عن الخطوة الحاسمة المتمثلة في عمل caching للـ achievements قبل الكتابة فيها. لا يمكنك تعديل تقدم الـ achievement دون الاستعلام أولاً (querying) من السيرفر عن الـ schema الحالية. استدعاء Write Achievements دون استدعاء Query Achievements مسبق يفرز فشلاً صامتًا أو خطأ EOS_InvalidUser في console log لأن حجم local cached achievement map هو 0.

تدفق Asynchronous Blueprint الصحيح

لحل هذه المشكلة، يجب عليك فصل تشغيل (trigger) تسجيل الدخول عن تحديث الـ achievement. أولاً، استدعِ login node وحدد الـ authentication credentials الصحيحة.

ثانيًا، اربط حدثًا (event) بالـ delegate المسمى On Login Complete الخاص بـ identity interface. بمجرد تشغيل هذا الحدث وإرجاع حالة النجاح، قم بجلب الـ Unique Net ID الخاص بالمستخدم.

ثالثًا، مرر هذا المعرف (ID) إلى node الـ Query Achievements. أخيرًا، اربط حدثًا بالـ delegate المسمى On Query Achievements Complete، واستدعِ Write Achievement Progress فقط داخل ذلك الـ callback.

تنفيذ متحكم EOS Login & Achievement قوي باستخدام C++

للتعامل مع هذه العمليات الـ asynchronous بشكل نظيف، يوصى بشدة بتنفيذ حلقة الـ authentication في لغة C++. يوفر هذا وصولاً مباشرًا إلى IOnlineSubsystem وواجهاته.

فيما يلي تنفيذ كامل وجاهز للإنتاج بلغة C++ لمعالج تسجيل الدخول والـ achievements:

#include "OnlineSubsystem.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Interfaces/OnlineAchievementsInterface.h"
#include "OnlineSubsystemUtils.h"

void UEOSLoginHandler::LoginToEpicGames(APlayerController* PlayerController)

{
    if (!PlayerController) return;

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    if (!Subsystem)
    {
        UE_LOG(LogTemp, Warning, TEXT("EOS Subsystem not found. Make sure the plugin is enabled."));
        return;
    }

    IOnlineIdentityPtr Identity = Subsystem->GetIdentityInterface();
    if (Identity.IsValid())
    {
        FOnlineAccountCredentials Credentials;
        Credentials.Type = TEXT("ExchangeCode");
        Credentials.Id = TEXT("");
        Credentials.Token = TEXT(""); // The launcher passes this via command line arguments

        Identity->AddOnLoginCompleteDelegate_Handle(0, FOnLoginCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnLoginComplete));
        Identity->Login(0, Credentials);
    }
}

void UEOSLoginHandler::OnLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
    if (!bWasSuccessful)
    { 
        UE_LOG(LogTemp, Error, TEXT("Epic Online Services Login Failed. Error: %s"), *Error);
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("EOS Login Successful. ID resolved: %s"), *UserId.ToString());

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    { 
        Achievements->QueryAchievements(UserId, FOnQueryAchievementsCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnQueryAchievementsComplete));
    }
}

void UEOSLoginHandler::OnQueryAchievementsComplete(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (!bWasSuccessful)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to query achievements from Epic Developer Portal."));
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("Achievements queried successfully. Now writing progress."));

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    {
        FOnlineAchievementsWriteRef WriteObject = MakeShared<FOnlineAchievementsWrite>();
        WriteObject->SetFloatStat(TEXT("ACH_FIRST_BLOOD"), 100.0f);

        Achievements->WriteAchievements(PlayerId, WriteObject, FOnAchievementsWrittenDelegate::CreateUObject(this, &UEOSLoginHandler::OnAchievementsWritten));
    }
}

void UEOSLoginHandler::OnAchievementsWritten(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (bWasSuccessful)
    {
        UE_LOG(LogTemp, Log, TEXT("Achievement successfully unlocked on Epic Games Store!"));
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to write achievement progress. Check sandbox mappings."));
    }
}

يستخدم هذا الـ controller استدعاءات رجعية (callbacks) متتالية. ويضمن عدم الوصول إلى واجهة الـ achievements إلا بعد أن تنجح واجهة الهوية (identity interface) في حل كل من Epic Account ID و Product User ID.

تهيئة ملف DefaultEngine.ini الخاص بك لـ EOS

ستفشل تهيئة (initialize) Blueprint login nodes إذا كانت الإعدادات في ملف Config/DefaultEngine.ini الخاص بك غير صحيحة. يتطلب الـ plugin الخاص بـ Online Subsystem EOS عوامل (parameters) معينة للربط بـ game client الخاص بك.

استخدم نموذج التهيئة التالي لضمان تهيئة الـ subsystem بشكل صحيح:

[OnlineSubsystem]
DefaultPlatformService=EOS

[OnlineSubsystemEOS]
bEnabled=true
bUseEpicConnect=true
ProductId=your_product_id_here
SandboxId=your_sandbox_id_here
DeploymentId=your_deployment_id_here
ClientId=your_client_id_here
ClientSecret=your_client_secret_here

[OnlineSubsystemEOS.Auth]
bUseEpicAccount=true

تعمل تهيئة ملف DefaultEngine.ini كجسر بين ملفات اللعبة بعد الـ compilation و Epic Developer Portal. يجب أن تتطابق الـ parameters الموجودة تحت [OnlineSubsystemEOS] مع إعدادات منتجك تمامًا.

من المصادر الشائعة للخلط هو الفرق بين client credentials و sandbox IDs. يمثل كل من Client ID و Client Secret بيانات اعتماد (credentials) الـ game client الخاص بك، مما يسمح له بالاتصال بـ EOS backend. بينما يمثل Sandbox ID البيئة المحددة (مثل Development أو Staging أو Live) التي يدخل إليها الـ client الخاص بك.

إذا حاول الـ game client الخاص بك عمل authentication باستخدام Sandbox ID لا يتطابق مع سياسة Client Credentials، فسيتم رفض طلب الـ authentication. بالإضافة إلى ذلك، يجب تعيين قيمة bUseEpicConnect إلى true للتأكد من أن Online Subsystem يتعامل مع mapping الخاص بـ EAID إلى PUID تلقائيًا خلف الكواليس.

إدارة عدم تطابق تركيز الـ Overlay وتكامل المنصات

يعتمد الـ plugin الخاص بـ EOS في Unreal بشكل كبير على Epic Games Overlay لعرض الإشعارات، والتعامل مع دعوات الأصدقاء، وإجراء عمليات OAuth web authentications. عندما يقوم الـ game client الخاص بك بتشغيل تسجيل الدخول باستخدام نوع credentials المسمى AccountPortal، يطلق نظام التشغيل نافذة متصفح أو يفتح الـ overlay.

يقوم هذا التوجيه بنقل التركيز بعيدًا عن نافذة اللعبة. إذا لم تكن أوضاع الإدخال (input modes) لديك مهيأة للتعامل مع فقدان التركيز، فقد تتجمد اللعبة أو تسجل مفاتيح معلقة (stuck keys).

يُعد التعامل مع مشاكل الإدخال الناتجة عن الـ overlay صداعًا شائعًا لمطوري ألعاب الـ PC. على سبيل المثال، يمكن أن يتعارض overlay injection مع raw input drivers ويتسبب في تجميد حالة لوحة المفاتيح/الماوس. لمنع هذه الثغرات من إفساد تجربة اللاعبين، يمكنك مراجعة تحليلنا في The Marathon Input Issues Fix: Architecting PC Games to Survive Overlay Conflicts.

علاوة على ذلك، غالبًا ما تكون إخفاقات التهيئة (initialization failures) أثناء الاختبار ناتجة عن إعدادات خاطئة لـ socket connections أو حدود المهلة (timeout thresholds) في التهيئة المحلية. إذا كنت تقوم باستكشاف أخطاء مهلة برنامج التشغيل (driver timeouts) وإصلاحها أثناء بدء تشغيل الجلسة، فراجع دليلنا حول Uefn Session Launch Timeout Nightmares: Diagnosing Unreal Engine Network Drivers.

تبسيط الـ Storefront Backends العابرة للمنصات (Cross-Platform)

تستغرق تهيئة الـ storefront subsystems يدويًا، وكتابة سلاسل C++ delegate chains، والتعامل مع مشكلات تركيز الـ overlay الخاصة بكل منصة، وقتًا طويلاً للغاية. القيام بذلك لعدة منصات مثل Steam و EGS والكونسول يضاعف من أعباء التطوير.

يتطلب بناء هذا بنفسك إعداد load balancers، وعمل sharding لقواعد البيانات، وإدارة شهادات SSL — وهو ما يستغرق بسهولة من 4 إلى 6 أسابيع من العمل.

باستخدام horizOn, تحصل على cross-platform backend موحد يقوم بعمل abstraction لهذه الـ storefront APIs. بدلاً من التعامل مع رموز الهوية المزدوجة (EAID/PUID)، أو بيئات Epic sandboxes، أو كود تهيئة Steamworks، فإنك تستخدم API واحدًا يتعامل مع ملفات تعريف المستخدمين، والـ achievements، والـ matchmaking، ولوحات المتصدرين (leaderboards).

يعمل horizOn كجسر يقوم تلقائيًا بمزامنة (syncing) الـ achievements وتقدم اللاعب مع أي storefront يتم إطلاق المستخدم منه، مما يتيح لك التركيز على الـ game loop بدلاً من كتابة كود storefront boilerplate المكرر. هذا يعني أن لاعبيك يمكنهم فتح الـ achievements، والوصول إلى الـ cloud saves، والبحث عن المباريات بغض النظر عن المنصة أو الـ launcher الذي يستخدمونه، دون الحصول على سطر برمجيات واحد من كود الـ SDK المخصص للـ storefront.

أفضل الممارسات الحديثة لعمليات دمج Epic Online Services

  1. لا تقم أبدًا بربط الـ Asynchronous Nodes بشكل مباشر: انتظر دائمًا تشغيل delegates الـ OnLoginComplete و OnQueryAchievementsComplete قبل تشغيل العمليات التالية مثل كتابة الـ stats أو الـ achievements.

  2. تفعيل الـ Verbose Logging أثناء التطوير: أضف السطور التالية إلى ملف DefaultEngine.ini تحت [Core.Log] لتتبع تحذيرات EOS المخفية:

    [Core.Log]
    LogOnline=VeryVerbose
    LogOnlineGame=VeryVerbose
    LogEOSSDK=VeryVerbose
    
  3. ربط الـ Achievements بـ EOS في الـ Dev Portal: تأكد من أن الـ achievements التي تم إنشاؤها تحت مظلة Epic Games Store (EGS) مربوطة بشكل صريح بـ EOS backend الخاص بك. إذا لم تكن مرتبطة، فلن يتعرف EOS SDK على معرف الـ achievement ID.

  4. استخدم ExchangeCode في الـ Shipping Builds: أثناء التطوير المحلي، يكون نوع تسجيل الدخول Developer مع أداة Dev Auth Tool ملائمًا، ولكن يجب عليك تبديل نوع الـ credentials إلى ExchangeCode في الـ shipping builds التي يتم إطلاقها من Epic Games Launcher. استخدام نوع الـ auth المسمى ExchangeCode والمقدم من الـ launcher يتخطى إعادة توجيه متصفح الويب لـ OAuth، مما يقلل مدة تسلسل تسجيل الدخول من 8-12 ثانية من التفاعل اليدوي للمستخدم إلى مصافحة تلقائية (automated handshake) تستغرق أقل من 500 مللي ثانية.

تبسيط تكامل الـ Storefronts

ينحصر حل أخطاء تسجيل الدخول والـ achievement في Epic Online Services في Unreal Engine في فهم الـ async pipeline وإبقاء ملفات التهيئة (configuration profiles) متطابقة تمامًا. من خلال إدارة الانتقال بين واجهات الهوية وخدمات اللعبة، يمكن للمطورين التخلص من الأخطاء الصامتة وتقديم تجربة لاعب سلسة.

هل أنت مستعد لتوسيع نطاق الـ multiplayer backend الخاص بك؟ جرب horizOn مجانًا أو اطلع على API docs.


المصدر: Epic Games Store (Online Services AND Achievements)