update DESIGN document, put skeleton code in place

This commit is contained in:
Iris Lightshard 2021-06-30 00:52:57 -06:00
parent a0163016c2
commit 28c2e3cac1
Signed by: Iris Lightshard
GPG key ID: 3B7FBC22144E6398
3 changed files with 88 additions and 37 deletions

45
DESIGN
View file

@ -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 @@
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?
/carts/: available game/app cartridges for this server
/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
/realms/: open/saved realms
/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
/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)

View file

80
xrxs.c
View file

@ -29,20 +29,28 @@ char *ccat(char *dst, char c) { int len = slen(dst); dst[len] = c; dst[len + 1]
/* 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;
vlong offset;
@ -102,7 +110,7 @@ void fswrite(Req* r) {
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 @@ void handlectl(Req* r) {
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;
}
}
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;
}
respond(r, nil);
}
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 @@ String** listdir(char* path) {
Srv fs = {
.open = fsopen,
.read = fsread,
.write = xrxswrite,
.read = xrs_read,
.write = xrxs_write,
.create = fsopen,
.wstat = wstat};
@ -190,10 +226,6 @@ void threadmain(int argc, char* argv[]) {
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 @@ void threadmain(int argc, char* argv[]) {
}
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 @@ void threadmain(int argc, char* argv[]) {
} else {
srv(&fs);
}
}
}