Persistent Data - MSUTeam/MSU GitHub Wiki

Description

Persistent Data refers to data that the modder and users wish to retain between between campaigns. This includes Mod Settings and Keybinds, but can be extended to other mod functionality as well.

As of MSU 1.3.0, the Persistent Data system no longer requires BBParser and is more flexible, allowing modders to create files with arbitrary data. This data is stored in save files with a specific name format. These do get synced by Steam, and would normally be visible to users as save games, but we filter them out in save/load menu.

Mods are able to create an arbitrary number of files to store their data, but we recommend minimizing the number of files your mod creates. The Persistent Data System is able to reserialize information an arbitrary number of times, and is able to gracefully handle your mod changing serialization methods, so the only time your mod should require more than one file is if you want to allow users to share files. In that case, those additional files should only be created on user request.

In general, when using the persistent data system a mod will use the createFile function and pass in any arbitrary information, and will then be able to retrieve that data using the readFile function.

Functions

createFile

function createFile( _filename, _data )
// _filename is a string
// _data can be a null, string, integer, float, boolean, array, table (but not a BB class or BB object), or SerializationData instance

Creates a new persistent file with the given _filename containing a serialized representation of _data.

Filenames only need to be unique within a specific mod, so two different mods can have the same _filename without conflicts. This will overwrite existing files with that name.

readFile

function readFile( _filename )
// _filename is a string

Returns the data stored in a file using createFile with the specified _filename.

This data (except for any SerializationData it contains) should Globals#deepequals the data initially passed to createFile.

hasFile

function hasFile( _filename )
// _filename is a string

Return true if _filename exists as a persistent data store on the users machine, false otherwise.

getFiles

function getFiles()

Returns an array of strings with the names of the files created by your mod using createFile, these can then be read using readFile.

deleteFile

function deleteFile( _filename )
// _filename is a string

Permanently removes a file created by your mod with the given _filename.

Example

::Hooks.register("mod_my_mod", "1.0.0", "My Mod");
local mod = ::MSU.Class.Mod("mod_my_mod", "1.0.0", "My Mod");
local oldData = {
	i = 1,
	f = 3.14,
	s = "astring",
	b = true,
	n = null,
	a = [[null], {}, 2], // arbitrary nesting is permitted
	s = ::MSU.Class.SerializationData()
};
local serEmu = oldData.s.getSerializationEmulator() // if we want to serialize a bb object we have to use a SerializationEmulator.
local myItem = ::new("scripts/items/weapons/named/named_sword");
myItem.onSerialize(serEmu);
mod.PersistentData.createFile("MyFile", oldData);
local myFiles = mod.PersistentData.getFiles(); // myFiles should be ["MyFile"]
local readData = mod.PersistentData.readFile("MyFile"); // should normally be prefixed by a hasFile check
// to simplify an equality check we use ::MSU.deepEquals, but for that we need to first remove the instances and compare them separately.
local oldSerData = delete oldData.s;
local readSerData = delete readData.s;
assert(::MSU.deepEquals(oldData, readData));
// create a DeserializationEmulator from the SerializationData
local deserEmu = readSerData.getDeserializationEmulator();
// create a new item to deserialize into
local readItem = ::new("scripts/items/weapons/named/named_sword");
// deserialize the item
readItem.onDeserialize(deserEmu);
assert(::MSU.deepEquals(myItem.m, readItem.m));

Mod Settings and Keybinds

The Mod-Settings and Keybinds systems are integrated with PersistentData. If a Mod Setting or Keybind is changed, a command will automatically be printed to the log. This will include the ID of the mod the setting belongs to.

BBParser (Deprecated)

BBParser is deprecated as of 1.3.0 as it is superseded by the above functions For example, the user of Plan your Perks might want to keep his planned builds between campaigns. It relies on BBParser to read through the log.html file and find specific commands. Parsed commands will be saved in a Battle Brothers\data\mod_config\<modID>\<fileID>.nut file. For example: Battle Brothers\data\mod_config\mod_msu\ModSetting.nut

Writing commands

writeToLog

<Mod>.PersistentData.writeToLog( _fileID, _payload )

With this function, the modder can instruct the PersistentDataSystem to write a command to the log. _fileID is a string variable. _payload can be either a string, or an array of variables that will be transformed to strings.

_fileID can be any string. It refers to the type of command. It is used to identify commands for the purpose of reading them, as well as for instructing BBParser to treat the command in a specific way.

At this time, BBParser recognizes the following fileIDs:

  • ModSetting: Used automatically by the Mod Settings system.
  • Keybind: Used automatically by the Keybinds system.
  • String / *: Any ID that is not one of the above is treated as a String ID.

String ID

The String ID _payload parameter takes either a string, or an array of values of types string, boolean, integer, float that will be transformed to string. By default, any string setting will overwrite the settings file of that type. This will avoid the settings file filling up with functionally equivalent commands.

To demonstrate, here is a possible command:

<Mod>.PersistentData.writeToLog("Greeting", "this.logInfo('Welcome to my mod!')")

When this setting is being read, it will print 'Welcome to my mod!' to the log. Should you then add another command, such as

<Mod>.PersistentData.writeToLog("Greeting", "this.logInfo('Goodbye!');")

then only 'Goodbye!' will print to the log. To overwrite this behavior, add :APPEND to the _settingID:

<Mod>.PersistentData.writeToLog("Greeting:APPEND", "this.logInfo('Welcome to my mod!');")
<Mod>.PersistentData.writeToLog("Greeting:APPEND", "this.logInfo('Goodbye!');")

Now, both strings will be printed. :APPEND will be removed from the ID.

If you pass an array for _payload, each entry will be added to the file with a linebreak inbetween.

<Mod>.PersistentData.writeToLog("Test", ["array", "of", "strings"]);

will translate to

array
of
strings

Reading files

Every file can only be read and executed once per game start. This is a limitation of the include() function that is used to read and execute the files.

loadFile

<Mod>.PersistentData.loadFile( _fileID )

This will load and execute the file with the ID _fileID belonging to your mod.

loadAllFiles

<Mod>.PersistentData.loadAllFiles()

This will load and execute every file belonging to your mod.

BBParser Example

// this will write the command to the log
myMod.PersistentData.writeToLog("VerboseMode", "this.Const.AI.VerboseMode = true;");

// this will read and execute the command with ID `VerboseMode`
myMod.PersistentData.loadFile("VerboseMode");
// VerboseMode will now be set to 'true'
⚠️ **GitHub.com Fallback** ⚠️