|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T a
Length: 12245 (0x2fd5) Types: TextFile Names: »announce.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cubes/announce.c«
/* vi:set sw=4 ts=4: */ #ifndef lint static char sccsid[] = "@(#)announce.c 5.1 (G.M. Paris) 89/01/22"; #endif lint /* ** ** cubes 5.1 Copyright 1989 Gregory M. Paris ** Permission granted to redistribute on a no charge basis. ** All other rights are reserved. ** */ #include <stdio.h> #include <strings.h> #include <syslog.h> #include <signal.h> #include <errno.h> #include <setjmp.h> #include <ctype.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/ioctl.h> #include "cubes.h" extern int errno; extern player plr[]; extern int waiting; extern int watching; extern int winscore; extern boolean jokermode; extern boolean clearobs; extern boolean adjroster; extern boolean isproxy(); extern struct timeval poll; /* ** announce: send a message to all human players but one */ announce(but, mesg) int but; char *mesg; { register int c; for(c = 0;c < PLAYERS;++c) if(c != but) { switch(plr[c].p_stat) { case Active: case Waiting: case Watching: case Spider: (void) message(c, mesg); break; } } } /* ** annscore: announce player score ** announces differently for affected player */ annscore(sc) int sc; { char msgbuf[MESGLEN+64]; sprintf(msgbuf, "%d Player %d now has %d points (squandered %d).\r\n", M_OSCO, sc+1, plr[sc].p_score, plr[sc].p_squander); announce(sc, msgbuf); if(plr[sc].p_stat != Computer) { sprintf(msgbuf, "%d You now have %d points (squandered %d).\r\n", M_MSCO, plr[sc].p_score, plr[sc].p_squander); (void) message(sc, msgbuf); } } /* ** annfarewell: bid farewell to player ** doesn't tell player about own leave */ annfarewell(oldc) int oldc; { char msgbuf[MESGLEN+64]; sprintf(msgbuf, "%d farewell %d %s\r\n", M_FARE, oldc+1, plr[oldc].p_name); announce(oldc, msgbuf); adjroster = True; } /* ** clearroster: send clear roster message to all clients */ clearroster() { register int c; clearobs = False; for(c = 0;c < PLAYERS;++c) { switch(plr[c].p_stat) { case Active: case Waiting: case Watching: case Spider: (void) simp(c, M_CPLR, "Clear player roster."); break; } } } /* ** heartbeat: send a noop to Spiders */ heartbeat() { register int c; for(c = 0;c < PLAYERS;++c) if(plr[c].p_stat == Spider) (void) simp(c, M_NOOP, "Bum-Bum Bum-Bump."); } /* ** observers: tell how many observers there are ** If clearobs is True the observers are showing in the ** roster, so report unseen observers as zero. */ observers(c) { register int cc; char msgbuf[MESGLEN]; cc = (clearobs == True) ? 0 : (waiting + watching); sprintf(msgbuf, "%d %d observer%s\r\n", M_NOBS, cc, cc == 1 ? "" : "s"); if(c != -1) return message(c, msgbuf); for(cc = 0;cc < PLAYERS;++cc) { switch(plr[cc].p_stat) { case Active: case Waiting: case Watching: case Spider: (void) message(cc, msgbuf); break; } } return 0; } /* ** roster: broadcast the roster to all players */ roster(only, showobs) int only; boolean showobs; { register int c, cc; char msgbuf[MESGLEN]; clearobs = False; /* no observers showing (yet) */ /* ** Tell each player who he/she is then give a roster. ** Tell Waiting and Watching players, but don't show them ** unless showobjs is True. */ for(c = 0;c < PLAYERS;++c) { if(only != -1 && c != only) continue; if(plr[c].p_stat == Inactive || plr[c].p_stat == Computer) continue; if(simp(c, M_CPLR, "New player roster.") < 0) continue; for(cc = 0;cc < PLAYERS;++cc) { switch(plr[cc].p_stat) { case Watching: case Waiting: case Spider: if(showobs == False) break; clearobs = True; /* fall */ case Active: case Computer: if(cc == c) sprintf(msgbuf, "%d You are player %d %s\r\n", M_UARE, cc+1, plr[cc].p_name); else sprintf(msgbuf, "%d player %d %s\r\n", M_PNUM, cc+1, plr[cc].p_name); if(message(c, msgbuf) < 0) goto nextrecip; if(cc == c) sprintf(msgbuf, "%d You now have %d points (squandered %d).\r\n", M_MSCO, plr[cc].p_score, plr[cc].p_squander); else sprintf(msgbuf, "%d Player %d now has %d points (squandered %d).\r\n", M_OSCO, cc+1, plr[cc].p_score, plr[cc].p_squander); if(message(c, msgbuf) < 0) goto nextrecip; break; } } (void) observers(c); nextrecip: ; } /* ** If this was a broadcast, the roster is now up to date. */ if(only == -1) adjroster = False; } /* ** tellstatus: report a player's status */ tellstatus(c) { m_num t; char *m; switch(plr[c].p_stat) { case Inactive: return -1; case Active: if(isproxy(c) == False) { t = M_ACTV, m = "You're now an active player."; break; } t = M_AUTO, m = "You're on autopilot; type g<return> to gain control."; break; case Waiting: t = M_WAIT, m = "You're waiting to be added..."; break; case Watching: t = M_VOYR, m = "You're now a voyeur; type g<return> to enter this game."; break; case Spider: t = M_SPDR, m = "You're now a spider; type g<return> to start a game."; break; case Computer: if(plr[c].p_fd < 0) return -1; t = M_AUTO, m = "You've somehow become a computer!!!"; syslog(LOG_DEBUG, "tellstatus: `%s' is a Computer", plr[c].p_name); break; default: t = M_ARGE, m = "You're now in an unknown mode!"; syslog(LOG_ERR, "tellstatus: `%s' is in an unknown mode (%d)", plr[c].p_name, plr[c].p_stat); break; } return simp(c, t, m); } /* ** tellwinscore: notify one or all players about the current winscore ** also notifies player as to whether jokermode is True or False. */ tellwinscore(c) { register int cc; char msgbuf[MESGLEN]; sprintf(msgbuf, "%d This is a %d point game%s.\r\n", M_PARM, winscore, jokermode == True ? " with jokers" : ""); if(c >= 0) (void) message(c, msgbuf); else { for(cc = 0;cc < PLAYERS;++cc) { switch(plr[cc].p_stat) { case Active: case Waiting: case Watching: case Spider: (void) message(cc, msgbuf); break; } } } } /* ** simp: format and send a simple message */ simp(c, type, mtxt) int c; m_num type; char *mtxt; { char msgbuf[MESGLEN]; sprintf(msgbuf, "%3d %.*s\r\n", type, MESGLEN-7, mtxt); return message(c, msgbuf); } /* ** invalid: invalid command message */ invalid(c) int c; { return simp(c, M_ARGE, "Invalid command; type ?<return> for help."); } /* ** multimesg: send multi-line message */ multimesg(c, type, marr) int c; m_num type; char *marr[]; { int n; char msgbuf[MESGLEN]; /* ** Send each line of the message. All but the final ** has a period between the type number and the message. */ for(n = 0;marr[n] != 0;++n) { sprintf(msgbuf, "%d%c%s\r\n", type, marr[n+1] == 0 ? ' ' : '.', marr[n]); if(message(c, msgbuf) < 0) return -1; } return n; } /* ** messagejmp, messagetimo: message timeout handler */ static jmp_buf messagejmp; static int messagetimo(sig) { longjmp(messagejmp, sig); } /* ** message: write a message on a channel */ message(c, mesg) int c; char *mesg; { int n, mesglen; int (*oldalrm)(); int (*oldpipe)(); unsigned alarmv; fd_set rdy; switch(plr[c].p_stat) { case Inactive: syslog(LOG_DEBUG, "message: to Inactive: %s", mesg); return -1; case Computer: syslog(LOG_DEBUG, "message: to Computer: %s", mesg); return 0; /* "succeeds" */ } if(plr[c].p_fd < 0) { if(plr[c].p_stat == Waiting && plr[c].p_computer != 0) return 0; /* OK, Waiting Computer */ syslog(LOG_DEBUG, "message: bad fd (c=%d;stat=%d): %s", c, plr[c].p_stat, mesg); oldplayer(c); return -1; } if((mesglen = strlen(mesg)) == 0) return 0; oldalrm = signal(SIGALRM, SIG_IGN); oldpipe = signal(SIGPIPE, SIG_IGN); alarmv = alarm(0); switch(setjmp(messagejmp)) { case 0: break; case SIGALRM: syslog(LOG_DEBUG, "message: timeout: %s", mesg); goto common; case SIGPIPE: (void) alarm(0); syslog(LOG_DEBUG, "message: dead pipe: %s", mesg); goto common; default: (void) alarm(0); syslog(LOG_DEBUG, "message: unexpected signal: %s", mesg); common: oldplayer(c); (void) signal(SIGALRM, oldalrm); (void) signal(SIGPIPE, oldpipe); (void) alarm(alarmv); return -1; } (void) signal(SIGALRM, messagetimo); (void) signal(SIGPIPE, messagetimo); /* treat like timeout */ (void) alarm(WRITETIMO); FD_ZERO(&rdy); FD_SET(plr[c].p_fd, &rdy); (void) select(plr[c].p_fd+1, NOSEL, &rdy, NOSEL, &poll); if((n = write(plr[c].p_fd, mesg, mesglen)) < 0) { if(errno != EPIPE && errno != ENOTCONN) syslog(LOG_DEBUG, "message: write: %m: %s", mesg); (void) alarm(0); oldplayer(c); (void) signal(SIGALRM, oldalrm); (void) signal(SIGPIPE, oldpipe); (void) alarm(alarmv); return -1; } (void) alarm(0); (void) signal(SIGALRM, oldalrm); (void) signal(SIGPIPE, oldpipe); (void) alarm(alarmv); return n; } /* ** replyjmp, replytimo: reply timeout handler */ static jmp_buf replyjmp; static int replytimo() { longjmp(replyjmp, 1); } /* ** reply: read a reply from a channel */ reply(c, mesg, mesglen) int c, mesglen; char *mesg; { int n, ntot; int (*oldalrm)(); unsigned alarmv; fd_set rdy; switch(plr[c].p_stat) { case Inactive: syslog(LOG_DEBUG, "reply: from Inactive"); return -1; case Computer: syslog(LOG_DEBUG, "reply: from Computer"); return -1; } /* ** Handle read timeout. */ oldalrm = signal(SIGALRM, SIG_IGN); alarmv = alarm(0); if(setjmp(replyjmp) != 0) { (void) signal(SIGALRM, oldalrm); (void) alarm(alarmv); /* ** Increment this player's timeout count. If we've ** reached or exceeded MAXTIMO, then say goodbye. */ if(++plr[c].p_timeouts >= MAXTIMO) { syslog(LOG_DEBUG, "reply: too many timeouts"); (void) simp(c, M_DOWN, "Too many timeouts -- goodbye!"); oldplayer(c); return -1; } /* ** Send a message to the client indicating timeout. */ syslog(LOG_DEBUG, "reply: timeout"); if(simp(c, M_INFO, "Timed out waiting for your response!") < 0) return -1; (void) send(plr[c].p_fd, "\001", 1, MSG_OOB); return -2; } /* ** Read until we get a newline. */ (void) signal(SIGALRM, replytimo); (void) alarm(READTIMO); ntot = 0, mesg[0] = '\0'; while(index(mesg, '\n') == 0) { FD_ZERO(&rdy); FD_SET(plr[c].p_fd, &rdy); (void) select(plr[c].p_fd+1, &rdy, NOSEL, NOSEL, HANG); if((n = read(plr[c].p_fd, mesg+ntot, mesglen-ntot)) <= 0) { (void) alarm(0); #ifdef notdef if(n < 0) syslog(LOG_DEBUG, "reply: read: %m"); else syslog(LOG_DEBUG, "reply: read got zero bytes"); #endif notdef oldplayer(c); (void) signal(SIGALRM, oldalrm); (void) alarm(alarmv); return -1; } ntot += n, mesg[ntot] = '\0'; } (void) alarm(0); /* ** Had a successful read, so zero timeout count. */ plr[c].p_timeouts = 0; /* ** Strip trailing control characters and spaces. ** Then get rid of control characters in the message. ** We must be careful to preserve ASYNCMARK at position 0. XXX Assumes iscntrl(ASYNCMARK) is true. */ for(n = ntot - 1;n >= 0;--n) { mesg[n] &= 0x7f; /* strip high bit */ if(iscntrl(mesg[n])) { if(n != 0 || mesg[n] != ASYNCMARK) mesg[n] = '\0'; } else if(isspace(mesg[n])) mesg[n] = '\0'; else break; } for(n = 0;mesg[n] != '\0';++n) if(iscntrl(mesg[n])) if(n != 0 || mesg[n] != ASYNCMARK) mesg[n] = '?'; (void) signal(SIGALRM, oldalrm); (void) alarm(alarmv); return n; } /* ** dialogue: flush pending data, send message, read reply */ dialogue(c, msg, len) int c, len; char *msg; { long q; int n; char junk[MESGLEN]; if(plr[c].p_fd < 0) return -1; if(plr[c].p_stat == Inactive || plr[c].p_stat == Computer) return -1; /* ** Because of the way this protocol is set up, any pending input at ** this point should be discarded, because it means that the client ** has somehow gotten out of sync. */ while(ioctl(plr[c].p_fd, FIONREAD, (char *)&q) != -1 && q > 0) { if(q > sizeof junk) q = sizeof junk; (void) read(plr[c].p_fd, junk, (int)q); } /* ** Send message. */ if(message(c, msg) < 0) return -1; /* ** We keep reading here until we get a non-asynchronous reply. */ for(;;) { if((n = reply(c, msg, len)) < 0) /* error or timeout */ return n; if(msg[0] != ASYNCMARK) /* not asynchronous */ return n; } }