Need Assist on Saving Data to Save File

Discuss modding questions and implementation details.
User avatar
TheLacus
Posts: 1059
Joined: Wed Sep 14, 2016 6:22 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by TheLacus » Tue Dec 17, 2019 3:49 pm

IHasModSaveData is an interface. You must created a class that implements this interface:

Code: Select all

public class Example : IHasModSaveData
{
}
Then you can pass an instance of this class to Mod instance. Tipically you use the main Monobehaviour (where the Init method is located) to load/save, but you can also use a different class. For example here a new instance of Example is created and assigned.

Code: Select all

mod.SaveDataInterface = new Example();
l3lessed i suggest you to invest some time into learning how OOP - and interfaces specifically - works in C#. It's really important in order to understand how a lot of things work. 😉
Mod System documentation - Learn how to create mods for Daggerfall Unity.
Modder Discussion - General help and discussion for the mod system.
Github Issues - Submit a bug report for the game, including the mod system.

l3lessed
Posts: 223
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by l3lessed » Tue Dec 17, 2019 4:59 pm

Okay, that makes sense, let me take a crack at it again.
My Skyrim Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 223
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by l3lessed » Tue Dec 17, 2019 7:46 pm

AS for OOP, I understand the theory behind it. I just suck at understanding the coding implementation, since I largely learned coding as a hobby.

This has been really helpful in understanding where am screwing it up though and getting a better grip on how OOP is implemented in the code itself. Funny thing, my first go around was the right approach, if I am understanding the above post. I just was wanting to break it into a separate script file, instead of its own class in the main script file.

Here is my current approach. As of now, it seems to work. I just need to define the values for the IHasModSaveData class through the outfit manager class, but, I believe I got it now. mod.savedinterface is accepting my class object without any errors, other than the undefined class vars. The one thing the threw me off, when I implement the example getsave object in the interface class using the example code from the documentation page, the line end semicolon in the object class NewSaveData() & GetSaveData() screws the whole script file code lines. If I remove the semicolon, like in my below code, it compiles fine. Am I misunderstanding the OOP implementation coding, yet again? :shock:

Code: Select all

namespace DaggerfallWorkshop.Game
{
    public class OutfitManager : MonoBehaviour
    {
    	public class MyModSaveData
        {
            public string Text;
            public int Items;
        }

        [FullSerializer.fsObject("v1")]
        public class Example : IHasModSaveData
        {
            public Type SaveDataType
            {
                get { return typeof(MyModSaveData); }
            }

            public object NewSaveData()
            {
                return new MyModSaveData
                {
                    Text = "Default text",
                    Items = 5
                };
            }

            public object GetSaveData()
            {
                return new MyModSaveData
                {
                    Text = text,
                    Items = items
                };
            }

            public void RestoreSaveData(object saveData)
            {
                var myModSaveData = (MyModSaveData)saveData;
                text = myModSaveData.Text;
                items = myModSaveData.Items;
            }
Then the outfitmanager monobehavior class kicks in under the interface class, and the init runs the mod interface to retrieve the data.

Code: Select all

        [Invoke(StateManager.StateTypes.Game, 1)]
        public static void Init2(IHasModSaveData IHasModSaveData)
        {
            Debug.Log("Loads saved data");
            mod.SaveDataInterface = new Example();
        }
Last edited by l3lessed on Tue Dec 17, 2019 8:18 pm, edited 1 time in total.
My Skyrim Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

User avatar
Hazelnut
Posts: 1996
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by Hazelnut » Tue Dec 17, 2019 8:16 pm

I've not really been following this discussion, but looking at the last example I have a query. Does the implementation of the IHasModSaveData interface need to be a nested class or could the OutfitManager mono behaviour implement it?
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

l3lessed
Posts: 223
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by l3lessed » Tue Dec 17, 2019 10:14 pm

If I'm understanding it correctly, it just needs to be its own class. It can be implemented within the monobehavior as a sub-class of the main mod script class or it can be implemented as a separate class. If I'm understanding lucas and the documents correctly, all the matters is the interface being in its own class, so it can push and pull the value objects between the mod class pulled from the mod manager and the save data itself.

Once it is setup in its own class, with the proper class objects and values, it merely needs linked via the mod.SaveDataInterface to either push or pull the values between the save file itself and the mod instance.

The only thing I am worried about, is I did have to remove the line end colon in the interface object methods, as seen in my code. In the original documentation, there is a line ending semicolon in the object method and the object class for GetSaveData that screws my script all up.
My Skyrim Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

User avatar
Hazelnut
Posts: 1996
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by Hazelnut » Tue Dec 17, 2019 10:28 pm

Hmm, I'm failing to see why the mono class can't just implement the interface. Maybe TheLacus will clarify for me... it's not that important as I don't plan to do this on my mods yet, just curious.

Also the issue you mention with the semi-colons, that's just how interfaces are defined isn't it? They specify the method signatures that comprise the interface, without implementing code (else it would be a concrete or abstract class not an interface) and each is ended with a ; like all statements are.
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

l3lessed
Posts: 223
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by l3lessed » Tue Dec 17, 2019 10:43 pm

It's the other way around. You need to define an implementation for the interface, meaning that you must write their content; the game will calls these methods at the appropriate times. For example when GetSaveData() is called, you must return your custom data (i.e. a class defined in your mod) casted to object. When RestoreSaveData() is called, you receive an object as argument and you can cast it back to your custom type.

Any class can implement this interface, even your main MonoBehaviour.
It can be. I'm just screwing up the OOP language, since I have very little hands on programming with OOP based language.

To implement it in your monobehavior, you have to define it, like so? At this point, the OutfitManager class should have the IHasModSaveData interface implemented in it, correct?

Code: Select all

    public class OutfitManager : IHasModSaveData
    {
Back to scrapping the forced in class interface, and trying to call it through the mono behavior again.
My Skyrim Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

User avatar
Hazelnut
Posts: 1996
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by Hazelnut » Tue Dec 17, 2019 11:08 pm

I think it would be like this, so it extends MonoBehaviour and implements IHasModSaveData...

Code: Select all

    public class OutfitManager : MonoBehaviour, IHasModSaveData
    {
        public Type SaveDataType { get; }

        public object NewSaveData() { return null; }

        public object GetSaveData() { return null; }

        public void RestoreSaveData(object saveData) {}
...      
Also included the method implementation stubs that will allow it to compile. Obviously you need to code the implementations. :)
See my mod code for examples of how to change various aspects of DFU: https://github.com/ajrb/dfunity-mods

User avatar
TheLacus
Posts: 1059
Joined: Wed Sep 14, 2016 6:22 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by TheLacus » Tue Dec 17, 2019 11:36 pm

Any class can implement the interface, it doesn't need to be nested. What Hazelnut suggested is pretty much what i do for my Harvestable Crops mod. Here's a snippet with the relevant parts, maybe is useful to see a complete example ;)

Code: Select all

namespace HarvestableCrops
{
    #region Types

    [FullSerializer.fsObject("v1")]
    public class HarvestableCropsSaveData
    {
        public int Progress;
        public Dictionary<HarvestedCrop, int> HarvestedCrops;
    }

    #endregion

    /// <summary>
    /// Make crops harvestable.
    /// </summary>
    public class HarvestableCrops : MonoBehaviour, IHasModSaveData
    {
        #region Fields

        /// <summary>
        /// Progress of harvesting skill.
        /// </summary>
        int progress;

        /// <summary>
        /// Crops which are currently harvested. Values is harvest day from zero.
        /// </summary>
        Dictionary<HarvestedCrop, int> harvestedCrops = new Dictionary<HarvestedCrop, int>();

        #endregion

        #region Properties

        public Type SaveDataType
        {
            get { return typeof(HarvestableCropsSaveData); }
        }

        #endregion

        #region Unity

        [Invoke(StateManager.StateTypes.Start, 0)]
        public static void Init(InitParams initParams)
        {
            GameObject go = new GameObject("HarvestableCrops");
            instance = go.AddComponent<HarvestableCrops>();

            mod = initParams.Mod;
            mod.SaveDataInterface = instance;
            mod.LoadSettingsCallback = (settings, _) => settings.Deserialize("Options", ref instance);
            mod.LoadSettings();

            mod.IsReady = true;
        }

        #endregion

        #region Public Methods

        public object NewSaveData()
        {
            return new HarvestableCropsSaveData {
                Progress = 0,
                HarvestedCrops = new Dictionary<HarvestedCrop, int>()
            };
        }

        public object GetSaveData()
        {
            if (harvestedCrops.Count == 0)
                return null;

            return new HarvestableCropsSaveData {
                Progress = progress,
                HarvestedCrops = harvestedCrops
            };
        }

        public void RestoreSaveData(object saveData)
        {
            var harvestableCropsSaveData = (HarvestableCropsSaveData)saveData;
            progress = Mathf.Clamp(harvestableCropsSaveData.Progress, 0, maxProgress);
            harvestedCrops = harvestableCropsSaveData.HarvestedCrops;
        }

        #endregion
    }
}
Mod System documentation - Learn how to create mods for Daggerfall Unity.
Modder Discussion - General help and discussion for the mod system.
Github Issues - Submit a bug report for the game, including the mod system.

l3lessed
Posts: 223
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: Need Assist on Saving Data to Save File

Post by l3lessed » Tue Dec 17, 2019 11:51 pm

Thanks so much hazel for clarifying this. OOP language should not be this hard for me, but I just can't seem to fully understand the implementation.

Here is where I am at. This code keeps my compiler running just fine.

Code: Select all

    public class OutfitManager : IHasModSaveData
    {
        public Type SaveDataType { get { return typeof(MyModSaveData); } }

        public object NewSaveData() { return null; }

        public object GetSaveData() { return null; }

        public void RestoreSaveData(object saveData)
        {
            var myModSaveData = (MyModSaveData)saveData;
            text = myModSaveData.Text;
            items = myModSaveData.Items;
        }
The public objects are the things that have been giving me issues this whole time. When I get to setting them up, I get the issue with the semicolon throwing off the whole compilers line reading. What am I misunderstanding about how these objects are suppose to be setup within a class?

Heres the code with the bad language.

Code: Select all

        public object GetSaveData() 
        { 
	    return new MyModSaveData
    		{
       		 Text = text,
       		 Items = items;
    		};
        }
Both objects throw off my line compiling when I use the documentation setup shown on the webpage. I know I'm probably just setting up the objects in the wrong area or class or using the wrong casting, but I keep pounding by head into a wall with casting the objects.

There is also an issue of turning the class into an interface when you want to try to add the class component to the mod. The interface changes the class type, and as a result, kicks out an error with this code, when it didn't before. Again, pretty sure its improper OOPS language. I need to convert it back into the proper object type.

Code: Select all

            //just an example of how to add a mono-behavior to a scene.
            GameObject outfitGo = new GameObject("outfit");
            OutfitManager outfit = outfitGo.AddComponent<OutfitManager>();
Severity Code Description Project File Line Suppression State
Error CS0311 The type 'DaggerfallWorkshop.Game.OutfitManager' cannot be used as type parameter 'T' in the generic type or method 'GameObject.AddComponent<T>()'. There is no implicit reference conversion from 'DaggerfallWorkshop.Game.OutfitManager' to 'UnityEngine.Component'. Assembly-CSharp C:\Games\daggerfall-unity-master 10.13\Assets\Scripts\OutfitManager.cs 130 Active
My Skyrim Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

Post Reply