ref: 8760478c61d8b96d9aab1511b8759daeb84378c5
parent: 05825e751d69cde554c21ddfd00e646049425e31
author: rodri <rgl@antares-labs.eu>
date: Wed Sep 27 11:59:53 EDT 2023
implemented spectator mode.
--- a/bts.c
+++ b/bts.c
@@ -26,6 +26,11 @@
CMwatching,
CMwin,
CMlose,
+ CMplayeroutlay,
+ CMplayerhit,
+ CMplayermiss,
+ CMplayerplays,
+ CMplayerwon,
};
Cmdtab svcmd[] = {
CMid, "id", 1,
@@ -41,9 +46,14 @@
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 @@
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 @@
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 @@
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);
+ }
}
}
@@ -756,6 +776,24 @@
}
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)
{
Cmdbuf *cb;
@@ -762,7 +800,7 @@
Cmdtab *ct;
Point2 cell;
uchar buf[BY2MAP];
- int i;
+ int i, idx;
if(debug)
fprint(2, "rcvd '%s'\n", cmd);
@@ -795,14 +833,12 @@
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 @@
snprint(oid, sizeof oid, "%s", cb->f[1]);
break;
case Watching:
- /* <idx?> <uid> (hit|missed) <coord> */
- /*
- * 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){
--- a/btsd.c
+++ b/btsd.c
@@ -162,6 +162,30 @@
}
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)
{
Chanpipe *cp;
@@ -316,8 +340,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,8 +464,9 @@
}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;
@@ -441,17 +473,19 @@
}
}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 @@
Finish:
if(debug)
fprint(2, "[%d] battleproc ending\n", getpid());
- free(stands.seats);
+ freeseats(&stands);
rmmatch(m);
freematch(m);
threadexits(nil);
--- a/dat.h
+++ b/dat.h
@@ -34,7 +34,7 @@
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 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;
--- a/fns.h
+++ b/fns.h
@@ -25,6 +25,7 @@
int min(int, int);
int bitpackmap(uchar*, ulong, Map*);
int bitunpackmap(Map*, uchar*, ulong);
+int chanvprint(Channel*, char*, va_list);
/*
* menulist
--- a/util.c
+++ b/util.c
@@ -197,3 +197,17 @@
}
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;
+}