Alpha 17 postponed - For good reason

Alex Anderson 🚀Alex Anderson 🚀, February 7, 2025

Hi Friends. Happy Thorium Thursday,

I've been slowly working on a lot of tricky things - better ship AI and improving the app package. Let's talk about recent progress.

tl;dr: No more alphas for the foreseeable future due to third-party dependencies missing critical features. You can still access the source code and build it on your own computer if you want to play around with it.

Music

Before we dive in, I want to give a shout-out to Ryan who dropped not one, but two music sketches for Thorium Nova. I'm a huge fan of his work and am incredibly grateful for his willingness to volunteer his time and talents to make Thorium Nova sound amazing. If you want to hear the tracks, join the Discord server and check out the songs he uploaded.

Ship AI

I've made a list of a bunch of things that need to be done for good ship AI. So far, I've completed:

  • Autopilot improvements, including better speed picking
  • Path planner and following, for avoiding obstacles
  • Determining if an object is a friend or enemy

What's left?

  • Choosing an action to perform - engage, hold, retreat, etc. - based on nearby ships and orders.
  • Target finding, by assigning a score to every nearby object based on distance and how much of an enemy the target is.
  • AI controls for positioning and rotating the ship to put its target in weapons range.
  • Choosing, arming, and firing weapons to use against a target.

Once all of that is done, I'll start testing dogfights and tweaking the parameters to make it fun to fight other ships.

I'm also in the process of adding more ships to Thorium Nova to better represent different factions and to see how different engines and weapons settings behave.

App Packaging

I've been unsatisfied with the state of Thorium Classic and Thorium Nova's app package. It's big, bulky, and complicated. I wanted to improve it for Thorium Nova, and while my experiments were successful, the underlying tools I'm using are missing some features that make it impossible for me to distribute the app package like I have for previous alphas. However, I don't want to leave this work unfinished.

So, to maintain momentum and not let this halt all progress, I've decided to temporarily stop releasing alphas - just until the tools I'm using are able to support the features I need. I don't know when that will be. Hopefully soon.

Let me explain the benefits of this new approach, and then we can get into the technical details and why that prevents me from releasing alphas.

What's good

Here's how my efforts have paid off

  • Half the app size
  • Half the startup time
  • More stable - less weird bugs
  • Built-in headless server releases - so you can easily run Thorium Nova's server on any server computer you want, even in the cloud, without needing to download or install anything extra.
  • More control over when the server starts and stops, which makes kiosk mode and connecting to remote servers easier.

Not much else has changed. You can use the Kiosk app, or connect via a separate browser

Read on for technical details about why this was necessary and how it works now.

Technical Explanation

The old setup bundled the Thorium Nova Node.js server with a Chrome-based web browser using a tool called Electron, which is also used by apps like Slack, Spotify, and Discord. This has a number of downsides:

  • Bundling a web browser is a little redundant and makes the app bundle really big.
  • It also uses a lot of extra system resources.
  • The Node.js server and Chrome browser versions are tied to the Electron version, so I can't upgrade them independently.

I've switched from using Electron to Tauri. Instead of bundling Chrome, Tauri uses your operating system's default browser - Safari on Mac, Edge on Windows, and who knows on desktop Linux. This is the biggest downside of this approach - no more consistent browser for the kiosk.

However, I think it's an acceptable tradeoff. The intention was always for the primary way for people to play was by firing up their favorite web browser and connecting to the Thorium Nova server. I was already expected that people would use non-Chrome browsers. And a major goal for this project is to make the game play really well on iPads, which only run the Safari web browser engine. This means a bit more work for me to make sure there aren't major compatibility issues, but ultimately it means a better experience for everyone.

Anyway, for the non-browser stuff like messing with system resources and starting servers, Tauri lets you write Rust code.

I really didn't want to rewrite the entire backend in Rust, so I opted to bundle the server into its own executable and embed that into the Tauri app. Fun fact - this is similar to what I did with the pre-Electron version of Thorium Classic.

