Saving mod data/settings - Thoughts

Users who are viewing this thread

Alright, a bit of necro here ...

I used this methods for a long time in my mod UsefulCompanions to store data about the auto buy food features i implemented in it, and it worked very fine for more than a year until i got people recently complaining the feature was resetting back to default values on each game loading (default is off).

I was using this simple method
Code:
public override void SyncData(IDataStore dataStore)
        {
            //resuply data
            dataStore.SyncData("UC_resuply_active", ref qd.active);
            dataStore.SyncData("UC_resuply_day", ref qd.sDays);
            dataStore.SyncData("UC_resuply_gold", ref qd.goldMax);
            dataStore.SyncData("UC_resuply_moral", ref qd.improveM);
        }
and today when i put a break point in there i can see the point is correctly reached on saving but never on loading a game.

I also checked how the campaign was doing it now and spotted a change in the SyncData method used, it was now with a type (<T>) so i changed it all to be like in TW code now, but surprisingly i don't have any better result. All seem to be fine on saving but when loaded it's simply skipped.

syncdata.png


Here is a screen when actually saving, the dataStore object seem just like expected but apparently have no effect. I dug a bit in the code and the only method using campaignBehavior.SyncData(IDataStore) is actually TaleWorlds.CampaignSystem.CampaignBehaviorDataStore in the methods internal void SaveBehaviorData(CampaignBehaviorBase campaignBehavior) and internal void LoadBehaviorData(CampaignBehaviorBase campaignBehavior) (and the dirty internal keyword here ...).

And i'm a bit clueless from here as to why it's not working anymore.
Do you have multiple campaignbehaviors with the name UCDialogues, or are you using custom stringid for your behavior?

Additionally, if you send me your mod, we can debug it.
 
Hey, no i only have a single UCDialogues behavior that i use to load all the dialogues i want to add in games. Not sure what you mean about custom stringID but if you want the version compatible with 1.7.1, here it is, starting to be a bit old as i do not update before i could solve this issue.

I don't know if you needed the source code or the complied version, if you need the source i'll make a zip if needed.
 
Hey, no i only have a single UCDialogues behavior that i use to load all the dialogues i want to add in games. Not sure what you mean about custom stringID but if you want the version compatible with 1.7.1, here it is, starting to be a bit old as i do not update before i could solve this issue.

I don't know if you needed the source code or the complied version, if you need the source i'll make a zip if needed.
We tried to debug it and it seemed fine. Our suggestion is to use stringid for your behaviours, something like this:
Code:
public class FooCampaignBehavior  : CampaignBehaviorBase
    {
        public FooCampaignBehavior() :base("UNIQUE_NAME_FooCampaignBehavior_12345") { }
        public override void RegisterEvents() { }
        public override void SyncData(IDataStore dataStore) { }
    }
 
No change for me. When you say it seemed fine that mean you couldn't find an error in the code or it was running fine in the debugger?

Even with your change i can't get back the values from my savegame, they are not stored in or not loaded, but all i put inside simply vanishes. (And was working perfectly before 1.7.1)

I'd love to debug it myself but i don't have the tools. I can't check in the saves if my variables makes it there as it's in binary format and can't read it properly. I can't either really check your code as DNspy do not give me values, but only allows me to put break points.
 
Last edited:
Alright so trying to continue debugging where it's being messed up.

Went to CampaignBehaviorManager witch manages all the data collected in the various behaviors with two methods OnBeforeSave() and OnGameLoaded() obviously the names are self explanatory.

On before load checks every behavior in the game (They are stored in a dictionary) and call for each behavior in the dic, CampaignBehaviorDataStore.SaveBehaviorData(campaignBehavior). Here my behavior is in the dic and the method is correctly called on him. On a comment, putting a string id as you asked is a bit useless as it already have a unique name including the prefix of my mod. So unless you start to call all your behaviors UCsomething it's unlikely i'll have any issues with this :wink:

So after a bit of trying i found my behavior was the #192 in the list making it a lot easier to check what was saved or not. And at save my values are correctly collected. And to my biggest surprise they were also correctly collected on loading! Checking the values with a break in OnGameLoaded() with DNspy got me the values i saved correctly with my old methods pre 1.7.1.
So all is fine and correctly loaded in CampaignBehaviorDataStore but the issue is my behaviors are completely missing in private List<CampaignBehaviorBase> _campaignBehaviors; and since you make a foreach on this list to run syncdata that explains why my syncdata are constantly skipped.

Now that i have the root of the issue i focus my attention on this list ... SetBehaviors(IEnumerable<CampaignBehaviorBase> inputComponents) seem to also miss them completely and list only basegame behaviors.
So loading a game, no matter how, _campaignBehaviors shows 189 items and saving right behind it shows 193 items and ...from 194 to 379 (so 185 more) null values ...
ghostvalues.jpg

I'm not an absolute expert at C# coding but this really looks like something that shouldn't be there.

