Saving/Loadind Data - Easier way?

Hi, I’ve seen the Techority(Peach Pellen’s blog) and I’d like to know if there is any easier way to save and load data to my app. I’d like to save A LOT of stuff, so I guess I’d have to create a lot of files for save all the data, which could be not so cool. [import]uid: 75946 topic_id: 16634 reply_id: 316634[/import]

My Ice library might be able to help you - http://developer.anscamobile.com/code/ice [import]uid: 5833 topic_id: 16634 reply_id: 62116[/import]

I absolutely recommend Ice library. It’s insanely easy to use and amazingly robust system. I struggled to understand json, and I got it working, but honestly, I wish Ice came along before I dealt with save/load implementation. It would’ve saved days of headache. [import]uid: 67217 topic_id: 16634 reply_id: 62118[/import]

Thanks, I took a glimpse at the ice library and it seems so efficient.

I’d like to thank you, like, A LOT. [import]uid: 75946 topic_id: 16634 reply_id: 62125[/import]

For future reference there is also Jon Beebe’s Games Class - that is very neat :slight_smile:
http://jonbeebe.tumblr.com/post/2320935925/beebegames-class-for-corona-sdk [import]uid: 52491 topic_id: 16634 reply_id: 62186[/import]

I haven’t tried Ice but I used to use JSON but didn’t fully understand it. Now I’ve switched to SQLite/Lua Tables and haven’t looked back.

If you are willing to take some time to figure out how to get it all set up and working, it works great! [import]uid: 23649 topic_id: 16634 reply_id: 62192[/import]

Using JSON is uber easy.

Here is the settings.lua file I’m using with a game I’m currently developing. I’ve not fully tested it, but:

[lua]module(…, package.seeall)


– settings.lua - Load/Save game settings

local json = require(“json”)

local mySettings = {}
mySettings.playername = “player”
mySettings.password = “”
mySettings.firstname = “”
mySettings.lastname = “”
mySettings.email = “”
mySettings.sessionID = “”
mySettings.offline = false
mySettings.hasRegistered = false
mySettings.rememberLogin = false
mySettings.hasRated = false
mySettings.soundFx = true
mySettings.music = true
mySettings.scores = {0,0,0,0,0,0,0,0,0,0}

local settingsFile = “settings.json”
local function saveSettings()
local path = system.pathForFile( settingsFile, system.DocumentsDirectory)
local file = io.open(path, “w”)
if file then
local contents = json.encode(mySettings)
file:write( contents )
io.close( file )
return true
else
return false
end
end

function loadSettings()
local path = system.pathForFile( settingsFile, system.DocumentsDirectory)
local contents = “”
local file = io.open( path, “r” )
if file then
– read all contents of file into a string
local contents = file:read( “*a” )
mySettings = json.decode(contents);
io.close( file )
if mySettings.hasRated == nil then
mySettings.hasRated = false
end
if mySettings.soundFx == nil then
mySettings.soundFx = true
end
if mySettings.music == nil then
mySettings.music = true
end
if mySettings.playername == nil then
mySettings.playername = “player”
end
if mySettings.password == nil then
mySettings.password = “”
end
if mySettings.firstname == nil then
mySettings.firstname = “”
end
if mySettings.lastname == nil then
mySettings.lastname = “”
end
if mySettings.email == nil then
mySettings.email = “”
end
if mySettings.sessionID == nil then
mySettings.sessionID = “”
end
if mySettings.offline == nil then
mySettings.offline = false
end
if mySettings.hasRegsitered == nil then
mySettings.hasRegistered = false
end
if mySettings.rememberLogin == nil then
mySettings.rememberLogin = false
end
else
saveSettings() – initialize the settings!
end
return mySettings
end[/lua]

Basically the way this works is you have a Lua table that has a member for each thing you want to save. You can see where I’ve created my table “mySettings” at the top of the code.

Next, I have my saveSettings function which simply passes the settings table to json.encode(). It spits out a string and then we use FileIO to write it out to the filesystem. That’s about as easy as it can get.

Now the loadSettings function is a touch more complicated, but really only visually. We open the file that we previously saved. I do a simple test to make sure I was able to open the file. If I’m successful, then I read the entire file into a string and then pass that string to json.decode which returns me a table which I use to basically overwrite my existing settings table.

Where it gets visually complicated, is that I need to test to make sure each of my settings values is there. This is a future proofing technique so that when I read the settings file in, if its missing a value, then I initialize a value.

Finally, if I failed to read the file (most likely didn’t exist) then I call saveSettings to write out my default table.

That’s about as simple of a settings setup as you can get. JSON is just used as the method of turning your table into text and convert it back.

Enjoy!
[import]uid: 19626 topic_id: 16634 reply_id: 62202[/import]

hey rob you can simplify your code more by changing

if mySettings.hasRated == nil then
mySettings.hasRated = false

to

mySettings.hasRated = mySettings.hasRated or false

this will remove alot of the if statements and run better [import]uid: 7911 topic_id: 16634 reply_id: 62205[/import]

Thanks Rob!

I am curious, how are using the settings.lua? I will asume you would require it on each module where you need to save/load persistent data like soundFX and so on. I would really appreciate if you could give a quick example on the use of your settings.lua in a different module like a game screen for instance.

Thanks a lot.

Mo

Ps: cool trick jstrahan! Thanks. [import]uid: 49236 topic_id: 16634 reply_id: 66924[/import]

Right now I’m being a bad boy and loading the settings in main.lua and sticking them in the _G. table so the settings are available.

FYI, I’ve renamed it to “libsettings.lua” since my settings.lua will be the settings screen in my app.

You have two usage options:

  1. Load it globally in main.lua

libsettings = require(“libsettings”)

note the lack of local!
Then whenever you need to use it, you can just do:

libsettings.saveSettings()
local mySettings = libsettings.loadSettings()

  1. Local it locally in the modules where you will need it:

local libsettings = require(“libsettings”)

then call as above.

This is work in progress and I’ve not tested it all out yet. In fact I’m being such a bad boy I’m using the settings table globally instead of trying to do it the right way at the moment.
[import]uid: 19626 topic_id: 16634 reply_id: 66984[/import]

Try ice before trying anything else! It’s super easy and super awesome :slight_smile: [import]uid: 77183 topic_id: 16634 reply_id: 66988[/import]

Why not just use an SQLite database? Surely this is exactly what databases are for? [import]uid: 95579 topic_id: 16634 reply_id: 67005[/import]

@kevin.partner, sure SQLite is useful for this, but you have to write a lot more code to accomplish a very simple task. If you have a lot of data and you want to query it to return smaller sets, databases are the best choice, but for a hand full of settings that fits nicely into a table, SQLite is overkill. [import]uid: 19626 topic_id: 16634 reply_id: 67007[/import]

A really great benefit of using JSON to save locally is the data is packaged to load/save in whole or in part over the web.

-David
[import]uid: 96411 topic_id: 16634 reply_id: 67010[/import]

WOW thanks for all the info guys (that is why I LOVE this community!)

I am going to try ICE. It seems to be a nice module which uses JSON and sqlite3.

THANKS!

@robmiracle: ha ha, yes I know what you mean. I have been bad too with _G before…but it feels sooo good:) [import]uid: 49236 topic_id: 16634 reply_id: 67082[/import]

Ok guys, I started using ICE and it ROCKS! (you were right Naomi) Still i have maybe some stupid questions:

1 - I have something like this in main.lua:
[lua]require( “ice” )

local box3 = IceBox:new( “player” )
box3:store( “score”, 0 )
box3:store( “hiScore”,100 )
box3:store( “medals”, {0,0,0} )
box3:store( “promotion”,0 )
box3:store( “rank”, 1 )
box3:store( “bonus”,0 )
box3:store( “bullet”,1000 )
box3:save()[/lua]

Now in one my other module1 (score screen) i have:

[lua]local myData = ice:loadBox( “player” )
local hiScore = myData:retrieve( “hiScore”)
hiScoreText.text = "HighScore: "…string.format ( “%08d”, hiScore )

– (TEST) Now let check if the “hiscore” can doubled and then saved back
hiScore = hiScore*2
myData:store( “hiScore”,hiScore)
myData:save()[/lua]

It works great (ie: shows hiscore = 100 when I first go to module1 and then shows 200 when i go back to the main screen and then go back to score screen) The problem is when I quit the app completely (in the simulator) and then try to re-start the app. It shows hiscore = 100 not 200. My assumption of course is that the app start by running main.lua and ICE sets the box3:store( “hiScore”,100 ) so of course the hiscore will always be a 100 at startup.

Anyway to deal with this? Do I need to setup some type of IF THEN to check if the ice boxes are already been saved to not replacing them with default values?
2- You noticed that there is a box3:store( “medals”, {0,0,0} )
Which means I am trying to save on disk a table called medals which I think is possible with ICE (maybe I am wrong) In that case, how do you guys (if you use ICE of course) can access elements of that medals table using myData:retrieve? Would something like this work?

local medal = myData:retrieve( “medals”[1])

I am going to try it but my guess is that won’t work that way.

Thanks guys. And once again thank so much GrahamRanson for this great module!

Mo

[import]uid: 49236 topic_id: 16634 reply_id: 67087[/import]

Hey, lemsim, as you guessed, the reason why you keep getting the hiscore = 100 is because you are setting hiscore to 100 in main.lua and saving over whatever the value you might’ve saved during the previous session.

What I would do instead is, first I’d retrieve hiscore data and check if it returns nil (or, in your case, maybe check if it returns 100). If it returns nil, I’ll give it a default value such as 0. If it returns a value (not nil), I’d use the returned value (or, in your case, if it returns a value greater than 100, use the returned value).

I hope it makes sense…

Naomi

EDIT: Just to clarify, if no data has been saved before, retrieving a data would return nil. You can safely save a value, initializing the data then. Once retrieving a data returns a value that is not nil, you know it’s a valid data that you’ve saved before, and you will then need to decide whether or not you want to save over it. [import]uid: 67217 topic_id: 16634 reply_id: 67109[/import]

Hello Naomi.

Thank you so much for taking the time and the quick response. Yes I changed this in my main.lua

[lua]if box3 ~= nil then

local box3 = IceBox:new( “player” )
box3:store( “score”, 0 )
box3:store( “hiScore”,100 )
box3:store( “medals”, {0,50,0} )
box3:store( “promotion”,0 )
box3:store( “rank”, 1 )
box3:store( “bonus”,0 )
box3:store( “bullet”,1000 )
box3:save()
end[/lua]

As you said, that this seems to work as expected. I can see that it saves the value between app re-starts which makes me very happy! I have couple more ice boxes and I will do the same check in the main.lua (testing for nil) I am not sure it is the best way to do it but it seems to work…

By the way I also figure out the way to access a selected item in a table saved in a ICE box (see my question 2 above)

[lua]local myData = ice:loadBox( “player” )
local medal = myData:retrieve( “medals”)
hiScoreText.text = "Medal: "…string.format ( “%02d”, medal[2] )[/lua]

Would display 50 which the second element in the table medals({0,50,0}) shown in my player ICE box above. So you simply need to add [x]

I hope this will a newbie (like me) somewhere.

Thanks again Naomi for your help and for suggesting ICE in the first place. I appreciate it.

Cheers

Mo
[import]uid: 49236 topic_id: 16634 reply_id: 67128[/import]

opps! I have couple more questions if I may. Now that I can save/retrieve game settings with ICE,

1- How do I start from scratch if I wanted to? I mean, for testing i may need to want to start from where the high score for instance is that zero. I will think that when I am ready to distribute the app on the App store, whoever download the app, the ICE boxes will be build on his/her iphone/ipad documents folder so they will start from the default values right?

2- What happen with ICE if in future app upgrades I need to change something with the boxes. Say I decide to make the game multiplayer (local) and need say 2 high scores for 2 different players. Could simply change (in my main.lua code)

box3:store( “hiScore”, 0 )

TO

box3:store( “hiScore”,{0,0} ) ???

Would that screwed up people who have an older version of my game?

Maybe I can just add a new ICE box called “player2” and have a new high score element?

Any comments/suggestions will be very welcomed.

Once again thank you!

Mo [import]uid: 49236 topic_id: 16634 reply_id: 67130[/import]

Hi Naomi,

Thanks a lot as always. I really appreciate your willingness to help. I like the suggestion just to add a need ice box for new version of the game. I will also post the same question on the ice author post to see what he think as well. That way every new Corona user will know what to do when the time comes to save/load data with ice.

THANKS :slight_smile:

Mo.

ps: By the way i should read that ice post more carefully. You already asked about how to reset an ice box before (box:clear()) I am not how I missed that one. For my defense I try to read a lot of posts before posting myself but sometime I read too many posts and tend to forget what I read!! [import]uid: 49236 topic_id: 16634 reply_id: 67178[/import]