From the Burrow

TaleSpire Dev Log 334

2022-03-31 02:13:36 +0000

Hey folks, I’m quite frazzled today so no proper update of what’s going on. I can say though, that it’s all going well, and we are still expecting the beta launch to happen on the same date that we still aren’t telling you :P

In lieu of text content, have some video content!

This is a speedy video showing the process of connecting a HeroForge account, linking some creatures to the campaign, and showing how things respond to being unlinked and relinked.

We are still working on bugs[0], and the UI is still changing. However, we are excited to make the experience as smooth as possible. So that this could be done effortlessly, even in the heat of battle.

Please note that you do not need a HeroForge account to play with miniatures created by other players, even if you are the GM. An account is only required for sharing your creations with a campaign.

Alright, I’m heading out.

Have a good one!

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] that ugly lag when opening the HeroForge menu is already fixed, for example.

TaleSpire Dev Log 333

2022-03-24 15:59:14 +0000

So while we wait to announce the release date, let’s talk about some other details.

The road to HeroForge release

The release itself is going to be a two-parter. First, there will be a short open beta, and then the more public “launch.”

Now, this is a bit unusual for us, so why are we doing it?

In short, HeroForge has a much larger community than ours[0], and when they publicize a new feature, a lot of folks will rock up to try it out. By having a short beta before pushing announcements everywhere, we all collectively get to kick the tires and find any scary bugs that snuck in before any bigger rush of people.

There are no restrictions to participate in the beta beyond having a HeroForge account[1]. The doors will be open, and you are all welcome in, but we won’t be doing a big PR push immediately, and the word “beta” will be slapped on a bunch of places.

Once both teams are happy, we will remove the “beta” label and then fire up the social medias, blast out announcements, and see how it goes!

The fact there is no restriction will confuse a few people, but after having you wait for so long, we couldn’t stand the idea of only letting some of you in for the beta. This feels like a decent trade-off.

Coming soon

Already we are turning our eyes to what we will be working on next. The art team will be busy with the cyber-punk and scifi pack, so it’s definitely a good time to ship some features.

This is ideal as we have a ton of stuff that feels 80% done and just needs that work to get it out the door.

Even if we marked it experimental and required turning it on in the settings, it would be good to get this stuff out.

One example of this is the change to rulers I’ve been calling “tactical rulers” internally. That change means a ruler isn’t cleared the instant you click on something else. This makes moving creatures (especially troops) pre-measured distances much easier. It is also a requirement for us to be able to start on area-of-effect markers (but that’s a different story).

Another thing that marries well with tactical rulers is group movement. Ree has already put a lot of work into that, and getting the last 20% feels doable.[2]

I feel like I’m tempting fate if I start teasing the other features in that “80%” category, so I’ll leave it for now, but I’m feeling very optimistic about what comes next.

That’s all for today

So yeah, that’s the lot for this log.

More news in the next one.

Have a good day!

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] But hey, no trades, our community rules!

[1] Of course, you or one of your party will need to have bought some digital minis to be able to bring them into your campaign.

[2] We will probably limit the number of uniques that can be added to a group initially. This is due to the amount of data that must be sent to the backend on each movement. I know what changes I need to make on the backend to make this more efficient, but that will take some more time. Non-unique creatures are handled totally different and so won’t have that restriction.

TaleSpire Dev Log 332

2022-03-23 10:13:35 +0000

Hey folks!

Things are going great over here. Here’s the latest from me.

Server stuff

Last week was given almost entirely to server work, continuing the work from the previous log. I was able to get our Packer scripts up to the point where it completely replaces how we were building our old docker containers and Amazon AMIs. They’re also way easier to maintain.


We had a planning meeting with the HeroForge team last Friday, and we now have our internal timeline, which is super exciting for us. I say ‘for us’ because we aren’t telling you the date yet so as not to put extra pressure on both our teams.

While that date is ‘soon,’ I’ve probably said ‘soon’ in so many different ways already that it doesn’t really convey how soon we are talking, but you’ll see pretty quickly from what I’m up to how close things are.

Speaking of which!

I am currently working through my last coding tasks marked mandatory for release. All of these are related to handling failure cases and how to expose the issues to the user.

Then I’m onto the tasks marked as things we really should have. This currently includes:

  • Adding a panel for managing errors (which will be essential for modding)
  • Changing where the HeroForge thumbnail data is stored

