Strange problem with file i/o

Hey again everyone,

I’m having a weird problem with file i/o. One of two things will happen, depending on what I do with my code,

here are the functions in question:

function settings:accessData() local path = system.pathForFile( "appData.json",system.DocumentsDirectory ) local file = io.open( path,"r" ) if(file) then contents = file:read("\*a") gameSettings = json.decode( contents ) io.close( file ) return gameSettings else -- leaving this else here if we decide to add any debugging messages. print( "Something Failed" ) end --io.close( file ) end

function settings:checkLives() gameSettings = settings:accessData() --print( "Checking the JSON file " .. gameSettings.twentyFourHourTimer ) local OSTime = os.time( ) local nextDayTime = (gameSettings.twentyFourHourTimer) + 120 --86400 = 24 hours replace when done testing timeLeft = print( "time difference is " .. (OSTime - nextDayTime) ) if(OSTime \< nextDayTime ) then print( "24 hours has not passed" ) lives = gameSettings.currentLives return lives else --print( "24 hours have passed" ) lives = gameSettings.maxLives gameSettings.twentyFourHourTimer = os.time( ) local encode = json.encode( gameSettings ) --print( os.time( ) ) File:save(encode, "appData") end end

With that code, i will get this error

Runtime error h:\sticky-fingers-adventure\trunk\code base\settings.lua:86: attempt to perform arithmetic on field 'twentyFourHourTimer' (a nil value) stack traceback: h:\sticky-fingers-adventure\trunk\code base\settings.lua:86: in function 'checkLives' h:\sticky-fingers-adventure\trunk\code base\menu-scene.lua:63: in function \<h:\sticky-fingers-adventure\trunk\code base\menu-scene.lua:61\> ?: in function 'dispatchEvent' ?: in function 'gotoScene' h:\sticky-fingers-adventure\trunk\code base\main.lua:43: in main chunk

if I eliminate the io.close() from settings:accessData(), than I don’t get that error, but I’m not able to save when the timer has expired, because the engine still has the file open… Any idea what I can do?

You’re (probably unintentionally) making several global variables. For example:

contents = file:read("*a")

gameSettings = json.decode( contents )

… etc

 

These lines should be prepended by “local”. I believe that’s what confusing you, and causing your app to reach a state where gameSettings.twentyFourHourTimer is nil (which, of course, you can’t add 120 to)

I appreciate the help, in the immediate situation I don’t think that’s what is causing my problem. I’m watching the file I’m writing. The first time I do it, the file is this:

{"currentLives":3,"twentyFourHourTimer":1406728377,"maxLives":3,"curLocIndex":2}

and that is written by the else portion of this function:

 function settings:checkFiles() if(File:doesFileExist("appData.json",system.DocumentsDirectory)) then --stuff to do if the result is true. Read the json file, get time of last play, number of lives, and max lives local gameSettings = settings:accessData() else --things to do if the file isn't there. instaniate the defualt values for lives, and max lives, set the time --Since there is no file in the local user directory, this must be the first time playing the game. Load the file from the resource directory local path = system.pathForFile( "Data/user.json", system.ResourceDirectory ) local file = io.open( path,"r" ) local contents = file:read( "\*a" ) appData = json.decode( contents ) appData.twentyFourHourTimer = os.time( ) appData = json.encode( appData ) --now save that data to the document directory so we can read/write from it as needed local path = system.pathForFile( "appData.json",system.DocumentsDirectory ) local file = io.open( path,"w" ) file:write( appData ) io.close( file ) file = nil settings:checkFiles() end end

however, when the 2 minutes has passed, and the checkLives() function runs, the resultant file looks like this:

"{\"curLocIndex\":2,\"twentyFourHourTimer\":1406728787,\"currentLives\":3,\"maxLives\":3}"

as you can see, there’s now \s added to the file, including one within each name, so, according to my json file, my key isn’t “twentyFourHourtimer” but it’s twentyFourHourtimer" …

So any idea where my encode is getting the extra characters from? Am I inadvertently sending a string, when I need a table?

Looks like you’re json encoding twice.

local json = require("json") local j = {} j.a = 1 j.b = 2 j.c = 3 print(json.encode(j)) print(json.encode(json.encode(j)))

This snippet would print

{"a":1,"c":3,"b":2} {\"a\":1,\"c\":3,\"b\":2}

You didn’t show us the rest of the accessData code, so what value does it return?

I think _memo was on the right track … you’re using gameSettings as a global variable and it’s getting accidentally overwritten.

You directly set gameSettings in line 41 of accessData:

          gameSettings = json.decode( contents )

But then you overwrite it with the return value of the function in line 84:

          gameSettings = settings:accessData()

So if accessData returns nothing, or returns anything other than the whole gameSettings table, you’re overwriting it in line 84 and losing the decoded json data.  I.e. line 84 says throw out any old data from gameSettings and replace it with the return value of the function (which could be nil).

Does printing gameSettings at line 42 show the correct data? and printing at line 85 shows the wrong data?

so i guess i don’t actually have accessData returning anything at the end, whoops, guess i’d better fix that.

As for the print. I’m struggling, I’m following http://www.webdevils.com/corona-arrays/ to do array prints, but none of them seem to be doing anything, and as expected if i just do print(table) i get the (I’m assuming) memory address of the table.

You’re (probably unintentionally) making several global variables. For example:

contents = file:read("*a")

gameSettings = json.decode( contents )

… etc

 

These lines should be prepended by “local”. I believe that’s what confusing you, and causing your app to reach a state where gameSettings.twentyFourHourTimer is nil (which, of course, you can’t add 120 to)

I appreciate the help, in the immediate situation I don’t think that’s what is causing my problem. I’m watching the file I’m writing. The first time I do it, the file is this:

{"currentLives":3,"twentyFourHourTimer":1406728377,"maxLives":3,"curLocIndex":2}

and that is written by the else portion of this function:

 function settings:checkFiles() if(File:doesFileExist("appData.json",system.DocumentsDirectory)) then --stuff to do if the result is true. Read the json file, get time of last play, number of lives, and max lives local gameSettings = settings:accessData() else --things to do if the file isn't there. instaniate the defualt values for lives, and max lives, set the time --Since there is no file in the local user directory, this must be the first time playing the game. Load the file from the resource directory local path = system.pathForFile( "Data/user.json", system.ResourceDirectory ) local file = io.open( path,"r" ) local contents = file:read( "\*a" ) appData = json.decode( contents ) appData.twentyFourHourTimer = os.time( ) appData = json.encode( appData ) --now save that data to the document directory so we can read/write from it as needed local path = system.pathForFile( "appData.json",system.DocumentsDirectory ) local file = io.open( path,"w" ) file:write( appData ) io.close( file ) file = nil settings:checkFiles() end end

however, when the 2 minutes has passed, and the checkLives() function runs, the resultant file looks like this:

"{\"curLocIndex\":2,\"twentyFourHourTimer\":1406728787,\"currentLives\":3,\"maxLives\":3}"

as you can see, there’s now \s added to the file, including one within each name, so, according to my json file, my key isn’t “twentyFourHourtimer” but it’s twentyFourHourtimer" …

So any idea where my encode is getting the extra characters from? Am I inadvertently sending a string, when I need a table?

Looks like you’re json encoding twice.

local json = require("json") local j = {} j.a = 1 j.b = 2 j.c = 3 print(json.encode(j)) print(json.encode(json.encode(j)))

This snippet would print

{"a":1,"c":3,"b":2} {\"a\":1,\"c\":3,\"b\":2}

You didn’t show us the rest of the accessData code, so what value does it return?

I think _memo was on the right track … you’re using gameSettings as a global variable and it’s getting accidentally overwritten.

You directly set gameSettings in line 41 of accessData:

          gameSettings = json.decode( contents )

But then you overwrite it with the return value of the function in line 84:

          gameSettings = settings:accessData()

So if accessData returns nothing, or returns anything other than the whole gameSettings table, you’re overwriting it in line 84 and losing the decoded json data.  I.e. line 84 says throw out any old data from gameSettings and replace it with the return value of the function (which could be nil).

Does printing gameSettings at line 42 show the correct data? and printing at line 85 shows the wrong data?

so i guess i don’t actually have accessData returning anything at the end, whoops, guess i’d better fix that.

As for the print. I’m struggling, I’m following http://www.webdevils.com/corona-arrays/ to do array prints, but none of them seem to be doing anything, and as expected if i just do print(table) i get the (I’m assuming) memory address of the table.