Week 14: A crash-log auto-reporter, and save/load support for platform-graphs (a big performance boost!)

Two icons: on the left is an outline of construction scaffolding, on the right is an outline of some blocks and a movement trajectory between them.
New icons for my Scaffolder and Surfacer frameworks!

tl;dr: I created a crash-log auto-reporter with Godot+GCP, and started adding save/load support for the Surfacer platform-graphs (a big performance boost!).

What happened last week?

  • Highlights:
    • Created an automatic crash-reporter.
    • Renamed my "Godot Scaffold" framework to "Scaffolder" and struggled with the consequences in Git (more on that below).
    • Added a lot to READMEs and other documentation.
    • Started implementing save/load support for Surfacer platform-graphs.

  • Laundry list:
    • Spent some time installing an Android v6.0 emulator to try debugging a crash report.
      • :( No such luck. It works for me!
      • Although, the touch input events seem to drop a lot and retrigger annoyingly, so maybe that’s a separate issue I could fix for some folks?
    • It looks like my Google Ads iOS campaign somehow got throttled on March 19th, and hasn’t been spending any much money or getting many views since?
      • I upped the budget for it, in case that’ll help...
    • Implemented an automatic crash-log reporting system.
      • This should help me debug some issues that only repro on certain devices that I don’t have access to.
    • Refactored bootstrap and configuration flow to delay as much as possible until after the new CrashReporter has finished uploading (if there is something to upload).
    • Researched options for uploading and storing these docs on GCP.
    • Decided to go with plain GCS:
      • https://cloud.google.com/storage/docs/uploading-objects#rest-upload-objects
    • Enabled a GCP Billing Budget.
    • Created and tested a GCP PubSub-based Cloud Function which will disable my billing account when it goes over budget.
      • https://cloud.google.com/billing/docs/how-to/notify?authuser=1#cap_disable_billing_to_stop_usage
    • Created a new dedicated log-handling scaffold class.
    • Updated the scaffold framework’s README to describe more of its features.
    • Created data-agreement versioning, so that I can force the player to re-agree to terms if they ever change.
    • Lost about 4 hours to trying to fix issues relating to Git submodules.
      • Tried to configure them to only sync shallowly.
      • But that led to always having detached heads, which I can’t commit.
      • And then I tried and failed a dozen times to undo things to get back to a stable repo state.
    • Verified that CrashReporter works on Android.
    • Created framework icons.
The Scaffolder icon, showing construction scaffolding
An icon for the Scaffolder framework.

The Surfacer icon, showing a path between surfaces
An icon for the Surfacer framework.
    • Started implementing platform-graph save/load support.
      • The save/load support primarily involves translating game state to and from JSON strings, which isn't too complicated, but there are a lot of data structures to encode and decode.
      • This give a big performance boost when starting levels.
    • Created a TileMap wrapper for Surfacer, which makes it easier to ID and configure defaults.

An automatic crash-log reporter

  • At the start of the week, I got a report from one user that the app was crashing whenever they tried to load it.
    • I tried to diagnose it with an emulator, but I couldn't get the crash to happen.
    • I think I need the app to be able to upload error logs if I want to be able to diagnose errors like this in the future.
  • I decided to use Google Cloud Storage (GCS) to host these crash logs.
  • Godot can record all standard and error output to files.
  • My system then just reads the latest log file and checks for any errors near the end of the file.
  • If an error is found, the file is automatically uploaded to my GCS bucket.
  • The problem: Most crashes happen at the very start, while the app is still initializing, and before my crash-reporter might have a chance to run.
  • The answer: I refactored how things are configured and the initial sequence of events, in order to let this crash-reporter run as early as possible, and then delay everything else to wait until it's done.

git rekt

tl;dr: Git is slow, because it copies all of history, and submodules can be a huge pain to setup and maintain.

Ugggghhhhhh, version control can be such a headache sometimes.

At one point on Tuesday, I decided that I wanted to rename my scaffold repository from "godot_scaffold" to "scaffolder". And that's where the trouble began...

The problem with Git, is that it's designed around the idea that if you want to pull-down some code, you will need to not only pull-down the current code, but also the entire history of all changes that have ever been made to that code. So when you have a repository with thousands of files and thousands of commits in its history, it can take a long time to pull it all down the first time. Especially if you also have lots of media files in the repository, like images and sounds, which you need in a game project.

So I thought, there's got to be a better way. I did a tiny bit of research and learned that Git does have a handy "shallow" option for just pulling-down the latest snapshot. So I set that up for both my top-level repo, as well as for both of its submodules. But then I made some changes, and wanted to push my changes back up to GitHub. At that point, I learned that when you only have a shallow copy, you are in a "detached HEAD" state, which means that you can't push your changes back up (at least in the normal ways).

So then I re-looked-up the various ways of resetting Git history back to a previous version (which I always forget, since I don't do it very often). But there was some sort of conflict when I tried resetting the submodule state. So I banged my head against that problem without for a couple hours before getting things back to a stable state.

For now, I'm just going to stick with just pulling down the full history for all three repos whenever I want to make a fresh local clone.

And I don't have my submodule configuration setup to magically make things easy in fewer steps. After cloning the top-level repo, I have to run two other (very slow) commands to get the submodule state pulled-down.

Oh well. I'm sure there's a better way to set this up, but I'm not the deepest expert with all of the fiddly edge-cases that Git supports, and it's not particularly worth my time to experiment with them at the moment.

What's next?

  • Finish implementing save/load support for Surfacer platform-graphs.
  • Then take a brief break to learn about Unity!
    • I'm going to do another game jam at the end of the month, Ludum Dare 48.
    • I'll be doing this one with another group of friends, and we'll be using Unity and 3D tools.
    • So I need to learn to use Unity!
    • Unity is an industry-standard game engine (much more so than Godot), so it'll be really good for me to know more about it and how it compares to what I've been doing.
    • I'll go back to Surfacer fixes in Godot after the game jam.

🎉 Cheers!

This is a simple icon representing my sabbatical.