#define PORT_GAME 12001 #define PORT_CTL 12002 #define PORT_IDENT 113 #include #include #include #include #include #include #include #include #include #include #include #include "oq.h" #include "xf.h" #include "shapes.h" #include "pollloop.h" #include "protocol.h" #include "stdio-util.h" #include "linereader.h" #include "linesender.h" extern const char *__progname; typedef enum { CS_IDENT = 1, CS_STARTUP, CS_UNREG, CS_MEETING, CS_PLAYING, CS_DEAD } CLIENTSTATE; typedef enum { GT_OPEN = 1, GT_CLOSED } GAMETYPE; typedef struct conn CONN; typedef struct accsock ACCSOCK; typedef struct client CLIENT; typedef struct ctl CTL; typedef struct iip IIP; typedef struct mgame MGAME; typedef struct pgame PGAME; typedef struct join JOIN; typedef struct shape SHAPE; struct shape { int x; int y; char *map; int size; } ; #define MAP(sh,ix,iy) ((sh)->map[((iy)*(sh)->x)+(ix)]) struct join { CLIENT *c; MGAME *g; JOIN *cflink; JOIN *cblink; JOIN *gflink; JOIN *gblink; } ; struct mgame { MGAME *flink; MGAME *blink; GAMETYPE type; union { struct { char *pw; int pwlen; } closed; } ; char *id; int idlen; char *name; int namelen; int players_tot; int players_cur; int pcsize; int bdsize; JOIN *joined; } ; struct pgame { PGAME *flink; PGAME *blink; char *id; int idlen; int pcsize; int bdsize; int nplayers; int turn; int turn4; CLIENT **players; SHAPE *board; #define BOARD_EMPTY 4 unsigned int firstturn; unsigned int dead; char *pcavail[4]; } ; struct iip { CLIENT *c; int done; int fd; int ioid; OQ oq; char ibuf[1000]; int ilen; int gotcr; int port1; int port2; } ; struct accsock { ACCSOCK *link; int fd; int ioid; } ; struct conn { struct sockaddr *lcladdr; struct sockaddr *remaddr; int fd; OQ oq; } ; struct client { CLIENT *flink; CLIENT *blink; CONN c; int ioid; CLIENTSTATE state; LINEREADER *iline; LINESENDER *ms; char *name; int namelen; char *id; int idlen; char *ident; int identlen; char *addrtxt; int addrtxtlen; IIP *iip; JOIN *joined; PGAME *playing; } ; struct ctl { CONN c; int dead; int ioid; char *b; int a; int l; } ; static ACCSOCK *acc_game; static ACCSOCK *acc_ctl; static CLIENT *clients; static MGAME *mgames; static PGAME *pgames; static SHAPE *pcshapes; static char **pcids; static int *pcidlens; static const unsigned char xfcanon[][4] = { [XF_NIL] = { 0 }, [XF_R_CW] = { 2, XFOP_MIRROR_Y, XFOP_SWAP_X_Y }, [XF_R_CCW] = { 2, XFOP_MIRROR_X, XFOP_SWAP_X_Y }, [XF_R_180] = { 2, XFOP_MIRROR_X, XFOP_MIRROR_Y }, [XF_MIRROR_X] = { 1, XFOP_MIRROR_X }, [XF_MIRROR_Y] = { 1, XFOP_MIRROR_Y }, [XF_MIRROR_XY] = { 1, XFOP_SWAP_X_Y }, [XF_MIRROR_XMY] = { 3, XFOP_MIRROR_X, XFOP_MIRROR_Y, XFOP_SWAP_X_Y } }; /* Paper over ctype.h's damned incompatability with char args */ #define fixspace(x) isspace((unsigned char)(x)) #define fixdigit(x) isdigit((unsigned char)(x)) static ACCSOCK *acc_setup(int port, void (*accfn)(int, void *)) { struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; char *portstr; int e; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; int fd; ACCSOCK *as; ACCSOCK *list; list = 0; asprintf(&portstr,"%d",port); hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(0,portstr,&hints,&ai0); if (e) { fprintf(stderr,"%s: /%s: %s\n",__progname,portstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: /%s: successful lookup but no addresses?\n",__progname,portstr); exit(1); } for (ai=ai0;ai;ai=ai->ai_next) { if (getnameinfo(ai->ai_addr,ai->ai_addrlen,&hn[0],NI_MAXHOST,&pn[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV)) { fprintf(stderr,"%s: can't get numeric addr/port info [%s]\n",__progname,strerror(errno)); exit(1); } fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) { fprintf(stderr,"%s: %s/%s: socket: %s\n",__progname,&hn[0],&pn[0],strerror(errno)); continue; } if (bind(fd,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: %s/%s: bind: %s\n",__progname,&hn[0],&pn[0],strerror(errno)); close(fd); continue; } if (listen(fd,10) < 0) { fprintf(stderr,"%s: %s/%s: listen: %s\n",__progname,&hn[0],&pn[0],strerror(errno)); close(fd); continue; } as = malloc(sizeof(ACCSOCK)); as->fd = fd; as->ioid = add_poll_fd(fd,&rwtest_always,&rwtest_never,accfn,0,as); as->link = list; list = as; } freeaddrinfo(ai0); free(portstr); return(list); } static void teardown_conn(CONN *c) { free(c->lcladdr); free(c->remaddr); close(c->fd); oq_flush(&c->oq); } static int teardown_client(void *cv) { CLIENT *c; c = cv; if (c->flink) c->flink->blink = c->blink; if (c->blink) c->blink->flink = c->flink; else clients = c->flink; if (c->joined) abort(); remove_block_id(c->ioid); teardown_conn(&c->c); linereader_close(c->iline); linesender_close(c->ms); free(c->name); free(c->id); free(c->ident); free(c); return(BLOCK_LOOP); } static void drop_mgame(MGAME *g, CLIENT *c) { CLIENT *o; if (g->flink) g->flink->blink = g->blink; if (g->blink) g->blink->flink = g->flink; else mgames = g->flink; for (o=clients;o;o=o->flink) { if (o != c) { send_line(o->ms, SL_BLOCK("GAME:-",SL_STRLEN), SL_STRING(g->id,g->idlen), SL_END); } } switch (g->type) { case GT_OPEN: break; case GT_CLOSED: free(g->closed.pw); break; default: abort(); break; } free(g->id); free(g->name); if (g->joined) abort(); free(g); } static void announce_mgame_to(MGAME *g, CLIENT *c) { unsigned char flags[1]; int nflags; JOIN *j; int np; if (c->state != CS_MEETING) return; nflags = 0; switch (g->type) { case GT_OPEN: break; case GT_CLOSED: flags[nflags++] = GAME_FLAG_CLOSED; break; default: abort(); break; } send_open(c->ms); send_append(c->ms, SL_BLOCK("GAME:+",SL_STRLEN), SL_STRING(g->id,g->idlen), SL_STRING(&flags[0],nflags), SL_NUMBER(g->players_tot), SL_NUMBER(g->players_cur), SL_NUMBER(g->pcsize), SL_NUMBER(g->bdsize), SL_STRING(g->name,g->namelen), SL_END); np = 0; for (j=g->joined;j;j=j->gflink) { send_append(c->ms,SL_STRING(j->c->id,j->c->idlen),SL_END); np ++; } if (np != g->players_cur) abort(); send_send(c->ms); } static void announce_mgame_except(MGAME *g, CLIENT *c) { CLIENT *o; for (o=clients;o;o=o->flink) { if (o != c) announce_mgame_to(g,o); } } static void remove_join(JOIN *j, int postproc, CLIENT *xc) { if (j->cflink) j->cflink->cblink = j->cblink; if (j->cblink) j->cblink->cflink = j->cflink; else j->c->joined = j->cflink; if (j->gflink) j->gflink->gblink = j->gblink; if (j->gblink) j->gblink->gflink = j->gflink; else j->g->joined = j->gflink; j->g->players_cur --; if (postproc) { if (j->g->joined == 0) { drop_mgame(j->g,xc); } else { announce_mgame_except(j->g,xc); } } free(j); } static void unjoin_client(CLIENT *c) { while (c->joined) { remove_join(c->joined,1,c); } } static void meeting_leave(CLIENT *c) { CLIENT *o; for (o=clients;o;o=o->flink) { if ((o != c) && (o->state == CS_MEETING)) { send_line(o->ms, SL_BLOCK("PLAYER:-",SL_STRLEN), SL_STRING(c->id,c->idlen), SL_END); } } } static int wtest_conn(CONN *c) { return(oq_nonempty(&c->oq)); } static int rtest_client(int id __attribute__((__unused__)), void *cv) { return(((CLIENT *)cv)->state != CS_DEAD); } static int wtest_client(int id __attribute__((__unused__)), void *cv) { CLIENT *c; c = cv; return((c->state != CS_DEAD) && wtest_conn(&c->c)); } static int ident_destroy(void *iv) { IIP *i; i = iv; close(i->fd); remove_block_id(i->ioid); oq_flush(&i->oq); free(i); return(BLOCK_LOOP); } static void ident_abort(IIP *i) { i->done = 1; remove_poll_id(i->ioid); i->ioid = add_block_fn(&ident_destroy,i); } static void kill_client(CLIENT *c) { switch (c->state) { case CS_IDENT: ident_abort(c->iip); break; case CS_STARTUP: break; case CS_UNREG: break; case CS_MEETING: meeting_leave(c); break; case CS_PLAYING: /* XXX */ break; case CS_DEAD: return; break; default: abort(); break; } c->state = CS_DEAD; if (c->ioid != PL_NOID) remove_poll_id(c->ioid); c->ioid = add_block_fn(&teardown_client,c); unjoin_client(c); } static void rd_client(int id __attribute__((__unused__)), void *cv) { CLIENT *c; char buf[8192]; int r; c = cv; if (c->state == CS_DEAD) return; r = read(c->c.fd,&buf[0],sizeof(buf)); if (r < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: client read error: %s\n",__progname,strerror(errno)); kill_client(c); return; } if (r == 0) { kill_client(c); return; } if (linereader_input(c->iline,&buf[0],r)) kill_client(c); } static int wr_conn(CONN *c) { int w; w = oq_writev(&c->oq,c->fd,0); if (w < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return(0); break; } return(-1); } oq_dropdata(&c->oq,w); return(0); } static void wr_client(int id __attribute__((__unused__)), void *cv) { CLIENT *c; c = cv; if (c->state == CS_DEAD) return; if (wr_conn(&c->c) < 0) { fprintf(stderr,"%s: client write error: %s\n",__progname,strerror(errno)); kill_client(c); } } static void ident_start_client(CLIENT *c) { c->ioid = add_poll_fd(c->c.fd,&rtest_client,&wtest_client,&rd_client,&wr_client,c); c->state = CS_STARTUP; c->iip = 0; } static void ident_finish(IIP *i, char *s, int sl) { i->c->ident = s; i->c->identlen = sl; ident_abort(i); ident_start_client(i->c); } static int teardown_ctl(void *cv) { CTL *c; c = cv; remove_block_id(c->ioid); teardown_conn(&c->c); free(c->b); free(c); return(BLOCK_LOOP); } static void kill_ctl(CTL *c) { if (c->dead) return; c->dead = 1; remove_poll_id(c->ioid); c->ioid = add_block_fn(&teardown_ctl,c); } static void ctl_prompt(CTL *c) { if (c->dead) return; oq_queue_point(&c->c.oq,"blokus ctl> ",OQ_STRLEN); } static void ctl_line(CTL *c) { int x; char *cp; int cl; if (c->dead) return; for (x=0;(xl)&&fixspace(c->b[x]);x++) ; if (x >= c->l) return; cp = &c->b[x]; for (;(xl)&&!fixspace(c->b[x]);x++) ; cl = &c->b[x] - cp; for (;(xl)&&fixspace(c->b[x]);x++) ; if ((cl == 4) && !bcmp(cp,"quit",4)) { kill_ctl(c); return; } oq_queue_copy(&c->c.oq,cp,cl); oq_queue_point(&c->c.oq,"?\r\n",3); } static int rtest_ctl(int id __attribute__((__unused__)), void *cv) { return(!((CTL *)cv)->dead); } static int wtest_ctl(int id __attribute__((__unused__)), void *cv) { CTL *c; c = cv; return(!c->dead && wtest_conn(&c->c)); } static void rd_ctl(int id __attribute__((__unused__)), void *cv) { CTL *c; char buf[8192]; int r; int i; c = cv; if (c->dead) return; r = read(c->c.fd,&buf[0],sizeof(buf)); if (r < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: ctl read error: %s\n",__progname,strerror(errno)); kill_ctl(c); return; } if (r == 0) { kill_ctl(c); return; } for (i=0;il >= c->a) c->b = realloc(c->b,c->a=c->l+32); c->b[c->l++] = ch; } switch (buf[i]) { case '\r': case '\0': break; case '\n': savec('\0'); c->l --; ctl_line(c); if (c->dead) return; c->l = 0; ctl_prompt(c); break; default: savec(buf[i]); if (c->l >= 4096) { kill_ctl(c); return; } break; } } } static void wr_ctl(int id __attribute__((__unused__)), void *cv) { CTL *c; c = cv; if (c->dead) return; if (wr_conn(&c->c) < 0) { fprintf(stderr,"%s: ctl write error: %s\n",__progname,strerror(errno)); kill_ctl(c); } } static int lrgot_startup(char *line, int len) { unsigned char *rmagic; int rmlen; unsigned char dir; unsigned int ver; if (recv_line(line,len, RL_ONEOF(), RL_OPTION("MAGIC:"), RL_STRING(&rmagic,&rmlen), RL_OCTET(&dir), RL_NUMBER(&ver), RL_ENDOF(), RL_END) < 0) { return(-1); } if ((rmlen != magiclen) || bcmp(rmagic,&magic[0],magiclen)) { free(rmagic); return(-1); } free(rmagic); if ((dir != MAGIC_DIR_CLIENT) || (ver != 1)) { return(-1); } return(0); } static void new_id(char **idp, int *idlenp) { static int serial = 1; *idlenp = asprintf(idp,"%d",serial++); } static void client_cmd_reg(CLIENT *c, unsigned char *name, int len) { CLIENT *o; printf("client reg: %.*s\n",len,name); new_id(&c->id,&c->idlen); c->name = name; c->namelen = len; c->state = CS_MEETING; send_line(c->ms, SL_BLOCK("YOUARE:",SL_STRLEN), SL_STRING(c->id,c->idlen), SL_END); for (o=clients;o;o=o->flink) { if (o->state == CS_MEETING) { send_line(o->ms, SL_BLOCK("PLAYER:+",SL_STRLEN), SL_STRING(c->id,c->idlen), SL_STRING(c->addrtxt,c->addrtxtlen), SL_STRING(c->ident,c->identlen), SL_STRING(c->name,c->namelen), SL_END); if (o != c) send_line(c->ms, SL_BLOCK("PLAYER:+",SL_STRLEN), SL_STRING(o->id,o->idlen), SL_STRING(o->addrtxt,o->addrtxtlen), SL_STRING(o->ident,o->identlen), SL_STRING(o->name,o->namelen), SL_END); } } } static int pingpong(CLIENT *c, char *line, int len) { const unsigned char *body; int bodylen; void (*xfn)(void); static void got_ping(void) { send_line(c->ms, SL_BLOCK("PONG:",SL_STRLEN), SL_BLOCK(body,bodylen), SL_END); } static void got_pong(void) { } if (recv_line(line,len, RL_ONEOF(), RL_OPTION("PING:"), RL_SET_FN(&xfn,&got_ping), RL_REST(&body,&bodylen), RL_OPTION("PONG:"), RL_SET_FN(&xfn,&got_pong), RL_REST(&body,&bodylen), RL_ENDOF(), RL_END) == 0) { (*xfn)(); return(1); } return(0); } static int lrgot_unreg(CLIENT *c, char *line, int len) { unsigned char *name; int namelen; void (*xfn)(void); static void got_reg(void) { client_cmd_reg(c,name,namelen); } if (pingpong(c,line,len)) return(0); if (recv_line(line,len, RL_ONEOF(), RL_OPTION("REG:"), RL_SET_FN(&xfn,&got_reg), RL_STRING(&name,&namelen), RL_ENDOF(), RL_END) < 0) { return(-1); } (*xfn)(); return(0); } static void add_join(CLIENT *c, MGAME *g) { JOIN *j; j = malloc(sizeof(JOIN)); j->c = c; j->g = g; j->cflink = c->joined; j->cblink = 0; if (c->joined) c->joined->cblink = j; c->joined = j; j->gflink = g->joined; j->gblink = 0; if (g->joined) g->joined->gblink = j; g->joined = j; g->players_cur ++; } static int player_to_play(PGAME *g) { switch (g->nplayers) { case 1: return(0); break; case 2: return(g->turn&1); break; case 3: return((g->turn==3)?g->turn4:g->turn); break; case 4: return(g->turn); break; } abort(); } static void announce_turn(PGAME *g) { int i; CLIENT *c; for (i=g->nplayers-1;i>=0;i--) { c = g->players[i]; if (g->nplayers == 3) { send_line(c->ms, SL_BLOCK("TURN:",SL_STRLEN), SL_OCTET((i==player_to_play(g))?'+':'-'), SL_NUMBER(g->turn), SL_NUMBER(g->turn4), SL_END); } else { send_line(c->ms, SL_BLOCK("TURN:",SL_STRLEN), SL_OCTET((i==player_to_play(g))?'+':'-'), SL_NUMBER(g->turn), SL_END); } } } static SHAPE *newshape(int x, int y) { SHAPE *p; p = malloc(sizeof(SHAPE)+(x*y)); p->map = (void *) (p+1); p->x = x; p->y = y; return(p); } static void begin_game(MGAME *mg) { PGAME *pg; JOIN *j; int x; int y; int i; CLIENT *c; CLIENT *d; if (mg->players_cur != mg->players_tot) abort(); if ((mg->players_tot < 1) || (mg->players_tot > 4)) abort(); if ((mg->pcsize < 1) || (mg->pcsize > MAXSIZE)) abort(); if (mg->bdsize != boardsizes[mg->pcsize-1]) abort(); pg = malloc(sizeof(PGAME)); pg->id = mg->id; pg->idlen = mg->idlen; mg->id = 0; mg->idlen = 0; pg->pcsize = mg->pcsize; pg->nplayers = mg->players_tot; pg->players = malloc(pg->nplayers*sizeof(CLIENT *)); pg->bdsize = boardsizes[pg->pcsize-1]; pg->board = newshape(pg->bdsize,pg->bdsize); memset(pg->board->map,BOARD_EMPTY,pg->bdsize*pg->bdsize); pg->firstturn = 0xf; pg->dead = 0; pg->turn = random() % 4; if (pg->nplayers == 3) pg->turn4 = random() % 3; x = 0; for (j=mg->joined;j;j=j->gflink) { if (x >= pg->nplayers) abort(); pg->players[x] = j->c; x ++; } if (x != pg->nplayers) abort(); for (x=0;x<4;x++) { pg->pcavail[x] = malloc(numpc[pg->pcsize-1]); for (i=numpc[pg->pcsize-1]-1;i>=0;i--) pg->pcavail[x][i] = 1; } for (x=0;xnplayers;x++) { c = pg->players[x]; c->state = CS_PLAYING; meeting_leave(c); c->playing = pg; } for (x=0;xnplayers;x++) { c = pg->players[x]; unjoin_client(c); send_open(c->ms); send_append(c->ms, SL_BLOCK("BEGIN:",SL_STRLEN), SL_NUMBER(pg->nplayers), SL_NUMBER(pg->pcsize), SL_NUMBER(pg->bdsize), SL_NUMBER(x), SL_END); for (y=0;ynplayers;y++) { d = pg->players[y]; send_append(c->ms,SL_STRING(d->id,d->idlen),SL_END); } send_send(c->ms); for (i=numpc[pg->pcsize-1]-1;i>=0;i--) { SHAPE *s; s = &pcshapes[i]; send_line(c->ms, SL_BLOCK("PIECE:",SL_STRLEN), SL_STRING(pcids[i],pcidlens[i]), SL_STRING(pcinit[i],(((s->x*s->y)+7)>>3)+2), SL_END); } } announce_turn(pg); pg->flink = pgames; pg->blink = 0; if (pgames) pgames->blink = pg; pgames = pg; } static void just_joined(MGAME *g) { if (g->players_cur >= g->players_tot) { begin_game(g); } else { announce_mgame_except(g,0); } } static void create_mgame( CLIENT *c, unsigned char *name, int namelen, unsigned int totplayers, unsigned int size, GAMETYPE type, unsigned char *pw, int pwlen ) { MGAME *g; if ((totplayers < 1) || (totplayers > 4)) return; if ((size < 1) || (size > MAXSIZE)) return; g = malloc(sizeof(MGAME)); g->type = type; switch (type) { case GT_OPEN: break; case GT_CLOSED: g->closed.pw = pw; g->closed.pwlen = pwlen; break; default: abort(); break; } new_id(&g->id,&g->idlen); g->name = name; g->namelen = namelen; g->players_tot = totplayers; g->players_cur = 0; g->pcsize = size; g->bdsize = boardsizes[size-1]; g->joined = 0; g->flink = mgames; g->blink = 0; if (mgames) mgames->blink = g; mgames = g; add_join(c,g); just_joined(g); } static void join_game(CLIENT *c, unsigned char *id, int idlen, int type, ...) { va_list ap; unsigned char *pw; int pwlen; MGAME *g; static int (*checkfn)(MGAME *); static int pwcheck_open(MGAME *mg __attribute__((__unused__))) { return(1); } static int pwcheck_closed(MGAME *mg) { return((mg->closed.pwlen == pwlen) && !bcmp(pw,mg->closed.pw,pwlen)); } va_start(ap,type); switch (type) { case GT_OPEN: checkfn = &pwcheck_open; break; case GT_CLOSED: pw = va_arg(ap,unsigned char *); pwlen = va_arg(ap,int); checkfn = &pwcheck_closed; break; default: abort(); break; } va_end(ap); for (g=mgames;g;g=g->flink) { if ((g->type == type) && (g->idlen == idlen) && !bcmp(id,g->id,idlen)) { if ((*checkfn)(g)) { send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_WORKED), SL_END); add_join(c,g); just_joined(g); return; } send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_BADPASS), SL_END); return; } } send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_NOTFOUND), SL_END); } static void unjoin_game(CLIENT *c, unsigned char *id, int idlen) { MGAME *g; JOIN *j; for (g=mgames;g;g=g->flink) { if ((g->idlen == idlen) && !bcmp(id,g->id,idlen)) { for (j=g->joined;j;j=j->gflink) { if (j->c == c) { send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_WORKED), SL_END); remove_join(j,1,0); return; } } send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_NOTJOINED), SL_END); return; } } send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_NOTFOUND), SL_END); } static int lrgot_meeting(CLIENT *c, char *line, int len) { void (*xfn)(void); unsigned char *name; int namelen; unsigned char *pw; int pwlen; unsigned char *to; int tolen; unsigned char *msg; int msglen; unsigned char *flags; int flagslen; unsigned int totplayers; unsigned int size; static int newgame_closed(void) { return((flagslen > 0) && memchr(flags,GAME_FLAG_CLOSED,flagslen)); } static void got_join_open(void) { join_game(c,name,namelen,GT_OPEN); } static void got_join_closed(void) { join_game(c,name,namelen,GT_CLOSED,pw,pwlen); } static void got_unjoin(void) { unjoin_game(c,name,namelen); } static void got_newgame(void) { create_mgame( c, name, namelen, totplayers, size, newgame_closed() ? GT_CLOSED : GT_OPEN, pw, pwlen ); free(flags); } static void got_chat(void) { /* XXX */ } if (pingpong(c,line,len)) return(0); if (recv_line(line,len, RL_ONEOF(), RL_OPTION("JOIN:+o"), RL_SET_FN(&xfn,&got_join_open), RL_STRING(&name,&namelen), RL_OPTION("JOIN:+c"), RL_SET_FN(&xfn,&got_join_closed), RL_STRING(&name,&namelen), RL_STRING(&pw,&pwlen), RL_OPTION("JOIN:-"), RL_SET_FN(&xfn,&got_unjoin), RL_STRING(&name,&namelen), RL_OPTION("NEWGAME:"), RL_SET_FN(&xfn,&got_newgame), RL_STRING(&flags,&flagslen), RL_NUMBER(&totplayers), RL_NUMBER(&size), RL_STRING(&name,&namelen), RL_IF(&newgame_closed), RL_STRING(&pw,&pwlen), RL_ENDIF(), RL_OPTION("CHAT:"), RL_SET_FN(&xfn,&got_chat), RL_STRING(&to,&tolen), RL_STRING(&msg,&msglen), RL_ENDOF(), RL_END) < 0) { return(-1); } (*xfn)(); return(0); } static int test_placement(SHAPE *p, const XF *xf, int atx, int aty, PGAME *g) { int sx; int sy; int px; int py; int x; int y; int ctouch; ctouch = 0; sx = (*xf->sizex)(p->x,p->y); sy = (*xf->sizey)(p->x,p->y); for (y=sy-1;y>=0;y--) for (x=sx-1;x>=0;x--) { px = (*xf->locx)(x,y,sx,sy); py = (*xf->locy)(x,y,sx,sy); if (MAP(p,px,py)) { if (MAP(g->board,atx+x,aty+y) != BOARD_EMPTY) { return(PLAYFAIL_OVERLAP_PLAYED); } if ( ( (atx+x > 0) && (MAP(g->board,atx+x-1,aty+y) == g->turn) ) || ( (aty+y > 0) && (MAP(g->board,atx+x,aty+y-1) == g->turn) ) || ( (atx+x+1 < g->bdsize) && (MAP(g->board,atx+x+1,aty+y) == g->turn) ) || ( (aty+y+1 < g->bdsize) && (MAP(g->board,atx+x,aty+y+1) == g->turn) ) ) { return(PLAYFAIL_EDGE_TOUCH); } if ( !ctouch && ( ( (atx+x > 0) && (aty+y > 0) && (MAP(g->board,atx+x-1,aty+y-1) == g->turn) ) || ( (atx+x > 0) && (aty+y+1 < g->bdsize) && (MAP(g->board,atx+x-1,aty+y+1) == g->turn) ) || ( (atx+x+1 < g->bdsize) && (aty+y+1 < g->bdsize) && (MAP(g->board,atx+x+1,aty+y+1) == g->turn) ) || ( (atx+x+1 < g->bdsize) && (aty+y > 0) && (MAP(g->board,atx+x+1,aty+y-1) == g->turn) ) ) ) { ctouch = 1; } } } if (! ctouch) return(PLAYFAIL_NO_CORNER_TOUCH); return(-1); } static int has_a_play(PGAME *g, int cx) { int npx; int px; int xx; int sx; int sy; int atx; int aty; const XF *xf; SHAPE *p; static const int xlist[8] = { XF_NIL, XF_R_CW, XF_R_CCW, XF_R_180, XF_MIRROR_X, XF_MIRROR_Y, XF_MIRROR_XY, XF_MIRROR_XMY }; if ((cx < 0) || (cx > 3)) abort(); if (g->firstturn & (1U << cx)) return(1); npx = numpc[g->pcsize-1]; for (px=0;pxpcavail[cx][px]) continue; p = &pcshapes[px]; for (xx=8-1;xx>=0;xx--) { xf = &xfs[xlist[xx]]; sx = (*xf->sizex)(p->x,p->y); sy = (*xf->sizey)(p->x,p->y); for (aty=g->bdsize-sy;aty>=0;aty--) { for (atx=g->bdsize-sx;atx>=0;atx--) { if (test_placement(p,xf,atx,aty,g) < 0) return(1); } } } } return(0); } static void advturn(PGAME *g) { g->turn = (g->turn + 1) % 4; if ((g->nplayers == 3) && (g->turn == 0)) { g->turn4 = (g->turn4 + 1) % 3; } } static void compute_scores(PGAME *g, int *scores) { int i; int x; int y; int p; for (i=4-1;i>=0;i--) scores[i] = 0; for (y=g->bdsize-1;y>=0;y--) for (x=g->bdsize-1;x>=0;x--) { i = MAP(g->board,x,y); if (i == BOARD_EMPTY) continue; if ((i < 0) || (i > 3)) abort(); scores[i] ++; } for <"colour"> (i=4-1;i>=0;i--) { for (p=numpc[g->pcsize-1]-1;p>=0;p--) { if (g->pcavail[i][p]) continue <"colour">; } scores[i] += scores[i] >> 3; } } static void teardown_pgame(PGAME *g) { int i; if (g->flink) g->flink->blink = g->blink; if (g->blink) g->blink->flink = g->flink; else pgames = g->flink; free(g->id); free(g->players); free(g->board); for (i=4-1;i>=0;i--) free(g->pcavail[i]); } static void do_play(CLIENT *c, unsigned char *id, int idlen, unsigned char *xf, int xflen, int atx, int aty) { int x; int y; int i; const XF *xform; PGAME *g; int sx; int sy; SHAPE *p; int pcnum; g = c->playing; if (c != g->players[player_to_play(g)]) { send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_NOT_YOUR_TURN), SL_END); return; } x = XF_NIL; for (i=0;ims, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_BAD_TRANSFORM), SL_END); return; break; } } xform = &xfs[x]; do <"pcfound"> { for (pcnum=numpc[g->pcsize-1]-1;pcnum>=0;pcnum--) { if ((idlen == pcidlens[pcnum]) && !bcmp(id,pcids[pcnum],idlen)) break <"pcfound">; } send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_BAD_PIECE_ID), SL_END); return; } while (0); if (! g->pcavail[g->turn][pcnum]) { send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_PIECE_ALREADY_PLAYED), SL_END); return; } p = &pcshapes[pcnum]; sx = (*xform->sizex)(p->x,p->y); sy = (*xform->sizey)(p->x,p->y); if ((atx+sx > g->bdsize) || (aty+sy > g->bdsize)) { send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_OFF_BOARD), SL_END); return; } if (g->firstturn & (1U << g->turn)) { switch (g->turn) { case 0: i = (atx == 0) && (aty == 0) && MAP(p,(*xform->locx)(0,0,sx,sy),(*xform->locy)(0,0,sx,sy)); break; case 1: i = (atx == 0) && (aty+sy == g->bdsize) && MAP(p,(*xform->locx)(0,sy-1,sx,sy),(*xform->locy)(0,sy-1,sx,sy)); break; case 2: i = (atx+sx == g->bdsize) && (aty+sy == g->bdsize) && MAP(p,(*xform->locx)(sx-1,sy-1,sx,sy),(*xform->locy)(sx-1,sy-1,sx,sy)); break; case 3: i = (atx+sx == g->bdsize) && (aty == 0) && MAP(p,(*xform->locx)(sx-1,0,sx,sy),(*xform->locy)(sx-1,0,sx,sy)); break; default: abort(); break; } if (! i) { send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(PLAYFAIL_NO_CORNER_COVER), SL_END); return; } g->firstturn &= ~(1U << g->turn); } else { int reason; reason = test_placement(p,xform,atx,aty,g); if (reason >= 0) { send_line(c->ms, SL_BLOCK("PLAYFAIL:",SL_STRLEN), SL_OCTET(reason), SL_END); return; } } for (y=sy-1;y>=0;y--) for (x=sx-1;x>=0;x--) { if (MAP(p,(*xform->locx)(x,y,sx,sy),(*xform->locy)(x,y,sx,sy))) { MAP(g->board,atx+x,aty+y) = 1; } } g->pcavail[g->turn][pcnum] = 0; for (i=g->nplayers-1;i>=0;i--) { send_line(g->players[i]->ms, SL_BLOCK("PLAYED:",SL_STRLEN), SL_NUMBER(g->turn), SL_STRING(pcids[pcnum],pcidlens[pcnum]), SL_STRING(&xfcanon[xform->inx][1],xfcanon[xform->inx][0]), SL_NUMBER(atx), SL_NUMBER(aty), SL_END); } for (i=4-1;i>=0;i--) { if (! (g->dead & (1U << i))) { if (! has_a_play(g,i)) { g->dead |= 1U << i; } } } if (g->dead == 0xf) { int scores[4]; compute_scores(g,&scores[0]); for (i=g->nplayers-1;i>=0;i--) { switch (g->nplayers) { case 1: send_line(g->players[i]->ms, SL_BLOCK("TURN:DONE",SL_STRLEN), SL_END); break; case 2: send_line(g->players[i]->ms, SL_BLOCK("TURN:DONE",SL_STRLEN), SL_NUMBER(scores[0]+scores[2]), SL_NUMBER(scores[1]+scores[3]), SL_END); break; case 3: send_line(g->players[i]->ms, SL_BLOCK("TURN:DONE",SL_STRLEN), SL_NUMBER(scores[0]), SL_NUMBER(scores[1]), SL_NUMBER(scores[2]), SL_END); break; case 4: send_line(g->players[i]->ms, SL_BLOCK("TURN:DONE",SL_STRLEN), SL_NUMBER(scores[0]), SL_NUMBER(scores[1]), SL_NUMBER(scores[2]), SL_NUMBER(scores[3]), SL_END); break; default: abort(); break; } } for (i=g->nplayers-1;i>=0;i--) { g->players[i]->state = CS_UNREG; break; } teardown_pgame(g); } else { do advturn(g); while (g->dead & (1U << g->turn)); announce_turn(g); } } static int lrgot_playing(CLIENT *c, char *line, int len) { void (*xfn)(void); unsigned char *name; int namelen; unsigned char *pw; int pwlen; unsigned char *to; int tolen; unsigned char *id; int idlen; unsigned char *xf; int xflen; unsigned char *msg; int msglen; unsigned char *flags; int flagslen; unsigned int totplayers; unsigned int size; int atx; int aty; static int newgame_closed(void) { return((flagslen > 0) && memchr(flags,GAME_FLAG_CLOSED,flagslen)); } static void got_join(void) { send_line(c->ms, SL_BLOCK("JOINSTAT:",SL_STRLEN), SL_OCTET(JOINSTAT_NOTMEETING), SL_END); free(name); free(pw); } static void got_newgame(void) { free(flags); free(name); free(pw); } static void got_play(void) { do_play(c,id,idlen,xf,xflen,atx,aty); free(id); free(xf); } static void got_chat(void) { /* XXX */ } if (pingpong(c,line,len)) return(0); pw = 0; pwlen = 0; if (recv_line(line,len, RL_ONEOF(), RL_OPTION("JOIN:+o"), RL_SET_FN(&xfn,&got_join), RL_STRING(&name,&namelen), RL_OPTION("JOIN:+c"), RL_SET_FN(&xfn,&got_join), RL_STRING(&name,&namelen), RL_STRING(&pw,&pwlen), RL_OPTION("JOIN:-"), RL_SET_FN(&xfn,&got_join), RL_STRING(&name,&namelen), RL_OPTION("NEWGAME:"), RL_SET_FN(&xfn,&got_newgame), RL_STRING(&flags,&flagslen), RL_NUMBER(&totplayers), RL_NUMBER(&size), RL_STRING(&name,&namelen), RL_IF(&newgame_closed), RL_STRING(&pw,&pwlen), RL_ENDIF(), RL_OPTION("CHAT:"), RL_SET_FN(&xfn,&got_chat), RL_STRING(&to,&tolen), RL_STRING(&msg,&msglen), RL_OPTION("PLAY:"), RL_SET_FN(&xfn,&got_play), RL_STRING(&id,&idlen), RL_STRING(&xf,&xflen), RL_NUMBER(&atx), RL_NUMBER(&aty), RL_ENDOF(), RL_END) < 0) { return(-1); } (*xfn)(); return(0); } static void lrgot_client(char *line, int len, void *cv) { CLIENT *c; c = cv; switch (c->state) { case CS_DEAD: return; break; case CS_STARTUP: if (lrgot_startup(line,len) < 0) { kill_client(c); } else { c->state = CS_UNREG; } break; case CS_UNREG: if (lrgot_unreg(c,line,len) < 0) kill_client(c); break; case CS_MEETING: if (lrgot_meeting(c,line,len) < 0) kill_client(c); break; case CS_PLAYING: if (lrgot_playing(c,line,len) < 0) kill_client(c); break; default: kill_client(c); break; } } static int accept_common(ACCSOCK *as, CONN *c) { struct sockaddr_storage sslcl; struct sockaddr_storage ssrem; int sslen; int new; sslen = sizeof(ssrem); new = accept(as->fd,(void *)&ssrem,&sslen); if (new < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: break; default: fprintf(stderr,"%s: accept: %s\n",__progname,strerror(errno)); break; } return(-1); } sslen = sizeof(sslcl); if (getsockname(new,(void *)&sslcl,&sslen) < 0) { fprintf(stderr,"%s: getsockname: %s\n",__progname,strerror(errno)); close(new); return(-1); } c->fd = new; c->lcladdr = malloc(sslcl.ss_len); bcopy(&sslcl,c->lcladdr,sslcl.ss_len); c->remaddr = malloc(ssrem.ss_len); bcopy(&ssrem,c->remaddr,ssrem.ss_len); return(0); } static void client_queue(const void *data, int len, void *cv) { oq_queue_copy(&((CLIENT *)cv)->c.oq,data,len); } static void ident_done_error(IIP *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void ident_done_error(IIP *i, const char *fmt, ...) { char *s; int sl; va_list ap; FILE *f; f = fopen_acc(&s,&sl); fprintf(f,"Error: "); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fclose(f); ident_finish(i,s,sl); } static void ident_response(IIP *i) { __label__ ret; char *bp; int l; int p1; int p2; char *t; int tl; char *s; int sl; FILE *f; static void adv(int n) { bp += n; l -= n; } auto void proto(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); auto void proto(const char *fmt, ...) { va_list ap; fprintf(f,"IDENT protocol error: "); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); va_start(ap,fmt); fprintf(stderr,"IDENT protocol error: "); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\nOffending response: %.*s\n",i->ilen,&i->ibuf[0]); goto ret; } static void token(const char *what) { do adv(1); while ((l > 0) && fixspace(*bp)); t = bp; while ((l > 0) && !fixspace(*bp)) adv(1); tl = bp - t; if ((tl < 2) || (tl > 64)) proto("bad %s length %d",what,tl); } bp = &i->ibuf[0]; l = i->ilen; f = fopen_acc(&s,&sl); while ((l > 0) && fixspace(*bp)) adv(1); p1 = 0; while ((l > 0) && fixdigit(*bp)) { p1 = (10 * p1) + (*bp - '0'); adv(1); } while ((l > 0) && fixspace(*bp)) adv(1); if ((l < 1) || (*bp != ',')) proto("missing comma"); do adv(1); while ((l > 0) && fixspace(*bp)); p2 = 0; while ((l > 0) && fixdigit(*bp)) { p2 = (10 * p2) + (*bp - '0'); adv(1); } if ((p1 != i->port1) || (p2 != i->port2)) proto("port numbers wrong"); while ((l > 0) && fixspace(*bp)) adv(1); if ((l < 1) || (*bp != ':')) proto("missing colon 1"); adv(1); token("response type"); if ((tl == 5) && !bcmp(t,"ERROR",5)) { while ((l > 0) && fixspace(*bp)) adv(1); if ((l < 1) || (*bp != ':')) proto("missing colon 2"); adv(1); token("error"); if ((tl == 12) && !bcmp(t,"INVALID-PORT",12)) { fprintf(f,"ERROR [INVALID-PORT]"); } else if ((tl == 7) && !bcmp(t,"NO-USER",7)) { fprintf(f,"ERROR [NO-USER]"); } else if ((tl == 11) && !bcmp(t,"HIDDEN-USER",11)) { fprintf(f,"ERROR [HIDDEN-USER]"); } else if ((tl == 13) && !bcmp(t,"UNKNOWN-ERROR",13)) { fprintf(f,"ERROR [UNKNOWN-ERROR]"); } else if (*t == 'X') { fprintf(f,"ERROR [%.*s]",tl,t); } else { proto("bad error token %.*s",tl,t); } } else if ((tl == 6) && !bcmp(t,"USERID",6)) { while ((l > 0) && fixspace(*bp)) adv(1); if ((l < 1) || (*bp != ':')) proto("missing colon 2"); do adv(1); while ((l > 0) && (*bp != ':')); if (l < 1) proto("missing colon 3"); adv(1); fwrite(bp,1,l,f); } else { proto("bad response type %.*s",tl,t); } ret:; fclose(f); ident_finish(i,s,sl); } static void ident_input(IIP *i, unsigned char *data, int len) { int x; for (x=0;xilen >= 1000) { ident_done_error(i,"Excessively long response"); return; } switch (data[x]) { case '\0': ident_done_error(i,"NUL in response"); return; break; case '\r': if (i->gotcr) { ident_done_error(i,"Invalid CR in response"); return; } i->ibuf[i->ilen++] = '\r'; i->gotcr = 1; break; case '\n': if (i->gotcr) { i->ilen --; ident_response(i); return; } ident_done_error(i,"Invalid LF in response"); return; break; default: i->ibuf[i->ilen++] = data[x]; i->gotcr = 0; break; } } } static void ident_send(IIP *i) { char *query; int querylen; if (i->c->c.lcladdr->sa_family != i->c->c.remaddr->sa_family) { ident_done_error(i,"server AF %d, client AF %d", i->c->c.lcladdr->sa_family,i->c->c.remaddr->sa_family); return; } switch (i->c->c.lcladdr->sa_family) { case AF_INET: i->port1 = ntohs(((struct sockaddr_in *)i->c->c.remaddr)->sin_port); i->port2 = ntohs(((struct sockaddr_in *)i->c->c.lcladdr)->sin_port); break; case AF_INET6: i->port1 = ntohs(((struct sockaddr_in6 *)i->c->c.remaddr)->sin6_port); i->port2 = ntohs(((struct sockaddr_in6 *)i->c->c.lcladdr)->sin6_port); break; default: abort(); break; } querylen = asprintf(&query,"%d,%d\r\n",i->port1,i->port2); oq_queue_free(&i->oq,query,querylen); } static int rtest_ident(int id __attribute__((__unused__)), void *iv __attribute__((__unused__))) { return(!((IIP *)iv)->done); } static int wtest_ident(int id __attribute__((__unused__)), void *iv) { IIP *i; i = iv; if (i->done) return(0); return(oq_nonempty(&i->oq)); } static void rd_ident(int id __attribute__((__unused__)), void *iv) { IIP *i; unsigned char buf[8192]; int r; i = iv; if (i->done) return; r = read(i->fd,&buf[0],sizeof(buf)); if (r < 0) { int e; e = errno; switch (e) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: ident read error: %s\n",__progname,strerror(e)); ident_done_error(i,"read error: %s",strerror(e)); return; } ident_input(i,&buf[0],r); } static void wr_ident(int id __attribute__((__unused__)), void *iv) { IIP *i; int w; i = iv; if (i->done) return; w = oq_writev(&i->oq,i->fd,0); if (w < 0) { int e; e = errno; switch (e) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: ident write error: %s\n",__progname,strerror(e)); ident_done_error(i,"write error: %s",strerror(e)); return; } oq_dropdata(&i->oq,w); } static void ident_connect_ok(IIP *i) { if (i->ioid != PL_NOID) remove_poll_id(i->ioid); i->ioid = add_poll_fd(i->fd,&rtest_ident,&wtest_ident,&rd_ident,&wr_ident,i); ident_send(i); } static void ident_conndone(int id __attribute__((__unused__)), void *iv) { IIP *i; int e; socklen_t el; i = iv; el = sizeof(e); if (getsockopt(i->fd,SOL_SOCKET,SO_ERROR,&e,&el) < 0) { ident_done_error(i,"getsockopt error: %s",strerror(errno)); return; } if (e != 0) { ident_done_error(i,"connect error: %s",strerror(e)); return; } ident_connect_ok(i); } static void start_ident(CLIENT *c) { __label__ ret; IIP *i; int e; auto void fail(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); auto void fail(const char *fmt, ...) { va_list ap; va_start(ap,fmt); c->identlen = vasprintf(&c->ident,fmt,ap); va_end(ap); ident_start_client(c); if (i) { oq_flush(&i->oq); free(i); } goto ret; } i = 0; if (c->c.lcladdr->sa_family != c->c.remaddr->sa_family) { fail("server AF %d, client AF %d", c->c.lcladdr->sa_family,c->c.remaddr->sa_family); } switch (c->c.lcladdr->sa_family) { case AF_INET: case AF_INET6: break; default: fail("can't do ident for AF %d",c->c.lcladdr->sa_family); break; } i = malloc(sizeof(IIP)); c->iip = i; i->c = c; i->done = 0; i->ilen = 0; i->gotcr = 0; i->ioid = PL_NOID; oq_init(&i->oq); switch (c->c.lcladdr->sa_family) { case AF_INET: { struct sockaddr_in sin; sin = *(struct sockaddr_in *)c->c.lcladdr; sin.sin_port = 0; i->fd = socket(AF_INET,SOCK_STREAM,0); if (i->fd < 0) { fail("ident socket: %s",strerror(errno)); } if (bind(i->fd,(void *)&sin,sizeof(sin)) < 0) { e = errno; close(i->fd); fail("ident bind: %s",strerror(e)); } fcntl(i->fd,F_SETFL,fcntl(i->fd,F_GETFL,0)|O_NONBLOCK); sin = *(struct sockaddr_in *)c->c.remaddr; sin.sin_port = htons(PORT_IDENT); if (connect(i->fd,(void *)&sin,sizeof(sin)) < 0) { e = errno; if (e == EINPROGRESS) { i->ioid = add_poll_fd(i->fd,&rwtest_never,&rwtest_always,0,&ident_conndone,i); return; } close(i->fd); fail("ident connect: %s",strerror(e)); } else { ident_connect_ok(i); } } break; case AF_INET6: { struct sockaddr_in6 sin; sin = *(struct sockaddr_in6 *)c->c.lcladdr; sin.sin6_port = 0; i->fd = socket(AF_INET6,SOCK_STREAM,0); if (i->fd < 0) { fail("ident socket: %s",strerror(errno)); } if (bind(i->fd,(void *)&sin,sizeof(sin)) < 0) { e = errno; close(i->fd); fail("ident bind: %s",strerror(e)); } fcntl(i->fd,F_SETFL,fcntl(i->fd,F_GETFL,0)|O_NONBLOCK); sin = *(struct sockaddr_in6 *)c->c.remaddr; sin.sin6_port = htons(PORT_IDENT); if (connect(i->fd,(void *)&sin,sizeof(sin)) < 0) { e = errno; if (e == EINPROGRESS) { i->ioid = add_poll_fd(i->fd,&rwtest_never,&rwtest_always,0,&ident_conndone,i); return; } close(i->fd); fail("ident connect: %s",strerror(e)); } else { ident_connect_ok(i); } } break; default: abort(); break; } ret:; } static void accept_game(int id __attribute__((__unused__)), void *asv) { CONN conn; CLIENT *c; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; if (accept_common(asv,&conn) < 0) return; if (getnameinfo(conn.remaddr,conn.remaddr->sa_len,&hn[0],NI_MAXHOST,&pn[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV)) { fprintf(stderr,"%s: can't get numeric addr/port info [%s]\n",__progname,strerror(errno)); teardown_conn(&conn); return; } c = malloc(sizeof(CLIENT)); c->flink = clients; c->blink = 0; if (clients) clients->blink = c; clients = c; c->c = conn; c->state = CS_IDENT; c->ioid = PL_NOID; c->iline = linereader_open(&lrgot_client,c); oq_init(&c->c.oq); c->ms = linesender_open(&client_queue,c); c->name = 0; c->namelen = 0; c->id = 0; c->idlen = 0; c->ident = 0; c->identlen = 0; c->addrtxtlen = asprintf(&c->addrtxt,"%s/%s",&hn[0],&pn[0]); c->joined = 0; c->playing = 0; send_line(c->ms, SL_BLOCK("MAGIC:",SL_STRLEN), SL_STRING(&magic[0],magiclen), SL_OCTET(MAGIC_DIR_SERVER), SL_NUMBER(1), SL_END); start_ident(c); } static void accept_ctl(int id __attribute__((__unused__)), void *asv) { CONN conn; CTL *c; if (accept_common(asv,&conn) < 0) return; c = malloc(sizeof(CTL)); c->c = conn; c->ioid = add_poll_fd(c->c.fd,&rtest_ctl,&wtest_ctl,&rd_ctl,&wr_ctl,c); c->b = 0; c->a = 0; c->l = 0; oq_init(&c->c.oq); ctl_prompt(c); } static void setup_shapes(void) { int i; int nb; unsigned char *map; const unsigned char *bits; SHAPE *s; int size; int x; int y; unsigned char bit; nb = 0; for (i=numpc[MAXSIZE-1]-1;i>=0;i--) nb += pcinit[i][0] * pcinit[i][1]; pcshapes = malloc((numpc[MAXSIZE-1]*sizeof(SHAPE))+nb); map = (void *)(pcshapes+numpc[MAXSIZE-1]); for (i=numpc[MAXSIZE-1]-1;i>=0;i--) { s = &pcshapes[i]; s->x = pcinit[i][0]; s->y = pcinit[i][1]; s->map = map; bits = pcinit[i] + 2 - 1; size = 0; bit = 0; for (y=0;yy;y++) for (x=0;xx;x++) { if (bit == 0) { bit = 0x80; bits ++; } if (bit & *bits) { MAP(s,x,y) = 1; size ++; } else { MAP(s,x,y) = 0; } bit >>= 1; } s->size = size; if ((size < 1) || (size > MAXSIZE) || (i > numpc[size-1])) abort(); map += s->x * s->y; } if (map - (__typeof__(map))(pcshapes+numpc[MAXSIZE-1]) != nb) abort(); pcids = malloc(numpc[MAXSIZE-1]*sizeof(char *)); pcidlens = malloc(numpc[MAXSIZE-1]*sizeof(int)); for (i=numpc[MAXSIZE-1]-1;i>=0;i--) { pcidlens[i] = asprintf(&pcids[i],"p%dc",i); } } int main(void); int main(void) { srandom(time(0)); setup_shapes(); acc_game = acc_setup(PORT_GAME,&accept_game); acc_ctl = acc_setup(PORT_CTL,&accept_ctl); clients = 0; mgames = 0; pgames = 0; while (1) { pre_poll(); if (do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"poll: %s",strerror(errno)); exit(1); } post_poll(); } }