Inform 7 ability to read and write Game data to an external file for sharing between different Plays

Inform 7 does not inherently support the functionality to continue a story from a shared point between separate game files, which is a crucial feature for creating a persistent, evolving world where player actions have lasting consequences across different games.

The concept described involves a shared file that would save player data (like stats and inventory) and changes made to the game world.

This would allow a player to start a new game in a different sector, loading the previous data, and have any new changes reflect back in the original game.

OR

This would allow a different player to use the game in

While this is not a built-in feature of Inform 7, it would require a custom solution involving external file handling to achieve this kind of shared, persistent universe functionality.

3 Likes

The external file mechanics do allow for the possibility of files communicating information between projects, as described in the documentation here. I’ve never managed to do it myself, though!

4 Likes

Hey, I made it work! Before I show off the example, let me point out a couple things:

First, the syntax The file of [whatever] (owned by project "[insert IFID here]") is called "[name]". as given in the documentation didn’t work for me. I pasted the IFID for one project into the file declaration for the other project, and I ended up with a runtime error. Maybe it was a matter of different hyphens?

But secondofly, it turns out I didn’t really want to stipulate the IFID, for reasons that will become apparent. The syntax The file of [whatever] (owned by another project) is called "name". suited my purposes perfectly.

Let us imagine a series of games called “The Chronicles of Swords.” In each installment, your goal is to collect a sword. Each new game should remember which swords from previous games you have collected already.

First I made an example pair of games where Episode 1 was only able to your progress, and couldn’t receive any information generated by Episode 2. I realized this could cause some problems. The most obvious one is: What if you’re making games that don’t have to be played in a specific order? So I messed around a little more. In these examples, your progress from Episode 2 will be preserved in Episode 1 if you happen to play them in that order.

I wrote some blog posts a while back about using external files for autosaving, and the same basic principles apply here. You have to be careful about how the format of external files. You kind of have to know what kind of information you’re passing from game to game before you start making any actual games! For purposes of this example, I picked this format that should be easy to read, where everything is just texts:

Table of Chronicles
state	status
"claimed sword of daggers"	"no"
"claimed crystal rose"	"no"
"claimed sword of trowels"	"no"

Saving and restoring progress is a matter of reading from and writing to (a file with the same format as) (and crucially the same number of rows as) this table. Here’s the first game in the series:

"The Sword of Daggers" by Ryan Veeder

The story headline is "Episode 1 of the Chronicles of Swords".

Ancient Tomb is a room.

The crystal rose is here.

The scorched altar is here.

The Sword of Daggers is on the scorched altar.

The Sword of Trowels is a thing. [footnote 1]

The File of Chronicles (owned by another project) [footnote 2] is called "swordchronicles".

Table of Chronicles
state	status
"claimed sword of daggers"	"no"
"claimed crystal rose"	"no"
"claimed sword of trowels"	"no"

When play begins: [footnote 3]
	if the File of Chronicles exists:
		read the File of Chronicles into the Table of Chronicles;
	choose row with state of "claimed sword of daggers" in Table of Chronicles;
	if status entry is "yes":
		now player carries the Sword of Daggers;
	choose row with state of "claimed crystal rose" in Table of Chronicles;
	if status entry is "yes":
		now player carries the crystal rose;
	choose row with state of "claimed sword of trowels" in Table of Chronicles;
	if status entry is "yes":
		now player carries the Sword of Trowels;

After taking the Sword of Daggers: [footnote 4]
	if player carries the crystal rose:
		choose row with state of "claimed crystal rose" in Table of Chronicles;
		now status entry is "yes";
	if player carries the Sword of Trowels:
		choose row with state of "claimed sword of trowels" in Table of Chronicles;
		now status entry is "yes";
	choose row with state of "claimed sword of daggers" in Table of Chronicles;
	now status entry is "yes";
	write File of Chronicles from Table of Chronicles;
	say "You claimed the Sword of Daggers, as was your destiny!";
	end the story saying "But what other destinies await?"

Footnote 1: Although the Sword of Trowels isn’t directly obtainable in this game, we make it an offstage thing here so that it can show up if you bring it from Episode 2.

Footnote 2: Here’s the utility of saying the file is owned by another project. At this point, this is only necessary for letting the player finish Episode 2 first. If you tell this project that the “File of Chronicles” is owned by another project, then it’ll be able to read from a file named “swordchronicles” that was created by the other episode.

Footnote 3: This beginning-of-play routine is only necessary to restore the player’s progress from Episode 2.

Footnote 4: In this example I decided to save progress only at the end of the story. In an open world situation, you might want to save things as you go along, so that the player can putter about in one game, make some progress, find some amulets, find a good place to stop in that game, and then open another game and still have all their amulets.

Here’s the second game:

"The Sword of Trowels" by Ryan Veeder

The story headline is "Episode 2 of the Chronicles of Swords".

Icy Grotto is a room.

The Sword of Trowels is here.

The File of Chronicles (owned by another project) is called "swordchronicles".

Table of Chronicles
state	status
"claimed sword of daggers"	"no"
"claimed crystal rose"	"no"
"claimed sword of trowels"	"no"

The crystal rose is a thing.

The Sword of Daggers is a thing.

When play begins:
	if the File of Chronicles exists:
		read the File of Chronicles into the Table of Chronicles;
	choose row with state of "claimed sword of daggers" in Table of Chronicles;
	if status entry is "yes":
		now player carries the Sword of Daggers;
	choose row with state of "claimed crystal rose" in Table of Chronicles;
	if status entry is "yes":
		now player carries the crystal rose;
	choose row with state of "claimed sword of trowels" in Table of Chronicles;
	if status entry is "yes":
		now player carries the Sword of Trowels;
		

After taking the Sword of Trowels:
	if player carries the crystal rose:
		choose row with state of "claimed crystal rose" in Table of Chronicles;
		now status entry is "yes";
	if player carries the Sword of Daggers:
		choose row with state of "claimed sword of daggers" in Table of Chronicles;
		now status entry is "yes";
	choose row with state of "claimed sword of trowels" in Table of Chronicles;
	now status entry is "yes";
	write File of Chronicles from Table of Chronicles;
	say "You claimed the Sword of Trowels, as was your destiny!";
	end the story saying "But what other destinies await?"

It’s kind of exactly the same! The only difference is, now all the details that optionally enabled out-of-sequence play in Episode 1 (the presence of items from other games, the phrase “(owned by another project),” the When Play Begins rule) are necessary in order for Episode 2 to function properly as a second episode.

I wasn’t sure I could get away with telling both projects that the file was owned by another project (actually owned by no project at all?) but it works!

5 Likes

Thanks for proividing the useful guide. Now where should be the external file for read/write session saved in relative to save game file or project(whatever).

When you write File of Whatever…, if the file doesn’t exist already, the game creates the .glkdata file in the same directory as the game file (which is also the directory where it checks if the File of Whatever exists). You never have to create the file yourself.

When playing with the Lectrote interpreter on MacOS, I notice that the files don’t appear in any visible directory—but apparently it’s creating them, and putting them somewhere where it can find them!

1 Like

In linux it is saving it in /user/home/ folder. Any suggestion to change its location. I believe it needs update the whole software for an eelgant solution. For now the default location is just ok.

/usr/home/< usrname >/ aka ~/ isn’t the best place, esp. if is a dot file (that is, a mildly hidden file, used under *nix for per-user configuration file) because often already held a mass of configuration files)

The best place is the same directory of the story file, together with the save files, to be copied in the directory of the next installment.(that imply that the mere existence of the end game external file is enough for signalling that the prior story file was completed.)

Because save files can’t be ported between story file releases for obvious reasons, it’s the only mean for implementing a “game_won” record portable between releases. (actually, I’m mulling around a similiar scheme for easing the ßtesting of Isekai’s next acts…)

Alternatively, there was the ancient “password” system, not only the well-known one used in multi-part 8-bit Quill/GAC story files, but also the one from early console games, prior of the introduction of the battery-backed savefile RAM, where the few key variables ends encoded in a short string, to be written down by the player (today is easily copypasted into a text file, so theoretically can even deliver a dual, I daresay “belt and suspenders”, system of external file sharing…)

So, I guess that there are more than one alternative for the problem, methink.

Best regards from Italy,
dott. Piergiorgio.

The progress variable works great. I have successfully saved and restore player basic inventory from a simple game.

Updated(I have now used chatgpt to generate an Inform code successfully load all the entries of inventory in table however it carries (key on desk) along with the desk even if i have dropped the key before. And it requires the location of each object like desk and key to be copied as well

Two things of note for this thread:

  1. @Afterward: The run-time error that you saw was due to a bug in the handling of files owned by another project that is specified by ID. If Game A creates a data file and Game B modifies it, then Game B’s IFID is incorrectly written to the file. This makes it look like an invalid file to both Game A and Game B, both of which expect to see Game A’s IFID there.

  2. @Piergiorgio_d_errico: The Glk spec section 6.1 details that data files should be placed in a common location, specifically in order to facilitate sharing information between different games. [“If a game interpreter uses a data-specific directory, there is a question of whether to use a common location, or divide it into game-specific subdirectories. (Or to put it another way: should the namespace of named files be per-game or app-wide?) Since data files may be exchanged between games, they should be given an app-wide namespace.”]

My opinion is that namespace ought to be per-game (for example, this is the current root dir of …/if/bin/glulx:

glulx$ ls
2009  2016  2023       balances    idolswar  moments2   risorg
2010  2017  2024       beyond      Ingold    museum     robin
2011  2018  2025       Cadre       ita       nightfall  samfort
2012  2019  abuses     chipmonk    Kerker    old        sequitur
2013  2020  acg        deadcities  king      ottumwa    Short
2014  2021  airport    eleusin     lacuna    Plotkin    theabbey
2015  2022  andromeda  everdie     litlGirl  resort     zork-I7

if, for example, sequitur/ is actually a sequitur, all I have to do is copying from the earlier story’s dir the external file into the sequitur/ directory. This allow user’s maximum control on what the VM are allowed to access from the system and at the same time allows porting data between story files.

Best regards from Italy,
dott. Piergiorgio.

I have checked the other game with separate IFID cannot even open for read(yet alone modify).

The only workaround is to create a script to transport information between two files for example the player stats or some parameter.

I really wish if developers allow atleast separate gamefiles to atleast read common progress files to allow releasing a persitent multilevel text based game without the need for a 3rd party script.