-
Notifications
You must be signed in to change notification settings - Fork 6
en SaveContent Save System
HoCha113 edited this page Jun 21, 2026
·
5 revisions
InnoVault provides a generic save framework based on NBT (
TagCompound), supporting mod-level and world-level data persistence.
The save system consists of the following components:
| Component | Description |
|---|---|
ISaveContent |
Save interface, defines the minimum contract for save/load |
SaveContent<T> |
Generic save base class, provides complete NBT file read/write capability |
SaveMod |
Inherits SaveContent<SaveMod>, used for mod-global data storage |
TagCache |
Path → TagCompound cache layer, reduces redundant disk I/O |
Save files use a nested TagCompound structure:
rootTag
└─ "mod:YourModName" (TagCompound)
└─ "SavePrefix:Name" (TagCompound)
└─ your data...
SaveMod is used to save data outside of game worlds, such as settings, statistics, etc. Its save path is VaultSave.RootPath/ModDatas/mod_{Mod.Name}.nbt.
using InnoVault.GameSystem;
using Terraria.ModLoader.IO;
public class MyModData : SaveMod
{
public int TotalKills;
public string LastPlayer;
public override void SaveData(TagCompound tag) {
tag["totalKills"] = TotalKills;
tag["lastPlayer"] = LastPlayer ?? "";
}
public override void LoadData(TagCompound tag) {
TotalKills = tag.GetInt("totalKills");
LastPlayer = tag.GetString("lastPlayer");
}
}Note: SaveMod does not automatically call save/load logic — you need to manually trigger it at appropriate times:
// Save (only saves MyModData's data, does not affect other instances)
SaveContent<SaveMod>.DoSave<MyModData>();
// Load
SaveContent<SaveMod>.DoLoad<MyModData>();If you need an independent storage path, define a new generic base class:
public class SaveWorld : SaveContent<SaveWorld>
{
// All classes inheriting SaveWorld share the same .nbt file
public override string SavePath => Path.Combine(VaultSave.RootPath, "WorldDatas", $"world_{Main.worldID}.nbt");
}
public class MyWorldData : SaveWorld
{
public bool BossDefeated;
public override void SaveData(TagCompound tag) {
tag["bossDefeated"] = BossDefeated;
}
public override void LoadData(TagCompound tag) {
BossDefeated = tag.GetBool("bossDefeated");
}
}| Member | Type | Description |
|---|---|---|
ForceReload |
bool |
Whether to force reload from disk (bypass cache), default false
|
LoadenName |
string |
Identifier key name in the NBT file |
SavePath |
string |
Full path to the storage file |
SaveData(tag) |
void |
Write data to TagCompound
|
LoadData(tag) |
void |
Read data from TagCompound
|
| Member | Description |
|---|---|
SaveContents |
List<T> — All registered instances |
TypeToInstance |
Dictionary<Type, T> — Type → instance mapping |
ModToSaves |
Dictionary<Mod, List<T>> — Mod → instance list mapping |
SavePrefix |
Save key prefix, defaults to base class name (without generic suffix) |
LoadenName |
Full identifier: SavePrefix:Name
|
SavePath |
File path, defaults to VaultSave.RootPath/content_{T}.nbt
|
HasSave |
Whether the target file exists |
GenericInstance |
Directly get the singleton instance of type T
|
// ——— Global batch operations ———
SaveContent<T>.DoSave(); // Save all T-type instances to the same file
SaveContent<T>.DoLoad(forceReload); // Load all T-type instances
// ——— Single instance operations (recommended) ———
SaveContent<T>.DoSave<TTarget>(); // Save only the specified type, does not affect other data
SaveContent<T>.DoLoad<TTarget>(); // Load only the specified type
// ——— ISaveContent interface operations ———
SaveContent<T>.DoSave(ISaveContent); // Save via interface
SaveContent<T>.DoLoad(ISaveContent); // Load via interface
// ——— Get instance ———
SaveContent<T>.GetInstance<TTarget>();| Hook | Parameters | Description |
|---|---|---|
PreSaveData(tag, style) |
style: 0=global save, 1=single save |
Return false to skip this save |
SaveData(tag) |
— | Write custom data |
PreLoadData(tag, style) |
style: 0=global load, 1=single load |
Return false to skip this load |
LoadData(tag) |
— | Read custom data |
| Method | Description |
|---|---|
TryLoadRootTag(path, out tag, forceReload) |
Load NBT from path, prioritizes cache |
SaveTagToFile(tag, path) |
Write TagCompound to .nbt file |
SaveTagToZip(tag, zipPath, addTimeStamp) |
Compress and save TagCompound as .zip
|
PruneBackups(baseZipPath, maxCount) |
Clean old backups, retains latest 7 by default |
TagCache is a thread-safe cache layer based on ConcurrentDictionary:
- Maximum cache capacity: 12 entries
- Eviction strategy: FIFO (First In, First Out)
- Cache is automatically updated when writing saves
- If file doesn't exist, corresponding cache entry is automatically invalidated
// Manual cache operations
TagCache.Set(path, tag); // Write/update cache
TagCache.TryGet(path, out var tag); // Try to read from cache
TagCache.Invalidate(path); // Invalidate cache for specified path
TagCache.Clear(); // Clear all cacheusing InnoVault.GameSystem;
using Terraria.ModLoader;
using Terraria.ModLoader.IO;
// 1. Define data class
public class PlayerStats : SaveMod
{
public int PlayTime;
public float BestDamage;
public override void SaveData(TagCompound tag) {
tag["playTime"] = PlayTime;
tag["bestDamage"] = BestDamage;
}
public override void LoadData(TagCompound tag) {
PlayTime = tag.GetInt("playTime");
BestDamage = tag.GetFloat("bestDamage");
}
}| Previous | Home | Next |
|---|---|---|
| Basic UI | Home | Override System |