تفعيل الوصول من جهة الخادم إلى "خدمات ألعاب Google Play"

إذا كانت لعبتك تستخدم خادمًا في الخلفية، ننصحك باستخدام تسجيل الدخول باستخدام حساب Google لمصادقة اللاعبين ونقْل هوية اللاعب بأمان إلى خادم الخلفية. يتيح ذلك أيضًا للعبتك استرداد هوية اللاعب وبيانات أخرى بشكل آمن بدون التعرّض للتلاعب المحتمل أثناء مرورها عبر الجهاز.

في هذا السيناريو، تطلب لعبتك من اللاعب تسجيل الدخول إلى "خدمات ألعاب Google Play" كالمعتاد. عندما يُسجِّل العميل (اللاعب) الدخول بنجاح، يحتوي العنصر GoogleSignInAccount على رمز خاص للاستخدام مرة واحدة (يُعرف باسم رمز مصادقة الخادم) يُرسله العميل إلى الخادم. بعد ذلك، استبدِل على الخادم رمز مصادقة الخادم برمز مميز لبروتوكول OAuth 2.0 يمكن للخادم استخدامه لإجراء طلبات إلى واجهة برمجة التطبيقات Google Play Games Services API.

للحصول على إرشادات إضافية حول إضافة ميزة تسجيل الدخول في ألعابك، يُرجى الاطّلاع على مقالة تسجيل الدخول في ألعاب Android.

للاطّلاع على نموذج رمز مفصّل يوضّح كيفية استخدام ميزة "تسجيل الدخول باستخدام حساب Google" لمصادقة اللاعبين، اطّلِع على ملف clientserverskeleton على GitHub.

يجب اتّباع الخطوات التالية للوصول إلى المحتوى بلا إنترنت:

  1. في Google Play Console: أنشئ بيانات اعتماد لخادم اللعبة. سيكون نوع عميل OAuth لبيانات الاعتماد هو "الويب".
  2. في تطبيق Android: كجزء من عملية تسجيل الدخول، اطلب رمز مصادقة الخادم لبيانات اعتماد الخادم، ثم أعِد توجيهه إلى الخادم.
  3. على خادم اللعبة: استبدِل رمز مصادقة الخادم برمز مميّز للوصول عبر بروتوكول OAuth باستخدام خدمات مصادقة Google، ثم استخدِم هذا الرمز للاتّصال بواجهات برمجة تطبيقات REST في "خدمات ألعاب Play".

قبل البدء

قبل أن تتمكّن من دمج ميزة "تسجيل الدخول باستخدام حساب Google" في لعبتك، عليك أولاً إضافة لعبتك في Google Play Console، كما هو موضّح في مقالة إعداد "خدمات ألعاب Google Play".

إنشاء تطبيق ويب مرتبط من جهة الخادم للعبة

لا توفّر "خدمات ألعاب Google Play" دعمًا لألعاب الويب من الخلفية. ومع ذلك، يقدّم هذا الإصدار دعمًا لخادم الخلفية لخادم لعبتك على Android.

إذا أردت استخدام واجهات برمجة التطبيقات REST لخدمات ألعاب Google Play في تطبيقك من جهة الخادم، اتّبِع الخطوات التالية:

  1. أنشئ تطبيق ويب مرتبطًا لعبتك في قسم التطبيقات المرتبطة في Google Play Console. يُرجى العِلم أنّه لا يتم استخدام الرمز launch_url في هذه العملية ويمكن تركه فارغًا.
  2. للحصول على معلومات بيانات الاعتماد لتطبيقك، اتّبِع الخطوات التالية:
    1. من لعبتك في Google Play Console، انقر على تفاصيل اللعبة.
    2. انتقِل للأسفل إلى قسم مشروع وحدة تحكّم واجهة برمجة التطبيقات وانقر على الرابط الذي يؤدي إلى مشروع وحدة تحكّم واجهة برمجة التطبيقات.
    3. من شاشة واجهات برمجة التطبيقات والخدمات > بيانات الاعتماد في Google API Console، حمِّل ملف client_secret.json لتطبيقك على الويب واحفظه في مكان يمكن لخادمك الوصول إليه. سجِّل معرِّف العميل لبيانات الاعتماد للرجوع إليه لاحقًا.
  3. أعِد تشغيل تطبيقك من جهة الخادم ليكون جاهزًا لقبول الطلبات من تطبيق العميل الخاص باللعبة.

تسجيل الدخول على الجهاز العميل

فئة GoogleSignInClient هي نقطة الدخول الرئيسية لاسترداد حساب اللاعب الذي سجّل الدخول حاليًا، ولتسجيل دخول اللاعب إذا لم يسبق له ذلك على تطبيقك في الجهاز.

