Mega Tree API
Persistence
Where levels are stored, and why uninstalling your mod does not destroy the player's progress.
You do not have to do anything
Saving is handled for you. Levels are written the moment something is bought or sold, into a file of Mega Tree's own, per save slot. Your mod never opens a file and never touches the game's save.
BepInEx/config/MegaTree/slot0.datMegastore's Save_N.data is binary. A mod writing into it makes the player's save hostage to the mod: uninstall and they are left with orphan keys in a file that is not ours, and a bug on our side corrupts something with no repair path.
A plain-text .dat alongside has none of that. It opens in Notepad, it disappears with the mod, and the worst case of a bug is losing upgrades - never the store.
What the file looks like
walk=5
sprint=3
cashier=2
# kept for mods that are not currently installed; do not edit
nightowl.late_hours=2
supercart.wheels=1Plain id=level text. The block at the bottom is the interesting
part.
Uninstalling your mod does not delete progress
When Mega Tree reads the file and finds an id it does not recognise - because the mod that registered it is not installed right now - it keeps that line aside and writes it back out untouched on every save.
| Player does | What happens to bigboxes.capacity=3 |
|---|---|
| Removes your dll, keeps playing, buys something else | Still in the file, under the "kept for mods" comment |
| Puts your dll back | Your Register claims it; the node comes back at level 3 |
| Starts a new save | Nothing carried over - it is per slot |
| Uses two saves with different mod sets | Each slot keeps its own kept-lines |
Until 1.1.0 an unknown id was dropped on read, and the next save wrote the file without it. With no third-party nodes that never mattered. The moment mods can register, it becomes a player who tried a mod, removed it and came back to find the money they spent is gone - silently.
So the kept block is not a nicety. It is the thing that makes uninstalling a mod safe.
The cap is 4096 kept lines per slot, which no realistic mod set will reach.
Registering late is safe
Registration is not tied to load order. If you register a node after a save has already been
read - mid-game, after a config change, from a coroutine - Mega Tree checks the kept lines for
your id and adopts the level immediately. Your OnLevelChanged does not
fire for it, but GetLevel is correct from the next line onwards.
var handle = tree.Register(definition);
Logger.LogInfo($"level restored from save: {handle.Level}");Reading the current level
int lvl = MegaTreeApi.GetLevel("bigboxes.capacity"); // 0 if unknown or not ownedThis is a dictionary lookup, so calling it inside a Harmony postfix that runs often is fine - that is the intended usage.
Slots and switching saves
The slot is read from the game's own save system, so it follows the player through every load
path - Continue, Load, New Game. If they switch saves without quitting, Mega Tree notices within
half a second, reloads the right file and fires SaveLoaded with the new
slot index.
Anything you remembered about the previous save - base values keyed by instance id, "already applied" flags, per-day counters - is wrong the moment another slot loads. Pooled objects reuse instance ids across saves, so a stale base value silently corrupts the new game.
MegaTreeApi.SaveLoaded += slot =>
{
Base.Clear();
_appliedToday = false;
};What is not saved
Only the level number, per node id. Not your definition, not your icon, not your translations - those come from your dll every launch. Which is exactly what makes changing a price or an icon in an update harmless: the player's level is what persists.
The id is the key. Renaming
bigboxes.capacity to bigboxes.box_size in
version 1.1 means every player starts that line from zero, and their old level sits in the kept
block forever waiting for an id that will never come back.
If you truly have to rename one, LegacyIds below is the way out.
Renaming an id without losing progress
Sometimes it is unavoidable - you shipped an id that does not fit the rules, or your mod changed shape. List the old names and the level comes with them:
tree.Register(new UpgradeDefinition
{
Id = "pallets.permit",
LegacyIds = new[] { "pallet_permit" }, // what it used to be called
// ...
});On registration, Mega Tree looks for a kept level under each old id and moves the highest one onto the new id. The player never notices.
| Situation | What happens |
|---|---|
| New id at 0, old id has a level | Moves over, and the log says so |
| New id already has a level | Nothing moves. Someone who already played with the new id outranks any old value |
| Several old ids with levels | The highest wins; all of them are cleared from the save |
| An old id that was never used | Silence, no error |
| The current id listed among the old ones | Ignored |
The migration happens when the node registers and
finds a kept level. After that the old id is gone from the save file, so leaving
LegacyIds in your code forever costs nothing - it simply finds nothing
on every later launch.
Telling a player where to look
When someone reports lost progress, these are the two files that matter:
| File | What it is |
|---|---|
BepInEx/config/MegaTree/slotN.dat | Every upgrade level, yours included. Text. |
AppData/LocalLow/Yolo Games Studio/Megastore Simulator/Save_N.data | The game's own save. Binary - a text editor shows garbage, and that is normal, not corruption. |
Deleting the game save does not delete mod progress, and uninstalling a mod cannot corrupt the game save. They are separate files on purpose.