Ree has been working on the UI, and as soon as that lands, I’ll merge my stuff and start recording videos of the entire flow of importing minis. It’s a very short process, but we need tutorial content for TaleSpire and HeroForge’s sites.

That takes us to the rest of the work leading up to release. We need to:

  • Produce various texts, videos, and images for documentation
  • Set up a new Steam branch for testers
  • Chase things up with lawyers (for the wording of the ToS around HeroForge)
  • Work out our point of contact for HF if they need support
  • Write up our PR stuff for the release
  • More testing

Writing stuff for humans always takes way longer than I think, so I’m aiming to have as much of the coding done by the middle of next week as possible.

Aaaaaaaa it’s exciting!

We have also been preparing for the feature work we will be doing immediately after this ships. I think we’ll be able to have some cool features landing before sci-fi ships :)

Alright, enough vague info for one day! I’ll be back soon with as many extra details as I can share.


Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

TaleSpire Dev Log 331

2022-03-17 10:10:30 +0000

Hi folks,

Let’s start with HeroForge news. We’ve been working closely, iterating, and finding shortcomings in the format for some time now. As of yesterday, we have finalized the format and conversion process and are looking to the next milestone. We obviously wouldn’t announce any dates for a potential release without HeroForge’s involvement, as there are naturally a lot of overlapping concerns. Not just technically, but from the PR side too. So today, all I’ll say is: Things are looking good!

I’ve also continued work on the backend. I have been working heavily using terraform to get all our backend infrastructure defined as code. In the future, we want to be able to spin up services[0] in whole new regions trivially, and this should let us do that.

Currently, I’m experimenting with an unfinished setup in the EU. This never went live but is a reasonably clean setup and makes for a good testbed.

Please note: I’m not experimenting with live infrastructure. None of this is currently used by TaleSpire, and so any bugs you hit are not related to this :)

As part of this process, I’m working with another tool from HashiCorp, packer. Packer is letting me define our docker containers and AMIs from common scripts.

Previously we have run docker containers on our EC2 servers. However, the separation from the host has caused complications in the past, and, personally, I prefer just using docker to make development cleaner. My goal with packer is to make our AMIs and containers as close to functionally identical as is reasonable. This will remove a small overhead[1], simplify some edge cases, and make building AMIs waaaay easier[2].

Today I’ll continue with that backend work.

Seeya folks!

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] Using this term generally, we don’t use a microservice architecture [1] Which won’t matter now, but that I believe certainly would if we start taking on more real-time traffic [2] I could probably argue that it will make the dev environment closer to the running environment, but that’s another ramble.

TaleSpire Dev Log 330

2022-03-15 22:11:30 +0000

Hi folks,

After the latest Photon issue, I decided to look into our options for our real-time traffic going forward. We don’t have any immediate plans to change our setup, but it was bothering me that I didn’t have a good overview.

That took all of Monday and half of today, and it led neatly into reading about tooling for helping the portion of the backend we host.

It has all been research, so I don’t have anything specific to talk about yet, but you’ll have a blog post about it as soon as that changes!

Hope you are doing well, folks Chris

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

p.s: Hats off to the art team for the latest patch!

TaleSpire Dev Log 329

2022-03-09 16:46:38 +0000

Yesterday the code gods conspired against us, so we didn’t get to start on the latest iteration of the HeroForge mini specification.

Instead, I focused on packages.

We use a bunch of experimental packages, and naturally, these have bugs from time to time. Yesterday we had such a case with Unity.Entities. The package had already been fixed in the latest update, but that fix was incompatible with the latest version of another package with its own bug (and so on it goes).

This led to me shuffling around packages versions until I got a setup that will work for now.

The amusing part is that we will be removing the Entities package soon anyway. We currently have it because:

  1. Unity.Physics depends on it
  2. We use a couple of serialization tools that happen to live in that package.

Neither of these reasons will be valid for long, however. We are forking Unity.Physics to optimize it for TaleSpire’s specific use case. As we don’t use Unity’s ECS, we don’t need the ECS integration in the physics engine.[0]

As for Serialization, I have already started working on internal tooling for defining data formats and convertors between them. This will help in a few ways:

  • It will reduce the amount of hard to maintain serialization code.
  • It will make writing & upgrading binary formats more approachable to more of the team.
  • It should make it easier to iterate on the formats used in mods without worrying about breaking what is already out there.

