Score: 0 Moves: 0
You are standing in a repository. There is a README here.
This project aims to provide a simple web service that can bring up and run games in a z-machine. It was originally written to be interacted with by a hubotscript, but could presumably be used by anything you like, which is the fun of APIs.
The API uses the dfrotz z-machine interpreter. Each game you start is run in a different dfrotz process, and APIs for managing these processes are included.
This was thrown together by a few developers, and could almost certainly be better than it is. Please feel free to contribute improvements!
- nodejs
- npm
- wget
- dfrotz (the makefile will install this for you)
Really just the dependencies need to be built. It should just be a matter of:
make all
Frotz 2.44 will be downloaded and built, and a symlink created so zmachine-api can find it. All node dependencies will be installed as well.
You can copy the existing .env.example and customize it to set the port and LOG_LEVEL options.
We support writing save files to Amazon S3. This is configured in the .env file with your own AWS credentials to use S3 saves. If S3 is not configured, games will be saved to disk only.
node src/server.js
Create a zcode directory to store your games. We currently only suport z3 - z8 format games.
We don't provide you with any games, but feel free to drop any zcode files you come across in the zcode directory. There are lots of good public domain games out there, why not try a bunch?
There is a great selection of zcodes games here as well as many other places. if-archive/games/zcode
You can also skip most of this and use the provided Dockerfile, if it suits you. It'll take care of the dependencies and build process and give you just a container listening for HTTP requests.
If you're using Docker, and you don't have S3 configured, take steps to ensure
that /root/saves
is preserved (use a Volume, mount a network drive, etc) or your
saved games will be lost when the container is redeployed.
Here's how the API works.
Returns a list of all installed z-code games. These are what you can use with
POST /games
Response:
[
{
"name": "ZORK1",
"zFile": "ZORK1.z5"
},
{
"name": "ZORK2",
"zFile": "ZORK2.z5"
}
]
Returns a list of all active games.
Response:
[
{
"pid": 12345,
"name": "foo",
"zFile": "foo.z5",
"label": "foo game"
}
]
Spawns a new zmachine with a specific game.
Request body:
{
"game": "foo",
"label": "foo game"
}
- game is the zmachine file (without the file extension) you wish to play
- label is an arbitrary label, used as part of the filename when you save
Response:
{
"pid": 12345,
"data": "Startup text from the game"
}
- pid is the process ID of the z-machine that was spawned by the creation of the game
- data is the text that the game returned when it started.
Stops a running zmachine process and deletes from the list of active games
Response:
Game for :pid terminated.
Send a game action to a running zmachine
Request body:
{
"action": "go west",
}
- action is the command that the player typed to the game
Response:
{
"pid": 12345,
"data": "You go west. It's okay."
}
- pid the zmachine process id
- data the game's response
Saves the game's current state to a file and uploads to S3. The filename that is
written to S3 is saves/:label-:game-:file.sav
where :label
is the label you
used when spawning the zmachine instance, :game
is the game file that is running
(without the file extension), and :file
is the filename specified in the
request body of this API call.
Request body:
{
"file": "somefile",
}
- file is a saved game name that you can use later to restore the game.
Response:
{
"pid": 12345,
"data": "Whatever the game says in response to the save action"
}
- pid is the process id of the zmachine instance that you sent it to. Will be the same as the pid in the URL you posted to.
- data is the response the game returned when it was saved.
Restore a saved file into a zmachine process.
Request body:
{
"file": "somefile",
}
- file is a game name that you previously saved. The
game
andlabel
for the pid (created when you spawned a new zmachine) are combined with this to find the file to restore.
Response:
{
"pid": 12345,
"data": "Whatever the game says in response to the load action"
}
- pid is the process id of the zmachine instance that you sent it to. Will be the same as the pid in the URL you posted to.
- data is the response the game returned when it was restored.
This tutorial will walk through the steps to create a long-running game, one where you don't want the zmachine process to stay up and running. In this tutorial the game will be saved and restored between every single command.
Dfrotz is extremely lightweight (Zork was originally run on a TRS-80, after all), and keeping the processes around generally won't hurt anything. But if your machine is very ephemeral (yay, cloud!), you might not be able to rely on the processes being alive if a player comes back to the game after months. Or you might just be a neat freak who can't stand to have all those processes lying around. Regardless, this tutorial will be helpful in understanding how the API interacts with the dfrotz process and how saved games work.
A session ID provided by your application is used to keep track of the game for each player. The session ID can be anything you like, it just needs to be something you can keep track of for the entire length of the game.
POST /games
with a zmachine game name and your session ID.
{
"game": "zork",
"label": "player1234"
}
zmachine-api will respond with the OS process id of the instance it spawned, and the startup text from the game.
{
"pid": 12345,
"data": " welcome to the game"
}
Return the data
to the player.
- Send a game command to the PID of the game
POST /games/12345/action
{
"action": "go west",
}
zmachine-api responds with
{
"pid": 12345,
"data": "You go west. It's okay."
}
Return the data
to the player.
- Pick a file name you wish to save this game under. We're going to use the same name for every saved game. Saved games automatically have the session ID appended to them, so we don't need to use something unique for the name.
Send POST /games/12345/save
using the saved game name you selected:
{"file": "save"}
The response will contain a data element with whatever the game responds with when you save. Normally, a player would see this, but since they didn't ask for the save, telling them you just saved after every game action would be pretty confusing.
-
Quit the zmachine process with a
DELETE /games/12345
-
When you get a new command, it's time to restore the game.
POST /games
using the same session IDlabel
andgame
as you used in the first step:
{
"game": "zork",
"label": "player1234"
}
zmachine-api responds with a new process ID, and the initial game text.
{
"pid": 67890,
"data": "Startup text from the game"
}
-
Because we're restoring the game automatically based on the label you provided, don't send the
data
element back to the player. They don't know we're saving and restoring repeatedly, game save and restore messages would be confusing to them. -
Using
POST /game/67890/restore
restore the game in the background to the new zmachine process, using the same file name as was used to save it.{ "file": "somefile", }
-
Start over at step 2, sending the command you received in step 5.
Yes, probably.
By all means, open an issue on GitHub. Or, better yet, submit a pull request!