啟用 Google Play 遊戲服務的伺服器端存取權

如果您的遊戲使用後端伺服器,建議您使用 Google 登入來驗證玩家,並安全地將玩家的身分傳送至後端伺服器。如此一來,您的遊戲還能安全地擷取玩家的身分及其他資料,而無需在裝置遭傳輸時受到潛在竄改。

在這種情況下,您的遊戲會照常提示玩家登入 Google Play 遊戲服務。玩家成功登入後,GoogleSignInAccount 物件會包含特殊的單次使用代碼 (稱為「伺服器驗證碼」),以供用戶端傳遞至伺服器。然後在伺服器上交換 OAuth 2.0 憑證的伺服器驗證碼,讓伺服器用來呼叫 Google Play Games Services API。

如需在遊戲中新增登入的其他指引,請參閱在 Android 遊戲中登入

如要查看如何使用 Google 登入功能驗證玩家的詳細程式碼範例,請參閱 GitHub 上的 clientserverskeleton 範例

離線存取必須採取以下步驟:

  1. 在 Google Play 管理中心:為遊戲伺服器建立憑證。憑證的 OAuth 用戶端類型為「網站」。
  2. 在 Android 應用程式中:登入時要求取得伺服器憑證的伺服器驗證碼,然後將該驗證碼傳遞至伺服器。
  3. 您的遊戲伺服器:以 Google 驗證服務交換 OAuth 存取憑證的伺服器驗證碼,然後用來呼叫 Play 遊戲服務 REST API

事前準備

將 Google 登入功能整合至您的遊戲之前,您必須先在 Google Play 管理中心新增遊戲,詳情請參閱設定 Google Play 遊戲服務的說明。

為遊戲建立相關的伺服器端網路應用程式

Google Play 遊戲服務無法提供網路遊戲的後端支援,但是會提供 Android 遊戲伺服器的後端伺服器支援。

如果要在伺服器端應用程式中使用 Google Play 遊戲服務的 REST API,請按照以下步驟操作:

  1. Google Play 管理中心的「已連結的應用程式」部分中為遊戲建立相關聯的網路應用程式。請注意,此流程不會使用 launch_url,但也可以留空。
  2. 如要取得應用程式的憑證資訊,請按照下列步驟操作:
    1. 在 Google Play 管理中心的遊戲中,按一下 [遊戲詳細資訊]。
    2. 向下捲動至「API 主控台專案」部分,然後按一下 API 控制台專案的連結。
    3. 在 Google API 控制台的「API 和服務」>「憑證」畫面中,下載網路應用程式的 client_secret.json 檔案,然後儲存到伺服器可存取的位置。記下憑證的用戶端 ID 以供日後參考。
  3. 重新啟動伺服器端應用程式,以便接受來自遊戲用戶端應用程式的要求。

在用戶端執行登入作業

GoogleSignInClient 類別是擷取目前登入玩家帳戶的主要進入點;如果之前裝置上未在裝置上執行過應用程式,則用來登入玩家。

建立登入用戶端的步驟如下:

  1. 透過 GoogleSignInOptions 物件建立登入用戶端。在 GoogleSignInOptions.Builder 中設定登入時,您必須指定 GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN
  2. 您還必須透過呼叫伺服器用戶端 ID 做為參數的 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);
}

取得伺服器驗證碼

如要擷取遊戲在後端伺服器上用於存取存取憑證的伺服器驗證碼,請針對成功登入的使用者所傳回的 GoogleSignInAccount 物件呼叫 getServerAuthCode() 方法。

範例如下:


// 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 遊戲服務 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 API,請參閱啟用伺服器端存取權

處理玩家登出

如要登出玩家,請在 GoogleSignInClient 上呼叫 signOut() 方法。如需程式碼片段範例,請參閱將玩家登出一文。

從伺服器呼叫 REST API

如需可用 API 呼叫的完整說明,請參閱 Google Play 遊戲服務的 REST API

以下是幾個實用的 REST API 呼叫範例,歡迎參考:

玩家

  • 想要取得已登入玩家的 ID 和設定檔資料嗎?呼叫 Players.get 並使用 'me' 做為 ID。

朋友

請務必查看好友指南,進一步瞭解好友。

成就

請務必詳閱成就指南,其中會進一步說明各項成就。

排行榜

請務必參閱排行榜指南,進一步瞭解排行榜的詳細資訊。

  • 想要取得遊戲中所有計分板的清單嗎?呼叫 排行榜 s.list
  • 玩家完成遊戲了嗎?您可以將他們的分數提交至 Scores.submit,看看這是否為新的高分。
  • 想要顯示排行榜嗎?從 Scores.list 取得資料並向使用者顯示。
  • 使用 Scores.listWindow 尋找接近使用者最高分的分數。
  • 如要進一步瞭解特定排行榜中玩家的分數 (例如,假設玩家在前 12% 的排行榜中),請呼叫 Scores.get
  • 您是否正在對遊戲進行偵錯?嘗試透過 Management API 呼叫 Scores.reset,重設特定排行榜中該玩家的所有分數