I only need the basics of this to allow us to drop Unity.Entities. That, in turn, will let us to pull the latest version of the conflicting packages and put us in a great place to improve the performance of physics.

And that, for today, is that!

Seeya you around.

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] In fact, I did this today! It’s sitting on a branch, ready to merge

TaleSpire Dev Log 328

2022-03-07 18:29:39 +0000


Mac support has been going surprisingly well. Let’s natter about it!


The first order of business was pixel-picking. This requires custom native code as we need to use features of the graphics API not exposed by Unity. I had written the DirectX version over a year ago, but this time I needed something for Metal. I haven’t written any Metal code before, but I found this article and this code sample and felt pretty confident I could bodge something useful.

This turned out to be correct. I am very lucky that, at my previous job, I suffered through so much ObjectiveC development as it made the process much easier. After a bit of experimenting, the solution turned out to match the form of the DirectX one very closely, so the API for our plugin could remain the same.

It was fantastic to see picking come to life!

picking on mac

As you’ll have just seen, the cursors are HUGE! It turns out that Unity will try to match the cursor size as close to the source image as possible, so I need to go adjust the import settings.

Next up, I set myself to fixing a mistake in the build scripts[0], and then I needed to look at some more platform-specific code.

In Unity, if the game is in fullscreen mode, the reported resolution of the display is that of the fullscreen game. This is handy in some cases, but not for us. We want the full pixel resolution of the monitor that the game is currently on (according to the OS). This is easy to get from the Cocoa API, and so this was another job for some native code.

With the pixel-picker under my belt, it was extremely simple to get this written.

Another thing that turned out to be super easy was supporting the talespire:// URLs. Unity has a simple setting, allowing you to set up custom URL schemes. It was so easy that there is nothing else to say about it. So here’s a picture instead:

custom url support


There are plenty! The big one is that lots of shaders are broken. As an example, here is water:

refreshing glitches

The fps is also bizarrely low. Below I have a screenshot of the profiler. You can see that the work on the main thread is done in under 6ms, and the render thread is done by ~9ms. VSync is enabled, so I expect it to wait until 16ms before starting the next frame; however, it is waiting until ~25ms. Clearly, something is wrong or is missing from the graph.

This could be anything from a two-minute fix to a two-month one. We’ll have to see!


What’s next?

Clearly, there is still plenty to do before mac support can ship, but tomorrow I need to switch back to HeroForge and modding. I am also no shader expert, so I’ll definitely have to kick this to Ree so he can work out what is breaking there.

I’m delighted with the 10Gb network gear we purchased to link my dev PC and the Mac. It has made cross-platform development so much nicer than what we have had in the past and has helped Mac support progress smoothly.

So that’s it. I left out a bunch of smaller things for the sake of time, but I hope you’re as excited as I am for where this is going.

Have a good one folks!

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] If BuildPlayerOptions.locationPathName does not end with .app, the dll containing the compiled burst jobs is not being copied to the plugins folder.

TaleSpire Dev Log 327

2022-03-04 23:15:41 +0000

‘Allo folks.

Yesterday seemed to want to stop me from working on Mac support, and it won. Today I turned the tables.

running on m1

Before we get into that, let’s skim through some tasks from earlier in the day.

The first order of business was handling the renewable of some server certificates. This led neatly into one of the problems from yesterday, certificate authentication callbacks failing on the latest Unity version.

It turned out that, while the HttpClientHandler.ServerCertificateCustomValidationCallback callback is now broken, the global ServicePointManager.ServerCertificateValidationCallback one still functions.

I hate this global event as it is fragile to modification by other code[0] and is called for all requests when we only need it for specific ones. We only need to check requests to the backend against our pinned certificate, so I have to filter those cases in the callback. However, at least we can continue work.

I then updated our build scripts, so we could perform mac builds. As I previously had issues building directly to the network share, I now build to a temp directory and copy the files after completion.

Next was updating platform-specific code:

  • Reliably obtaining the monitor size on Windows required some calls to the win32 API. For Mac, I’m starting by assuming the Unity commands will suffice and will write a native alternative if needed
  • I updated the dylib for miniz to one that supports M1
  • Updated a couple of shaders #ifdefs to include them for Metal
  • Disabling the pixel-picker on mac
  • A whole slew of code house-keeping stuff

