Skip to content

en SaveContent Save System

HoCha113 edited this page Mar 29, 2026 · 5 revisions

中文版本

SaveContent Save System

InnoVault provides a generic save framework based on NBT (TagCompound), supporting mod-level and world-level data persistence.


Core Concepts

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

Data Structure

Save files use a nested TagCompound structure:

rootTag
 └─ "mod:YourModName"        (TagCompound)
      └─ "SavePrefix:Name"   (TagCompound)
           └─ your data...

Quick Start

1. Create a SaveMod Subclass (Mod-Global 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>();

2. Create a Custom SaveContent (World-Level Data)

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");
    }
}

API Reference

ISaveContent Interface

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

SaveContent<T> Properties and Methods

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

Save/Load Methods

// ——— 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>();

Lifecycle Hooks

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

File Utility Methods

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 Caching Mechanism

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 cache

Complete Usage Example

using 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");
    }
}

Navigation

Previous Next
Home Override System

Clone this wiki locally