Persistent Data
-
So I thought it might be useful to have a way of persisting data across program runs and this is what I came up with (download code: FPH33MNDNN). Let me know if you find any problems!
// Persistent data store // Allows values to be persisted across program runs // V0.1 by pianofire // V0.11 added missing close() to loadStore // V0.2 removed firstRun flag (thanks MikeDX) // Note: This will overwrite the current program file store // Only supports string, int and float types // Not sure about performance with big list // Todo: Add remove key function // Add support for other types store = [] // data store //clearStore() // uncomment to reset if !loadStore() then // init on first run setValue( "test", 99, "int" ) setValue( "test1", "", "str" ) setValue( "test2", pi, "float" ) endif // get values from store test = getValue("test") test1 = getValue("test1") test2 = getValue("test2") finished = false while !finished loop clear() printAt( 0, 0, "Press X to exit" ) printAt( 0, 1, "test : " , test ) printAt( 0, 2, "test1 : ", test1 ) printAt( 0, 3, "test2 : ", test2 ) c = controls( 0 ) finished = c.x update() repeat // change values test = test + 1 test1 = test1 + "wibble" test2 = test2 * 2 // save current values to store setValue( "test", test, "int" ) setValue( "test1", test1, "str" ) setValue( "test2", test2, "float" ) saveStore() // save store to file // clear saved store function clearStore() store = [] saveStore() return void // list the current store values (for debugging) function showStore() clear() for i = 0 to len( store ) loop print( padstr( store[i].key, 10, " " ), padstr( store[i].type, 10, " " ), store[i].value, "\n" ) sleep(0.5) repeat print( "Press A to quit" ) finished = false while !finished loop c = controls( 0 ) finished = c.a update() repeat return void // set a value in the store - create new key if necessary function setValue( key, value, type ) i = 0 found = false while i < len( store ) and !found loop if store[i].key == key then found = true else i += 1 endif repeat store[i] = [ .key = key, .value = str( value ), .type = type ] return void // Get the value for the specified key function getValue( key ) i = 0 result = "Undefined" found = false while i < len(store) and !found loop if store[i].key == key then found = true result = store[i].value else i += 1 endif repeat if found then if store[i].type == "int" then result = int( result ) endif if store[i].type == "str" then result = str( result ) endif if store[i].type == "float" then result = float( result ) endif endif return result // Pad a string to a set length with a character function padstr( value, padlen, padchar ) result = value while len( result ) < padlen loop result += padchar repeat return result // Save the current store values function saveStore() handle = open() entries = len( store ) write(handle, padstr( str( entries ), 4 , " ")) for i = 0 to entries loop writeString( handle, store[i].key ) writeString( handle, store[i].type ) writeString( handle, store[i].value ) repeat close( handle ) return void // Load the current store values function loadStore() result = true handle = open() entries = int( read( handle, 4 ) ) if entries > 0 then for i = 0 to entries loop key = readString( handle ) type = readString( handle ) value = readString( handle ) store[i] = [ .key = key, .type = type, .value = value ] repeat else result = false endif close( handle ) return result // read a string from the file function readString( handle ) strlen = int( read( handle, 4 ) ) value = read( handle, strlen ) return value // write a string to the file function writeString( handle, value ) write( handle, padstr( str( len( value ) ), 4, " " ) ) write( handle, str( value ) ) return void
-
Thanks for this @pianofire - I assume you can have 'many' save files - one for each program yes? Dont want it to be that fuze overwrites another file if you have these functions running in two programs for example?
-
@SwitchedOn Yes there is currently a single file per program. Be aware that if you share the program the contents of this file is shared as well
-
im really going to have to mess with this soon. the most i ever done for save files while i was messing around with Processing was using CSV files
-
Updated with suggestion from @MikeDX
-
Awesome. This is very useful and the code looks neat too. Couldn't find bugs.
I do suspect to have found a limitation though (haven't tested it): the current file format probably only supports 9999 entries.
However, I think that's more than enough for anything reasonable. If you need more than that, you probably need to rethink what you're doing, or otherwise need to tweak a lot of code to get it performing for your use case anyway :) -
@pianofire if someone (me, and probably some of the readers) would like to use this in their code, how should they approach it?
Do they need to ask permission, or can they just use it (or not)? And if so, how would you like to be credited about it? -
@PB____ Please just use it! That's what I wrote it for
-
@pianofire Thanks! I will of course put in some form of credit, but this was the reply I was hoping for / kind of expecting :)
-
No problem. Feel free to improve on it too!
-
I've noticed that you don't declare your function scoped variables.
It is likely that someone uses a variable called
i
in their for loop. If they do this in the global scope and usegetValue
within that for loop, the i value of the global variblei
would be changed bygetValue
. This could be fixed as follows:// Get the value for the specified key function getValue( key ) var i = 0 var result = "Undefined" var found = false while i < len(store) and !found loop if store[i].key == key then found = true result = store[i].value else i += 1 endif repeat if found then if store[i].type == "int" then result = int( result ) endif if store[i].type == "str" then result = str( result ) endif if store[i].type == "float" then result = float( result ) endif endif return result
I now
var
is not a keyword (you could use int in stead), it's a personal preference, mainly sinceboolean found = false
doesn't appear to be technically correct either. -
Wait I’m confused is this so you can access data from any program
-
@LinkCraft02 I think you might have misread "a way of persisting data across programs" in stead of " a way of persisting data across program runs" ?
I think you can only access the file that belongs to project you are running.
-
I have updated this in line with the @PB____ recommendations.
Also it now supports vector types
-
@pianofire please post a usage example.
-
There are some examples in the download:
// Initialize values or load from the store if !loadStore() then // init on first run setValue( "test1", 99, "int" ) setValue( "test2", "", "str" ) setValue( "test3", pi, "float" ) setValue( "test4", { 0, 1, 2, 3 }, "vector" ) endif // Get values from store test1 = getValue( "test1" ) test2 = getValue( "test2" ) test3 = getValue( "test3" ) test4 = getValue( "test4" ) // Show current values finished = false while !finished loop clear() c = controls( 0 ) printAt( 0, 0, "Press X to exit" ) printAt( 0, 1, "test1 : ", test1 ) printAt( 0, 2, "test2 : ", test2 ) printAt( 0, 3, "test3 : ", test3 ) printAt( 0, 4, "test4 : ", test4 ) finished = c.x update() repeat // Change values test1 = test1 + 1 test2 = test2 + "wibble" test3 = test3 * 2 test4 = { test4.x + 1, test4.y * 2, test4.z - 1, test4.w / 2 } // Save current values to store setValue( "test1", test1, "int" ) setValue( "test2", test2, "str" ) setValue( "test3", test3, "float" ) setValue( "test4", test4, "vector" ) saveStore() // save store to file
-
@pianofire I can't seem to download from the code NDZQHDNDNN anymore.
I did evolve the code to my preferences anyway (since you're in my friends-list, you could check it out on NDSF6DND1Z, it's part of my template project that I intend to use for my new projects) -
@PB____ Sorry my fault I unshared it so the code changed. The new code is FPH33MNDNN
I have had a quick look at what you have done and it looks very impressive. That looks like an incredible amount of work! I will update my example using your code if that is OK (with credit of course)
-
@pianofire Thanks :-) I hope the work pays off. Since it's my template project, I hope that the work I put in it, will benefit all my future projects, so as a net result, it saves me work overall :)
And of course I'm OK with it if you can improve the file storage example based on my code :-). I'd like to clarify my thought process about how I start the file:// file format (to enable compatibility check) file_writeLine(file, "PB") // reserved for updates to the file format: file_writeLine(file, 0)
Currently projects can only have one file, and it's not possible to share files across different projects. But still I thought it was a good idea to mark a file format at the start of the file, just in case the mentioned limitations change in the future. Of course this means that I hope that other people won't mark their files with the same opening as I did, to prevent potential errors in the future :-)
The second line (
0
) is to mark a version of the format. In case people download an updated version of a program, and I need to check how to handle the format of an older file (in case the download mechanism doesn't include saved files in the future, since it has been suggested that that behavior is a bug). -
could this be simplified to save a sprites location using maps