Google 致力于为黑人社区推动种族平等。查看具体举措

通过 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.4.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 Games 服务,请使用Google Play Games 服务 C++ SDK提供的 API。

要注销用户,请调用SignOut()

auth->SignOut();