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

您可以使用 Google Play 遊戲服務讓玩家登錄基於 Firebase 並用 C++ 編寫的 Android 遊戲。要使用通過 Firebase 登錄的 Google Play Games 服務,請先使用 Google Play Games 登錄播放器,然後在執行此操作時請求 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 控制台的設置頁面中設置。

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

    ./gradlew signingReport

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

    1. 查找項目的 Web 服務器客戶端 ID 和客戶端密碼。 Web 服務器客戶端 ID 向 Google Play 身份驗證服務器標識您的 Firebase 項目。

      要找到這些值:

      1. Google API 控制台憑據頁面中打開您的 Firebase 項目。
      2. OAuth 2.0 客戶端 ID部分中,打開Web 客戶端(由 Google 服務自動創建)詳細信息頁面。此頁面列出了您的 Web 服務器客戶端 ID 和密碼。
    2. 然後,在Firebase 控制台中,打開身份驗證部分。

    3. 登錄方法選項卡上,啟用Play Games登錄提供程序。您需要指定您從 API 控制台獲得的項目的 Web 服務器客戶端 ID 和客戶端密碼。

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

  1. Google Play Console中,打開您的 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 Games 登錄。

向 C++ Android 項目添加對 Play Games 登錄的支持的最簡單且推薦的方法是使用Google Sign-in C++ SDK

要使用 Google Sign-in C++ SDK 將 Play Games 登錄添加到您的遊戲,請執行以下操作:

  1. 克隆或下載Google Sign-in Unity 插件存儲庫,其中還包含 C++ SDK。

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

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

  3. 在遊戲的原生代碼 make 文件中包含 Google Sign-in C++ SDK:

    製作

    在您的頂級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.2.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 Games 登錄並檢索服務器身份驗證代碼:

    #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::User*> result =
        auth->SignInWithCredential(credential);
    
  3. 如果您的程序有一個定期運行的更新循環(例如每秒 30 或 60 次),您可以使用Auth::SignInWithCredentialLastResult每次更新檢查一次結果:

    firebase::Future<firebase::auth::User*> result =
        auth->SignInWithCredentialLastResult();
    if (result.status() == firebase::kFutureStatusComplete) {
      if (result.error() == firebase::auth::kAuthErrorNone) {
        firebase::auth::User* user = *result.result();
        printf("Sign in succeeded for `%s`\n", 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::User*> 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::User*> 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 != nullptr) {
  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 Games 玩家信息或訪問 Play 遊戲服務,請使用Google Play 遊戲服務 C++ SDK提供的 API。

要註銷用戶,請調用SignOut()

auth->SignOut();