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

您可以使用 Google Play 遊戲服務讓玩家登錄基於 Firebase 構建並用 C++ 編寫的 Android 遊戲。要使用 Google Play 遊戲服務登錄 Firebase,首先使用 Google Play 遊戲登錄播放器,然後在執行此操作時請求 OAuth 2.0 授權代碼。然後,將授權代碼傳遞給PlayGamesAuthProvider以生成 Firebase 憑據,您可以使用該憑據向 Firebase 進行身份驗證。

在你開始之前

在使用Firebase Authentication之前,您需要:

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

    如果您的 C++ 項目已經使用 Firebase,那麼它已經針對 Firebase 進行了註冊和配置。

  • Firebase C++ SDK添加到您的 C++ 項目。

請注意,將 Firebase 添加到您的 C++ 項目涉及Firebase 控制台和您打開的 C++ 項目中的任務(例如,您從控制台下載 Firebase 配置文件,然後將它們移動到您的 C++ 項目中)。

設置您的 Firebase 項目

  1. 如果您還沒有,請在 Firebase 控制台的“設置”頁面中設置遊戲的 SHA-1 指紋。

    您可以使用 gradle signingReport命令獲取簽名證書的 SHA 哈希值:

    ./gradlew signingReport

  2. 啟用 Google Play 遊戲作為登錄提供商:

    1. 在 Firebase 控制台中,打開身份驗證部分

    2. 生成並獲取項目的 Web 服務器客戶端 ID 和客戶端密碼:

      1. 登錄方法選項卡中,啟用Google登錄提供程序。

      2. Google登錄提供商處複製網絡服務器客戶端 ID 和密碼。

    3. 登錄方法選項卡中,啟用Play 遊戲登錄提供程序,並指定您在上一步中獲得的項目的 Web 服務器客戶端 ID 和客戶端密碼。

使用您的 Firebase 應用信息配置 Play 遊戲服務

  1. Google Play Console中,打開您的 Google Play 應用或創建一個。

  2. 發展部分,點擊Play 遊戲服務 > 設置和管理 > 配置

  3. 點擊Yes, my game already uses Google APIs ,從列表中選擇您的 Firebase 項目,然後點擊Use

  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 登錄 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 登錄 C++ SDK:

    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:20.7.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 Games 登錄後,您可以使用授權碼向 Firebase 進行身份驗證。

  1. 玩家使用 Play Games 成功登錄後,獲取玩家帳戶的授權碼。

  2. 然後,將 Play 遊戲服務中的授權代碼換成 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();