From 8760478c61d8b96d9aab1511b8759daeb84378c5 Mon Sep 17 00:00:00 2001 From: rodri Date: Wed, 27 Sep 2023 15:59:53 +0000 Subject: implemented spectator mode. --- bts.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ btsd.c | 56 +++++++++++++++++++++++++++++++++++--------- dat.h | 8 +++++-- fns.h | 1 + util.c | 14 +++++++++++ 5 files changed, 135 insertions(+), 28 deletions(-) diff --git a/bts.c b/bts.c index 296fa5f..fe87836 100644 --- a/bts.c +++ b/bts.c @@ -26,6 +26,11 @@ enum { CMwatching, CMwin, CMlose, + CMplayeroutlay, + CMplayerhit, + CMplayermiss, + CMplayerplays, + CMplayerwon, }; Cmdtab svcmd[] = { CMid, "id", 1, @@ -41,9 +46,14 @@ Cmdtab svcmd[] = { CMmatchesb, "matches", 1, CMmatch, "m", 4, CMmatchese, "end", 1, - CMwatching, "watching", 6, + CMwatching, "watching", 4, CMwin, "win", 1, CMlose, "lose", 1, + CMplayeroutlay, "outlayed", 3, + CMplayerhit, "hit", 3, + CMplayermiss, "miss", 3, + CMplayerplays, "plays", 2, + CMplayerwon, "won", 2, }; int debug; @@ -310,11 +320,12 @@ drawinfo(Image *dst) static Image *c; Point p; char *s, aux[32]; + int i; s = ""; switch(game.state){ case Watching: - snprint(aux, sizeof aux, "watching %s vs. %s", match.pl[0], match.pl[1]); + snprint(aux, sizeof aux, "watching %s vs. %s", match.pl[0].uid, match.pl[1].uid); s = aux; break; case Ready: s = "looking for players"; break; @@ -333,9 +344,9 @@ drawinfo(Image *dst) vstring(dst, p, display->white, ZP, font, s); p = Pt(alienboard.bbox.max.x+2, alienboard.bbox.min.y); - vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[1]: oid); + vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[1].uid: oid); p = subpt(localboard.bbox.min, Pt(font->width+2,0)); - vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[0]: uid); + vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[0].uid: uid); if(game.state == Outlaying){ if(c == nil) @@ -349,6 +360,15 @@ drawinfo(Image *dst) p = Pt(SCRW/2 - stringwidth(font, s)/2, SCRH-Boardmargin); string(dst, p, c, ZP, font, s); } + }else if(game.state == Watching){ + if(c == nil) + c = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellow); + for(i = 0; i < nelem(match.pl); i++) + if(match.pl[i].state == Playing){ + snprint(aux, sizeof aux, "it's %s's turn", match.pl[i].uid); + p = Pt(SCRW/2 - stringwidth(font, aux)/2, SCRH-Boardmargin); + string(dst, p, c, ZP, font, aux); + } } } @@ -755,6 +775,24 @@ keelhaul(void) conclusion.s = s; } +void +announcewinner(char *winner) +{ + static Image *c; + static char s[16]; + + if(winner == nil) + return; + + /* TODO build a global color palette. this static color referencing is BS. */ + if(c == nil) + c = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen); + + snprint(s, sizeof s, "%s WON", winner); + conclusion.c = c; + conclusion.s = s; +} + void processcmd(char *cmd) { @@ -762,7 +800,7 @@ processcmd(char *cmd) Cmdtab *ct; Point2 cell; uchar buf[BY2MAP]; - int i; + int i, idx; if(debug) fprint(2, "rcvd '%s'\n", cmd); @@ -795,14 +833,12 @@ processcmd(char *cmd) matches->filling = 0; else if(ct->index == CMwatching){ match.id = strtoul(cb->f[1], nil, 10); - match.pl[0] = estrdup(cb->f[2]); - match.pl[1] = estrdup(cb->f[3]); + snprint(match.pl[0].uid, sizeof match.pl[0].uid, "%s", cb->f[2]); + snprint(match.pl[1].uid, sizeof match.pl[1].uid, "%s", cb->f[3]); + match.pl[0].state = Outlaying; + match.pl[1].state = Outlaying; match.bl[0] = &localboard; match.bl[1] = &alienboard; - dec64(buf, sizeof buf, cb->f[4], strlen(cb->f[4])); - bitunpackmap(match.bl[0], buf, sizeof buf); - dec64(buf, sizeof buf, cb->f[5], strlen(cb->f[5])); - bitunpackmap(match.bl[1], buf, sizeof buf); game.state = Watching; } break; @@ -814,10 +850,28 @@ processcmd(char *cmd) snprint(oid, sizeof oid, "%s", cb->f[1]); break; case Watching: - /* (hit|missed) */ - /* - * TODO can't use the id as the key because they can collide. - */ + if(ct->index == CMplayeroutlay){ + idx = strtoul(cb->f[1], nil, 10); + if(dec64(buf, sizeof buf, cb->f[2], strlen(cb->f[2])) < 0) + sysfatal("dec64 failed"); + bitunpackmap(match.bl[idx], buf, sizeof buf); + match.pl[idx].state = Waiting; + }else if(ct->index == CMplayerhit){ + idx = strtoul(cb->f[1], nil, 10); + cell = coords2cell(cb->f[2]); + settile(match.bl[idx^1], cell, Thit); + }else if(ct->index == CMplayermiss){ + idx = strtoul(cb->f[1], nil, 10); + cell = coords2cell(cb->f[2]); + settile(match.bl[idx^1], cell, Tmiss); + }else if(ct->index == CMplayerplays){ + idx = strtoul(cb->f[1], nil, 10); + match.pl[idx].state = Playing; + match.pl[idx^1].state = Waiting; + }else if(ct->index == CMplayerwon){ + idx = strtoul(cb->f[1], nil, 10); + announcewinner(match.pl[idx].uid); + } break; case Outlaying: if(ct->index == CMwait){ diff --git a/btsd.c b/btsd.c index 0df1c52..b691128 100644 --- a/btsd.c +++ b/btsd.c @@ -161,6 +161,30 @@ leaveseat(Stands *s, Player *p) memmove(&s->seats[i], &s->seats[i+1], --s->nused * sizeof p); } +void +freeseats(Stands *s) +{ + int i; + + for(i = 0; i < s->nused; i++){ + s->seats[i]->state = Waiting0; + s->seats[i]->battle = nil; + } + free(s->seats); +} + +void +broadcast(Stands *s, char *fmt, ...) +{ + va_list arg; + int i; + + va_start(arg, fmt); + for(i = 0; i < s->nused; i++) + chanvprint(s->seats[i]->io.out, fmt, arg); + va_end(arg); +} + void netrecvthread(void *arg) { @@ -316,8 +340,8 @@ battleproc(void *arg) Cmdbuf *cb; Cmdtab *ct; Player *p, *op; - Stands stands; - uchar buf1[BY2MAP], buf2[BY2MAP]; + Stands stands; /* TODO make this a member of Match */ + uchar buf[BY2MAP]; uint n0; Point2 cell; @@ -367,6 +391,8 @@ battleproc(void *arg) settiles(p, cell, orient, shiplen(i), Tship); } p->state = Waiting; + bitpackmap(buf, sizeof buf, p); + broadcast(&stands, "outlayed %d %.*[\n", p == m->pl[0]? 0: 1, sizeof buf, buf); if(op->state == Waiting){ if(debug){ fprint(2, "%s's map:\n", p->name); @@ -380,6 +406,7 @@ battleproc(void *arg) chanprint(m->pl[n0%2]->io.out, "play\n"); m->pl[n0%2]->state = Playing; chanprint(m->pl[(n0+1)%2]->io.out, "wait\n"); + broadcast(&stands, "plays %d\n", n0%2); } } break; @@ -391,6 +418,7 @@ battleproc(void *arg) settile(op, cell, Thit); chanprint(p->io.out, "hit\n"); chanprint(op->io.out, "hit %s\n", cell2coords(cell)); + broadcast(&stands, "hit %d %s\n", p == m->pl[0]? 0: 1, cell2coords(cell)); if(countshipcells(op) < (debug? 17: 1)){ chanprint(p->io.out, "win\n"); chanprint(op->io.out, "lose\n"); @@ -398,6 +426,7 @@ battleproc(void *arg) p->battle = nil; op->state = Waiting0; op->battle = nil; + broadcast(&stands, "won %d\n", p == m->pl[0]? 0: 1); freemsg(msg); goto Finish; } @@ -406,11 +435,13 @@ battleproc(void *arg) settile(op, cell, Tmiss); chanprint(p->io.out, "miss\n"); chanprint(op->io.out, "miss %s\n", cell2coords(cell)); + broadcast(&stands, "miss %d %s\n", p == m->pl[0]? 0: 1, cell2coords(cell)); Swapturn: chanprint(p->io.out, "wait\n"); chanprint(op->io.out, "play\n"); p->state = Waiting; op->state = Playing; + broadcast(&stands, "plays %d\n", op == m->pl[0]? 0: 1); break; } if(debug) @@ -433,25 +464,28 @@ Nocmd: }else{ op = p == m->pl[0]? m->pl[1]: m->pl[0]; chanprint(op->io.out, "win\n"); - op->battle = nil; op->state = Waiting0; + op->battle = nil; + broadcast(&stands, "won %d\n", op == m->pl[0]? 0: 1); freeplayer(p); freemsg(msg); goto Finish; } }else if(strcmp(msg->body, "take seat") == 0){ takeseat(&stands, p); - p->battle = m; p->state = Watching; - bitpackmap(buf1, sizeof buf1, m->pl[0]); - bitpackmap(buf2, sizeof buf2, m->pl[1]); - chanprint(p->io.out, "watching %d %s %s %.*[ %.*[\n", - m->id, m->pl[0]->name, m->pl[1]->name, - sizeof buf1, buf1, sizeof buf2, buf2); + p->battle = m; + chanprint(p->io.out, "watching %d %s %s\n", + m->id, m->pl[0]->name, m->pl[1]->name); + for(i = 0; i < nelem(m->pl); i++) + if(m->pl[i]->state != Outlaying){ + bitpackmap(buf, sizeof buf, m->pl[i]); + chanprint(p->io.out, "outlayed %d %.*[\n", i, sizeof buf, buf); + } }else if(strcmp(msg->body, "leave seat") == 0){ leaveseat(&stands, p); - p->battle = nil; p->state = Waiting0; + p->battle = nil; } freemsg(msg); @@ -461,7 +495,7 @@ Nocmd: Finish: if(debug) fprint(2, "[%d] battleproc ending\n", getpid()); - free(stands.seats); + freeseats(&stands); rmmatch(m); freematch(m); threadexits(nil); diff --git a/dat.h b/dat.h index bef3596..f53caf9 100644 --- a/dat.h +++ b/dat.h @@ -34,7 +34,7 @@ enum { SCRH = Boardmargin+MAPH*TH+TH+MAPH*TH+Boardmargin, KB = 1024, - BY2MAP = TBITS*MAPW*MAPH/8+1, + BY2MAP = (TBITS*MAPW*MAPH+7)/8, }; typedef struct Ship Ship; @@ -114,8 +114,12 @@ struct Stands struct MatchInfo { int id; - char *pl[2]; + struct { + char uid[8+1]; + int state; + } pl[2]; Board *bl[2]; + char conclusion[16]; }; typedef struct Mentry Mentry; diff --git a/fns.h b/fns.h index 65eb0c4..58ba86e 100644 --- a/fns.h +++ b/fns.h @@ -25,6 +25,7 @@ int max(int, int); int min(int, int); int bitpackmap(uchar*, ulong, Map*); int bitunpackmap(Map*, uchar*, ulong); +int chanvprint(Channel*, char*, va_list); /* * menulist diff --git a/util.c b/util.c index eee2a3d..585f81b 100644 --- a/util.c +++ b/util.c @@ -197,3 +197,17 @@ bitunpackmap(Map *m, uchar *buf, ulong len) } return n+1; } + +int +chanvprint(Channel *c, char *fmt, va_list arg) +{ + char *p; + int n; + + p = vsmprint(fmt, arg); + if(p == nil) + sysfatal("vsmprint failed: %r"); + n = sendp(c, p); + yield(); /* let recipient handle message immediately */ + return n; +} -- cgit v1.2.3