That was enough to get TaleSpire to start, login, and load a board. This felt huge as the last time I tried this, I couldn’t make a standalone build, steam’s API didn’t support M1 mac, and I had run a hacker server locally just to get something running.

With all of this, I am finally ready to start coding the thing I tried to start midday yesterday :D

Well… almost ready. I just need to download XCode, and I sure do hate me some XCode. That’s just the price of Mac support, I guess.

Anyhoo, that’s enough for today.

Hope you have a great weekend. Ciao.

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] because, as the docs say, “Despite being a multicast delegate, only the value returned from the last-executed event handler is considered authoritative”

TaleSpire Dev Log 326

2022-03-03 23:35:01 +0000

Hey everyone.

Today has not wanted me to make progress!

I have a few working days before the new HeroForge asset changes are available, so I thought I’d look at other upcoming features. So I:

  • reviewed the roadmap
  • reread the server chat code
  • got an overview of rulers
  • came up with more emote ideas
  • made some more notes for some potential gm tool for previewing what you players can see

But these are just notes. I wanted to dig into something. I did some reading around Metal (Apple’s graphics API), specifically how to read back data from textures. It looked fun, so I thought I’d dig into Mac support.

So I:

  • updated the Unity version
  • worked around a new bug
  • pulled the latest version of Steamworks.NET

…and then promptly ran into a bug where https requests weren’t working. It’s looking like HttpClientHandler.ServerCertificateCustomValidationCallback is not working, and so certificate pinning is broken.

This is maddening, and I’ve spent a good few hours poking around code to see if another code path would work. So far, it seems not.

This means I should probably roll back to earlier Unity builds to see if they work. However, while this was going wrong, I found some server-side work requiring prompt attention. So it looks like that’s where my time is going next.

Yeah, so it was a bit of an annoying day. I was pumped to make stuff, and I ran into walls instead.

No worries, though. That’s just how it goes some days.

Have a good one.

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

TaleSpire Dev Log 325

2022-02-28 19:53:17 +0000

Hey folks,

How the hell has it been a week since the last one of these?! Let’s remedy that.


The AssetDb is a chunk of code that handles the loading of assets and indexes. It keeps track of metadata such as tags and groups and provides hooks for other systems.

Up until now, we haven’t needed to unload packages at runtime. With modding, this all changes, so I’ve been adding that functionality.

There were a few false starts and dead-ends as I tried to find a balance between simplicity and speed. I’m still trying to learn the skill of choosing the approach that is just dumb enough to work while still being amenable to development over time. Getting stuff working faster and iterating is so valuable.

Asset code obviously touches many things in TaleSpire, so you find yourself in bits of the codebase that are quite old (like the inventory bar at the bottom). Once again, we don’t want to spend too much effort on these things as we already have proper improvements planned for the future. So I find myself holding off any smart fix that would be too invasive.

All that rambling aside, it’s going well. Unloading is working great. Next up will be testing and fixing systems that don’t know how to react to assets becoming unavailable dynamically.

Fix tesla-coil & blue-fire

We have had an intermittent bug with tesla-coils occasionally not showing their lightning for a long time now. It’s always been tricky to track down as I didn’t have a board that could reliably reproduce the issue. Every time I would test, the damn thing would work, so the ticket would keep falling further back in the bug list.

However, last week the aberration pack shipped, and we had reports of the blue fire asset not working for some too. Ree quickly managed to track down one issue[0], but the fire behaved suspiciously like the tesla-coils even with that patched.

Unlike previous efforts, this time, we got lucky. One of our moderators had a board where they could semi-reliably get the tesla-coil bug. She published it, passed me the link, and we were off!

I was able to find out the following:

  • If I imported the board and then placed a coil, it would fail
  • If I reloaded the board, the previously placed coil and all new ones would work

This is precisely the kind of place I love to start, and I quickly tried to delete parts of the board until I could get the smallest number of tiles that triggered the issue. This can yield a minimal test case, but this time it was very stubborn. If I removed any significant chunk of the board, the tesla coils just started working again.

That in itself is data, of course, so I changed tactic and started stepping through the batching code in the debugger.

I should take a tiny diversion to explain that the commonality between the coils and fire is that they are both ‘multi-mesh’ animations. To get the stop-motion-like effect of the flames, the spaghet script that runs the fire is swapping out the active mesh over time[1]. I quickly confirmed that the multiple meshes existed but that the batching code didn’t think any of them was current due to an incorrect index.

