建議您驗證玩家,安全地將玩家身分傳遞至後端伺服器。這可讓遊戲以安全的方式擷取玩家身分資料和其他資料,避免在透過裝置傳遞的過程中可能遭到竄改的風險。
在這種情況下,玩家成功登入後,您就可以透過 Play 遊戲服務第 2 版 SDK 要求一組一次性代碼 (稱為「伺服器驗證碼」),然後用戶端就會傳遞至伺服器。接著,在伺服器上交換伺服器驗證碼,以取得伺服器可用以呼叫 Google Play Games Services API 的 OAuth 2.0 權杖。
如需在遊戲中加入登入的其他指引,請參閱「Android 遊戲登入」。
離線存取必須採取以下步驟:
- 在 Google Play 管理中心:建立遊戲伺服器的憑證。憑證的 OAuth 用戶端類型為「網站」。
- 在 Android 應用程式:在登入時要求取得伺服器憑證的伺服器驗證碼,然後將該驗證碼傳遞至伺服器。
- 在遊戲伺服器:使用 Google 驗證服務交換 OAuth 存取權杖的伺服器驗證碼,然後使用此驗證碼呼叫 Play 遊戲服務 REST API。
事前準備
您必須先按照「設定 Google Play 遊戲服務」的說明,在 Google Play 管理中心新增遊戲,並整合 Play 遊戲服務登入與遊戲。
建立伺服器端網頁應用程式
Google Play 遊戲服務無法提供網路遊戲的後端支援,但會為 Android 遊戲伺服器提供後端伺服器支援。
如果要在伺服器端應用程式中使用 Google Play 遊戲服務的 REST API,請按照下列步驟操作:
- 在 Google Play 管理中心中選取遊戲。
- 依序前往「Play 遊戲服務」>「設定與管理」>「設定」。
- 選取「新增憑證」,即可前往「新增憑證」頁面。選取「遊戲伺服器」做為憑證類型,然後繼續前往「授權」部分。
- 如果您的遊戲伺服器已有 OAuth 用戶端 ID,請從下拉式選單中選取。儲存變更後,請繼續前往下一節。
- 如果您的遊戲伺服器沒有現有的 OAuth 用戶端 ID,可以建立一個。
- 點選「建立 OAuth 用戶端」,然後點選「建立 OAuth 用戶端 ID」連結。
- 系統會導向 Google Cloud Platform 的「建立 OAuth 用戶端 ID」頁面,顯示與遊戲相關聯的專案。
- 填寫頁面的表單,然後按一下「建立」。請務必將應用程式類型設為網頁應用程式。
- 返回「新增憑證」頁面的「授權」部分,選取新建立的 OAuth 用戶端並儲存變更。
取得伺服器驗證碼
如要擷取遊戲在後端伺服器取得存取權杖使用的伺服器驗證碼,請按照下列步驟操作:
從用戶端呼叫
requestServerSideAccess
。- 請務必使用為遊戲伺服器註冊的 OAuth 用戶端 ID,而非 Android 應用程式的 OAuth 用戶端 ID。
- (選用) 如果遊戲伺服器需要離線存取 (使用更新權杖的長期存取權) Play 遊戲服務,可將 forceRefreshToken 參數設為 true。
GamesSignInClient gamesSignInClient = PlayGames.getGamesSignInClient(this); gamesSignInClient .requestServerSideAccess(OAUTH_2_WEB_CLIENT_ID, /* forceRefreshToken= */ false) .addOnCompleteListener( task -> { if (task.isSuccessful()) { String serverAuthToken = task.getResult(); // Send authentication code to the backend game server to be // exchanged for an access token and used to verify the player // via the Play Games Services REST APIs. } else { // Failed to retrieve authentication code. } });
將 OAuth 驗證碼權杖傳送至後端伺服器,以便讓使用者交換、透過 Play 遊戲服務 REST API 驗證玩家 ID,並且透過遊戲進行驗證。
傳送伺服器驗證碼
將伺服器驗證碼傳送至後端伺服器,以交換取得存取權和更新權杖。請使用存取權杖代表玩家呼叫 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 shouldn't 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;
}
從伺服器呼叫 REST API
如需可用的 API 呼叫的完整說明,請參閱「Google Play 遊戲服務的 REST API」。
以下是幾個實用的 REST API 呼叫範例,歡迎參考:
球員
想要取得已登入玩家的 ID 和設定檔資料嗎?以 'me'
做為 ID 呼叫 Players.get。
六人行
詳情請參閱「好友」指南。
如要擷取玩家的好友名單,請以
friends_all
為collection
呼叫 Players.list。如要確認能否存取好友名單,請以
me
做為playerID
呼叫 Players.get,然後在回應中查看profileSettings.friendsListVisibility
欄位。
成就
詳情請參閱「成就」指南。
如要取得目前成就的清單,請呼叫 AchievementDefinitions.list。
然後結合對 Achievements.list 的呼叫,找出玩家已解鎖的成就。
呼叫 Achievements.unlock 以解鎖玩家成就。
呼叫 Achievements.increment 回報成就的進度,瞭解玩家是否已解鎖成就。
如果您正在對尚未成為正式版的遊戲進行偵錯,可以從 Management API 呼叫 Achievements.reset 或 Achievements.resetAll,將成就重設為其原始狀態。
排行榜
詳情請參閱「排行榜」指南。
想要取得遊戲中所有計分板的清單嗎?呼叫 Leaderboards.list。
如果玩家已完成遊戲,您可以將玩家分數提交至 Scores.submit,瞭解該分數是否為新的最高分記錄。
如要顯示排行榜,請從 Scores.list 取得資料,並向使用者顯示。
使用 Scores.listWindow 找出接近使用者最高分的分數。
如要進一步瞭解特定排行榜中的玩家得分 (舉例來說,如果玩家的排名是所有玩家中的前 12%),請呼叫 Scores.get。
如果要為遊戲偵錯,您可以從 Management API 呼叫 Scores.reset,重設玩家在特定排行榜的所有分數。