גישה מצד השרת אל Google Play Games Services

מומלץ להשתמש ב-GamesSignInClient כדי לאמת שחקנים ולהעביר את הזהות שלהם בצורה מאובטחת לשרת העורפי. כך המשחק יכול לאחזר בצורה מאובטחת את הזהות של השחקן ונתונים אחרים בלי להיחשף לשיבוש פוטנציאלי בזמן שהנתונים עוברים דרך המכשיר.

אחרי שהשחקן יאומת בהצלחה, תוכלו לבקש קוד מיוחד לשימוש חד-פעמי (שנקרא קוד אימות שרת) מ-Play Games Services v2 SDK, שהלקוח מעביר לשרת. לאחר מכן, בשרת, מחליפים את קוד האימות של השרת באסימון OAuth 2.0 שהשרת יכול להשתמש בו כדי לבצע קריאות ל-Google Play Games Services API.

הנחיות נוספות להוספת אימות במשחקים זמינות במאמר בנושא אימות פלטפורמה למשחקי Android.

כדי לקבל גישה במצב אופליין, צריך לבצע את השלבים הבאים:

  1. ב-Google Play Console: יוצרים פרטי כניסה לשרת המשחקים. סוג לקוח ה-OAuth של פרטי הכניסה יהיה 'אינטרנט'.
  2. באפליקציית Android: כחלק מאימות הפלטפורמה, מבקשים קוד אימות של השרת עבור פרטי הכניסה של השרת, ומעבירים אותו לשרת. אפליקציית GamesSigninClient יכולה לבקש שלושה היקפי הרשאות של OAuth 2.0 כשהיא מבקשת גישה ל-Play Games Services web APIs בצד השרת. ההיקפים האופציונליים הם EMAIL, PROFILE ו-OPEN_ID. שני היקפי ברירת המחדל הם DRIVE_APPFOLDER ו-GAMES_LITE.
  3. בשרת המשחק: מחליפים את קוד האימות של השרת באסימון גישה ל-OAuth באמצעות שירותי האימות של Google, ואז משתמשים בו כדי להתקשר אל ממשקי ה-REST API של Play Games Services.

לפני שמתחילים

קודם צריך להוסיף את המשחק ב-Google Play Console, כמו שמתואר במאמר הגדרה של Google Play Games Services, ולשלב את אימות הפלטפורמה של Play Games Services במשחק.

יצירת אפליקציית אינטרנט בצד השרת

שירות המשחקים של Google Play לא מספק תמיכה בבק-אנד למשחקי אינטרנט. עם זאת, הוא מספק תמיכה בשרת עורפי לשרת של משחק Android.

כדי להשתמש בממשקי ה-API של REST לשירותי Google Play Games באפליקציה בצד השרת, צריך לפעול לפי השלבים הבאים:

  1. ב-Google Play Console, בוחרים משחק.
  2. עוברים אל Play Games Services > Setup and management > Configuration (שירותי Play Games > הגדרה וניהול > הגדרה).
  3. לוחצים על הוספת פרטי כניסה כדי לעבור אל הדף להוספת פרטי כניסה. בוחרים באפשרות שרת משחקים כסוג פרטי הכניסה וממשיכים לקטע הרשאה.
    1. אם לשרת המשחקים שלכם כבר יש מזהה לקוח OAuth, בוחרים אותו מהתפריט הנפתח. אחרי ששומרים את השינויים, עוברים אל הקטע הבא.
    2. אם אין לכם מזהה לקוח OAuth קיים לשרת המשחקים, אתם יכולים ליצור אחד.
      1. לוחצים על Create OAuth client (יצירת לקוח OAuth) ועוקבים אחרי ההוראות במאמר יצירת מזהה לקוח OAuth.
      2. תועברו לדף יצירת מזהה לקוח OAuth ב-Google Cloud Platform, שבו מופיע הפרויקט שמשויך למשחק.
      3. ממלאים את הטופס שבדף ולוחצים על 'יצירה'. חשוב להגדיר את סוג האפליקציה כ'אפליקציית אינטרנט'.
      4. חוזרים לקטע Authorization בדף Add credential, בוחרים את לקוח ה-OAuth החדש שנוצר ושומרים את השינויים.

קבלת קוד האימות של השרת

כדי לאחזר קוד אימות של השרת שהמשחק יכול להשתמש בו לאסימוני גישה בשרת הקצה העורפי:

  1. התקשרות אל requestServerSideAccess מהלקוח.
    1. חשוב לוודא שאתם משתמשים במזהה הלקוח ב-OAuth שרשום עבור שרת המשחק ולא במזהה הלקוח ב-OAuth של אפליקציית Android.
    2. (אופציונלי) אם שרת המשחקים שלכם דורש גישה אופליין (גישה לטווח ארוך באמצעות אסימון רענון) לשירותי Play Games, אתם יכולים להגדיר את הפרמטר forceRefreshToken כ-true.
  2. (אופציונלי) כחלק מהאימות, משתמשים חדשים צריכים לראות מסך הסכמה יחיד להיקפי הרשאות נוספים. אחרי שהמשתמש מאשר את בקשת ההסכמה, מגדירים את הפרמטר scopes עם היקפי ההרשאות של OAuth‏ EMAIL,‏ PROFILE ו-OPEN_ID. אם המשתמשים לא מביעים הסכמה, רק שני היקפי ברירת המחדל DRIVE_APPFOLDER ו-GAMES_LITE נשלחים אל ה-Backend.

    מסך הסכמה להיקפי הרשאות נוספים של OAuth.
    מסך הסכמה להיקפי הרשאות נוספים של OAuth. (לוחצים כדי להגדיל).

     GamesSignInClient gamesSignInClient = PlayGames.getGamesSignInClient(this);
     gamesSignInClient.requestServerSideAccess(OAUTH_2_WEB_CLIENT_ID, /* forceRefreshToken= / false,
         / Additional AuthScope */ scopes)
       .addOnCompleteListener( task -> {
         if (task.isSuccessful()) {
           AuthResponse authresp = task.getResult();
           // Send the authorization code as a string and a
           // list of the granted AuthScopes that were granted by the
           // user. Exchange for an access token.
           // Verify the player with Play Games Services REST APIs.
         } else {
           // Failed to retrieve authentication code.
         }
     });
     

  3. שולחים את אסימון קוד ההרשאה של OAuth לשרת הקצה העורפי כדי להחליף אותו, לאמת את מזהה השחקן מול ממשקי ה-API של REST ב-Play Games Services ואז לאמת אותו במשחק.

שליחת קוד האימות של השרת

שולחים את קוד האימות של השרת לשרת הקצה העורפי כדי לקבל בתמורה אסימוני גישה ורענון. משתמשים באסימון הגישה כדי לקרוא ל-Play Games Services API בשם השחקן, ואם רוצים, מאחסנים את אסימון הרענון כדי לקבל אסימון גישה חדש כשפג תוקף אסימון הגישה.

מידע נוסף על אופן הפעולה של מזהי שחקנים זמין במאמר מזהי שחקנים מהדור הבא.

בקטע הקוד הבא אפשר לראות איך מטמיעים את הקוד בצד השרת בשפת התכנות Java כדי להחליף את קוד האימות של השרת באסימוני גישה.

Java

/**
 * Exchanges the authcode for an access token credential. The credential
 * is 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 shouldn't 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();

    TokenVerifier(tokenResponse);

    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;
}

אפשר לאחזר את היקפי ההרשאות של OAuth באמצעות ספריות הלקוח של Google API ב-Java או ב-Python כדי לקבל את אובייקט GoogleIdTokenVerifier. בקטע הקוד הבא מוצגת ההטמעה בשפת התכנות Java.

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

/**
 * Gets the GoogleIdTokenVerifier object and additional OAuth scopes.
 * If additional OAuth scopes are not requested, the idToken will be null.
 *
 * @param tokenResponse - the tokenResponse passed from the exchangeAuthCode
 *                        function.
 *
 **/

