How to Add Firebase to Your C++ Desktop Game

Patrick Martin
Apr 13, 2020 · 8 min read

Many games have a core thing in common when you really dig under the surface: there’s almost always a soft nougaty core of C++. Firebase offers native feeling SDKs for Android, iOS, Unity, and the Web which makes it super easy to connect your game or application to its globe spanning infrastructure. Since idiomatic C++ can differ substantially from the patterns that make sense in other languages and frameworks, Firebase provides a bespoke C++ SDK to ensure that game developers get the same seamless experience one would expect from any other Firebase client SDK. This way you can focus your team on just being great game devs rather than becoming experts in the nuanced differences in each underlying platform.

The Firebase C++ SDK has five platform targets: Android, iOS, and desktop (Windows, Linux, and MacOS). The APIs exposed are identical through all five, but there are some platform specific nuances that need to be covered to successfully compile a game with Firebase and initialize the library. In this post, I’ll cover the Desktop.

One word of warning for the Desktop SDK: its primary purpose is to serve as a way to test your games without having to go through a full deployment cycle to a phone or tablet, which is particularly useful in game engines with custom build pipelines that can’t always take advantage of their target platforms’ incremental build pipelines or debugging tools. The C++ desktop SDKs are all open source, issues are actively fixed, and more work continues to be done on them. But because their intended purpose, some Firebase products are missing on the desktop and when you invoke missing products or APIs they no-op rather than crashing as you might expect. With all that said, if you find yourself wanting to port your mobile game onto the desktop or some other unsupported platform, it may not be impossible to keep most of your Firebase logic in place. If you do run into issues, you can still reach out to Firebase support.

C++

Cocos 2dx 4.0 uses CMake as its default build system, which happens to be the build system Firebase’s C++ SDK is built with. Everything I cover here should be easily adaptable to other build systems or game engines. If you’d like to follow along, now would be a great time to type cocos new:

cocos new CocosFirebase -p <com.your_company.your_game> -l cpp -d .

This basically says:

  • cocos new CocosFirebase: create a new project named CocosFirebase
  • -p <com.your_company.your_game>: the package name for your game, sans the <> I threw in.
  • -l cpp: the language is C++
  • -d .: create the game in my current working directory

If CMake is new to you, I highly recommend that you read my brief intro article. Now I need to download the latest version of the Firebase C++ SDK. I chose to place this in third_party/firebase_cpp_sdk for the purposes of this tutorial. Unless you’re planning on distributing your game on Linux, I would recommend starting with the binary Firebase SDK for now. I do explain the pros and cons of the binary and open source SDKs here.

The C++ Build System

Start by opening your project’s root CMakeLists.txt file.

First you need to add_subdirectory the directory where you installed the Firebase C++ SDK, this is third_party/firebase_cpp_sdk in my project:

This will make CMake aware of the various Firebase targets.

Next you need to locate the executable or library that will depend on Firebase in your CMake project. By default, Cocos places this in the ${APP_NAME} variable. If you’re not using Cocos, remember that on the Desktop this is usually an executable (via add_executable) although it can be a library as well.

Once you know the target name of your game, you need to look up the required Firebase targets for the Firebase products you wish to use. Normally I’d set up Google Analytics for Firebase, but that product is not yet supported on the Desktop. Instead, I’ll use Firebase Authentication as it’s also a really easy integration and is something that would typically be nontrivial for a desktop C++ game. So if you look at that target entry, you’ll see that you need firebase_auth and firebase_app.

With this information on hand, update your game’s target_link_libraries with the Firebase dependencies you need:

The Firebase Configuration

Now you’ll need to tell your game how to find your Firebase project. Since Desktop support is currently beta, there is not a unique config file like google-services.json on Android or GoogleService-Info.plist on iOS. Instead you’ll reuse one of your existing configuration files. Functionally it doesn’t matter which you use as long as they’re part of the same project, but practically google-services.json is a little less effort to include.

If you plan to use google-services.json, copy it into the working directory for your game. For a Cocos project this means copying google-services.json into the root of the Resources/ directory. Note that you must keep a copy of this in proj.android/app as well for Android to work and you’ll have to update both files if you re-download it.

If you only have a GoogleService-Info.plist file for an iOS app, you will have to use generate_xml_from_google_services_json.py included with the Firebase C++ SDK to convert this into google-services-desktop.json. Just like Android’s process, the generated file should be placed at the root of your runtime working directory for your game (Resources/ for a Cocos game). The specific command I used when writing this post was:

Note that if you’re on Windows and don’t want to install Python as part of your build pipeline, you can use generate_xml_from_google_services_json.exe. This simply wraps the Python script in an executable, although there are some compatibility issues with Windows 7 and 8.

You can turn this into a CMake step using add_custom_target by writing:

You could also make this part of your CMake configuration step, but you’d have to remember to configure CMake every time you re-downloaded GoogleService-Info.plist.

Finally, you need to pull in some platform specific APIs to get this all working. If you look here, you can see that Authentication on Windows needs advapi32, ws2_32, and crypt32, whereas you need pthread, CoreFoundation, Foundation, Security, GSS, and Kerberos on MacOS. I don’t have a Windows machine to test the exact process with, but on MacOS I can to add:

Testing

If you’ve read the other variants of this article, you know that I’d typically test by writing:

And then checking my Firebase Console for a new user to appear. But Google Analytics is one of the services that doesn’t actually do anything in a desktop environment (that call would be a no-op.). Since I can’t use Analytics to test, I’ll anonymously authenticate a user!

I have to start by clicking on Authentication in my Firebase Console. Then clicking “Set up sign-in method”:

Near the bottom I see “Anonymous”. I expand that box and click “Enable”:

If you’re following along at home, open up Cocos generated AppDelegate.h and add:

Note that you’ll need the following includes:

Then in AppDelegate.cpp, I can add:

to applicationDidFinishLaunching(). If you’re not using Cocos, just make sure you can ensure App::Create() runs before any other Firebase calls. applicationDidFinishLaunching() is more or less equivalent to your main() function.

So first I create a unique_ptr to the firebase::App that I created with firebase::App::Create().

Then I use that to initialize Firebase Authentication with firebase::auth::Auth::GetAuth(_pFirebaseApp.get()), wrapping that in a unique_ptr as well to simplify memory management. If you’re not accustomed to unique_ptr, .get() will retrieve a standard pointer to whatever is in the unique_ptr (as in firebase::auth::Auth*).

Before you sign in, you should always check if current_user() is null. If you don’t do this with an anonymous account, you’ll simply keep creating new anonymous users. Of course, if you want to test a new login, you’ll want to comment out this check or implement a sign-out.

To sign in anonymously, I call _pFirebaseAuth->SignInAnonymously(). This returns a Firebase specific class called Future, which imitates the system futures and promises as you may expect from other languages. You can run code when this future completes with a lambda expression: .OnCompletion([](const ::firebase::Future<::firebase::auth::User*> &result){.

I pull out the user with ::firebase::auth::User* user = *result.result();, and print the user’s User Id.

The result of running this application should be something like: Current user: qb1c5BnjJrW791BSWEH524TEUIO2. I can also check for my new user in the firebase console:

What next?

Once you’ve authenticated, you can now safely access Cloud Storage, Firestore, or Realtime Database. Use Cloud Functions to not only invoke serverless functions, but to do so whilst also securely transmitting your authentication data via Callable Functions in a manner that’s much easier than issuing raw web requests via typical C++ frameworks.

I can’t wait to see what you build!

Resources

Firebase Developers

Tutorials, deep-dives, and random musings from Firebase…

Firebase Developers

Tutorials, deep-dives, and random musings from Firebase developers all around the world. Views expressed are those of the authors and don’t necessarily reflect those of Firebase or its parent companies.

Patrick Martin

Written by

I’ve been a software engineer on everything from games to connected toys. I’m now a developer advocate for Google’s Firebase.

Firebase Developers

Tutorials, deep-dives, and random musings from Firebase developers all around the world. Views expressed are those of the authors and don’t necessarily reflect those of Firebase or its parent companies.