However, at the same time, I decided to switch from using Node.js to using Bun - a new JavaScript runtime that's incredibly fast while using much less memory than Node.js. Bun makes it really easy to compile an app as a single-file executable - perfect for embedding in Tauri. This is also what allows me to provide headless executables for folks who want to run the server without the kiosk. Between Bun and Tauri, it should be more feasible to run Thorium Nova on lower-powered servers.

That said, building the Bun binary and setting it up as an embedded sidecar in Tauri proved to be a bit of a challenge, but one that I've almost solved.

Challenge 1 - Starting the Bun Server

Confession - I've never written any Rust, and yet here I am trying to build an app with a Rust backend. Fortunately, with help from the Tauri docs, some Stack Overflow answers, and a helpful LLM, I was able to piece together a way to get the Bun app started. The main trick is creating a state object, tying that to the Tauri app instance, and putting some methods in that state for starting and stopping the server.

When the Tauri app starts up, it calls the command to start the Bun server automatically. (In the future, I might make it so it remembers the last server it was connected to and attempts to connect to that server before starting its own.) It reads the stdout stream from the Bun server, looking for Server running on... to know that the HTTP server has started and what the server address is. It then sends the window to that address, and ta-da! You're now looking at Thorium Nova's UI.

It also includes commands for stopping and restarting the server which can be called from the frontend for when the user connects their kiosk to another server - don't need to run your own server if you're connecting to someone else's.

Challenge 2 - Bundling Static Assets

The Thorium Nova server needs to both serve the API endpoints which provide the data about the simulation as well as the frontend JavaScript and CSS code sent to browsers. Bun claims to be able to embed static assets into self-contained executables, but I wasn't able to get it to work. I could bundle individual files, like the Default Plugins archive which is extracted when you first run Thorium Nova. But a whole bunch of files didn't seem possible.

So I decided to turn the whole bunch of files into a single file in the most unhinged way possible - I took all of the assets used in the frontend code and smooshed them together into a single binary file.

It starts with a single byte which says how long the file's name is - so we're limited to 255 character long file names, which seems like it'll be enough. The file's name is next. Then 4 bytes for the length of the file, which maxes out files at 4GB - more than enough for our purposes. And then the bytes of the file. Repeat that for every file that needs to be served by the HTTP server.

What's great about this approach is that it doesn't have to read the entire file - it reads the file name and if it doesn't match, it skips ahead past that file to read the next file name until it matches. Once it does, it just streams down the bytes for that file without touching any of the rest of the file. Great for conserving memory.

When I tested it, the files returned almost instantly. You can feel it when you load the app or change screens - the JavaScript, images, and styles download very quickly, making for a snappy experience.

Challenge 3 - Signing the Bun server

For apps to run on macOS, they need to be signed and notarized by Apple. It's possible to get around this, but I don't expect casual users to know how to do this. Fortunately, Tauri provides tools for signing apps with very little effort or ceremony. In fact, I intend to sign Windows apps in the future, but Windows isn't quite as restrictive about what apps you run as macOS is.

There's just one problem - it's not just the Tauri app that needs to be signed, it's also the Bun server. It's a known issue related to the way Bun formats its executables, and unfortunately it's not something that I believe I could fix on my own. I need to wait for Bun to fix this on their end before I can provide an alpha release that works on macOS.

I've also been struggling with getting Windows to build - apparently it doesn't like it when the version includes text in it, like the 1.0.0-alpha.16 versions we've been using with Thorium Nova.

And I'm having a hard time getting the headless server executables to upload.

In other words, it's a whole mess and I haven't gotten it sorted out. Like I said at the beginning, I'm not going to let all of this stop progress on the game. Because of that, I'm putting this work to the side, unfinished, while I continue working on other parts of the game. At a later date, probably when Bun has proper support for signing executables, I'll come back to this and make it all work nicely.


Hopefully this isn't too disappointing for anybody. The instructions for starting Thorium Nova in development mode have been updated, the only major change being using Bun instead of Node.js and npm. So it is still possible for the inquisitive to play around with the controls between alphas.

I'm looking forward to sharing more progress as it is made. Until then, take care.