view socket.c @ 3:5a977ccbc7a9 default tip

Empty changelog
author darius
date Sat, 06 Dec 1997 05:41:29 +0000
parents
children
line wrap: on
line source

/* $Id: socket.c,v 1.1.1.1 1997/12/06 05:41:31 darius Exp $ */

/*
 * Socket.c
 *
 * Kevin P. Smith 1/29/89
 * UDP stuff v1.0 by Andy McFadden  Feb-Apr 1992
 *
 * UDP protocol v1.0
 *
 * Routines to allow connection to the xtrek server.
 */
#include "copyright2.h"

/* to see the packets sent/received: [BDyess] */
#if 0
#define SHOW_SEND
#define SHOW_RECEIVED
#endif				/* 0 */

#ifndef GATEWAY
#define USE_PORTSWAP		/* instead of using recvfrom() */
#endif

#undef USE_PORTSWAP		/* recvfrom is a better scheme */

#include <stdio.h>
#ifdef __STDC__
#include <stdlib.h>
#endif
#include <sys/types.h>
#include <sys/time.h>
#ifdef RS6K
#include <sys/select.h>
#endif
#ifndef DNET
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#else
#include <devices/timer.h>
#include <dos/dos.h>
#endif				/* DNET */
#include <math.h>
#include <errno.h>
#include <zlib.h>		/* for terrain info */
#include "Wlib.h"
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "packets.h"
#include "proto.h"
#include "gameconf.h"
#ifdef SOUND
#include "Slib.h"
#endif				/* SOUND */
#include "sound.h"

#define INCLUDE_SCAN		/* include Amdahl scanning beams */
#define INCLUDE_VISTRACT	/* include visible tractor beams */

#ifdef GATEWAY
/*
 * (these values are now defined in "main.c":)
 * char *gw_mach        = "charon";     |client gateway; strcmp(serverName)
 * int   gw_serv_port   = 5000;         |what to tell the server to use
 * int   gw_port        = 5001;         |where we will contact gw
 * int   gw_local_port  = 5100;         |where we expect gw to contact us
 *
 * The client binds to "5100" and sends "5000" to the server (TCP).  The
 * server sees that and sends a UDP packet to gw on port udp5000, which passes
 * it through to port udp5100 on the client.  The client-gw gets the server's
 * host and port from recvfrom.  (The client can't use the same method since
 * these sockets are one-way only, so it connect()s to gw_port (udp5001)
 * on the gateway machine regardless of what the server sends.)
 *
 * So all we need in .gwrc is:
 *      udp 5000 5001 tde.uts 5100
 *
 * assuming the client is on tde.uts.  Note that a UDP declaration will
 * work for ANY server, but you need one per player, and the client has to
 * have the port numbers in advance.
 *
 * If we're using a standard server, we're set.  If we're running through a
 * gatewayed server, we have to do some unpleasant work on the server side...
 */
#endif

#ifdef SIZE_LOGGING
int     send_total = 0;
int     receive_total = 0;
#endif				/* SIZE_LOGGING */

/* Prototypes */
static void resetForce P((void));
static void checkForce P((void));
#ifdef nodef
static void set_tcp_opts P((int s));
static void set_udp_opts P((int s));
#endif				/* nodef */
static int doRead P((int asock));
static void handleTorp P((struct torp_spacket * packet));
static void handleTorpInfo P((struct torp_info_spacket * packet));
static void handleStatus P((struct status_spacket * packet));
static void handleSelf P((struct you_spacket * packet));
static void handlePlayer P((struct player_spacket * packet));
static void handleWarning P((struct warning_spacket * packet));
void sendServerPacket P((struct player_spacket * packet));
static void handlePlanet P((struct planet_spacket * packet));
static void handlePhaser P((struct phaser_spacket * packet));
void handleMessage P((struct mesg_spacket * packet));
static void handleQueue P((struct queue_spacket * packet));
static void handlePickok P((struct pickok_spacket * packet));
static void handleLogin P((struct login_spacket * packet));
static void handlePlasmaInfo P((struct plasma_info_spacket * packet));
static void handlePlasma P((struct plasma_spacket * packet));
static void handleFlags P((struct flags_spacket * packet));
static void handleKills P((struct kills_spacket * packet));
static void handlePStatus P((struct pstatus_spacket * packet));
static void handleMotd P((struct motd_spacket * packet));
static void handleMask P((struct mask_spacket * packet));
static void pickSocket P((int old));
static void handleBadVersion P((struct badversion_spacket * packet));
int gwrite P((int fd, char *buf, int bytes));
static void handleHostile P((struct hostile_spacket * packet));
static void handlePlyrLogin P((struct plyr_login_spacket * packet));
static void handleStats P((struct stats_spacket * packet));
static void handlePlyrInfo P((struct plyr_info_spacket * packet));
static void handlePlanetLoc P((struct planet_loc_spacket * packet));
static void handleReserved P((struct reserved_spacket * packet));

static void handleScan P((struct scan_spacket * packet));
static void handleSequence P((struct sequence_spacket * packet));
static void handleUdpReply P((struct udp_reply_spacket * packet));
static void informScan P((int p));
static int openUdpConn P((void));
#ifdef USE_PORTSWAP
static int connUdpConn P((void));
#endif
static int recvUdpConn P((void));
static void printUdpInfo P((void));
/*static void dumpShip P((struct ship *shipp ));*/
/*static int swapl P((int in ));*/
static void handleShipCap P((struct ship_cap_spacket * packet));
static void handleMotdPic P((struct motd_pic_spacket * packet));
static void handleStats2 P((struct stats_spacket2 * packet));
static void handleStatus2 P((struct status_spacket2 * packet));
static void handlePlanet2 P((struct planet_spacket2 * packet));
static void handleTerrain2 P((struct terrain_packet2 * pkt));
static void handleTerrainInfo2 P((struct terrain_info_packet2 *pkt));
static void handleTempPack P((struct obvious_packet * packet));
static void handleThingy P((struct thingy_spacket * packet));
static void handleThingyInfo P((struct thingy_info_spacket * packet));
static void handleRSAKey P((struct rsa_key_spacket * packet));
void    handlePing();
static void handleExtension1 P((struct paradiseext1_spacket *));

static void handleEmpty();


#ifdef SHORT_PACKETS
void    handleShortReply(), handleVPlayer(), handleVTorp(),
        handleSelfShort(), handleSelfShip(), handleVPlanet(), handleSWarning();
void    handleVTorpInfo(), handleSMessage();
#endif

#ifdef FEATURE
void    handleFeature();	/* feature.c */
#endif

struct packet_handler handlers[] = {
    {NULL},			/* record 0 */
    {handleMessage},		/* SP_MESSAGE */
    {handlePlyrInfo},		/* SP_PLAYER_INFO */
    {handleKills},		/* SP_KILLS */
    {handlePlayer},		/* SP_PLAYER */
    {handleTorpInfo},		/* SP_TORP_INFO */
    {handleTorp},		/* SP_TORP */
    {handlePhaser},		/* SP_PHASER */
    {handlePlasmaInfo},		/* SP_PLASMA_INFO */
    {handlePlasma},		/* SP_PLASMA */
    {handleWarning},		/* SP_WARNING */
    {handleMotd},		/* SP_MOTD */
    {handleSelf},		/* SP_YOU */
    {handleQueue},		/* SP_QUEUE */
    {handleStatus},		/* SP_STATUS */
    {handlePlanet},		/* SP_PLANET */
    {handlePickok},		/* SP_PICKOK */
    {handleLogin},		/* SP_LOGIN */
    {handleFlags},		/* SP_FLAGS */
    {handleMask},		/* SP_MASK */
    {handlePStatus},		/* SP_PSTATUS */
    {handleBadVersion},		/* SP_BADVERSION */
    {handleHostile},		/* SP_HOSTILE */
    {handleStats},		/* SP_STATS */
    {handlePlyrLogin},		/* SP_PL_LOGIN */
    {handleReserved},		/* SP_RESERVED */
    {handlePlanetLoc},		/* SP_PLANET_LOC */
    {handleScan},		/* SP_SCAN (ATM) */
    {handleUdpReply},		/* SP_UDP_STAT */
    {handleSequence},		/* SP_SEQUENCE */
    {handleSequence},		/* SP_SC_SEQUENCE */
    {handleRSAKey},		/* SP_RSA_KEY */
    {handleMotdPic},		/* SP_MOTD_PIC */
    {handleStats2},		/* SP_STATS2 */
    {handleStatus2},		/* SP_STATUS2 */
    {handlePlanet2},		/* SP_PLANET2 */
    {handleTempPack},		/* SP_TEMP_5 */
    {handleThingy},		/* SP_THINGY */
    {handleThingyInfo},		/* SP_THINGY_INFO */
    {handleShipCap},		/* SP_SHIP_CAP */

#ifdef SHORT_PACKETS
    {handleShortReply},		/* SP_S_REPLY */
    {handleSMessage},		/* SP_S_MESSAGE */
    {handleSWarning},		/* SP_S_WARNING */
    {handleSelfShort},		/* SP_S_YOU */
    {handleSelfShip},		/* SP_S_YOU_SS */
    {handleVPlayer},		/* SP_S_PLAYER */
#else
    {handleEmpty},		/* 40 */
    {handleEmpty},		/* 41 */
    {handleEmpty},		/* 42 */
    {handleEmpty},		/* 43 */
    {handleEmpty},		/* 44 */
    {handleEmpty},		/* 45 */
#endif
    {handlePing},		/* SP_PING */
#ifdef SHORT_PACKETS
    {handleVTorp},		/* SP_S_TORP */
    {handleVTorpInfo},		/* SP_S_TORP_INFO */
    {handleVTorp},		/* SP_S_8_TORP */
    {handleVPlanet},		/* SP_S_PLANET */
#else
    {handleEmpty},		/* 47 */
    {handleEmpty},
    {handleEmpty},
    {handleEmpty},		/* 50 */
#endif
    {handleGameparams},
    {handleExtension1},
    {handleTerrain2},		/* 53 */
    {handleTerrainInfo2},
    {handleEmpty},
    {handleEmpty},
    {handleEmpty},
    {handleEmpty},
    {handleEmpty},		/* 59 */
#ifdef FEATURE
    {handleFeature},		/* SP_FEATURE */
#else
    {handleEmpty},		/* 60 */
#endif
};

#define NUM_HANDLERS	(sizeof(handlers)/sizeof(*handlers))

#define NUM_PACKETS (sizeof(handlers) / sizeof(handlers[0]) - 1)

int     serverDead = 0;

int UdpLocalPort = 0;	/* do we want a specified local UDP port */

#ifdef SIZE_LOGGING
void
print_totals()
/* prints the total number of bytes sent/received.  Called when exiting the
   client [BDyess] */
{
    time_t  timeSpent = time(NULL) - timeStart;

    /*
       printf("Total bytes sent:     %d\nTotal bytes received: %d\n",
       send_total, receive_total);
    */
    /* ftp format [BDyess] */
    if (timeSpent < 600 /* 10 minutes */ ) {
	printf("%8d bytes sent     in %d seconds (%.3f Kbytes/s)\n",
	       send_total, timeSpent,
	       (float) send_total / (1024.0 * timeSpent));
	printf("%8d bytes received in %d seconds (%.3f Kbytes/s)\n",
	       receive_total, timeSpent,
	       (float) receive_total / (1024.0 * timeSpent));
    } else {			/* number too big for seconds, use minutes */
	printf("%8d bytes sent     in %.1f minutes (%.3f Kbytes/s)\n",
	       send_total, timeSpent / 60.0,
	       (float) send_total / (1024.0 * timeSpent));
	printf("%8d bytes received in %.1f minutes (%.3f Kbytes/s)\n",
	       receive_total, timeSpent / 60.0,
	       (float) receive_total / (1024.0 * timeSpent));
    }
}
#else
#define EXIT exit
#endif				/* SIZE_LOGGING */

int     udpLocalPort = 0;
static int udpServerPort = 0;
static long serveraddr = 0;
static long sequence = 0;
static int drop_flag = 0;
static int chan = -1;		/* tells sequence checker where packet is
				   from */
static short fSpeed, fDirection, fShield, fOrbit, fRepair, fBeamup, fBeamdown, fCloak,
        fBomb, fDockperm, fPhaser, fPlasma, fPlayLock, fPlanLock, fTractor,
        fRepress;

/* reset all the "force command" variables */
static void
resetForce()
{
    fSpeed = fDirection = fShield = fOrbit = fRepair = fBeamup = fBeamdown =
    fCloak = fBomb = fDockperm = fPhaser = fPlasma = fPlayLock = fPlanLock =
    fTractor = fRepress = -1;
}

/*
 * If something we want to happen hasn't yet, send it again.
 *
 * The low byte is the request, the high byte is a max count.  When the max
 * count reaches zero, the client stops trying.  Checking is done with a
 * macro for speed & clarity.
 */
#define FCHECK_FLAGS(flag, force, const) {                      \
        if (force > 0) {                                        \
            if (((me->p_flags & flag) && 1) ^ ((force & 0xff) && 1)) {  \
                speedReq.type = const;                          \
                speedReq.speed = (force & 0xff);                \
                sendServerPacket((struct player_spacket *)&speedReq);   \
                V_UDPDIAG(("Forced %d:%d\n", const, force & 0xff));     \
                force -= 0x100;                                 \
                if (force < 0x100) force = -1;  /* give up */   \
            } else                                              \
                force = -1;                                     \
        }                                                       \
}
#define FCHECK_VAL(value, force, const) {                       \
        if (force > 0) {                                        \
            if ((value) != (force & 0xff)) {                    \
                speedReq.type = const;                          \
                speedReq.speed = (force & 0xff);                \
                sendServerPacket((struct player_spacket *)&speedReq);   \
                V_UDPDIAG(("Forced %d:%d\n", const, force & 0xff));     \
                force -= 0x100;                                 \
                if (force < 0x100) force = -1;  /* give up */   \
            } else                                              \
                force = -1;                                     \
        }                                                       \
}
#define FCHECK_TRACT(flag, force, const) {                      \
        if (force > 0) {                                        \
            if (((me->p_flags & flag) && 1) ^ ((force & 0xff) && 1)) {  \
                tractorReq.type = const;                        \
                tractorReq.state = ((force & 0xff) >= 0x40);    \
                tractorReq.pnum = (force & 0xff) & (~0x40);     \
                sendServerPacket((struct player_spacket *)&tractorReq); \
                V_UDPDIAG(("Forced %d:%d/%d\n", const,          \
                        tractorReq.state, tractorReq.pnum));    \
                force -= 0x100;                                 \
                if (force < 0x100) force = -1;  /* give up */   \
            } else                                              \
                force = -1;                                     \
        }                                                       \
}

static void
checkForce()
{
    struct speed_cpacket speedReq;
    struct tractor_cpacket tractorReq;

    /* upgrading kludge [BDyess] */
    if (!upgrading)
	FCHECK_VAL(me->p_speed, fSpeed, CP_SPEED);	/* almost always repeats */
    FCHECK_VAL(me->p_dir, fDirection, CP_DIRECTION);	/* (ditto) */
    FCHECK_FLAGS(PFSHIELD, fShield, CP_SHIELD);
    FCHECK_FLAGS(PFORBIT, fOrbit, CP_ORBIT);
    FCHECK_FLAGS(PFREPAIR, fRepair, CP_REPAIR);
    FCHECK_FLAGS(PFBEAMUP, fBeamup, CP_BEAM);
    FCHECK_FLAGS(PFBEAMDOWN, fBeamdown, CP_BEAM);
    FCHECK_FLAGS(PFCLOAK, fCloak, CP_CLOAK);
    FCHECK_FLAGS(PFBOMB, fBomb, CP_BOMB);
    FCHECK_FLAGS(PFDOCKOK, fDockperm, CP_DOCKPERM);
    FCHECK_VAL(phasers[me->p_no].ph_status, fPhaser, CP_PHASER);	/* bug: dir 0 */
    FCHECK_VAL(plasmatorps[me->p_no].pt_status, fPlasma, CP_PLASMA);	/* (ditto) */
    FCHECK_FLAGS(PFPLOCK, fPlayLock, CP_PLAYLOCK);
    FCHECK_FLAGS(PFPLLOCK, fPlanLock, CP_PLANLOCK);

#ifdef HOCKEY
    /* kludge to help prevent self-deflects */
    if(! (hockey && me->p_tractor == 'g'-'a'+10 /*puck*/)) {
#endif
      FCHECK_TRACT(PFTRACT, fTractor, CP_TRACTOR);
      FCHECK_TRACT(PFPRESS, fRepress, CP_REPRESS);
#ifdef HOCKEY
    }
#endif
}


int
idx_to_mask(i)
    int     i;
{
    if (i == number_of_teams)
	return ALLTEAM;
    return 1 << i;
}

int
mask_to_idx(m)
    int     m;
{
    int     i, j;
    for (i = 1, j = -1; m > 0 && i < 5; i++, m >>= 1)
	if (m & 1)
	    j += i;
    if (j > number_of_teams)
	j = number_of_teams;
    return j;
}


void
connectToServer(port)
    int     port;
{
#ifndef DNET
    int     s;
    struct sockaddr_in addr;
    struct sockaddr_in naddr;
    int     len;
    fd_set  readfds;
    struct timeval timeout;
    struct hostent *hp;

    serverDead = 0;
    if (sock != -1) {
	shutdown(sock, 2);
	sock = -1;
    }
    sleep(3);			/* I think this is necessary for some unknown
				   reason */

#ifdef RWATCH
    printf("rwatch: Waiting for connection. \n");
#else
    printf("Waiting for connection (port %d). \n", port);
#endif				/* RWATCH */

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#ifdef RWATCH
	printf("rwatch: I can't create a socket\n");
#else
	printf("I can't create a socket\n");
#endif				/* RWATCH */
#ifdef AUTOKEY
	if (autoKey)
	    W_AutoRepeatOn();
#endif

	EXIT(2);
    }
#ifndef RWATCH
#ifdef nodef			/* don't use for now */
    set_tcp_opts(s);
#endif				/* nodef */
#endif				/* RWATCH */

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    if (bind(s, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
	sleep(10);
	if (bind(s, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
	    sleep(10);
	    if (bind(s, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
#ifdef RWATCH
		printf("rwatch: I can't bind to port!\n");
#else
		printf("I can't bind to port!\n");
#endif				/* RWATCH */
#ifdef AUTOKEY
		if (autoKey)
		    W_AutoRepeatOn();
#endif

		EXIT(3);
	    }
	}
    }
    listen(s, 1);

    len = sizeof(naddr);

tryagain:
    timeout.tv_sec = 240;	/* four minutes */
    timeout.tv_usec = 0;
    FD_ZERO(&readfds);
    FD_SET(s, &readfds);
    if (select(32, &readfds, NULL, NULL, &timeout) == 0) {
#ifdef RWATCH
	printf("rwatch: server died.\n");
#else
	printf("Well, I think the server died!\n");
#endif				/* RWATCH */
#ifdef AUTOKEY
	if (autoKey)
	    W_AutoRepeatOn();
#endif

	EXIT(0);
    }
    sock = accept(s, (struct sockaddr *) & naddr, &len);

    if (sock == -1) {
#ifdef RWATCH
	perror("rwatch: accept");
#else
	perror("accept");
#endif				/* RWATCH */
	goto tryagain;
    }
    close(s);
    pickSocket(port);		/* new socket != port */


    /*
       This is strictly necessary; it tries to determine who the caller is,
       and set "serverName" and "serveraddr" appropriately.
    */
    len = sizeof(struct sockaddr_in);
    if (getpeername(sock, (struct sockaddr *) & addr, &len) < 0) {
	perror("unable to get peername");
	serverName = "nowhere";
    } else {
	serveraddr = addr.sin_addr.s_addr;
	hp = gethostbyaddr((char *) &addr.sin_addr.s_addr, sizeof(long), AF_INET);
	if (hp != NULL) {
	    serverName = (char *) malloc(strlen(hp->h_name) + 1);
	    strcpy(serverName, hp->h_name);
	} else {
	    serverName = (char *) malloc(strlen((char *) inet_ntoa(addr.sin_addr)) + 1);
	    strcpy(serverName, (char *) inet_ntoa(addr.sin_addr));
	}
    }
    printf("Connection from server %s (0x%x)\n", serverName, serveraddr);

#else				/* DNET */
    /*
       unix end DNet server process connects to the server, on specified *
       port
    */
    ConnectToDNetServer(port);
#endif				/* DNET */

}


#ifndef DNET
#ifdef nodef
static void
set_tcp_opts(s)
    int     s;
{
    int     optval = 1;
    struct protoent *ent;

    ent = getprotobyname("TCP");
    if (!ent) {
	fprintf(stderr, "TCP protocol not found.\n");
	return;
    }
    if (setsockopt(s, ent->p_proto, TCP_NODELAY, &optval, sizeof(int)) < 0)
	perror("setsockopt");
}

static void
set_udp_opts(s)
    int     s;
{
    int     optval = BUFSIZ;
    struct protoent *ent;
    ent = getprotobyname("UDP");
    if (!ent) {
	fprintf(stderr, "UDP protocol not found.\n");
	return;
    }
    if (setsockopt(s, ent->p_proto, SO_RCVBUF, &optval, sizeof(int)) < 0)
	perror("setsockopt");
}
#endif				/* nodef */

#endif				/* DNET */

void
callServer(port, server)
    int     port;
    char   *server;
{
#ifndef DNET
    int     s;
    struct sockaddr_in addr;
    struct hostent *hp;
#endif
    serverDead = 0;

    printf("Calling %s on port %d.\n", server, port);
#ifdef DNET
    CallDNetServer(server, port);
#else
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	printf("I can't create a socket\n");
	EXIT(0);
    }
#ifndef RWATCH
#ifdef nodef
    set_tcp_opts(s);
#endif				/* nodef */
#endif				/* RWATCH */

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    if ((addr.sin_addr.s_addr = inet_addr(server)) == -1) {
	if ((hp = gethostbyname(server)) == NULL) {
	    printf("Who is %s?\n", server);
	    EXIT(0);
	} else {
	    addr.sin_addr.s_addr = *(long *) hp->h_addr;
	}
    }
    serveraddr = addr.sin_addr.s_addr;

    if (connect(s, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
	printf("Server not listening!\n");
	EXIT(0);
    }
    printf("Got connection.\n");

    sock = s;
#endif				/* DNET */
/* pickSocket is utterly useless with DNet, but the server needs the
   packet to tell it the client is ready to start. */

#ifdef RECORDER
    startRecorder();
#endif
    pickSocket(port);		/* new socket != port */
}

int
isServerDead()
{
    return (serverDead);
}

void
socketPause(sec, usec)
    int     sec, usec;
{
    struct timeval timeout;
    fd_set  readfds;

#ifdef RECORDER
    if (playback)
	return;
#endif
#ifdef DNET
    (void) DNetServerPause(sec, usec, 0);
#else

    timeout.tv_sec = sec;
    timeout.tv_usec = usec;
    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);
    if (udpSock >= 0)		/* new */
	FD_SET(udpSock, &readfds);
    select(32, &readfds, 0, 0, &timeout);
#endif				/* DNET */
}

int
readFromServer()
{
    struct timeval timeout;
    fd_set  readfds;
    int     retval = 0, rs;

#ifdef RECORDER
    if (playback) {
	while (!pb_update)
	    doRead(sock);
	return 1;
    }
#endif

    if (serverDead)
	return (0);
    if (commMode == COMM_TCP)
	drop_flag = 0;		/* just in case */

#ifndef DNET
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);
    if (udpSock >= 0)
	FD_SET(udpSock, &readfds);
    if ((rs = select(32, &readfds, 0, 0, &timeout)) != 0) {
	if (rs < 0) {
	    /* NEW */
	    perror("select");
	    return 0;
	}
#else
	/* this should have been done before calling the function.. not sure about this, should maybe be doing
	   DNetServerPause here? -JR*/
	/*	sigsPending=Wait(sockMask | udpSockMask | SIGBREAKF_CTRL_C);
	 */
	if (sigsPending & SIGBREAKF_CTRL_C) {
	    printf("readFromServer: Ctrl-C break, exiting\n");
	    exit(0);
	}
	/* note for DNet FD_ISSET is  redefined. */
#endif				/* DNET */
	/* Read info from the xtrek server */
	if (FD_ISSET(sock, &readfds)) {
	    chan = sock;
	    retval += doRead(sock);
	}
	if (udpSock >= 0 && FD_ISSET(udpSock, &readfds)) {
	    /* WAS V_ *//* should be! */
	    V_UDPDIAG(("Activity on UDP socket\n"));
	    chan = udpSock;
	    if (commStatus == STAT_VERIFY_UDP) {
		warning("UDP connection established");
		sequence = 0;	/* reset sequence #s */
		resetForce();
		
		if (udpDebug)
		    printUdpInfo();
		UDPDIAG(("UDP connection established.\n"));
		
		commMode = COMM_UDP;
		commStatus = STAT_CONNECTED;
		commSwitchTimeout = 0;
		if (udpClientRecv != MODE_SIMPLE)
		    sendUdpReq(COMM_MODE + udpClientRecv);
		if (udpWin) {
		    udprefresh(UDP_CURRENT);
		    udprefresh(UDP_STATUS);
		}
	    }
	    retval += doRead(udpSock);
	}
#ifndef DNET
    }
#endif

    /* if switching comm mode, decrement timeout counter */
    if (commSwitchTimeout > 0) {
	if (!(--commSwitchTimeout)) {
	    /*
	      timed out; could be initial request to non-UDP server (which won't
	      be answered), or the verify packet got lost en route to the
	      server.  Could also be a request for TCP which timed out (weird),
	      in which case we just reset anyway.
	      */
	    commModeReq = commMode = COMM_TCP;
	    commStatus = STAT_CONNECTED;
	    if (udpSock >= 0)
		closeUdpConn();
	    if (udpWin) {
		udprefresh(UDP_CURRENT);
		udprefresh(UDP_STATUS);
	    }
	    warning("Timed out waiting for UDP response from server");
	    UDPDIAG(("Timed out waiting for UDP response from server\n"));
	}
    }
    /* if we're in a UDP "force" mode, check to see if we need to do something */
    if (commMode == COMM_UDP && udpClientSend > 1)
	checkForce();
    
    return (retval != 0);		/* convert to 1/0 */
}


/* this used to be part of the routine above */
char    buf[BUFSIZ * 2 + 16];

