Generalizing Protected World States

Greetings all! Without a doubt, there is a better title for this post. I’ll figure that out as we go on and will edit accordingly.

I’m building an extension where I’m using @protect to save information across world states. Here’s the basics so far:

Include (-
Array world_reset -> 2;

[ AddWorldResetProtection;
	@protect world_reset 2;
];

[ RemoveWorldResetProtection;
	@protect world_reset 0;
];
-).

To set world reset preservation:
	(- AddWorldResetProtection(); -).

To remove world reset preservation:
	(- RemoveWorldResetProtection(); -).

To establish a world reset:
	(- world_reset -> 0 = 1; -).

To establish death in previous world state:
	(- world_reset -> 1 = 1; -);

To decide whether a world reset has happened:
	(- (world_reset -> 0 == 1); -).

To decide whether the player died in a previous world state:
	(- (world_reset -> 1 == 1); -).

To say establish a world reset:
	establish a world reset.

To say establish death in previous world state:
	establish death in previous world state.

To reset the world:
	(- @restart; -).

Carry out restoring the game (this is the ignore world preservation before restore rule):
	remove world reset preservation.
	
Carry out saving the game (this is the respect world preservation after save rule):
	set world reset preservation.

Very simple and it all works great. What I would like to do is generalize things. For example, right now, I handle persisting that a world state reset (“restart”) happened and whether the player died.

Certain things might happen in the game world, such as an object becoming known when it was unknown the first time through. So note that this is a case where everything is reset to the way it was (since, behind the scenes, it’s just a forced restart), but some knowledge is retained. I still need to investigate the autosave extension as well.

I’m unsure if it’s possible to generalize what I have above to allow the author to have that control. For example, via phrases, they could add something like this:

now the known state of the amulet is preserved

Behind the scenes, of course, I would have to have the array with enough values to allow for this so there would be limits.

What do folks think? Am I on a good path with this?

The context here, in case it’s not clear, is that restart is being made into a diegetic concept. My main focus on this is coming from looking at time travel concepts, where maybe a time paradox causes the world to reset to its original state, but the player character retains their memory through the transition. This feels like it could have some interesting ways to mirror growing player knowledge that the player character would also have.

1 Like

Glulx’s protect feature is very basic and quite limiting. It’s usually better to instead write to a file (“external file” in Inform 7’s lingo) and then read back after restarting, restoring etc.

1 Like

Yep, I need to consider that more. That said, the overall feature I need is pretty limited: I just need an element in an array to be 0 (not preserved) or 1 (preserved). That’s pretty much it. It doesn’t even matter to the author where in the array a given value is “stored” because it’s really just a check of “is x set to 0 or 1”.

On the other hand, the more I think about the array approach, the more complicated it is. After all, if the author does do something like this:

now the known state of the amulet is preserved

Even assuming I have an array allocated with enough values, there’s really nothing to indicate which one is being used or not. Hmm.

One of my concerns with the external file is simply that it is an external file, and it feels like more can go wrong there. But I don’t have evidence for that. It’s more just that idea of something external that now has to be available in any context that the player engages with the game. What if the file can’t be saved? What if it can’t be read? What if they start play on one machine and continue on another? (Maybe unlikely, but my career is in testing so I think of edge cases a lot!) I do know that if everything is stored in the state of the game, then dependencies become less problematic, but, on the other hand, logistics seem to become complicated.

An I7 table is just an array under the hood, so you could put all your needed information into a table and protect that instead of using a raw I6 array.

1 Like

I’m embarrassed I didn’t think of that. Even more so since I’m using a variation of the Robo examples from the manual to store actions in a table for the player’s “past self.”

The big caveat there is that some data types in Inform are stored in a table by reference rather than by value, and protecting the pointer won’t protect the data it points to. But if you’re just using numbers, truth values, objects, and enums, that’ll be fine.

(The ones to look out for are anything that doesn’t fit in a single 32-bit word. Text and stored actions and such.)

1 Like

When I was working along similar lines in 2015 (oy, time flies!), the answer was also to use an external file, but before realizing that I wrote an almost complete extension to do this kind of thing. I’ll attach it here on the off chance it’s useful to anybody. There is some documentation at the end and a few explanatory comments in the implementation.
extension.i7x (16.9 KB)

1 Like

This is very cool! Thank you for sharing. Just reading through some of it already, you gave me some thoughts. It’s really interesting to play around with these ideas.