Saving Data
Sometimes a mod setting or other data should persist between sessions. Mods can do this really easily using the built in methods provided.
BaseMod Methods
Let’s say you have this mod:
public class MyMod : BaseMod
{
public override string Name => "My Mod";
public override string Description => "My new test mod";
public override ModsWindow ModsWindow => Plugin.MyModsWindow;
[ModSetting]
public static float MySetting = 1f;
} We can save MySetting to disk by making it a Ref<float> and using the BindData method in OnStaticInit since its a static field. BindData stores the value in the shared data.json of your mod, at <Game>/lstwoMODS/<Namespace>.MyMod/data.json:
public class MyMod : BaseMod
{
public override string Name => "My Mod";
public override string Description => "My new test mod";
public override ModsWindow ModsWindow => Plugin.MyModsWindow;
[ModSetting]
public static Ref<float> MySetting = new(1f);
protected override void OnStaticInit()
{
BindData(
MySetting, // Ref field
nameof(MySetting), // Key
MySetting.Value // Default value
);
}
} You can also save using the SaveData<T> method to save and the LoadData<T> method to load.
These give the value its own file instead of putting it in the bag, so they don’t share storage with BindData:
public class MyMod : BaseMod
{
public override string Name => "My Mod";
public override string Description => "My new test mod";
public override ModsWindow ModsWindow => Plugin.MyModsWindow;
[ModSetting]
public static Ref<float> MySetting = new(1f);
protected override void OnStaticInit()
{
if(DataExists("MySetting"))
MySetting.Value = LoadData<float>("MySetting");
MySetting.Changed += num => SaveData<float>("MySetting", num);
}
} Load first and subscribe after, the other way around would immediately save the value you just loaded.
If you need to delete data, use the DeleteData method.
For storing larger data you may also want to store one value in its own file.
For that you can use BindDataFile.
What You Can Save
Anything that serializes to JSON cleanly works.
Unity types like Color, Vector3 and Quaternion do not.
They have properties that return their own type (Color.linear, Vector3.normalized), so the serializer follows them forever and takes the game down with it.
Save them as Col, Vec2, Vec3 or Vec4 instead.
Those are plain structs that convert to and from the Unity types on their own, so you only need them for the saving part:
// the UI still wants a Ref<Color>
public static readonly Ref<Color> MyColor = new(Color.white);
protected override void OnStaticInit()
{
if(DataExists("MyColor"))
MyColor.Value = LoadData<Col>("MyColor");
// BindData would hand the Color straight to the serializer, so convert on the way out
MyColor.Changed += c => SaveData("MyColor", (Col)c);
} DataStorage Methods
If you want to save data from outside of a mod you can use the DataStorage class directly:
var myData = new MyData();
// <Game>/lstwoMODS/net.lstwo.MyMod/MyData.json
var id = "net.lstwo.MyMod";
var key = "MyData";
// Save object as file
DataStorage.Save(
id, // Unique id for the folder
key, // Unique key for the file name
myData // Data to save in the file
);
// Check if the file exists
if(!DataStorage.Exists(id, key))
return;
// Load saved data from file
var mySavedData = DataStorage.Load<MyData /* Data type to load */>(
id, // Unique id of the folder
key // Unique key of the file name
);
// Delete the file
DataStorage.Delete(id, key); A key can contain forward slashes to put the file in a subfolder, e.g. "outfits/pirate".
That’s handy for storing a list of things the user can share around, since ListKeys gives you back everything in one of those folders:
// every key in <Game>/lstwoMODS/net.lstwo.MyMod/outfits/
foreach(var outfitKey in DataStorage.ListKeys(id, "outfits"))
{
var outfit = DataStorage.Load<MyOutfit>(id, "outfits/" + outfitKey);
} Files dropped into that folder by hand show up here too, so this is all sharing needs.
If you want to point the user at one of the files, DataStorage.GetFilePath(id, key) gives you the full path to it.
Or to save multiple keys within a single file use bags:
var myFloat = 1.5f;
var myInt = 25;
// <Game>/lstwoMODS/net.lstwo.MyMod/data.json
var id = "net.lstwo.MyMod";
// Saves a key to the data.json file
DataStorage.SaveToBag(
id, // Unique id for the folder
"myFloat", // Key within the data.json
myFloat // Value to save
);
DataStorage.SaveToBag(id, "myInt", myInt);
// Check if an entry exists
if(!DataStorage.BagEntryExists(id, "myFloat"))
return;
// Load one key from the data.json
var savedMyFloat = DataStorage.LoadFromBag<float>(id, "myFloat");
// Delete one key and its value from the data.json
DataStorage.DeleteFromBag(id, "myInt");