Remember back, oh, about April – a million weeks ago – I said that I wanted to get autosave working in Glulxe+RemGlk?
It works now. See the bleeding-edge repositories:
This may be useful if you’re creating a bot or online game-playing service. It supports two slightly different use cases:
Hedging against the possibility of process termination
This is how the iOS interpreters work. (Or worked, since I’m not supporting them on current iOS.) The app starts and runs normally, but it could be killed at any time (when in the background). Therefore, we autosave every turn. At startup time, if autosave files exist, we restore them and continue play.
This allows you to run an ongoing game with no background process! Every time a user input arrives, you launch the interpreter and shove the input into stdin. The interpreter reads the autosave file, processes the input, generates a turn’s worth of output, updates the autosave, and exits.
I’ve run through a bunch of the Glulx unit tests with this interpreter (in single-turn mode). It seems to work, but there are undoubtedly bugs lurking. Keeping a stream open for several turns is a likely trouble spot. (I tested keeping a transcript file open, at least.)
In case you’re wondering: the Fizmo Z-code interpreter has support for the autosave API. However, it’s old (2012!) and is mostly written in ObjC, because I only ever used it on iOS. It could be updated to work with this new RemGlk, but I’m not sure when I’d get to it. Feel free to give it a shot…
So I’m in the process of changing how Emglken works: I am getting rid of my custom Glk library and incorporating the whole of Remglk, and so far it’s been surprisingly straightforward.
I have a couple of questions about the autosaving stuff:
How graceful is it with autosaves produced from older versions or even entirely different autosaving systems? If we put an autosaving version of Glulxe in Lectrote will it be able to handle (ie, ignore) the autosaves from Quixe?
If I want to build Glulxe without autosaving (or another terp that doesn’t have autosaving), will it work to just exclude unixautosave.c? I’m wondering will the dead code eliminator then clean up everything from the libremglk.a. Probably? But it looks somewhat closely integrated. I could comment out the GLKUNIX_AUTOSAVE_FEATURES definition, but I’m hoping for something that will work without me needing to maintain a fork.
If I want to build Glulxe without autosaving (or another terp that doesn’t have autosaving), will it work to just exclude unixautosave.c?
You’re just trying to get the code size down? You can’t comment out GLKUNIX_AUTOSAVE_FEATURES in remglk (it won’t compile). You can add
after each #include “glkstart.h” line in unixstrt.c and unixautosave.c.
Autosave compatibility is not graceful at all. Lectrote’s assumption is that if the interpreter changes in any significant way, the autorestore will just fail and the game will start at the beginning. (The user might have to use the Reset menu option.)
This has never happened with Quixe. The advantage of slow development, I guess…
For supporting several interpreters, I’d say use the --autoname argument (or equivalent) and get each interpreter to use a different base filename.
Hmm, I’d prefer not to have to fork Glulxe, so I may just keep it in. It’s probably not too much code space compared with the whole.
I’m using a virtual filesystem and then passing the files to Dialog.js to handle, so the filenames don’t matter. But, I have just thought of a solution: I can add in a property to the JSON before giving it to Dialog, and then check if it’s there before giving it to the VM. That should work!
I noticed that if you send an event for line input on a non-existent window (such as if you just copied the example from the docs and forgot to change the window…) nothing happens. Is that expected?
How is the process termination procedure meant to work? When you do an autorestore it doesn’t output the Glk state. And the autosave.json data is in a different format to what glkapi.js would produce, so we can’t just send it to GlkOte. In case it matters, I’m running this after killing the previous process.
You use -singleturn so that the process shuts itself down.
You can’t pass the autosave.json to GlkOte; it’s not compatible. The idea is that GlkOte stays live, so it only needs incremental updates. The interpreter running with --autosave --autorestore -singleturn -autometrics gives incremental updates. That is, it should be giving the same output as a regular (blocking) RemGlk interpreter, running with no arguments, receiving turn-by-turn input.
If you just run gcc without -ansi or -std=c99 or whatever, the headers define BSD-like functions, SystemV-like functions, and GNU extensions all intermingled, which is easy to use but can lead to portability surprises later on. On the other hand, if you want earlier warning about portability issues, you can choose what language standard you want with (say) -std=c99 and what platform standard you want with (say) -D_POSIX_C_SOURCE=200809L and if your code compiles it should be guaranteed to work on any platform that meets those requirements.
I know how the single turn mode works, but I was asking how you intended for restoring killed apps to work. The refresh message does result in the text content being sent, but not the window definitions you’d need to put that text in, and you’d also need to parse the JSON to know which generation number to send. Seems like the best option is just for the app to autosave its output state alongside what glulxe autosaves.