ID:154969
 
Basically I'm trying to figure out the best way to do a save file given the following situation:

I want savefiles to be saved to the person's key, so they can always load a few variables no matter where they play from, or who is hosting the game.

Also another big thing is I don't want these files to be tampered with. How might I go about this?


Also not to mention savefiles are another weakpoint of mine, and I don't even really know how to work with even the most basic of this.
Speedro wrote:
Basically I'm trying to figure out the best way to do a save file given the following situation:

I want savefiles to be saved to the person's key, so they can always load a few variables no matter where they play from, or who is hosting the game.

Import() and Export().

Also another big thing is I don't want these files to be tampered with. How might I go about this?

A Hash system using md5() proc.
In response to Hashir
This is my attempt, which doesn't work

mob
var
infostring = 0
admin
verb
Save_Data()
var/savefile/F = new("S")
F << infostring
client.Export(F)
src << "Saved data: [infostring]"


Load_Data()
var/L = client.Import()
if(L)
infostring = L
src << "Loaded: [L]"



Add_infostring()
world << "[infostring++]"
In response to Speedro
Speedro wrote:
This is my attempt, which doesn't work

Well, your saving is about right, except for that random argument in new(). For the loading, you need to create a /savefile object from the return value of Import().

The reference has a great example on this.

As for making it safer from tampering, the typical method is to store a hash along with the data, which is a unique string generated based on the data and a "salt". The salt is some constant random data that you use to obfuscate your hash, so someone else can't guess how to create their own to match the data they want to insert.

mob
var
infostring = 0
admin
verb
Save_Data()
var/savefile/F = new()
F["value"] << infostring // Save the data
F["hash"] << getHash(infostring) // Save a hash of the data
client.Export(F)
src << "Saved data: [infostring]"


Load_Data()
var/client_file = client.Import()
if(client_file)
var/savefile/F = new(client_file) //open it as a savefile
var/value,hash
F["value"] >> value
F["hash"] >> hash
if(hash != getHash(value)) // If the hashes don't match, then someone altered the file!
world<<"SAVEFILE TAMPERED WITH!"
return

infostring = value
src << "Loaded: [value]"
else
src << "No file to load"


Add_infostring()
world << "[++infostring]"

proc/getHash(value)
// This process should generate a random string based on the value and a "salt"
// A salt is basically random data that you keep secret, so others can't figure out
// how to create their own hash. Obviously, you should change these values if you
// put this in your game.
return md5("SALTY_SALT_[infostring]_NEEDSMOARSALT")



For example, if you remember my old game Cosmic Peril, it used four hashes based on a random number: (well, #2 was fake, I went a bit overboard)
var/Check5 = rand(10,99)
F["I"] << md5("a[Money]tH[Ships.len]14[Check5]39#[Items.len]38SJ[DepoBox.len]q[round(InUniverse.Number*36.2)]q")
F["II"] << md5("M@[rand(1,999)/2]Udisj[rand(1,9)*1000]asqD[pick(5377,"JfkkdsJf#","AfdfSDDd@")]dj#")
F["III"] << md5("DA[round(sqrt(Money))*2]S65[round(Ships.len*(267/100))]fgAfjt[round(sqrt(DepoBox.len*5))]DfasgEG[round((Money/-1000))]SDArfw")
F["IV"] << md5("SDJsk[world.version]_sjda2322[round(sqrt(Check5))]237ash23[round((Check5+125)/3)]SajkjdS")
F["V"] << ((Check5*2)+1)/5


Of course, while being hard to guess, this was a rather poor method, because it didn't protect a wide-enough range of the stats (ie the stats of their equipment/ships). I would probably automate the hashing if I had to do it all over again.

Ironically, after all of that, someone (looking at you AD) just used a memory editor and completely side-stepped my protection :P