xrxs
xrxs
is an experimental game server using the Plan 9 protocol 9p
.
The client is a specialized uxn ROM that can load other ROMs over the network via the 9p
service.
design
This is the working structure of the 9p filesystem:
/ctl
: Read/write control file for inputing system commands. Reading the file shows the status of the last input command: 1 for success, 0 for failure;logout
is a special case, and the status code will be -1 if it was succesful. the following are valid command syntax:login PW
: Authenticate withxrxs
– password is hashed against realm password hash.logout
: Gracefully remove yourself from the users table.load CART
: Load a cartridge.chunk TYPE N
: Load data of type TYPE and chunk number N.create REALM
: Create a new realm (start a new game) – must have a cartridge loadedprotect PW
: Protect the curent realm with a password if you are the master.transfer USER
: Transfer ownership of the realm to another user.delete REALM
: Delete the realm off the server if you are the master and it is empty.enter REALM
: Join an existing realm.leave
: Leave the current realm.unload
: Unload the cartridge.
/users
: Read-only; Self and others in the realm are readable from here, one per line. It contains only yourself before joining a realm. Your username on your machine is used as your username inxrxs
– if your name is taken, you will get an error on attaching./carts
: Available game/app cartridges for this server, read only; Carts are listed per line upon reading the file. It is backed by files on the server in a directory structure likecarts/CART_NAME/{CART_NAME.rom, data/, realms/}
./slot
: After loading the cartridge, its ROM is read from here; Read-only./data/
: Any supporting data that comes with the cartridge will be found here; They are in three parts:sprite
,audio
, andtext
. Whileuxn
has the ability to seek through a file up to 4GB in size, it can be beneficial to separate game assets into discrete files. Thechunk
command should be used to page different files into the service when needed. The files on the server should be likeTYPEN
whereTYPE
is one ofsprite
,audio
, andtext
, andN
is any sequence of characters (canonically a nonnegative integer). When first loading the cartridge,N == 0
. Issuing the commandchunk TYPE XXX
will attempt to load data from filecarts/CART_NAME/data/TYPEXXX
into the correct data file. IfTYPE
is not one ofsprite
,audio
, ortext
, or the fileTYPEXXX
doesn’t exist in the data directory, thechunk
command does nothing./realms
: Open/saved realms, read-only. Realms and their associated universe are backed by real files on the server so that they can be preserved across service instantiations, in a directory structure like:carts/CART_NAME/realms/REALM_NAME/{realm, universe}
. Realms can either be solo, open, or protected; Open or protected realms can have limited member numbers. Depending on the cartridge, these settings can be user-managed or managed by the cartridge itself. Realms are listed per line upon reading the file like:REALM_NAME 1 4 1
. First would obviously be the name of the realm. The first number is number of members, second is member limit, third is 1 if protected, 0 if not.0 1 1
represents a protected solo realm that is empty (saved game with password).0 1 0
represents an unprotected solo realm that is empty (saved game with no password)./universe
: Write here to update serverside state for this cart/realm; Read from here to get the complete current state. This is backed by a key-value-pair list on the server./scope
: Write here to tell the server the names of theAtom
s (key/value pair of aUniverse
) you’re interested in (one per line), and read from here to retrieve their values (one per line). In many cases this will be preferrable to fetching the entireUniverse
./random
: Read-only, get a random number from 0 to 99./grandom
: Read-only, get a random number from 0 to 99 – These are doled out on a per-realm basis, and the number stays the same until everyone in the realm has had a chance to read it. If you’ve already read it this round or aren’t in a realm, it will be empty./version
: Read-only, outputs the version of thexrxs
server.
realm format
Each realm directory on the server should have the following files:
realm
: Basic data for the realm, file should contain only the maximum number of members, the master’s name, and the password hash, if any (otherwise 0), separated by spaces.universe
: The actual game state for the realm as key value pairs, one per line, likeKEY = VALUE
; limit 15 characters for keys, 63 for values.
The realm should be synchronized to disc when realm membership, limit, or password change. Fenagling some periodic autosave should be possible…
configuration
config.h
in the source contains the following configuration macros:
MAX_USERS
: the maximum number of simultaneous users able to attach to thexrxs
serviceDATA_DIR
: the path to the root of the cartridge and realm storage; can be absolute or relative to thexrxs
executable, but must have the trailing/
build/run
server
The xrxs
server is built/tested in a Linux environment with plan9port
and the C standard library as the only dependencies. With minimal modifications it will probably run just as well on Plan9, *BSD, WSL, and MacOS.
Running ./build.sh
from the server
directory should build the xrxs
executable.
You can run a local server (for testing, split-screen games, or single-player games) with:
./xrxs -m /path/to/mountpoint
or expose a service on the network (uses 9pserve
to support multiple users and gracefully handle disconnects) with:
./xrxs-srv.sh start
Add the -d
option to the above command to enable 9p debugging output. The default port is 5460
but can be changed by setting the XRXS_PORT
environment variable.
Similarly, you can stop the service with:
./xrxs-srv.sh stop
The executable itself supports the following options, one of which is required (no options prints the help text):
-m MOUNTPOINT
: mount the 9p filesystem locally at MOUNTPOINT-s SOCKET
: serve the 9p filesystem over a socket named SOCKET-v
: print the version information-h | --help
: show the help text
client
There are two versions of the client ROM. One is the normal xrxs
client, and one is a standalone client for use as a generic uxn
bootloader. The latter reads a ROM list out of a file called index
, and appends the .rom
extension to your selection and loads a file so named. Note that while the 9p
filesystem will be located under the parent directory of ./n
, the standalone bootloader assumes a flat filesystem.
You can run ./build.sh
with no options to build the normal xrxs
client, or with any of the following options:
-r
: build the normal client and run it for local testing (don’t mount the remote9p
filesystem)-l
: build the standalone bootloader-lr
: build the standalone bootloader and run it
When the client ROM has been built, run ./uxn-xrxs.sh
to mount the 9p
service and run the client. As with the server, the default port is 5460
but can be overridden with the XRXS_PORT
environment variable. The default server address is 127.0.0.1
but can be similarly changed by setting the XRXS_ADDR
environment variable.
The scripts assume you have uxnasm
and uxnemu
in your PATH
. 9pfuse
is used to mount the service, but other implementations could possibly be used.