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

隨著 Google Sign-In API 淘汰,我們將在 2026 年移除遊戲第 1 版 SDK。2025 年 2 月後,您將無法在 Google Play 上發布新整合 games v1 SDK 的遊戲。建議您改用 games v2 SDK。
雖然採用舊版遊戲第 1 版整合功能的現有遊戲仍可繼續運作幾年,但建議您從 2025 年 6 月開始遷移至第 2 版

如果您的遊戲使用後端伺服器,建議您使用 Google 登入驗證玩家,並安全地將玩家身分傳遞至後端伺服器。這也能讓遊戲以安全的方式擷取玩家身分資料和其他資料,避免在透過裝置傳遞的過程中可能遭到竄改的風險。

在這種情況下,遊戲會提示玩家照常登入 Google Play 遊戲服務。玩家成功登入後,GoogleSignInAccount 物件就會包含一組一次性代碼 (稱為「伺服器驗證碼」),用戶端會將這組代碼傳遞至伺服器。接著,在伺服器上交換伺服器驗證碼,以取得伺服器可用以呼叫 Google Play 遊戲服務 API 的 OAuth 2.0 權杖。

如需在遊戲中加入登入功能的其他指引,請參閱「Android 遊戲登入」。

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

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

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

事前準備

您必須先在 Google Play 管理中心中新增遊戲,才能將 Google 登入功能整合至遊戲,如「設定 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. 您也必須指定遊戲需要後端伺服器的驗證碼,方法是呼叫 GoogleSignInOptions.Builder.requestServerAuthCode() 方法,並將伺服器的用戶端 ID 做為參數。您稍後會在後端伺服器上擷取驗證碼,以取得存取權杖,如「取得伺服器驗證碼」一節所述。
  3. 呼叫 GoogleSignIn.getClient() 方法,並傳入先前設定的選項。如果呼叫成功,Google 登入 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);
}

取得伺服器驗證碼

如要擷取遊戲在後端伺服器取得存取權杖使用的伺服器驗證碼,請在 Google 登入成功登入玩家後傳回的 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 和設定檔資料嗎?以 'me' 做為 ID 呼叫 Players.get

六人行

請務必詳閱「好友」指南,進一步瞭解好友功能。

成就

請務必參閱「成就」指南,進一步瞭解成就。

排行榜

請務必參閱「排行榜」指南,進一步瞭解排行榜。

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