void TokenVerifier(GoogleTokenResponse tokenResponse) {

    string idTokenString = tokenResponse.getIdToken();

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
        // Specify the WEB_CLIENT_ID of the app that accesses the backend:
        .setAudience(Collections.singletonList(WEB_CLIENT_ID))
        // Or, if multiple clients access the backend:
        //.setAudience(Arrays.asList(WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3))
        .build();

    GoogleIdToken idToken = verifier.verify(idTokenString);

    // The idToken can be null if additional OAuth scopes are not requested.
    if (idToken != null) {
        Payload payload = idToken.getPayload();

    // Print user identifier
    String userId = payload.getSubject();
    System.out.println("User ID: " + userId);

    // Get profile information from payload
    String email = payload.getEmail();
    boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
    String name = (String) payload.get("name");
    String pictureUrl = (String) payload.get("picture");
    String locale = (String) payload.get("locale");
    String familyName = (String) payload.get("family_name");
    String givenName = (String) payload.get("given_name");

    // Use or store profile information
    // ...

    } else {
      System.out.println("Invalid ID token.");
    }
}

קריאה לממשקי API ל-REST מהשרת

במאמר ממשקי REST API לשירותי Google Play Games מופיע תיאור מלא של הקריאות ל-API שזמינות.

ריכזנו כאן דוגמאות לקריאות API בארכיטקטורת REST שעשויות להיות שימושיות:

שחקן

רוצים לקבל אימות עם מזהה השחקן ונתוני הפרופיל? מתקשרים אל Players.get עם 'me' כמזהה.

חברים

פרטים נוספים זמינים במדריך בנושא חברים.

  • כדי לאחזר את רשימת החברים של השחקן, קוראים ל-Players.list עם friends_all כ-collection.

  • כדי לבדוק אם יש לכם גישה לרשימת החברים, מתקשרים אל Players.get עם me בתור playerID, ומציגים את השדה profileSettings.friendsListVisibility בתשובה.

הישגים

פרטים נוספים זמינים במדריך בנושא הישגים.

  • כדי לקבל רשימה של ההישגים הנוכחיים, קוראים ל-AchievementDefinitions.list.

  • אפשר לשלב את זה עם קריאה ל-Achievements.list כדי לגלות אילו הישגים השחקן פתח.

  • כדי לבטל את הנעילה של הישג של שחקן, צריך להפעיל את הפונקציה Achievements.unlock.

  • כדי לדווח על התקדמות בהשגת הישג ולבדוק אם השחקן השיג אותו, צריך לקרוא ל-Achievements.increment.

  • אם אתם מנפים באגים במשחק שלא הגיע לשלב הייצור, אתם יכולים לקרוא ל-Achievements.reset או ל-Achievements.resetAll מתוך ממשקי ה-API לניהול כדי לאפס את ההישגים למצב המקורי שלהם.

לוחות לידרבורד

פרטים נוספים זמינים במדריך בנושא טבלאות הישגים.

  • כדי לקבל רשימה של כל טבלאות הניקוד במשחק, קוראים ל-Leaderboards.list.

  • כששחקן מסיים משחק, אפשר לשלוח את הניקוד שלו אל Scores.submit ולבדוק אם הוא השיג שיא חדש.

  • כדי להציג טבלת הישגים, צריך לקבל את הנתונים מ-Scores.list ולהציג אותם למשתמש.

  • אפשר להשתמש ב-Scores.listWindow כדי למצוא מגוון ציונים שקרובים לציון הגבוה של המשתמש.

  • כדי לקבל מידע נוסף על הניקוד של השחקן בטבלת לידרבורד מסוימת (לדוגמה, אם השחקן נמצא ב-12% המובילים מכל השחקנים), צריך לקרוא ל-Scores.get.

  • אם אתם מנפים באגים במשחק, אתם יכולים לקרוא ל-Scores.reset מממשקי ה-API לניהול כדי לאפס את כל הניקוד של השחקן ב-Leaderboard מסוים.