DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T a

⟦4f5efeb34⟧ TextFile

    Length: 12245 (0x2fd5)
    Types: TextFile
    Names: »announce.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cubes/announce.c« 

TextFile

/*	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;
	}
}