Page 1 of 1

What is your advice for saving game state?

Posted: Fri May 07, 2021 3:29 am
by togFox
I have an isometric tile based dungeon crawling RPG and need to provide a "save/load" game function. I'm thinking about the best way to do that.

I suppose I can save as a single text file or mulitple text files. I can use a DB database (done that before). The game is in prototyping stage so the it will change. My chosen technique will need to be scalable and flexible without being overkill.

I'm thinking a DB falls into the overkill category. I like the neatness of different text files. My 'data' will be like any other rpg:

- map (tiles - 2D array
- character data (health, position in the world) (array)
- inventory (2d array)
- object state within the world (2d array)

I'm thinking of just dumping each array/list/table to a file when saving and then just read them back in during load. The map file will likely be large but the others would be small.

Is there something else I should be considering?

Re: What is your advice for saving game state?

Posted: Fri May 07, 2021 7:44 am
by darkfrei
I save some table data as table:

savefile.lua:

Code: Select all

return {
	player = {name = "Hero", health = 10, position = {x=4, y=8}},
	enemies = {
		{health = 8, position = {x=10, y=18}},
		{health = 12, position = {x=12, y=20}, hidden = true},
	}
}
And the

Code: Select all

save = require ('savefile')
to load it.

Re: What is your advice for saving game state?

Posted: Fri May 07, 2021 7:56 am
by Gunroar:Cannon()
Maybe you could serialize all the classes that hold data into different filrs(kind if like your last suggestion), leaving out the class functions, then to load it you would have a class/function(for each?) like MapLoader, that makes a new map class, for example, then takes all the variables for that deserialized data and replace them. Or you could put in all the functions that the data doesn't have, that's how I do it.
But for a case where classes are inside classes (like Tile classes inside Map) I give each class instance a _class variable that holds the name of the class they belong to, and I store all the classes in tables.

Re: What is your advice for saving game state?

Posted: Fri May 07, 2021 8:54 pm
by Xii
I use moonblob. Makes it easy to save/load any table from disk.

Code: Select all

local writer, reader = require("BlobWriter"), require("BlobReader")

local filesystem_identity = "68841bcfb1b0c875"
state = {}

function save_state()
	love.filesystem.setIdentity(filesystem_identity)
	local blob = writer()
	blob:write(state)
	assert(love.filesystem.write("state", blob:tostring()))
end

function maybe_load_state()
	love.filesystem.setIdentity(filesystem_identity)
	local fileinfo = love.filesystem.getInfo("state")
	if not fileinfo then
		return false
	end
	
	local data = assert(love.filesystem.read("state"))
	local blob = reader(data)
	state = blob:read()
	return true
end

Re: What is your advice for saving game state?

Posted: Sat May 08, 2021 7:51 pm
by DrNefario
I put all my saveable data into one table, and used a library called table_save to save and load it. It works fine, but could easily be swapped out for some other system if I wanted more security or whatever.

My inventory and level state data are in sub-tables inside the main savedata table.

The other thing I do, which I thought helped during development, is create a fresh savegame and then copy the loaded table over the top of it. That way if I add a new field (at some point there are going to be player stats, like STR, for instance), it will be added to the loaded file without me having to start my game from scratch. I also (belatedly) added a version number so I can update or invalidate out-of-date saves.

I also have a slot-based system so i don't need to worry about filenames.

Code: Select all

require("lib/table_save")

-- globals
selectedSlot = 1
-- save data
player = {}

function loadGame(slot)
	slot = slot or selectedSlot -- allow quickload from current slot
	
	if (slotIsFull(slot)) then
		-- do some magic stuff
		local fileName = "slot" .. slot .. ".sav"
		local loaded = table.load(fileName)
		-- clone the table into player (this means we keep whatever is in player now
		-- that doesn't exist in loaded, so it will have new fields we added since the
		-- save, as long as they aren't subfields)
		player = newSaveData()
		for k, v in pairs(loaded) do
			player[k] = v
		end
		
		-- make sure flags and automap are created
		if (player.levelflags[player.area] == nil) then player.levelflags[player.area] = {} end
		if (player.levelmaps[player.area] == nil) then player.levelmaps[player.area] = {} end
		
		selectedSlot = slot

		level.loadLevel(player.area)
		return true
	end
	return false
end

function saveGame(slot)
	slot = slot or selectedSlot -- allow us to call with no slot (bit hacky)
	
	local fileName = "slot" .. slot .. ".sav"
	table.save(player, fileName)
end

function newSaveData()
	local saveData = {}
	
	saveData.version = 1
	saveData.x = 1
	saveData.y = 1
	saveData.facing = 2
	saveData.area = 1
	saveData.hp = 100
	saveData.maxhp = saveData.hp
	saveData.xp = 0
	saveData.level = 1
	
	-- event tracking
	saveData.globalflags = {}
	saveData.levelflags = {}
	-- automaps
	saveData.levelmaps = {}
	-- inventory
	saveData.inventory = {
		{ "fist", -1 },
		{ "knife", 10 },
		{ "heal", 5 },
	}
	
	-- make sure flags and automap are created
	if (saveData.levelflags[saveData.area] == nil) then saveData.levelflags[saveData.area] = {} end
	if (saveData.levelmaps[saveData.area] == nil) then saveData.levelmaps[saveData.area] = {} end
	
	return saveData
end
Edit: Just noticed my comment "Do some magic stuff". That was a placeholder from before I implemented it :)

Re: What is your advice for saving game state?

Posted: Sun May 09, 2021 1:11 am
by togFox
Thanks for all the replies. I eventually found this on github:

https://github.com/BroccoliRaab/SaveData

It is very simple and pretty much does what everyone here is saying but it is neatly encapsulated so you just need savedata.save and savedata.load. I thought that was worth posting here.

Re: What is your advice for saving game state?

Posted: Sun May 09, 2021 1:37 am
by pgimeno
Maybe not hundreds, but there are probably dozens of serialization libraries. For T2R I decided to use JSON; today I would have chosen Smallfolk, but if I wanted the files to be compact and not necessarily human-readable, bitser. I don't trust serialization libraries that generate Lua code.