#include #include #include #include #include #include #include #include extern const char *__progname; #include "oq.h" #include "mjp.h" #include "msg.h" #include "csget.h" #include "client.h" #include "strings.h" #include "pollloop.h" #define RBUFSIZE 8192 unsigned char wallpresent[WALLSIZE]; unsigned char replpresent[REPLSIZE]; unsigned char dorapresent[DORASIZE]; TILE doratile[DORASIZE]; char *windnames[NWIND]; int mywind; int nhand[NWIND]; TILE hand[NWIND][MAXHAND]; int nmeld[NWIND]; MELD melds[NWIND][MAXMELDS]; int ndiscard[NWIND]; TILE discard[NWIND][DECKSIZE]; int last_discard_to; int nfs[NWIND]; TILE fs[NWIND][MAXFS]; int riichi_at[NWIND]; typedef enum { PQK_NONE = 1, PQK_INPUT, PQK_CB } PQKIND; typedef enum { PQP_PRE = 1, PQP_LIVE, PQP_DONE } PQPHASE; typedef struct rops ROPS; typedef struct pq PQ; typedef struct pqstate PQSTATE; struct pqstate { PQPHASE phase; ROPS *old_ops; PQ *pq; PQ **tail; void *arg; void *priv; } ; struct pq { PQ *link; PQKIND kind; union { struct { void (*pre)(PQSTATE *); int (*rdfn)(PQSTATE *, unsigned char *, int); void (*post)(PQSTATE *); void *arg; } input; void (*cb)(void); } ; } ; struct rops { int (*rd)(unsigned char *, int); } ; #define OPS(suff) { &rd_##suff } static const char *debugfile = 0; static FILE *debugf = 0; static int debug_havech; static unsigned char debug_ch; static int servfd; static OQ servoq; static char rbuf[RBUFSIZE]; static int rfill; static int rptr; static ROPS *rops; static const char *serverhost; static int ioid; static int servblockid; static PQSTATE pqs; static int mypid; static char *playername[NWIND]; static int handwind; static int playerwind[NWIND]; static int break_roll_1; static int break_roll_2; static TILE move_tile; static PLACE move_src; static PLACE move_dst; static int want_notify; static int notify_id; static const char *unimpl_text; static struct timeval unimpl_time; static int unimpl_id; static int chat_src; static unsigned int chat_othermask; static char *chat_text; static int chat_len; static char *reject_text; static int reject_len; static void handleargs(int ac, char **av) { int skip; int errs; int i; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-debug")) { WANTARG(); debugfile = av[skip]; continue; } if (!strcmp(*av,"-server")) { WANTARG(); serverhost = av[skip]; continue; } #undef WANTARG i = client_flag(*av); switch (i) { case CLIENT_FLAG_UNKNOWN: fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; break; case CLIENT_FLAG_ERRORED: errs ++; break; default: if (i < 0) abort(); if (i >= ac) goto needarg; client_args((const void *)(av+1)); skip = i; break; } } if (errs) exit(1); } static void debugf_open(void) { if (debugf) return; if (! debugf) { debugf = fopen(debugfile,"w"); if (! debugf) { fprintf(stderr,"%s: %s: %s\n",__progname,debugfile,strerror(errno)); exit(1); } } } void dbg(const char *fmt, ...) { va_list ap; if (! debugfile) return; debugf_open(); va_start(ap,fmt); vfprintf(debugf,fmt,ap); va_end(ap); } void dbgwrite(const char *buf, int len) { if (! debugfile) return; debugf_open(); fwrite(buf,1,len,debugf); } void dbg_vis_data_open(void) { debug_havech = 0; } void dbg_vis_data_one(unsigned char ch) { char enc[5]; if (debug_havech) { switch (debug_ch) { case '\a': dbgwrite("\\a",2); break; case '\b': dbgwrite("\\b",2); break; case '\f': dbgwrite("\\f",2); break; case '\n': dbgwrite("\\n",2); break; case '\r': dbgwrite("\\r",2); break; case ' ': dbgwrite("\\s",2); break; case '\t': dbgwrite("\\t",2); break; case '\v': dbgwrite("\\v",2); break; default: if ((debug_ch < 32) || ((debug_ch > 126) && (debug_ch < 160))) { dbgwrite(&enc[0],sprintf(&enc[0],"\\%*o",((ch<'0')||(ch>'9'))?1:3,debug_ch)); } else { dbgwrite(&debug_ch,1); } break; } } debug_havech = 1; debug_ch = ch; } void dbg_vis_data_part(const char *data, int len) { const unsigned char *dp; int i; dp = (const void *)data; for (i=0;iai_next) { s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(errno)); continue; } if (connect(s,(void *)ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: connect: %s\n",__progname,strerror(errno)); close(s); continue; } freeaddrinfo(ai0); servfd = s; return; } exit(1); } static int rtest_serv(void *arg __attribute__((__unused__))) { return(!!rops); } static int wtest_serv(void *arg __attribute__((__unused__))) { return(oq_nonempty(&servoq)); } static void rd_serv(void *arg __attribute__((__unused__))) { int r; int n; if (! rops) return; if (rfill < RBUFSIZE) { r = read(servfd,&rbuf[rfill],RBUFSIZE-rfill); if (r < 0) { fprintf(stderr,"%s: read: %s\n",__progname,strerror(errno)); exit(1); } if (r == 0) { fprintf(stderr,"%s: read: unexpected EOF\n",__progname); exit(1); } if (debugfile) { dbg("Server read %d: ",r); dbg_vis_data_single(&rbuf[rfill],r); dbg("\n"); } rfill += r; } while ((rptr < rfill) && rops) { n = (*rops->rd)(&rbuf[rptr],rfill-rptr); if (n > 0) rptr += n; else break; } if (rptr >= rfill) { rfill = 0; rptr = 0; } else if (rptr >= (RBUFSIZE/2)) { bcopy(&rbuf[rptr],&rbuf[0],rfill-rptr); rfill -= rptr; rptr = 0; } } static void wr_serv(void *arg __attribute__((__unused__))) { int w; w = oq_writev(&servoq,servfd); if (w < 0) { fprintf(stderr,"%s: writev: %s\n",__progname,strerror(errno)); exit(1); } if (w > 0) { if (debugfile) { dbg("Server write %d: ",w); dbg_vis_data_open(); oq_cbwalk(&servoq,&dbg_vis_data_part,w); dbg_vis_data_close(); dbg("\n"); } oq_dropdata(&servoq,w); } } static int block_serv(void *arg __attribute__((__unused__))) { return(BLOCK_NIL); } static int fixed_len_number(const unsigned char *buf, int len, int *vp) { int i; int v; int d; v = 0; for (i=0;ikind) { default: abort(); break; case PQK_INPUT: pqs.arg = pqs.pq->input.arg; if (pqs.pq->input.pre) (*pqs.pq->input.pre)(&pqs); break; case PQK_CB: break; } break; case PQP_LIVE: switch (pqs.pq->kind) { default: abort(); break; case PQK_INPUT: { int n; if (len < 1) return(consumed); n = (*pqs.pq->input.rdfn)(&pqs,buf,len); if ((n < 0) || (n > len)) abort(); consumed += n; buf += n; len -= n; } break; case PQK_CB: (*pqs.pq->cb)(); pqs.phase = PQP_DONE; break; } break; case PQP_DONE: { PQ *q; switch (pqs.pq->kind) { default: abort(); break; case PQK_INPUT: if (pqs.pq->input.post) (*pqs.pq->input.post)(&pqs); break; case PQK_CB: break; } q = pqs.pq; pqs.pq = q->link; free(q); if (! pqs.pq) rops = pqs.old_ops; pqs.phase = PQP_PRE; } break; } } return(consumed); } static ROPS pq_ops = OPS(pq); static PQ *pq_append(void) { PQ *q; if (rops != &pq_ops) { pqs.phase = PQP_PRE; pqs.old_ops = rops; pqs.pq = 0; pqs.tail = &pqs.pq; pqs.priv = 0; rops = &pq_ops; } q = malloc(sizeof(PQ)); *pqs.tail = q; pqs.tail = &q->link; q->link = 0; q->kind = PQK_NONE; return(q); } static void pq_queue_input(void (*pre)(PQSTATE *), int (*rd)(PQSTATE *, unsigned char *, int), void (*post)(PQSTATE *), void *arg) { PQ *q; q = pq_append(); q->kind = PQK_INPUT; q->input.pre = pre; q->input.rdfn = rd; q->input.post = post; q->input.arg = arg; } static void pq_queue_cb(void (*cb)(void)) { PQ *q; q = pq_append(); q->kind = PQK_CB; q->cb = cb; } static void pq_r_done(PQSTATE *s) { if (s->phase != PQP_LIVE) abort(); s->phase = PQP_DONE; } static void *pq_r_arg(PQSTATE *s) { switch (s->phase) { case PQP_LIVE: case PQP_DONE: break; default: abort(); break; } return(s->arg); } static void *pq_r_getpriv(PQSTATE *s) { switch (s->phase) { case PQP_LIVE: case PQP_DONE: break; default: abort(); break; } return(s->priv); } static void pq_r_setpriv(PQSTATE *s, void *v) { switch (s->phase) { case PQP_LIVE: case PQP_DONE: break; default: abort(); break; } s->priv = v; } static int r_playerid(PQSTATE *s, unsigned char *buf, int len) { int i; int j; for (i=0;i= 0) && (j < NWIND)) { *(int *)pq_r_arg(s) = j; pq_r_done(s); return(i+1); } fprintf(stderr,"%s: server sent bad wind %02x\n",__progname,buf[i]); exit(1); } return(len); } static void pq_queue_input_wind(int *loc) { pq_queue_input(0,&r_wind,0,loc); } static int r_d6(PQSTATE *s, unsigned char *buf, int len) { int i; for (i=0;i= len) return(len); j = 1; switch (buf[i]) { case 'k': case 'K': *(PLACE *)pq_r_arg(s) = makeplace(PT_DECK); pq_r_done(s); return(i+1); break; case 'w': case 'W': if (i+3 >= len) return(i); if (fixed_len_number(buf+i+1,3,&j)) { *(PLACE *)pq_r_arg(s) = makeplace(PT_WALL,j); pq_r_done(s); return(i+4); } j = 4; break; case 'r': case 'R': if (i+2 >= len) return(i); if (fixed_len_number(buf+i+1,2,&j)) { *(PLACE *)pq_r_arg(s) = makeplace(PT_REPL,j); pq_r_done(s); return(i+3); } j = 3; break; case 'a': case 'A': if (i+1 >= len) return(i); if (fixed_len_number(buf+i+1,1,&j)) { *(PLACE *)pq_r_arg(s) = makeplace(PT_DORA,j); pq_r_done(s); return(i+2); } j = 3; break; { PLACETYPE pt; case 'h': case 'H': pt = PT_HAND; if (0) { case 'd': case 'D': pt = PT_DISC; } if (0) { case 'f': case 'F': pt = PT_FS; } if (i+1 >= len) return(i); j = pidx(buf[i+1]); if ((j >= 0) || (j < NWIND)) { *(PLACE *)pq_r_arg(s) = makeplace(pt,j); pq_r_done(s); return(i+2); } j = 2; break; } case 'm': case 'M': if (i+2 >= len) return(i); j = pidx(buf[i+1]); if ((j >= 0) || (j < NWIND)) { if (fixed_len_number(buf+i+2,1,&k)) *(PLACE *)pq_r_arg(s) = makeplace(PT_MELD,j,k); pq_r_done(s); return(i+3); } j = 3; break; } fprintf(stderr,"%s: server sent bad place",__progname); for (;j>0;j--) fprintf(stderr," %02x",buf[i++]); fprintf(stderr,"\n"); exit(1); } static void pq_queue_input_place(PLACE *loc) { pq_queue_input(0,&r_place,0,loc); } typedef struct q_tile Q_TILE; struct q_tile { TILE *loc; int notile_ok; } ; static int r_tile(PQSTATE *s, unsigned char *buf, int len) { Q_TILE *q; int n; static int l[TILE__N] = { -1 }; static int maxl; int i; int any; q = pq_r_arg(s); n = 0; while ((len > 0) && whitespace(buf[0])) { n ++; len --; buf ++; } if (len < 1) return(n); if (q->notile_ok && (buf[0] == '?')) { *q->loc = TILE_UNKTILE; pq_r_done(s); return(n+1); } if (l[0] < 0) { maxl = 0; for (i=TILE__N-1;i>=0;i--) { l[i] = strlen(tilenames[i]); if (l[i] > maxl) maxl = l[i]; } } for (i=TILE__N-1;i>=0;i--) { if (len < l[i]) { if (! bcmp(tilenames[i],buf,len)) any = 1; } else { if (! bcmp(tilenames[i],buf,l[i])) { *q->loc = i; pq_r_done(s); return(n+l[i]); } } } if (! any) { fprintf(stderr,"%s: server sent bad tile name",__progname); for (i=0;(iloc = loc; q->notile_ok = notile_ok; pq_queue_input(0,&r_tile,&post_tile,q); } typedef struct q_cstring Q_CSTRING; struct q_cstring { char **strp; int *lenp; int maxlen; } ; static void pre_cstring(PQSTATE *s) { Q_CSTRING *q; q = pq_r_arg(s); pq_r_setpriv(s,csget_start(q->maxlen)); } static int r_cstring(PQSTATE *s, unsigned char *buf, int len) { Q_CSTRING *q; CSGET *csg; int i; q = pq_r_arg(s); csg = pq_r_getpriv(s); for (i=0;ilenp) { *q->strp = csget_result_len(csg,q->lenp); } else { *q->strp = csget_result_nul(csg); } pq_r_done(s); return(i+1); } } return(len); } static void post_cstring(PQSTATE *s) { free(pq_r_arg(s)); } static void pq_queue_input_cs_nul(char **strp, int maxlen) { Q_CSTRING *q; q = malloc(sizeof(Q_CSTRING)); q->strp = strp; q->lenp = 0; q->maxlen = maxlen; pq_queue_input(&pre_cstring,&r_cstring,&post_cstring,q); } static void pq_queue_input_cs_len(char **strp, int *lenp, int maxlen) { Q_CSTRING *q; q = malloc(sizeof(Q_CSTRING)); q->strp = strp; q->lenp = lenp; q->maxlen = maxlen; pq_queue_input(&pre_cstring,&r_cstring,&post_cstring,q); } static int unimpl_block(void *arg __attribute__((__unused__))) { struct timeval now; int ms; gettimeofday(&now,0); if ( (now.tv_sec > unimpl_time.tv_sec) || ( (now.tv_sec == unimpl_time.tv_sec) && (now.tv_usec >= unimpl_time.tv_usec) ) ) { fprintf(stderr,"\n%s unimplemented\n",unimpl_text); exit(1); } ms = ((unimpl_time.tv_sec - now.tv_sec) * 1000) + ((unimpl_time.tv_usec+1000000-now.tv_usec)/1000) - 999; if (debugfile) { dbg("unimpl_block at %lu.%06lu: returning %d\n", (unsigned long int)now.tv_sec, (unsigned long int)now.tv_usec, ms); dbgflush(); } return(ms); } static void unimplemented(const char *s) { unimpl_text = s; gettimeofday(&unimpl_time,0); if (debugfile) { dbg("unimplemented at %lu.%06lu: %s\n", (unsigned long int)unimpl_time.tv_sec, (unsigned long int)unimpl_time.tv_usec, s); dbgflush(); } unimpl_time.tv_sec += 2; unimpl_id = add_block_fn(&unimpl_block,0); } static void do_game(void) { int i; if (debugfile) { dbg("Players (me = %d):\n",mypid); for (i=0;i=0;i--) { nhand[i] = 0; ndiscard[i] = 0; nfs[i] = 0; riichi_at[i] = -1; nmeld[i] = 0; } } static void do_newhand(void) { int i; if (debugfile) { dbg("Hand wind: %c\n",windchar(handwind)); for (i=0;i=0;i--) windnames[playerwind[i]] = playername[i]; for (i=WALLSIZE-1;i>=0;i--) wallpresent[i] = 0; for (i=REPLSIZE-1;i>=0;i--) replpresent[i] = 0; want_notify = 1; } static int pcmd_newhand(void) { int i; pq_queue_input_wind(&handwind); for (i=0;i=0;i--) { if (v[i] == t) { if (i != n) v[i] = v[n]; return(0); } } return(1); } static void do_move(void) { int w; MELD *m; if (debugfile) { OQ oq; oq_init(&oq); msg_write( &oq, S_LITERAL, "Move ", S_TILE, move_tile, S_LITERAL, " from ", S_PLACE, move_src, S_LITERAL, " to ", S_PLACE, move_dst, S_LITERAL, "\n", S_END ); oq_cbwalk(&oq,&dbgwrite,-1); oq_flush(&oq); dbgflush(); } switch (move_src.type) { default: abort(); break; case PT_DECK: break; case PT_WALL: if ((move_src.wall < 0) || (move_src.wall >= WALLSIZE)) abort(); if (! wallpresent[move_src.wall]) { fprintf(stderr,"%s: server moved from empty wall spot %d\n", __progname,move_src.wall); exit(1); } wallpresent[move_src.wall] = 0; break; case PT_HAND: if ((move_src.hand < 0) || (move_src.hand >= NWIND)) abort(); w = playerwind[move_src.hand]; if (nhand[w] < 1) { fprintf(stderr,"%s: server moved from empty hand %d (%c)\n", __progname,move_src.hand,windchar(w)); exit(1); } nhand[w] --; if (w == mywind) { if (move_tile == TILE_UNKTILE) { fprintf(stderr,"%s: server sent hidden move from my hand\n", __progname); exit(1); } if (delete_tile_from_vec(move_tile,&hand[w][0],nhand[w])) { fprintf(stderr,"%s: server moved tile I don't have from my hand\n", __progname); exit(1); } } break; case PT_FS: fprintf(stderr,"%s: server moved from flowers/seasons\n",__progname); exit(1); break; case PT_REPL: if ((move_src.repl < 0) || (move_src.repl >= REPLSIZE)) abort(); if (! replpresent[move_src.repl]) { fprintf(stderr,"%s: server moved from empty replacement spot %d\n", __progname,move_src.repl); exit(1); } replpresent[move_src.repl] = 0; break; case PT_DORA: if ((move_src.dora < 0) || (move_src.dora >= DORASIZE)) abort(); if ( (move_dst.type != PT_DORA) || (move_tile == TILE_UNKTILE) || (move_dst.dora != move_src.dora) ) { fprintf(stderr,"%s: server sent invalid move from dora\n",__progname); exit(1); } break; case PT_DISC: if ((move_src.disc < 0) || (move_src.disc >= NWIND)) abort(); w = playerwind[move_src.disc]; if (ndiscard[w] < 1) { fprintf(stderr,"%s: server moved from empty discards %d (%c)\n", __progname,move_src.disc,windchar(w)); exit(1); } ndiscard[w] --; if (last_discard_to == w) last_discard_to = -1; break; case PT_MELD: if ( (move_tile == TILE_UNKTILE) && (move_dst.type == PT_MELD) && (move_dst.meld.h == move_src.meld.h) && (move_dst.meld.m == move_src.meld.m) ) { /* concealed kong special case */ abort(); /* XXX */ } else { fprintf(stderr,"%s: server moved from meld\n",__progname); exit(1); } break; } switch (move_dst.type) { default: abort(); break; case PT_WALL: if ((move_dst.wall < 0) || (move_dst.wall >= WALLSIZE)) abort(); if (move_src.type != PT_DECK) { fprintf(stderr,"%s: server moved from non-deck to wall\n",__progname); exit(1); } if (move_tile != TILE_UNKTILE) { fprintf(stderr,"%s: non-concealed deck-to-wall move\n",__progname); exit(1); } wallpresent[move_dst.wall] = 1; break; case PT_HAND: if ((move_dst.hand < 0) || (move_dst.hand >= NWIND)) abort(); w = playerwind[move_dst.hand]; if (nhand[w] >= MAXHAND) { fprintf(stderr,"%s: server moved to full hand %d (%c)\n", __progname,move_dst.hand,windchar(w)); exit(1); } if (w == mywind) { if (move_tile == TILE_UNKTILE) { fprintf(stderr,"%s: server sent hidden move to my hand\n", __progname); exit(1); } } hand[w][nhand[w]++] = move_tile; break; case PT_FS: if ((move_dst.fs < 0) || (move_dst.fs >= NWIND)) abort(); w = playerwind[move_dst.fs]; if (nfs[w] >= MAXFS) { fprintf(stderr,"%s: server moved to full flowers/seasons %d (%c)\n", __progname,move_dst.hand,windchar(w)); exit(1); } if (move_tile == TILE_UNKTILE) { fprintf(stderr,"%s: server sent hidden move to flowers/seasons\n", __progname); exit(1); } fs[w][nfs[w]++] = move_tile; break; case PT_REPL: if ((move_dst.repl < 0) || (move_dst.repl >= REPLSIZE)) abort(); switch (move_src.type) { case PT_DECK: case PT_WALL: if (move_tile != TILE_UNKTILE) { fprintf(stderr,"%s: non-concealed move to replacement\n",__progname); exit(1); } break; default: fprintf(stderr,"%s: invalid server move to replacement\n",__progname); exit(1); break; } replpresent[move_dst.repl] = 1; break; case PT_DORA: if ((move_dst.dora < 0) || (move_dst.dora >= DORASIZE)) abort(); switch (move_src.type) { case PT_DECK: case PT_WALL: if (move_tile != TILE_UNKTILE) { fprintf(stderr,"%s: non-concealed move to dora\n",__progname); exit(1); } break; case PT_DORA: break; default: fprintf(stderr,"%s: invalid server move to dora\n",__progname); exit(1); break; } dorapresent[move_dst.dora] = 1; doratile[move_dst.dora] = move_tile; break; case PT_DISC: if ((move_dst.disc < 0) || (move_dst.disc >= NWIND)) abort(); w = playerwind[move_dst.disc]; if (ndiscard[w] >= DECKSIZE) { fprintf(stderr,"%s: server moved to full discards %d (%c)\n", __progname,move_dst.hand,windchar(w)); exit(1); } if (move_tile == TILE_UNKTILE) { fprintf(stderr,"%s: server sent hidden move to discard\n", __progname); exit(1); } discard[w][ndiscard[w]++] = move_tile; last_discard_to = w; break; case PT_MELD: if ( (move_dst.meld.h < 0) || (move_dst.meld.h >= NWIND) || (move_dst.meld.m < 0) || (move_dst.meld.m >= MAXMELDS) ) abort(); w = playerwind[move_dst.meld.h]; if (move_dst.meld.m > nmeld[w]) { fprintf(stderr,"%s: server moved to strange meld %d (%c) #%d\n", __progname,move_dst.meld.h,windchar(w),move_dst.meld.m); exit(1); } m = &melds[w][move_dst.meld.m]; if (move_dst.meld.m == nmeld[w]) { nmeld[w] ++; m->ntiles = 0; m->claimsrc = -1; m->claimtile = TILE_NOTILE; } if (m->ntiles >= MELDSIZE) { if ( (move_tile == TILE_UNKTILE) && (move_src.type == PT_MELD) && (move_src.meld.h == move_dst.meld.h) && (move_src.meld.m == move_dst.meld.m) ) { /* concealed kong special case */ abort(); /* XXX */ } else { fprintf(stderr,"%s: server moved to full meld %d (%c) #%d\n", __progname,move_dst.meld.h,windchar(w),move_dst.meld.m); exit(1); } } else { m->tiles[m->ntiles++] = move_tile; if (move_src.type == PT_DISC) { if (m->claimsrc < 0) { m->claimsrc = move_src.disc; m->claimtile = move_tile; } else { fprintf(stderr,"%s: server moved another claim to %d (%c) #%d\n", __progname,move_dst.meld.h,windchar(w),move_dst.meld.m); exit(1); } } } break; } want_notify = 1; } static int pcmd_move(void) { pq_queue_input_tile(&move_tile,1); pq_queue_input_place(&move_src); pq_queue_input_place(&move_dst); pq_queue_cb(&do_move); return(4); } static int pcmd_riichi(void) { unimplemented("riichi"); return(6); } static int pcmd_play(void) { client_play(); return(4); } static int pcmd_claim(void) { client_claim(); return(5); } static void do_chat(void) { int i; unsigned int wm; if (debugfile) { dbg("chat: from=%d others=%u text=%.*s\n", chat_src,chat_othermask,chat_len,chat_text); dbgflush(); } wm = 0; for (i=NWIND-1;i>=0;i--) { if (chat_othermask & (1 << i)) wm |= 1 << playerwind[i]; } client_chat(playerwind[chat_src],wm,chat_text,chat_len); free(chat_text); } static int pcmd_chat(void) { pq_queue_input_playerid(&chat_src); pq_queue_input_playerids(&chat_othermask); pq_queue_input_cs_len(&chat_text,&chat_len,MJP_MAXCHATLEN); pq_queue_cb(&do_chat); return(4); } static void do_reject(void) { if (debugfile) { dbg("reject: text=%.*s\n",reject_len,reject_text); dbgflush(); } client_reject(reject_text,reject_len); free(reject_text); } static int pcmd_reject(void) { pq_queue_input_cs_len(&reject_text,&reject_len,MJP_MAXREJECT); pq_queue_cb(&do_reject); return(6); } static int rd_idle(unsigned char *buf, int len) { static struct { const char *cmd; int (*impl)(void); int len; } cmds[] = { { "game", &pcmd_game }, { "newhand", &pcmd_newhand }, { "break", &pcmd_break }, { "move", &pcmd_move }, { "riichi", &pcmd_riichi }, { "play", &pcmd_play }, { "claim", &pcmd_claim }, { "chat", &pcmd_chat }, { "reject", &pcmd_reject } }; static int maxlen; int i; int any; int n; if (cmds[0].len == 0) { maxlen = 0; for (i=(sizeof(cmds)/sizeof(cmds[0]))-1;i>=0;i--) { cmds[i].len = strlen(cmds[i].cmd); if (cmds[i].len > maxlen) maxlen = cmds[i].len; } } n = 0; while ((n < len) && whitespace(*buf)) { n ++; buf ++; len --; } any = 0; for (i=(sizeof(cmds)/sizeof(cmds[0]))-1;i>=0;i--) { if (len < cmds[i].len) { if (! bcmp(cmds[i].cmd,buf,len)) any = 1; } else { if (! bcmp(cmds[i].cmd,buf,cmds[i].len)) { return(n+(*cmds[i].impl)()); } } } if (! any) { fprintf(stderr,"%s: server sent unrecognized command",__progname); for (i=0;(i=0;i--) { if (to & (1 << playerwind[i])) msg_write(&servoq,S_ONECHAR,pxid(i),S_END); } msg_write(&servoq,S_ONECHAR,'.',S_CS_COPY,text,len,S_END); } void server_discard(TILE t) { msg_write(&servoq,S_LITERAL,"discard",S_TILE,t,S_END); } void server_pass(void) { msg_write(&servoq,S_LITERAL,"pass",S_END); } void server_ckong(TILE t) { msg_write(&servoq,S_LITERAL,"ckong",S_TILE,t,S_END); } void server_pkong(TILE t) { msg_write(&servoq,S_LITERAL,"pkong",S_TILE,t,S_END); } void server_swap(TILE t) { msg_write(&servoq,S_LITERAL,"swap",S_TILE,t,S_END); } void server_claim(TILE t1, TILE t2, TILE t3) { msg_write(&servoq,S_LITERAL,"claim",S_TILE,t1,S_TILE,t2,S_TILE,t3,S_END); } void server_discard_riichi(TILE t) { msg_write(&servoq,S_LITERAL,"riichi",S_TILE,t,S_END); } void server_out(void) { msg_write(&servoq,S_LITERAL,"out",S_END); } void server_ron(void) { msg_write(&servoq,S_LITERAL,"ron",S_END); } int main(int, char **); int main(int ac, char **av) { client_original_args(ac,(const void *)av); handleargs(ac,av); server_connect(); init_polling(); server_startup(); setup_notify(); reset_handvars(); client_startup(); while (1) { pre_poll(); if (do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } post_poll(); } }