Saving and Loading Tables (in text files?) [solved]

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
sanjiv
Citizen
Posts: 88
Joined: Mon Feb 27, 2012 5:11 am

Re: Saving and Loading Tables (in text files?)

Post by sanjiv »

The Burrito wrote: then it saves like this:

Code: Select all

for i,v in ipairs(things) do
love.filesystem.write( filename, "makeThing("..v.x..","..v.y..","..v.a..","..v.d..")\n")
end
This is from memory so it's probably wrong (don't have access to my code right now). I prefer to generate readable files so they're fairly easy to edit by hand if need be. To load rather than parsing the file back into a table it runs like any other lua file. This means I can use this as a way to inject custom code into specific maps and stuff like that.
This is what most closely seems to fit my intent,

but I haven't been using "love.filesystem.write" correctly. A literal example would help. To avoid confusion, might an example be given using the following details?

file to store and load info from
boxList.txt --or should this be a .lua file?

info
listOfBoxes={
{a1,b1,c1,d1},
{a2,b2,c2,d2},
{a3,b3,c3,d3}
}

how info will be used in game
drawBoxes(listOfBoxes) --some function that will draw 3 boxes using the 3 lists in listOfBoxes
updateBoxes(listOfBoxes) --some other funtion might make them move or something
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Saving and Loading Tables (in text files?)

Post by Roland_Yonaba »

sanjiv wrote: but I haven't been using "love.filesystem.write" correctly. A literal example would help. To avoid confusion, might an example be given using the following details?
file to store and load info from
boxList.txt --or should this be a .lua file?
info
listOfBoxes={
{a1,b1,c1,d1},
{a2,b2,c2,d2},
{a3,b3,c3,d3}
}
how info will be used in game
drawBoxes(listOfBoxes) --some function that will draw 3 boxes using the 3 lists in listOfBoxes
updateBoxes(listOfBoxes) --some other funtion might make them move or something
Well, love.filesystem.write actually writes contents into a file...But it is done through a protected way. Remember, love.filesystem can access to specific places and only them.
To have it working properly, first set the folder using love.filesystem.setIdentity. You can call this function in love.load callback.

Code: Select all

love.filesystem.setIdentity("MyGame")
This will create a folder named "MyGame" in you user folder. Depending on the OS, that folder will have different pathes:
Windows XP: C:\Documents and Settings\user\Application Data\Love\ or %appdata%\Love\MyGame
Windows Vista and 7: C:\Users\user\AppData\Roaming\LOVE\MyGame or %appdata%\Love\MyGame
Linux: $XDG_DATA_HOME/love/MyGame or ~/.local/share/love/MyGame
mac: /Users/user/Library/Application Support/LOVE/MyGame
The you can write/read anything inside this folder using love.filesystem.write or love.filesystem.read.

Now, concerning data output , i'd rather recommend to tweak a bit TheBurrito's code. Simply iterates using ipairs.

Code: Select all

local function save(myTable)
   local str = "{"
   for k,v in ipairs(myTable) do
      if type(v)=="table" then str = str..save(v)..','
      else str = str..v..","
      end
   end
  return str.."}"
end
This assumes that keys in table are alwaysnumeric ... It seems to be the case.
Although it would have be simple to use TSerial library. You would have get the same results no matter what the input table was.
Using it should be quite simple.File extension doesn't matter, Lua will just execute the piece of code inside. But I'll prefer saving data a way one can load it back using return, instead of setting it as var in the global environment. It is more safe, to me...So

Code: Select all


function love.load()
   love.filesystem.setIdentity("myGame/")
   --bla bla bla
end

function love.draw()
    -- bla bla bla
    --saving
    love.filesystem.write('data.txt', 'return '..save(listOfBoxes),'all')
    ---bla bla bla
    -- loading back
    local listOfBoxes = love.filesystem.load('data.txt')()
   --- bla bla bla
end
PS: Ima party member now... Walkin' on the street with my new lafreak ^^ :cool:
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Saving and Loading Tables (in text files?)

Post by bartbes »

The Burrito wrote: then it saves like this:

Code: Select all

for i,v in ipairs(things) do
love.filesystem.write( filename, "makeThing("..v.x..","..v.y..","..v.a..","..v.d..")\n")
end
I'd like to note this doesn't work, because love.filesystem.write doesn't append, it replaces, so you'll be overwriting your previous lines with every new one. Use newFile and friends instead.
User avatar
The Burrito
Party member
Posts: 153
Joined: Mon Sep 21, 2009 12:14 am
Contact:

Re: Saving and Loading Tables (in text files?)

Post by The Burrito »

bartbes wrote:I'd like to note this doesn't work, because love.filesystem.write doesn't append
You're right, should've waited until I could looks at my stuff. Here's some of my actual code:

