使用 Google Play 游戏服务进行身份验证 (C++)

您可以使用 Google Play 游戏服务,使玩家能够登录在 Firebase 上构建的以 C++ 编写的 Android 游戏。要将 Google Play 游戏服务的登录服务用于 Firebase,请首先让玩家登录 Google Play 游戏,并在执行此操作时请求一个 OAuth 2.0 身份验证代码。然后,将此身份验证代码传递给 PlayGamesAuthProvider 以便生成一个 Firebase 凭据,使用此凭据即可进行 Firebase 身份验证。

开始之前

将 Firebase 添加到您的 Android NDK 项目

按照 Firebase C++ SDK 设置指南中的 Android 设置步骤操作,将 Firebase 添加到您的 Android NDK 项目。

将 Firebase C++ SDK 添加到您项目的原生代码 make 文件中:

CMake

在您的顶级 CMakeLists.txt 文件中:

set(FIREBASE_CPP_SDK_DIR "/path/to/firebase_cpp_sdk")
string(REGEX REPLACE "(.*)_.*" "\\1" STL ${ANDROID_STL})

add_library(firebase_app STATIC IMPORTED)
set_target_properties(firebase_app PROPERTIES IMPORTED_LOCATION
    ${FIREBASE_CPP_SDK_DIR}/libs/android/${ANDROID_ABI}/${STL}/libapp.a)

add_library(firebase_auth STATIC IMPORTED)
set_target_properties(firebase_auth PROPERTIES IMPORTED_LOCATION
    ${FIREBASE_CPP_SDK_DIR}/libs/android/${ANDROID_ABI}/${STL}/libauth.a)

...

target_link_libraries(
    ...
    firebase_app
    firebase_auth)

ndk-build

在您的 Android.mk 文件中:

STL := $(firstword $(subst _, ,$(APP_STL)))
FIREBASE_CPP_SDK_DIR := /path/to/frebase_cpp_sdk
FIREBASE_LIBRARY_PATH := \
    $(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)

include $(CLEAR_VARS)
LOCAL_MODULE := firebase_app
LOCAL_SRC_FILES := $(FIREBASE_LIBRARY_PATH)/libapp.a
LOCAL_EXPORT_C_INCLUDES := $(FIREBASE_CPP_SDK_DIR)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := firebase_auth
LOCAL_SRC_FILES := $(FIREBASE_LIBRARY_PATH)/libauth.a
LOCAL_EXPORT_C_INCLUDES := $(FIREBASE_CPP_SDK_DIR)/include
include $(PREBUILT_STATIC_LIBRARY)

在您的模块级 build.gradle 文件中,将 Firebase 声明为依赖项:

dependencies {
    implementation 'com.google.firebase:firebase-auth:15.1.0'
    implementation 'com.google.android.gms:play-services-base:15.0.1'
    implementation 'com.google.android.gms:play-services-auth:15.0.1'
}

设置您的 Firebase 项目

  1. 从 Firebase 控制台的设置页面设置游戏的 SHA-1 指纹。

    您可以使用 keytool 命令获得您的密钥的 SHA-1 指纹:

    keytool -exportcert -list -v \
        -alias YOUR-KEY-NAME -keystore PATH-TO-KEYSTORE

  2. 启用 Google Play 游戏作为登录服务提供方:

    1. 找到您的项目的网络服务器客户端 ID 和客户端口令。网络服务器客户端 ID 可向 Google Play 身份验证服务器表明您的 Firebase 项目的身份。

      要找到这些值,请执行以下操作:

      1. Google API 控制台凭据页面中打开您的 Firebase 项目。
      2. OAuth 2.0 客户端 ID (OAuth 2.0 client IDs) 部分中,打开网络客户端(由 Google 服务自动创建)(Web client (auto created by Google Service)) 详细信息页面。此页面会列出您的网络服务器客户端 ID 和口令。
    2. 然后,在 Firebase 控制台中,打开 Authentication(身份验证)部分。

    3. 登录方法标签上,启用 Play 游戏登录服务提供方。您需要指定从 API 控制台中获得的您的项目的网络服务器客户端 ID 和客户端口令。

  1. 打开 Google Play 管理中心,然后点击游戏服务
  2. 点击添加新游戏。在新游戏对话框中,点击我已经在自己的游戏中使用了 Google API,然后在列表中点击您的 Firebase 项目的名称。选择一个游戏类别,然后点击继续进入“游戏详情”页面。
  3. 在“游戏详情”页面的末尾,确保已启用所有必需的 API。
  4. 接下来,打开关联的应用页面并点击 Android。指定您游戏的软件包名称,然后点击保存并继续。管理中心将显示您的 Android 客户端 ID。您可以忽略这个值。
  5. 测试页面上,将所有需要登录您的游戏的用户的电子邮件地址列入白名单,然后在 Play 商店中发布游戏。

将 Play 游戏登录服务集成到您的游戏中

您必须先集成 Google Play 游戏登录服务,然后才能够让玩家登录游戏。

推荐使用 Google Sign-in C++ SDK 为 C++ Android 项目添加 Play 游戏登录服务支持,这是最简单的方法。

要使用 Google Sign-in C++ SDK 将 Play 游戏登录服务添加到您的游戏中,请执行以下操作:

  1. 克隆或下载 Google 登录 Unity 插件库,其中也包含 C++ SDK。

  2. 使用 Android Studio 或 gradlew buildstaging/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-build

    在您的 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:15.0.1'
        // 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 游戏:

    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 游戏服务提供的身份验证代码换取一个 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 的回调

有些程序会有 Update 函数,其调用频率为每秒 30 次或 60 次。例如,许多游戏都采用这种模型。这些程序可以调用 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 实时数据库和 Cloud Storage 安全规则中,您可以从 auth 变量中获取已登录用户的唯一身份用户 ID,然后用此 ID 来控制用户可以访问哪些数据。

要获取用户的 Play 游戏玩家信息或访问 Play 游戏服务,可使用 Google Play 游戏服务 C++ SDK 提供的 API。

要让用户退出登录,可调用 SignOut()

auth->SignOut();

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面