- Home /
Hide variables in PlayerPrefs
My game uses the PlayerPrefs file to save important info of characters like health. The problem is that anybody can get into the file by going to the directory, and when they get there, they can edit the info to cheat and such. Is there a way to hide the variables in the PlayerPref's file so that people can't see the variable with the naked eye? If not, is there a way thats similar?
Answer by Eric5h5 · Apr 14, 2013 at 05:25 PM
You can use ArrayPrefs2 to save data with PlayerPrefs, where values are converted to bytes which are stored as strings using Base64. Technically they can still be edited, but would be much harder (for non-programmers anyway). Also, even if you're not actually using arrays, you can make a dummy array and hide the "real" value in it, which would further increase the difficulty.
Answer by janzdott · Apr 14, 2013 at 05:11 PM
If you want a good way of saving data and keeping players from cheating, I would recommend using binary serialization. It's very easy to set up, and creates a nice save file in binary, so players can't edit it. Heres one that I wrote.
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public static class BinarySerializer {
public static void serialize<T>(T data, string filePath) {
Stream stream = File.Open(filePath, FileMode.Create);
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Binder = new VersionDeserializationBinder();
binaryFormatter.Serialize(stream, data);
stream.Close();
}
public static T deserialize<T>(string filePath) {
try {
Stream stream = File.Open(filePath, FileMode.Open);
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Binder = new VersionDeserializationBinder();
T data = (T)binaryFormatter.Deserialize(stream);
stream.Close();
return data;
}
catch (FileNotFoundException exception) {
throw exception;
}
}
}
And this second class is just to make sure the save files can be loaded after unity recompiles scripts
using System.Runtime.Serialization;
using System.Reflection;
public class VersionDeserializationBinder : SerializationBinder {
public override System.Type BindToType(string assemblyName, string typeName) {
if (!string.IsNullOrEmpty(assemblyName) && !string.IsNullOrEmpty(typeName)) {
System.Type typeToDeserialize = null;
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().FullName;
typeToDeserialize = System.Type.GetType(string.Format("{0}, {1},", typeName, assemblyName));
return typeToDeserialize;
}
return null;
}
}
And heres an example class that can be serialized and deserialized.
using UnityEngine;
using System.Collections;
using System.Runtime.Serialization;
[System.Serializable()]
public class MySaveData : ISerializable {
public int savedInt = 0;
public MySaveData() {}
public MySaveData(SerializationInfo info, StreamingContext context) {
savedInt = (int)info.GetValue("savedInt", typeof(int));
}
public void GetObjectData(SerializationInfo info, StreamingContext context) {
info.AddValue("savedInt", savedInt);
}
}
And here is an example showing how to save and load a file
MySaveData saveData = new MySaveData();
saveData.savedInt = 12345;
string filePath = "blah/blah/blah"
//Saves saveData
BinarySerializer.serialize<MySaveData>(saveData, filePath);
//Load saveData
saveData = BinarySerializer.deserialize<MySaveData>(filePath);
Guessing you'd need to check for things compiled in first pass on Plugins for changes too though right (does your code support serializing those? Looks like perhaps it doesn't?) Na$$anonymous$$g shouldn't be strong in any case - so do you actually find it necessary to do this binding?
whydoidoit - I have never used the Plugins folder for anything, so I never thought about if it would make a difference or not. And yes its necessary to use the custom binder, otherwise a file cannot be deserialized after Unity has recompiled. It is not necessary for the final build. But you cannot test it in the editor without using the custom binder, since Unity recompiles every time you click the play button.
GameDude - I don't know JS, but shouldn't be hard at all to convert. Just gotta get rid of the generic stuff, which isn't needed. Or you could find an example on Google probably
So the Plugins and the Javascript assemblies are different and so the ExecutingAssembly stuff wouldn't work for that - normally you'd iterate across the assemblies in AppDomain.CurrentDomain.GetAssemblies() to try all of them. I'm confused as to why a recompile would cause a problem as the assemblies are not strongly named? Put it this way - I use it without a typename resolver without issue and without causing a problem of referencing one of the other assemblies.
I do seem to remember they accidentally made UnityEngine strongly named a version back - but that's been fixed again.
Hmm before I wrote this, I read somewhere that you needed the custom binder or it wouldn't work. When I get home, I'll comment out those lines and see if it still works!