لإنشاء برنامج عملاء لتسجيل الدخول، اتّبِع الخطوات التالية:

  1. أنشئ عميلًا لتسجيل الدخول من خلال عنصر GoogleSignInOptions. في GoogleSignInOptions.Builder لضبط إعدادات تسجيل الدخول، عليك تحديد GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN.
  2. يجب أيضًا تحديد أنّ لعبتك تتطلّب رمز مصادقة لخادم الخلفية من خلال استدعاء الأسلوب GoogleSignInOptions.Builder.requestServerAuthCode() باستخدام معرّف عميل الخادم كمَعلمة. ستسترجع رمز التفويض لاحقًا للرموز المميَّزة للوصول على خادم الخلفية، كما هو موضّح في الحصول على رمز مصادقة الخادم.
  3. استخدِم طريقة GoogleSignIn.getClient() وأدخِل الخيارات التي ضبطتها سابقًا. إذا كان الطلب ناجحًا، تعرض Google Sign-In API مثيلًا من GoogleSignInClient.
  4. بعد الحصول على مثيل GoogleSignInClient، عليك مواصلة تسجيل دخول اللاعب بدون إشعار من onResume() النشاط، كما هو موضّح في تنفيذ عملية تسجيل الدخول بدون إشعار.

وفي ما يلي مثال لذلك:

private static final int RC_SIGN_IN = 9001;
private GoogleSignInClient mGoogleSignInClient;

private void startSignInForAuthCode() {

  // Client ID for your backend server.
  String webClientId = getString(R.string.webclient_id);

  GoogleSignInOptions signInOption = new
      GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
      .requestServerAuthCode(webClientId)
      .build();

  GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption);
  Intent intent = signInClient.getSignInIntent();
  startActivityForResult(intent, RC_SIGN_IN);
}

الحصول على رمز التفويض الخاص بالخادم

لاسترداد رمز مصادقة الخادم الذي يمكن للعبة استخدامه للحصول على الرموز المميّزة للوصول على خادم الخلفية، استدِع الأسلوب getServerAuthCode() على العنصر GoogleSignInAccount الذي تعرضه ميزة "تسجيل الدخول باستخدام حساب Google" عند تسجيل دخول اللاعب بنجاح.

وفي ما يلي مثال لذلك:

// Auth code to send to backend server.
private String mServerAuthCode;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == RC_SIGN_IN) {
    GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
    if (result.isSuccess()) {
      mServerAuthCode = result.getSignInAccount().getServerAuthCode();
    } else {
      String message = result.getStatus().getStatusMessage();
      if (message == null || message.isEmpty()) {
        message = getString(R.string.signin_other_error);
      }
      new AlertDialog.Builder(this).setMessage(message)
          .setNeutralButton(android.R.string.ok, null).show();
    }
  }
}

استبدال رمز مصادقة الخادم برمز مميّز للوصول على الخادم

أرسِل رمز التفويض الخاص بالخادم إلى خادم الخلفية لتبديله برموز الوصول وإعادة التحميل. استخدِم رمز الوصول لاستدعاء Google Play Games Services API نيابةً عن اللاعب، واختَر، إن أردت، تخزين الرمز المميّز لإعادة التحميل للحصول على رمز وصول جديد عند انتهاء صلاحية رمز الوصول.

يعرض مقتطف الرمز التالي كيفية تنفيذ الرمز من جهة الخادم بلغة برمجة Java لتبديل رمز مصادقة الخادم برموز الوصول. يتم استخدام نموذج تطبيق clientserverskeleton:

/**
 * Exchanges the authcode for an access token credential.  The credential
 * is the associated with the given player.
 *
 * @param authCode - the non-null authcode passed from the client.
 * @param player   - the player object which the given authcode is
 *                 associated with.
 * @return the HTTP response code indicating the outcome of the exchange.
 */
private int exchangeAuthCode(String authCode, Player player) {
try {

    // The client_secret.json file is downloaded from the Google API
    // console.  This is used to identify your web application.  The
    // contents of this file should not be shared.
    //
    File secretFile = new File("client_secret.json");

    // If we don't have the file, we can't access any APIs, so return
    // an error.
    if (!secretFile.exists()) {
        log("Secret file : " + secretFile
                .getAbsolutePath() + "  does not exist!");
        return HttpServletResponse.SC_FORBIDDEN;
    }

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
            JacksonFactory.getDefaultInstance(), new
            FileReader(secretFile));

    // Extract the application id of the game from the client id.
    String applicationId = extractApplicationId(clientSecrets
            .getDetails().getClientId());

    GoogleTokenResponse tokenResponse =
            new GoogleAuthorizationCodeTokenRequest(
            HTTPTransport,
            JacksonFactory.getDefaultInstance(),
            "https://oauth2.googleapis.com/token",
            clientSecrets.getDetails().getClientId(),
            clientSecrets.getDetails().getClientSecret(),
            authCode,
            "")
            .execute();

    log("hasRefresh == " + (tokenResponse.getRefreshToken() != null));
    log("Exchanging authCode: " + authCode + " for token");
    Credential credential = new Credential
            .Builder(BearerToken.authorizationHeaderAccessMethod())
            .setJsonFactory(JacksonFactory.getDefaultInstance())
            .setTransport(HTTPTransport)
            .setTokenServerEncodedUrl("https://www.googleapis.com/oauth2/v4/token")
            .setClientAuthentication(new HttpExecuteInterceptor() {
                @Override
                public void intercept(HttpRequest request)
                        throws IOException {
                        }
            })
            .build()
            .setFromTokenResponse(tokenResponse);

    player.setCredential(credential);

    // Now that we have a credential, we can access the Games API.
    PlayGamesAPI api = new PlayGamesAPI(player, applicationId,
            HTTPTransport, JacksonFactory.getDefaultInstance());

    // Call the verify method, which checks that the access token has
    // access to the Games API, and that the player id used by the
    // client matches the playerId associated with the accessToken.
    boolean ok = api.verifyPlayer();

    // Call a Games API on the server.
    if (ok) {
        ok = api.updatePlayerInfo();
        if (ok) {
            // persist the player.
            savePlayer(api.getPlayer());
        }
    }

    return ok ? HttpServletResponse.SC_OK :
            HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

  } catch (IOException e) {
    e.printStackTrace();
  }
  return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}

للاطّلاع على مزيد من المعلومات عن الوصول إلى واجهات برمجة تطبيقات Google من خادم خلفية بالنيابة عن لاعب سجّل الدخول، اطّلِع على تفعيل الوصول من جهة الخادم.

التعامل مع تسجيل خروج اللاعب

لتسجيل خروج اللاعبين من لعبتك، يمكنك استدعاء طريقة signOut() في GoogleSignInClient. للحصول على مثال على مقتطف رمز، يُرجى الاطّلاع على تسجيل خروج اللاعب.

طلب بيانات من واجهات برمجة تطبيقات REST من الخادم

يُرجى الرجوع إلى واجهات برمجة تطبيقات REST لخدمة "ألعاب Google Play" للحصول على وصف كامل لطلبات بيانات واجهة برمجة التطبيقات المتاحة.

تشمل أمثلة طلبات البيانات من واجهة برمجة التطبيقات REST API التي قد تجدها مفيدة ما يلي:

اللاعب

  • هل تريد الحصول على رقم تعريف اللاعب الذي سجّل الدخول وبيانات ملفه الشخصي؟ استخدِم Players.get مع 'me' كرقم تعريف.

الأصدقاء

يُرجى الاطّلاع على دليل الأصدقاء الذي يشرح ميزة "الأصدقاء" بمزيد من التفصيل.

  • هل تريد استرداد قائمة أصدقاء اللاعب؟ استخدِم Players.list مع تحديد 'friends_all' على أنّه collection.
  • هل يمكنك الوصول إلى قائمة الأصدقاء؟ استخدِم Players.get للعنصر me، وانظر إلى الحقل profileSettings.friendsListVisibility في الردّ.

الإنجازات

احرص على مراجعة دليل الإنجازات الذي يوضّح الإنجازات بمزيد من التفصيل.

  • هل تريد الحصول على قائمة بالإنجازات الحالية؟ يمكنك إجراء مكالمة إلى AchievementDefinitions.list.
  • يمكنك دمج ذلك مع طلب Achievements.list لمعرفة الإنجازات التي حقّقها اللاعب.
  • هل حصل اللاعب على إنجاز؟ استخدِم Achievements.unlock لفتح قفله.
  • هل أحرز اللاعب تقدّمًا نحو إنجاز جزئي؟ استخدِم Achievements.increment لتسجيل مستوى التقدّم (ومعرفة ما إذا كان اللاعب قد حصل على الإنجاز).
  • هل تصحِّح أخطاء لعبة لم يتم طرحها بعد؟ حاوِل استدعاء Achievements.reset أو Achievements.resetAll من واجهات برمجة التطبيقات Management API لإعادة ضبط الإنجازات إلى حالتها الأصلية.

لوحات الصدارة

احرص على مراجعة دليل لوحات الصدارة الذي يوضّح لوحات الصدارة بمزيد من التفاصيل.

  • هل تريد الحصول على قائمة بجميع قوائم النتائج في اللعبة؟ أجرِ مكالمة إلى Leaderboards.list.
  • هل انتهى اللاعب من لعبة؟ يمكنك إرسال النتيجة إلى Scores.submit ومعرفة ما إذا كانت هذه هي أعلى نتيجة جديدة.
  • هل تريد عرض قائمة صدارة؟ احصل على البيانات من Scores.list وعرِضها للمستخدم.
  • استخدِم Scores.listWindow للعثور على مجموعة من النتائج القريبة من أعلى نتيجة للمستخدِم.
  • للحصول على مزيد من المعلومات عن نتيجة اللاعب في قائمة صدارة معيّنة (على سبيل المثال، إذا كان اللاعب ضمن أفضل% 12 من جميع اللاعبين)، يمكنك استدعاء Scores.get.
  • هل تصحِّح أخطاء لعبة؟ جرِّب طلب Scores.reset من واجهات برمجة تطبيقات Management لإعادة ضبط جميع نتائج هذا اللاعب من قائمة صدارة معيّنة.