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, 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. 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.
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.
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
 But hey, no trades, our community rules!
 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.
 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.
Things are going great over here. Here’s the latest from me.
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
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 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, simplify some edge cases, and make building AMIs waaaay easier.
Today I’ll continue with that backend work.
Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team
 Using this term generally, we don’t use a microservice architecture  Which won’t matter now, but that I believe certainly would if we start taking on more real-time traffic  I could probably argue that it will make the dev environment closer to the running environment, but that’s another ramble.
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
p.s: Hats off to the art team for the latest patch!
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:
- Unity.Physics depends on it
- 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.
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.
 In fact, I did this today! It’s sitting on a branch, ready to merge
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!
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, 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:
There are plenty! The big one is that lots of shaders are broken. As an example, here is water:
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!
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!
BuildPlayerOptions.locationPathName does not end with
.app, the dll containing the compiled burst jobs is not being copied to the plugins folder.
Yesterday seemed to want to stop me from working on Mac support, and it won. Today I turned the tables.
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 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.
 because, as the docs say, “Despite being a multicast delegate, only the value returned from the last-executed event handler is considered authoritative”
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.
- 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.
How the hell has it been a week since the last one of these?! Let’s remedy that.
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, 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. 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, 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:
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 , 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.
Debug.DrawLine method was updated to work from Burst compiled jobs, so I decided to rewrite
Handles to use
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.
 A missing texture causing odd behavior on some GPUs
 We also use standard vertex animations.
 This is an approximation, but it will do.
 For a reminder of how slow it was, even with lots of caching and work, check out the video in this dev log: https://bouncyrock.com/news/articles/talespire-dev-log-202
The HeroForge work is rapidly solidifying. We have had some testers playing with it and finding plenty of bugs, but nothing looks too scary. We have been able to try out some more normal creations…
…and some that are a little more out there.
In recent days I’ve been working on:
- Support for multiple creatures on a single base
- A mistake in mipmap generation
- A Linux (via Proton) specific bug due to how I was calling some WinAPI functions for random data
- Issues with miniature placement
- A line-of-sight bug which arose because of the changes needed to the creature code.
- Better use of head/hand/etc positions from model, and sensible fallback values;
The eagle-eyed among you may have noticed some large creatures with surprisingly small bases. Until now, the base size has matched the creature size (0.5x0.5 -> 4x4). After some discussions with HeroForge, we decided that the scale of the base relative to the creature should match the HeroForge editor as closely as possible.
One problem that immediately arose was that you can’t just use the base size as the default scale. Otherwise, this chap
who is a 1x1 creature can be scaled up to 4x4, which gives you this
Which is unplayable.
This meant decoupling base size and creature size. We use the total size of the mesh to pick a sensible default size and let the base be rescaled to keep the proportions required. This keeps things looking good while also being playable
So far today, I’ve hooked up the emissive map, which is where HeroForge bakes its lights.
Note: As you can see there is still work to do on the strength of the emissive
It seems that the emissive map is included even if it is empty, so I am updating the asset conversion process to check for this. We can then exclude blank maps, saving some GPU memory in the process.
I am then back on bugs and loose ends. I have a lot of ‘todos’ in the code where I need to handle failure cases and decide how to relay them to the user. This will be fine, but it takes a little time. All of these cases will be relevant for the creature modding too.
I won’t finish all the fixes today naturally, but it’s all looking very positive. Getting a mini from HeroForge into TaleSpire takes about ten seconds, depending on download time (mine is pretty slow).
Until next time!
 I’m trying not to be hyperbolic here. I tried playing with it, and it was utterly useless at those scales.
 Even today, scale can be tricky in TaleSpire. I definitely don’t want to claim that our approach with HeroForge is ideal, but this feels in line with the current experience of the built-in assets.
 Except for the “dark magic” lights. We need to work out how/if we can support those.