Hoping we have someone with Emscripten knowledge who can help me further with this.
In short, after restarting the browser, my interpreter does no longer see the save file that was stored in the browser’s IndexedDB during the previous session.
Longer:
I recompiled my interpreter that is written in C so it can be run in a browser. I used a jquery terminal, Emscripten and help from forum member jkj yuio to glue everything together. Emscripten compiles the sources to a .wasm, a .js a datafile and an html file.
And it works, as you can see here (click).
What remains is an issue with the save file. Because the code does not have access to the local file system everything is stored in the browser. Save and restore work, but as soon as you close the browser the save data is gone.
Emscripten optionally supports the indexedDB file system which is persistent. So I recompiled with this option.
After a save I can see the save file with the browser’s dev tools. After I shut down the browser and restart the interpreter, the save file is still there, but the interpreter does not see it anymore. A restore gives a file not found error.
I feel I’m missing an action to reconnect to the database or so but I cannot find any examples.
I checked the Inform online play and they also keep the save files after the browser closes. But I don’t see any files in the IndexedDB so I think they use another mechanism.
Not the autopersist and chdir (I include the full path when saving the file). Will add the autopersist and see what happens.
This routine is for when there’s no save directory yet and it is created. When you start the interpreter how can you check that the directory is already there from previous play? Maybe I must only mount it when it is already present?
I’ve been looking for an overview of all the FS-methods but cannot find one.
int32_t InitIndexedDB() {
// EM_ASM is a macro to call in-line JavaScript code.
EM_ASM(
// Make a directory other than '/'
FS.mkdir('/XVAN_savefiles');
// Then mount with IDBFS type
FS.mount(IDBFS, {autoPersist: true}, '/XVAN_savefiles');
// Then sync
FS.syncfs(true, function (err) {
// Error
});
);
return(OK);
}
And it seems to work now. I can save and restore and when I close the browser, restart it and use restore as the first command I am back at the saved position. I must do more testing but it looks good.
Thank you very much for your help.
I now only have the possibility for one save file. Once I’m confident this works I want to expand for more save files, like with inform online play. How do you keep track of the saved files and filenames? With the fs.readdir() method? Or have you built something yourself?
If you allow players to choose filenames, then that is probably the way, though I guess you could remember the filenames in a separate file.
An idea I might try is having a limited number of slots, e.g. 20, and then you can just check that a filename like “save#.xyz” exists, where # is replaced by the slot number. Something like the save/restore screen from Doom or Quake. The downside is that players would need to re-use slots once they use all 20 for a particular game.
Yes, I was thinking about a separate file too, that is read when the interpreter starts. When the player types restore present all the filenames and let them pick one. I’ll see if I can get this working.
I’ve been working on this idea with a file with the names of the savefiles and while doing this I ran into another issue with IndexedDB.
After startup the interpreter looks if the file with the savefile names already exists from an earlier session. If not, this is the first time play and the interpreter will create the file.
The issue I’m running into is that, since FS.syncfs() is asynchronous, the interpreter is too fast with it’s check. When it checks for the file, syncfs() is not ready yet and the interpreter concludes the file does not exist.
I worked around it by adding a “Hit any key…” routine after syncfs() that slows the interpreter down before it checks whether the file is there.
I found it is common practice for asynchronous actions to have a callback function that is executed after the asynchronous action is completed. So my approach was to pass a callback function to syncfs() that will set a sync_finished flag and then have the interpreter in a while loop until it sees the flag is set.
FS.syncfs() has a parameter for a callback function but it is only for error handling from what I can see. I’ve been playing around with it but no success.
callback – A notification callback function that is invoked on completion of the synchronization. If an error occurred, it will be provided as a parameter to this function.
It sounds like the callback is called for both success and failure, with err being undefined (or null?) for success. (The docs could be clearer, sigh.)
If that’s still not enough time you could try using addRunDependency.
Btw, I didn’t realise that getting a directory listing wasn’t simple in C. Even so, Emscripten’s environment acts like Linux/POSIX, so something like this should work. You shouldn’t need to maintain your own list of files in a file.