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]
|
||||
/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)
|
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 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue