all repos — xrxs @ 59c83e2bd234f627ce137925c5c530add7246a7c

experimental networked application/game server with 9p

implement users table and users_read()
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmDdcqwACgkQO3+8IhRO
Y5jABw//X+nTMzxvSYwQiGpqni2YOXX0URjKu6T3XnKjNq9zWpS4XS2VJzJ5YZI6
bHPjnttDs6L28rzotj6hI9omLNtMcQz7+avSPH+p5ZrB9Lzu59N86hUeHcC2pGQ/
o29Ilmx9bVaVxwFPJ+Dj5TI93KZMgAJQTaEpAcEZ+OrPA2JghDOekzqkBIK3VITV
Y6Wegq/YjTmEWpq5VHWQyzCZkNmrrltuAmgD3S+eylhX9fS+09Ohwq2U7dMnmuvU
lnI6vvkYqOgCLA7jDzuPH5xJEVkEj4UlW2EsE+VyIqKlJEODfLhp3BR/WqWwIhht
aOKxksMv0uS9cdZM1g2DSVB1lxRb729IA5jh2CRubpKE+d5dlCkIJvnlgxkGARUg
qTX2njL5jorU+UHd4R5IK22jqAivwgOpf+5wLxDGuZukTjHr9X5y9pjiBFDRcFfZ
p6iffZvhugC1d5uojjN5okNyjXABclewCTsyYFmn7UXsYRRr6K8Qw90PfvevSJ2p
lCc2oT+0Jv+wFXiJy2Q6GLCOemPI0nRcGV3M1z5kTUY1cJEMfFJKCKnEU/ui5YzA
IWisEjTQDYK+3KcC9EXktvJI49afbm7iQniceMQ+Six6IoTcdxEslcjhYkqfwsdi
T0QtWmWfoGTnb6VyEgSiC0HjTSi9eykeCZnmc381ncEYdouxsjM=
=XNGh
-----END PGP SIGNATURE-----
commit

59c83e2bd234f627ce137925c5c530add7246a7c

parent

7ff7a1e29e5d87b155150e4f2c0bde82f4b127e5

3 files changed, 121 insertions(+), 78 deletions(-)

jump to
D DESIGN

@@ -1,53 +0,0 @@