Code: Select all

function mapSave(map)
	mapfile = love.filesystem.newFile(map)
	mapfile:open('w')
	for i,v in ipairs(background) do
	mapfile:write("makeBG('"..v.file.."',"..v.x..","..v.y..","..v.s..")\n")
	end
...
function makeBG(img, x,y, s)
	local t = {}
	t.img = love.graphics.newImage(img)
	t.file = img
	t.x = x
	t.y = y
	t.s = s or 1
	table.insert(background, t)
end
Where "map" is the file name and then love.filesystem.load to load the created file.

The various table to string methods are probably going to be more straight forward, being able to read and edit it by hand really stops being all that useful once everything in your editor actually works.
User avatar
sanjiv
Citizen
Posts: 88
Joined: Mon Feb 27, 2012 5:11 am

Re: Saving and Loading Tables (in text files?)

Post by sanjiv »

Roland_Yonaba wrote: Well, love.filesystem.write actually writes contents into a file...But it is done through a protected way. Remember, love.filesystem can access to specific places and only them.
To have it working properly, first set the folder using love.filesystem.setIdentity. You can call this function in love.load callback.

Code: Select all

love.filesystem.setIdentity("MyGame")
This will create a folder named "MyGame" in you user folder. Depending on the OS, that folder will have different pathes:
Windows XP: C:\Documents and Settings\user\Application Data\Love\ or %appdata%\Love\MyGame
Windows Vista and 7: C:\Users\user\AppData\Roaming\LOVE\MyGame or %appdata%\Love\MyGame
Linux: $XDG_DATA_HOME/love/MyGame or ~/.local/share/love/MyGame
mac: /Users/user/Library/Application Support/LOVE/MyGame
The you can write/read anything inside this folder using love.filesystem.write or love.filesystem.read.
The most intuitive thing would be to place this directory in the same place as main.lua, so everything for the game is contained in the same folder. Is that possible, or do too many things break as you move the folder around, or turn it into a .LOVE file?