static int
doRead(asock)
    int     asock;
{
    char   *bufptr;
    int     size;
    int     count;
    int     temp;
#ifdef HANDLER_TIMES
    struct timeval htpre, htpost;
    extern void log_time(int, struct timeval *, struct timeval *);
#endif

#ifndef DNET
    struct timeval timeout;
    fd_set  readfds;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
#endif				/* DNET */

    count = sock_read(asock, buf, 2 * BUFSIZ);
#ifdef DNET
    if (count == 0) {		/* yuck. */
	return 0;
    }
#endif
/* TMP */
#ifdef nodef
    if (asock == udpSock)
	printf("read %d bytes\n", count);
#endif				/* nodef */

    if (count <= 0) {
	if (asock == udpSock) {
#ifndef DNET
	    if (errno == ECONNREFUSED) {
		struct sockaddr_in addr;

		UDPDIAG(("asock=%d, sock=%d, udpSock=%d, errno=%d\n",
			 asock, sock, udpSock, errno));
		UDPDIAG(("count=%d\n", count));
		UDPDIAG(("Hiccup(%d)!  Reconnecting\n", errno));
		addr.sin_addr.s_addr = serveraddr;
		addr.sin_port = htons(udpServerPort);
		addr.sin_family = AF_INET;
		if (connect(udpSock, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
		    perror("connect");
		    UDPDIAG(("Unable to reconnect\n"));
		    /* and fall through to disconnect */
		} else {
		    UDPDIAG(("Reconnect successful\n"));
		    return (0);
		}
	    }
#endif				/* DNET */
	    UDPDIAG(("*** UDP disconnected (res=%d, err=%d)\n",
		     count, errno));
	    warning("UDP link severed");
	    printUdpInfo();
	    closeUdpConn();
	    commMode = commModeReq = COMM_TCP;
	    commStatus = STAT_CONNECTED;
	    if (udpWin) {
		udprefresh(UDP_STATUS);
		udprefresh(UDP_CURRENT);
	    }
	    return (0);
	}
#ifndef RWATCH
	printf("1) Got read() of %d. Server dead\n", count);
	perror("");
#endif				/* RWATCH */
	serverDead = 1;
	return (0);
    }
    bufptr = buf;
    while (bufptr < buf + count) {
#ifdef SHORT_PACKETS
computesize:
	if ((*bufptr == SP_S_MESSAGE && (buf + count - bufptr <= 4))
	    || (buf + count - bufptr < 4)) {	/* last part may only be
						   needed for DNet...I'm not
						   so sure any more.
						   certainly doesn't hurt to
						   have it.-JR */
	    /*
	       printf("buf+count-bufptr=%d, *bufptr=%d\n",buf + count -
	       bufptr,*bufptr);
	    */
	    size = 0;		/* problem with reads breaking before size
				   byte for SP_S_MESSAGE has been read. Only
				   a problem for messages because reads break
				   on 4 byte boundaries,  other size bytes
				   are always in the first 4. -JR */
	} else
#endif
	{
	    size = size_of_spacket(bufptr);
	    if (size < 1) {
#if 1
		fprintf(stderr, "Unknown packet %d.  Faking it.\n",
			*bufptr);
		size = 4;
#else
		fprintf(stderr, "Unknown packet %d.  Aborting.\n",
			*bufptr);
		return (0);
#endif
	    }
#ifdef SHOW_RECEIVED
	    printf("recieved packet type %d, size %d\n", *bufptr, size);
#endif				/* SHOW_RECEIVED */
#ifdef PACKET_LIGHTS
	    light_receive();
#endif				/* PACKET_LIGHTS */
#ifdef SIZE_LOGGING
	    receive_total += size;
#endif				/* SIZE_LOGGING */
	}
	while (size > count + (buf - bufptr) || size == 0) {
	    /*
	       We wait for up to ten seconds for rest of packet. If we don't
	       get it, we assume the server died.
	    */
	    /*
	       printf("er, possible packet fragment, waiting for the
	       rest...\n");
	    */
#ifdef RECORDER
	    if (!playback)
#endif
	    {
#ifdef DNET
		temp = DNetServerPause(20, 0, asock);
#else				/* DNET */
		timeout.tv_sec = 20;
		timeout.tv_usec = 0;
		FD_ZERO(&readfds);
		FD_SET(asock, &readfds);
		/* readfds=1<<asock; */
		temp = select(32, &readfds, 0, 0, &timeout);
#endif
		if (temp == 0) {
		    printf("Packet fragment.  Server must be dead\n");
		    serverDead = 1;
		    return (0);
		}
	    }
#ifdef SHORT_PACKETS
	    if (size == 0)
		/* 84=largest short packet message - the 4 we have */
		temp = sock_read(asock, buf + count, 84);
	    else
#endif
		temp = sock_read(asock, buf + count, size - (count + (buf - bufptr)));
	    count += temp;
#ifdef DNET
	    if (temp < 0)
#else
	    if (temp <= 0)
#endif				/* DNET */
	    {
#ifndef RWATCH
		printf("2) Got read() of %d.  Server is dead\n", temp);
#endif				/* RWATCH */
		serverDead = 1;
		return (0);
	    }
#ifdef SHORT_PACKETS
	    if (size == 0)	/* for the SP_S_MESSAGE problem */
		goto computesize;
#endif
	}
#ifdef UPDATE_SIZES
	totalbytes += size;
	packetbytes[*bufptr] += size;
#endif
#ifdef RECORDER
	if (playback && (*bufptr == REC_UPDATE)) {
	    pb_update++;
	    me->p_tractor = bufptr[1];
	    if (me->p_flags & PFPLOCK)
		me->p_playerl = bufptr[2];
	    else
		me->p_planet = bufptr[2];
/*	    printf("Read REC_UPDATE pseudo-packet!\n");*/
	} else
#endif
	    if (*bufptr >= 1 &&
		*bufptr < NUM_HANDLERS &&
		handlers[(int) *bufptr].handler != NULL) {
	    if (asock != udpSock ||
		(!drop_flag || *bufptr == SP_SEQUENCE || *bufptr == SP_SC_SEQUENCE)) {
		if (asock == udpSock)
		    packets_received++;	/* ping stuff */
#ifdef RECORDER
		if (recordGame)
		    recordPacket(bufptr, size);
#endif
#ifdef HANDLER_TIMES
		gettimeofday(&htpre,0);
#endif
		(*(handlers[(unsigned char)*bufptr].handler)) (bufptr);
#ifdef HANDLER_TIMES
		gettimeofday(&htpost,0);
		log_time(*bufptr, &htpre, &htpost);
#endif
		/* printf("handled packet %d\n", (unsigned char)*bufptr); */
	    } else
		UDPDIAG(("Ignored type %d\n", *bufptr));
	} else {
	    printf("Handler for packet %d not installed...\n", *bufptr);
	}

	bufptr += size;
	if (bufptr > buf + BUFSIZ) {
	    bcopy(buf + BUFSIZ, buf, BUFSIZ);
	    if (count == BUFSIZ * 2) {
#ifdef RECORDER
		if (playback)
		    temp = 0;
		else
#endif
		{
#ifdef DNET
		    temp = DNetServerPause(3, 0, asock);
#else
		    FD_ZERO(&readfds);
		    FD_SET(asock, &readfds);
		    /* readfds = 1<<asock; */
		    temp = select(32, &readfds, 0, 0, &timeout);
#endif				/* DNET */
		}
		if (temp != 0) {
		    temp = sock_read(asock, buf + BUFSIZ, BUFSIZ);
		    count = BUFSIZ + temp;
#ifdef DNET
		    if (temp < 0)
#else
		    if (temp <= 0)
#endif				/* DNET */
		    {
#ifndef RWATCH
			printf("3) Got read() of %d.  Server is dead\n", temp);
#endif				/* RWATCH */
			serverDead = 1;
			return (0);
		    }
		} else {
		    count = BUFSIZ;
		}
	    } else {
		count -= BUFSIZ;
	    }
	    bufptr -= BUFSIZ;
	}
    }
    return (1);
}

#define SANITY_TORPNUM(idx) \
	if ( (unsigned)(idx) >= ntorps*nplayers) { \
	    fprintf(stderr, "torp index %d out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_PNUM(idx) \
	if ( (unsigned)(idx) >= nplayers) { \
	    fprintf(stderr, "player number %d out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_PHASNUM(idx) \
	if ( (unsigned)(idx) >= nplayers*nphasers) { \
	    fprintf(stderr, "phaser number %d out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_PLASNUM(idx) \
	if ( (unsigned)(idx) >= nplayers*nplasmas) { \
	    fprintf(stderr, "plasma number %d out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_THINGYNUM(idx) \
	if ( (unsigned)(idx) >= npthingies*nplayers + ngthingies) { \
	    fprintf(stderr, "thingy index %x out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_PLANNUM(idx) \
	if ( (unsigned)(idx) >= nplanets) { \
	    fprintf(stderr, "planet index %d out of bounds\n", (idx)); \
	    return; \
	}

#define SANITY_SHIPNUM(idx) \
	if ( (unsigned)(idx) >= nshiptypes) { \
	    fprintf(stderr, "ship type %d out of bounds\n", (idx)); \
	    return; \
	}


static void
handleTorp(packet)
    struct torp_spacket *packet;
{
    struct torp *thetorp;

    SANITY_TORPNUM(ntohs(packet->tnum));

    thetorp = &torps[ntohs(packet->tnum)];
    thetorp->t_x = ntohl(packet->x);
    thetorp->t_y = ntohl(packet->y);
    thetorp->t_dir = packet->dir;

#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&thetorp->t_x, &thetorp->t_y);
	rotate_dir(&thetorp->t_dir, rotate_deg);
#endif
#ifdef BORGTEST
	if (bd)
	    bd_test_torp(ntohs(packet->tnum), thetorp);
#endif
    }
}


static void
handleTorpInfo(packet)
    struct torp_info_spacket *packet;
{
    struct torp *thetorp;

    SANITY_TORPNUM(ntohs(packet->tnum));

    thetorp = &torps[ntohs(packet->tnum)];

    if (packet->status == TEXPLODE && thetorp->t_status == TFREE) {
	/* FAT: redundant explosion; don't update p_ntorp */
	/*
	   printf("texplode ignored\n");
	*/
	return;
    }

    if (thetorp->t_status == TFREE && packet->status) {
	players[thetorp->t_owner].p_ntorp++;
	thetorp->frame = 0;
#ifdef BORGTEST
	if (bd)
	    bd_new_torp(ntohs(packet->tnum), thetorp);
#endif

    }
    if (thetorp->t_status && packet->status == TFREE) {
	players[thetorp->t_owner].p_ntorp--;
    }
    thetorp->t_war = packet->war;

    if (packet->status != thetorp->t_status) {
	/* FAT: prevent explosion reset */
	thetorp->t_status = packet->status;
	if (thetorp->t_status == TEXPLODE) {
	    thetorp->t_fuse = NUMDETFRAMES;
	}
    }
}

static void
handleStatus(packet)
    struct status_spacket *packet;
{
    status->tourn = packet->tourn;
    status->armsbomb = ntohl(packet->armsbomb);
    status->planets = ntohl(packet->planets);
    status->kills = ntohl(packet->kills);
    status->losses = ntohl(packet->losses);
    status->time = ntohl(packet->time);
    status->timeprod = ntohl(packet->timeprod);
}

static void
handleSelf(packet)
    struct you_spacket *packet;
{
    SANITY_PNUM(packet->pnum);
    me = (ghoststart ? &players[ghost_pno] : &players[packet->pnum]);
    myship = (me->p_ship);
    mystats = &(me->p_stats);
    me->p_hostile = packet->hostile;
    me->p_swar = packet->swar;
    me->p_armies = packet->armies;
    me->p_flags = ntohl(packet->flags);
    me->p_damage = ntohl(packet->damage);
    me->p_shield = ntohl(packet->shield);
    me->p_fuel = ntohl(packet->fuel);
    me->p_etemp = ntohs(packet->etemp);
    me->p_wtemp = ntohs(packet->wtemp);
    me->p_whydead = ntohs(packet->whydead);
    me->p_whodead = ntohs(packet->whodead);
    status2->clock = (unsigned long) packet->pad2;
    status2->clock += ((unsigned long) packet->pad3) << 8;
#ifdef INCLUDE_VISTRACT
    if (packet->tractor & 0x40)
	me->p_tractor = (short) packet->tractor & (~0x40);	/* ATM - visible trac
								   tors */
#ifdef nodef			/* tmp */
    else
	me->p_tractor = -1;
#endif				/* nodef */
#endif
#ifdef SOUND
    S_HandlePFlags();
#endif

}

static void
handlePlayer(packet)
    struct player_spacket *packet;
{
    register struct player *pl;
    unsigned char newdir;

    SANITY_PNUM(packet->pnum);


    pl = &players[packet->pnum];
    newdir = packet->dir;
#ifdef ROTATERACE
    if (rotate)
	rotate_dir(&newdir, rotate_deg);
#endif
#ifdef CHECK_DROPPED
    /* Kludge to fix lost uncloak packets! [3/94] -JR */
    if (pl->p_flags & PFCLOAK && pl->p_cloakphase >= (CLOAK_PHASES - 1)) {
	if (pl->p_dir != newdir) {	/* always sends the same direction
					   when cloaked! */
	    int     i, plocked = 0;

	    /*
	       nplayers is for paradise, probably want MAX_PLAYERS for other
	       clients
	    */
	    for (i = 0; i < nplayers; i++) {	/* except when someone has
						   this person phasered :( */
		if ((phasers[i].ph_status & PHHIT) && (phasers[i].ph_target == packet->pnum)) {
		    plocked = 1;
		    break;
		}
	    }
	    if (!plocked) {
		pl->p_flags &= ~(PFCLOAK);
		if (reportDroppedPackets)
		    printf("Uncloak kludge, player %d\n", pl->p_no);
	    }
	}
    }
#endif
    pl->p_dir = newdir;
    pl->p_speed = packet->speed;
    pl->p_x = ntohl(packet->x);
    pl->p_y = ntohl(packet->y);
    if (pl == me) {
	extern int my_x, my_y;	/* from shortcomm.c */
	my_x = me->p_x;
	my_y = me->p_y;
    }
    redrawPlayer[packet->pnum] = 1;

    if (me == pl) {
	extern int my_x, my_y;	/* short packets need unrotated co-ords! */
	my_x = pl->p_x;
	my_y = pl->p_y;
    }
#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&pl->p_x, &pl->p_y);
    }
#endif

}


static void
handleWarning(packet)
    struct warning_spacket *packet;
{
    warning((char *) packet->mesg);
}

static void
handleThingy(packet)
    struct thingy_spacket *packet;
{
    struct thingy *thetorp;

    SANITY_THINGYNUM(ntohs(packet->tnum));

    thetorp = &thingies[ntohs(packet->tnum)];
    thetorp->t_x = ntohl(packet->x);
    thetorp->t_y = ntohl(packet->y);
    /* printf("drone at %d, %d\n", thetorp->t_x, thetorp->t_y); */
    thetorp->t_dir = packet->dir;


#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&thetorp->t_x, &thetorp->t_y);
	rotate_dir(&thetorp->t_dir, rotate_deg);
    }
#endif

    if (thetorp->t_shape == SHP_WARP_BEACON)
	redrawall = 1;		/* shoot, route has changed */

}

static void
handleThingyInfo(packet)
    struct thingy_info_spacket *packet;
{
    struct thingy *thetorp;

    SANITY_THINGYNUM(ntohs(packet->tnum));

    thetorp = &thingies[ntohs(packet->tnum)];

#if 1
    thetorp->t_owner = ntohs(packet->owner);
#else
    /* we have the gameparam packet now */
#endif

    if (thetorp->t_shape == SHP_WARP_BEACON)
	redrawall = 1;		/* redraw the lines, I guess */

    if (ntohs(packet->shape) == SHP_BOOM && thetorp->t_shape == SHP_BLANK) {
	/* FAT: redundant explosion; don't update p_ntorp */
	/*
	   printf("texplode ignored\n");
	*/
	return;
    }

    if (thetorp->t_shape == SHP_BLANK && ntohs(packet->shape) != SHP_BLANK) {
	players[thetorp->t_owner].p_ndrone++;	/* TSH */
    }
    if (thetorp->t_shape != SHP_BLANK && ntohs(packet->shape) == SHP_BLANK) {
	players[thetorp->t_owner].p_ndrone--;	/* TSH */
    }
    thetorp->t_war = packet->war;

    if (ntohs(packet->shape) != thetorp->t_shape) {
	/* FAT: prevent explosion reset */
	thetorp->t_shape = ntohs(packet->shape);
	if (thetorp->t_shape == SHP_BOOM ||
	    thetorp->t_shape == SHP_PBOOM) {
	    thetorp->t_fuse = NUMDETFRAMES;
	}
    }
}

void
sendShortPacket(type, state)
    char    type, state;
{
    struct speed_cpacket speedReq;

    speedReq.type = type;
    speedReq.speed = state;
#ifdef UNIX_SOUND
    if (type == CP_SHIELD) play_sound (SND_SHIELD); /* Shields */
#endif
    sendServerPacket((struct player_spacket *) & speedReq);
    /* printf("Sending packet #%d\n",type); */

    /* if we're sending in UDP mode, be prepared to force it */
    if (commMode == COMM_UDP && udpClientSend >= 2) {
	switch (type) {
	case CP_SPEED:
	    fSpeed = state | 0x100;
	    break;
	case CP_DIRECTION:
	    fDirection = state | 0x100;
	    break;
	case CP_SHIELD:
	    fShield = state | 0xa00;
	    break;
	case CP_ORBIT:
	    fOrbit = state | 0xa00;
	    break;
	case CP_REPAIR:
	    fRepair = state | 0xa00;
	    break;
	case CP_CLOAK:
	    fCloak = state | 0xa00;
	    break;
	case CP_BOMB:
	    fBomb = state | 0xa00;
	    break;
	case CP_DOCKPERM:
	    fDockperm = state | 0xa00;
	    break;
	case CP_PLAYLOCK:
	    fPlayLock = state | 0xa00;
	    break;
	case CP_PLANLOCK:
	    fPlanLock = state | 0xa00;
	    break;
	case CP_BEAM:
	    if (state == 1)
		fBeamup = 1 | 0x500;
	    else
		fBeamdown = 2 | 0x500;
	    break;
	}

	/* force weapons too? */
	if (udpClientSend >= 3) {
	    switch (type) {
	    case CP_PHASER:
		fPhaser = state | 0x100;
		break;
	    case CP_PLASMA:
		fPlasma = state | 0x100;
		break;
	    }
	}
    }
}

void
sendServerPacket(packet)
/* Pick a random type for the packet */
    struct player_spacket *packet;
{
    int     size;

#ifdef RWATCH
    return;
#endif				/* RWATCH */

    if (serverDead)
	return;
    size = size_of_cpacket(packet);
    if (size < 1) {
	printf("Attempt to send strange packet %d!\n", packet->type);
	return;
    }
#ifdef SHOW_SEND
    printf("sending packet type %d, size %d\n", packet->type,
	   size);
#endif				/* SHOW_SEND */
#ifdef PACKET_LIGHTS
    light_send();
#endif				/* PACKET_LIGHTS */
#ifdef SIZE_LOGGING
    send_total += size;
#endif				/* SIZE_LOGGING */
    if (commMode == COMM_UDP) {
	/* for now, just sent everything via TCP */
    }
    if (commMode == COMM_TCP || !udpClientSend) {
	/* special case for verify packet */
	if (packet->type == CP_UDP_REQ) {
	    if (((struct udp_req_cpacket *) packet)->request == COMM_VERIFY)
		goto send_udp;
	}
	/*
	   business as usual (or player has turned off UDP transmission)
	*/
	if (gwrite(sock, (char *) packet, size) != size) {
	    printf("gwrite failed.  Server must be dead\n");
	    serverDead = 1;
	}
    } else {
	/*
	   UDP stuff
	*/
	switch (packet->type) {
	case CP_SPEED:
	case CP_DIRECTION:
	case CP_PHASER:
	case CP_PLASMA:
	case CP_TORP:
	case CP_QUIT:
	case CP_PRACTR:
	case CP_REPAIR:
	case CP_ORBIT:
	case CP_BOMB:
	case CP_BEAM:
	case CP_DET_TORPS:
	case CP_DET_MYTORP:
	case CP_TRACTOR:
	case CP_REPRESS:
	case CP_COUP:
	case CP_DOCKPERM:
	case CP_SCAN:
	case CP_PING_RESPONSE:
	case CP_CLOAK:
	case CP_SHIELD:
	case CP_PLANLOCK:
	    /*
	       these are non-critical but don't expire, send with TCP
	       [BDyess]
	    */
	    /* case CP_REFIT: */
	    /* case CP_PLAYLOCK: */
	    /* non-critical stuff, use UDP */
    send_udp:
	    packets_sent++;	/* ping stuff */

	    V_UDPDIAG(("Sent %d on UDP port\n", packet->type));
	    if (gwrite(udpSock, (char *) packet, size) != size) {
		UDPDIAG(("gwrite on UDP failed.  Closing UDP connection\n"));
		warning("UDP link severed");
		/* serverDead=1; */
		commModeReq = commMode = COMM_TCP;
		commStatus = STAT_CONNECTED;
		commSwitchTimeout = 0;
		if (udpWin) {
		    udprefresh(UDP_STATUS);
		    udprefresh(UDP_CURRENT);
		}
		if (udpSock >= 0)
		    closeUdpConn();
	    }
	    break;

	default:
	    /* critical stuff, use TCP */
	    if (gwrite(sock, (char *) packet, size) != size) {
		printf("gwrite failed.  Server must be dead\n");
		serverDead = 1;
	    }
	}
    }
}

static void
handlePlanet(packet)
    struct planet_spacket *packet;
{
    struct planet *plan;
    /* FAT: prevent excessive redraw */
    int     redraw = 0;
#ifdef HOCKEY
    int     hockey_update = 0;
#endif /*HOCKEY*/

    SANITY_PLANNUM(packet->pnum);

    plan = &planets[packet->pnum];
    if (plan->pl_owner != packet->owner) {
	redraw = 1;
#ifdef HOCKEY
	hockey_update = 1;
#endif
    }
    plan->pl_owner = packet->owner;

    if (plan->pl_owner < (1 << 0) || plan->pl_owner > (1 << (number_of_teams - 1)))
	plan->pl_owner = NOBODY;

    if (plan->pl_info != packet->info)
	redraw = 1;
    plan->pl_info = packet->info;
    /* Redraw the planet because it was updated by server */

    if (plan->pl_flags != (int) ntohs(packet->flags))
	redraw = 1;
    plan->pl_flags = (unsigned short) ntohs(packet->flags);

    if (plan->pl_armies != ntohl(packet->armies))
	redraw = 1;

    plan->pl_armies = ntohl(packet->armies);
    if (plan->pl_info == 0) {
	plan->pl_owner = NOBODY;
    }
    if (redraw) {
	plan->pl_flags |= PLREDRAW;
	pl_update[packet->pnum].plu_update = 1;	/* used to mean the planet
						   had moved, now set as a
						   sign we need to erase AND
						   redraw. -JR */
	pl_update[packet->pnum].plu_x = planets[packet->pnum].pl_x;
	pl_update[packet->pnum].plu_y = planets[packet->pnum].pl_y;
	if (infomapped && infotype == PLANETTYPE &&
	    ((struct planet *) infothing)->pl_no == packet->pnum)
	    infoupdate = 1;
#ifdef HOCKEY
        if(hockey_update && hockey) hockeyInit();
#endif /*HOCKEY*/
    }
}

static void
handlePhaser(packet)
    struct phaser_spacket *packet;
{
    struct phaser *phas;

    SANITY_PHASNUM(packet->pnum);

    phas = &phasers[packet->pnum];
#ifdef CHECK_DROPPED
    /* can't fire weapons cloaked, this guy must be uncloaked.. */
    /* applying this to torps is trickier... -JR */
    if (packet->status != PHFREE) {
	if (reportDroppedPackets && (players[packet->pnum].p_flags & PFCLOAK))
	    printf("Dropped uncloak, player %d. (fired phaser)\n", packet->pnum);
	players[packet->pnum].p_flags &= ~(PFCLOAK);
    } else {
	if (longest_ph_fuse < phas->ph_fuse)
	    longest_ph_fuse = phas->ph_fuse;
    }
#endif
    phas->ph_status = packet->status;
    phas->ph_dir = packet->dir;
    phas->ph_x = ntohl(packet->x);
    phas->ph_y = ntohl(packet->y);
    phas->ph_target = ntohl(packet->target);
    phas->ph_fuse = 0;

#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&phas->ph_x, &phas->ph_y);
	rotate_dir(&phas->ph_dir, rotate_deg);
    }
#endif
#ifdef SOUND
    if ((me->p_no == packet->pnum) && (packet->status != PHFREE)) {
	S_PlaySound(S_PHASER);
    }
#endif
#ifdef UNIX_SOUND
    if ((me->p_no == packet->pnum) && (packet->status != PHFREE)) {
        play_sound(SND_PHASER); /* Phasers */
    }
#endif
}

void
handleMessage(packet)
    struct mesg_spacket *packet;
{
    if ((int) packet->m_from >= nplayers)
	packet->m_from = 255;
    dmessage(packet->mesg, packet->m_flags, packet->m_from, packet->m_recpt);
}


static void
handleQueue(packet)
    struct queue_spacket *packet;
{
    queuePos = ntohs(packet->pos);
    /* printf("Receiving queue position %d\n",queuePos); */
}

static void
handleEmpty(ptr)
    char   *ptr;
{
    printf("Unknown packet type: %d\n", *ptr);
    return;
}

void
sendTeamReq(team, ship)
    int     team, ship;
{
    struct outfit_cpacket outfitReq;

    outfitReq.type = CP_OUTFIT;
    outfitReq.team = team;
    outfitReq.ship = ship;
    sendServerPacket((struct player_spacket *) & outfitReq);
}

static void
handlePickok(packet)
    struct pickok_spacket *packet;
{
    pickOk = packet->state;
#ifdef RECORDER
    if (playback) {		/* added when the packet is recorded. */
	extern int lastTeamReq;
	lastTeamReq = packet->pad2;
    }
#endif
}

void
sendLoginReq(name, pass, login, query)
    char   *name, *pass;
    char   *login;
    char    query;
{
    struct login_cpacket packet;

    strcpy(packet.name, name);
    strcpy(packet.password, pass);
    if (strlen(login) > 15)
	login[15] = 0;
    strcpy(packet.login, login);
    packet.type = CP_LOGIN;
    packet.query = query;
    packet.pad2 = 0x69;		/* added 1/19/93 KAO */
    packet.pad3 = 0x43;		/* added 1/19/93 KAO *//* was 0x42 3/2/93 */
    sendServerPacket((struct player_spacket *) & packet);
}

static void
handleLogin(packet)
    struct login_spacket *packet;
{


    loginAccept = packet->accept;
    if ((packet->pad2 == 69) && (packet->pad3 == 42))
	paradise = 1;
    else {
	/*nshiptypes = 8;*/
	nplayers=20;
	nplanets=40;
    }
    if (packet->accept) {

	/* we no longer accept keymaps from the server */

	mystats->st_flags = ntohl(packet->flags);
	keeppeace = (me->p_stats.st_flags / ST_KEEPPEACE) & 1;
    }
}

void
sendTractorReq(state, pnum)
    char    state;
    char    pnum;
{
    struct tractor_cpacket tractorReq;

    tractorReq.type = CP_TRACTOR;
    tractorReq.state = state;
    tractorReq.pnum = pnum;
    sendServerPacket((struct player_spacket *) & tractorReq);

    if (state)
	fTractor = pnum | 0x40;
    else
	fTractor = 0;
}

void
sendRepressReq(state, pnum)
    char    state;
    char    pnum;
{
    struct repress_cpacket repressReq;

    repressReq.type = CP_REPRESS;
    repressReq.state = state;
    repressReq.pnum = pnum;
    sendServerPacket((struct player_spacket *) & repressReq);

    if (state)
	fRepress = pnum | 0x40;
    else
	fRepress = 0;
}

void
sendDetMineReq(torp)
    short   torp;
{
    struct det_mytorp_cpacket detReq;

    detReq.type = CP_DET_MYTORP;
    detReq.tnum = htons(torp);
    sendServerPacket((struct player_spacket *) & detReq);
}

static void
handlePlasmaInfo(packet)
    struct plasma_info_spacket *packet;
{
    struct plasmatorp *thetorp;

    SANITY_PLASNUM(ntohs(packet->pnum));

    thetorp = &plasmatorps[ntohs(packet->pnum)];
    if (packet->status == PTEXPLODE && thetorp->pt_status == PTFREE) {
	/* FAT: redundant explosion; don't update p_nplasmatorp */
	return;
    }
    if (!thetorp->pt_status && packet->status) {
	players[thetorp->pt_owner].p_nplasmatorp++;
#ifdef UNIX_SOUND
    play_sound (SND_PLASMA); /* Plasma */
#endif
    }
    if (thetorp->pt_status && !packet->status) {
	players[thetorp->pt_owner].p_nplasmatorp--;
    }
    thetorp->pt_war = packet->war;
    if (thetorp->pt_status != packet->status) {
	/* FAT: prevent explosion timer from being reset */
	thetorp->pt_status = packet->status;
	if (thetorp->pt_status == PTEXPLODE) {
	    thetorp->pt_fuse = NUMDETFRAMES;
	}
    }
}

static void
handlePlasma(packet)
    struct plasma_spacket *packet;
{
    struct plasmatorp *thetorp;

    SANITY_PLASNUM(ntohs(packet->pnum));

    thetorp = &plasmatorps[ntohs(packet->pnum)];
    thetorp->pt_x = ntohl(packet->x);
    thetorp->pt_y = ntohl(packet->y);

#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&thetorp->pt_x, &thetorp->pt_y);
    }
#endif
}

static void
handleFlags(packet)
    struct flags_spacket *packet;
{
    SANITY_PNUM(packet->pnum);

    if (players[packet->pnum].p_flags != ntohl(packet->flags) ||
    players[packet->pnum].p_tractor != ((short) packet->tractor & (~0x40))) {
	/* FAT: prevent redundant player update */
	redrawPlayer[packet->pnum] = 1;
    } else
	return;

#ifdef CHECK_DROPPED
/* TEST */
/* For the dropped uncloak kludge, completely ignore uncloaks :-) */
/*    if(players[packet->pnum].p_flags & PFCLOAK) packet->flags |= htonl(PFCLOAK);*/
/* TEST */
    /* when a player cloaks, clear his phaser */
    if (ntohl(packet->flags) & PFCLOAK) {
	if (phasers[packet->pnum].ph_status != PFREE) {
	    if (reportDroppedPackets)
		printf("Lost phaser free packet, player %d. (cloaked)\n", packet->pnum);
	    phasers[packet->pnum].ph_status = PHFREE;
	    phasers[packet->pnum].ph_fuse = 0;
	}
    }
#endif

    players[packet->pnum].p_flags = ntohl(packet->flags);
#ifdef INCLUDE_VISTRACT
    if (packet->tractor & 0x40)
	players[packet->pnum].p_tractor = (short) packet->tractor & (~0x40);	/* ATM - visible
										   tractors */
    else
#endif				/* INCLUDE_VISTRACT */
	players[packet->pnum].p_tractor = -1;
}

static void
handleKills(packet)
    struct kills_spacket *packet;
{

    SANITY_PNUM(packet->pnum);

    if (players[packet->pnum].p_kills != ntohl(packet->kills) / 100.0) {
	players[packet->pnum].p_kills = ntohl(packet->kills) / 100.0;
	/* FAT: prevent redundant player update */
	updatePlayer[packet->pnum] |= ALL_UPDATE;
	if (infomapped && infotype == PLAYERTYPE &&
	    ((struct player *) infothing)->p_no == packet->pnum)
	    infoupdate = 1;
#ifdef PLPROF
	printf("Got handleKills for %d\n", packet->pnum);
#endif				/* PLPROF */
#ifdef ARMY_SLIDER
	if (me == &players[packet->pnum]) {
	    calibrate_stats();
	    redrawStats();
	}
#endif				/* ARMY_SLIDER */
    }
}

static void
handlePStatus(packet)
    struct pstatus_spacket *packet;
{
    SANITY_PNUM(packet->pnum);

    if (packet->status == PEXPLODE) {
	players[packet->pnum].p_explode = 0;
    }
    /*
       Ignore DEAD status. Instead, we treat it as PEXPLODE. This gives us
       time to animate all the frames necessary for the explosions at our own
       pace.
    */
    if (packet->status == PDEAD) {
	packet->status = PEXPLODE;
    }
    if (players[packet->pnum].p_status != packet->status) {
	players[packet->pnum].p_status = packet->status;
	redrawPlayer[packet->pnum] = 1;
#ifdef PLPROF
	printf("Got handlePStatus for %d\n", packet->pnum);
#endif				/* PLPROF */
	updatePlayer[packet->pnum] |= ALL_UPDATE;
	if (infomapped && infotype == PLAYERTYPE &&
	    ((struct player *) infothing)->p_no == packet->pnum)
	    infoupdate = 1;
#ifdef CHECK_DROPPED
	if (players[packet->pnum].p_status == POUTFIT) {
	    int     i;
	    /* clear phasers on this guy */
#if 0
	    for (i = 0; i < nplayers; i++) {
		if (phasers[i].ph_target == packet->pnum && phasers[i].ph_status == PHHIT) {
		    if (reportDroppedPackets)
			printf("Lost phaser free packet, player %d->player %d (target not alive)\n",
			       i, packet->pnum);
		    phasers[i].ph_status = PHFREE;
		}
	    }
#endif
	    if (phasers[packet->pnum].ph_status != PHFREE) {
		if (reportDroppedPackets)
		    printf("Lost phaser free packet, player %d (outfitting)\n", packet->pnum);
		phasers[packet->pnum].ph_status = PHFREE;	/* and his own */
	    }
	    if (reportDroppedPackets && players[packet->pnum].p_ntorp > 1)
		/* only report it on 2 or more left, always clear it. */
		printf("Lost torp free packet, (%d torps) player %d (outfitting)\n",
		       players[packet->pnum].p_ntorp, packet->pnum);
	    players[packet->pnum].p_ntorp = 0;
	    for (i = packet->pnum * ntorps; i < (packet->pnum + 1) * ntorps; i++) {
		torps[i].t_status = TFREE;
	    }
	}
#endif
    }
}

static void
handleMotd(packet)
    struct motd_spacket *packet;
{
    newMotdLine((char *) packet->line);
}

void
sendMessage(mes, group, indiv)
    char   *mes;
    int     group, indiv;
{
    struct mesg_cpacket mesPacket;

#ifdef SHORT_PACKETS
    if (recv_short) {
	int     size;
	size = strlen(mes);
	size += 5;		/* 1 for '\0', 4 packetheader */
	if ((size % 4) != 0)
	    size += (4 - (size % 4));
	mesPacket.pad1 = (char) size;

	/*
	   OH SHIT!!!! sizes[CP_S_MESSAGE] = size;
	*/

	mesPacket.type = CP_S_MESSAGE;
    } else
#endif
	mesPacket.type = CP_MESSAGE;
    mesPacket.group = group;
    mesPacket.indiv = indiv;
    strcpy(mesPacket.mesg, mes);
    sendServerPacket((struct player_spacket *) & mesPacket);
}

static void
handleMask(packet)
    struct mask_spacket *packet;
{
    tournMask = packet->mask;
}

void
sendOptionsPacket()
{
    struct options_cpacket optPacket;
    register int i;
    long    flags;

    optPacket.type = CP_OPTIONS;
    flags = (
	     ST_NOBITMAPS * (!sendmotdbitmaps) +
	     ST_KEEPPEACE * keeppeace +
	     0
	);
    optPacket.flags = htonl(flags);
    /* copy the keymap and make sure no ctrl chars are sent [BDyess] */
    for (i = 32; i < 128; i++) {
	optPacket.keymap[i - 32] =
	    (myship->s_keymap[i] & 128) ? i : myship->s_keymap[i];
    }
    /* bcopy(mystats->st_keymap+32, optPacket.keymap,96); */
    sendServerPacket((struct player_spacket *) & optPacket);
}

static void
pickSocket(old)
    int     old;
{
    int     newsocket;
    struct socket_cpacket sockPack;
#ifdef RWATCH
    nextSocket = old;
#else
    newsocket = (getpid() & 32767);
    if (ghoststart)
	nextSocket = old;
    while (newsocket < 2048 || newsocket == old) {
	newsocket = (newsocket + 10687) & 32767;
    }
    sockPack.type = CP_SOCKET;
    sockPack.socket = htonl(newsocket);
    sockPack.version = (char) SOCKVERSION;
    sockPack.udp_version = (char) UDPVERSION;
    sendServerPacket((struct player_spacket *) & sockPack);
    /* Did we get new socket # sent? */
    if (serverDead)
	return;
    nextSocket = newsocket;
#endif				/* RWATCH */
}

static void
handleBadVersion(packet)
    struct badversion_spacket *packet;
{
    switch (packet->why) {
    case 0:
	printf("Sorry, this is an invalid client version.\n");
	printf("You need a new version of the client code.\n");
	break;
    default:
	printf("Sorry, but you cannot play xtrek now.\n");
	printf("Try again later.\n");
	break;
    }
    EXIT(1);
}

int
gwrite(fd, buf, bytes)
    int     fd;
    char   *buf;
    register int bytes;
{
    long    orig = bytes;
    register long n;
#ifdef RECORDER
    if (playback)		/* pretend all is well */
	return (bytes);
#endif
    while (bytes) {
	n = sock_write(fd, buf, bytes);
	if (n < 0) {
	    if (fd == udpSock) {
		fprintf(stderr, "Tried to write %d, 0x%x, %d\n",
			fd, (unsigned int) buf, bytes);
		perror("write");
		printUdpInfo();
	    }
	    return (-1);
	}
	bytes -= n;
	buf += n;
    }
    return (orig);
}

int
sock_read(sock, data, size)
    int     sock, size;
    char   *data;
{
#ifdef RECORDER
    if (playback)
	return readRecorded(sock, data, size);
#endif

#ifndef AMIGA
    return read(sock, data, size);
#else
#ifdef DNET
    return DNet_Read(sock, data, size);
#else
    /* NET_SOCKETS code here */
    /* No idea if this works, old version had it: */
    return recv(sock, data, size, 0);
#endif				/* DNET  */
#endif				/* AMIGA */
}

static void
handleHostile(packet)
    struct hostile_spacket *packet;
{
    register struct player *pl;

    SANITY_PNUM(packet->pnum);

    pl = &players[packet->pnum];
    if (pl->p_swar != packet->war ||
	pl->p_hostile != packet->hostile) {
	/* FAT: prevent redundant player update & redraw */
	pl->p_swar = packet->war;
	pl->p_hostile = packet->hostile;
	/* updatePlayer[packet->pnum]=1; why? */
	redrawPlayer[packet->pnum] = 1;
    }
}

static void
handlePlyrLogin(packet)
    struct plyr_login_spacket *packet;
{
    register struct player *pl;

    SANITY_PNUM(packet->pnum);

#ifdef PLPROF
    printf("Got handlPlyrLogin for %d\n", packet->pnum);
#endif				/* PLPROF */
    updatePlayer[packet->pnum] |= ALL_UPDATE;
    pl = &players[packet->pnum];

    strcpy(pl->p_name, packet->name);
    strcpy(pl->p_monitor, packet->monitor);
    strcpy(pl->p_login, packet->login);
    pl->p_stats.st_rank = packet->rank;
    if (packet->pnum == me->p_no) {
	/* This is me.  Set some stats */
	if (lastRank == -1) {
	    if (loggedIn) {
		lastRank = packet->rank;
	    }
	} else {
	    if (lastRank != packet->rank) {
		lastRank = packet->rank;
		promoted = 1;
	    }
	}
    }
}

static void
handleStats(packet)
    struct stats_spacket *packet;
{
    register struct player *pl;

    SANITY_PNUM(packet->pnum);

#ifdef PLPROF
    printf("Got handleStats for %d\n", packet->pnum);
#endif				/* PLPROF */
    updatePlayer[packet->pnum] |= LARGE_UPDATE;
    if (infomapped && infotype == PLAYERTYPE &&
	((struct player *) infothing)->p_no == packet->pnum)
	infoupdate = 1;
    pl = &players[packet->pnum];
    pl->p_stats.st_tkills = ntohl(packet->tkills);
    pl->p_stats.st_tlosses = ntohl(packet->tlosses);
    pl->p_stats.st_kills = ntohl(packet->kills);
    pl->p_stats.st_losses = ntohl(packet->losses);
    pl->p_stats.st_tticks = ntohl(packet->tticks);
    pl->p_stats.st_tplanets = ntohl(packet->tplanets);
    pl->p_stats.st_tarmsbomb = ntohl(packet->tarmies);
    pl->p_stats.st_sbkills = ntohl(packet->sbkills);
    pl->p_stats.st_sblosses = ntohl(packet->sblosses);
    pl->p_stats.st_armsbomb = ntohl(packet->armies);
    pl->p_stats.st_planets = ntohl(packet->planets);
    pl->p_stats.st_maxkills = ntohl(packet->maxkills) / 100.0;
    pl->p_stats.st_sbmaxkills = ntohl(packet->sbmaxkills) / 100.0;
}

static void
handlePlyrInfo(packet)
    struct plyr_info_spacket *packet;
{
    register struct player *pl;
    static int lastship = -1;

    SANITY_PNUM(packet->pnum);

#ifdef PLPROF
    printf("Got PlyrInfo for %d\n", packet->pnum);
#endif				/* PLPROF */
    updatePlayer[packet->pnum] |= ALL_UPDATE;
    if (infomapped && infotype == PLAYERTYPE &&
	((struct player *) infothing)->p_no == packet->pnum)
	infoupdate = 1;
    pl = &players[packet->pnum];
    pl->p_ship = getship(packet->shiptype);
    if (packet->pnum == me->p_no && currentship != packet->shiptype) {
	currentship = packet->shiptype;
	/* sendOptionsPacket(); */
    }
    pl->p_teami = mask_to_idx(packet->team);
    pl->p_mapchars[0] = teaminfo[pl->p_teami].letter;
    /* printf("team: %d, letter: %c\n",pl->p_teami,pl->p_mapchars[0]); */
    pl->p_mapchars[1] = shipnos[pl->p_no];
    if (me == pl && lastship != currentship) {
	lastship = currentship;
	redrawTstats();
	calibrate_stats();
	redrawStats();		/* tsh */
    }
    redrawPlayer[packet->pnum] = 1;
}

void
sendUpdatePacket(speed)
    long    speed;
{
    struct updates_cpacket packet;

    packet.type = CP_UPDATES;
    timerDelay = speed;
    packet.usecs = htonl(speed);
    sendServerPacket((struct player_spacket *) & packet);
#ifdef DEBUG
    printf("sent request for an update every %d microseconds (%d/sec)\n",speed,1000000/speed);
#endif
}

static void
handlePlanetLoc(packet)
    struct planet_loc_spacket *packet;
{
    struct planet *pl;

    SANITY_PLANNUM(packet->pnum);

    pl = &planets[packet->pnum];
    pl_update[packet->pnum].plu_x = pl->pl_x;
    pl_update[packet->pnum].plu_y = pl->pl_y;

    if (pl_update[packet->pnum].plu_update != -1) {
	pl_update[packet->pnum].plu_update = 1;
	/*
	   printf("update: %s, old (%d,%d) new (%d,%d)\n", pl->pl_name,
	   pl->pl_x, pl->pl_y, ntohl(packet->x),ntohl(packet->y));
	*/
    } else {
	pl_update[packet->pnum].plu_update = 0;
	pl_update[packet->pnum].plu_x = ntohl(packet->x);
	pl_update[packet->pnum].plu_y = ntohl(packet->y);
    }
    pl->pl_x = ntohl(packet->x);
    pl->pl_y = ntohl(packet->y);
    strcpy(pl->pl_name, packet->name);
    pl->pl_namelen = strlen(packet->name);
    pl->pl_flags |= PLREDRAW;
    if (infomapped && infotype == PLANETTYPE &&
	((struct planet *) infothing)->pl_no == packet->pnum)
	infoupdate = 1;
    reinitPlanets = 1;
    if (pl->pl_x > blk_gwidth) {
	blk_gwidth = 200000;
	blk_windgwidth = ((float) WINSIDE) / blk_gwidth;
    }
#ifdef ROTATERACE
    if (rotate) {
	rotate_gcenter(&pl->pl_x, &pl->pl_y);
    }
#endif
}

#if 0

static void
handleReserved(packet)
    struct reserved_spacket *packet;
{
    struct reserved_cpacket response;

#ifndef RWATCH
    encryptReservedPacket(packet, &response, serverName, me->p_no);
    sendServerPacket((struct player_spacket *) & response);
#endif				/* RWATCH */
}

#else


static void
handleReserved(packet)
    struct reserved_spacket *packet;
{
#ifdef AUTHORIZE
    struct reserved_cpacket response;

    response.type = CP_RESERVED;
    if (RSA_Client) {		/* can use -o option for old blessing */
	warning(RSA_VERSION);
	strncpy((char *)response.resp, RSA_VERSION, RESERVED_SIZE);
	memcpy(response.data, packet->data, RESERVED_SIZE);
    } else {
#if 0
	/*
	   if we ever go back to reserved.c checking, we'll reactivate this
	   code.  However, our reserved.c module hasn't done any real
	   computation since paradise 1.
	*/
	encryptReservedPacket(packet, &response, serverName, me->p_no);
#else
	memcpy(response.data, packet->data, 16);
	memcpy(response.resp, packet->data, 16);
#endif
    }
    sendServerPacket((struct player_spacket *) & response);
#endif
}

static void
handleRSAKey(packet)
    struct rsa_key_spacket *packet;
{
#ifdef AUTHORIZE
    struct rsa_key_cpacket response;
#ifndef DNET
    struct sockaddr_in saddr;
#endif
    unsigned char *data;
    int     len;

#ifdef GATEWAY
    extern unsigned long netaddr;
    extern int serv_port;
#endif

#ifndef DNET
#ifdef GATEWAY
    /* if we didn't get it from -H, go ahead and query the socket */
    if (netaddr == 0) {
	len = sizeof(saddr);
	if (getpeername(sock, (struct sockaddr *) & saddr, &len) < 0) {
	    perror("getpeername(sock)");
	    exit(1);
	}
    } else {
	saddr.sin_addr.s_addr = netaddr;
	saddr.sin_port = htons(serv_port);
    }
#else				/* GATEWAY */
    /* query the socket to determine the remote host (ATM) */
    len = sizeof(saddr);
    if (getpeername(sock, (struct sockaddr *) & saddr, &len) < 0) {
	perror("getpeername(sock)");
	exit(1);
    }
#endif				/* GATEWAY */

    data = packet->data;
    bcopy(&saddr.sin_addr.s_addr, data, sizeof(saddr.sin_addr.s_addr));
    data += sizeof(saddr.sin_addr.s_addr);
    bcopy(&saddr.sin_port, data, sizeof(saddr.sin_port));
#else				/* DNET */
    {
	extern long netrek_server_addr;
	extern unsigned short netrek_server_port;

	data = packet->data;
	bcopy(&netrek_server_addr, data, sizeof(netrek_server_addr));
	data += sizeof(netrek_server_addr);
	bcopy(&netrek_server_port, data, sizeof(netrek_server_port));
    }
#endif				/* DNET */

#ifdef DEBUG
    {
	struct timeval start, end;
	gettimeofday(&start, NULL);
#endif
	rsa_black_box(response.resp, packet->data,
		      response.public, response.global);
#ifdef DEBUG
	gettimeofday(&end, NULL);
	printf("rsa_black_box took %d ms.\n",
	       (1000 * (end.tv_sec - start.tv_sec) +
		(end.tv_usec - start.tv_usec) / 1000));
    }
#endif
    response.type = CP_RSA_KEY;

    sendServerPacket((struct player_spacket *) & response);
#ifdef DEBUG
    printf("RSA verification requested.\n");
#endif
#endif				/* AUTHORIZE */
}

#endif

#ifdef INCLUDE_SCAN
static void
handleScan(packet)
    struct scan_spacket *packet;
{
    struct player *pp;

    SANITY_PNUM(packet->pnum);

    if (packet->success) {
	pp = &players[packet->pnum];
	pp->p_fuel = ntohl(packet->p_fuel);
	pp->p_armies = ntohl(packet->p_armies);
	pp->p_shield = ntohl(packet->p_shield);
	pp->p_damage = ntohl(packet->p_damage);
	pp->p_etemp = ntohl(packet->p_etemp);
	pp->p_wtemp = ntohl(packet->p_wtemp);
	informScan(packet->pnum);
    }
}

static void
informScan(p)
    int     p;
{
}

#endif				/* INCLUDE_SCAN */

/*
 * UDP stuff
 */
void
sendUdpReq(req)
    int     req;
{
    struct udp_req_cpacket packet;
#ifdef RWATCH
    return;
#endif				/* RWATCH */

    packet.type = CP_UDP_REQ;
    packet.request = req;

    if (req >= COMM_MODE) {
	packet.request = COMM_MODE;
	packet.connmode = req - COMM_MODE;
	sendServerPacket((struct player_spacket *) & packet);
	return;
    }
    if (req == COMM_UPDATE) {
#ifdef SHORT_PACKETS
	if (recv_short) {	/* not necessary */
/* Let the client do the work, and not the network :-) */
	    register int i;
	    for (i = 0; i < nplayers * ntorps; i++)
		torps[i].t_status = TFREE;

	    for (i = 0; i < nplayers * nplasmas; i++)
		plasmatorps[i].pt_status = PTFREE;

	    for (i = 0; i < nplayers; i++) {
		players[i].p_ntorp = 0;
		players[i].p_nplasmatorp = 0;
		phasers[i].ph_status = PHFREE;
	    }
	}
#endif
	sendServerPacket((struct player_spacket *) & packet);
	warning("Sent request for full update");
	return;
    }
    if (req == commModeReq) {
	warning("Request is in progress, do not disturb");
	return;
    }
    if (req == COMM_UDP) {
	/* open UDP port */
	if (openUdpConn() >= 0) {
	    UDPDIAG(("Bound to local port %d on fd %d\n", udpLocalPort, udpSock));
	} else {
	    UDPDIAG(("Bind to local port %d failed\n", udpLocalPort));
	    commModeReq = COMM_TCP;
	    commStatus = STAT_CONNECTED;
	    commSwitchTimeout = 0;
	    if (udpWin)
		udprefresh(UDP_STATUS);
	    warning("Unable to establish UDP connection");

	    return;
	}
    }
    /* send the request */
    packet.type = CP_UDP_REQ;
    packet.request = req;
    packet.port = htonl(udpLocalPort);
#ifdef GATEWAY
    if (!strcmp(serverName, gw_mach)) {
	packet.port = htons(gw_serv_port);	/* gw port that server should
						   call */
	UDPDIAG(("+ Telling server to contact us on %d\n", gw_serv_port));
    }
#endif
#ifdef USE_PORTSWAP
    packet.connmode = CONNMODE_PORT;	/* have him send his port */
#else
    packet.connmode = CONNMODE_PACKET;	/* we get addr from packet */
#endif
    sendServerPacket((struct player_spacket *) & packet);

    /* update internal state stuff */
    commModeReq = req;
    if (req == COMM_TCP)
	commStatus = STAT_SWITCH_TCP;
    else
	commStatus = STAT_SWITCH_UDP;
    commSwitchTimeout = 25;	/* wait 25 updates (about five seconds) */

    UDPDIAG(("Sent request for %s mode\n", (req == COMM_TCP) ?
	     "TCP" : "UDP"));

#ifndef USE_PORTSWAP
    if ((req == COMM_UDP) && recvUdpConn() < 0) {
	UDPDIAG(("Sending TCP reset message\n"));
	packet.request = COMM_TCP;
	packet.port = 0;
	commModeReq = COMM_TCP;
	sendServerPacket((struct player_spacket *) & packet);
	/* we will likely get a SWITCH_UDP_OK later; better ignore it */
	commModeReq = COMM_TCP;
	commStatus = STAT_CONNECTED;
	commSwitchTimeout = 0;
	closeUdpConn();
    }
#endif

    if (udpWin)
	udprefresh(UDP_STATUS);
}

static void
handleUdpReply(packet)
    struct udp_reply_spacket *packet;
{
    struct udp_req_cpacket response;

    UDPDIAG(("Received UDP reply %d\n", packet->reply));
    commSwitchTimeout = 0;

    response.type = CP_UDP_REQ;

    switch (packet->reply) {
    case SWITCH_TCP_OK:
	if (commMode == COMM_TCP) {
	    UDPDIAG(("Got SWITCH_TCP_OK while in TCP mode; ignoring\n"));
	} else {
	    commMode = COMM_TCP;
	    commStatus = STAT_CONNECTED;
	    warning("Switched to TCP-only connection");
	    closeUdpConn();
	    UDPDIAG(("UDP port closed\n"));
	    if (udpWin) {
		udprefresh(UDP_STATUS);
		udprefresh(UDP_CURRENT);
	    }
	}
	break;
    case SWITCH_UDP_OK:
	if (commMode == COMM_UDP) {
	    UDPDIAG(("Got SWITCH_UDP_OK while in UDP mode; ignoring\n"));
	} else {
	    /* the server is forcing UDP down our throat? */
	    if (commModeReq != COMM_UDP) {
		UDPDIAG(("Got unsolicited SWITCH_UDP_OK; ignoring\n"));
	    } else {
#ifdef USE_PORTSWAP
		udpServerPort = ntohl(packet->port);
#ifdef nodef			/* simulate calvin error */
		/* XXX TMP */
		udpServerPort = 3333;
#endif
		if (connUdpConn() < 0) {
		    UDPDIAG(("Unable to connect, resetting\n"));
		    warning("Connection attempt failed");
		    commModeReq = COMM_TCP;
		    commStatus = STAT_CONNECTED;
		    if (udpSock >= 0)
			closeUdpConn();
		    if (udpWin) {
			udprefresh(UDP_STATUS);
			udprefresh(UDP_CURRENT);
		    }
		    response.request = COMM_TCP;
		    response.port = 0;
		    goto send;
		}
#else
		/* this came down UDP, so we MUST be connected */
		/* (do the verify thing anyway just for kicks) */
#endif
		UDPDIAG(("Connected to server's UDP port\n"));
		commStatus = STAT_VERIFY_UDP;
		if (udpWin)
		    udprefresh(UDP_STATUS);
		response.request = COMM_VERIFY;	/* send verify request on UDP */
		response.port = 0;
		commSwitchTimeout = 25;	/* wait 25 updates */
#ifdef USE_PORTSWAP
	send:
#endif				/* USE_PORTSWAP */
		sendServerPacket((struct player_spacket *) & response);
	    }
	}
	break;
    case SWITCH_DENIED:
	if (ntohs(packet->port)) {
	    UDPDIAG(("Switch to UDP failed (different version)\n"));
	    warning("UDP protocol request failed (bad version)");
	} else {
	    UDPDIAG(("Switch to UDP denied\n"));
	    warning("UDP protocol request denied");
	}
	commModeReq = commMode;
	commStatus = STAT_CONNECTED;
	commSwitchTimeout = 0;
	if (udpWin)
	    udprefresh(UDP_STATUS);
	if (udpSock >= 0)
	    closeUdpConn();
	break;
    case SWITCH_VERIFY:
	UDPDIAG(("Received UDP verification\n"));
	break;
    default:
	fprintf(stderr, "netrek: Got funny reply (%d) in UDP_REPLY packet\n",
		packet->reply);
	break;
    }
}

#define MAX_PORT_RETRY  10
static int
openUdpConn()
{
#ifndef DNET
    struct sockaddr_in addr;
    struct hostent *hp;
    int     attempts;

#ifdef RECORDER
    if (playback)
	return 0;
#endif
    if (udpSock >= 0) {
	fprintf(stderr, "netrek: tried to open udpSock twice\n");
	return (0);		/* pretend we succeeded (this could be bad) */
    }
    if ((udpSock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror("netrek: unable to create DGRAM socket");
	return (-1);
    }
#ifdef nodef
    set_udp_opts(udpSock);
#endif				/* nodef */

    if(UdpLocalPort == 0) {
	UdpLocalPort = intDefault("baseUdpLocalPort", 0);
	UDPDIAG(("using base port %d\n", UdpLocalPort));
    }

    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_family = AF_INET;

    errno = 0;
    udpLocalPort = (getpid() & 32767) + (random() % 256);
    for (attempts = 0; attempts < MAX_PORT_RETRY; attempts++) {
	if(UdpLocalPort){
	    /* force UDP to be starting at a base address */
	    udpLocalPort = UdpLocalPort + attempts;
	} else while (udpLocalPort < 2048) {
	    udpLocalPort = (udpLocalPort + 10687) & 32767;
	}
#ifdef GATEWAY
	/* we need the gateway to know where to find us */
	if (!strcmp(serverName, gw_mach)) {
	    UDPDIAG(("+ gateway test: binding to %d\n", gw_local_port));
	    udpLocalPort = gw_local_port;
	}
#endif
	addr.sin_port = htons(udpLocalPort);
	if (bind(udpSock, (struct sockaddr *) & addr, sizeof(addr)) >= 0)
	    break;
    }
    if (attempts == MAX_PORT_RETRY) {
	perror("netrek: bind");
	UDPDIAG(("Unable to find a local port to bind to\n"));
	close(udpSock);
	udpSock = -1;
	return (-1);
    }
    UDPDIAG(("Local port is %d\n", udpLocalPort));
    if(UdpLocalPort) {
	fprintf(stdout, "Local UDP port is %d\n", udpLocalPort);
    }

    /* determine the address of the server */
    if (!serveraddr) {
	if ((addr.sin_addr.s_addr = inet_addr(serverName)) == -1) {
	    if ((hp = gethostbyname(serverName)) == NULL) {
		printf("Who is %s?\n", serverName);
#ifdef AUTOKEY
		if (autoKey)
		    W_AutoRepeatOn();
#endif

		EXIT(0);
	    } else {
		addr.sin_addr.s_addr = *(long *) hp->h_addr;
	    }
	}
	serveraddr = addr.sin_addr.s_addr;
	UDPDIAG(("Found serveraddr == 0x%x\n", (unsigned int) serveraddr));
    }
    return (0);
#else				/* DNET */
#ifdef RECORDER
    if (playback)
	return 0;
#endif
    return DNetOpenUDPConn(serverName);
#endif				/* DNET */
}

#ifdef USE_PORTSWAP
static int
connUdpConn()
{
#ifndef DNET
    struct sockaddr_in addr;
    int     len;

#ifdef RECORDER
    if (playback)
	return 0;
#endif
    addr.sin_addr.s_addr = serveraddr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(udpServerPort);

    UDPDIAG(("Connecting to host 0x%x on port %d\n", serveraddr, udpServerPort));
    if (connect(udpSock, &addr, sizeof(addr)) < 0) {
	fprintf(stderr, "Error %d: ");
	perror("netrek: unable to connect UDP socket");
	printUdpInfo();		/* debug */
	return (-1);
    }
#ifdef nodef
    len = sizeof(addr);
    if (getsockname(udpSock, &addr, &len) < 0) {
	perror("netrek: unable to getsockname(UDP)");
	UDPDIAG(("Can't get our own socket; connection failed\n"));
	close(udpSock);
	udpSock = -1;
	return -1;
    }
    printf("udpLocalPort %d, getsockname port %d\n",
	   udpLocalPort, addr.sin_port);
#endif

    return (0);
#endif				/* DNET */
}
#endif

#ifndef USE_PORTSWAP
static int
recvUdpConn()
{
#ifndef DNET
    fd_set  readfds;
    struct timeval to;
    struct sockaddr_in from;
    int     fromlen, res;

#ifdef RECORDER
    if (playback)
	return 0;
#endif
    bzero(&from, sizeof(from));	/* don't get garbage if really broken */

    /* we patiently wait until the server sends a packet to us */
    /* (note that we silently eat the first one) */
    UDPDIAG(("Issuing recvfrom() call\n"));
    printUdpInfo();
    fromlen = sizeof(from);
    FD_ZERO(&readfds);
    FD_SET(udpSock, &readfds);
    to.tv_sec = 6;		/* wait 3 seconds, then abort */
    to.tv_usec = 0;
    if ((res = select(32, &readfds, 0, 0, &to)) <= 0) {
	if (!res) {
	    UDPDIAG(("timed out waiting for response"));
	    warning("UDP connection request timed out");
	    return (-1);
	} else {
	    perror("select() before recvfrom()");
	    return (-1);
	}
    }
    if (recvfrom(udpSock, buf, BUFSIZ, 0, (struct sockaddr *) & from, &fromlen) < 0) {
	perror("recvfrom");
	UDPDIAG(("recvfrom failed, aborting UDP attempt"));
	return (-1);
    }
    if (from.sin_addr.s_addr != serveraddr) {
	/* safe? */
	serveraddr = from.sin_addr.s_addr;
	UDPDIAG(("Warning: from 0x%x, but server is 0x%x\n",
	   (unsigned int) from.sin_addr.s_addr, (unsigned int) serveraddr));
    }
    if (from.sin_family != AF_INET) {
	UDPDIAG(("Warning: not AF_INET (%d)\n", from.sin_family));
    }
    udpServerPort = ntohs(from.sin_port);
    UDPDIAG(("recvfrom() succeeded; will use server port %d\n", udpServerPort));
#ifdef GATEWAY
    if (!strcmp(serverName, gw_mach)) {
	UDPDIAG(("+ actually, I'm going to use %d\n", gw_port));
	udpServerPort = gw_port;
	from.sin_port = htons(udpServerPort);
    }
#endif

    if (connect(udpSock, (struct sockaddr *) & from, sizeof(from)) < 0) {
	perror("netrek: unable to connect UDP socket after recvfrom()");
	close(udpSock);
	udpSock = -1;
	return (-1);
    }
    return (0);
#else				/* DNET */
#ifdef RECORDER
    if (playback)
	return 0;
#endif
    return DNetRecvUDPConn();
#endif				/* DNET */
}
#endif

int
closeUdpConn()
{
    V_UDPDIAG(("Closing UDP socket\n"));
#ifdef RECORDER
    if (playback)
	return 0;
#endif

#ifdef DNET
    DNetCloseUDP();
    return 0;
#else
    if (udpSock < 0) {
	fprintf(stderr, "netrek: tried to close a closed UDP socket\n");
	return (-1);
    }
    shutdown(udpSock, 2);
    close(udpSock);
    udpSock = -1;
    return 0;
#endif				/* DNET */
}

static void
printUdpInfo()
{
#ifndef DNET
    struct sockaddr_in addr;
    int     len;

    len = sizeof(addr);
    if (getsockname(udpSock, (struct sockaddr *) & addr, &len) < 0) {
/*      perror("printUdpInfo: getsockname");*/
	return;
    }
    UDPDIAG(("LOCAL: addr=0x%x, family=%d, port=%d\n",
	     (unsigned int) addr.sin_addr.s_addr,
	     addr.sin_family, ntohs(addr.sin_port)));

    if (getpeername(udpSock, (struct sockaddr *) & addr, &len) < 0) {
/*      perror("printUdpInfo: getpeername");*/
	return;
    }
    UDPDIAG(("PEER : addr=0x%x, family=%d, port=%d\n",
	     (unsigned int) addr.sin_addr.s_addr,
	     addr.sin_family, ntohs(addr.sin_port)));
#endif
}

static void
handleSequence(packet)
    struct sequence_spacket *packet;
{
    static int recent_count = 0, recent_dropped = 0;
    long    newseq;

    drop_flag = 0;
    if (chan != udpSock)
	return;			/* don't pay attention to TCP sequence #s */
    udpTotal++;
    recent_count++;

    /* update percent display every 256 updates (~50 seconds usually) */
    if (!(udpTotal & 0xff))
	if (udpWin)
	    udprefresh(UDP_DROPPED);

    newseq = (long) ntohs(packet->sequence);
/*    printf("read %d - ", newseq);*/

    if (((unsigned short) sequence) > 65000 &&
	((unsigned short) newseq) < 1000) {
	/* we rolled, set newseq = 65536+sequence and accept it */
	sequence = ((sequence + 65536) & 0xffff0000) | newseq;
    } else {
	/* adjust newseq and do compare */
	newseq |= (sequence & 0xffff0000);

	if (!udpSequenceChk) {	/* put this here so that turning seq check */
	    sequence = newseq;	/* on and off doesn't make us think we lost */
	    return;		/* a whole bunch of packets. */
	}
	if (newseq > sequence) {
	    /* accept */
	    if (newseq != sequence + 1) {
		udpDropped += (newseq - sequence) - 1;
		udpTotal += (newseq - sequence) - 1;	/* want TOTAL packets */
		recent_dropped += (newseq - sequence) - 1;
		recent_count += (newseq - sequence) - 1;
		if (udpWin)
		    udprefresh(UDP_DROPPED);
		UDPDIAG(("sequence=%ld, newseq=%ld, we lost some\n",
			 sequence, newseq));
	    }
	    sequence = newseq;
	} else {
	    /* reject */
	    if (packet->type == SP_SC_SEQUENCE) {
		V_UDPDIAG(("(ignoring repeat %ld)\n", newseq));
	    } else {
		UDPDIAG(("sequence=%ld, newseq=%ld, ignoring transmission\n",
			 sequence, newseq));
	    }
	    /*
	       the remaining packets will be dropped and we shouldn't count
	       the SP_SEQUENCE packet either
	    */
	    packets_received--;
	    drop_flag = 1;
	}
    }
/*    printf("newseq %d, sequence %d\n", newseq, sequence);*/
    if (recent_count > UDP_RECENT_INTR) {
	/* once a minute (at 5 upd/sec), report on how many were dropped */
	/* during the last UDP_RECENT_INTR updates                       */
	udpRecentDropped = recent_dropped;
	recent_count = recent_dropped = 0;
	if (udpWin)
	    udprefresh(UDP_DROPPED);
    }
}


/*
static void dumpShip(shipp)
struct ship *shipp;
{
  printf("ship stats:\n");
  printf("phaser range = %d\n", shipp->s_phaserrange);
  printf("max speed = %d\n", shipp->s_maxspeed);
  printf("max shield = %d\n", shipp->s_maxshield);
  printf("max damage = %d\n", shipp->s_maxdamage);
  printf("max egntemp = %d\n", shipp->s_maxegntemp);
  printf("max wpntemp = %d\n", shipp->s_maxwpntemp);
  printf("max armies = %d\n", shipp->s_maxarmies);
  printf("type = %d\n", shipp->s_type);
  printf("torp speed = %d\n", shipp->s_torpspeed);
  printf("letter = %c\n", shipp->s_letter);
  printf("desig = %2.2s\n", shipp->s_desig);
  printf("bitmap = %d\n\n", shipp->s_bitmap);
}
*/

static void
handleShipCap(packet)		/* SP_SHIP_CAP */
    struct ship_cap_spacket *packet;
{
    struct shiplist *temp;

    /*
       What are we supposed to do?
    */

    SANITY_SHIPNUM(ntohs(packet->s_type));

    if (packet->operation) {	/* remove ship from list */
	temp = shiptypes;
	if (temp->ship->s_type == (int) ntohs(packet->s_type)) {
	    shiptypes = temp->next;
	    shiptypes->prev = NULL;
	}
	while (temp->next != NULL) {
	    if (temp->next->ship->s_type == (int) ntohs(packet->s_type)) {
		temp = temp->next;
		temp->prev->next = temp->next;
		if (temp->next)
		    temp->next->prev = temp->prev;
		free(temp->ship);
		free(temp);
		return;
	    } else {
		temp = temp->next;
	    }
	}
    }
    /*
       Since we're adding the ship, we need to find out if we already have
       that ship, and if so, replace it.
    */

    temp = shiptypes;
    while (temp != NULL) {
	if (temp->ship->s_type == (int) ntohs(packet->s_type)) {
	    temp->ship->s_type = ntohs(packet->s_type);
	    temp->ship->s_torpspeed = ntohs(packet->s_torpspeed);
	    temp->ship->s_phaserrange = ntohs(packet->s_phaserrange);
	    if (temp->ship->s_phaserrange < 200)	/* backward
							   compatibility */
		temp->ship->s_phaserrange *= PHASEDIST / 100;
	    temp->ship->s_maxspeed = ntohl(packet->s_maxspeed);
	    temp->ship->s_maxfuel = ntohl(packet->s_maxfuel);
	    temp->ship->s_maxshield = ntohl(packet->s_maxshield);
	    temp->ship->s_maxdamage = ntohl(packet->s_maxdamage);
	    temp->ship->s_maxwpntemp = ntohl(packet->s_maxwpntemp);
	    temp->ship->s_maxegntemp = ntohl(packet->s_maxegntemp);
	    temp->ship->s_maxarmies = ntohs(packet->s_maxarmies);
	    temp->ship->s_letter = packet->s_letter;
	    temp->ship->s_desig[0] = packet->s_desig1;
	    temp->ship->s_desig[1] = packet->s_desig2;
	    temp->ship->s_bitmap = ntohs(packet->s_bitmap);
	    buildShipKeymap(temp->ship);
/*dumpShip(temp->ship);*/
	    return;
	}
	temp = temp->next;
    }

    /*
       Not there, so we need to make a new entry in the list for it.
    */
    temp = (struct shiplist *) malloc(sizeof(struct shiplist));
    temp->next = shiptypes;
    temp->prev = NULL;
    if (shiptypes)
	shiptypes->prev = temp;
    shiptypes = temp;
    temp->ship = (struct ship *) malloc(sizeof(struct ship));
    temp->ship->s_type = ntohs(packet->s_type);
    temp->ship->s_torpspeed = ntohs(packet->s_torpspeed);
    temp->ship->s_phaserrange = ntohs(packet->s_phaserrange);
    temp->ship->s_maxspeed = ntohl(packet->s_maxspeed);
    temp->ship->s_maxfuel = ntohl(packet->s_maxfuel);
    temp->ship->s_maxshield = ntohl(packet->s_maxshield);
    temp->ship->s_maxdamage = ntohl(packet->s_maxdamage);
    temp->ship->s_maxwpntemp = ntohl(packet->s_maxwpntemp);
    temp->ship->s_maxegntemp = ntohl(packet->s_maxegntemp);
    temp->ship->s_maxarmies = ntohs(packet->s_maxarmies);
    temp->ship->s_letter = packet->s_letter;
    temp->ship->s_desig[0] = packet->s_desig1;
    temp->ship->s_desig[1] = packet->s_desig2;
    temp->ship->s_bitmap = ntohs(packet->s_bitmap);
    buildShipKeymap(temp->ship);
/*  dumpShip(temp->ship);*/
}

static void
handleMotdPic(packet)		/* SP_SHIP_CAP */
    struct motd_pic_spacket *packet;
{
    int     x, y, page, width, height;

    x = ntohs(packet->x);
    y = ntohs(packet->y);
    width = ntohs(packet->width);
    height = ntohs(packet->height);
    page = ntohs(packet->page);

    newMotdPic(x, y, width, height, (char *) packet->bits, page);
}

static void
handleStats2(packet)
    struct stats_spacket2 *packet;
{
    struct stats2 *p;		/* to hold packet's player's stats2 struct */

    SANITY_PNUM(packet->pnum);

    updatePlayer[packet->pnum] |= LARGE_UPDATE;
    if (infomapped && infotype == PLAYERTYPE &&
	((struct player *) infothing)->p_no == packet->pnum)
	infoupdate = 1;
    p = &(players[packet->pnum].p_stats2);	/* get player's stats2 struct */
    p->st_genocides = ntohl(packet->genocides);
    p->st_tmaxkills = (float) ntohl(packet->maxkills) / 100.0;
    p->st_di = (float) ntohl(packet->di) / 100.0;
    p->st_tkills = (int) ntohl(packet->kills);
    p->st_tlosses = (int) ntohl(packet->losses);
    p->st_tarmsbomb = (int) ntohl(packet->armsbomb);
    p->st_tresbomb = (int) ntohl(packet->resbomb);
    p->st_tdooshes = (int) ntohl(packet->dooshes);
    p->st_tplanets = (int) ntohl(packet->planets);
    p->st_tticks = (int) ntohl(packet->tticks);
    p->st_sbkills = (int) ntohl(packet->sbkills);
    p->st_sblosses = (int) ntohl(packet->sblosses);
    p->st_sbticks = (int) ntohl(packet->sbticks);
    p->st_sbmaxkills = (float) ntohl(packet->sbmaxkills) / 100.0;
    p->st_wbkills = (int) ntohl(packet->wbkills);
    p->st_wblosses = (int) ntohl(packet->wblosses);
    p->st_wbticks = (int) ntohl(packet->wbticks);
    p->st_wbmaxkills = (float) ntohl(packet->wbmaxkills) / 100.0;
    p->st_jsplanets = (int) ntohl(packet->jsplanets);
    p->st_jsticks = (int) ntohl(packet->jsticks);
    if (p->st_rank != (int) ntohl(packet->rank) ||
	p->st_royal != (int) ntohl(packet->royal)) {
	p->st_rank = (int) ntohl(packet->rank);
	p->st_royal = (int) ntohl(packet->royal);
	updatePlayer[packet->pnum] |= ALL_UPDATE;
    }
}

static void
handleStatus2(packet)
    struct status_spacket2 *packet;
{
    updatePlayer[me->p_no] |= LARGE_UPDATE;
    if (infomapped && infotype == PLAYERTYPE &&
	((struct player *) infothing)->p_no == me->p_no)
	infoupdate = 1;
    status2->tourn = packet->tourn;
    status2->dooshes = ntohl(packet->dooshes);
    status2->armsbomb = ntohl(packet->armsbomb);
    status2->resbomb = ntohl(packet->resbomb);
    status2->planets = ntohl(packet->planets);
    status2->kills = ntohl(packet->kills);
    status2->losses = ntohl(packet->losses);
    status2->sbkills = ntohl(packet->sbkills);
    status2->sblosses = ntohl(packet->sblosses);
    status2->sbtime = ntohl(packet->sbtime);
    status2->wbkills = ntohl(packet->wbkills);
    status2->wblosses = ntohl(packet->wblosses);
    status2->wbtime = ntohl(packet->wbtime);
    status2->jsplanets = ntohl(packet->jsplanets);
    status2->jstime = ntohl(packet->jstime);
    status2->time = ntohl(packet->time);
    status2->timeprod = ntohl(packet->timeprod);
}

static void
handlePlanet2(packet)
    struct planet_spacket2 *packet;
{
    SANITY_PLANNUM(packet->pnum);

    planets[packet->pnum].pl_owner = packet->owner;
    planets[packet->pnum].pl_info = packet->info;
    planets[packet->pnum].pl_flags = ntohl(packet->flags);
    planets[packet->pnum].pl_timestamp = ntohl(packet->timestamp);
    planets[packet->pnum].pl_armies = ntohl(packet->armies);
    planets[packet->pnum].pl_flags |= PLREDRAW;
    pl_update[packet->pnum].plu_update = 1;
    pl_update[packet->pnum].plu_x = planets[packet->pnum].pl_x;
    pl_update[packet->pnum].plu_y = planets[packet->pnum].pl_y;
    if (infomapped && infotype == PLANETTYPE &&
	((struct planet *) infothing)->pl_no == packet->pnum)
	infoupdate = 1;
}

static void 
handleTerrainInfo2(pkt)
  struct terrain_info_packet2 *pkt;
{
#ifdef ZDIAG2
    fprintf( stderr, "Receiving terrain info packet\n" );
    fprintf( stderr, "Terrain dims: %d x %d\n", ntohs(pkt->xdim), ntohs(pkt->ydim) );
#endif
    received_terrain_info = TERRAIN_STARTED;
    terrain_x = ntohs(pkt->xdim);
    terrain_y = ntohs(pkt->ydim);
} 

static void
handleTerrain2(pkt)
    struct terrain_packet2 *pkt;
{
    static int curseq = 0, totbytes = 0, done = 0;
    int i, status;
    unsigned long dlen;
#ifdef ZDIAG2
    static unsigned char sum = 0;
    static unsigned numnz = 0;
#endif
    static unsigned char *gzipTerrain = NULL, *orgTerrain = NULL;
    
#ifdef ZDIAG2
    fprintf( stderr, "Receiving Terrain packet.  This should be %d.\n", curseq+1 );
#endif

    if( (done == TERRAIN_DONE) && (received_terrain_info == TERRAIN_STARTED ) ){
      /* receiving new terrain info */
      free( gzipTerrain );
      free( orgTerrain );
      free( terrainInfo );
      gzipTerrain = orgTerrain = NULL;
      terrainInfo = NULL;
      curseq = done = totbytes = 0;
    }
      
    curseq++;
    if( (curseq != pkt->sequence) || !(received_terrain_info) ){
      /* Should fill in a list of all packets missed */
      /* or request header packet from server */
      fprintf( stderr, "Blech!  Received terrain packet before terrain_info\n" );
      return;
    }
#ifdef ZDIAG2
    fprintf( stderr, "Receiving packet %d out of %d\n", curseq, pkt->total_pkts );
#endif
    if( !gzipTerrain ){
      gzipTerrain = (unsigned char *)malloc( pkt->total_pkts << 7 );
#if defined(ZDIAG) || defined(ZDIAG2)
      fprintf( stderr, "Allocating %d bytes for gzipTerrain.\n", pkt->total_pkts << 7 );
#endif
		/* another yukko constant */
    }
    if( !orgTerrain ){
      orgTerrain = (unsigned char *)malloc( terrain_x*terrain_y );
      dlen = terrain_x * terrain_y;
#if defined(ZDIAG) || defined(ZDIAG2)
      fprintf( stderr, "Allocating %d bytes for orgTerrain.\n", dlen );
#endif
    }
    for( i = 0; i < pkt->length; i++ ){
#ifdef ZDIAG2
      if( !(i%10) ){
        fprintf( stderr, "Params: %d, %d\n", ((curseq-1)<<7)+i, i );
      }
#endif
      gzipTerrain[((curseq-1)<<7)+i] = pkt->terrain_type[i];
    }
    totbytes += pkt->length;
    if( curseq == pkt->total_pkts ){
#if defined(ZDIAG) || defined(ZDIAG2)
      status = uncompress( orgTerrain, &dlen, gzipTerrain, totbytes );
      if( status != Z_OK ){
        if( status == Z_BUF_ERROR ){
          fprintf( stderr, "Unable to uncompress -- Z_BUF_ERROR.\n" );
        }
        if( status == Z_MEM_ERROR ){
          fprintf( stderr, "Unable to uncompress -- Z_MEM_ERROR.\n" );
        }
        if( status = Z_DATA_ERROR ){
          fprintf( stderr, "Unable to uncompress -- Z_DATA_ERROR!\n" );
        }
      }
      else{
        fprintf( stderr, "Total zipped terrain received: %d bytes\n", totbytes );
      }
#else
      uncompress( orgTerrain, &dlen, gzipTerrain, totbytes );
#endif
      terrainInfo = (struct t_unit *)malloc( dlen * sizeof( struct t_unit ) );
      for( i = 0; i < dlen; i++ ){
        terrainInfo[i].types = orgTerrain[i];
#ifdef ZDIAG2
	sum |= orgTerrain[i];
        if( orgTerrain[i] != 0 ){
          numnz++;
        }
#endif
      }
      done = received_terrain_info = TERRAIN_DONE;
#ifdef ZDIAG2
      fprintf( stderr, "Sum = %d, numnz = %d\n", sum, numnz );
#endif
    }
}    

static void
handleTempPack(packet)		/* SP_SHIP_CAP */
    struct obvious_packet *packet;
{
    struct obvious_packet reply;
    /* printf("New MOTD info available\n"); */
    erase_motd();
    reply.type = CP_ASK_MOTD;
    sendServerPacket((struct player_spacket *) & reply);
}

/* handlers for the extension1 packet */

int
compute_extension1_size(pkt)
    char   *pkt;
{
    if (pkt[0] != SP_PARADISE_EXT1)
	return -1;

    switch (pkt[1]) {
    case SP_PE1_MISSING_BITMAP:
	return sizeof(struct pe1_missing_bitmap_spacket);
    case SP_PE1_NUM_MISSILES:
	return sizeof(struct pe1_num_missiles_spacket);
    default:
	return -1;
    }
}

static void
handleExtension1(packet)
    struct paradiseext1_spacket *packet;
{
    switch (packet->subtype) {
    case SP_PE1_MISSING_BITMAP:
	{
	    struct pe1_missing_bitmap_spacket *pkt =
	    (struct pe1_missing_bitmap_spacket *) packet;

	    newMotdPic(ntohs(pkt->x),
		       ntohs(pkt->y),
		       ntohs(pkt->width),
		       ntohs(pkt->height),
		       0,
		       ntohs(pkt->page));
	}
	break;
    case SP_PE1_NUM_MISSILES:
	me->p_totmissiles = ntohs(((struct pe1_num_missiles_spacket *) packet)->num);
	/* printf("updated totmissiles to %d\n",me->p_totmissiles); */
	if (me->p_totmissiles < 0)
	    me->p_totmissiles = 0;	/* SB/WB have -1 */
	break;
    default:
	printf("unknown paradise extension packet 1 subtype = %d\n",
	       packet->subtype);
    }
}