Skip to content

0.4.13 preview

Pre-release
Pre-release

Choose a tag to compare

@kramsey458 kramsey458 released this 21 Sep 02:25
· 70 commits to main since this release

Preview of Late Game Performance for Timberborn 1.1.2.4. Nothing new in this version has been played in-game yet. The latest release is still 0.4.9. This preview includes everything in 0.4.10 to 0.4.12, which have been played.

Multiplayer: every player installs this same version. That is all. Nothing here is a setting that could differ in a way that matters.

Because this version touches saving: keep a manual save from before you install it, and after the first autosave, check that Player.log has a BackgroundSave: ... written line and that the autosave loads.

New: autosaves and menu saves finish on a worker thread

A save froze the game for 0.8 s on a fast computer and up to 2.4 s on a slower one in the colony this is measured in. About a third of that is the snapshot, which reads live game objects and has to stay on the game thread. More than half is turning the snapshot into JSON, compressing it and writing the file, and none of that needs the game: the snapshot is plain data from the moment it is taken. That half now runs on a worker thread while the game carries on.

It works inside the game's own save instead of replacing it:

  • GameSaver.Save is not patched. BeaverBuddies wraps it (to move saves to a tick boundary), and that keeps working because everything this does happens inside it.
  • On the game thread, in the game's order: the snapshot, the thumbnail, the metadata, and any entry another mod adds. Only the world entry's JSON is left for the worker.
  • The worker builds the archive exactly as the game does (same zip mode, entry order, compression level), reads it back and checks every entry, writes it to <name>.timber.saving, flushes it to the disk, and only then renames it over the target. The game lists saves by .timber, so an unfinished file is never offered for loading, and a save you overwrite stays intact until its replacement is complete (the game itself truncates the old file first and then writes).
  • The game's completion callback (for an autosave: delete the oldest autosaves) runs on the game thread only once the new file is in place. If a save cannot be written, no old autosave is deleted because of it.

What is deliberately left alone: the save on exit, BeaverBuddies' rehost save, and the saves BeaverBuddies streams to a joining player. Those callers expect the result on return, so they run as the game's own code.

What happens when something goes wrong:

The .saving file cannot be opened, another mod supplies the file service, or the save is not the queued one written into the game's own stream, there and then, the game's errors included. A prepared save never ends as an empty file.
The worker fails (disk, rename refused after five tries, anything thrown) saved again on the game thread from the same snapshot, the game's way; background saving stays off for the session
That fails too SAVE FAILED in the log, the game's callback does not run, the game gets the same GameSaverException it would have had
Preparing fails nothing has been written; the feature turns itself off and the game performs that save in full
Another save, a new scene, quitting, or anything asking the save list about files while a save is being written waits for it first (the worker is a foreground thread, so the process does not end under it)

BackgroundSave = false in LateGamePerformance.cfg switches it off. It does not touch the simulation, so it does not matter in multiplayer who has it on.

New: district resource counts are added up on worker threads

Every tick each district adds up, for every good, what all its storage holds and how much room it has: 1.07 ms per tick in the measured colony. The numbers feed the top bar, the tooltips and, inside the simulation, the automation resource counter, so they have to be exactly the game's numbers at the game's moment. Counting less often or only what changed was looked at and dropped for that reason.

Both questions are pure reads of an inventory, and the answer is a sum of whole numbers, which comes out the same in any order. So the inventories are dealt out to worker threads, each asks the game's own questions and adds up into its own table, and the main thread adds the tables into the game's. An inventory whose capacity rule comes from a mod is counted on the main thread; if another mod patches a method the workers would call, the feature stands down with one log line and the game counts. The result is the same either way and on every computer.

New log lines

[LateGamePerformance] Simulation features: HaulCache on, RouteMaps on, YielderSearch on, TerrainMaps on, PlantWater on, DistrictCounts on. These are the same for every player on this version.
[LateGamePerformance] Save: 310 ms total = finishing the tick 14 ms + snapshot 190 ms + world JSON and compression 0 ms + thumbnail 60 ms + everything else 46 ms (world JSON, compression and the file write follow on a worker thread)
[LateGamePerformance] BackgroundSave: 2026-09-20 21h14m, Day 412.autosave.timber written (14.2 MB). Off the game thread: world JSON, compression and check 540 ms, file 35 ms. On the game thread: snapshot and the other entries 262 ms.
[LateGamePerformance] Last 1000 ticks. DistrictCounts: 1000 counts of 640 inventories on 7 workers in 310.0 ms (0.310 ms each); 0 inventories per count on the main thread; 0 counts of small districts left to the game

(The numbers are illustrations, not measurements.)

What was tested, and what was not

The harness passes against the installed game's assemblies; 58 patch targets resolve (11 new), none with an exception filter.

  • The file writer, with real files: same entries, order, contents and size as the game's writer; an overwritten save readable until its replacement is complete; a refused rename retried; a rename that never works, a writer that throws, and a world entry that comes out empty all leave the old save untouched, and the second attempt behaves as described. (The tests caught one real bug here before release: an archive that had failed its check could have been reused by the second attempt.)
  • The game's real WorldSerializer writes the same bytes on a worker thread as on the main thread for a 4000-entity world, and the game's own reader loads what the worker wrote, every entity in order.
  • The whole chain of hooks in the order GameSaver.Save calls them, with a real SaveWriter and the real WorldEntryWriter type: the callback runs once, on the game thread, with the complete file in place; a second save waits for the first; instant saves and saves to a stream are the game's; a save that is not the queued one, a foreign file service and a blocked .saving name all end as complete files on return; a doomed save raises GameSaverException and never reports completion; a new scene finishes the file and drops the old scene's callback.
  • District counts: 700 real Inventory objects with the game's own capacity rules plus one stand-in mod rule, six rounds with stock moving in between, every good compared through the game's public GetResourceCount; the mod rule was only ever asked on the main thread. In the harness one count takes the game 1.9 ms and the mod 0.5 ms.
  • Not tested: anything inside the game. Three things can only be seen there:
    1. That the first autosave produces the BackgroundSave: ... written line and loads.
    2. In a BeaverBuddies session, whether its wrapper leaves GameSaver.Save recognisable as the caller. If not, saves simply stay the game's own and one line says BackgroundSave: a queued save is pending but the writer was called by '...'. Send me that line and it is a one-line fix.
    3. The DistrictCounts: "ms each" figure under Unity's runtime, to compare with 1.07 ms. DistrictCountsVerify = true makes the game count as well and compares; verify mismatches 0 is the line to look for.

If anything about saving looks wrong, set BackgroundSave = false, or go back to 0.4.12, and send Player.log.

Install

  1. Close Timberborn. Delete any older LateGamePerformance folder, then extract LateGamePerformance-0.4.13.zip into Documents/Timberborn/Mods.
  2. Requires the Harmony (2.4.1+) and Mod Settings Workshop mods.

SHA-256 of the zip: 683c7afa8831b56efb6a9f0cabe4ab24e14463a96ab0566ea7a93f15cfd06a59