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

אם המשחק שלכם משתמש בשרת לקצה העורפי, מומלץ להשתמש בכניסה באמצעות חשבון 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 Games לא מספקים תמיכה לקצה העורפי של משחקים באינטרנט. עם זאת, הוא מספק תמיכה בשרתים לקצה העורפי של השרת של המשחק ל-Android.

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

  1. יוצרים אפליקציית אינטרנט משויכת למשחק בקטע אפליקציות מקושרות ב-Google Play Console. הערה: השדה launch_url לא משמש בתהליך הזה וניתן להשאיר אותו ריק.
  2. כדי לקבל את פרטי הכניסה של האפליקציה:
    1. במשחק ב-Google Play Console, לוחצים על פרטי המשחק.
    2. גוללים למטה לקטע API Console Project ולוחצים על הקישור לפרויקט במסוף ה-API.
    3. במסוף Google API, במסך APIs & Services > Credentials, מורידים את הקובץ client_secret.json של אפליקציית האינטרנט ושומרים אותו במיקום שאליו יש לשרת גישה. מתעדים את מזהה הלקוח של פרטי הכניסה לשימוש עתידי.
  3. מפעילים מחדש את האפליקציה בצד השרת כדי שהיא תהיה מוכנה לקבל בקשות מאפליקציית הלקוח של המשחק.

ביצוע כניסה לחשבון בצד הלקוח

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

כדי ליצור לקוח כניסה:

  1. יוצרים לקוח כניסה באמצעות האובייקט GoogleSignInOptions. כדי להגדיר את הכניסה, צריך לציין את הערך GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN ב-GoogleSignInOptions.Builder.
  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 Sign-In לאחר כניסה מוצלחת של משתמש.

הנה דוגמה:

// 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 משרת לקצה העורפי מטעם שחקן שמחובר לחשבון זמין במאמר הפעלת גישה בצד השרת.

טיפול ביציאה של שחקנים

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

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

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

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

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

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