אימות באמצעות Google Play Games Services עם C++

אתם יכולים להשתמש ב-Google Play Games Services כדי להכניס שחקנים למשחק ל-Android שנבנה על Firebase ונכתב ב-C++. כדי להשתמש בכניסה ל-Google Play Games Services עם Firebase, קודם צריך להכניס את השחקן באמצעות Google Play Games, ולבקש קוד אימות 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. אם עוד לא עשיתם זאת, מציינים את טביעת האצבע מסוג SHA-1 של האפליקציה.

    1. במסוף Firebase, עוברים אל הגדרות > הכרטיסייה כללי.

    2. גוללים למטה אל הכרטיס האפליקציות שלך, בוחרים את אפליקציית Android ומוסיפים את טביעת האצבע מסוג SHA-1 בשדה טביעות אצבע של אישור SHA.

    אפשר לקבל את הגיבוב SHA של אישור החתימה באמצעות הפקודה gradle signingReport:

    ./gradlew signingReport

    במאמר אימות הלקוח מוסבר איך מקבלים את טביעת האצבע של ה-SHA של האפליקציה.

  2. הפעלת Google Play Games כספק כניסה:

    1. במסוף Firebase, עוברים אל Security (אבטחה) >‏ Authentication (אימות).

    2. יוצרים ומקבלים את מזהה הלקוח של שרת האינטרנט של הפרויקט ואת הסוד של הלקוח:

      1. בכרטיסייה Sign in method (שיטת הכניסה), מפעילים את ספק הכניסה Google.

      2. מעתיקים את מזהה הלקוח ואת הסוד של שרת האינטרנט מספק הכניסה Google.

    3. בכרטיסייה Sign in method (שיטת הכניסה), מפעילים את ספק הכניסה Play Games ומציינים את מזהה הלקוח (Client ID) ואת סוד הלקוח (Client Secret) של שרת האינטרנט של הפרויקט, שקיבלתם בשלב הקודם.

הגדרת Play Games services עם פרטי האפליקציה ב-Firebase

  1. במסוף Google Play, פותחים את אפליקציית Google Play או יוצרים אחת.

  2. בקטע צמיחה, לוחצים על Play Games services > הגדרה וניהול > הגדרות.

  3. לוחצים על כן, המשחק שלי כבר עושה שימוש ב-Google APIs, בוחרים את הפרויקט ב-Firebase מהרשימה ולוחצים על שימוש.

  4. בדף ההגדרה של Play Games services, לוחצים על הוספת פרטי כניסה.

    1. בוחרים בסוג שרת גיימינג.
    2. בשדה לקוח OAuth, בוחרים את מזהה לקוח האינטרנט של הפרויקט. חשוב לוודא שזהו אותו מספר לקוח שציינתם כשמפעילים את הכניסה לחשבון Play Games.
    3. שומרים את השינויים.
  5. עדיין בדף ההגדרה של Play Games services, לוחצים שוב על הוספת פרטי כניסה.

    1. בוחרים בסוג Android.
    2. בשדה לקוח OAuth, בוחרים את מזהה הלקוח של Android בפרויקט. (אם מזהה הלקוח של Android לא מופיע, צריך לוודא שהגדרתם את טביעת האצבע של SHA-1 של המשחק ב-Firebase).
    3. שומרים את השינויים.
  6. בדף Testers, מוסיפים את כתובות האימייל של כל המשתמשים שצריכים להיות מסוגלים להיכנס למשחק לפני שמפרסמים אותו ב-Play Store.

שילוב של התחברות ל-Play Games במשחק

כדי לאפשר לשחקנים להיכנס למשחק, צריך לשלב את תהליך הכניסה ל-Google Play Games.

הדרך הקלה והמומלצת להוסיף תמיכה בכניסה ל-Play Games לפרויקט Android בשפת C++‎ היא באמצעות Google Sign-in C++ SDK.

כדי להוסיף למשחק שלכם כניסה באמצעות Play Games באמצעות Google Sign-in C++ SDK, מבצעים את הפעולות הבאות:

  1. משכפלים או מורידים את מאגר הפלאגין של Google Sign-in Unity, שמכיל גם את ה-SDK בשפת C++‎.

  2. יוצרים את הפרויקט שנמצא בספרייה staging/native/ באמצעות Android Studio או gradlew build.

    תהליך ה-build מעתיק את הפלט שלו לספרייה בשם google-signin-cpp.

  3. כוללים את Google Sign-in C++ SDK בקובץ ה-make של קוד Native של המשחק:

    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. לאחר מכן, צריך לכלול את רכיב העזר של Java, שנדרש על ידי C++ SDK.

    כדי לעשות את זה, בקובץ build.gradle ברמת הפרויקט, מוסיפים את ספריית הפלט של ה-SDK כמאגר מקומי:

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

    בנוסף, בקובץ build.gradle ברמת המודול, צריך להצהיר על רכיב העזר כתלות:

    dependencies {
        implementation 'com.google.android.gms:play-services-auth:22.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 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();
    

    כשערך ה-Future שמוחזר על ידי SignIn() נפתר, אפשר לקבל את קוד האימות של השרת מהתוצאה:

    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 Games Services בפרטי כניסה ל-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

יש תוכניות עם פונקציות 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::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // `&my_program_context` is passed verbatim to OnCreateCallback().
  result.OnCompletion(OnCreateCallback, &my_program_context);
}
אם רוצים, פונקציית הקריאה החוזרת יכולה להיות גם למדה.
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 Games. החשבון החדש הזה נשמר כחלק מפרויקט Firebase, ואפשר להשתמש בו כדי לזהות משתמש בכל האפליקציות בפרויקט.

במשחק, אפשר לקבל את ה-UID של המשתמש ב-Firebase מהאובייקט firebase::auth::User:

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 ו-Cloud Storage, אפשר לקבל את מזהה המשתמש הייחודי של המשתמש המחובר מהמשתנה auth ולהשתמש בו כדי לקבוע לאילו נתונים המשתמש יכול לגשת.

כדי לקבל את פרטי השחקן של משתמש ב-Play Games או לגשת ל-Play Games Services, צריך להשתמש בממשקי ה-API שמסופקים על ידי Google Play Games Services C++ SDK.

כדי להוציא משתמש מהחשבון, מתקשרים למספר SignOut():

auth->SignOut();