Tech Tree API

Registration

When to call, what comes back, the rules ids must follow, and what happens when two mods want the same one.

When to call

Register from your plugin's Load(). That is early - before any scene exists - and it is fine, because registration only records data. Nothing touches Unity until the player opens the tree window.

You can also register later: mid-game, after a config reload, after the player unlocks something. A node that appears while the window is open shows up on the next repaint, and if the player already owned levels on that id from a previous session, those levels come back with it (see Persistence).

The API never throws

Register is called from inside your Load(). If it threw, the exception would abort your plugin, not Tech Tree - which is a terrible failure mode for a mistake as small as a typo in an id. So it does not throw. Every failure comes back as a result and is written to the log with your guid.

csharp
UpgradeHandle handle = tree.Register(new UpgradeDefinition { /* ... */ });

if (!handle.Success)
{
    // handle.Result.Status  -> RegisterStatus enum
    // handle.Result.Message -> the same text that went to the log
    Log.LogError($"Tech Tree refused the node: {handle.Result}");
    return;
}

int owned = handle.Level;   // 0 on a fresh save

RegisterStatus

StatusMeaning
OkThe node was added.
ReplacedYou re-registered your own id. The definition was swapped and the owned level kept.
MissingFieldNo DescribeLevel, no name, or a null definition.
InvalidIdThe id is not prefix.name, or uses characters outside a-z 0-9 _ - .
ReservedIdThe id starts with techtree. or tt.
OwnedByAnotherAnother plugin got there first. The first registration is untouched.
InvalidLevelsMaxLevel is outside 1..20.
InvalidCostNo positive BaseCost and no CostForLevel.
InvalidModuleYou passed an empty plugin guid to ForPlugin.

Id rules

An id registered through the API must be in prefix.name form:

IdVerdict
bigboxes.capacityok
night_owl.open-lateok
capacityno dot
BigBoxes.Capacityupper case
bigboxes..capacitydouble dot
tt.capacityreserved prefix
cashier_speedno dot (and it is a built-in id)
Why the dot is mandatory

None of Tech Tree's own ten ids contain a dot - they are cashier_speed, more_customers and so on. Requiring a dot from everyone else means a third-party mod cannot collide with or hijack a built-in node by construction. No renaming, no save migration, and the rule is one line to remember.

Already shipped an id you regret?

Renaming it normally costs your players their levels, because the id is the save key. Since 1.3.1, LegacyIds carries the level over to the new name. How it works

Duplicate ids

SituationWhat happens
Same plugin, same id, twice The definition is replaced and the owned level is kept. This is what makes hot-reload work.
Different plugin, same id Rejected with OwnedByAnother, naming both guids in the log. The first registration is untouched and neither mod breaks.
Same prefix, different plugins Allowed, with a warning. Mod suites legitimately share a prefix.

Limits

LimitValue
Nodes per plugin64
Levels per node1 to 20
Sub-tab label18 characters
Icon PNG512 KB

Removing a node

Unregister takes a node out of the tree. It does not delete the player's level - that stays in the save file and comes back if you register the id again. Use it for config toggles, not as a cleanup step on shutdown.

csharp
// Via the handle...
handle.Unregister();

// ...or by id, from the module.
tree.Unregister("bigboxes.capacity");
Removing every node hides your tab

A sub-tab with no nodes is not drawn, and if yours was the only mod registered, the whole Mods tab disappears too. If that happens while the window is open, the view falls back to the Staff tab instead of going blank.