הפעלת גישה בצד השרת ל-Google Play Games Services

בעקבות הוצאה משימוש של Google Sign-In API, אנחנו מסירים את ה-SDK בגרסה Games v1 בשנת 2026. אחרי פברואר 2025, לא תהיה לך אפשרות לפרסם ב-Google Play משחקים ששולבו לאחרונה עם ה-SDK בגרסה Games v1. מומלץ להשתמש ב-SDK בגרסה Games v2.
משחקים קיימים עם שילובים קודמים של Games v1 ימשיכו לפעול למשך כמה שנים, אבל מומלץ לעבור לגרסה 2 החל מיוני 2025.
המדריך הזה מיועד לשימוש ב-Play Games Services SDK בגרסה 1. מידע על גרסת ה-SDK העדכנית זמין במאמרי העזרה בנושא גרסה 2.

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

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

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

כדי לראות קוד לדוגמה שמראה איך להשתמש בכניסה לחשבון Google כדי לאמת שחקנים, אפשר לעיין בדוגמה clientserverskeleton ב-GitHub.

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

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

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

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

יצירת אפליקציית אינטרנט משויכת בצד השרת למשחק

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

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

  1. יוצרים אפליקציית אינטרנט משויכת למשחק בקטע אפליקציות מקושרות ב-Google Play Console. הערה: לא משתמשים בlaunch_url בתהליך הזה, ואפשר להשאיר אותו ריק.
  2. כדי לקבל את פרטי האישורים של האפליקציה, פועלים לפי השלבים הבאים:
    1. במשחק ב-Google Play Console, לוחצים על פרטי המשחק.
    2. גוללים למטה לקטע API Console Project ולוחצים על הקישור לפרויקט של API Console.
    3. במסך APIs & Services > Credentials ב-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 APIs משרת backend בשם שחקן שמחובר לחשבון זמין במאמר הפעלת גישה בצד השרת.

טיפול ביציאה של שחקן מהחשבון

כדי להוציא שחקנים מהמשחק, קוראים לשיטה signOut() ב-GoogleSignInClient. קטע קוד לדוגמה זמין במאמר בנושא יציאה מהנגן.

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

במאמר הזה מפורטות כל קריאות ה-API שזמינות לכם.

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

שחקן

  • רוצים לקבל את מזהה השחקן המחובר ואת נתוני הפרופיל שלו? מתקשרים אל 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 מתוך ממשקי ה-API לניהול כדי לאפס את ההישגים למצב המקורי שלהם.

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

מומלץ לעיין במדריך בנושא לוחות לידרבורד, שבו מוסבר על לוחות לידרבורד בפירוט רב יותר.

  • רוצה לקבל רשימה של כל טבלאות התוצאות במשחק? מתקשרים אל Leaderboards.list.
  • האם השחקן סיים משחק? אפשר לשלוח את הניקוד שלהם אל Scores.submit ולבדוק אם מדובר בשיא חדש.
  • רוצים להציג לידרבורד? מקבלים את הנתונים מ-Scores.list ומציגים אותם למשתמש.
  • אפשר להשתמש ב-Scores.listWindow כדי למצוא מגוון ציונים שקרובים לציון הגבוה של המשתמש.
  • כדי לקבל מידע נוסף על הניקוד של השחקן בטבלת לידרבורד מסוימת (לדוגמה, אם השחקן נמצא ב-12% המובילים מכל השחקנים), קוראים ל-Scores.get.
  • האם אתה מנפה באגים במשחק? אפשר לנסות להתקשר אל Scores.reset מתוך ממשקי ה-API של הניהול כדי לאפס את כל הניקוד של השחקן בטבלת Leaderboard מסוימת.