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

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

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

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

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

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

כדי לקבל גישה אופליין:

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

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

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

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

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

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

  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. מפעילים את ה-method‏ 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;
}

מידע נוסף על גישה לממשקי API של Google משרת בק-אנד מטעם שחקן מחובר זמין במאמר הפעלת גישה מצד השרת.

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

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

ביצוע קריאות ל-REST API מהשרת

כאן אפשר לקרוא תיאור מלא של קריאות ה-API שזמינות ל-Google Play Games Services.

דוגמאות לקריאות ל-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 מ-Management APIs כדי לאפס את ההישגים למצב המקורי שלהם.

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

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

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