建議您驗證玩家,安全地將玩家身分傳遞至後端伺服器。這可讓遊戲以安全的方式擷取玩家身分資料和其他資料,避免在透過裝置傳遞的過程中可能遭到竄改的風險。
在這種情況下,玩家成功登入後,您就可以透過 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,重設玩家在特定排行榜的所有分數。