saveLocalData(), string, number or nil only?

Now that I managed to get the input from the user about his name and getting the score from my game I was thinking about combining these data into a highscore / player name list and store it as local data in a table or a data structure. But Codea told me, that I can store only numbers, strings or nil with saveLocalData(). Is that True? Is there a workaround or is it a question of more general io routines to come? Thanks!

The workaround is to store seperately.

There has been some discussion on how to open file.io in a way that would be safe.

@Absche you can store a string that contains both the bits of data you want.

player = "bob"
score = 200

combined = string.format("return {%s, %d}", player, score)
saveLocalData("highScore", combined)

-- get score back
f = loadstring(readLocalData("highScore"))
combined = f()
print("player: ", combined[0])
print("score: ", combined[1]) 

Load string will just execute a string and return a function that will execute that code. In this code your string reconstructs a table containing both your player name and score. You can make more elaborate tables, and there is plenty of code on the net showing how to serialise arbitrary tables fairly easily using this method.

Oh yes, Thank you both for your replies. As I plan to save only the Top 5 or 10 Players I will go for your straight forward solution, John using two different separators for colums and lines.

@John - I’m playing around with the best way of providing high score functionality so I can add it to my next tutorial. I quite like the Chipmunk JSON parser but it seems like overkill.

I tried your approach above but Codea prints out nil for both values. I haven’t played with the loadString functionality much so let me have a go at explaining what is going on and you can tell me if I have got it wrong.

combined is initialised with a string which looks like: “return {bob, 200}” and is saved locally with the key “highScore”. So far so good.

As I understand it loadString will return to f, a function which executes return {bob, 200}. So the line:

combined = f()

will now create a table in combined, initialised with bob and 200 as the elements. Now things start getting hazy. I would have thought that bob was at index 1 not index 0? And 200 would be at index 2 not 1?

Also bob is being treated like a variable and not a string, which would explain why nil is returned as bob is not initialised. I would have expected the table constructor to look more like:

return {“bob”, 200}

So if I make these tweaks, you get:

player = "bob"
score = 200

combinedString = string.format("return {\"%s\", %d}", player, score)
saveLocalData("highScore", combinedString)

-- get score back

f = loadstring(readLocalData("highScore"))
combinedTable = f()

print("player: ", combinedTable[1])
print("score: ", combinedTable[2])

Which seems to work and as a side effect I think I understand loadstring better (which is a very cool function BTW).

@john- Although I like your method better after looking at it, this is the way that I came up with for saving the top ten high scores (it’s part of a bigger project)-

HighScores = class()

function HighScores:init()
    self.updated = false
    self.timesUpdated = 0
    self:load()
end

function HighScores:draw()
    pushStyle()
        textMode(CENTER)
        fill(255, 255, 255, 255)
        fontSize(50)
        --self:update()
        for i, highScore in ipairs(self.scores) do
            text(i..": "..highScore["score"].." by "..highScore["name"], self.x, self.topY - (50 * i)) 
        end
    popStyle()
end

function HighScores:load()
    --self:clear()
    self.scores = {}
    for i=1, 10 do
        local highScoreTable = {
        score = readProjectData("highScore#"..tostring(i).."score", 0),
        name = readProjectData("highScore#"..tostring(i).."name", "Not yet accquired")
        }
        table.insert(self.scores, highScoreTable)
    end
    self.x = WIDTH/2
    self.topY = HEIGHT - 225
end

function HighScores:updatePosition(myPosition, checkPosition)
    local score = game.player.moolahEarned
    if score > readProjectData("highScore#"..tostring(checkPosition).."score", 0) then
        return myPosition - 1
    else
        return myPosition
    end
end 

function HighScores:saveAtPosition(position)
    local score = game.player.score
        
        saveProjectData("highScore#"..tostring(position).."score",
         readProjectData("highScore#"..tostring(tonumber(position - 1)).."score", 0))

        saveProjectData("highScore#"..tostring(position).."name",
         readProjectData("highScore#"..tostring(tonumber(position - 1)).."name", "Not yet accquired"))
end

function HighScores:update()
    print("updating high scores")
    self.updated = true
    local score = game.player.score
    local position  = 11
    for i=0, 9 do
        position = self:updatePosition(position, tonumber(10 - i))
    end
    
    local savePosition = 10
    for i=position, 10 do
        self:saveAtPosition(savePosition)
        savePosition = savePosition - 1
    end
        saveProjectData("highScore#"..tostring(position).."score", score)
        saveProjectData("highScore#"..tostring(position).."name", game.player.name)
    self:load()
end