Week 2 — C#, Systems and the Coding Masterclass · w2d5 · 3h 04m

W2D5: Microservices: how games talk to backends

Wiring a Unity game to Firebase — email auth, push notifications and a realtime database, plus the Android build setup needed to test it on a real phone.

Lucian Lazar · 20 Jul 2024

Last session of week two, and it’s the one where the game stops being a closed box on your machine. Lucian walks through what a microservice actually is in game terms, then spends most of the three hours proving it with Firebase: registration and login, push notifications delivered to a physical Android phone, and a realtime database storing per-user data. There’s theory at the front, but the bulk is hands-on and occasionally goes wrong on camera, which is arguably the useful part.

What’s covered

The concept, without the buzzword fog

  • Microservices as small backend apps, each specialised for one job: chat, leaderboards, matchmaking, inventory, auth, purchases.
  • Third-party vs internal. PlayFab, Amazon GameLift, Firebase on one side; your own server with your own uncensored LLM or your own leaderboard on the other.
  • Why you should start with a monolith. Splitting your backend into separate services running in different regions is a problem you get after you have a team and traffic, not before.
  • Idle games as the clearest case: the logic runs on the server, the client is a window onto it, and a push notification tells the player their crops are done.
  • Hosting options, with a dry aside that Google Cloud is unnecessarily complicated at times.

Firebase setup done the lean way

  • Creating a project, disabling analytics, enabling the email/password sign-in provider.
  • Downloading only the .tgz packages you need — external dependency manager, Firebase App (core), Auth, Messaging, Realtime Database — into a GooglePackages folder next to the project.
  • Wiring them up by hand in Packages/manifest.json with relative file:../ paths, instead of importing the giant Unity SDK. Lucian is firm about this: import what you need, know what you have, avoid the headaches.
  • google-services.json, bundle identifiers, and why you decide the bundle ID in Unity first and then match it in Firebase.

Auth, messaging, database

  • A canvas with two TMP input fields and two buttons, with click listeners added in code rather than dragged in the inspector — so the logic lives in one place and fields stay private.
  • Push notifications: getting the FCM token, subscribing to TokenReceived and MessageReceived, and unsubscribing in OnDestroy because the messaging object outlives the scene.
  • A detour into C# events, +=, lambdas and event parameters, since the messaging API forces you to use them.
  • Realtime database with an assembly definition file, because Firebase ships DLLs that aren’t auto-referenced.

Android build reality

  • Developer options, USB debugging, IL2CPP, target API 33, custom Gradle templates, force resolve.
  • Faster builds vs faster runtime, and why you flip that switch back before you ship.

Timestamps

  • 00:04:30 — Switching to the day-five branch in the repo, and where the branch switcher lives in Rider vs VS Code
  • 00:08:00 — Week two recap and an honest note that this session is theory-heavy by design
  • 00:11:00 — What a microservice actually is: mobile, web and desktop clients hitting the same backend
  • 00:17:00 — Third-party services vs your own: PlayFab, GameLift, Firebase, and rolling your own LLM endpoint
  • 00:24:00 — Monolith first. Splitting into separate services comes much later, when the project outgrows you
  • 00:29:00 — Push notifications explained through the idle-farming-game example
  • 00:36:00 — Creating the Firebase project, disabling analytics, enabling email/password sign-in
  • 00:44:00 — Downloading the .tgz packages and editing manifest.json by hand instead of importing the full SDK
  • 00:58:00google-services.json, bundle identifier, and matching it in player settings
  • 01:08:00 — Building the login canvas, then the auth script: FirebaseAuth.DefaultInstance and ContinueWithOnMainThread
  • 01:22:00 — Live register and login, including a too-short password and a deliberately wrong one
  • 01:29:00 — Scripting define symbols and #if blocks stripping code out of the build
  • 01:33:00 — Viewing users in the console, and why passwords are hashed (with Lucian admitting he stored plaintext two years ago)
  • 01:45:00 — Cloud Messaging package, getting the FCM token, and a full aside on C# events and lambdas
  • 02:05:00 — Android build setup: developer mode, USB debugging, IL2CPP, target API 33, Gradle templates
  • 02:20:00 — Build and run to the phone, building the wrong scene, and the much faster second build
  • 02:32:00 — Sending a campaign notification from the console, then a test send to a single device token
  • 02:50:00 — Realtime Database: test mode, the DLL problem, and creating an asmdef with the right references
  • 03:02:00 — Toggles, anchors, prefabbing the auth UI, and the submit script writing a dictionary to the database

What you build

By the end you have a Unity project talking to three Firebase services. A login scene where users register and sign in with email and password, with errors surfaced and successful users appearing in the Firebase console. A notifications scene that pulls the device token, displays it in a copyable input field, and handles messages that arrive while the app is in the foreground — tested for real by sending a campaign from the console to a Pixel over USB. And a data scene where three toggles representing collected items get serialised into a dictionary and written under users/{userId} in the realtime database, visible updating live in the console.

The auth handler is short:

private void HandleLogin()
{
    auth.SignInWithEmailAndPasswordAsync(emailInput.text, passwordInput.text)
        .ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                Debug.LogError($"Login failed: {task.Exception}");
                return;
            }

            if (task.IsCompleted)
                Debug.Log($"Login successful, user is {task.Result.User.Email}");
        });
}
And the database write, built from whichever toggles are on:

```csharp
var userData = new Dictionary<string, object>
{
    ["name"] = "player",
    ["lastUpdate"] = DateTime.UtcNow.ToString("o"),
    ["items"] = enabledToggles.ConvertAll(t => t.GetComponentInChildren<TMP_Text>().text)
};

db.GetReference("users").Child(userId)
  .UpdateChildrenAsync(userData)
  .ContinueWithOnMainThread(OnSaveResult);
Small habit worth stealing from that last one: the submit button gets disabled the moment it's clicked and re-enabled in the callback. It stops double submits and gives the player immediate feedback. Lucian reckons 90% of apps and sites skip it.

## Who should watch this

If you've only ever made single-player games that save to `PlayerPrefs`, this is where the other half of the job appears. It's aimed at people who want a login, a leaderboard or notifications in a real shipped game and don't want to wade through outdated tutorials to get there. Absolute beginners can watch it for the concepts and come back for the setup later — the value is in seeing the whole chain, from a tarball in a folder to a notification landing on a phone.

Part of the full curriculum. Course overview.