So why on loading a game my behaviors (All of them) are completely ignored and on saving, using the same class of yours (not you personally but TW), the same values, they are correctly listed. Now i know the issue is on your side and you really changed something more than only the save system with 1.7.1.


Edit : So apparently, the game load my save before i actually use AddBehavior(CampaignBehaviorBase campaignBehavior), seriously ... So now loading happens earlier in the loading process and will mess a bit more with all it's phases. When will we have a proper documentation about things like this? When will you correctly communicate on so sensible changes? I mean come on, it's how to set up a mod properly and we have not a single word about how to use it properly.
 
Last edited:
I'm trying to save and I keep getting the "Save Failed! Cannot create save data." issue.
I'm using the following types:

  • List<string>
  • Dictionary<Hero, CampaignTime>
  • Dictionary<string, Hero>
  • Dictionary<string, string>
  • Dictionary<string,int>
  • Dictionary<string, MBEquipmentRoster>
  • Dictionary<string, LocationCharacter>
So I'm not using any non-native or custom types, any ideas?

Hate to be a thread necromancer, but the save system should be straightforward for types that are already defined by Bannerlord no?
 
This might help

Thanks for that, though in that video he mentions that you only need to perform those steps if they're not an existing saveable type, I had already looked at the documentation available exactly the same article he suggested to look at, that's why I'm confused, I'm not defining a new structure, object or anything like that, I'm trying to save dictionaries containing already existing datatypes. I've used this method of saving data to saves before in other parts of my mod without issue, it is fairly frustrating that Taleworlds hasn't decided to document their own code (despite all of their games being kept alive by modders) and that site where it's a "wiki" style documentation site is bare bones at best.
 
Thanks for that, though in that video he mentions that you only need to perform those steps if they're not an existing saveable type, I had already looked at the documentation available exactly the same article he suggested to look at, that's why I'm confused, I'm not defining a new structure, object or anything like that, I'm trying to save dictionaries containing already existing datatypes. I've used this method of saving data to saves before in other parts of my mod without issue, it is fairly frustrating that Taleworlds hasn't decided to document their own code (despite all of their games being kept alive by modders) and that site where it's a "wiki" style documentation site is bare bones at best.
TW have only provided API documentation, beyond that you have to look at other modders‘ open source code https://forums.taleworlds.com/index.php?threads/source-code-for-bannerlord-mods.448829/
 
I've laid out my structure like this:
Code:
internal class CustomSavedTypeDefiner : SaveableTypeDefiner
{
    // CR32 Hash based on mod name : 7c42d641
    public CustomSavedTypeDefiner() : base(0x7c42d6) { }
    protected override void DefineClassTypes()
    {
        AddClassDefinition(typeof(Dictionary<string,Hero>), 1);
        AddClassDefinition(typeof(Dictionary<string,MBEquipmentRoster>), 2);
        AddClassDefinition(typeof(Dictionary<string,LocationCharacter>), 3);       
    }
    protected override void DefineContainerDefinitions()
    {
        ConstructContainerDefinition(typeof(Dictionary<string, Hero>));
        ConstructContainerDefinition(typeof(Dictionary<string, MBEquipmentRoster>));
        ConstructContainerDefinition(typeof(Dictionary<string, LocationCharacter>));           
    }
}

And I've added [SaveableField(1)] etc. to the fields associated with the declared types.
And I'm still getting the same error, unable to save the game.
Isn't there anyway to get more specific error than "save failed"? Because that message doesn't help me isolate the issue.
 
I've laid out my structure like this:
Code:
internal class CustomSavedTypeDefiner : SaveableTypeDefiner
{
    // CR32 Hash based on mod name : 7c42d641
    public CustomSavedTypeDefiner() : base(0x7c42d6) { }
    protected override void DefineClassTypes()
    {
        AddClassDefinition(typeof(Dictionary<string,Hero>), 1);
        AddClassDefinition(typeof(Dictionary<string,MBEquipmentRoster>), 2);
        AddClassDefinition(typeof(Dictionary<string,LocationCharacter>), 3);     
    }
    protected override void DefineContainerDefinitions()
    {
        ConstructContainerDefinition(typeof(Dictionary<string, Hero>));
        ConstructContainerDefinition(typeof(Dictionary<string, MBEquipmentRoster>));
        ConstructContainerDefinition(typeof(Dictionary<string, LocationCharacter>));         
    }
}

And I've added [SaveableField(1)] etc. to the fields associated with the declared types.
And I'm still getting the same error, unable to save the game.
Isn't there anyway to get more specific error than "save failed"? Because that message doesn't help me isolate the issue.
You could try BetterExceptionWindow https://www.nexusmods.com/mountandblade2bannerlord/mods/404 or use Dnspy for debugging
or you could ask in the #Bl-coding channel of the Mount & Blade Modding Discord - someone might know/help.
 
Back
Top Bottom