Mega Tree API

Versioning

The const trap, what a Mega Tree update can and cannot break, and how to change your own numbers without hurting players.

The const trap

This is the single most common way a mod that depends on another mod breaks, and it is worth understanding before anything else on this page.

In C#, a const is inlined into your assembly at compile time. If Mega Tree exposed its API version as a const and you wrote a check against it, your compiled dll would carry the number that was current on your machine the day you built - and would keep comparing against that number forever, on every player's install, no matter which Mega Tree they actually have.

csharp
// If ApiVersion were a const, this is what YOU write...
if (MegaTreeApi.ApiVersion < 2)
    Logger.LogWarning("Mega Tree too old");
csharp
// ...and this is what the compiler bakes into your dll:
if (1 < 2)                      // the value from YOUR build machine, frozen
    Logger.LogWarning("Mega Tree too old");

// The player upgrades Mega Tree to ApiVersion 5. Your check still says 1.

So MegaTreeApi.ApiVersion is deliberately declared public static readonly int, not const. A readonly field is read from the installed dll at runtime, which is the only thing that can answer "what does this player actually have".

The same trap in your own code

If you expose constants to other mods, apply the rule to yourself. const is right for something that can never change, like ModGuid. Anything a future version might bump should be static readonly.

Checking the version

csharp
private void Awake()
{
    if (MegaTreeApi.ApiVersion < 1)
    {
        Logger.LogWarning($"Mega Tree API {MegaTreeApi.ApiVersion} is older than this mod needs.");
        return;
    }

    RegisterUpgrades();
}
ApiVersionMega TreeWhat it means
(does not exist)1.0.xNo API. Your mod fails to load with a TypeLoadException.
11.1.0Everything documented here.

The number only goes up on a breaking change. New optional fields, new events and new built-in icons do not bump it - code written against version 1 keeps compiling and keeps working.

Declaring the dependency

csharp
// This is the one to use. Any Mega Tree satisfies it.
[BepInDependency(MegaTreeApi.ModGuid)]
Do not put a version in BepInDependency

The second argument reads like "minimum version", and it does not behave that way reliably. Tech Tree learned this the hard way in the other game: a plugin declaring a minimum was refused with a NEWER version installed and loaded, printing "missing dependencies" on the line right after the dependency loaded successfully.

So pinning a version does not protect you from an older Mega Tree. It breaks your mod the day Mega Tree updates. Use the guid alone.

Then how do I guard a newer field?

ApiVersion answers "is the contract still the same". It does not answer "does this specific field exist" - a mod that sets a field added in a later version throws MissingFieldException on an older install, and a runtime check cannot save you because the failure happens as the method is compiled, not where the line sits.

Say the minimum version in your mod page description, and use the field.

When Mega Tree updates

ChangeEffect on your mod
New optional field on UpgradeDefinitionNone. Old code compiles and runs.
New built-in iconNone.
New eventNone.
Built-in node prices rebalancedNone - yours are yours.
A built-in id renamedOnly if you gated on it. Prefer gating on your own nodes.
A new built-in tabNone. Yours is always the Mods tab.
ApiVersion bumped to 2Read the changelog. Your version check catches it.
The game updating is a separate risk

Mega Tree keeps its own API stable. Neither of us controls Megastore Simulator, and a patch that renames the class your Harmony patch targets breaks your effect while leaving your node perfectly visible and buyable.

Log the patch count at startup. A mod whose node works and whose effect silently does nothing is the hardest kind of report to answer, and one line saying "applied 2 Harmony patches" - or not saying it - settles it in seconds.

Changing your own numbers

Levels are saved by id, and nothing else about the node is. That makes most updates free:

You changePlayer who already owns levels
PriceKeeps their levels; the new price applies to levels not yet bought.
IconJust changes.
Display name / translationsJust changes.
Effect strengthApplies immediately at their current level.
MaxLevel upNew levels appear, buyable in order. Free win.
MaxLevel downSee below.
The idLoses everything, unless you list it in LegacyIds.

Lowering MaxLevel

Say a player owns level 5 and your update drops MaxLevel to 3. The raw number 5 stays in the save file; only what is displayed and applied is clamped to 3. If you change your mind and put it back to 5 in the next version, their level 5 returns.

Nothing is destroyed, but the player did pay for two levels they no longer have. If you must narrow a line, say so in your changelog.

Removing a node for good

If a feature is gone, simply stop registering the id. The player's level stays in their save file under the "kept for mods" block indefinitely; it costs a few bytes and it means a rollback to your older version restores everything. Unregister does the same thing at runtime.

There is no API to erase a level, on purpose - deleting something a player spent in-game money on should not be a side effect of a mod update.

Semantic versioning for your mod

BumpWhen
PatchBug fix, wording, icon.
MinorNew node, more levels on an existing one, rebalance.
MajorA node removed, MaxLevel lowered, or a new minimum Mega Tree version.