Page 1 of 1

Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 3:53 am
by NotVeryProfessional
I have a custom saver that for reasons I can't figure out is applied TWICE to my scene, with obvious not desireable results.

The saver is definitely present only once in the scene and in the save file, and the call stack is directly from SaveSystem:

Code: Select all

BlackForest.VillageSaver:ApplyData (string) (at Assets/0_game/Scripts/Savers/VillageSaver.cs:119)
PixelCrushers.SaveSystem:ApplySavedGameData (PixelCrushers.SavedGameData) (at Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs:674)
PixelCrushers.SaveSystem/<LoadSceneCoroutine>d__111:MoveNext () (at Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs:785)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&) (at /Users/bokken/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:189)
LoadScene also definitely gets called only once. Everything else appears to work (SaveSystem loads the correct scene, applies all other savers correctly, etc.)

I'm staring at the source and can't for the life of me figure out why it is calling ApplyData() two times. The two calls have the same timestamp, but from my debug output I see that the first one is applied entirely before the second one is called, so they're called one after the other, not in parallel.

Also, the debug output from line 770 in SaveSystem.cs:

Code: Select all

            if (debug) Debug.Log("Save System: Loading scene " + savedGameData.sceneName +
appears only once in my console. That one leads up to the

Code: Select all

ApplySavedGameData(savedGameData);
call, so that's not duplicated, either.

I suspect the culprit somewhere near Line 674. For whatever reason the SaveSystem thinks it has this saver twice, maybe?


My custom saver is set up with Append Saver Type true and Save Across Scene Changes true.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 4:19 am
by NotVeryProfessional
Adding debug outpot to SaveSystem like this:

Code: Select all

        /// <summary>
        /// Applies the saved game data to the savers in the current scene.
        /// </summary>
        /// <param name="savedGameData">Saved game data.</param>
        public static void ApplySavedGameData(SavedGameData savedGameData)
        {
            if (savedGameData == null) return;
	        m_savedGameData = savedGameData;
	        Debug.Log(m_savers);
	        foreach (Saver s in m_savers) {
	        	Debug.Log("saver: "+s+" with key "+s.key);
	        }
            if (m_savers.Count <= 0) return;
            for (int i = m_savers.Count - 1; i >= 0; i--) // A saver may remove itself from list during apply.
            {
                try
                {
                    if (0 <= i && i < m_savers.Count)
                    {
	                    var saver = m_savers[i];
	                    Debug.Log("apply data to saver "+saver+" at position "+i+" with key "+saver.key);
                        if (saver != null) saver.ApplyData(savedGameData.GetData(saver.key));
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
            }
            saveDataApplied();
        }

results in revealing that SaveSystem finds my saver twice - at position 20 and 25. However in the first debug (the list) it appears only once. So my conclusion is that the issue is in the for loop after that. I do have some gameobjects that could possibly despawn immediately after init, taking the saver with them. Looking at the debug output it looks like everything between positions 31 and 25 gets applied twice.

That's as far as I can dig in. The deeper workings of the SaveSystem elude me further.

Here's the console from that:
Bildschirmfoto 2021-12-23 um 10.20.14.png
Bildschirmfoto 2021-12-23 um 10.20.14.png (251.84 KiB) Viewed 701 times

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 8:25 am
by Tony Li
Hi,

Did you tick your saver's 'Restore On Start' checkbox? If so, it will apply data in Start() as well as when the Save System would normally apply data on a scene change or saved game load.

If that doesn't help, can you confirm that the SaveSystem.m_savers list has two copies of your custom saver? If so, does your scene perhaps have two instances of your custom saver component?

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 1:49 pm
by NotVeryProfessional
Neither of those. "Restore on Start" is false, I've verified the saver is in the scene only once, and when I Debug.Log the savers list just before the loop, it contains this saver only once.


I found the line that makes the difference:

Code: Select all

		public override void ApplyData(string m_data) {
			Debug.LogError("VillageSaver starting up...", gameObject);
			if (string.IsNullOrEmpty(m_data)) return;
			var villageData = SaveSystem.Deserialize<VillageData>(m_data);
			if (villageData == null) return;

			// clear out the village, so we don't get duplicates
			foreach (Transform child in transform) {
				BuildingController BC = child.GetComponent<BuildingController>();
				if (BC != null) {
					//Destroy(child.gameObject);
				}
			}
That Destroy is the culprit. If I disable it as above, the saver gets applied only once. If I enable it, twice.

What it's doing is this: I have essentially and RTS game with a pre-defined village to start with. But all buildings can be destroyed and new ones built. So instead of tracking the original buildings differently than newly built ones, I just save all buildings (original or new) and on load wipe out the village and put all buildings from the save.

This actually works well. Except for it all happening twice. :-)

Buildings have their own savers to save their internal state. So this deleting them inside here is apparently throwing things off.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 1:55 pm
by NotVeryProfessional
Yes, it's the list.

if I insert

Code: Select all

	        List<Saver> copied_savers = new List<Saver>(m_savers);
at about line 670 in SaveSystem.cs and then operate on that, everything works.

So the problem indeed is that it doesn't handle the list being modified properly. Is there a reason I don't see against iterating over a copy of the savers list?

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 2:37 pm
by Tony Li
Okay, you can count SaveSystem iterating over a copy of the savers list. Currently it allows any saver to unregister itself in the ApplyData loop but not destroy+unregister other savers. I did some performance checks, and the change is basically negligible in terms of GC compared to the GC cost of Unity simply loading any scene.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 3:56 pm
by NotVeryProfessional
Excellent. Thanks.

Is this going to be an option in future versions or do I have to maintain my own fork? (well, one function fork, but still) - just so I know how to deal with updates.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Thu Dec 23, 2021 3:57 pm
by Tony Li
It will be the default behavior of the SaveSystem script.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Sat Dec 25, 2021 2:27 am
by NotVeryProfessional
Brilliant, as always. Dialogue System is an asset I'm always happy to use, and your relation to your customers is something that large companies could learn from.

Re: Save System: Custom saver applied twice for no apparent reason

Posted: Sat Dec 25, 2021 8:30 am
by Tony Li
Thank you! :-)