update DESIGN document, put skeleton code in place
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmDcFMkACgkQO3+8IhRO Y5hWOQ/+PTjLMyGD4z04T1ijTl4hNGnAWXOZHbjwxhCmojFTGRRFWcomxQ6kPgWI WNIcIo49YUXecnjzHgYFaRnnWFd/bQpTGQ/AiwSEUSydfJO/359tACzeBuWNw3Gm +tk4mV8tiOdJ336iyYKS1AmI0V2mpm0VDBX7ZUp1u5i5X1bHXd+U0PXs64/dWfBj x9kGcONnW1LVQyX/HbW02aSiAscw8/YoQg5RpZfcLg0xvO9o9YySxcilbowKhbUt r/hAejnU3JsrvSm9ko54bEKR7G5uc9yDTdLTxceWN3U6jBDd6NvlC0js02q0jhGJ vi6AK6JxjnARacOHSequj83jbMZTY/BYMQHrS+beNy6Cz2ZFPonE292onriLK3ud yPXFfWqOcD0TK9NwiWNwCTT5zgqqOyDpyM0ZNqacCJ1E91N/Rzr2BCFip6L698b2 D2bh0uAi5w09bRJMvtZbdncO94j7OAHy7T3X5hadwHuopvwKjarir6g1VK48ACm7 KbxiXkba0OTt65ieFCndCA2N2F3Gqfn9tp5bdllXvsjqRZ2p+OjmPTGIJdcmJzlC uayw1pjypYklBeYgSGS+XFUrr2fi5P6Vq3K2poZLgzTw7PNBaq8nfHX8wqMDciNm 8pXmMeW2heF3z7UuGciMFE6Ij5Uu02iO7vaHjOvq73aE6dEMTQ8= =GWnR -----END PGP SIGNATURE-----
M
DESIGN
→
DESIGN
@@ -1,5 +1,6 @@
[9p directory structure] -/ctl: control file for inputing system commands +/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@@ -8,25 +9,45 @@ 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 - shutdown: disconnect from the xrxs server + + 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 -/carts/: available game/app cartridges for this server +/data/: any supporting data that comes with the cartridge will be found here; read only -/realms/: open/saved realms +/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 - -/realm/REALM/index: realm data readable by the client before entering, eg: - 1 4 1 + 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) -/realms/REALM/members: members of the realm -realms/REALM/allowed: allowed membernames (if empty, anyone is allowed) -realms/REALM/password: realm password -realms/REALM/universe/: universe data synced to the realm -/universe/: arbitrary game data not yet synced to the realm+/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)
M
xrxs.c
→
xrxs.c
@@ -29,19 +29,27 @@ char *ccat(char *dst, char c) { int len = slen(dst); dst[len] = c; dst[len + 1] = '\0'; return dst; }
/* clang-format on */ int chatty9p = 1; -static int serving = 1; static char Ebad[] = "something bad happened"; static char Enomem[] = "no memory"; -enum { CTL = 1, SLOT, CART, REALM, UNIVERSE }; +typedef enum { CTL = 1, USERS, CARTS, SLOT, DATA, REALMS, UNIVERSE } FileType; typedef struct Aux Aux; struct Aux { - int type; + FileType type; char* data; int count; }; + +Aux* create_aux(FileType t) { + Aux* self = (Aux*)malloc(sizeof(Aux)); + self.type = t; + self.data = nil; + self.count = 0; + + return self; +} void fsread(Req* r) { Aux* a;@@ -102,7 +110,7 @@ r->ofcall.count = count;
respond(r, nil); } -void handlectl(Req* r) { +void write_ctl(Req* r) { char cmd[16]; char* c = r->ifcall.data; int i;@@ -114,33 +122,61 @@
unsigned long long const cmd_hashv = hash(cmd); switch (cmd_hashv) { - case SHUTDOWN_HASHV: - printf("what"); - break; default: break; } r->ofcall.count = r->ifcall.count; r->fid->file->dir.length = r->ifcall.count; + respond(r, nil); } -void xrxswrite(Req* r) { +void xrxs_write(Req* r) { Aux* a = r->fid->file->aux; switch (a->type) { case CTL: - handlectl(r); + write_ctl(r); + break; + case UNIVERSE: + //write_universe(r); break; default: + respond(r, nil); break; } - respond(r, nil); +} + +void xrxs_read(Req* r) { + Aux* a = r->fid->file->aux; + switch (a->type) { + case USERS: + //read_users(r); + break; + case CARTS: + //read_carts(r); + break; + case SLOT: + //read_slot(r); + break; + case DATA: + //read_data(r); + break; + case REALMS: + //read_realms(r); + break; + case UNIVERSE: + //read_universe(r); + break; + default: + respond(r, nil); + break; + } } void fsopen(Req* r) { respond(r, nil); } void wstat(Req* r) { respond(r, nil); } -void fsdestroyfile(File* f) { +void fs_destroy_file(File* f) { Aux* a = f->aux; if (a && a->data) { free(a->data);@@ -177,8 +213,8 @@ }
Srv fs = { .open = fsopen, - .read = fsread, - .write = xrxswrite, + .read = xrs_read, + .write = xrxs_write, .create = fsopen, .wstat = wstat};@@ -190,10 +226,6 @@ char* mtpt = nil;
char* usocket = nil; int i; String** cart; - Aux* ctlfile = malloc(sizeof(Aux)); - ctlfile->type = CTL; - ctlfile->data = malloc(256 * sizeof(char)); - ctlfile->count = 0; /* if -m PATH is supplied, mount on PATH */ /* if -s NAME is supplied, create a socket for the namespace */@@ -212,19 +244,18 @@ }
} fs.foreground = 1; - fs.tree = alloctree(nil, nil, DMDIR | 0777, fsdestroyfile); + fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file); tree = fs.tree; - closefile(createfile(tree->root, "carts", nil, DMDIR | 0555, nil)); - closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0300, ctlfile)); + 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/"); cart = carts; + while (*cart) { - closefile( - createfile(walkfile(tree->root, "carts"), (*cart)->base, nil, 0444, nil)); - cart++; + // just concatenate the carts into a multiline string, and put it in CARTS.data } - while (serving) { + if (argc >= 3) { if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0) sysfatal("mountpoint %s does not exist", mtpt);@@ -234,5 +265,4 @@ threadexits(0);
} else { srv(&fs); } - } }