mcgrew@aramis.rutgers.edu (Charles Mcgrew) (05/06/89)
Submitted-by: kent@wsl.dec.com Posting-number: Volume 1, Issue 2 Archive-name: mazewar/part04 #!/bin/sh sed 's/^X//' >./mazewar.c << 'xxFUNNYxx' X/* $Header: mazewar.c,v 1.13 88/08/25 09:57:53 kent Exp $ */ X X/* X * mazewar.c - Rats in a maze X * X * Author: Christopher A. Kent X * Western Research Laboratory X * Digital Equipment Corporation X * Date: Wed Sep 24 1986 X */ X X/*********************************************************** XCopyright 1986 by Digital Equipment Corporation, Maynard, Massachusetts, X X All Rights Reserved X XPermission to use, copy, modify, and distribute this software and its Xdocumentation for any purpose and without fee is hereby granted, Xprovided that the above copyright notice appear in all copies and that Xboth that copyright notice and this permission notice appear in Xsupporting documentation, and that the names of Digital not be Xused in advertising or publicity pertaining to disstribution of the Xsoftware without specific, written prior permission. X XDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING XALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL XDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR XANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, XWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, XARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS XSOFTWARE. X X******************************************************************/ X X/* X * This is an adaptation of the Mazewar program built at Xerox PARC in X * Mesa for Altos by Jim Guyton and others in the late 1970s and beyond. X * Unlike other programs that have appeared from time to time, this X * one attempts to remain faithful to the original spirit of the game, X * with no fancy additions (like seeing your opponents or teleport X * traps). I hope you enjoy it. X */ X X/* X * $Log: mazewar.c,v $ X * Revision 1.13 88/08/25 09:57:53 kent X * Copyright. X * X * Revision 1.12 88/02/11 18:00:39 kent X * Changes so the value of M.theSocket doesn't change (this makes the X * window system code simpler). X * X * Revision 1.11 88/02/11 17:52:41 kent X * Move some code out of the play loop so it can be called by an "external" X * play loop (such as is needed for the X11 toolkit). X * X * Revision 1.10 87/03/31 15:47:34 kent X * Handle duplicated RAT_NEW packets while joining the game. If the X * guy is already in the game, just send him a status packet; previously, X * he'd get added to the game again! X * X * Revision 1.9 87/03/31 14:37:37 kent X * Portability considerations, especially byteswapping to/from the net. X * X * Revision 1.8 86/12/04 17:44:53 kent X * Notify user also if shot; make sure non-dukes get notified on game joins. X * X * Revision 1.7 86/12/03 18:13:10 kent X * Cleaned up the shot handling code a bit. Was waiting two seconds X * instead of one, and would only handle one shot off the queue X * every time around the loop, instead of all applicable. X * X * Also cleaned up a race in the port moving code for when the mover and X * the quitter were on the same machine. X * X * Revision 1.6 86/12/03 13:31:10 kent X * X * X * Revision 1.5 86/12/03 10:15:03 kent X * Only send location when moving, not every time screen needs updating. X * X * Revision 1.4 86/12/03 10:00:19 kent X * Changes to allow multiple players per machine. X * X * Revision 1.3 86/12/01 23:44:42 kent X * Housecleaning and documentation pass. X * X * Revision 1.2 86/12/01 14:47:04 kent X * Changes for a realistic implementation of shooting. X * X * Revision 1.1 86/11/26 16:57:53 kent X * Initial revision X * X */ X X#ifndef lint Xstatic char rcs_ident[] = "$Header: mazewar.c,v 1.13 88/08/25 09:57:53 kent Exp $"; X#endif X X#include <sys/types.h> X#include <sys/socket.h> X#include <sys/time.h> X X#include <netinet/in.h> X X#include <signal.h> X#include <stdio.h> X#include <strings.h> X X#include "mazewar.h" X Xstatic int i1 = 0; /* random number hackery */ Xstatic int i2 = 24; X Xstatic Boolean updateView; /* true if update needed */ Xstatic Boolean sendLocation; /* true if must send out location */ X Xstatic MazeTypePtr mp = M.maze; /* easy access to Maze */ X Xstatic RatHealth ratHealth; /* keep track of other players */ X Xmain(argc, argv) Xchar **argv; X{ X int quit(); X X signal(SIGHUP, quit); X signal(SIGINT, quit); X signal(SIGTERM, quit); X MazeInit(argc, argv); X play(); X} X Xplay() X{ X MWEvent event; X RatPacket incoming; X Boolean KBEventPending(); X X event.eventDetail = &incoming; X X while (1) { X NextEvent(&event); X if (!M.peeking) X switch(event.eventType) { X case EVENT_A: X aboutFace(); X break; X X case EVENT_S: X leftTurn(); X break; X X case EVENT_D: X forward(); X break; X X case EVENT_F: X rightTurn(); X break; X X case EVENT_BAR: X backward(); X break; X X case EVENT_I: X makeInvincible(TRUE); X break; X X case EVENT_K: X makeInvincible(FALSE); X break; X X case EVENT_O: X makeOmniscient(TRUE); X break; X X case EVENT_L: X makeOmniscient(FALSE); X break; X X case EVENT_LEFT_D: X peekLeft(); X break; X X case EVENT_MIDDLE_D: X shoot(); X break; X X case EVENT_RIGHT_D: X peekRight(); X break; X X case EVENT_NETWORK: X readRats(&event); X break; X X case EVENT_INT: X quit(); X break; X X } X else X switch (event.eventType) { X case EVENT_RIGHT_U: X case EVENT_LEFT_U: X peekStop(); X break; X X case EVENT_NETWORK: X readRats(&event); X break; X } X X ratDoctor(); /* clean house */ X X DoRatKillQ(); X X DoViewUpdate(); X X SendLocation(); X } X} X Xstatic Direction _aboutFace[NDIRECTION] ={SOUTH, NORTH, WEST, EAST}; Xstatic Direction _leftTurn[NDIRECTION] = {WEST, EAST, NORTH, SOUTH}; Xstatic Direction _rightTurn[NDIRECTION] ={EAST, WEST, SOUTH, NORTH}; X XaboutFace() X{ X M.dir = _aboutFace[M.dir]; X updateView = TRUE; X sendLocation = TRUE; X} X XleftTurn() X{ X M.dir = _leftTurn[M.dir]; X updateView = TRUE; X sendLocation = TRUE; X} X XrightTurn() X{ X M.dir = _rightTurn[M.dir]; X updateView = TRUE; X sendLocation = TRUE; X} X X/* remember ... "North" is to the right ... positive X motion */ X Xforward() X{ X register int tx = M.xloc; X register int ty = M.yloc; X X switch(M.dir) { X case NORTH: if (!mp[tx+1].y[ty]) tx++; break; X case SOUTH: if (!mp[tx-1].y[ty]) tx--; break; X case EAST: if (!mp[tx].y[ty+1]) ty++; break; X case WEST: if (!mp[tx].y[ty-1]) ty--; break; X default: X MWError("bad direction in Forward"); X } X if ((M.xloc != tx) || (M.yloc != ty)) { X M.xloc = tx; M.yloc = ty; X updateView = TRUE; X sendLocation = TRUE; X } X} X Xbackward() X{ X register int tx = M.xloc; X register int ty = M.yloc; X X switch(M.dir) { X case NORTH: if (!mp[tx-1].y[ty]) tx--; break; X case SOUTH: if (!mp[tx+1].y[ty]) tx++; break; X case EAST: if (!mp[tx].y[ty-1]) ty--; break; X case WEST: if (!mp[tx].y[ty+1]) ty++; break; X default: X MWError("bad direction in Backward"); X } X if ((M.xloc != tx) || (M.yloc != ty)) { X M.xloc = tx; M.yloc = ty; X updateView = TRUE; X sendLocation = TRUE; X } X} X XmakeInvincible(neverDie) XBoolean neverDie; X{ X M.invincible = neverDie; X ShowPosition(M.xloc, M.yloc, M.invincible, M.dir); X} X XmakeOmniscient(allSeeing) XBoolean allSeeing; X{ X M.omniscient = allSeeing; X ShowAllPositions(); X} X XpeekLeft() X{ X M.xPeek = M.xloc; X M.yPeek = M.yloc; X M.dirPeek = M.dir; X X switch(M.dir) { X case NORTH: if (!mp[M.xloc+1].y[M.yloc]) { X M.xPeek = M.xloc + 1; X M.dirPeek = WEST; X } X break; X X case SOUTH: if (!mp[M.xloc-1].y[M.yloc]) { X M.xPeek = M.xloc - 1; X M.dirPeek = EAST; X } X break; X X case EAST: if (!mp[M.xloc].y[M.yloc+1]) { X M.yPeek = M.yloc + 1; X M.dirPeek = NORTH; X } X break; X X case WEST: if (!mp[M.xloc].y[M.yloc-1]) { X M.yPeek = M.yloc - 1; X M.dirPeek = SOUTH; X } X break; X X default: X MWError("bad direction in PeekLeft"); X } X X /* if any change, display the new view without moving! */ X X if ((M.xPeek != M.xloc) || (M.yPeek != M.yloc)) { X M.peeking = TRUE; X updateView = TRUE; X } X} X XpeekRight() X{ X M.xPeek = M.xloc; X M.yPeek = M.yloc; X M.dirPeek = M.dir; X X switch(M.dir) { X case NORTH: if (!mp[M.xloc+1].y[M.yloc]) { X M.xPeek = M.xloc + 1; X M.dirPeek = EAST; X } X break; X X case SOUTH: if (!mp[M.xloc-1].y[M.yloc]) { X M.xPeek = M.xloc - 1; X M.dirPeek = WEST; X } X break; X X case EAST: if (!mp[M.xloc].y[M.yloc+1]) { X M.yPeek = M.yloc + 1; X M.dirPeek = SOUTH; X } X break; X X case WEST: if (!mp[M.xloc].y[M.yloc-1]) { X M.yPeek = M.yloc - 1; X M.dirPeek = NORTH; X } X break; X X default: X MWError("bad direction in PeekRight"); X } X X /* if any change, display the new view without moving! */ X X if ((M.xPeek != M.xloc) || (M.yPeek != M.yloc)) { X M.peeking = TRUE; X updateView = TRUE; X } X} X XpeekStop() X{ X M.peeking = FALSE; X updateView = TRUE; X} X XDoViewUpdate() X{ X if (updateView) { /* paint the screen */ X ShowPosition(M.xloc, M.yloc, M.invincible, M.dir); X if (M.peeking) X ShowView(M.xPeek, M.yPeek, M.dirPeek); X else X ShowView(M.xloc, M.yloc, M.dir); X updateView = FALSE; X } X} X Xshoot() X{ X M.score--; X M.ratcb.rats[M.myRatId].score--; X UpdateScoreCard(M.myRatId); X sendKill(); X} X X/* X * add the shot to the shot queue. It'll be processed later. X */ X XholdBreath(ratKill) XRatKill ratKill; X{ X RatKillQ_t rkp; X struct timeval now; X X DeadRatCursor(); X gettimeofday(&now, NULL); X X if (RatKillQ == NULL) { X RatKillQ = (RatKillQ_t) malloc(sizeof(AqRatKillQ)); X rkp = RatKillQ; X } else { X for (rkp = RatKillQ; rkp->nextOne != NULL; rkp = rkp->nextOne) X ; X rkp->nextOne = (RatKillQ_t) malloc(sizeof(AqRatKillQ)); X rkp = rkp->nextOne; X } X X bcopy((char *)ratKill, (char *)&rkp->thisOne, sizeof(AqRatKill)); X rkp->nextOne = NULL; X rkp->shotHits = now; X rkp->shotHits.tv_sec++; X} X X/* X * finally see if the shot hit home. X */ X XhandleKill(tx, ty, td, ratId) XLoc tx, ty; XDirection td; XRatId ratId; X{ X while (!M.maze[tx].y[ty]) { X switch (td) { X case NORTH: tx++; break; X case SOUTH: tx--; break; X case EAST: ty++; break; X case WEST: ty--; break; X } X if ((M.xloc == tx) && (M.yloc == ty)) { /* Oh oh... */ X sendDead(ratId); X NewPosition(); /* avoid multiple hits */ X FlashScreen(); X M.score -= 5; /* minus 5 points for getting killed */ X M.ratcb.rats[M.myRatId].score = M.score; X UpdateScoreCard(M.myRatId); X NotifyPlayer(); X updateView = TRUE; X sendLocation = TRUE; X } X } X RatCursor(); X} X X/* X * Process the pending shots, if any. X */ X XDoRatKillQ() X{ X struct timeval now; X RatKillQ_t rkp = RatKillQ; X RatKill ratKill; X X if (RatKillQ != NULL) { X gettimeofday(&now, NULL); X while (rkp != NULL) { X if (now.tv_sec >= rkp->shotHits.tv_sec) { X if (now.tv_usec >= X rkp->shotHits.tv_usec) { X ratKill = &RatKillQ->thisOne; X handleKill(ratKill->xLoc, X ratKill->yLoc, X ratKill->dir, X ratKill->ratId); X RatKillQ = RatKillQ->nextOne; X free((char *) rkp); X rkp = RatKillQ; X } else X break; X } else X break; X } X if (RatKillQ == NULL) X RatCursor(); X } X} X X/* X * Convert the contents of a packet to network order before sending. X */ X XConvertOutgoing(p) XRatPacket *p; X{ X char buf[64]; X RatId ratId; X RatLocation ratLoc; X RatKill ratKill; X RatDead ratDead; X RatStatus ratStatus; X RatNew ratNew; X RatGone ratGone; X RatQuery ratQuery; X RatAlive ratAlive; X RatMove ratMove; X X switch(p->type) { X case RAT_LOCATION: X ratLoc = (RatLocation) &p->body; X ratLoc->ratId = htonl(ratLoc->ratId); X ratLoc->xLoc = htons(ratLoc->xLoc); X ratLoc->yLoc = htons(ratLoc->yLoc); X ratLoc->dir = htons(ratLoc->dir); X ratLoc->score = htonl(ratLoc->score); X break; X X case RAT_KILL: X ratKill = (RatKill) &p->body; X ratKill->ratId = htonl(ratKill->ratId); X ratKill->xLoc = htons(ratKill->xLoc); X ratKill->yLoc = htons(ratKill->yLoc); X ratKill->dir = htons(ratKill->dir); X break; X X case RAT_DEAD: X ratDead = (RatDead) &p->body; X ratDead->ratId = htonl(ratDead->ratId); X ratDead->killedBy = htonl(ratDead->killedBy); X break; X X case RAT_STATUS: X ratStatus = (RatStatus) &p->body; X ratStatus->dukeRat = htonl(ratStatus->dukeRat); X for (ratId = 0; ratId < MAXRATS; ratId++) { X RatInfo ratInfo; X X ratInfo = &ratStatus->rats[ratId]; X ratInfo->playing = htons(ratInfo->playing); X ratInfo->xLoc = htons(ratInfo->xLoc); X ratInfo->yLoc = htons(ratInfo->yLoc); X ratInfo->dir = htons(ratInfo->dir); X ratInfo->score = htonl(ratInfo->score); X ratInfo->addr.sin_family = X ntohs(ratInfo->addr.sin_family); X /* don't touch address or name */ X } X break; X X case RAT_NEW: X ratNew = (RatNew) &p->body; X ratNew->pass = htons(ratNew->pass); X ratNew->xLoc = htons(ratNew->xLoc); X ratNew->yLoc = htons(ratNew->yLoc); X ratNew->dir = htons(ratNew->dir); X ratNew->addr.sin_family = X htons(ratNew->addr.sin_family); X /* don't touch address or name */ X break; X X case RAT_GOING: X ratGone = (RatGone) &p->body; X ratGone->ratId = htonl(ratGone->ratId); X break; X X case RAT_QUERY: X ratQuery = (RatQuery) &p->body; X ratQuery->ratId = htonl(ratQuery->ratId); X break; X X case RAT_ALIVE: X ratAlive = (RatAlive) &p->body; X ratAlive->ratId = htonl(ratAlive->ratId); X break; X X case RAT_SURVEY: X ratNew = (RatNew) &p->body; X ratNew->pass = htons(ratNew->pass); X ratNew->xLoc = htons(ratNew->xLoc); X ratNew->yLoc = htons(ratNew->yLoc); X ratNew->dir = htons(ratNew->dir); X /* don't touch address or name */ X break; X X case RAT_MOVE: X ratMove = (RatMove) &p->body; X ratMove->ratId = htonl(ratMove->ratId); X break; X X default: X sprintf(buf, "ConvertOutgoing bad type %d (%d)", X p->type, htons(p->type)); X MWError(buf); X } X p->type = htonl(p->type); X} X X/* X * Convert the contents of a packet to host order after ConvertIncoming. X */ X XConvertIncoming(p) XRatPacket *p; X{ X char buf[64]; X RatId ratId; X RatLocation ratLoc; X RatKill ratKill; X RatDead ratDead; X RatStatus ratStatus; X RatNew ratNew; X RatGone ratGone; X RatQuery ratQuery; X RatAlive ratAlive; X RatMove ratMove; X X p->type = ntohl(p->type); X switch(p->type) { X case RAT_LOCATION: X ratLoc = (RatLocation) &p->body; X ratLoc->ratId = ntohl(ratLoc->ratId); X ratLoc->xLoc = ntohs(ratLoc->xLoc); X ratLoc->yLoc = ntohs(ratLoc->yLoc); X ratLoc->dir = ntohs(ratLoc->dir); X ratLoc->score = ntohl(ratLoc->score); X break; X X case RAT_KILL: X ratKill = (RatKill) &p->body; X ratKill->ratId = ntohl(ratKill->ratId); X ratKill->xLoc = ntohs(ratKill->xLoc); X ratKill->yLoc = ntohs(ratKill->yLoc); X ratKill->dir = ntohs(ratKill->dir); X break; X X case RAT_DEAD: X ratDead = (RatDead) &p->body; X ratDead->ratId = ntohl(ratDead->ratId); X ratDead->killedBy = ntohl(ratDead->killedBy); X break; X X case RAT_STATUS: X ratStatus = (RatStatus) &p->body; X ratStatus->dukeRat = ntohl(ratStatus->dukeRat); X for (ratId = 0; ratId < MAXRATS; ratId++) { X RatInfo ratInfo; X X ratInfo = &ratStatus->rats[ratId]; X ratInfo->playing = ntohs(ratInfo->playing); X ratInfo->xLoc = ntohs(ratInfo->xLoc); X ratInfo->yLoc = ntohs(ratInfo->yLoc); X ratInfo->dir = ntohs(ratInfo->dir); X ratInfo->score = ntohl(ratInfo->score); X ratInfo->addr.sin_family = X ntohs(ratInfo->addr.sin_family); X /* don't touch address or name */ X } X break; X X case RAT_NEW: X ratNew = (RatNew) &p->body; X ratNew->pass = ntohs(ratNew->pass); X ratNew->xLoc = ntohs(ratNew->xLoc); X ratNew->yLoc = ntohs(ratNew->yLoc); X ratNew->dir = ntohs(ratNew->dir); X ratNew->addr.sin_family = X ntohs(ratNew->addr.sin_family); X /* don't touch address or name */ X break; X X case RAT_GOING: X ratGone = (RatGone) &p->body; X ratGone->ratId = ntohl(ratGone->ratId); X break; X X case RAT_QUERY: X ratQuery = (RatQuery) &p->body; X ratQuery->ratId = ntohl(ratQuery->ratId); X break; X X case RAT_ALIVE: X ratAlive = (RatAlive) &p->body; X ratAlive->ratId = ntohl(ratAlive->ratId); X break; X X case RAT_SURVEY: X ratNew = (RatNew) &p->body; X ratNew->pass = ntohs(ratNew->pass); X ratNew->xLoc = ntohs(ratNew->xLoc); X ratNew->yLoc = ntohs(ratNew->yLoc); X ratNew->dir = ntohs(ratNew->dir); X /* don't touch address or name */ X break; X X case RAT_MOVE: X ratMove = (RatMove) &p->body; X ratMove->ratId = ntohl(ratMove->ratId); X break; X X default: X sprintf(buf, "ConvertIncoming bad type %d (%d)", X p->type, ntohs(p->type)); X MWError(buf); X } X} X X#ifdef PACKET_TRACE Xstatic char *packTypes[] = { X "RAT_LOCATION", X "RAT_KILL", X "RAT_DEAD", X "RAT_STATUS", X "RAT_NEW", X "RAT_GOING", X "RAT_QUERY", X "RAT_ALIVE", X "RAT_SURVEY", X "RAT_MOVE", X 0 X}; X#endif PACKET_TRACE X XreadRats(evp) XMWEvent *evp; X{ X register RatLocation ratLoc; X register RatLook ratLook; X register RatAlive ratAlive; X RatPacket *pack = evp->eventDetail; X RatInfo ratInfo; X Boolean oldVisible; X RatId ratId; X RatStatus status; X RatNew ratNew; X RatGone ratGone; X RatKill ratKill; X RatDead ratDead; X RatQuery ratQuery; X RatMove ratMove; X Sockaddr nullAddr; X char buf[32]; X int newSocket; X X#ifdef PACKET_TRACE X printf("received %s (%d)\n", X packTypes[pack->type - 1], pack->type); X#endif PACKET_TRACE X X switch(pack->type) { X case RAT_LOCATION: /* someone moved */ X ratLoc = (RatLocation) &pack->body; X ratLook = &R2d2[ratLoc->ratId]; X if ((oldVisible = ratLook->visible) == TRUE) X XORToken(ratLoc->ratId); X ratInfo = &M.ratcb.rats[ratLoc->ratId]; X ratInfo->xLoc = ratLoc->xLoc; X ratInfo->yLoc = ratLoc->yLoc; X ratInfo->dir = ratLoc->dir; X DisplayOthersPosition(ratLoc->ratId, ratLoc->xLoc, X ratLoc->yLoc, ratLoc->dir); X TokenVisible(ratLoc->ratId); X if (ratLook->visible == TRUE) X XORToken(ratLoc->ratId); X if ((oldVisible != ratLook->visible) || X (ratInfo->score != ratLoc->score)) { X ratInfo->score = ratLoc->score; X UpdateScoreCard(ratLoc->ratId); X } X ratHealth[ratLoc->ratId].rcvd = TRUE; X break; X X case RAT_KILL: /* someone shot at me */ X if (!M.invincible) { X ratKill = (RatKill) &pack->body; X holdBreath(ratKill); X } X break; X X case RAT_DEAD: /* I hit someone */ X ratDead = (RatDead) &pack->body; X if (ratDead->killedBy == M.myRatId) { X FlashTop(); /* got him! */ X M.score += 10; /* 10 points for a kill */ X M.score += 1; /* make up for shot cost */ X M.ratcb.rats[M.myRatId].score = M.score; X UpdateScoreCard(M.myRatId); X sendLocToAll(); X } X break; X X case RAT_STATUS: /* new info about everyone */ X status = (RatStatus) &pack->body; X if (bcmp(&status->rats[M.myRatId].addr, &M.myAddr, X sizeof(M.myAddr)) != 0) X break; /* not for me, from another game */ X /* perhaps left over from findDuke() */ X X /* Have a new table, turn off any visible opponents */ X X for (ratId = 0; ratId < MAXRATS; ratId++) X if (R2d2[ratId].visible == TRUE) X XORToken(ratId); X bcopy((char *)status, (char *)&M.ratcb, sizeof(RatCb)); X if (M.ratcb.dukeRat == M.myRatId) X M.duke = TRUE; X for (ratId = 0; ratId < MAXRATS; ratId++) { X TokenVisible(ratId); X if (R2d2[ratId].visible == TRUE) X XORToken(ratId); X } X NewScoreCard(); X ratInfo = &M.ratcb.rats[M.myRatId]; X if ((ratInfo->xLoc != M.xloc) || X (ratInfo->yLoc != M.yloc) || X (ratInfo->dir != M.dir)) X sendLocToAll(); X X break; X X case RAT_NEW: /* new player */ X ratNew = (RatNew) &pack->body; X if (ratNew->pass == RAT_PASSWORD) { X if (M.duke) { X /* X * need to check for duplicates here; X * a previous reply might have been X * lost. Can't let the same guy in the X * game twice. X */ X X register RatId id; X X for (id = 0; id < MAXRATS; id++) X if (M.ratcb.rats[id].playing && X !bcmp(&M.ratcb.rats[id].addr, X &evp->eventSource, X sizeof (Sockaddr))) { X X /* already there */ X sendStatus(evp->eventSource); X break; X } X if (id >= MAXRATS) { /* fell through */ X allocateNewRat(ratNew); X sendAllStatus(); X } X } else X sendStatus(evp->eventSource); X } X break; X X case RAT_GOING: /* player leaving, only rcvd if duke */ X ratGone = (RatGone) &pack->body; X ratLeft(ratGone->ratId); X break; X X case RAT_QUERY: /* are you alive? */ X /* X * register their net address, in case they got a X * RAT_MOVE, moved, and the change got lost somewhere. X */ X ratQuery = (RatQuery) &pack->body; X M.ratcb.rats[ratQuery->ratId].addr = evp->eventSource; X X sendAlive(); X break; X X case RAT_ALIVE: /* I am alive */ X ratAlive = (RatAlive) &pack->body; X ratHealth[ratAlive->ratId].rcvd = TRUE; X break; X X case RAT_SURVEY: /* who's out there? */ X ratNew = (RatNew) &pack->body; X if (ratNew->pass == RAT_PASSWORD) X sendStatus(evp->eventSource); X break; X X case RAT_MOVE: /* move to M.mazePort */ X ratMove = (RatMove) &pack->body; X if (ratMove->ratId != M.ratcb.dukeRat) X MWError("bogus RAT_MOVE"); X close(M.theSocket); X X /* X * If the socket being closed is on this machine, X * leave some time for the socket to close down before X * I try to grab it. X */ X X if (bcmp((char *) &evp->eventSource.sin_addr, X (char *) &M.myAddr.sin_addr, X sizeof(M.myAddr.sin_addr)) == 0) X sleep(1); X X /* grab the socket */ X X newSocket = socket(AF_INET, SOCK_DGRAM, 0); X if (newSocket < 0) X MWError("RAT_MOVE lost socket"); X if (dup2(newSocket, M.theSocket) < 0) X MWError("RAT_MOVE dup2 failed"); X if (setsockopt(M.theSocket, SOL_SOCKET, SO_REUSEADDR, X NULL, 0) < 0) X MWError("RAT_MOVE can't reuse addresses"); X M.myAddr.sin_port = M.mazePort; X nullAddr = M.myAddr; /* see netInit() */ X bzero((char *) &nullAddr.sin_addr, sizeof(nullAddr.sin_addr)); X if (bind(M.theSocket, &nullAddr, sizeof(nullAddr)) < 0) X MWError("RAT_MOVE can't bind"); X M.ratcb.rats[M.myRatId].addr = M.myAddr; X sendAllStatus(); X break; X X default: X sprintf(buf, "readRats bad packet type 0x%x", pack->type); X MWError(buf); X } X} X X/* X * In order to reduce the network traffic, only send out the location change if X * there's no keyboard input pending. X */ X XSendLocation() X{ X if (!KBEventPending()) X if (sendLocation) { X sendLocToAll(); X sendLocation = FALSE; X } X} X X/* X * Let everyone know I've moved. X */ X XsendLocToAll() X{ X RatPacket pack; X RatLocation ratloc; X RatId i; X RatInfo ratInfo = &M.ratcb.rats[M.myRatId]; X X ratInfo->xLoc = M.xloc; /* update my table, too */ X ratInfo->yLoc = M.yloc; X ratInfo->dir = M.dir; X ratInfo->score = M.score; X X pack.type = RAT_LOCATION; X ratloc = (RatLocation) &pack.body; X ratloc->ratId = M.myRatId; X ratloc->xLoc = ratInfo->xLoc; X ratloc->yLoc = ratInfo->yLoc; X ratloc->dir = ratInfo->dir; X ratloc->score = ratInfo->score; X ConvertOutgoing(&pack); X X /* X * Would really like this to be asynchronous, so play could X * continue while the packets are being sent. Of course, then, X * the data might change in the midst of all this... X */ X X for (i = 0; i < MAXRATS; i++) X if ((i != M.myRatId) && (M.ratcb.rats[i].playing)) X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[i].addr, sizeof(Sockaddr)) < 0) X MWError("sendLocToAll"); X} X XsendAllStatus() X{ X RatId i; X X for (i = 0; i < MAXRATS; i++) X if ((i != M.myRatId) && (M.ratcb.rats[i].playing)) X sendStatus(M.ratcb.rats[i].addr); X} X X/* X * Send the entire status data to a rat. X */ X XsendStatus(to) XSockaddr to; X{ X RatPacket pack; X X pack.type = RAT_STATUS; X pack.body = M.ratcb; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, &to, sizeof(to)) < 0) X MWError("sendStatus"); X} X X/* X * Tell a player he's being shot at. X */ X XsendKill() X{ X RatPacket pack; X RatKill ratKill; X RatId ixRatId; X X for (ixRatId = 0; ixRatId < MAXRATS; ixRatId++) { X if (ixRatId == M.myRatId) X continue; X if (R2d2[ixRatId].visible) { X pack.type = RAT_KILL; X ratKill = (RatKill) &pack.body; X ratKill->ratId = M.myRatId; X ratKill->xLoc = M.xloc; X ratKill->yLoc = M.yloc; X ratKill->dir = M.dir; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[ixRatId].addr, X sizeof(M.ratcb.rats[ixRatId].addr)) < 0) X MWError("sendKill"); X } X } X} X X/* X * Tell a shooter he hit me. X */ X XsendDead(killerRatId) XRatId killerRatId; X{ X RatPacket pack; X RatDead ratDead; X X pack.type = RAT_DEAD; X ratDead = (RatDead) &pack.body; X ratDead->ratId = M.myRatId; X ratDead->killedBy = killerRatId; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[killerRatId].addr, sizeof(Sockaddr)) < 0) X MWError("sendDead"); X} X X/* X * Tell the duke I'm leaving. X */ X XsendGoing() X{ X RatPacket pack; X RatGone ratGone; X X pack.type = RAT_GOING; X ratGone = (RatGone) &pack.body; X ratGone->ratId = M.myRatId; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[M.ratcb.dukeRat].addr, sizeof(Sockaddr)) < 0) X MWError("sendGoing"); X} X X/* X * Ask the silent types if they're alive. X */ X XsendQuery() X{ X RatPacket pack; X RatId ratId; X RatQuery ratQuery; X X for (ratId = 0; ratId < MAXRATS; ratId++) X if (ratHealth[ratId].send) { X pack.type = RAT_QUERY; X ratQuery = (RatQuery) &pack.body; X ratQuery->ratId = M.myRatId; X ratHealth[ratId].send = FALSE; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[ratId].addr, X sizeof(Sockaddr)) < 0) X MWError("sendQuery"); X } X} X X/* X * Register someone as alive, and let them know we are, too. X */ X XsendAlive() X{ X RatPacket pack; X RatId ratId; X RatAlive ratAlive; X X for (ratId = 0; ratId < MAXRATS; ratId++) { X if ((ratId == M.myRatId) || X !M.ratcb.rats[ratId].playing) X continue; X pack.type = RAT_ALIVE; X ratAlive = (RatAlive) &pack.body; X ratAlive->ratId = M.myRatId; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[ratId].addr, sizeof(Sockaddr)) < 0) X MWError("sendAlive"); X } X} X X/* X * Let a new player in the game. X */ X XallocateNewRat(ratNew) XRatNew ratNew; X{ X RatId ratId; X RatInfo ratInfo; X X for (ratId = 0; ratId < MAXRATS; ratId++) { X ratInfo = &M.ratcb.rats[ratId]; X if (!ratInfo->playing) { X ratInfo->playing = TRUE; X ratInfo->xLoc = ratNew->xLoc; X ratInfo->yLoc = ratNew->yLoc; X ratInfo->dir = ratNew->dir; X ratInfo->score = 0; X ratInfo->addr = ratNew->addr; X strncpy(ratInfo->name, ratNew->name, NAMESIZE); X TokenVisible(ratId); X UpdateScoreCard(ratId); X if (R2d2[ratId].visible == TRUE) X XORToken(ratId); X AddNewPlayer(ratId, ratNew->xLoc, ratNew->yLoc, X ratNew->dir); X return; X } X } X} X X/* X * I wanna go home! X */ X Xquit() X{ X RatId ratId; X X if (!M.duke) X sendGoing(); X else { /* oh oh, I'm the duke rat */ X M.ratcb.rats[M.myRatId].playing = FALSE; X for (ratId = 0; ratId < MAXRATS; ratId++) X if (M.ratcb.rats[ratId].playing) { X M.ratcb.dukeRat = ratId; X sendAllStatus(); X break; X } X moveSomeone(M.myRatId); X } X StopWindow(); X exit(0); X} X X/* X * Clean up after someone who has left. Let everyone else know, too. X */ X XratLeft(ratId) XRatId ratId; X{ X if (R2d2[ratId].visible == TRUE) X XORToken(ratId); X R2d2[ratId].visible = FALSE; X M.ratcb.rats[ratId].playing = FALSE; X ExitPlayer(ratId); X UpdateScoreCard(ratId); X sendAllStatus(); X moveSomeone(ratId); X} X X/* X * See if the guy leaving has vacated the reserved port. If so, try to X * find someone else on that machine and tell him to move there. X */ X XmoveSomeone(ratId) XRatId ratId; X{ X Sockaddr hisAddr; X RatId newRat; X RatPacket pack; X RatMove ratMove; X X hisAddr = M.ratcb.rats[ratId].addr; X if (hisAddr.sin_port != M.mazePort) X return; X X for (newRat = 0; newRat < MAXRATS; newRat++) { X if (newRat == ratId) X continue; X if (M.ratcb.rats[newRat].playing == FALSE) X continue; X if (!bcmp(&M.ratcb.rats[newRat].addr.sin_addr, X &hisAddr.sin_addr, sizeof(hisAddr.sin_addr))) { X pack.type = RAT_MOVE; X ratMove = (RatMove) &pack.body; X ratMove->ratId = M.ratcb.dukeRat; X ConvertOutgoing(&pack); X if (sendto(M.theSocket, &pack, sizeof(pack), 0, X &M.ratcb.rats[newRat].addr, X sizeof(Sockaddr)) < 0) X MWError("moveSomeone"); X X /* X * If I'm the one leaving, must free up my port. X */ X X if (ratId == M.myRatId) X close(M.theSocket); X break; X } X } X X} X X/* X * Make sure nobody has died unnoticed. X */ X Xstatic Boolean started = FALSE; Xstruct timeval waitStart; Xstatic Boolean runDoctor = TRUE; X Xstatic XratDoctor() X{ X RatId ratId, nextRatId(); X struct timeval now; X X if (!runDoctor) X return; X X if (started == FALSE) { X gettimeofday(&waitStart, NULL); X for (ratId = 0; ratId < MAXRATS; ratId++) { X ratHealth[ratId].count = 0; X ratHealth[ratId].send = FALSE; X ratHealth[ratId].rcvd = FALSE; X } X started = TRUE; X return; X } else { X gettimeofday(&now, NULL); X if ((now.tv_sec - waitStart.tv_sec) < 5) X return; X for (ratId = 0; ratId < MAXRATS; ratId++) { X if ((!M.ratcb.rats[ratId].playing) || X (ratId == M.myRatId)) X continue; X if (ratHealth[ratId].rcvd) { X ratHealth[ratId].count = 0; X ratHealth[ratId].rcvd = FALSE; X continue; X } X if (++ratHealth[ratId].count < 5) { X ratHealth[ratId].send = TRUE; X continue; X } X if (M.duke || X ((M.ratcb.dukeRat == ratId) && X (nextRatId(ratId) == M.myRatId))) { X M.duke = TRUE; X M.ratcb.dukeRat = M.myRatId; X ratLeft(ratId); X } X } X sendQuery(); X gettimeofday(&waitStart, NULL); X } X X} X XRatId XnextRatId(ratId) XRatId ratId; X{ X RatId ixRatId; X X for (ixRatId = 0; ixRatId < MAXRATS; ixRatId++) X if (M.ratcb.rats[ixRatId].playing && X (ixRatId != ratId)) X return ixRatId; X return ixRatId; X} X XNewPosition() X{ X register rndCnt = 0; X X M.xloc = M.yloc = 0; /* start on occupied square */ X while (mp[M.xloc].y[M.yloc]) { X M.xloc = random(MAZEXMAX); X M.yloc = random(MAZEYMAX); X if (++rndCnt == 100) { X rndCnt = 0; X InitRandom(); X } X } X X /* prevent a blank wall at first glimpse */ X X if (!M.maze[M.xloc+1].y[M.yloc]) M.dir = NORTH; X if (!M.maze[M.xloc-1].y[M.yloc]) M.dir = SOUTH; X if (!M.maze[M.xloc].y[M.yloc+1]) M.dir = EAST; X if (!M.maze[M.xloc].y[M.yloc-1]) M.dir = WEST; X X return; X} X X/* re-initialize the maze randomization vector */ XInitRandom() X{ X struct timeval t; X struct timezone tz; X register int i; X X gettimeofday(&t, &tz); X for (i = 0; i < VECTORSIZE; i++) X M.randomVector[i] = M.randomVector[i] + t.tv_sec & 0xffff; X} X Xrandom(limit) Xregister int limit; X{ X register unsigned int ret; X X ret = M.randomVector[i1] = M.randomVector[i1] + M.randomVector[i2]; X if (++i1 >= VECTORSIZE) X i1 = 0; X if (++i2 >= VECTORSIZE) X i2 = 0; X return ret%limit; X} X XMWError(s) Xchar *s; X{ X StopWindow(); X fprintf(stderr, "MazeWar: %s\n", s); X perror("MazeWar"); X exit(-1); X} xxFUNNYxx