(speaking as a newb, it'd be really nice if that was the default setting)
User avatar
sanjiv
Citizen
Posts: 88
Joined: Mon Feb 27, 2012 5:11 am

Re: Saving and Loading Tables (in text files?)

Post by sanjiv »

I'm looking at " love.filesystem.setSource ", but "getSource seems conspicuously missing. It seems like that'd be exactly what I need to find the current location of my game folder with the main.lua in it. I assume there are good reasons why there is no love.filesystem.getSource? Or can I hope for something like that in future iterations of LOVE?

No biggie. If I want the level editor to work for future collaborators, I guess I'll just put everything on the C drive, and get them to do the same.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Saving and Loading Tables (in text files?)

Post by Robin »

sanjiv wrote:The most intuitive thing would be to place this directory in the same place as main.lua, so everything for the game is contained in the same folder. Is that possible, or do too many things break as you move the folder around, or turn it into a .LOVE file?
The problem with that is that you might not have the rights to write to that place, so then you would need to be administrator to run your game, which is a very bad idea.

There is no getSource because it makes no sense: the root of the virtual filesystem is the combination of the .love and the write directory..
Help us help you: attach a .love.
Bannana97
Citizen
Posts: 77
Joined: Wed May 16, 2012 2:49 pm

Re: Saving and Loading Tables (in text files?)

Post by Bannana97 »

I made a nice XML save/load for tables because I am making a game using them and I had to make everything saveable.
It's pretty easy to do, so I encourage you to do the same.
User avatar
Centauri Soldier
Prole
Posts: 42
Joined: Mon May 21, 2012 6:38 am

Re: Saving and Loading Tables (in text files?)

Post by Centauri Soldier »

Long time coder (of lua, not LOVE) but first this is my first post on the LOVE forums. I hope the forum is as friendly as I've read it is.

Well, I figured for my first post, I'd start with a contribution, so here ya go.

I whipped up a table to string function some time ago that is quite good. It will not convert user data but it will convert everything else. Once you have the string, you can save it to a text file.

Code: Select all

--[[
Code by Centauri Soldier
www.AMSPublic.com
CentauriSoldier@AMSPublic.com


You are free to redistribute this
code and use it wherever and however
you please so long as this header
remains present and unchanged.

No futher attribution required.
]]


local tEscapeChars = {
	[1] = {
		Char = "\\",
		RelacementChar = "\\\\",
	},
	[2] = {
		Char = "\a",
		RelacementChar = "\\a",
	},
	[3] = {
		Char = "\b",
		RelacementChar = "\\b",
	},
	[4] = {
		Char = "\f",
		RelacementChar = "\\f",
	},
	[5] = {
		Char = "\r\n",
		RelacementChar = "\\r\\n",
	},
	[6] = {
		Char = "\t",
		RelacementChar = "\\t",
	},
	[7] = {
		Char = "\v",
		RelacementChar = "\\v",
	},
	[8] = {
		Char = "\"",
		RelacementChar = "\\\"",
		},
	[9] = {
		Char = "\'",
		RelacementChar = "\\'",
	},
	--[[ This version is for use with
		 pure lua as oppossed to AMS lua.
	[10] = {
		Char = "%[",
		RelacementChar = "%%[",
		},
	[11] = {
		Char = "%]",
		RelacementChar = "%%]",
		},
	]]
	[10] = {
		Char = "%[",
		RelacementChar = "\\[",
		},
	[11] = {
		Char = "%]",
		RelacementChar = "\\]",
		},
};


function GetFunctionName(fFunc)

if type(fFunc) == "function" then

	for vIndex, vItem in pairs(getfenv(fFunc)) do
		
		if vIndex ~= "_G" then
		local sItemType = type(vItem);
			
			if sItemType == "function" then
				
				if vItem == fFunc then
				return vIndex
				end
			
			elseif sItemType == "table" then
				
				for vIndex2, vItem2 in pairs(vItem)	do
				local sItemType2 = type(vItem2);
	
					if sItemType2 == "function" then
				
						if vItem2 == fFunc then
						return vIndex.."."..vIndex2
						end
					
					elseif sItemType2 == "table" then
					
						for vIndex3, vItem3 in pairs(vItem2) do
						local sItemType3 = type(vItem3);
			
							if sItemType3 == "function" then
						
								if vItem3 == fFunc then
								return vIndex.."."..vIndex2.."."..vIndex3
								end
													
							end
							
						end			
							
					end
					
				end
				
			end
			
		end
	
	end
	
end
	
return ""
end


--[[
The number(argument #2) tells
the function how many indents
we want from the start. This
is required but can be 0.
]]
function TableToString(tInput, nCount)
local sRet = "";

local sTab = "";

for x = 1, nCount do
sTab = sTab.."\t";
end

local sIndexTab = sTab.."\t";

nCount = nCount + 1;

	if type(tInput) == "table" then
	sRet = sRet.."{\r\n";
				
		for vIndex, vItem in pairs(tInput) do
		local sIndexType = type(vIndex);
		local sItemType = type(vItem);
		local sIndex = "";
				
			--write the index to string
			if sIndexType == "number" then
			sRet = sRet..sTab.."["..vIndex.."] = ";
					
			elseif sIndexType == "string" then
							
				if string.find(vIndex, '%W', 1) then
				sIndex = sIndexTab.."[\""..vIndex.."\"] = ";
				else
				sIndex = sIndexTab..vIndex.." = ";
				end
						
			end
			
			--write the	item to string
			if sItemType == "number" then
			sRet = sRet..sIndex..vItem..",\r\n"
			
			elseif sItemType == "string" then
			
				for nIndex, tChar in pairs(tEscapeChars) do
				vItem = string.gsub(vItem, tChar.Char, tChar.RelacementChar);
				end
			
			sRet = sRet..sIndex.."\""..vItem.."\",\r\n";
			
			elseif sItemType == "boolean" then
			
				if vItem then
				sRet = sRet..sIndex.."true,\r\n";
				else
				sRet = sRet..sIndex.."false,\r\n";
				end
			
			elseif sItemType == "nil" then
			sRet = sRet..sIndex.."nil,\r\n"
			
			elseif sItemType == "function" then
			sRet = sRet..sIndex..GetFunctionName(vItem, getfenv(vItem), "")..",\r\n";
									
			elseif sItemType == "userdata" then
			--do the userdata stuff here...
			
			elseif sItemType == "table" then
			sRet = sRet..sIndex..TableToString(vItem, nCount)..",\r\n";			
			
			end
			
		end
			
	end

sRet = sRet..sTab.."}"

return sRet
end
User avatar
sanjiv
Citizen
Posts: 88
Joined: Mon Feb 27, 2012 5:11 am

Re: Saving and Loading Tables (in text files?)

Post by sanjiv »

Thanks for posting! I should have come back here earlier and updated that this problem was solved (?). For my short term purposes anyway. I can see that as I understand more stuff and get more ambitious, I'll probably be able to make use of the rest of the advice in the thread.

For me, the solution involved
- getting comfortable creating a strings (along with horizontal tabs and line breaks)
- writing, reading, and just moving around files in the save directory.

Once I practiced those things separately, it wasn't a big deal getting custom 'table to string' functions. They only work for the specific structure of tables I'm using, but that's good enough for me.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 4 guests