0
\$\begingroup\$

I am saving a list of ScriptableObjects in a JSON file using NewtonSoft JSON, the problem being that the following error message is thrown when it tries to save:

NotSupportedException: rigidbody property has been deprecated(wrapper dynamic-method) UnityEngine.GameObject.Getrigidbody(object)

My code for saving and loading:

private static readonly string SaveFileFolder = Application.persistentDataPath + "/Saves/";

    public static void Save(SaveData data)
    {
        if(Directory.Exists(SaveFileFolder) == false)
        {
            Directory.CreateDirectory(SaveFileFolder);
        }

        File.WriteAllText(SaveFileFolder + "Data.sav", JsonConvert.SerializeObject(data));
    }

    public static SaveData Load()
    {
        string _savePath = SaveFileFolder + "Data.sav";

        if(File.Exists(_savePath) == true)
        {
            return(JsonConvert.DeserializeObject<SaveData>(File.ReadAllText(_savePath)));
        }
        else
        {
            Debug.LogWarning("SaveFile could not be found in: " + _savePath);
        }
    }

    [System.Serializable]
    public class SaveData
    {
        [SerializeField] public GlobalGameStatistics.ProgressData ProgressData = new GlobalGameStatistics.ProgressData();
        [SerializeField] public GlobalGameStatistics.StatisticsData StatisticsData = new GlobalGameStatistics.StatisticsData();

        public SaveData(SaveData data)
        {
            ProgressData = data.ProgressData;
            StatisticsData = data.StatisticsData;
        }

        public SaveData(GlobalGameStatistics.ProgressData progress, GlobalGameStatistics.StatisticsData statistics)
        {
            ProgressData = progress;
            StatisticsData = statistics;
        }
    }

The ScriptableObject I'm trying to save:

[CreateAssetMenu(fileName="MissionTemplate", menuName="Game/Mission Template"), Serializable]
public class MissionTemplate : ScriptableObject
{
    [Header("Settings:")]
    public float time;
    public MapTemplate map;
    public PopulationTemplate population;
    public TargetTemplate target;
    public StoryMessage[] messages;

    #region Classes

    [Serializable]
    public class StoryMessage
    {
        [HorizontalGroup("Header")]
        public string headline;
        [HorizontalGroup("Header")]
        public string sender;
        [TextArea(8, 8)] public string text;
        [HideInInspector] public bool read;
    }

    #endregion
}

The GlobalGameStatistics.Progress class is where a list of MissionTemplates is contained which should be saved.

\$\endgroup\$
2
  • \$\begingroup\$ Questions about debugging a problem in your game should include a Minimal Complete Verifiable Example. That means a reader should be able to paste the code, and it should compile, run, and demonstrate the issue, without them needing to ask any follow-up questions or stub-in code you didn't provide. Right now this code depends on several types like MapTemplate, PopulationTemplate, etc. that you have not provided. You should slim this down to a synthetic example that shows the kinds of serialization operations you need to support without the full dependency complexity of your whole game. \$\endgroup\$
    – DMGregory
    Commented Jul 24, 2023 at 23:52
  • \$\begingroup\$ I'd create a MissionTemplateData, put all the fields in that and serialze/deserialize that -- ScriptableObjects aren't going to work with JSON serialization. \$\endgroup\$
    – Dustin_00
    Commented Jul 25, 2023 at 1:33

1 Answer 1

0
\$\begingroup\$

Here's what I use (swap in JSON for binary serialization if you really must):

public static class DataStorage
{
    static readonly string Data_Extension = ".GameData";

    public static string Save<T>(T obj, string savePath, string key)
    {
        string path = Path.Combine(Application.persistentDataPath, savePath);
        Directory.CreateDirectory(path);
        path = Path.Combine(path, $"{key}{Data_Extension}");
        File.Delete(path);

        Debug.Log($"Saving: {path}");
        using (FileStream stream = new FileStream(path, FileMode.OpenOrCreate)) // Overwrite
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, obj);
        }

        return path; // this is here for test automation and/or debugging help
    }

    public static T Load<T>(string savePath, string key)
    {
        string path = Path.Combine(Application.persistentDataPath, savePath, $"{key}{Data_Extension}");
        T loadObject = default;
        if (!File.Exists(path)) return loadObject;

        Debug.Log($"Loading: {path}");
        using (FileStream stream = new FileStream(path, FileMode.Open))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            loadObject = (T) formatter.Deserialize(stream);
        }

        return loadObject;
    }
}

The data and ScriptableObject:

public class GameStateData
{
    public GameSource GameSource;
    public States State;
    public Vector3Int PlayerMapLocation;
}

public class GameStateSO : ScriptableObject
{
    public GameStateData Data = new GameStateData();
}

Then I call it with:

DataStorage.Save(gameStateSO.Data, savePath, "GameStateSO");

gameStateSO.Data = DataStorage.Load<GameStateData>(savePath, "GameStateSO");
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Careful, BinaryFormatter is insecure and can be used to execute arbitrary code. Microsoft says apps should stop using it. This might not seem like a big deal; your game might not have any sensitive/secret stuff in it. But if an attacker can convince one of your players to load a malicious save file ("want a save with 100% completion and the ultimate weapon unlocked? Here you go!") then they can potentially use this pathway to install malware on the player's computer, or leak your player's secrets. \$\endgroup\$
    – DMGregory
    Commented Jul 25, 2023 at 3:37
  • \$\begingroup\$ I'd call that list of impacted versions kind of a big deal if they're calling it by design/won't fix. Ugh. Thanks for the heads-up! \$\endgroup\$
    – Dustin_00
    Commented Jul 26, 2023 at 2:05
  • \$\begingroup\$ Unfortunately, BinaryFormatter dates back to a time when deserialization vulnerabilities were not so well understood by the global community — it wasn't just an isolated mistake. And that flaw is baked into the design of the API. It's not "won't fix" so much as "cannot fix" — no code conforming to the same API will ever be safe. They've provided other (de)serialization tools to serve secure implementations, but it's sadly not a drop-in replacement. \$\endgroup\$
    – DMGregory
    Commented Jul 26, 2023 at 2:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .