Can a Twine game create and write to an external txt file?

Twine Version: I’m assuming this would require Sugarcube if possible at all.

More of a “is this possible with any format at all” question.

The title says it all. I was wondering if a Twine game, run locally offline, could produce a txt file saved to the same folder the game is downloaded to. Also, if yes, could this txt file ostensibly be any format? Like, perhaps saving table information that could be read by a separate Inform game on bootup?

3 Likes

It depends on what you want in the Text File. If it’s a save file, unlikely. SugarCube saves playthroughs as a .save. If it’s just some variables, Chapel has a macro that lets you download a file with selected variables (that you, the author code) as a text/json/etc…

I am not sure if it is possible to save it in a specific folder however. Twine games are usually opened on browsers, and whatever you download ends up in the Download folder (or wherever you asked the browser to save files usually).

EDIT EDIT: It could be possible, but how :woman_shrugging:
Would require quite a bit of custom code for sure.

2 Likes

Thank you, Manon. I’ll have to poke further. I appreciate you humoring this unusual Twine question.

Ultimately, I’d like certain choices made in this Twine game to result in the creation of a text file that a seemingly unrelated Inform game is checking for upon starting each time. The information coded into this text file would alter game states in the Inform game. So, you end up with a meta puzzle hidden across multiple games submitted to the same Comp.

2 Likes

Yeah no worries :slight_smile:

ooooh Like Hand Me Down did at the Comp this year but more connected?
Excited to see how this pan out :slight_smile:

3 Likes

The combination of the browser security model and the IF interpreter security model means that it’s impossible to do this behind the player’s back. (I think.) Browser downloading has to be visible to the player and it can’t write directly to an IF documents folder, unless the player explicitly selects that folder.

The last time we did a hidden metapuzzle in IFComp (buffs fingernails) we did it by hiding information. That is, stuff that the player could notice. There was no direct connection between the games outside the player’s brain.

3 Likes

Thank you for confirming this. Thankfully, I had some sleight of hand ideas to deal with this if such a thing turned out to be the case.

I was thinking the file would be dual purpose. Meaning, it would fully function as an exported save file for the Twine game as well, raising no suspicions about the file. Also, the game would prompt the player that it might be wise to save their game before continuing. The prominent SAVE GAME button would prompt a save file export.

As for the file destination, that would be a numbers game. I don’t need this to work for everyone, just enough to raise some consternation. Once an action from an unrelated Twine game is linked to a reaction in an entirely separate Inform game (like an object mailed in one game then arriving in the other), the community would quickly determine the how. I just need some users who either never move anything from the download folder, organizing nothing, or users who congregate all IF related files into the same folder upon download, organizing everything.

I’m familiar with that effort and I am an enormous fan and admirer as well. This line of inquiry and others were directly inspired by that metapuzzle.

2 Likes

If the games are in the same domain they can share browser localStorage, which could serve as a sync point.

Besides this, would embedding the games on a webpage with iframes allow to share information over the outer window scope?

4 Likes

This very crude test worked from the Twine online editor. The games below share a localStorage item (I called it sharedScope.message).

Sender game
:: StoryTitle
sender


:: StoryData
{
  "ifid": "8BB6EFEF-DE98-4459-8DF3-B723153063B2",
  "format": "SugarCube",
  "format-version": "2.36.1",
  "start": "Sender start",
  "zoom": 1
}


:: Sender start {"position":"750,250","size":"100,100"}
<<textarea "$message" "your message here">>
<<button "Send">>
  <<run localStorage.setItem("sharedScope.message", $message)>>
  <<goto "Sent">>
<</button>>


:: Sent {"position":"875,250","size":"100,100"}
You sent the message!

<<back>>

Receiver game
:: StoryTitle
receiver


:: StoryData
{
  "ifid": "0A907F53-1888-426D-B776-7C093E9316A4",
  "format": "SugarCube",
  "format-version": "2.36.1",
  "start": "Receiver",
  "zoom": 1
}


:: Receiver {"position":"750,250","size":"100,100"}
<<set $message to localStorage.getItem("sharedScope.message")>>
<<if $message>>
You have a message: "$message"
<<else>>
No messages :-(
<</if>>

How to test:

  1. Import twee files into your twine library
  2. Run receiver.
  3. Run sender, post a message.
  4. Reload receiver, check the message’s there.
3 Likes

That is very clever, @n-n ! Noted and added to my bag of nefarious tools.

3 Likes

The above trick won’t work at IFComp, as each game has its own subdomain there for online play. But using iplayif.com, ifarchive.org or the same itch.io profile would allow to create some cool cross-game side-effects (I’m thinking something like The Knot with harder interaction).

4 Likes

For the actual question, a brief internet search gives me this (javascript - How to create a file in memory for user to download, but not through server? - Stack Overflow) and a bunch of related questions. It’s definitely possible with Javascript (though I think the user has to make the choice to proceed with the download, by clicking a button or something). Which means it’s possible in Twine by extension, though you’d have to do it with Javascript.

There should be better ways to pass info between games, though, especially without the user’s knowing. n-n’s suggestion looks like it works and could lead to some really cool game interactions. It’s an interesting idea!

2 Likes

Thank you for that!

There are several ways I’ve identified so far. Trying to add to those methods as much as possible.

Yes and…

2 Likes

As Andrew points out, this is a major security concern, webpages being able to arbitrarily edit or download files without user intervention. Local HTML files cannot access specific folders without explicit permission.

However, as mentioned, there are multiple things to consider for similar approaches:

  • Using localStorage. If enabled, pages can access a browser-supplied API for saving key-value pairs. (This is how online version of Twine works, for example.) This comes with two caveats to consider: (1) users can disable, edit, or delete things whenever they want and (2) Apple Safari comes with a seven-day policy where, if the data has not been updated in that window, it will be removed.

  • Story format functionality. SugarCube has an API, Harlowe has a macro, Snowman supports anything JavaScript provides, and Chapbook does an auto-save approach (using localStorage).

  • Creating a text file. As multiple people mentioned, JavaScript can create any file format using the File class and work with it using the File System API. Potentially, the HTML file could produce a file and prompt a user to save the file somewhere. With careful instructions, they could be told to save it to the same folder as the other files or a specific location.

  • Server storage. “Locally” was mentioned, but a potential solution is setting up a web server to receive information and having multiple projects retrieve the information based some some random ID given to every player when they start. Using the same ID, they can retrieve their save data remotely between projects capable of accessing the server. (Historical note: many programs use a local web server to send data between processes. It would be much more complicated, but a Twine HTML file could be bundled with NPM packages to host data and share it with other processes.)

2 Likes

You can also copy to the clipboard, which may be nefarious enough for you.

2 Likes

Not if I hope to have a non-zero fraction of players have another game retrieve that information, and reflect those choices, months or years later.

2 Likes

First of all, thank you for providing more detail. I appreciate your time and insight. Noted on each and every point.

Something I alluded to above but didn’t necessarily spell out is that I don’t really need each and every player to have this happen successfully. In fact, it’d be better if the files were occasionally saved to the same folder serendipitously and this only happened infrequently to the occasional player. The discretion is more important than reliability for this particular effort.

1 Like

Yeah, there’s intentionally not supposed to be any way to have the browser save things to your computer secretly.

To condense what people have already said a little, there are basically two ways the browser can save/load:

  • Store it silently in the browser (e.g. localStorage, or a couple similar things). The user or the browser may delete it unexpectedly. It won’t work across separate websites (so someone said not separate IFComp games, but ok for IFArchive games, currently ok for itch but they may make games separate sites in the future).
  • Have the user click something to download a file: you’ll always get the downloading/download-completed animations/icons, and depending on the user’s setup it may pop up dialog boxes for “do you want to save this? What do you want to name the file?” and there’s no way (I don’t think) to only do it if it’s going to be quieter. So what you get is kinda visible or very visible, not the kinda visible or silently fail that you want.

Uploading a file is worse: AFAIK the user always has to choose a file to upload, you can never just read the one you want.

Or, I guess, option 3: run a server that sends and receives data from the Twine and Inform games, and then you can possibly jigger the security stuff so you can send data around silently (or silently fail).

I don’t see a good option for IFComp other than hosting the actual games somewhere else yourself so all the games could be on the same domain/subdomain. Itch might work for that in 2024, but people pointed out the problem during their recent hosting switch, and they’ve said their new hosting would allow them to make each game a separate site, so it may not continue to work in the future. There might be other free hosting that’d be better for longer-term stuff. Or hmm… I guess you could ask someone: I have a handful of domain names for no particular reason so I could tuck a couple games in a corner somewhere. And I imagine there are other people here like that too.

2 Likes

At the risk of stripping any ambiguity from this whatsoever, what I originally envisioned was such:

A twine game is entered into a comp, maybe SpringThing, maybe EctoComp, whichever.

When the player chooses to save their game, they press the SAVE GAME button provided.

This game has a save system that tracks an arbitrary and limited amount of game states in the form of a table saved into a txt file. The structure of this file is formatted in a manner that it meets the requirements for Inform to read the table successfully.

This save file has a default name which could be changed by the player, breaking the intended the effect.

This save file can be saved anywhere the player wishes; there is only one location they could coincidentally save it that wouldn’t break this effect.

When they chose to, they could select this file to load a saved game by selecting the LOAD GAME button provided, and navigating to and selecting the desired save file.

For most players, this is the full functionality of this feature. Full stop.

We are now done with the Twine game.

Months later at a subsequent Comp, perhaps IFComp, perhaps not, an Inform game is submitted without a play online option to maximize the number of people playing the game through a downloaded interpreter at the cost of total plays.

Then, assuming the earlier file still exists, wasn’t renamed, and was coincidentally saved in the same folder this Inform game was saved to, the Inform game looks for and finds a txt file by the default save game name upon running.

Upon finding it, it reads the table and applies certain specific game state info saved from this player’s session with the Twine game and applies relevant changes to the player’s session with the Inform game.

As the player then plays this Inform game something happens that reflects the game’s knowledge of choices made in the player’s earlier playthrough of the Twine game.

This only needs to happen a few times, perhaps even just once, before a player brings word of it to the greater community.

I expect this to fail the vast majority of the time, but it only needs to work once to be worthwhile.

It’s messy, unreliable, and clumsy. But it’s also discrete.

3 Likes

This sounds like the sort of thing cookies were created to do: one web site saves the cookie ‘this person searched for canapes’ and another web site shows you canape ads.

I don’t know a good way to get an Inform game, played in-browser, to read a cookie, but I would think it could be done cross-Twine? You do have to tell the player you’re saving cookies, but people are used to that.

2 Likes

Agreed. That’s why I was leaning towards potentially pissing folks off with no “play online” button forcing them to either download the game and run it locally in an interpreter or to simply not play the game (most will choose the latter, obviously).