update DESIGN document, put skeleton code in place
This commit is contained in:
parent
a0163016c2
commit
28c2e3cac1
3 changed files with 88 additions and 37 deletions
45
DESIGN
45
DESIGN
|
@ -1,5 +1,6 @@
|
||||||
[9p directory structure]
|
[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
|
load CART: load a cartridge
|
||||||
create REALM: create a new realm (start a new game or app session)
|
create REALM: create a new realm (start a new game or app session)
|
||||||
protect REALM: protect the realm with auth data
|
protect REALM: protect the realm with auth data
|
||||||
|
@ -8,25 +9,45 @@
|
||||||
save: save the current universe to the realm
|
save: save the current universe to the realm
|
||||||
reset: reset the cartridge (attempts to leave the realm, but doesn't save)
|
reset: reset the cartridge (attempts to leave the realm, but doesn't save)
|
||||||
unload: unload the cartridge
|
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;
|
realms can either be solo, open, or protected;
|
||||||
open or protected realms can have limited member numbers
|
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
|
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/REALM/index: realm data readable by the client before entering, eg:
|
realmx 1 4 1
|
||||||
1 4 1
|
|
||||||
first number is number of members
|
first number is number of members
|
||||||
second is member limit
|
second is member limit
|
||||||
third is 1 if protected, 0 if not
|
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 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)
|
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)
|
80
xrxs.c
80
xrxs.c
|
@ -29,20 +29,28 @@ char *ccat(char *dst, char c) { int len = slen(dst); dst[len] = c; dst[len + 1]
|
||||||
|
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
int chatty9p = 1;
|
int chatty9p = 1;
|
||||||
static int serving = 1;
|
|
||||||
|
|
||||||
static char Ebad[] = "something bad happened";
|
static char Ebad[] = "something bad happened";
|
||||||
static char Enomem[] = "no memory";
|
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;
|
typedef struct Aux Aux;
|
||||||
struct Aux {
|
struct Aux {
|
||||||
int type;
|
FileType type;
|
||||||
char* data;
|
char* data;
|
||||||
int count;
|
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) {
|
void fsread(Req* r) {
|
||||||
Aux* a;
|
Aux* a;
|
||||||
vlong offset;
|
vlong offset;
|
||||||
|
@ -102,7 +110,7 @@ void fswrite(Req* r) {
|
||||||
respond(r, nil);
|
respond(r, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlectl(Req* r) {
|
void write_ctl(Req* r) {
|
||||||
char cmd[16];
|
char cmd[16];
|
||||||
char* c = r->ifcall.data;
|
char* c = r->ifcall.data;
|
||||||
int i;
|
int i;
|
||||||
|
@ -114,33 +122,61 @@ void handlectl(Req* r) {
|
||||||
unsigned long long const cmd_hashv = hash(cmd);
|
unsigned long long const cmd_hashv = hash(cmd);
|
||||||
|
|
||||||
switch (cmd_hashv) {
|
switch (cmd_hashv) {
|
||||||
case SHUTDOWN_HASHV:
|
|
||||||
printf("what");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
r->ofcall.count = r->ifcall.count;
|
r->ofcall.count = r->ifcall.count;
|
||||||
r->fid->file->dir.length = 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;
|
Aux* a = r->fid->file->aux;
|
||||||
switch (a->type) {
|
switch (a->type) {
|
||||||
case CTL:
|
case CTL:
|
||||||
handlectl(r);
|
write_ctl(r);
|
||||||
|
break;
|
||||||
|
case UNIVERSE:
|
||||||
|
//write_universe(r);
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
respond(r, nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fsopen(Req* r) { respond(r, nil); }
|
void fsopen(Req* r) { respond(r, nil); }
|
||||||
|
|
||||||
void wstat(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;
|
Aux* a = f->aux;
|
||||||
if (a && a->data) {
|
if (a && a->data) {
|
||||||
free(a->data);
|
free(a->data);
|
||||||
|
@ -177,8 +213,8 @@ String** listdir(char* path) {
|
||||||
|
|
||||||
Srv fs = {
|
Srv fs = {
|
||||||
.open = fsopen,
|
.open = fsopen,
|
||||||
.read = fsread,
|
.read = xrs_read,
|
||||||
.write = xrxswrite,
|
.write = xrxs_write,
|
||||||
.create = fsopen,
|
.create = fsopen,
|
||||||
.wstat = wstat};
|
.wstat = wstat};
|
||||||
|
|
||||||
|
@ -190,10 +226,6 @@ void threadmain(int argc, char* argv[]) {
|
||||||
char* usocket = nil;
|
char* usocket = nil;
|
||||||
int i;
|
int i;
|
||||||
String** cart;
|
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 -m PATH is supplied, mount on PATH */
|
||||||
/* if -s NAME is supplied, create a socket for the namespace */
|
/* 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.foreground = 1;
|
||||||
fs.tree = alloctree(nil, nil, DMDIR | 0777, fsdestroyfile);
|
fs.tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file);
|
||||||
tree = fs.tree;
|
tree = fs.tree;
|
||||||
closefile(createfile(tree->root, "carts", nil, DMDIR | 0555, nil));
|
closefile(createfile(tree->root, "carts", nil, 0444, create_aux(CARTS)));
|
||||||
closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0300, ctlfile));
|
closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0300, create_aux(CTL)));
|
||||||
String** carts = listdir("carts/");
|
String** carts = listdir("carts/");
|
||||||
cart = carts;
|
cart = carts;
|
||||||
|
|
||||||
while (*cart) {
|
while (*cart) {
|
||||||
closefile(
|
// just concatenate the carts into a multiline string, and put it in CARTS.data
|
||||||
createfile(walkfile(tree->root, "carts"), (*cart)->base, nil, 0444, nil));
|
|
||||||
cart++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (serving) {
|
|
||||||
if (argc >= 3) {
|
if (argc >= 3) {
|
||||||
if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
|
if (mtpt != nil && access(mtpt, AEXIST) < 0 && access(mtpt, AEXIST) < 0)
|
||||||
sysfatal("mountpoint %s does not exist", mtpt);
|
sysfatal("mountpoint %s does not exist", mtpt);
|
||||||
|
@ -234,5 +265,4 @@ void threadmain(int argc, char* argv[]) {
|
||||||
} else {
|
} else {
|
||||||
srv(&fs);
|
srv(&fs);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue