透過 C++ 使用 Google Play 遊戲服務進行身份驗證

您可以使用 Google Play 遊戲服務讓玩家登入基於 Firebase 建置並用 C++ 編寫的 Android 遊戲。若要使用 Google Play 遊戲服務登入 Firebase,請先使用 Google Play 遊戲登入玩家,然後在執行此操作時請求 OAuth 2.0 驗證程式碼。然後,將身份驗證程式碼傳遞給PlayGamesAuthProvider以產生 Firebase 憑證,您可以使用該憑證透過 Firebase 進行身份驗證。

在你開始之前

在使用Firebase 驗證之前,您需要:

  • 註冊您的 C++ 專案並將其配置為使用 Firebase。

    如果您的 C++ 專案已使用 Firebase,則它已針對 Firebase 進行註冊和設定。

  • Firebase C++ SDK加入到您的 C++ 專案。

請注意,將 Firebase 新增至 C++ 專案涉及Firebase 控制台和開啟的 C++ 專案中的任務(例如,從控制台下載 Firebase 設定文件,然後將它們移至 C++ 專案中)。

設定您的 Firebase 項目

  1. 如果您尚未設定遊戲的 SHA-1 指紋,請在 Firebase 控制台的「設定」頁面中設定。

    您可以使用signingReport指令來取得簽章憑證的 SHA 雜湊值:

    ./gradlew signingReport

  2. 啟用 Google Play 遊戲作為登入提供者:

    1. 在 Firebase 控制台中,開啟「驗證」部分

    2. 產生並取得專案的 Web 伺服器客戶端 ID 和客戶端金鑰:

      1. 「登入方法」標籤中,啟用Google登入提供者。

      2. Google登入提供者複製 Web 伺服器用戶端 ID 和密碼。

    3. 「登入方法」標籤中,啟用Play 遊戲登入提供程序,並指定您在上一個步驟中獲得的項目的 Web 伺服器用戶端 ID 和用戶端金鑰。

使用您的 Firebase 應用程式資訊設定 Play 遊戲服務

  1. Google Play 管理中心中,開啟您的 Google Play 應用程式或建立一個。

  2. 「發展」部分中,點選Play 遊戲服務 > 設定與管理 > 設定

  3. 點擊是,我的遊戲已使用 Google API ,從清單中選擇您的 Firebase 項目,然後點擊使用

  4. 在 Play 遊戲服務設定頁面上,點選新增憑證

    1. 選擇遊戲伺服器類型。
    2. OAuth 用戶端欄位中,選擇專案的 Web 用戶端 ID。確保這與您啟用 Play 遊戲登入時指定的用戶端 ID 相同。
    3. 儲存您的變更。
  5. 仍在 Play 遊戲服務設定頁面上,再次點選新增憑證

    1. 選擇安卓類型。
    2. OAuth 用戶端欄位中,選擇專案的 Android 用戶端 ID。 (如果您沒有看到 Android 用戶端 ID,請務必在 Firebase 控制台中設定遊戲的 SHA-1 指紋。)
    3. 儲存您的變更。
  6. 「測試人員」頁面上,新增在 Play 商店上發布遊戲之前需要能夠登入遊戲的所有使用者的電子郵件地址。

將 Play 遊戲登入整合到您的遊戲中

您必須先整合 Google Play 遊戲登入功能,然後才能讓玩家登入您的遊戲。

在 C++ Android 專案中新增對 Play 遊戲登入的支援的最簡單且推薦的方法是使用Google Sign-in C++ SDK

若要使用 Google 登入 C++ SDK 將 Play 遊戲登入功能新增至您的遊戲中,請執行以下操作:

  1. 複製或下載Google Sign-in Unity 外掛程式儲存庫,其中還包含 C++ SDK。

  2. 使用 Android Studio 或gradlew build建置staging/native/目錄中包含的項目。

    建置將其輸出複製到名為google-signin-cpp的目錄。

  3. 將 Google Sign-in C++ SDK 包含在遊戲的本機程式碼 make 檔案中:

    CMake

    在您的頂級CMakeLists.txt檔案中:

    set(GSI_PACKAGE_DIR "/path/to/google-signin-cpp")
    add_library(lib-google-signin-cpp STATIC IMPORTED) set_target_properties(lib-google-signin-cpp PROPERTIES IMPORTED_LOCATION     ${GSI_PACKAGE_DIR}/lib/${ANDROID_ABI}/libgoogle-signin-cpp.a )
    ...
    target_link_libraries(     ...     lib-google-signin-cpp)

    ndk-構建

    在您的Android.mk檔案中:

    include $(CLEAR_VARS)
    LOCAL_MODULE := google-signin-cpp
    GSI_SDK_DIR := /path/to/google-signin-cpp
    LOCAL_SRC_FILES := $(GSI_SDK_DIR)/lib/$(TARGET_ARCH_ABI)/libgoogle-signin-cpp.a
    LOCAL_EXPORT_C_INCLUDES := $(GSI_SDK_DIR)/include
    include $(PREBUILT_STATIC_LIBRARY)
    

  4. 接下來,包含 C++ SDK 所需的 Java 幫助程式元件。

    為此,請在專案級build.gradle檔案中將 SDK 建置輸出目錄新增為本機儲存庫:

    allprojects {
        repositories {
            // ...
            flatDir {
                dirs 'path/to/google-signin-cpp'
            }
        }
    }
    

    並且,在模組級build.gradle檔案中,將輔助元件宣告為依賴項:

    dependencies {
        implementation 'com.google.android.gms:play-services-auth:21.0.0'
        // Depend on the AAR built with the Google Sign-in SDK in order to add
        // the Java helper classes, which are used by the C++ library.
        compile(name:'google-signin-cpp-release', ext:'aar')
    }
    
  5. 然後,在您的遊戲中配置GoogleSignIn物件以使用 Play 遊戲登入並擷取伺服器驗證程式碼:

    #include "google_signin.h"
    #include "future.h"
    
    using namespace google::signin;
    
    // ...
    
    GoogleSignIn::Configuration config = {};
    config.web_client_id = "YOUR_WEB_CLIENT_ID_HERE";
    config.request_id_token = false;
    config.use_game_signin = true;
    config.request_auth_code = true;
    
    GoogleSignIn gsi = GoogleSignIn(GetActivity(), GetJavaVM());
    gsi.Configure(config);
    
  6. 最後,呼叫SignIn()讓玩家登入 Play Games:

    Future<GoogleSignIn::SignInResult> &future = gsi.SignIn();
    

    SignIn()傳回的 Future 解析時,您可以從結果中取得伺服器驗證程式碼:

    if (!future.Pending()) {
        const GoogleSignIn::StatusCode status =
                static_cast<GoogleSignIn::StatusCode>(future.Status());
        if (status == GoogleSignIn::kStatusCodeSuccess) {
            // Player successfully signed in to Google Play! Get auth code to
            //   pass to Firebase
            const GoogleSignIn::SignInResult result =
                    static_cast<GoogleSignIn::SignInResult>(future.Result());
            const char* server_auth_code = result.User.GetServerAuthCode();
        }
    }
    

使用 Firebase 進行身份驗證

玩家登入 Play 遊戲後,您可以使用身份驗證程式碼向 Firebase 進行身份驗證。

  1. 玩家使用 Play 遊戲成功登入後,取得玩家帳號的授權碼。

  2. 然後,將 Play Games 服務中的驗證程式碼交換為 Firebase 憑證,並使用 Firebase 憑證對玩家進行驗證:

    firebase::auth::Credential credential =
        firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code);
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  3. 如果您的程式有定期執行的更新循環(例如每秒 30 或 60 次),您可以使用Auth::SignInAndRetrieveDataWithCredentialLastResult檢查每次更新一次的結果:

    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredentialLastResult();
    if (result.status() == firebase::kFutureStatusComplete) {
      if (result.error() == firebase::auth::kAuthErrorNone) {
        firebase::auth::AuthResult auth_result = *result.result();
        printf("Sign in succeeded for `%s`\n",
               auth_result.user.display_name().c_str());
      } else {
        printf("Sign in failed with error '%s'\n", result.error_message());
      }
    }

    或者,如果您的程式是事件驅動的,您可能更願意在 Future 上註冊回調

在 Future 註冊回調

有些程式具有每秒呼叫 30 或 60 次的Update函數。例如,許多遊戲都遵循這種模式。這些程式可以呼叫LastResult函數來輪詢非同步呼叫。但是,如果您的程式是事件驅動的,您可能更喜歡註冊回呼函數。 Future 完成後呼叫回調函數。
void OnCreateCallback(const firebase::Future<firebase::auth::User*>& result,
                      void* user_data) {
  // The callback is called when the Future enters the `complete` state.
  assert(result.status() == firebase::kFutureStatusComplete);

  // Use `user_data` to pass-in program context, if you like.
  MyProgramContext* program_context = static_cast<MyProgramContext*>(user_data);

  // Important to handle both success and failure situations.
  if (result.error() == firebase::auth::kAuthErrorNone) {
    firebase::auth::User* user = *result.result();
    printf("Create user succeeded for email %s\n", user->email().c_str());

    // Perform other actions on User, if you like.
    firebase::auth::User::UserProfile profile;
    profile.display_name = program_context->display_name;
    user->UpdateUserProfile(profile);

  } else {
    printf("Created user failed with error '%s'\n", result.error_message());
  }
}

void CreateUser(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // `&my_program_context` is passed verbatim to OnCreateCallback().
  result.OnCompletion(OnCreateCallback, &my_program_context);
}
如果您願意,回呼函數也可以是 lambda。
void CreateUserUsingLambda(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // The lambda has the same signature as the callback function.
  result.OnCompletion(
      [](const firebase::Future<firebase::auth::User*>& result,
         void* user_data) {
        // `user_data` is the same as &my_program_context, below.
        // Note that we can't capture this value in the [] because std::function
        // is not supported by our minimum compiler spec (which is pre C++11).
        MyProgramContext* program_context =
            static_cast<MyProgramContext*>(user_data);

        // Process create user result...
        (void)program_context;
      },
      &my_program_context);
}

下一步

用戶首次登入後,系統會建立一個新用戶帳戶並將其連結到其 Play 遊戲 ID。此新帳戶作為 Firebase 專案的一部分存儲,可用於識別專案中每個應用程式中的使用者。

在您的遊戲中,您可以從firebase::auth::User物件取得使用者的 Firebase UID:

firebase::auth::User user = auth->current_user();
if (user.is_valid()) {
  std::string playerName = user.displayName();

  // The user's ID, unique to the Firebase project.
  // Do NOT use this value to authenticate with your backend server,
  // if you have one. Use firebase::auth::User::Token() instead.
  std::string uid = user.uid();
}

在 Firebase 即時資料庫和雲端儲存安全性規則中,您可以從auth變數取得登入使用者的唯一使用者 ID,並使用它來控制使用者可以存取哪些資料。

若要取得使用者的 Play 遊戲玩家資訊或存取 Play 遊戲服務,請使用Google Play 遊戲服務 C++ SDK提供的 API。

若要登出用戶,請呼叫SignOut()

auth->SignOut();