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.
// If ApiVersion were a const, this is what YOU write...
if (MegaTreeApi.ApiVersion < 2)
Logger.LogWarning("Mega Tree too old");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".
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
private void Awake()
{
if (MegaTreeApi.ApiVersion < 1)
{
Logger.LogWarning($"Mega Tree API {MegaTreeApi.ApiVersion} is older than this mod needs.");
return;
}
RegisterUpgrades();
}| ApiVersion | Mega Tree | What it means |
|---|---|---|
| (does not exist) | 1.0.x | No API. Your mod fails to load with a TypeLoadException. |
1 | 1.1.0 | Everything 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
// This is the one to use. Any Mega Tree satisfies it.
[BepInDependency(MegaTreeApi.ModGuid)]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.
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
| Change | Effect on your mod |
|---|---|
New optional field on UpgradeDefinition | None. Old code compiles and runs. |
| New built-in icon | None. |
| New event | None. |
| Built-in node prices rebalanced | None - yours are yours. |
| A built-in id renamed | Only if you gated on it. Prefer gating on your own nodes. |
| A new built-in tab | None. Yours is always the Mods tab. |
ApiVersion bumped to 2 | Read the changelog. Your version check catches it. |
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 change | Player who already owns levels |
|---|---|
| Price | Keeps their levels; the new price applies to levels not yet bought. |
| Icon | Just changes. |
| Display name / translations | Just changes. |
| Effect strength | Applies immediately at their current level. |
MaxLevel up | New levels appear, buyable in order. Free win. |
MaxLevel down | See below. |
| The id | Loses 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
| Bump | When |
|---|---|
| Patch | Bug fix, wording, icon. |
| Minor | New node, more levels on an existing one, rebalance. |
| Major | A node removed, MaxLevel lowered, or a new minimum Mega Tree version. |