It’s a weird one to explain, but in short, the index should have been an int, but instead, it was a byte.

The index was into our global collection of meshes[2], and this meant that if the index was <256, the mesh would work. If not, it wouldn’t.

It worked when loading a board that already has the tesla-coil as (purely by chance) it was one of the first 255 meshes added to the global collection.

It didn’t work when importing the mod’s board and THEN adding the tesla-coil because that board had a lot of stuff loaded already, and so the index of the tesla-coil’s mesh ended up being >255

This also explains why I couldn’t reduce the board easily. It wasn’t the number of assets in the board that mattered, but the variety. When I tried to trim the board to make it smaller, I was removing just enough kinds of assets that the tesla mesh would end up with a valid index again.

Looking at our git history, I think I was going to use a local mesh index at first (and so chose byte), but partway through, I switched to indexing the global pool, evidently without updating the type from byte to int.

So! Now to ship it. I’ve checked out the TaleSpire code from before all the HeroForge work, and I have cherrypicked the fix commits over. I hope to ship these fixes tomorrow. It’s tempting to rush it out tonight, but I’d rather have an extra pair of eyes on it first.


Until now, we tried to include the Steam build-id in the UI to help with bug reports. However, the steam command to get the build-id is super weird:

int GetAppBuildId();

Gets the buildid of this app, may change at any time based on backend updates to the game.

Returns: int The current Build Id of this App. Defaults to 0 if you’re not running a build downloaded from Steam.

Did you spot it? It returns the latest build-id on Steam, not the build-id of what you have installed!

Why? I don’t know. But I finally got annoyed enough to fix the problem.

I’ve modified our build scripts to use git to get the short hash of the current TaleSpire commit and include it as a const string in the c# code.

It’s dead simple and makes it trivial to ensure we are testing the build the players were playing.

For you lovely folks out there who are dll hacking and making unofficial mods, we append an extra _M to the end of the build-id when the AppStateManager.UsingCodeInjection flag is set.

This flag has been hugely helpful for helping sort bugs reports on the server side, so thank you so much for setting it. By including it in the build-id, we can speed up triaging some of the bug reports we’ve seen so far that have been mod-related.

In house tooling

Today I picked up a task that has been on my todo list for ages. Improve in-editor tools for working with tiles and props.

Tiles and props in TaleSpire are not made with Unity’s GameObjects. We made enormous performance improvements by creating a custom approach [3], but in doing so, we have lost the fantastic tooling we got trivially with GameObjects.

To improve this situation, I wrote a custom inspector that uses our pixel-picking code to display a bunch of our internal asset data. It’s ugly, but it’s already proving useful.

As I was apparently in the mood to work on stuff that had been waiting forever, I decided to optimize the physics debug drawing code. Until now, we used Unity’s Handles API, and it is slow. Slow to the point that I can’t visualize physics info for large boards.

Luckily the Debug.DrawLine method was updated to work from Burst compiled jobs, so I decided to rewrite Handles to use Debug.DrawLine instead.

This only took an hour or two, and the result was a HUGE boost in performance. However, there is a cap on the number of lines that can be drawn per frame, so my next step will be to replace Debug.DrawLine with my own approach. However, for now, this is great.

With that done, I tweaked the new inspector to enable visualizing the physics data for the selected asset. This used to be pretty tedious to debug, and now it’s two clicks :)


The AssetDb work was required for HeroForge as well as modding, so we’ve covered the bulk of the news. However, the HeroForge team has really gone to town packing extra data into their models so that we can hopefully support ‘Dark Magic’ as well as a bunch of other things.

As just one example, they now include metadata about their emissive textures, which helps us process them much faster in some cases. This is all stuff they didn’t have to do, so we’re super grateful!

And that’s the lot

It’s been a busy time, and it is not slowing down. The next asset pack is well underway, the next patch is ready for testing, and I’ll start integrating all the new stuff from HeroForge tomorrow.

Until next time. Stay safe.

Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team

[0] A missing texture causing odd behavior on some GPUs

[1] We also use standard vertex animations.

[2] This is an approximation, but it will do.

[3] For a reminder of how slow it was, even with lots of caching and work, check out the video in this dev log: