Important Update: This version of the Json Saving System is Outdated
Please follow the completed tutorial here: SavingSystem
-------------------------------------------------------------------------
Over the last month or so, I’ve been working on a replacement for the BinaryFormatter we currently use in our saving system. As some may be aware, Microsoft has officially declared the BinaryFormatter unsafe as there is a potential for an exploit that could allow a hacker to intercept a save file, inject data, and actually instantiate the hacker’s own classes into our project.
This thread will serve as a replacement model for the existing saving system. It is probably best read on my repo/wiki here: Wiki · Brian K. Trotter / RPGMods · GitLab
Why Convert?
When the RPG Course Saving System was introduced in the first course, Core Combat, we used the BinaryFormatter to serialize and deserialize the needed fields. Unfortunately, Microsoft recently announced that using the BinaryFormatter was not safe, and recommends that it no longer be used. This issue presents a problem for companies around the globe who used this system not knowing the potential threats. I won’t go into the specific threats, and to be clear, while we’re in the prototyping stage (up until we release games to the public using this system) there is little to no risk. It’s when those save files start going over the internet that the trust level declines, and the risk of a hack that can compromise your system could be introduced. Bottom line ***Don’t Panic!*** All of that being said, I’m presenting some modifications to the saving system that will allow us to save in the Json format. As a side effect of teaching how to convert the system, I’ll even be leaving the original saving system more or less in place (you’ll be able to deactivate it and remove it easily once we’re done).
Getting Started
You may want to start by learning how the Saving System works. This is covered in depth in the Core Combat Course, both with a “Here’s an asset Pack” style approach and a “Let’s build this from the ground up approach”. If you’re starting in one of our other courses, you should review [How The Saving System Works](SavingSystemPrimer)
Installing Json.NET
While Unity does come with some rudimentary Json serialization, it’s not sufficient to keep up with the demands of our Saving System. For starters, it doesn’t support Dictionaries, let alone what are essentially nested Dictionaries.
Fortunately, there is another solution. There is a package in the Asset store called Json.NET for Unity. If you’re not familiar with Json.NET, it is a very robust serialization system that is more than powerful enough for our needs. It even works with IC2CPP in Android and iOS. Best of all, it’s free!
Follow this link to the Unity Asset Store Listing and add it to your assets. Then you can install it from the Package Manager in your project under Packages: My Assets.
For this tutorial, I’m going to create a new interface IJsonSaveable, so that at least for now, we can keep the original code intact. At the end of the tutorial, you’ll be able to remove the original save code and simply progress with the new Saving System.
IJsonSaveable
using Newtonsoft.Json.Linq;
namespace GameDevTV.Saving
{
public interface IJsonSaveable
{
public JToken CaptureAsToken();
public void RestoreFromToken(JToken state);
}
}
This is the Interface we will be using to replace the CaptureState/RestoreState from the original save system. You can, if you wish, simply edit ISaveable directly and replace the object in CaptureState and RestoreState with JToken. This will, of course, mean you’ll need to change everything at once to compile the code, which is why I’m taking this approach.
JToken is a special class that can serve the same way as Object for passing information around. Essentially, you can serialize the state to a JToken by using JToken.FromObject(T item), and you can convert it back by using the method ToObject(). I’ll demonstrate this now.
So let’s start with our first IJsonSaveable, we’ll start with Mover.cs. Here’s the original code in Mover.cs
public object CaptureState()
{
return new SerializableVector3(transform.position);
}
public void RestoreState(object state)
{
SerializableVector3 position = (SerializableVector3)state;
navMeshAgent.enabled = false;
transform.position = position.ToVector();
navMeshAgent.enabled = true;
GetComponent<ActionScheduler>().CancelCurrentAction();
}
SerializableVector3 is a special class that was created for the purpose of capturing and restoring Vectors. This is because for the BinaryFormatter, Vector3 is not Serializable. Fortunately, Json.Net has no such trouble. You can directly convert a Vector3 to and from a JToken.
Here’s the code for our IJsonSaveable classes. Be sure to add ,IJsonSaveable
to your class declaration (right after ,ISaveable
), and to add using NewtonSoft.Json.Linq;
to your usings clauses.
// this is the added using declaration
using NewtonSoft.Json.Linq;
//this is the new class declaration
public class Mover : MonoBehavior, IAction, ISaveable, IJsonSaveable
// Add these classes to fulfill the IJsonSaveable
public JToken CaptureAsToken()
{
return JToken.FromObject(transform.position);
}
public void RestoreFromToken(JToken state)
{
navMeshAgent.enabled=false;
transform.position=state.ToObject<Vector3>();
navMeshAgent.enabled=true;
GetComponent<ActionScheduler>().CancelCurrentAction();
}
So for CaptureAsToken, we’re simply returning the position as a JToken… Since JToken can manage a Vector3, it’s just a simple matter of using the JToken static method FromObject().
For RestorFromToken, all we need to do is convert the JToken back into a Vector3 .
Ok, time for your first challenge. Head on over to Health.cs, and take a look at the CaptureState and RestoreState. Add IJsonSaveable to the class declaration, the using clause, and implement the the CaptureAsToken() and RestoreFromToken() methods.