-[9p directory structure] -/ctl: write only control file for inputing system commands - login NAME PW: authenticate with xrxs -- pw is hashed against realm pw hash - load CART: load a cartridge - create REALM: create a new realm (start a new game or app session) - protect REALM: protect the realm with auth data - enter REALM: join an existing realm - leave: leave the current realm - save: save the current universe to the realm - reset: reset the cartridge (attempts to leave the realm, but doesn't save) - unload: unload the cartridge - - should we check the /realms file for success or failure, or should we make ctl rw and return success or failure of the current user's last command on read? - -/users: read-only; self and others in the realm are readable from here in the form: - <AFID> <username> - one per line; - file is blank before login, - contains only self before joining a realm - -/carts: available game/app cartridges for this server, read only - carts are listed per line upon reading the file - backed by files on the server (carts/<cart_name>/{<cart_name.rom>, data/} - -/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; read only - -/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. (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: - realmx 1 4 1 - 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: read/write: write here to update serverside state for this cart/realm; read from here to get the current state. - each cart will have its own format for encoding data here - since this changes frequently in multiplayer games, perhaps only flush to disk at regular intervals instead of after every write - -[Realm format] - -each realm directory on the server should have the following files: - realm: basic data for the realm. file contents should contain only the maximum number of members, and the password hash, if any, separated by a space - members: members currently in the realm, in the form "<AFID> <username>", one per line - universe: the actual game state in for the realm, format is cartridge-specific - -everything in the realm directory is synchronized to disc both when realm membership, limit, or password changes, and at regular intervals. if a member makes no writes to the universe after a certain time, consider them gone from the realm (so there should be a common SYN message all cartridges implement to keep them alive in the realm)
A README.md

@@ -0,0 +1,41 @@

+# xrxs + +`xrxs` is an experimental game server using the Plan 9 protocol `9p`. + +The client is intended to be a specialized [https://wiki.xxiivv.com/site/uxn.html](uxn) ROM that can load other ROMs (possibly preserving its own code to maintain a common menu system or interface to `xrxs`). + +## design + +This is the working structure of the 9p filesystem: + +* `/ctl`: Write-only control file for inputing system commands (should we check the /realms file for success or failure, or should we make ctl r/w and return success or failure of the current user's last command on read?) + * `login PW`: Authenticate with `xrxs` -- password is hashed against realm password hash + * `load CART`: Load a cartridge + * `create REALM`: Create a new realm (start a new game) + * `protect REALM PW`: Protect the realm with a password + * `enter REALM`: Join an existing realm + * `leave`: Leave the current realm + * `save`: Save the current universe to the realm (redundant? or force universe/realm to flush?) + * `reset`: Reset the cartridge (attempts to leave the realm, but doesn't save) + * `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 in `xrxs` -- 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 like `carts/<cart_name>/{<cart_name.rom>, data/}`. + +* `/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; read only + +* `/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: `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: `realmx 1 4 1`. `realmx` would 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 current state. Each cartridge will have its own format for encoding data here. Since this changes frequently in multiplayer games, perhaps only flush to disk at regular intervals instead of after every write. + +## 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, and the password hash, if any, separated by a space. + * `members`: Members currently in the realm, one per line. + * `universe`: The actual game state in for the realm, format is cartridge-specific. + +Everything in the realm directory is synchronized to disc both when realm membership, limit, or password changes, and at regular intervals. If a member makes no writes to the universe after a certain time, consider them gone from the realm (so there should be a common SYN message all cartridges implement to keep them alive in the realm).
M xrxs.cxrxs.c

@@ -32,6 +32,7 @@ int chatty9p = 1;

static char Ebad[] = "something bad happened"; static char Enomem[] = "no memory"; +static char Euname[] = "username is already taken"; typedef enum { CTL = 1, USERS, CARTS, SLOT, DATA, REALMS, UNIVERSE } FileType;

@@ -44,11 +45,44 @@ };

Aux* create_aux(FileType t) { Aux* self = (Aux*)malloc(sizeof(Aux)); - self.type = t; - self.data = nil; - self.count = 0; - + self->type = t; + self->data = nil; + self->count = 0; + return self; +} + +typedef struct UserInfo UserInfo; +struct UserInfo { + char name[32]; + ushort id; +}; + +static UserInfo users_table[64]; + +void xrxs_attach(Req* r) { + static ushort id = 1; + int i = 0; + int l = 0; + char* usr; + char* username = r->ifcall.uname; + usr = username; + while (*usr) { + l++; + usr++; + } + for (i = 0; i < 64; i++) { + usr = users_table[i].name; + if (scmp(usr, username)) + respond(r, Euname); + return; + if (*usr == 0) { + scpy(username, usr, l+1); + users_table[i].id = id++; + break; + } + } + respond(r, nil); } void fsread(Req* r) {

@@ -137,7 +171,7 @@ case CTL:

write_ctl(r); break; case UNIVERSE: - //write_universe(r); + // write_universe(r); break; default: respond(r, nil);

@@ -145,26 +179,44 @@ break;

} } +void read_users(Req* r) { + char buf[2112] = {0}; + int i; + for (i = 0; i < 64; i++) { + if (scmp(users_table[i].name, "\0")) { + break; + } + scat(buf, users_table[i].name); + if (i == 63) { + ccat(buf, 0); + } else { + ccat(buf, '\n'); + } + } + readstr(r, buf); + respond(r, nil); +} + void xrxs_read(Req* r) { Aux* a = r->fid->file->aux; switch (a->type) { case USERS: - //read_users(r); + read_users(r); break; case CARTS: - //read_carts(r); + // read_carts(r); break; case SLOT: - //read_slot(r); + // read_slot(r); break; case DATA: - //read_data(r); + // read_data(r); break; case REALMS: - //read_realms(r); + // read_realms(r); break; case UNIVERSE: - //read_universe(r); + // read_universe(r); break; default: respond(r, nil);

@@ -212,6 +264,7 @@ return self;

} Srv fs = { + .attach = xrxs_attach, .open = fsopen, .read = xrxs_read, .write = xrxs_write,

@@ -247,22 +300,24 @@ fs.foreground = 1;

fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file); tree = fs.tree; closefile(createfile(tree->root, "carts", nil, 0444, create_aux(CARTS))); - closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0300, create_aux(CTL))); - String** carts = listdir("carts/"); + closefile( + createfile(tree->root, "ctl", nil, DMAPPEND | 0300, create_aux(CTL))); + closefile(createfile(tree->root, "users", nil, 0444, create_aux(USERS))); + /*String** carts = listdir("carts/"); cart = carts; - + while (*cart) { - // just concatenate the carts into a multiline string, and put it in CARTS.data - } + // just concatenate the carts into a multiline string, and put it in + CARTS.data + }*/ - - if (argc >= 3) { - if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0) - sysfatal("mountpoint %s does not exist", mtpt); + if (argc >= 3) { + if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0) + sysfatal("mountpoint %s does not exist", mtpt); - threadpostmountsrv(&fs, usocket, mtpt, MREPL | MCREATE); - threadexits(0); - } else { - srv(&fs); - } + threadpostmountsrv(&fs, usocket, mtpt, MREPL | MCREATE); + threadexits(0); + } else { + srv(&fs); + } }