/* $Log:	socket.c,v $
 * Revision 1.8  92/12/10  18:10:20  Nick Trown
 *
 * Added Clue player mods.
 *
 * Revision 1.7  92/04/08  10:08:34  terence
 *
 * Added UDP v1.0 mods.
 * 
 * Revision 1.6  92/04/02  15:44:17  terence
 * fixed 't-mode' stuck on bug
 * 
 * Revision 1.5  92/04/01  16:53:53  terence
 * removed #ifdefs corresponding to INCLUDE_SCAN and INCLUDE_VISTRACT
 * 
 * Revision 1.4  92/04/01  15:35:44  terence
 * UDP v0.3 mods added
 * 
 * Revision 1.3  92/03/19  21:34:01  terence
 * Added UDP mods (version 0.2, 3/17/92)
 * 
 * Revision 1.2  92/03/11  16:32:49  terence
 * added UDP mods (huge)
 *
 *  */

/*
 * Socket.c 
 *
 * Kevin P. Smith  1/26/89
 * UDP stuff v1.0 by Andy McFadden  Feb-Apr 1992
 *
 * UDP protocol v1.0
 *
 * Provides all of the support for sending packets to the client.
 */
#include "copyright2.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <math.h>
#include <errno.h>
#include <signal.h>

#include "defs.h"
#include INC_UNISTD
#include INC_STRINGS
#include INC_SYS_SELECT
#include "struct.h"
#include "data.h"
#include "packets.h"

#ifdef OOPS
#ifdef MIPSEL
#ifndef htonl

#define htonl(x) ((U_LONG) (((U_LONG) x << 24) | (((U_LONG) x & 0xff00) << 8) | \
		  (((U_LONG) x & 0xff0000) >> 8) | ((U_LONG) x >> 24)))
#define ntohl(x) htonl(x)
#define htons(x) ((u_short) (((u_short) x << 8) | ((u_short) x >> 8)))
#define ntohs(x) htons(x)

#endif htonl
#endif MIPSEL
#endif OOPS

#define FALSE 0
#define TRUE 1

/*#define DOUBLE_UDP		/* comment this out to disable it */

/*#define BROKEN	/* for local stress testing; drops 20% of packets */
/*#define HOSED		/* combine with BROKEN; drops 90% of packets */
/*#define FATDIAG	/* define to get LOTS of fat UDP debugging messages */

int handleTorpReq(), handlePhasReq(), handleSpeedReq();
int handleDirReq(), handleShieldReq(), handleRepairReq(), handleOrbitReq();
int handlePractrReq(), handleBombReq(), handleBeamReq(), handleCloakReq();
int handleDetTReq(), handleCopilotReq();
int handleOutfit(), handleLoginReq();
int handlePlasmaReq(), handleWarReq(), handlePlanlockReq();
int handlePlaylockReq(), handleDetMReq();
int handleTractorReq(), handleRepressReq();
int handleCoupReq(), handleRefitReq(), handleMessageReq();
int handleQuitReq(), handleOptionsPacket();
int handleSocketReq(), handleByeReq();
int handleDockingReq(), handleReset();
int handleUpdatesReq(), handleReserved();
int handleScan();
int handleUdpReq(), handleSequence();
#ifdef SB_TRANSWARP
int handleTranswarp();
#endif
#ifdef BASEPRACTICE
int handleOggV();
#endif
#ifdef FEATURE_PACKETS
int handleFeature();
#endif

extern int ignored[];

#ifdef RSA
int handleRSAKey();
#endif

#ifdef PING
int     handlePingResponse();
#endif
#ifdef SHORT_PACKETS
int     handleShortReq(), handleThresh(),handleSMessageReq()  ;
#endif

int clientVersion();

static int remoteaddr= -1;		/* inet address in net format */

struct packet_handler handlers[] = {
    { 0, NULL},		/* record 0 */
    { sizeof(struct mesg_cpacket), handleMessageReq },	   /* CP_MESSAGE */
    { sizeof(struct speed_cpacket), handleSpeedReq },	   /* CP_SPEED */
    { sizeof(struct dir_cpacket), handleDirReq },	   /* CP_DIRECTION */
    { sizeof(struct phaser_cpacket), handlePhasReq },	   /* CP_PHASER */
    { sizeof(struct plasma_cpacket), handlePlasmaReq },	   /* CP_PLASMA */
    { sizeof(struct torp_cpacket), handleTorpReq },	   /* CP_TORP */
    { sizeof(struct quit_cpacket), handleQuitReq },	   /* CP_QUIT */
    { sizeof(struct login_cpacket), handleLoginReq },	   /* CP_LOGIN */
    { sizeof(struct outfit_cpacket), handleOutfit },	   /* CP_OUTFIT */
    { sizeof(struct war_cpacket), handleWarReq },	   /* CP_WAR */
    { sizeof(struct practr_cpacket), handlePractrReq },	   /* CP_PRACTR */
    { sizeof(struct shield_cpacket), handleShieldReq },	   /* CP_SHIELD */
    { sizeof(struct repair_cpacket), handleRepairReq },	   /* CP_REPAIR */
    { sizeof(struct orbit_cpacket), handleOrbitReq },	   /* CP_ORBIT */
    { sizeof(struct planlock_cpacket), handlePlanlockReq },/* CP_PLANLOCK */
    { sizeof(struct playlock_cpacket), handlePlaylockReq },/* CP_PLAYLOCK */
    { sizeof(struct bomb_cpacket), handleBombReq },	   /* CP_BOMB */
    { sizeof(struct beam_cpacket), handleBeamReq },	   /* CP_BEAM */
    { sizeof(struct cloak_cpacket), handleCloakReq },	   /* CP_CLOAK */
    { sizeof(struct det_torps_cpacket), handleDetTReq },   /* CP_DET_TORPS */
    { sizeof(struct det_mytorp_cpacket), handleDetMReq },  /* CP_DET_MYTORP */
    { sizeof(struct copilot_cpacket), handleCopilotReq },  /* CP_COPLIOT */
    { sizeof(struct refit_cpacket), handleRefitReq }, 	   /* CP_REFIT */
    { sizeof(struct tractor_cpacket), handleTractorReq },  /* CP_TRACTOR */
    { sizeof(struct repress_cpacket), handleRepressReq },  /* CP_REPRESS */
    { sizeof(struct coup_cpacket), handleCoupReq },	   /* CP_COUP */
    { sizeof(struct socket_cpacket), handleSocketReq },	   /* CP_SOCKET */
    { sizeof(struct options_cpacket), handleOptionsPacket },/* CP_OPTIONS */
    { sizeof(struct bye_cpacket), handleByeReq },	   /* CP_BYE */
    { sizeof(struct dockperm_cpacket), handleDockingReq }, /* CP_DOCKPERM */
    { sizeof(struct updates_cpacket), handleUpdatesReq 	}, /* CP_UPDATES */
    { sizeof(struct resetstats_cpacket), handleReset },    /* CP_RESETSTATS */
    { sizeof(struct reserved_cpacket), handleReserved },   /* CP_RESERVED */
    { sizeof(struct scan_cpacket), handleScan },	   /* CP_SCAN (ATM) */
    { sizeof(struct udp_req_cpacket), handleUdpReq },	   /* CP_UDP_REQ */
    { sizeof(struct sequence_cpacket), handleSequence },   /* CP_SEQUENCE */
#ifdef RSA
    { sizeof(struct rsa_key_cpacket), handleRSAKey },	   /* CP_RSA_KEY */
#else
    { 0, NULL},						   /* 37 */
#endif
    { 0, NULL},						   /* 38 */
    { 0, NULL},						   /* 39 */
    { 0, NULL},						   /* 40 */
    { 0, NULL},						   /* 41 */
#ifdef PING	
    { sizeof(struct ping_cpacket), handlePingResponse },   /* CP_PING_RESPONSE*/
#endif
#ifdef SHORT_PACKETS
    { sizeof(struct shortreq_cpacket), handleShortReq },   /* CP_S_REQ */
    { sizeof(struct threshold_cpacket), handleThresh },    /* CP_S_THRS */
    { -1, handleSMessageReq},                              /* CP_S_MESSAGE */
#endif
    {0, NULL},						   /* 46 */
    {0, NULL},						   /* 47 */
    {0, NULL},						   /* 48 */
    {0, NULL},						   /* 49 */
#ifdef BASEPRACTICE
    {sizeof(struct oggv_cpacket), handleOggV},	           /* CP_OGGV */
#else
    {0, NULL},						   /* 50 */
#endif
    {0, NULL},						   /* 51 */
    {0, NULL},						   /* 52 */
    {0, NULL},						   /* 53 */
    {0, NULL},						   /* 54 */
    {0, NULL},						   /* 55 */
    {0, NULL},						   /* 56 */
    {0, NULL},						   /* 57 */
    {0, NULL},						   /* 58 */
    {0, NULL},						   /* 59 */
#ifdef FEATURE_PACKETS
    {sizeof(struct feature_cpacket), handleFeature },	   /* CP_FEATURE */
#endif
};

int sizes[] = {
    0,	/* record 0 */
    sizeof(struct mesg_spacket),		/* SP_MESSAGE */
    sizeof(struct plyr_info_spacket), 		/* SP_PLAYER_INFO */
    sizeof(struct kills_spacket),		/* SP_KILLS */
    sizeof(struct player_spacket),		/* SP_PLAYER */
    sizeof(struct torp_info_spacket),		/* SP_TORP_INFO */
    sizeof(struct torp_spacket), 		/* SP_TORP */
    sizeof(struct phaser_spacket),		/* SP_PHASER */
    sizeof(struct plasma_info_spacket),		/* SP_PLASMA_INFO */
    sizeof(struct plasma_spacket),		/* SP_PLASMA */
    sizeof(struct warning_spacket),		/* SP_WARNING */
    sizeof(struct motd_spacket),		/* SP_MOTD */
    sizeof(struct you_spacket),			/* SP_YOU */
    sizeof(struct queue_spacket),		/* SP_QUEUE */
    sizeof(struct status_spacket),		/* SP_STATUS */
    sizeof(struct planet_spacket),		/* SP_PLANET */
    sizeof(struct pickok_spacket),		/* SP_PICKOK */
    sizeof(struct login_spacket),		/* SP_LOGIN */
    sizeof(struct flags_spacket),		/* SP_FLAGS */
    sizeof(struct mask_spacket),		/* SP_MASK */
    sizeof(struct pstatus_spacket),		/* SP_PSTATUS */
    sizeof(struct badversion_spacket),		/* SP_BADVERSION */
    sizeof(struct hostile_spacket),		/* SP_HOSTILE */
    sizeof(struct stats_spacket),		/* SP_STATS */
    sizeof(struct plyr_login_spacket),		/* SP_PL_LOGIN */
    sizeof(struct reserved_spacket), 		/* SP_RESERVED */
    sizeof(struct planet_loc_spacket),		/* SP_PLANET_LOC */
    sizeof(struct scan_spacket),		/* SP_SCAN (ATM) */
    sizeof(struct udp_reply_spacket),		/* SP_UDP_REPLY */
    sizeof(struct sequence_spacket),		/* SP_SEQUENCE */
    sizeof(struct sc_sequence_spacket),		/* SP_SC_SEQUENCE */
#ifdef RSA
    sizeof(struct rsa_key_spacket),		/* SP_RSA_KEY */
#else
     0,						/* 31 */
#endif
     0,						/* 32 */
     0,						/* 33 */
     0,						/* 34 */
     0,						/* 35 */
     0,						/* 36 */
     0,						/* 37 */
     0,						/* 38 */
     sizeof(struct ship_cap_spacket),		/* SP_SHIP_CAP */
#ifdef SHORT_PACKETS
    sizeof(struct shortreply_spacket),          /* SP_S_REPLY */
    -1,                                         /* SP_S_MESSAGE */
    sizeof(struct warning_s_spacket),           /* SP_S_WARNING */
    sizeof(struct you_short_spacket),		/* SP_S_YOU */
    sizeof(struct youss_spacket),               /* SP_S_YOU_SS */
    -1, /* variable */                          /* SP_S_PLAYER */
#ifdef PING
    sizeof(struct ping_spacket),                /* SP_PING */
#else
    0,						/* SP_PING */
#endif
    -1, /* variable */                          /* SP_S_TORP  */
    -1, /* variable */				/* SP_S_TORP_INFO */
    20,                                         /* SP_S_8_TORP */
    -1, /* variable */                          /* SP_S_PLANET */
     0,						/* 51 */
     0,						/* 52 */
     0,						/* 53 */
     0,						/* 54 */
     0,						/* 55 */
     0,						/* 56 */
     -1, /* variable  S_P2 */			/* SP_S_PHASER */
     -1, /* variable */				/* SP_S_KILLS */
     sizeof (struct stats_s_spacket),           /* SP_S_STATS */
#ifdef FEATURE_PACKETS
     sizeof(struct feature_spacket),		/* SP_FEATURE */
#endif
#else 
     0,						/* 40 */
     0,						/* 41 */
     0,						/* 42 */
     0,						/* 43 */
     0,						/* 44 */
     0,						/* 45 */
#ifdef PING
    sizeof(struct ping_spacket),                /* SP_PING */
#endif
     0,						/* 47 */
     0,						/* 48 */
     0,						/* 49 */
     0,						/* 50 */
     0,						/* 51 */
     0,						/* 52 */
     0,						/* 53 */
     0,						/* 54 */
     0,						/* 55 */
     0,						/* 56 */
     0,						/* 57 */
     0,						/* 58 */
     0,						/* 59 */
#ifdef FEATURE_PACKETS
     sizeof(struct feature_spacket),		/* 60 */
#endif
#endif
};

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

int packetsReceived[NUM_PACKETS+1] = { 0 };
int packetsSent[NUM_SIZES+1] = { 0 };

#define BUFSIZE 16738
#define UDPBUFSIZE 960			/* (tweakable; should be under 1300) */
static char buf[BUFSIZE];		/* Socket buffer */
static char udpbuf[UDPBUFSIZE];		/* UDP socket buffer */
static char *bufptr=buf;
static char *udpbufptr=udpbuf;
#ifdef DOUBLE_UDP
static char scbuf[UDPBUFSIZE];		/* semi-critical UDP socket buffer */
static char *scbufptr=scbuf;		/* (only used for double UDP) */
#endif
int clientDead=0;
static LONG sequence;			/* the holy sequence number */

static int udpLocalPort = 0;
static int udpClientPort = 0;
static int udpMode = MODE_SIMPLE;	/* what kind of UDP trans we want */

#ifdef UDP_FIX 
static struct sockaddr_in caddr;        /* 18/8/92 SYSV time out fix SK */
#endif

/* this stuff is used for Fat UDP */
typedef void * PTR;			/* adjust this if you lack (void *) */
typedef struct fat_node_t {
    PTR packet;
    int pkt_size;
    struct fat_node_t *prev;
    struct fat_node_t *next;
} FAT_NODE;
/* needed for fast lookup of semi-critical fat nodes */
FAT_NODE fat_kills[MAXPLAYER];
FAT_NODE fat_torp_info[MAXPLAYER * MAXTORP];
FAT_NODE fat_phaser[MAXPLAYER];
FAT_NODE fat_plasma_info[MAXPLAYER * MAXPLASMA];
FAT_NODE fat_you;
#ifdef SHORT_PACKETS
FAT_NODE fat_s_you;
FAT_NODE fat_s_planet;
FAT_NODE fat_s_kills;	/* S_P2 */
FAT_NODE fat_s_phaser[MAXPLAYER]; 
#endif
FAT_NODE fat_status;
FAT_NODE fat_planet[MAXPLANETS];
FAT_NODE fat_flags[MAXPLAYER];
FAT_NODE fat_hostile[MAXPLAYER];
/* define the lists */
#define MAX_FAT_LIST	5		/* tweakable; should be > 1 */
typedef struct {
    FAT_NODE *head;
    FAT_NODE *tail;
} FAT_LIST;
FAT_LIST fatlist[MAX_FAT_LIST], tmplist[MAX_FAT_LIST];
/* tweakable parameters; compare with UDPBUFSIZE */
/* NOTE: FAT_THRESH + MAX_FAT_DATA must be < UDPBUFSIZE                */
/*       MAX_FAT_DATA must be larger than biggest semi-critical packet */
#define FAT_THRESH	500		/* if more than this, don't add fat */
#define MAX_FAT_DATA	100		/* add at most this many bytes */
#define MAX_NONFAT	10		/* if we have this much left, stop */


struct plyr_info_spacket clientPlayersInfo[MAXPLAYER];
struct plyr_login_spacket clientLogin[MAXPLAYER];
struct hostile_spacket clientHostile[MAXPLAYER];
struct stats_spacket clientStats[MAXPLAYER];
struct player_spacket clientPlayers[MAXPLAYER];
struct kills_spacket clientKills[MAXPLAYER];
struct flags_spacket clientFlags[MAXPLAYER];
struct pstatus_spacket clientPStatus[MAXPLAYER];
int msgCurrent;
struct torp_info_spacket clientTorpsInfo[MAXPLAYER*MAXTORP];
struct torp_spacket clientTorps[MAXPLAYER*MAXTORP];
struct phaser_spacket clientPhasers[MAXPLAYER];
#ifdef SHORT_PACKETS /* S_P2 */
struct phaser_s_spacket client_s_Phasers[MAXPLAYER];
#endif 
struct you_spacket clientSelf;
struct status_spacket clientStatus;
struct planet_spacket clientPlanets[MAXPLANETS];
struct planet_loc_spacket clientPlanetLocs[MAXPLANETS];
struct plasma_info_spacket clientPlasmasInfo[MAXPLAYER*MAXPLASMA];
struct plasma_spacket clientPlasmas[MAXPLAYER*MAXPLASMA];
int mustUpdate[MAXPLAYER];

#ifdef CHECKMESG
static FILE	*mlog, *glog;
#endif

#ifdef SHORT_PACKETS

struct youss_spacket		clientSelfShip;
struct you_short_spacket	clientSelfShort;

/* HW */
static u_char	clientVPlanets[MAXPLANETS*sizeof(struct planet_s_spacket)+2 +6];
static int	clientVPlanetCount;
static int	vtsize[9] ={ 4,8,8,12,12,16,20,20,24 }; /* How big is the */
							/* SP_S_TORP packet */
static int	vtdata[9] ={0,3,5,7,9,12,14,16,18 }; /* How big is Torpdata */
static int	mustsend;    /* Flag to remind me that i must send SP_S_TORP */

static u_char	clientVTorps[40];

static u_char	clientVTorpsInfo[16];

static u_char    clientVPlayers[MAXPLAYER*VPLAYER_SIZE + 16];

/* S_P2 */
static u_char clientVKills[MAXPLAYER*2+2 +4];
static int clientVKillsCount;
struct stats_s_spacket singleStats;
unsigned char n_flags[MAXPLAYER]; 
int highest_active_player;

#if MAXPLAYER > 32
static u_char	clientVXPlayers[33*4];
#endif
static int	clientVPlayerCount;
static int	clientVXPlayerCount;
static int	big;

/*int           send_short        = 0;*/ /* is now in data.c , because of robotII.c */
/* the following now in data.c due to FASTER_SB_MAXUPDATES */
/*static int	send_threshold    = 0;*/	/* infinity */
/* static int	actual_threshold  = 0;*/	/* == send_threshold / numupdates */
/*static int	numupdates	  = 5;*/	/* For threshold */
static int	send_mesg         = 1;
static int	send_kmesg        = 1;
static int	send_warn         = 1;

static int	spk_update_sall   = 0;	/* Small Update: Only weapons, */
					/* Kills and Planets */
static int	spk_update_all	  = 0;  /* Full Update minus SP_STATS */

#define         SPK_VOFF	0	/* variable packets off */
#define         SPK_VON		1	/* variable packets on */
#define         SPK_MOFF	2	/* message packets off */
#define         SPK_MON		3	/* message packets on */
#define         SPK_M_KILLS	4
#define         SPK_M_NOKILLS	5
#define         SPK_THRESHOLD	6
#define         SPK_M_WARN	7
#define         SPK_M_NOWARN	8
#define 	SPK_SALL	9	/* only planets,kills and weapons */
#define         SPK_ALL		10	/* Full Update - SP_STATS */

#endif

/* S_P2 */
#define SizeOfUDPUpdate()               (udpbufptr-udpbuf)


connectToClient(machine, port)
char *machine;
int port;
{
    int ns;
    struct sockaddr_in addr;
    struct hostent *hp;
    /*int len,cc;*/
    /*int i;*/

    if (sock!=-1) {
	shutdown(sock,2);
	sock= -1;
    }
    ERROR(3,("Connecting to %s through %d\n", machine, port)); 

    if ((ns=socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	ERROR(1,("I cannot create a socket\n"));
	exit(2);
    }

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

    if (remoteaddr!=-1) {
	addr.sin_addr.s_addr=remoteaddr;
    } else if ((addr.sin_addr.s_addr = inet_addr(machine)) == -1) {
	if ((hp = gethostbyname(machine)) == NULL) {
	    ERROR(1,("I cannot get host name\n"));
	    close(ns);
	    exit(1);
	} else {
	    addr.sin_addr.s_addr = *(U_LONG *) hp->h_addr;
	}
    }
    remoteaddr=addr.sin_addr.s_addr;

    if (connect(ns, &addr, sizeof(addr)) < 0) {
	 ERROR(3,("I cannot connect through port %d\n", port));
	close(ns);
	return(0);
    }
    sock=ns;
    initClientData();
    testtime = -1;
    return(1);
}

/* Check the socket to read it's inet addr for possible future use.
 */
checkSocket()
{
    struct sockaddr_in sin;
    int length;

    length=sizeof(sin);
    if (getpeername(sock, (struct sockaddr *) &sin, &length) < 0) {
	/* A bad thing. */
	return;
    }

    remoteaddr=sin.sin_addr.s_addr;
}

initClientData()
/* invalidates all data, so it is all sent to the client */
{
    int i;

#ifdef CHECKMESG
    if (logall) {
	if (!mlog) mlog = fopen(MesgLog, "a");
	if (!glog) perror(MesgLog);
    }
    if (loggod) {
	if (!glog) glog = fopen(GodLog,"a");
	if (!glog) perror(GodLog);
    }
#endif

    clientDead=0;
    for (i=0; i<MAXPLAYER; i++) {
	clientHostile[i].hostile= -1;
	clientStats[i].losses= -1;
	clientLogin[i].rank= -1;
	clientPlayersInfo[i].shiptype= -1;
	clientPStatus[i].status= -1;
	clientPlayers[i].x= htonl(-1);
	clientPhasers[i].status= -1;
#ifdef SHORT_PACKETS /* S_P2 */
      client_s_Phasers[i].status = -1;
#endif 
	clientKills[i].kills= htonl(-1);
	clientFlags[i].flags= htonl(-1);
	mustUpdate[i]=0;

	fat_hostile[i].packet = (PTR) &clientHostile[i];
	fat_hostile[i].pkt_size = sizeof(struct hostile_spacket);
	fat_hostile[i].prev = fat_hostile[i].next = (FAT_NODE *) NULL;
	fat_phaser[i].packet = (PTR) &clientPhasers[i];
	fat_phaser[i].pkt_size = sizeof(struct phaser_spacket);
	fat_phaser[i].prev = fat_phaser[i].next = (FAT_NODE *) NULL;
#ifdef SHORT_PACKETS /* S_P2 */
      fat_s_phaser[i].packet = (PTR) & client_s_Phasers[i];
      fat_s_phaser[i].pkt_size = sizeof (struct phaser_s_spacket);
      fat_s_phaser[i].prev = fat_s_phaser[i].next = (FAT_NODE *) NULL;
#endif 
	fat_kills[i].packet = (PTR) &clientKills[i];
	fat_kills[i].pkt_size = sizeof(struct kills_spacket);
	fat_kills[i].prev = fat_kills[i].next = (FAT_NODE *) NULL;
	fat_flags[i].packet = (PTR) &clientFlags[i];
	fat_flags[i].pkt_size = sizeof(struct flags_spacket);
	fat_flags[i].prev = fat_flags[i].next = (FAT_NODE *) NULL;
    }
    for (i=0; i<MAXPLAYER*MAXTORP; i++) {
	clientTorpsInfo[i].status= -1;
	clientTorps[i].x= -1;
	clientTorpsInfo[i].war = 0;

	fat_torp_info[i].packet = (PTR) &clientTorpsInfo[i];
	fat_torp_info[i].pkt_size = sizeof(struct torp_info_spacket);
	fat_torp_info[i].prev = fat_torp_info[i].next = (FAT_NODE *) NULL;
    }
    for (i=0; i<MAXPLAYER*MAXPLASMA; i++) {
	clientPlasmasInfo[i].status= -1;
	clientPlasmas[i].x= -1;

	fat_plasma_info[i].packet = (PTR) &clientPlasmasInfo[i];
	fat_plasma_info[i].pkt_size = sizeof(struct plasma_info_spacket);
	fat_plasma_info[i].prev = fat_torp_info[i].next = (FAT_NODE *) NULL;
    }
    for (i=0; i<MAXPLANETS; i++) {
	clientPlanets[i].armies= htonl(-1);
	clientPlanetLocs[i].x= htonl(-1);

	fat_planet[i].packet = (PTR) &clientPlanets[i];
	fat_planet[i].pkt_size = sizeof(struct planet_spacket);
	fat_planet[i].prev = fat_planet[i].next = (FAT_NODE *) NULL;
    }
    msgCurrent=(mctl->mc_current+1) % MAXMESSAGE;
    clientSelf.pnum= -1;

#ifdef SHORT_PACKETS
    clientSelfShip.damage = -1;
    clientSelfShort.pnum = -1;
    fat_s_you.packet = (PTR) &clientSelfShort;
    fat_s_you.pkt_size = sizeof(struct you_short_spacket);
    fat_s_you.prev = fat_s_you.next = (FAT_NODE *) NULL;
    fat_s_planet.packet = (PTR) clientVPlanets;
    fat_s_planet.pkt_size = 0; /* It's dynamic */
    fat_s_planet.prev =  fat_s_planet.next = (FAT_NODE *) NULL;
	/* S_P2 */
    fat_s_kills.packet = (PTR) clientVKills;
    fat_s_kills.pkt_size = 0;
    fat_s_kills.prev = fat_s_kills.next = (FAT_NODE *) NULL;
#endif

    fat_you.packet = (PTR) &clientSelf;
    fat_you.pkt_size = sizeof(struct you_spacket);
    fat_you.prev = fat_you.next = (FAT_NODE *) NULL;
    fat_status.packet = (PTR) &clientStatus;
    fat_status.pkt_size = sizeof(struct status_spacket);
    fat_status.prev = fat_status.next = (FAT_NODE *) NULL;

    for (i = 0; i < MAX_FAT_LIST; i++)
	fatlist[i].head = fatlist[i].tail = (FAT_NODE *) NULL;
}

isClientDead()
{
    return(clientDead);
}

updateClient()
{
    int i;
#ifdef SHORT_PACKETS
#ifdef SHORT_THRESHOLD
    static int skip = 0; /* If skip is set we skip next update */
/* This can halve your updates */
    if (send_short && skip) {
        skip = 0; /* back to default */
    if (bufptr==buf && (commMode!=COMM_UDP || udpbufptr==buf)) {
        /* We sent nothing!  We better send something to wake him */
        if (me->p_fuel < 61000)
                sendClientPacket((CVOID) &clientSelfShort);
        else
                sendClientPacket((CVOID) &clientSelf);
    }
        flushSockBuf();
        repCount++;
        return;
    }
#endif
#endif

    for (i=0; i<MAXPLAYER; i++) {
	mustUpdate[i]=0;
    }
#ifdef SHORT_PACKETS
        if(send_short){
    updatePlasmas();
/*    updateStatus(); S_P2 */
    updateSelf();
    updatePhasers();
    updateShips();
    SupdateTorps();
    updatePlanets();
    updateMessages();
    /* EXPERIMENT:  Don't inflate large packet with non-crucial stuff  S_P2 */
    if((SizeOfUDPUpdate() < 60) ||(clientStatus.tourn != status->tourn))
       	  updateStatus();
    if(SizeOfUDPUpdate() < 75)
         updatePlayerStats();
        }
        else {
#endif
    updateTorps();
    updatePlasmas();
    updateStatus();
    updateSelf();
    updatePhasers();
    updateShips();
    updatePlanets();
    updateMessages();
/* S_P2 */
    updatePlayerStats();
#ifdef SHORT_PACKETS
        }
#endif

    if (bufptr==buf && (commMode!=COMM_UDP || udpbufptr==buf)) {
	/* We sent nothing!  We better send something to wake him */
#ifdef SHORT_PACKETS
        if(send_short && (me->p_fuel < 61000))
                sendClientPacket((CVOID) &clientSelfShort);
        else
#endif
	sendClientPacket((CVOID) &clientSelf);
    }

#ifdef SHORT_PACKETS
#ifdef SHORT_THRESHOLD
    if ((actual_threshold != 0 ) &&  ((bufptr-buf) + (udpbufptr-udpbuf)) 
         > actual_threshold) skip = 1;
         /* Next update is skipped */
        /* Question: Should i only test udpbuffer, or separate both? */
#endif
#endif

#ifdef PING     /* NOTE: this must be the last thing we do before flushing
                   the socket buffer. */
    if(ping && (repCount % efticks(5*ping_freq) == 0) && testtime == 0 && 
	        /* these checks are here to make sure we don't ping from the
		   updateClient call in death().  The reason is that savestats()
		   can take > 100ms, invalidating the next ping lag calc */
	(me->p_status == PALIVE
#ifdef OBSERVERS
		|| me->p_status == POBSERV))
#else
		))
#endif
       sendClientPing();                /* ping.c */
#endif

    flushSockBuf();
    repCount++;
}

updateStatus()
{
    /* Update status every 10 seconds? */
    if (repCount % efticks(50) == 0 &&
	((ntohl(clientStatus.timeprod) != timeprod_int()) ||
	 (clientStatus.tourn != status->tourn))) { /* bug fix 4/2/92 TC */
	clientStatus.type=SP_STATUS;
	clientStatus.tourn=status->tourn;
	clientStatus.armsbomb=htonl(status->armsbomb/10);
	clientStatus.planets=htonl(status->planets/10);
	clientStatus.kills=htonl(status->kills/10);
	clientStatus.losses=htonl(status->losses/10);
	clientStatus.time=htonl(status->time/10);
	clientStatus.timeprod=htonl(timeprod_int());
	sendClientPacket((CVOID) &clientStatus);
    }
}

updateSelf()
{
#ifdef SHORT_PACKETS
static int again = 0; /* Flag, SP_S_YOU is always (almost) send two times. */
                      /* Because SP_S_YOU is now send via UDP  HW 27.06.93 */
   if(send_short &&  me->p_fuel < 61000  ){ /* A little margin ... */
      if(ntohs(clientSelfShip.damage) != me->p_damage ||
         ntohs(clientSelfShip.shield) != me->p_shield ||
         ntohs(clientSelfShip.fuel) != me->p_fuel ||
         ntohs(clientSelfShip.etemp) != me->p_etemp ||
         ntohs(clientSelfShip.wtemp) != me->p_wtemp){

        clientSelfShip.type = SP_S_YOU_SS;
        /* FEATURE SELF_8FLAGS */
	clientSelfShip.pad1 = me->p_flags & 255; /* For future use */
        clientSelfShip.damage=htons(me->p_damage);
        clientSelfShip.shield=htons(me->p_shield);
        clientSelfShip.fuel=htons(me->p_fuel);
        clientSelfShip.etemp=htons(me->p_etemp);
        clientSelfShip.wtemp=htons(me->p_wtemp);
        sendClientPacket((CVOID) &clientSelfShip);
      }

      if(clientSelfShort.pnum != me->p_no ||
         clientSelfShort.hostile != me->p_hostile ||
         clientSelfShort.swar != me->p_swar ||
         clientSelfShort.armies != me->p_armies ||
         clientSelfShort.whydead != me->p_whydead ||
         clientSelfShort.whodead != me->p_whodead ||
         ntohl(clientSelfShort.flags) != me->p_flags ||
	 again /* sortof boolean HW,sent SP_S_YOU again */ ){

         clientSelfShort.type = SP_S_YOU;
         clientSelfShort.pnum = me->p_no;
         clientSelfShort.hostile = me->p_hostile;
         clientSelfShort.swar = me->p_swar;
         clientSelfShort.armies = me->p_armies;
         clientSelfShort.whydead = me->p_whydead;
         clientSelfShort.whodead = me->p_whodead;
         clientSelfShort.flags = htonl(me->p_flags);
         sendClientPacket((CVOID) &clientSelfShort);
	 again ^= 1;	/* toggle */
      }
   }
   else
#endif 
    if (ntohl(clientSelf.fuel) != me->p_fuel ||
	    ntohl(clientSelf.shield) != me->p_shield ||
	    ntohl(clientSelf.damage) != me->p_damage ||
	    ntohs(clientSelf.etemp) != me->p_etemp ||
	    ntohs(clientSelf.wtemp) != me->p_wtemp ||
	    ntohl(clientSelf.flags) != me->p_flags || 
	    clientSelf.armies != me->p_armies ||
	    clientSelf.swar != me->p_swar ||
	    ntohs(clientSelf.whydead) != me->p_whydead ||
	    ntohs(clientSelf.whodead) != me->p_whodead ||
	    clientSelf.pnum != me->p_no) {

	/* we want to send it, but how? */
	clientSelf.type = SP_YOU;

	if (commMode == COMM_UDP) {
	    if (    ntohl(clientSelf.flags) != me->p_flags ||
		    clientSelf.armies != me->p_armies ||
		    clientSelf.swar != me->p_swar) {
		clientSelf.type=SP_YOU | 0x40;	/* mark as semi-critical */
	    }
	    if (    ntohs(clientSelf.whydead) != me->p_whydead ||
		    ntohs(clientSelf.whodead) != me->p_whodead ||
		    clientSelf.pnum != me->p_no) {
		clientSelf.type=SP_YOU | 0x80;	/* mark as critical */
	    }
	}

	clientSelf.pnum=me->p_no;
	clientSelf.flags=htonl(me->p_flags);
	clientSelf.swar=me->p_swar;
	clientSelf.hostile=me->p_hostile;
	clientSelf.armies=me->p_armies;
	clientSelf.shield=htonl(me->p_shield);
	clientSelf.fuel=htonl(me->p_fuel);
	clientSelf.etemp=htons(me->p_etemp);
	clientSelf.wtemp=htons(me->p_wtemp);
	clientSelf.whydead=htons(me->p_whydead);
	clientSelf.whodead=htons(me->p_whodead);
	clientSelf.damage=htonl(me->p_damage);
	/* clientSelf.tractor=(char)me->p_tractor |0x40;		 ATM - visible tractor */
	sendClientPacket((CVOID) &clientSelf);
    }
}

updateShips()
{
    register int i;
    register struct player *pl;
    register struct plyr_info_spacket *cpli;
    register struct player_spacket *cpl;
    register struct kills_spacket *kills;
    register struct flags_spacket *flags;
    register struct pstatus_spacket *pstatus;
    register struct plyr_login_spacket *login;
    register struct hostile_spacket *hostile;
    register struct stats_spacket *stats;
    int update;
    int x,y;
/*#define FLAGMASK (PFSHIELD|PFBOMB|PFORBIT|PFCLOAK|PFROBOT|PFBEAMUP|PFBEAMDOWN|PFPRACTR|PFDOCK|PFTRACT|PFPRESS|PFDOCKOK) atm mask */

/* #define FLAGMASK (PFSHIELD|PFBOMB|PFORBIT|PFCLOAK|PFROBOT|PFBEAMUP|PFBEAMDOWN|PFPRACTR|PFDOCK|PFTRACT|PFPRESS|PFDOCKOK) aieee, too much.  7/27/91 TC */ 

#define FLAGMASK (PFSHIELD|PFBOMB|PFORBIT|PFCLOAK|PFROBOT|PFPRACTR|PFDOCK|PFTRACT|PFPRESS|PFDOCKOK)
#define INVISOMASK (PFCLOAK|PFROBOT|PFPRACTR|PFDOCKOK)

#ifdef SHORT_PACKETS
/* S_P2 */
unsigned short shiftkills;
unsigned char *vkills = &clientVKills[2];
clientVKillsCount = 0;
highest_active_player = 0;
/* S_P2 end */
  clientVPlayerCount = 12;
  clientVXPlayerCount = 4;
  clientVPlayers[1]   = 0; /* How many packets are in the Big... */
#if MAXPLAYER > 32
  clientVXPlayers[1] = 0;
#endif
  big=0;
#endif

    /* Please excuse the ugliness of this loop declaration */
    for (i=0, pl=players, cpli=clientPlayersInfo, cpl=clientPlayers, kills=clientKills, flags=clientFlags, pstatus=clientPStatus, login=clientLogin, hostile=clientHostile, stats=clientStats;
    	    i<MAXPLAYER; 
	    i++, pl++, cpli++, cpl++, kills++, flags++, pstatus++, login++, hostile++, stats++) {
	
	update=0;
	if (strcmp(pl->p_name, login->name)!=0 ||
	    pl->p_stats.st_rank != login->rank ||
	    strcmp(pl->p_monitor, login->monitor)!=0) {
	    STRNCPY(login->name,pl->p_name,NAME_LEN);
	    STRNCPY(login->monitor,pl->p_monitor,NAME_LEN);
	    STRNCPY(login->login,pl->p_login,NAME_LEN);
	    login->name[NAME_LEN-1]=0;
	    login->monitor[NAME_LEN-1]=0;
	    login->login[NAME_LEN-1]=0;
	    login->type=SP_PL_LOGIN;
	    login->pnum=i;
	    login->rank=pl->p_stats.st_rank;
	    sendClientPacket(login);
	}
	if ((pl!=me && (pl->p_swar & me->p_team)!=hostile->war) ||
	    (pl!=me && (pl->p_hostile & me->p_team)!=hostile->hostile) ||
	    (pl==me && pl->p_swar != hostile->war) ||
	    (pl==me && pl->p_hostile != hostile->hostile)) {
	    hostile->type=SP_HOSTILE;
	    if (pl==me) {
		hostile->war=pl->p_swar;
		hostile->hostile=pl->p_hostile;
	    } else {
		hostile->war=(pl->p_swar & me->p_team);
		hostile->hostile=(pl->p_hostile & me->p_team);
	    }
	    hostile->pnum=i;
	    sendClientPacket(hostile);
	}

	if (pl->p_ship.s_type != cpli->shiptype ||
	    pl->p_team != cpli->team) {
	    cpli->type=SP_PLAYER_INFO;
	    cpli->pnum=i;
	    cpli->shiptype=pl->p_ship.s_type;
	    cpli->team=pl->p_team;
	    sendClientPacket(cpli);
	}
	if (kills->kills != htonl((int) (pl->p_kills*100))) {
	    kills->type=SP_KILLS;
	    kills->pnum=i;
	    kills->kills=htonl((int) (pl->p_kills*100));
#ifdef SHORT_PACKETS
	/* S_P2 */
	    if (send_short > 1 && (pl->p_kills*100 < 1001) && i < 64) {
		clientVKillsCount++;
		shiftkills = pl->p_kills * 100;
		shiftkills |= (i << 10);
		*vkills++ = (unsigned char) shiftkills & 0xff;
		*vkills++ = (shiftkills >> 8) & 0xff; 		
	    }
	    else
#endif
	    sendClientPacket(kills);
	}
#ifdef SHORT_PACKETS
#ifdef OBSERVERS
	if(pl->p_status != POBSERV){
#endif
/* S_P2 new flag sampling */
	if(!(pl->p_flags & PFROBOT)) { /* No robos please */
	switch(pl->p_status){
	    case PALIVE: /* huh, we must work */
		highest_active_player = i;
		if(pl->p_flags & PFCLOAK){
			n_flags[i] = 1;
			break;
	        }
		else if(pl->p_flags & PFSHIELD){
			n_flags[i] = 2;
			}
		else
			n_flags[i] = 3;
		break;

	    case PEXPLODE:
	    case PDEAD:
		highest_active_player = i;
		n_flags[i] = 0;
		break; 
	    default: 
                n_flags[i] = 0; 
		/* Is it ok to send the old value ? */
		break;
	}
        } /* robos out */ 
#endif
#ifdef OBSERVERS
	}
#endif
	if (pstatus->status != pl->p_status) {
	    /* We update the location of people whose status has changed.
	     * (like if they just re-entered...)
	     */
#ifdef OBSERVERS
            int pobserv = 0;
#endif
	    update=1;
	    pstatus->type=SP_PSTATUS;
	    pstatus->pnum=i;
	    pstatus->status=pl->p_status;
	    if (pl->p_status == PFREE) { /* I think this will turn off ignores
					  for players that leave. 7/24/91 TC */
		extern int ignored[];
		ignored[i] = 0;
	    }
#ifdef OBSERVERS
            if (pl->p_status == POBSERV) {
                pstatus->status = PALIVE;
                pobserv = 1;
            }
#endif 
#ifdef SHORT_PACKETS
/* S_P2 */
	    if (send_short < 2 || 
#ifdef OBSERVERS
		pobserv ||
#endif
		i > 31 ||
		pl->p_flags & PFROBOT ||
		pstatus->status != PALIVE ||
		pstatus->status != PEXPLODE ||
		pstatus->status != PDEAD) 
#endif
	    sendClientPacket(pstatus);
	    if (dead_warp) dead_warp = 1;
#ifdef OBSERVERS
            if (pobserv)
            pstatus->status = POBSERV;
#endif
	}

	/* Used to send flags here, see below 8/7/91 TC */

	/* hidden enemies?  -- note OBSERVer is always inviso to everyone
         else */

	if ((!hiddenenemy || pl->p_team == me->p_team || 
		(pl->p_flags & PFSEEN) || (status->tourn==0))
#ifdef OBSERVERS
                && (pl->p_status != POBSERV || pl == me)
#endif
		) {
	    x=pl->p_x;
	    y=pl->p_y;
	    if (me!=pl && flags->flags != htonl(FLAGMASK & pl->p_flags)) {
		flags->type=SP_FLAGS;
		flags->pnum=i;
		flags->flags=htonl(FLAGMASK & pl->p_flags);
		/* flags->tractor = (char) pl->p_tractor |0x40;	/* ATM - visible tractor */
#ifdef SHORT_PACKETS
/* S_P2 */
	    if(send_short < 2 || pl->p_ship.s_type == STARBASE || i > 31 || (pl->p_flags & PFROBOT))
#endif
	    sendClientPacket(flags);
	    }
	} else {
	    /* A hack to make him inviso */
	    x= -100000;
	    y= -100000;

	    /* reduce flag info if he's inviso 8/7/91 TC */

	    if (me!=pl && flags->flags != htonl(INVISOMASK & pl->p_flags)) {
		flags->type=SP_FLAGS;
		flags->pnum=i;
		flags->flags=htonl(INVISOMASK & pl->p_flags);
#ifdef SHORT_PACKETS
/* S_P2 */
		if (send_short < 2 || pl->p_ship.s_type == STARBASE || i > 31 || (pl->p_flags & PFROBOT))	
#endif
		sendClientPacket(flags);
	    }
	}
	if (x != ntohl(cpl->x) || y != ntohl(cpl->y) || 
		pl->p_dir != cpl->dir || pl->p_speed != cpl->speed) {
	    /* We update the player if:
	     * 1) haven't updated him for 9 intervals.
	     * 2) he is on the screen
	     * 3) he was on the screen recently.
	     */
	    if (!update && repCount % efticks(9) != 0 && 
		(ntohl(cpl->x) < me->p_x - SCALE*WINSIDE/2 ||
		 ntohl(cpl->x) > me->p_x + SCALE*WINSIDE/2 ||
		 ntohl(cpl->y) > me->p_y + SCALE*WINSIDE/2 ||
		 ntohl(cpl->y) < me->p_y - SCALE*WINSIDE/2) &&
		(y > me->p_y + SCALE*WINSIDE/2 ||
		 x > me->p_x + SCALE*WINSIDE/2 ||
		 x < me->p_x - SCALE*WINSIDE/2 ||
		 y < me->p_y - SCALE*WINSIDE/2)) continue;

	    /* If the guy is cloaked, give information only occasionally,
	     *  and make it slightly inaccurate.
	     * Also, we don't give a direction.  The client has no reason
	     *  to know.
	     */
	    if ((pl->p_flags & PFCLOAK) && 
		  (pl->p_cloakphase == (CLOAK_PHASES - 1)) && 
		  (me != pl) && !mustUpdate[i]) {
		if (repCount % efticks(9) != 0) {
                   /* NEW: Send old position again */
#ifdef SHORT_PACKETS
                    if (send_short)
                        updateVPlayer(cpl);
                    else
                        sendClientPacket((CVOID) cpl);
#else
                    sendClientPacket((CVOID) cpl);
#endif
                    continue;
                }

#ifdef SHORT_PACKETS
                cpl->type=SP_PLAYER;
                cpl->pnum=i;
#ifdef AS_CLOAK
              if (pl->p_ship.s_type == ASSAULT) {
		cpl->speed = 0xf;   /* NEW: Joe Rumsey, 15 == cloaked */
                cpl->x=htonl(x+(random() % 3000)-1500);
                cpl->y=htonl(y+(random() % 3000)-1500);
              } else {
		cpl->speed = 0xf;   /* NEW: Joe Rumsey, 15 == cloaked */
                cpl->x=htonl(x+(random() % 2000)-1000);
                cpl->y=htonl(y+(random() % 2000)-1000);
              }
#else
                /* Feature: CLOAK_MAXWARP */
                cpl->speed = 0xf;   /* NEW: Joe Rumsey, 15 == cloaked */
                cpl->x=htonl(x+(random() % 2000)-1000);
                cpl->y=htonl(y+(random() % 2000)-1000);
#endif
		/* FEATURE: dead_warp */
		if ((pl->p_status == PDEAD || pl->p_status == PEXPLODE) && 
		     dead_warp) {
		    cpl->dir = dead_warp++;
		    cpl->speed = 0xe;			/* 14 = dead */
		    cpl->x = cpl->y = htonl(-10000);
		}

                if(send_short){
                  updateVPlayer(cpl);
                }
                else {
                  sendClientPacket(cpl);
                }
#else
		cpl->type=SP_PLAYER;
		cpl->pnum=i;
		cpl->x=htonl(x+(random() % 2000)-1000);
		cpl->y=htonl(y+(random() % 2000)-1000);

		/* FEATURE: dead_warp */
		if ((pl->p_status == PDEAD || pl->p_status == PEXPLODE) && 
                     dead_warp) {
		    cpl->dir = dead_warp++;
		    cpl->speed = 0xe;			/* 14 = dead */
		    cpl->x = cpl->y = htonl(-10000);
		}

		sendClientPacket(cpl);
#endif
		continue;
	    }
#ifdef SHORT_PACKETS
            cpl->type=SP_PLAYER;
            cpl->pnum=i;
            cpl->x=htonl(x);
            cpl->y=htonl(y);
	
	    /* Feature: CLOAK_MAXWARP */
            if (pl != me && (pl->p_flags & PFCLOAK))
               cpl->speed = 0xf;           /* NEW: Joe Rumsey, 15 == cloaked */
            else
               cpl->speed = pl->p_speed;

            cpl->dir=pl->p_dir;
	
	    /* FEATURE: dead_warp */
	    if ((pl->p_status == PDEAD || pl->p_status == PEXPLODE) && 
                     dead_warp) {
		cpl->dir = dead_warp++;
		cpl->speed = 0xe;                   /* 14 = dead */
		cpl->x = cpl->y = htonl(-10000);
	    }
	
            if(send_short){
               updateVPlayer(cpl);
            }
            else {
               sendClientPacket(cpl);
            }
#else
	    cpl->type=SP_PLAYER;
	    cpl->pnum=i;
	    cpl->x=htonl(x);
	    cpl->y=htonl(y);
	    cpl->speed=pl->p_speed;
	    cpl->dir=pl->p_dir;

            /* FEATURE: dead_warp */       
	    if ((pl->p_status == PDEAD || pl->p_status == PEXPLODE) && 
                     dead_warp) {
		cpl->dir = dead_warp++;
                cpl->speed = 0xe;                   /* 14 = dead */
                cpl->x = cpl->y = htonl(-10000);
            }

	    sendClientPacket(cpl);
#endif
	}
    }
#ifdef SHORT_PACKETS
    if(send_short && (clientVPlayerCount > 12 ||        clientVXPlayerCount > 4
|| big != 0 || send_short > 1 /* S_P2 fl;agsampling */))
        sendVPlayers();

/* S_P2 */
   if(send_short > 1 && clientVKillsCount) {
	clientVKills[1] = (unsigned char) clientVKillsCount;
	clientVKillsCount = 2+ (clientVKillsCount *2);
        if((clientVKillsCount % 4) != 0)
		clientVKillsCount += (4 - (clientVKillsCount % 4));
	
	sizes[SP_S_KILLS] = clientVKillsCount;
	clientVKills[0] = SP_S_KILLS;
	sendClientPacket((struct kills_spacket *) clientVKills);
   }
#endif
}

updateTorps()
{
  struct torp *t;
  int i;
  struct torp_info_spacket *tpi;
  struct torp_spacket *tp;

  for (i=0, t=firstTorp, tpi=clientTorpsInfo, tp=clientTorps; 
       t<=lastTorp; i++, t++, tpi++, tp++) {
    /*
     * If it was free before, and is still, do nothing.   */
    if ((t->t_status == TFREE) && (tpi->status == TFREE))
      continue;

    /*
     * If it's my torp, send info on it regardless of position;
     * my client needs the info to accurately display number of torps. */
    if (myTorp(t)) {
      if ((t->t_war != tpi->war) || (t->t_status != tpi->status)) {
	tpi->type   = SP_TORP_INFO;
	tpi->war    = t->t_war;
	tpi->status = t->t_status;
	tpi->tnum   = htons(i);
	sendClientPacket(tpi);
      }
      if ((tp->x != htonl(t->t_x)) || (tp->y != htonl(t->t_y))) {
	tp->type = SP_TORP;
	tp->x    = htonl(t->t_x);
	tp->y    = htonl(t->t_y);
	tp->dir  = t->t_dir;
	tp->tnum = htons(i);
	sendClientPacket(tp);
      }
      continue;
    }

    /* 
     * Not my torp.
     * If not in view, only send limited information   */
    if ((t->t_y > me->p_y + SCALE*WINSIDE/2) ||
	(t->t_y < me->p_y - SCALE*WINSIDE/2) ||
	(t->t_x > me->p_x + SCALE*WINSIDE/2) ||
	(t->t_x < me->p_x - SCALE*WINSIDE/2) ||
	(t->t_status == TFREE)) {
      if ((t->t_status == TFREE) && (tpi->status == TEXPLODE)) {
	tpi->status = TFREE;
	continue;
      }
      if (tpi->status != TFREE) {
	tpi->type   = SP_TORP_INFO;
	tpi->status = TFREE;
	tpi->tnum   = htons(i);
	sendClientPacket(tpi);
	}
      continue;
    } 

    /* 
     * Not my torp, and in view.
     * Send torp info (we assume it moved) */
    tp->type = SP_TORP;
    tp->x    = htonl(t->t_x);
    tp->y    = htonl(t->t_y);
    tp->tnum = htons(i);
    sendClientPacket(tp);
    if ((t->t_status != tpi->status) ||
	((t->t_war & me->p_team) != tpi->war)) {
      tpi->type   = SP_TORP_INFO;
      tpi->war    = t->t_war & me->p_team;
      tpi->tnum   = htons(i);
      tpi->status = t->t_status;
      sendClientPacket(tpi);
    }
  }
}

updatePlasmas()
{
  struct torp *t;
  int i;
  struct plasma_info_spacket *tpi;
  struct plasma_spacket *tp;

  for (i=0, t=firstPlasma, tpi=clientPlasmasInfo, tp=clientPlasmas; 
       t<=lastPlasma; i++, t++, tpi++, tp++) {
    /*
     * If it was free before, and is still, do nothing.   */
    if ((t->t_status == TFREE) && (tpi->status == TFREE))
      continue;

    /*
     * If it's my torp, send info on it regardless of position;
     * my client needs the info to accurately display number of torps. */
    if (myTorp(t)) {
      if ((t->t_war != tpi->war) || (t->t_status != tpi->status)) {
	tpi->type   = SP_PLASMA_INFO;
	tpi->war    = t->t_war;
	tpi->status = t->t_status;
	tpi->pnum   = htons(i);
	sendClientPacket(tpi);
      }
      if ((tp->x != htonl(t->t_x)) || (tp->y != htonl(t->t_y))) {
	tp->type = SP_PLASMA;
	tp->x    = htonl(t->t_x);
	tp->y    = htonl(t->t_y);
	tp->pnum = htons(i);
	sendClientPacket(tp);
      }
      continue;
    }

    /* 
     * Not my torp.
     * If not in view, only send limited information   */
    if ((t->t_y > me->p_y + SCALE*WINSIDE/2) ||
	(t->t_y < me->p_y - SCALE*WINSIDE/2) ||
	(t->t_x > me->p_x + SCALE*WINSIDE/2) ||
	(t->t_x < me->p_x - SCALE*WINSIDE/2) ||
	(t->t_status == TFREE)) {
      if ((t->t_status == TFREE) && (tpi->status == TEXPLODE)) {
	tpi->status = TFREE;
	continue;
      }
      if (tpi->status != TFREE) {
	tpi->type   = SP_PLASMA_INFO;
	tpi->status = TFREE;
	tpi->pnum   = htons(i);
	sendClientPacket(tpi);
	}
      continue;
    } 

    /* 
     * Not my torp, and in view.
     * Send torp info (we assume it moved) */
    tp->type = SP_PLASMA;
    tp->x    = htonl(t->t_x);
    tp->y    = htonl(t->t_y);
    tp->pnum = htons(i);
    sendClientPacket(tp);
    if ((t->t_status != tpi->status) ||
	((t->t_war & me->p_team) != tpi->war)) {
      tpi->type   = SP_PLASMA_INFO;
      tpi->war    = t->t_war & me->p_team;
      tpi->pnum   = htons(i);
      tpi->status = t->t_status;
      sendClientPacket(tpi);
    }
  }
}

updatePhasers()
{
    register int i;
    register struct phaser_spacket *ph;
    register struct phaser *phase;
    register struct player *pl;
#ifdef SHORT_PACKETS /* S_P2 */
  register struct phaser_s_spacket *phs;

  for (i = 0, ph = clientPhasers,phs = client_s_Phasers, phase = phasers, pl = players; 
       i < MAXPLAYER; i++, ph++, phs++, phase++, pl++) { 
#else

    for (i=0, ph=clientPhasers, phase=phasers, pl=players; 
	 i<MAXPLAYER; i++, ph++, phase++, pl++) {
#endif 
	if (pl->p_y > me->p_y + SCALE*WINSIDE/2 ||
	    pl->p_x > me->p_x + SCALE*WINSIDE/2 ||
	    pl->p_x < me->p_x - SCALE*WINSIDE/2 ||
	    pl->p_y < me->p_y - SCALE*WINSIDE/2) {
	    if (ph->status!=PHFREE) {
		ph->pnum=i;
		ph->type=SP_PHASER;
		ph->status=PHFREE;
#ifdef SHORT_PACKETS    /* S_P2 */
              if (send_short > 1 && (!(ph->status & 0xf0))) {
                        /* I hope this is good,we could test for */
                           /* send_short == 2, but then we must rewrite*/
                                   /* for a mode 3 */
                  phs->pnum = i;
                  phs->type = SP_S_PHASER;
                  phs->status = PHFREE;
                  sizes[SP_S_PHASER] = 4; /* Only the top bytes needed */
                  sendClientPacket (phs);
              }
              else
#endif 
		sendClientPacket(ph);
	    }
	} else {
	    if (phase->ph_status==PHHIT) {
		mustUpdate[phase->ph_target]=1;
	    }
	    if (ph->status!=phase->ph_status ||
		  ph->dir!=phase->ph_dir ||
		  ph->target!=htonl(phase->ph_target)) { 
		ph->pnum=i;
		ph->type=SP_PHASER;
		ph->status=phase->ph_status;
		ph->dir=phase->ph_dir;
		ph->x=htonl(phase->ph_x);
		ph->y=htonl(phase->ph_y);
		ph->target=htonl(phase->ph_target);
#ifdef SHORT_PACKETS /* S_P2 */
#if MAXPLAYER < 65
		if (send_short > 1 && (!(ph->status & 0xf0)) )
#else
		if (send_short > 1 && (!(ph->status & 0xf0)) && i < 64) 
#endif
                {
		unsigned char damage;
		
                switch(ph->status)
                {
                  case PHFREE:
                        phs->pnum = i;
                        phs->type = SP_S_PHASER;
                        phs->status = PHFREE;
                        sizes[SP_S_PHASER] = 4;
                        sendClientPacket (phs);
                        break;
                  case PHHIT:
			if(phase->ph_damage > 255)
				damage = 255; /* Could be used as flag */
			else
				damage = phase->ph_damage;
                        phs->pnum = (i & 0x3f)| (damage & 0x30) << 2 ;
                        phs->type = SP_S_PHASER;
                        phs->status = (unsigned char)ph->status |(damage & 0x0f) << 4;
                        phs->target = (phase->ph_target & 0x3f)|(damage & 192); 
                        sizes[SP_S_PHASER] = 4;
                        sendClientPacket (phs);
                        break;
                  case PHMISS:
                        phs->pnum = i;
                        phs->type = SP_S_PHASER;
                        phs->status = ph->status;
                        phs->target = ph->dir; /* Do not get confused */ 
                        sizes[SP_S_PHASER] = 4;
                        sendClientPacket (phs);
                        break;
                  case PHHIT2:
                        phs->pnum = i;
                        phs->type = SP_S_PHASER;
                        phs->status = ph->status;
                        phs->x = htons ((phase->ph_x+SCALE/2) / SCALE);
                        phs->y = htons ((phase->ph_y+SCALE/2) / SCALE);
                        phs->target = phase->ph_target; 
                        sizes[SP_S_PHASER]= 8;
                        sendClientPacket (phs);
                        break;
                  default:
                        phs->pnum = i;
                        phs->type = SP_S_PHASER;
                        phs->status = ph->status;
                        phs->dir = ph->dir;
                        phs->x = htons ((phase->ph_x+SCALE/2) / SCALE);
                        phs->y = htons ((phase->ph_y+SCALE/2) / SCALE);
                        phs->target = phase->ph_target; 
                        sizes[SP_S_PHASER]= sizeof(struct phaser_s_spacket);
                        sendClientPacket (phs);
                        break;
                }
                }
              else
#endif 
		sendClientPacket(ph);
	    }
	}
    }
}

updatePlanets()
{
    register int i;
    register struct planet_spacket *pl;
    register struct planet *plan;
    register struct planet_loc_spacket *pll;
#ifdef SHORT_PACKETS
	register struct planet_s_spacket *npl =  (struct planet_s_spacket *) &clientVPlanets[2];
	register int numplanets = 0;
#endif

#define PLFLAGMASK (PLREPAIR|PLFUEL|PLAGRI|PLHOME|PLCOUP)

    for (i=0, pl=clientPlanets, plan=planets, pll=clientPlanetLocs; 
	 i<MAXPLANETS;
	 i++, pl++, plan++, pll++) {
	/* Send him info about him not having info if he doesn't but thinks
	 *  he does.
	 * Also send him info on every fifth cycle if the planet needs to
	 *  be redrawn.
	 */
	if (((plan->pl_info & me->p_team)==0) && (pl->info & me->p_team)) {
	    pl->type=SP_PLANET;
	    pl->pnum=i;
	    pl->info=0;
#ifdef SHORT_PACKETS
            if ( send_short ) {
                npl->pnum = i;
                npl->info = 0;
                npl->armies = 0;
                npl->owner = 0;
                npl->flags = 0;
                npl++;
                numplanets++;
            }
            else
#endif
	    sendClientPacket(pl);
	} else 
	if (plan->pl_info & me->p_team &&
	    (pl->info != plan->pl_info ||
	     pl->armies != htonl(plan->pl_armies) ||
	     pl->owner != plan->pl_owner ||
	     pl->flags != htons((short) (plan->pl_flags & PLFLAGMASK)))) {
	    pl->type=SP_PLANET;
	    pl->pnum=i;
	    pl->info=plan->pl_info;
	    pl->flags=htons((short) (plan->pl_flags & PLFLAGMASK));
	    pl->armies=htonl(plan->pl_armies);
	    pl->owner=plan->pl_owner;
#ifdef SHORT_PACKETS
            if ( send_short  && plan->pl_armies < 256 ) {
                    npl->pnum=i;
                    npl->info=plan->pl_info;
                    npl->flags=htons((short) (plan->pl_flags & PLFLAGMASK));
                    npl->armies= (u_char) plan->pl_armies;
                    npl->owner=plan->pl_owner;
                    npl++;
                    numplanets++;
            }
            else
#endif
	    sendClientPacket(pl);
	} 
	/* Assume that the planet only needs to be updated once... */

	/* Odd, changes in pl_y not supported.  5/31/92 TC */

	if ((pll->x != htonl(plan->pl_x)) || (pll->y != htonl(plan->pl_y))) {
	    pll->x=htonl(plan->pl_x);
	    pll->y=htonl(plan->pl_y);
	    pll->pnum=i;
	    strcpy(pll->name, plan->pl_name);
	    pll->type=SP_PLANET_LOC;
	    sendClientPacket(pll);
	}
    }
#ifdef SHORT_PACKETS
/* Now we can send our Planet Packet */
        if (numplanets != 0) {
                clientVPlanetCount = 2 +(numplanets * 6);
                   /* make sure on machine word boundary (assuming 4 bytes per word) */
                                if((clientVPlanetCount % 4) != 0)
                                   clientVPlanetCount += (4 - (clientVPlanetCount % 4));

                sizes[SP_S_PLANET] = clientVPlanetCount;
                clientVPlanets[0] = SP_S_PLANET; /* The type */
                clientVPlanets[1] = numplanets;
                sendClientPacket((struct planet_spacket *) clientVPlanets);

        }
#endif
}

updateMessages()
{
    int i;
    struct message *cur;
    struct mesg_spacket msg;
    int updated=0;
    int reset_flags = 0;
#ifdef SHORT_PACKETS
    struct mesg_s_spacket *shortmsg = (struct mesg_s_spacket *) &msg;
    register int size = 0;
#endif
#ifdef RCD
    struct distress dist;
#endif

    for (i=msgCurrent; i!=(mctl->mc_current+1) % MAXMESSAGE; i=(i+1) % MAXMESSAGE) {
	if (i>=MAXMESSAGE) i=0;
	cur= &messages[i];

	if ((macroignore && cur->m_flags & MMACRO) 
	     || (dooshignore && cur->m_from == DOOSHMSG)) {
		msgCurrent=(msgCurrent+1) % MAXMESSAGE;
		continue;
	}

	if (cur->m_flags & MVALID &&
	    (cur->m_flags & MALL ||
	     (cur->m_flags & MTEAM && cur->m_recpt & me->p_team) ||
	     (cur->m_flags & MINDIV && cur->m_recpt == me->p_no) 
	    )) {	     

/* hack for displaying MINDIV && MCONQ messages to stdout on the clients.  
   Clients only show MALL && MCONQ.  but we don't want to send to all.
   So at this point, MINDIV has already directed it to the right guy.
   Now, cancel MINDIV and set MALL, for the client's benefit.
   ATH, 11/5/95 */

	  if ((cur->m_flags & MINDIV) && (cur->m_flags & MCONQ)) {
	    cur->m_flags &= ~MINDIV;
	    cur->m_flags |= MALL;
	    reset_flags = 1;
	  }

#ifdef SHORT_PACKETS
            if(send_short && (cur->m_from == 255)) { /* Test if possible to send with SP_S_WARNING */
                switch(cur->args[0]) {
                    case DMKILL:
                        if (cur->args[3] < 32000 && cur->args[4] < 32 && cur->args[1] < 64 && cur->args[2] < 64 ){
                                u_short tmp;

				tmp= cur->args[3] | 
					((cur->args[4] & 16) << 11);
                                swarning(KILLARGS,(u_char)(tmp & 0xff), (u_char)((tmp >> 8) & 0xff));
#ifdef CHAIN_REACTION
				if (why_dead) {
				    swarning(KILLARGS2,(u_char)cur->args[5],0);
				}
#endif
				tmp= (u_char)(cur->args[2] |
					((cur->args[4] & 12)<< 4));
                                swarning(DMKILL,(u_char)(cur->args[1] |((cur->args[4] & 3)<< 6)),tmp);
                                msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                                continue;
                        }
                        break;
                     case KILLARGS: /* Only to help the compiler */
                        break;
                     case KILLARGS2: /* Only to help the compiler */
			break;
                     case DMKILLP:
#ifdef CHAIN_REACTION
                        if (why_dead) {
                            swarning(KILLARGS2,(u_char)cur->args[5],0);
                        }
#endif
                        swarning(DMKILLP, (u_char)cur->args[1], (u_char)cur->args[2]);
                        msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                        continue;
                     case DMBOMB:
                        swarning(ARGUMENTS, (u_char)cur->args[2],0); /* Damage */
                        swarning(DMBOMB, (u_char)cur->args[1], (u_char)cur->args[3]); /* To get the vital info in one packet */
                        msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                        continue;
                     case DMDEST:
                        swarning(DMDEST, (u_char)cur->args[1], (u_char)cur->args[2]);
                        msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                        continue;
                     case DMTAKE:
                        swarning(DMTAKE, (u_char)cur->args[1], (u_char)cur->args[2]);
                        msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                        continue;
                     case DGHOSTKILL:
                        if(cur->args[2] < 64000){
                        swarning(KILLARGS,(u_char)(cur->args[2] & 0xff), (u_char)((cur->args[2] >> 8) & 0xff));
#ifdef CHAIN_REACTION
                        if (why_dead) {
                            swarning(KILLARGS2,(u_char)cur->args[5],0);
                        }
#endif
                        swarning(DGHOSTKILL, (u_char)cur->args[1], 0);
                        msgCurrent=(msgCurrent+1) % MAXMESSAGE;
                        continue;
                        }
                        break;
		     case SVALID:      /* We can send it over SP_S_MESSAGE HW */
                        break;
                     case SINVALID:    /* Can't be send over SP_S_MESSAGE */
                        break;

                     default:
                      /* I must test if the message from GOD has a real header (== "GOD->") */
                      /* This tests gets all messages from tools */
                        if (strstr(cur->m_data,"GOD->") != NULL)
                               cur->args[0] = SVALID; /* It has a real header */
                        break;
                }
            }
#endif

	    if (!updated) {
		update_sys_defaults();
		updated=1;
#ifdef CHECKMESG
	        if (logall) {
	            if(!mlog) mlog = fopen(MesgLog, "a");
                        if(!mlog) perror(MesgLog);
                }
                if(loggod){
                        if(!glog) glog = fopen(GodLog, "a");
                        if(!glog) perror(GodLog);
		}
#endif
	    }
#ifdef SHORT_PACKETS
	    if ( send_short && (cur->m_from < MAXPLAYER || cur->args[0] == SVALID)
                && (!(cur->m_flags & MLEAVE)) ) {
                 /* This is the SAFE version.(All playermessages must have an header.) It's nicer without MLEAVE */

                shortmsg->type = SP_S_MESSAGE;
                shortmsg->m_flags=cur->m_flags;
                shortmsg->m_recpt=cur->m_recpt;
                shortmsg->m_from=cur->m_from;
                cur->m_data[MSG_LEN-1]='\0'; /* Is this all right ? */
                strcpy(&shortmsg->mesg,&cur->m_data[9]); /* 9 because of GOD messages */
                /* Now the size */
                size = strlen(&shortmsg->mesg);
                size += 6; /* 1 for '\0', 5 packetheader */
                if((size % 4) != 0)
                                        size += (4 - (size % 4));
                shortmsg->length = size;
                sizes[SP_S_MESSAGE] = size;
            }
            else {
#endif
	    msg.type=SP_MESSAGE;
	    STRNCPY(msg.mesg, cur->m_data, MSG_LEN);
	    msg.mesg[MSG_LEN-1]='\0';
	    msg.m_flags=cur->m_flags;
	    msg.m_recpt=cur->m_recpt;
	    msg.m_from=cur->m_from;

#ifdef RCD
	/* kludge for clients that can't handle RCD's */
	/* if I understood heiko's short_packet code I would put this in
	the above ... but I dont - jmn */

	if ((cur->m_recpt == me->p_team) && !me->gen_distress && 
	    (cur->m_flags == (MTEAM | MDISTR | MVALID))) {
		char buf[MSG_LEN];

		buf[0] = '\0';
		msg.m_flags ^= MDISTR; /* get rid of MDISTR flag so client
					  isn't confused */
		HandleGenDistr(cur->m_data,cur->m_from,cur->m_recpt,&dist);
		makedistress(&dist,buf,distmacro[dist.distype].macro);
		/* note that we do NOT send the F0->FED part 
		   so strncat is fine */
		strcpy(msg.mesg, buf);
	}
#endif

#ifdef SHORT_PACKETS
            }
#endif
	      if (cur->m_from==DOOSHMSG) msg.m_from=255;	/* god */
	      if ((cur->m_from < 0) || (cur->m_from > MAXPLAYER))
		  sendClientPacket((CVOID) &msg);
	      else if (cur->m_flags & MALL && !(ignored[cur->m_from] & MALL))
		  sendClientPacket((CVOID) &msg);
	      else if (cur->m_flags & MTEAM && !(ignored[cur->m_from] & MTEAM)){
		  sendClientPacket((CVOID) &msg);
	      }
	      else if (cur->m_flags & MINDIV) {
		  int query = 0;

		  /* session stats now parsed here.  parseQuery == true */
		  /* means eat message 4/17/92 TC */
		  query = parseQuery(&msg);
		  if (!query) 
		        if (ignored[cur->m_from] & MINDIV)
			    bounce(cur->m_from,
			    "That player is currently ignoring you.");
		        else  {
			    sendClientPacket((CVOID) &msg);
		        }
	      }
	}
/* Put eject message back as MINDIV, so everyone doesn't get it later */
	if (reset_flags) {
	  cur->m_flags &= ~MALL;
	  cur->m_flags |= MINDIV;
	  reset_flags = 0;
	}
	msgCurrent=(msgCurrent+1) % MAXMESSAGE;
    }
}

sendQueuePacket(pos)
short pos;
{
    struct queue_spacket qPacket;

    qPacket.type=SP_QUEUE;
    qPacket.pos=htons(pos);
    sendClientPacket((CVOID) &qPacket);
    flushSockBuf();
}

sendClientPacket(packet)
/* Pick a random type for the packet */
struct player_spacket *packet;
{
    int 		orig_type;
    int 		size, issc;
    size_t 		t;
#ifdef MAYBE
    static int		oldstatus = POUTFIT;
#endif

    orig_type = packet->type;
/*    packet->type &= ~(0x40 | 0x80);	/* clear special flags */
    packet->type &= (char) 0x3f;    /* above doesn't work? 4/18/92 TC */
#ifdef MAYBE
    /*
     * If we're dead, dying, or just born, we definitely want the transmission
     * to get through (otherwise we can get stuck).  I don't think this will
     * be a problem for anybody, though it might hang for a bit if the TCP
     * connection is bad.
     */
    /* Okay, now I'm not so sure.  Whatever. */
    if (oldstatus != PALIVE || (me != NULL && me->p_status != PALIVE))
	orig_type = packet->type | 0x80;	/* pretend it's critical */
#endif
    if (packet->type<1 || packet->type>NUM_SIZES || 
	    sizes[(int)packet->type]==0) {
	ERROR(1,("Attempt to send strange packet %d  %d\n", packet->type,NUM_SIZES));
	return;
    }
    packetsSent[(int)packet->type]++;
    if (commMode == COMM_TCP || (commMode == COMM_UDP && udpMode == MODE_TCP)) {
	/*
	 * business as usual
	 */
	size=(int) sizes[(int)packet->type];
	if (bufptr-buf+size >= BUFSIZE) {
	    t=bufptr-buf;
	    if (gwrite(sock, buf, t) != t) {
		perror("std gwrite failed, client marked dead ");
		clientDead=1;
	    }
	    bufptr=buf;
	}
	MCOPY(packet, bufptr, size);
	bufptr+=size;

    } else {
	/*
	 * do UDP stuff unless it's a "critical" packet
	 * (note that both kinds get a sequence number appended) (FIX)
	 */
	issc = 0;
	switch (orig_type) {
	case SP_KILLS:
	case SP_TORP_INFO:
	case SP_PHASER:
	case SP_PLASMA_INFO:
	case SP_YOU|0x40:	/* ??? what is this? */
/*	case SP_STATUS:		S_P2 */
	case SP_PLANET:
	case SP_FLAGS:
	case SP_HOSTILE:
#ifdef SHORT_PACKETS
	case SP_S_YOU:
	case SP_S_PLANET:
        case SP_S_PHASER:       /* S_P2 */
	case SP_S_KILLS:	/* S_P2 */
#endif
	    /* these are semi-critical; flag as semi-critical and fall through*/
	    issc = 1;

#ifdef SHORT_PACKETS
	case SP_S_YOU_SS:
	case SP_S_PLAYER:
	case SP_S_TORP:
	case SP_S_TORP_INFO:
	case SP_S_8_TORP:
	case SP_S_STATS:	/* S_P2 */
#endif

	case SP_PLAYER:
	case SP_TORP:
	case SP_YOU:
	case SP_PLASMA:
	case SP_STATS:
/*	case SP_SCAN:*/
	case SP_STATUS:	/* S_P2 */
#ifdef PING
        case SP_PING:
#endif
	case SP_UDP_REPLY:	/* only reply when COMM_UDP is SWITCH_VERIFY */
	    /* these are non-critical updates; send them via UDP */
	    V_UDPDIAG(("Sending type %d\n", packet->type));
	    size=sizes[packet->type];
#ifdef PING
            packets_sent ++;
#endif
	    if (udpbufptr-udpbuf+size >= UDPBUFSIZE) {
		t=udpbufptr-udpbuf;
		if (gwrite(udpSock, udpbuf, t) != t) {
		    perror("UDP gwrite failed, client marked dead once more");
#ifdef EXTRA_GB
		    clientDead=1;
#endif
		    UDPDIAG(("*** UDP disconnected for %s\n", me->p_name));
                    printUdpInfo();
                    closeUdpConn();
                    commMode = COMM_TCP;
		}
#ifdef DOUBLE_UDP
		sendSC();	/* send semi-critical info, if needed */
#endif
		udpbufptr=udpbuf + addSequence(udpbuf);
	    }
	    MCOPY(packet, udpbufptr, size);
	    udpbufptr+=size;
#ifdef DOUBLE_UDP
	    if (issc && udpMode == MODE_DOUBLE) {
		MCOPY(packet, scbufptr, size);
		scbufptr+=size;
		V_UDPDIAG((" adding SC\n"));
	    }
#endif
	    if (issc && udpMode == MODE_FAT) {
		updateFat(packet);
	    }
	    break;

	default:
	    /* these are critical packets; send them via TCP */
	    size=sizes[packet->type];
	    if (bufptr-buf+size >= BUFSIZE) {
		t=bufptr-buf;
		if (gwrite(sock, buf, t) != t) {
		    perror("TCP gwrite failed, client marked dead");
		    clientDead=1;
		}
		bufptr=buf /*+ addSequence(buf)*/;
	    }
	    MCOPY(packet, bufptr, size);
	    bufptr+=size;
	    break;
	}
    }
}

/* Flush the socket buffer */
flushSockBuf()
{
    size_t t;

    if (clientDead){
	return;
    }
    if (bufptr != buf) {
	t = bufptr - buf; /* GCC 2.4.5 Optimizer Bug don't touch this */
	if (gwrite(sock, buf, t) != t) {
	    perror("flushSockBuf: gwrite failed, client marked dead");
	    clientDead=1;
	}
	bufptr=buf /*+ addSequence(buf)*/;
    }
    /*
     * This is where we try to add fat.  There's no point in checking at
     * the other places which call gwrite(), because they only call it when
     * the buffer is already full.
     */
    if (udpSock >= 0 && udpMode == MODE_FAT && (udpbufptr-udpbuf)<FAT_THRESH)
	fatten();
    if (udpSock >= 0 && udpbufptr != udpbuf) {
#ifdef BROKEN
	/* debugging only!! */
# ifdef HOSED
	if (sequence % 10) {
# else
	if (sequence % 5 == 0) {
# endif
	    /* act as if we did the gwrite(), but don't */
	    udpbufptr=udpbuf + addSequence(udpbuf);
	    goto foo;
	}
#endif
        t=udpbufptr-udpbuf;
	if (gwrite(udpSock, udpbuf, t) != t){
	    perror("UDP flush gwrite failed, client marked dead once more");
#ifdef EXTRA_GB
	    clientDead=1;
#endif
	    UDPDIAG(("*** UDP disconnected for %s\n", me->p_name));
	    printUdpInfo();
	    closeUdpConn();
	    commMode = COMM_TCP;
	}
#ifdef DOUBLE_UDP
	sendSC();
#endif
	udpbufptr=udpbuf + addSequence(udpbuf);
    }
#ifdef BROKEN
foo:
#endif
    if (udpMode == MODE_FAT)
	fatMerge();
}

socketPause()
{
    struct timeval timeout;
    fd_set readfds;

#ifdef PING
        /* this looks like an oversight here .. orig socketPause forgot to
           select on the udp socket as well */
#endif

    timeout.tv_sec=1;
    timeout.tv_usec=0;
    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);
    if (udpSock >= 0)
	FD_SET(udpSock, &readfds);

    select(32,&readfds,0,0,&timeout); 
}

/* Find out if client has any requests */
readFromClient()
{
    struct timeval timeout;
    fd_set readfds;
    int retval = 0;

    if (clientDead) return(0);
    timeout.tv_sec=0;
    timeout.tv_usec=0;
    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);
    if (udpSock >= 0)
	FD_SET(udpSock, &readfds);
    if (select(32,&readfds,0,0,&timeout) != 0) {
	/* Read info from the xtrek client */
	if (FD_ISSET(sock, &readfds)) {
	    retval += doRead(sock);
	}
	if (udpSock >= 0 && FD_ISSET(udpSock, &readfds)) {
	    V_UDPDIAG(("Activity on UDP socket\n"));
	    retval += doRead(udpSock);
	}
    }
    return (retval != 0);               /* convert to 1/0 */
}

#ifdef PING
static int      rsock;
#endif

/* ripped out of above routine */
doRead(asock)
int asock;
{
    struct timeval timeout;
/*    int readfds;*/
    fd_set readfds;
    char buf[BUFSIZ*2];
    char *bufptr;
    int size;
    int count;
    int temp;

#ifdef PING             /* need the socket in the ping handler routine */
    rsock = asock;
#endif

    timeout.tv_sec=0;
    timeout.tv_usec=0;
/*    readfds = 1<<asock;*/
    FD_ZERO(&readfds);
    FD_SET(asock, &readfds);
        /* Read info from the xtrek server */
        count=read(asock,buf,BUFSIZ*2);
        if (count<=0) {
#ifdef DEBUG
	    /* (this happens when the client hits 'Q') */
	    ERROR(1,( "1) read() failed in doRead (%d, error %d)\n",
		count, errno));
	    ERROR(1,( "asock=%d, sock=%d\n", asock, sock));
#endif
	    if (asock == udpSock) {
		if (errno == ECONNREFUSED) {
		    struct sockaddr_in addr;

		    UDPDIAG(("Hiccup(%d)!  Reconnecting\n", errno));
		    addr.sin_addr.s_addr = remoteaddr;
		    addr.sin_port = htons(udpClientPort);
		    addr.sin_family = AF_INET;
		    if (connect(udpSock, &addr, sizeof(addr)) < 0) {
			perror("hiccup connect");
			UDPDIAG(("Unable to reconnect\n"));
			/* and fall through to disconnect */
		    } else {
			UDPDIAG(("Reconnect successful\n"));
			return (0);
		    }
		}

		UDPDIAG(("*** UDP disconnected (res=%d, err=%d)\n",
			count, errno));
		printUdpInfo();
		closeUdpConn();
		commMode = COMM_TCP;
#ifdef notdef
		return(0);  /* I have large questions here -- tell client? */
#endif
	    }
	    clientDead=1;
	    return(0);
        }
        bufptr=buf;
        while (bufptr < buf+count) {
	    if (*bufptr < 1 || *bufptr > NUM_PACKETS || handlers[*bufptr].size==0) {
                ERROR(1,("Unknown packet type: %d, aborting...\n", *bufptr));
                return (0);
            }
	    size=handlers[*bufptr].size;
#ifdef SHORT_PACKETS
            if(size == -1){     /* variable packet */
               switch(*bufptr){
                   case CP_S_MESSAGE:
                        size =  ((char)bufptr[3]);
                        break;
                  default:
                     ERROR(1,("Unknown variable packet\n"));
                     exit(1);
                     break;
               }
               if((size % 4) != 0)
                  size += (4 - (size % 4));

            }
#endif
	    while (size>count+(buf-bufptr)) {
		/* We wait for up to twenty seconds for rest of packet.
                 * If we don't get it, we assume the client died. 
                 */
		timeout.tv_sec=20;
                timeout.tv_usec=0;
                /*readfds=1<<asock;*/
		FD_ZERO(&readfds);
		FD_SET(asock, &readfds);
                if (select(32,&readfds,0,0,&timeout) == 0) {
		    logmessage("Died while waiting for packet...");
		    ERROR(1,( "1a) read() failed (%d, error %d)\n",
			count, errno));
                    clientDead=1;
                    return(0);
                }

		temp=read(asock,buf+count,size-(count+(buf-bufptr)));
		count+=temp;
		if (temp<=0) {
		    sprintf(buf, "Died in second read(), return=%d", temp);
		    logmessage(buf);
		    ERROR(1,( "2) read() failed (%d, error %d)\n",
				count, errno));
		    clientDead=1;
		    return(0);
		}
	    }
	    /* Check to see if the handler is there and the request is legal.
	     * The code is a little ugly, but it isn't too bad to worry about
	     * yet.
	     */
	    packetsReceived[*bufptr]++;
#ifdef PING
            if(asock == udpSock)
               packets_received ++;
#endif
	    if (handlers[*bufptr].handler != NULL) {
		if (((FD_ISSET(*bufptr, &inputMask)) && 
		     (me==NULL || !(me->p_flags & (PFWAR|PFREFITTING
#ifdef SB_TRANSWARP
		      | PFTWARP
#endif
				)))) || 
		     *bufptr==CP_RESETSTATS || *bufptr==CP_UPDATES ||
		     *bufptr==CP_OPTIONS || *bufptr==CP_RESERVED ||
#ifdef PING 			/* ping response always valid */
                     *bufptr==CP_PING_RESPONSE ||
#endif
#ifdef RSA            		/* NEW -- fix ghostbust problem */
                     *bufptr== CP_RSA_KEY ||
#endif
#ifdef FEATURE_PACKETS
                     *bufptr == CP_FEATURE ||
#endif
#ifdef MESSAGES_ALL_TIME	/* off for the moment */
                     *bufptr == CP_MESSAGE ||
#ifdef SHORT_PACKETS
                     *bufptr == CP_S_MESSAGE ||
#endif
#endif

		     *bufptr==CP_SOCKET || *bufptr==CP_BYE) {
		    if (me && me->p_flags & PFSELFDEST
#ifdef PING /* don't let it undo self destruct */
                        && *bufptr != CP_PING_RESPONSE
#endif
			) {
			me->p_flags &= ~PFSELFDEST;
                        new_warning(85,"Self Destruct has been canceled");
		    }
		    (*(handlers[*bufptr].handler))(bufptr);
		}
		/* Otherwise we ignore the request */
	    } else {
		ERROR(1,("Handler for packet %d not installed...\n", *bufptr));
	    }
            bufptr+=size;
            if (bufptr>buf+BUFSIZ) {
                MCOPY(buf+BUFSIZ, buf, BUFSIZ);
                if (count==BUFSIZ*2) {
		    /*readfds = 1<<asock;*/
		    FD_ZERO(&readfds);
		    FD_SET(asock, &readfds);
		    if (select(32,&readfds,0,0,&timeout)) {
			temp=read(asock,buf+BUFSIZ,BUFSIZ);
			count=BUFSIZ+temp;
			if (temp<=0) {
			    sprintf(buf, "Died in third read(), return=%d", temp);
			    ERROR(1,( "3) read() failed (%d, error %d)\n",
				count, errno));
			    logmessage(buf);
			    clientDead=1;
			    return(0);
			}
		    } else {
			count=BUFSIZ;
		    }
                } else {
		    count -=BUFSIZ;
		}
                bufptr-=BUFSIZ;
            }
        }
        return(1);
}

handleTorpReq(packet)
struct torp_cpacket *packet;
{
    ntorp(packet->dir, TWOBBLE | TOWNERSAFE | TDETTEAMSAFE);
}

handlePhasReq(packet)
struct phaser_cpacket *packet;
{
    phaser(packet->dir);
}

handleSpeedReq(packet)
struct speed_cpacket *packet;
{
    set_speed(packet->speed);
}

handleDirReq(packet)
struct dir_cpacket *packet;
{
    me->p_flags &= ~(PFPLOCK | PFPLLOCK);
    set_course(packet->dir);
}

handleShieldReq(packet)
struct shield_cpacket *packet;
{
    if (packet->state) {
	shield_up();
    } else {
	shield_down();
    }
}

handleRepairReq(packet)
struct repair_cpacket *packet;
{
    if (packet->state) {
	repair();
    } else {
	me->p_flags &= ~(PFREPAIR);
    }
}

handleOrbitReq(packet)
struct orbit_cpacket *packet;
{
    if (packet->state) {
	orbit();
    } else {
	me->p_flags &= ~PFORBIT;
	if (me->p_flags & PFDOCK) {
	    players[me->p_docked].p_docked--;
	    players[me->p_docked].p_port[me->p_port[0]] = VACANT;
	    me->p_flags &= ~PFDOCK;
	}
    }
}

/*ARGSUSED*/
handlePractrReq(packet)
struct practr_cpacket *packet;
{
    register		i;
    register struct player	*j;

    for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) {
	if (j->p_status != PALIVE)
	    continue;
	if (j == me)
	    continue;

    /*
     * if we reached here there are other players in the game, therefore
     * practice_robo would fail.
     */

#ifdef SB_TRANSWARP
    if (twarpMode) {
	    handleTranswarp();
	    return;
	}
#endif
    }

     /*
      * if we reach here there are no other players in the game and we can
      * start a practice robot
      */

    practice_robo();
}

handleBombReq(packet)
struct bomb_cpacket *packet;
{
    if (packet->state) {
	bomb_planet();
    } else {
	me->p_flags &= ~(PFBOMB);
    }
}

handleBeamReq(packet)
struct beam_cpacket *packet;
{
    if (packet->state==1) {
	beam_up();
    } else if (packet->state) {
	beam_down();
    } else {
	me->p_flags &= ~(PFBEAMUP | PFBEAMDOWN);
    }
}

handleCloakReq(packet)
struct cloak_cpacket *packet;
{
    if (packet->state) {
	cloak_on();
    } else {
	cloak_off();
    }
}

/*ARGSUSED*/
handleDetTReq(packet)
struct det_torps_cpacket *packet;
{
    detothers();
}

/*ARGSUSED*/
handleCopilotReq(packet)
struct copilot_cpacket *packet;
{
/*
 * Unsupported...
    if (packet->state) {
	me->p_flags |= PFCOPILOT;
    } else {
	me->p_flags &= ~PFCOPILOT;
    }
 */
}

handleOutfit(packet)
struct outfit_cpacket *packet;
{
    shipPick=packet->ship;
    teamPick=packet->team;
}

sendPickokPacket(state)
int state;
{
    struct pickok_spacket pickPack;

    pickPack.type=SP_PICKOK;
    pickPack.state=state;
    sendClientPacket((CVOID) &pickPack);
}

handleLoginReq(packet)
struct login_cpacket *packet;
{
    
    STRNCPY(namePick, packet->name, NAME_LEN);
    namePick[NAME_LEN-1]=0;
    STRNCPY(passPick, packet->password, NAME_LEN);
    passPick[NAME_LEN-1]=0;
    /* Is this a name query or a login? */
    if (packet->query) {
	passPick[15]=1;
    }
    STRNCPY(login, packet->login, NAME_LEN);
    login[NAME_LEN-1]=0;
}

sendClientLogin(stats)
struct stats *stats;
{
    struct login_spacket logPacket;

    logPacket.type=SP_LOGIN;
    if (stats==NULL) {
	logPacket.accept=0;
    } else {
	logPacket.accept=1;
	logPacket.flags=htonl(stats->st_flags);
	MCOPY(stats->st_keymap, logPacket.keymap, 96);
    }
    sendClientPacket((CVOID) &logPacket);
}

handlePlasmaReq(packet)
struct plasma_cpacket *packet;
{
    nplasmatorp(packet->dir, 0);
}

handleWarReq(packet)
struct war_cpacket *packet;
{
    declare_war(packet->newmask);
}

handlePlanlockReq(packet)
struct planlock_cpacket *packet;
{
    lock_planet(packet->pnum);
}

handlePlaylockReq(packet)
struct playlock_cpacket *packet;
{
    lock_player(packet->pnum);
}

handleDetMReq(packet)
     struct det_mytorp_cpacket *packet;
{
  struct torp *t;
  int n = ntohs(packet->tnum);

  if ((n < 0) || (n >= MAXPLAYER*MAXTORP))
    return;
  t = torp_(n);
  if (t->t_owner != me->p_no) 
    return;

#ifdef SHORT_PACKETS
  if (send_short) {
    for (t = firstTorpOf(me); t <= lastTorpOf(me); t++) {
      if (t->t_status == TMOVE)
	t->t_status = TOFF;
    }
    return;
  }
  else
#endif
    if (t->t_status == TMOVE) {
      t->t_status = TOFF;
    }
}

handleTractorReq(packet)
struct tractor_cpacket *packet;
{
    int target;
    struct player *player;

    if (weaponsallowed[WP_TRACTOR]==0) {
        new_warning(0,"Tractor beams haven't been invented yet.");
	return;
    }
    target=packet->pnum;
    if (packet->state==0) {
	me->p_flags &= ~(PFTRACT | PFPRESS);
	return;
    }
    if (me->p_flags & PFCLOAK) {
        new_warning(1,"Weapons's Officer:  Cannot tractor while cloaked, sir!");
	return;
    }
    if (target<0 || target>=MAXPLAYER || target==me->p_no) return;
    player= &players[target];
    if (player->p_flags & PFCLOAK) return;
    if (hypot((double) me->p_x-player->p_x,
	    (double) me->p_y-player->p_y) < 
	    ((double) TRACTDIST) * me->p_ship.s_tractrng) {
	if (player->p_flags & PFDOCK) {
	    players[player->p_docked].p_port[player->p_port[0]] = VACANT;
	    players[player->p_docked].p_docked--;
	}
	if (me->p_flags & PFDOCK) {
	    players[me->p_docked].p_docked--;
	    players[me->p_docked].p_port[me->p_port[0]] = VACANT;
	}
	player->p_flags &= ~(PFORBIT | PFDOCK);
	me->p_flags &= ~(PFORBIT | PFDOCK);
	me->p_tractor = target;
	me->p_flags |= PFTRACT;
    } else {
        new_warning(2,"Weapon's Officer:  Vessel is out of range of our tractor beam.");
    }
}

handleRepressReq(packet)
struct repress_cpacket *packet;
{
    int target;
    struct player *player;

    if (weaponsallowed[WP_TRACTOR]==0) {
        new_warning(0,"Tractor beams haven't been invented yet.");
	return;
    }
    target=packet->pnum;
    if (packet->state==0) {
        me->p_flags &= ~(PFTRACT | PFPRESS);
        return;
    }
    if (me->p_flags & PFCLOAK) {
        new_warning(1,"Weapons's Officer:  Cannot tractor while cloaked, sir!");
	return;
    }
    if (target<0 || target>=MAXPLAYER || target==me->p_no) return;
    player= &players[target];
    if (player->p_flags & PFCLOAK) return;
    if (hypot((double) me->p_x-player->p_x,
	    (double) me->p_y-player->p_y) < 
	    ((double) TRACTDIST) * me->p_ship.s_tractrng) {
	if (player->p_flags & PFDOCK) {
	    players[player->p_docked].p_port[player->p_port[0]] = VACANT;
	    players[player->p_docked].p_docked--;
	}
	if (me->p_flags & PFDOCK) {
	    players[me->p_docked].p_docked--;
	    players[me->p_docked].p_port[me->p_port[0]] = VACANT;
	}
	player->p_flags &= ~(PFORBIT | PFDOCK);
	me->p_flags &= ~(PFORBIT | PFDOCK);
	me->p_tractor = target;
	me->p_flags |= (PFTRACT | PFPRESS);
    } else {
        new_warning(2,"Weapon's Officer:  Vessel is out of range of our tractor beam.");
    }
}

sendMotdLine(line)
char *line;
{
    struct motd_spacket motdPacket;
    int len;

/* Well it seems that we can really only send 79 characters for the
   motd. Seems kinda hosed...

   Added wraparound capability (mostly for the compiler options)  DRG Jun 93
*/
    if ((len = strlen(line)) < (MSG_LEN - 1)) { /* no need to wrap around */
       motdPacket.type=SP_MOTD;
       STRNCPY(motdPacket.line, line, MSG_LEN);
       motdPacket.line[MSG_LEN-1]='\0';
       sendClientPacket((CVOID) &motdPacket);
     }
     else {
        char dmy[MSG_LEN];
        char *ptr1,*ptr2;
        int i,j;
        dmy[MSG_LEN-1] ='\0';
        for (i=0;i<(int)(((float)(len)/(float)(MSG_LEN-1.0))+0.99);i++) {
           ptr1 = dmy; ptr2 = &(line[i*(MSG_LEN-1)]);
           for (j=0;j<MSG_LEN-1;j++) *ptr1++ = *ptr2++;
           motdPacket.type=SP_MOTD;
           STRNCPY(motdPacket.line, dmy, MSG_LEN);
           motdPacket.line[MSG_LEN-1]='\0';
           sendClientPacket((CVOID) &motdPacket);
	}
     }

}

/*ARGSUSED*/
handleCoupReq(packet)
struct coup_cpacket *packet;
{
    coup();
}

handleRefitReq(packet)
struct refit_cpacket *packet;
{
    do_refit(packet->ship);
}

#ifdef SHORT_PACKETS
handleSMessageReq(packet)
struct mesg_s_cpacket *packet;
{
/* If someone would delete the hardcoded things in handleMessageReq */
/*      like     packet->mesg[69]='\0';   */
/* we could give handleMessageReq the packet without copying */
/* But i have no time  HW 04/6/93 */

    struct mesg_cpacket mesPacket;

    mesPacket.type =  CP_MESSAGE;
    mesPacket.group = packet->group;
    mesPacket.indiv  = packet->indiv;
    STRNCPY(mesPacket.mesg, packet->mesg, MSG_LEN);
    mesPacket.mesg[MSG_LEN - 1] = '\0';
    handleMessageReq(& mesPacket);
/* I hope this was it */
}
#endif

handleMessageReq(packet)
struct mesg_cpacket *packet;
{
    char addrbuf[9];
    static time_t lasttime=0 /*, time()*/ ;
    static int balance=0;	/* make sure he doesn't get carried away */
    time_t thistime;
    int group=0;

    int parseIgnore();		/* still more code, 7/24/91 TC */

    /* Some random code to make sure the player doesn't get carried away 
     *  about the number of messages he sends.  After all, he could try to 
     *  jam peoples communications if we let him.
     */
    thistime=time(NULL);
    if (lasttime!=0) {
	balance=balance-(thistime-lasttime);
	if (balance<0) balance=0;
    }
    lasttime=thistime;
    if (balance>=15) {
        new_warning(86,"Be quiet");
	balance+=3;
	if (status->tourn)
	    balance+=3;
	if (balance > time(NULL) + 60) {
	    balance=time(NULL)+60;
	}
	return;
    }
    balance+=3;
    packet->mesg[MSG_LEN - 11]='\0';	/* 11 = 10(why?) - 1 for 0 placement */
    sprintf(addrbuf, " %c%c->", teamlet[me->p_team], shipnos[me->p_no]);
    if (parseIgnore(packet)) return; /* moved this up 4/6/92 TC */

    /* Here is where we handle special client macros. This lets us use more 
	bits in group! Yah! 	   */
    if (packet->group & FMMACRO) {
	if (!(packet->group & 0x40)) {		/* 0x40 + FMMACRO = MDISTR */
	    packet->group &= ~FMMACRO;
	    group = MMACRO;
	}
    }
    switch(packet->group) {
    case MALL:
	sprintf(addrbuf+5, "ALL");
	break;
    case (MTEAM|MDISTR):
    case MTEAM:
	if (packet->indiv != FED && packet->indiv != ROM &&
	    packet->indiv != KLI && packet->indiv != ORI) return;
	sprintf(addrbuf+5, teamshort[packet->indiv]);
	clueFuse = 0;
	break;
    case MINDIV:
	if (packet->indiv < 0 || packet->indiv >= MAXPLAYER) return;
	if (players[packet->indiv].p_status == PFREE) return;
	if (ignored[packet->indiv] & MINDIV) {
            new_warning(88,"You are ignoring that player.  Message was not sent.");
	    return;
	}
	sprintf(addrbuf+5, "%c%c ", teamlet[players[packet->indiv].p_team],
	    shipnos[packet->indiv]);
	break;
    case (MINDIV|MCONFIG):
	clientVersion(packet);
	break;
    case MGOD:
	sprintf(addrbuf+5, "GOD");
	break;
    default:
	return;
    }
    group |= packet->group;
    pmessage2(packet->indiv, group, addrbuf, me->p_no,"%s",packet->mesg);
#ifdef CHECKMESG
    if(checkmessage){
       if(check_mesgs(packet))
            return;
    }
#endif
    if ((clue) && (!clueVerified))
	check_clue(packet);
}

/*ARGSUSED*/
handleQuitReq(packet)
struct quit_cpacket *packet;
{
    me->p_flags |= PFSELFDEST;
    selfdest = me->p_updates + 100;
    /* 60 seconds to destruct a starbase */
    if (me->p_ship.s_type==STARBASE) selfdest = me->p_updates + 600;
    new_warning(90,"Self destruct initiated");
}

sendMaskPacket(mask)
int mask;
{
    struct mask_spacket maskPacket;

    maskPacket.type=SP_MASK;
    maskPacket.mask=mask;
    sendClientPacket((CVOID) &maskPacket);
}

handleOptionsPacket(packet)
struct options_cpacket *packet;
{
    MCOPY(packet->keymap, mystats->st_keymap, 96);
/*    mystats->st_flags = ntohl(packet->flags);*/
    mystats->st_flags = ntohl(packet->flags) |
	(mystats->st_flags & ST_CYBORG); /* hacked fix 8/24/91 TC */
    keeppeace=(mystats->st_flags / ST_KEEPPEACE) & 1;
}

handleSocketReq(packet)
struct socket_cpacket *packet;
{
    nextSocket=ntohl(packet->socket);
    userVersion=packet->version;
    userUdpVersion=packet->udp_version;
}

/*ARGSUSED*/
handleByeReq(packet)
struct bye_cpacket *packet;
{
    noressurect=1;
}

checkVersion()
{
    struct badversion_spacket packet;

    if (userVersion != SOCKVERSION) {
	packet.type=SP_BADVERSION;
	packet.why=0;
	sendClientPacket((CVOID) &packet);
	flushSockBuf();
	return(0);
    }
    return(1);
}

logEntry()
{
    FILE *logfile;
    time_t curtime;

    logfile=fopen(LogFileName, "a");
    if (!logfile) return;
    curtime=time(NULL);
    fprintf(logfile, "Joining: %s, (%c) %s\t<%s@%s>\n", me->p_name,
	    shipnos[me->p_no],
	    ctime(&curtime),
	    me->p_login, /* debug 2/21/92 TMC */
#ifndef FULL_HOSTNAMES
	    /* revised join log message, 4/13/92 TC */
	    me->p_monitor);
#else
	    me->p_full_hostname);
#endif
    fclose(logfile);
}

gwrite(fd, wbuf, size)
int fd;
char *wbuf;
size_t size;
{
    size_t orig = size;
    register int n;
    char tempbuf[MSG_LEN];
    register size_t bytes = size;
    register int count = 0;

    if (clientDead) return (0);

    if (bytes>BUFSIZE) {
	ERROR(1,("ERROR!!! gwrite got passed buf size of %d\n",bytes));
	return -1;
    }

    while (bytes>0) {
	n = write(fd, wbuf, bytes);
	if (count++ > 100) {
		ERROR(1,("Gwrite hosed: too many writes (%d)\n",getpid()));
		clientDead = 1;
		return (-1);
	}
	if (n < 0) {
	    if (errno==ENOBUFS) {
		/* The man pages don't mention this as a possibility.
		 * Yet, it happens.  I guess I just wait for 1/10 sec, and
		 *  continue?
		 */
		USLEEP(100000);
		errno = (-1);		/* paranoia */
		continue;
	    }
	    else if (errno==EINTR) {
		/* We got interrupted... Let's try it again. */
		perror("gwrite:");
		errno = (-1);
		n = 0;
	    }
	    else if (errno==EPIPE) {
		/* The pipe is broken: i.e. the client is dead */
		/* clientDead is marked outside of gwrite */
		return (-1);
	    }
	    else {
	    	perror("gwrite:");
		return (-1);
	    }
	    if (fd == udpSock) {
		/* do we want Hiccup code here? */
		UDPDIAG(("Tried to write %d, 0x%x, %d (error %d)\n",
			fd, wbuf, bytes, errno));
		printUdpInfo();
		logmessage("UDP gwrite failed:");
	    }
#ifndef FULL_HOSTNAMES
	    sprintf(tempbuf, "Died in gwrite, n=%d, errno=%d <%s@%s>",
		    n, errno, me->p_login, me->p_monitor);
#else
	    sprintf(tempbuf, "Died in gwrite, n=%d, errno=%d <%s@%s>",
		    n, errno, me->p_login, me->p_full_hostname);
#endif
	    logmessage(tempbuf);
	    return(-1);
	}
	bytes -= n;
	wbuf += n;
    }
    return(orig);
}

handleDockingReq(packet)
struct dockperm_cpacket *packet;
{
    int i;

    if (me->p_ship.s_type == STARBASE) {
	me->p_docked = 0;
	for (i=0; i<NUMPORTS; i++) {
	    if (me->p_port[i] != VACANT)
		players[me->p_port[i]].p_flags &= ~PFDOCK;
	    me->p_port[i] = VACANT;  
	}
	if (packet->state) 
	    me->p_flags |= PFDOCKOK;
	else
	    me->p_flags &= ~PFDOCKOK;
    }
}

handleReset(packet)
struct resetstats_cpacket *packet;
{
#ifndef lint
    extern int startTkills, startTlosses, startTarms, 
	       startTplanets, startTticks;
#endif
    if (packet->verify != 'Y') return;

    /* Gee, they seem to want to reset their stats!  Here goes... 
     */
    mystats->st_maxkills=0.0;
    mystats->st_kills=0;
    mystats->st_losses=0;
    mystats->st_armsbomb=0;
    mystats->st_planets=0;
    mystats->st_ticks=0;
    mystats->st_tkills=0;
    mystats->st_tlosses=0;
    mystats->st_tarmsbomb=0;
    mystats->st_tplanets=0;
    mystats->st_tticks=1;
    mystats->st_rank=0;
    mystats->st_sbkills=0;
    mystats->st_sblosses=0;
    mystats->st_sbticks=0;
    mystats->st_sbmaxkills=0.0;

#ifdef GENO_COUNT
    mystats->st_genos=0;
#endif

#ifndef lint
    startTkills = mystats->st_tkills;
    startTlosses = mystats->st_tlosses;
    startTarms = mystats->st_tarmsbomb;
    startTplanets = mystats->st_tplanets;
    startTticks = mystats->st_tticks;
#endif
}

handleUpdatesReq(packet)
struct updates_cpacket *packet;
{
    /* the client sends how often he would like to receive a packet
    /* in microseconds.  This is converted to an update rate */

    me->p_timerdelay = ntohl(packet->usecs) / 100000;

#ifdef FASTER_SB_MAXUPDATES
    if (me->p_ship.s_type != STARBASE)   /* Allow SB's 10 ups/sec */
#endif
      if (me->p_timerdelay < UPDATE_SKIP)  me->p_timerdelay = UPDATE_SKIP;
      if (me->p_timerdelay > UPDATE_MAXSKIP) me->p_timerdelay = UPDATE_MAXSKIP;

#ifdef SHORT_PACKETS
#ifdef SHORT_THRESHOLD
/* I need the number of updates for the threshold handling  HW 93 */
    numupdates = (int) (10 / me->p_timerdelay);
    if ( send_threshold != 0) { /* We need to recompute the threshold */
        actual_threshold = send_threshold / numupdates;
        if ( actual_threshold < 60 ) { /* my low value */
             actual_threshold = 60; /* means: 1 SP_S_PLAYER+SP_S_YOU+36 bytes */
             new_warning(UNDEF, "Threshold set to %d .  %d / Update(Server limit!)", numupdates * 60, 60);
        }
        else {
             new_warning(UNDEF, "Threshold set to %d .  %d / Update.",send_threshold , actual_threshold);
        }
    }
#endif
#endif

}

logmessage(string)
char *string;
{
    FILE *fp;

    fp=fopen(LogFileName, "a");
    if (fp) {
	fprintf(fp, "%s\n", string);
	fclose(fp);
    }
}

int clientVersion(packet)
struct mesg_spacket *packet;
{

        if (packet->mesg[0] != '@')
		return FALSE;

	version = (char *)strdup(INDEX(packet->mesg,'@')+1);
	version[strlen(version)] = '\0';
	return TRUE;
}


#ifdef RSA

handleReserved(packet)
struct reserved_cpacket *packet;
{
    struct reserved_cpacket mycp;
    struct reserved_spacket mysp;
    struct rsa_key_spacket   rsp;
    char serverName[64]; /* now get serverName from system 8/2/92 TC */

    if (testtime==1) return;
    if (MCMP(packet->data, testdata, RESERVED_SIZE) != 0) {
        testtime=1;
        return;
    }
    if (!strncmp (packet->resp, RSA_VERSION, 3)) {
          /* This is an RSA type client */
          RSA_Client = 2;
          new_warning (UNDEF,"%s",RSA_VERSION);
      if (!strncmp (packet->resp, RSA_VERSION, strlen("RSA v??"))) {
          /* This is the right major version */
          RSA_Client = 1;
          makeRSAPacket(&rsp);
          MCOPY(rsp.data, testdata, KEY_SIZE);
          sendClientPacket (&rsp);
          return;
      }
      testtime=1;
      return;
    }
    MCOPY(testdata, mysp.data, RESERVED_SIZE);
    if (gethostname(serverName, 64)) fprintf(stderr, "gethostname() error\n"); /* 8/2/92 TC */
    encryptReservedPacket(&mysp, &mycp, serverName, me->p_no);
    if (MCMP(packet->resp, mycp.resp, RESERVED_SIZE) != 0) {
        ERROR(3, ("User verified incorrectly.\n"));
        testtime=1;
        return;
    }
    /* Use .sysdef CONFIRM flag to allow old style clients. */
    if (binconfirm==2)
        testtime=0;
    else
        testtime=1;
}

handleRSAKey(packet)
struct rsa_key_cpacket *packet;
{
    struct rsa_key_cpacket mycp;
    struct rsa_key_spacket mysp;
    char serverName[64]; 

    if (testtime==1) return;
    if (RSA_Client != 1) return;
    MCOPY(testdata, mysp.data, KEY_SIZE);
    if (gethostname(serverName, 64)) ERROR(1,( "gethostname() error\n"));
    if (decryptRSAPacket(&mysp, packet, serverName))
    {
        ERROR(3,("User verified incorrectly.\n"));
        testtime=1;
        return;
    }
    testtime=0;
}


#else

handleReserved(packet)
struct reserved_cpacket *packet;
{
/*    char temp[20];*/
    struct reserved_cpacket mycp;
    struct reserved_spacket mysp;
    char serverName[64]; /* now get serverName from system 8/2/92 TC */

    if (testtime==1) return;
    if (MCMP(packet->data, testdata, 16) != 0) {
	testtime=1;
	return;
    }
    MCOPY(testdata, mysp.data, 16);
    if (gethostname(serverName, 64)) fprintf(stderr, "gethostname() error\n"); /* 8/2/92 TC */
    encryptReservedPacket(&mysp, &mycp, serverName, me->p_no);
    if (MCMP(packet->resp, mycp.resp, 16) != 0) {
	ERROR(3,("User verified incorrectly.\n"));
	testtime=1;
	return;
    }
    testtime=0;
}
#endif


/*ARGSUSED*/
handleScan(packet)		/* ATM */
struct scan_cpacket *packet;
{
/*
    struct scan_spacket response;
    struct player *pp;

    MZERO(&response, sizeof(struct scan_spacket));
    response.type = SP_SCAN;
    response.pnum = packet->pnum;
    if (!weaponsallowed[WP_SCANNER]) {
        new_warning(91,"Scanners haven't been invented yet");
	response.success = 0;
    } else {
	response.success = scan(packet->pnum);

	if (response.success) {
	    / fill in all the goodies /
	    pp = &players[packet->pnum];
	    response.p_fuel = htonl(pp->p_fuel);
	    response.p_armies = htonl(pp->p_armies);
	    response.p_shield = htonl(pp->p_shield);
	    response.p_damage = htonl(pp->p_damage);
	    response.p_etemp = htonl(pp->p_etemp);
	    response.p_wtemp = htonl(pp->p_wtemp);
	}
    }
    sendClientPacket((CVOID) &response);
*/
}

#ifdef PING
handlePingResponse(packet)

   struct ping_cpacket  *packet;
{
   /* client requests pings by sending pingme == 1 on TCP socket */

   if(rsock == sock){
      if(!ping && packet->pingme == 1){
         ping = 1;
#if PING_DEBUG >= 1
         ERROR(1,( "ping on for %s@%s\n", me->p_name, me->p_monitor));
         fflush(stderr);
#endif
	 if (ping_freq)
             new_warning(UNDEF, "Server sending PING packets at %d second intervals", ping_freq);
	 else 
	     new_warning(UNDEF, "Server sending PING packets on every update.");
         return;
      }
      /* client says stop */
      else if(ping && !packet->pingme){
         ping = 0;
#if PING_DEBUG >= 1
         ERROR(1,( "ping off for %s@%s\n", me->p_name, me->p_monitor));
         fflush(stderr);
#endif
         new_warning(UNDEF,"Server no longer sending PING packets.");
         return;
      }
   }
   pingResponse(packet);        /* ping.c */
}
#endif /*PING*/

#ifdef BASEPRACTICE
/* these are sent by the robots when a parameter changes */
int
handleOggV(packet)
    struct oggv_cpacket            *packet;
{
    me->p_df = packet->def;
    me->p_tg = packet->targ;

    if (me->p_df > 100)
	me->p_df = 100;

    me->p_flags |= PFBPROBOT;

#ifdef DEBUG
    printf("%c%c (%s) %d, targ: %d\n",
	me->p_mapchars[0], me->p_mapchars[1],
	me->p_name, me->p_df, me->p_tg - 1);
    fflush(stdout);
#endif
}
#endif                          /* BASEPRACTICE */

#ifdef FEATURE_PACKETS

handleFeature(cpacket)

   struct feature_cpacket       *cpacket;
{
   struct feature_spacket       spacket;

   F_client_feature_packets = 1;        /* XX */

   cpacket->value = ntohl(cpacket->value);
   getFeature(cpacket, &spacket);
   sendFeature(&spacket);
}

sendFeature(packet)

   struct feature_spacket       *packet;
{
   packet->type = SP_FEATURE;
   packet->value = htonl(packet->value);
   sendClientPacket((CVOID) packet);
}

#endif /* FEATURE_PACKETS */

/*
 * ---------------------------------------------------------------------------
 *	Strictly UDP from here on
 * ---------------------------------------------------------------------------
 */

handleUdpReq(packet)
struct udp_req_cpacket *packet;
{
    struct udp_reply_spacket response;
    int mode;

    response.type = SP_UDP_REPLY;

    if (packet->request == COMM_VERIFY) {
	/* this request should ONLY come through the UDP connection */
	if (commMode == COMM_UDP) {
	    UDPDIAG(("Got second verify from %s; resending server verify\n",
		me->p_name));
	    response.reply = SWITCH_VERIFY;
	    goto send;
	}
	UDPDIAG(("Receieved UDP verify from %s\n", me->p_name));
	UDPDIAG(("--- UDP connection established to %s\n", me->p_name));
#ifdef BROKEN
        new_warning(92,"WARNING: BROKEN mode is enabled");
#endif

	sequence = 1;		/* reset sequence numbers */
	commMode = COMM_UDP;	/* at last */
	udpMode = MODE_SIMPLE;	/* just send one at a time */
		
	/* note that we don't NEED to send a SWITCH_VERIFY packet; the client */
	/* will change state when it receives ANY packet on the UDP connection*/
	/* (this just makes sure that it gets one) */
	/* (update: recvfrom() currently tosses the first packet it gets...)  */
	response.reply = SWITCH_VERIFY;
	goto send;
/*	return;*/
    }

    if (packet->request == COMM_MODE) {
	/* wants to switch modes; mode is in "conmode" */
	mode = packet->connmode;
	if (mode < MODE_TCP || mode > MODE_DOUBLE) {
            new_warning(93,"Server can't do that UDP mode");
	    UDPDIAG(("Got bogus request for UDP mode %d from %s\n",
		mode, me->p_name));
	} else {
	    /* I don't bother with a reply, though it can mess up the opt win */
	    switch (mode) {
	    case MODE_TCP:
                new_warning(94,"Server will send with TCP only");
		break;
	    case MODE_SIMPLE:
                new_warning(95,"Server will send with simple UDP");
		break;
	    case MODE_FAT:
		new_warning(UNDEF,"Server will send with fat UDP; sent full update");
		V_UDPDIAG(("Sending full update to %s\n", me->p_name));
		forceUpdate();
		break;
#ifdef DOUBLE_UDP
	    case MODE_DOUBLE:
#ifdef SHORT_PACKETS
        if(send_short){
                swarning(TEXTE,96,0);
                   mode = MODE_SIMPLE;
                   break;
               }
#endif
		new_warning(UNDEF,"Server will send with double UDP");
		scbufptr=scbuf + sizeof(struct sc_sequence_spacket);
		break;
#else
	    case MODE_DOUBLE:
                new_warning(97,"Request for double UDP DENIED (set to simple)");
		mode = MODE_SIMPLE;
		break;
#endif /*DOUBLE_UDP*/
	    }

	    udpMode = mode;
	    UDPDIAG(("Switching %s to UDP mode %d\n", me->p_name, mode));
	}
	return;
    }

    if (packet->request == COMM_UPDATE) {
	/* client wants a FULL update */
	V_UDPDIAG(("Sending full update to %s\n", me->p_name));
	forceUpdate();

	return;
    }

    UDPDIAG(("Received request for %s mode from %s\n",
	(packet->request == COMM_TCP) ? "TCP" : "UDP", me->p_name));
    if (packet->request == commMode) {
	/* client asking to switch to current mode */
	if (commMode == COMM_UDP) {
	    /*
	     * client must be confused... whatever the cause, he obviously
	     * isn't connected to us, so we better drop out end and retry.
	     */
	    UDPDIAG(("Rcvd UDP req from %s while in UDP mode; dropping old\n",
		    me->p_name));
	    closeUdpConn();
	    commMode = COMM_TCP;
	    /* ...and fall thru to the UDP request handler */
	} else {
	    /*
	     * Again, client is confused.  This time there's no damage though.
	     * Just tell him that he succeeded.  Could also happen if the
	     * client tried to connect to our UDP socket but failed, and
	     * decided to back off.
	     */
	    UDPDIAG(("Rcvd TCP req from %s while in TCP mode\n", me->p_name));

	    response.reply = SWITCH_TCP_OK;
	    sendClientPacket((CVOID) &response);

	    if (udpSock >= 0) {
		closeUdpConn();
		UDPDIAG(("Closed UDP socket\n"));
	    }
	    return;
	}
    }

    /* okay, we have a request to change modes */
    if (packet->request == COMM_UDP) {
	udpClientPort = ntohl(packet->port);	/* where to connect to */
	if (!udpAllowed) {
	    UDPDIAG(("Rejected UDP request from %s\n", me->p_name));
	    response.reply = SWITCH_DENIED;
	    response.port = htons(0);
	    goto send;
	} else {
	    if (userUdpVersion != UDPVERSION) {
		new_warning(UNDEF, "Server UDP is v%.1f, client is v%.1f",
			(float) UDPVERSION / 10.0,
			(float) userUdpVersion / 10.0);
		UDPDIAG(("Server UDP (rejected %s)\n", me->p_name));
		response.reply = SWITCH_DENIED;
		response.port = htons(1);
		goto send;
	    }
	    if (udpSock >= 0) {
		/* we have a socket open, but the client doesn't seem aware */
		/* (probably because our UDP verify got lost down the line) */
		UDPDIAG(("Receieved second request from %s, reconnecting\n",
			me->p_name));
		closeUdpConn();
	    }
	    /* (note no openUdpConn(); we go straight to connect) */
	    if (connUdpConn() < 0) {
		response.reply = SWITCH_DENIED;
		response.port = 0;
		goto send;
	    }
	    UDPDIAG(("Connected UDP socket (%d:%d) for %s\n", udpSock,
		    udpLocalPort, me->p_name));

	    /* we are now connected to the client, but he's merely bound */
	    /* don't switch to UDP mode yet; wait until client connects */
	    response.reply = SWITCH_UDP_OK;
	    response.port = htonl(udpLocalPort);

	    UDPDIAG(("packet->connmode = %d\n", packet->connmode));
	    if (packet->connmode == CONNMODE_PORT) {
		/* send him our port # so he can connect to us */
		goto send;
	    } else {
		/* send him a packet; he'll get port from recvfrom() */
		if (gwrite(udpSock, &response, sizeof(response)) !=
							    sizeof(response)) {
		    perror("send UDP packet failed, client not marked dead");
		    UDPDIAG(("Attempt to send UDP packet failed; using alt\n"));
		}
		goto send;
	    }
	}
    } else if (packet->request == COMM_TCP) {
	closeUdpConn();
	commMode = COMM_TCP;
	response.reply = SWITCH_TCP_OK;
	response.port = 0;
	UDPDIAG(("Closed UDP socket for %s\n", me->p_name));
	goto send;
    } else {
	ERROR(1,( "ntserv: got weird UDP request (%d)\n",
		packet->request));
	return;
    }
send:
    sendClientPacket((CVOID) &response);
}


connUdpConn()
{
    struct sockaddr_in addr;
    int len;

    if (udpSock > 0) {
	ERROR(2,( "ntserv: tried to open udpSock twice\n"));
	return (0);	/* pretend we succeeded (this could be bad) */
    }
    if (udpbufptr != udpbuf) {
	udpbufptr = udpbuf;		/* clear out any old data */
	sequence--;			/* we just killed a sequence packet */
    }

    if ((udpSock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror("ntserv: unable to create DGRAM socket");
	return (-1);
    }

#ifdef UDP_FIX 			/* 15/6/93 SK UDP connection time out fix */
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = 0;

    if (bind(udpSock, &addr, sizeof(addr)) < 0) {
        perror("ntserv: cannot bind to local port");
        close(udpSock);
	udpSock = -1;
	return (-1);
	}

    /* determine what our port is */
	len = sizeof(addr);
        if (getsockname(udpSock, (struct sockaddr *)&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);
	}
	udpLocalPort = (int) ntohs(addr.sin_port);

    caddr.sin_family = AF_INET;
    caddr.sin_addr.s_addr = remoteaddr; /* addr of our client */
    caddr.sin_port = htons(udpClientPort);      /* client's port */
	    
        if (connect(udpSock, &caddr, sizeof(caddr)) < 0) {
#else

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = remoteaddr;	/* addr of our client */
    addr.sin_port = htons(udpClientPort);	/* client's port */

				
	if (connect(udpSock, &addr, sizeof(addr)) < 0) {
#endif
	perror("ntserv: connect to client UDP port");
	UDPDIAG(("Unable to connect() to %s on port %d\n", me->p_name,
		udpClientPort));
	close(udpSock);
	udpSock = -1;
	return (-1);
    }
    UDPDIAG(("connect to %s's port %d on 0x%x succeded\n",
	me->p_name, udpClientPort, remoteaddr));

    /* determine what our port is */
    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);
    }
    udpLocalPort = (int) ntohs(addr.sin_port);

    if (udpAllowed > 2)		/* verbose debug mode? */
	printUdpInfo();

    return (0);
}

closeUdpConn()
{
    V_UDPDIAG(("Closing UDP socket\n"));
    if (udpSock < 0) {
	ERROR(2,( "ntserv: tried to close a closed UDP socket\n"));
	return (-1);
    }
    shutdown(udpSock, 2);	/* wham */
    close(udpSock);		/* bam */
    udpSock = -1;		/* (nah) */

    return (0);
}

/* used for debugging */
printUdpInfo()
{
    struct sockaddr_in addr;
    int len;

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

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

handleSequence()
{
    /* we don't currently deal with sequence numbers from clients */
}

/*
 * If we're in UDP mode, add a sequence number to the transmission buffer.
 * Returns the #of bytes inserted.
 *
 * This will add a sequence # to transmissions on either channel.  However,
 * the current implementation doesn't put sequences on TCP transmissions
 * because mixed TCP packets and UDP packets rarely arrive in the order
 * in which they were sent.
 */
addSequence(outbuf)
char *outbuf;
{
    struct sequence_spacket *ssp;

    if (commMode != COMM_UDP || udpMode == MODE_TCP)
	return (0);

#ifdef PING             /* this needs to be counted */
    packets_sent ++;
#endif

    ssp = (struct sequence_spacket *) outbuf;
    ssp->type = SP_SEQUENCE;
    ssp->sequence = htons((u_short) sequence);
    ssp->pad1 = (((unsigned int)me->p_flags >> 8) & 0xff); /* S_P2 */
    sequence++;

    return (sizeof(struct sequence_spacket));
}


#ifdef DOUBLE_UDP
/*
 * If we're in double-UDP mode, then we need to send a separate semi-critical
 * transmission over UDP.  We need to give it the same sequence number as the
 * previous transmission, but the sequence packet will have type
 * SP_CP_SEQUENCE instead of SP_SEQUENCE.
 */
sendSC()
{
    struct sequence_spacket *ssp;
    struct sc_sequence_spacket *sc_sp;
    int cc;

    if (commMode != COMM_UDP || udpMode != MODE_DOUBLE) {
	/* mode not active, keep buffer clear */
	scbufptr = scbuf;
	return;
    }
    if (scbufptr - scbuf <= sizeof(struct sc_sequence_spacket)) {
	/* nothing to send */
	return;
    }

    /* copy sequence #, send what we got, then reset buffer */
    sc_sp = (struct sc_sequence_spacket *) scbuf;
    ssp = (struct sequence_spacket *) udpbuf;
    sc_sp->type = SP_SC_SEQUENCE;
    sc_sp->sequence = ssp->sequence;
    int t; t=scbufptr-scbuf;
    if ((cc = gwrite(udpSock, scbuf, t)) != t) {
	perror("UDP sc gwrite failed");
	UDPDIAG(("*** UDP dis-Connected for %s\n", me->p_name));
	printUdpInfo();
	closeUdpConn();
	commMode = COMM_TCP;
	return;
    }

    scbufptr=scbuf + sizeof(struct sc_sequence_spacket);
}
#endif

/*
 * This is a truncated version of initClientData().  Note that it doesn't
 * explicitly reset all the fat UDP stuff; sendClientData will take care of
 * that by itself eventually.
 *
 * Only semi-critical data is sent, with a few exceptions for non-critical
 * data which would be nice to update (stats, kills, player posn, etc).
 * The critical stuff can't be lost, so there's no point in resending it.
 *
 * (Since the fat data begins in an unqueued state, forceUpdate() should be
 * called immediately after switching to fat mode.  This guarantees that every
 * packet will end up on a queue.  The only real reason for doing this is so
 * that switching to fat mode will clear up your display and keep it cleared;
 * otherwise you could have torps floating around forever because the packet
 * for them isn't on a queue.  Will it reduce the effectiveness of fat UDP?
 * No, because as soon as the player hits the "update all" key it's gonna
 * happen anyway...)
 */
forceUpdate()
{
    static time_t lastone=0;
    time_t now;
    int i;

#ifdef SHORT_PACKETS

/* WE must do this or change the torp implementation ( It is now changed )*/
/* But it should be no problem because normally NO packet is send */
   if ( send_short) {
    for (i=0; i<MAXPLAYER*MAXTORP; i++) {
        clientTorpsInfo[i].status= TFREE; /* That's the trick */
    }
    /* And again */
    for (i=0; i<MAXPLAYER*MAXPLASMA; i++)
        clientPlasmasInfo[i].status= TFREE;
    for (i=0; i<MAXPLAYER; i++)
        clientPhasers[i].status = PHFREE;
   }
#endif

    now = time(0);
    if (now - lastone < UDP_UPDATE_WAIT) {
	new_warning(98,"Update request DENIED (chill out!)");
#ifdef SHORT_PACKETS
	spk_update_sall = 0; /* Back to default */
	spk_update_all = 0;
#endif
	return;
    }
    lastone = now;

/*    clientDead=0;*/
#ifdef SHORT_PACKETS
if ( !spk_update_sall ){
#endif 
    for (i=0; i<MAXPLAYER; i++) {
#ifdef SHORT_PACKETS
        if(spk_update_all && players[i].p_status == PFREE) continue; /* Do not u
pdate free Slots */
#endif
	clientHostile[i].hostile= -1;
#ifdef SHORT_PACKETS
        if(!spk_update_all) clientStats[i].losses= -1;
#else
	clientStats[i].losses= -1;		/* (non-critical, but nice) */
#endif
/*	clientLogin[i].rank= -1;		(critical) */
/*	clientPlayersInfo[i].shiptype= -1;	(critical) */
/*	clientPStatus[i].status= -1;		(critical) */
	clientPlayers[i].x= htonl(-1);		/* (non-critical, but nice) */
#ifdef SHORT_PACKETS
     if(!send_short) clientPhasers[i].status= -1;
#else
	clientPhasers[i].status= -1;
#endif
	clientKills[i].kills= htonl(-1);	/* (non-critical, but nice) */
	clientFlags[i].flags= htonl(-1);
	mustUpdate[i]=0;
    }
#ifdef SHORT_PACKETS
} /* For Player for */
if ( !send_short ){
#endif
    for (i=0; i<MAXPLAYER*MAXTORP; i++) {
	clientTorpsInfo[i].status= -1;
	clientTorps[i].x= -1;		/*	(non-critical) */
    }
    for (i=0; i<MAXPLAYER*MAXPLASMA; i++) {
	clientPlasmasInfo[i].status= -1;
/*	clientPlasmas[i].x= -1;			(non-critical) */
    }
#ifdef SHORT_PACKETS
}
#endif
    for (i=0; i<MAXPLANETS; i++) {
	clientPlanets[i].info |= me->p_team;
	clientPlanets[i].armies= htonl(-1);
/*	clientPlanetLocs[i].x= htonl(-1);	(critical) */
    }
/*    msgCurrent=(mctl->mc_current+1) % MAXMESSAGE;*/
    clientSelf.pnum= -1;
#ifdef SHORT_PACKETS
     if(spk_update_sall){       /* Small update */
        for (i=0; i<MAXPLAYER; i++) {
                if(players[i].p_status == PFREE) continue; /* Do not update free Slots */
                clientKills[i].kills= htonl(-1);        /* (non-critical, but ni
ce) */
                mustUpdate[i]=0;
        }
     }
    clientSelfShort.pnum = -1;
    clientSelfShip.damage = -1;
    spk_update_sall = 0; /* Back to default */
    spk_update_all = 0;
#endif
}

/*
 * ---------------------------------------------------------------------------
 *	Fat City
 * ---------------------------------------------------------------------------
 */

/*
 * This updates the "fat" tables; it's called from sendClientData().
 */
updateFat(packet)
/* Pick a random type for the packet */
struct player_spacket *packet;
{
    FAT_NODE *fatp;
    struct kills_spacket *kp;
    struct torp_info_spacket *tip;
    struct phaser_spacket *php;
#ifdef SHORT_PACKETS /* S_P2 */
  struct phaser_s_spacket *phps;
#endif 
    struct plasma_info_spacket *pip;
    /*struct you_spacket *yp;*/
    /*struct status_spacket *sp;*/
    struct planet_spacket *plp;
    struct flags_spacket *fp;
    struct hostile_spacket *hp;
    int idx;

    /* step 1 : find the FAT_NODE for this packet */
    switch (packet->type) {
    case SP_KILLS:
	kp = (struct kills_spacket *) packet;
	idx = (int) kp->pnum;
	fatp = &fat_kills[idx];
	break;
    case SP_TORP_INFO:
	tip = (struct torp_info_spacket *) packet;
	idx = (int) ntohs(tip->tnum);
	fatp = &fat_torp_info[idx];
	break;
    case SP_PHASER:
	php = (struct phaser_spacket *) packet;
	idx = (int) php->pnum;
	fatp = &fat_phaser[idx];
	break;
    case SP_PLASMA_INFO:
	pip = (struct plasma_info_spacket *) packet;
	idx = (int) ntohs(pip->pnum);
	fatp = &fat_plasma_info[idx];
	break;
    case SP_YOU:
	/*yp = (struct you_spacket *) packet;*/
	fatp = &fat_you;
	break;
    case SP_STATUS:
	/*sp = (struct status_spacket *) packet;*/
	fatp = &fat_status;
	break;
    case SP_PLANET:
	plp = (struct planet_spacket *) packet;
	idx = plp->pnum;
	fatp = &fat_planet[idx];
	break;
    case SP_FLAGS:
	fp = (struct flags_spacket *) packet;
	idx = (int) fp->pnum;
	fatp = &fat_flags[idx];
	break;
    case SP_HOSTILE:
	hp = (struct hostile_spacket *) packet;
	idx = (int) hp->pnum;
	fatp = &fat_hostile[idx];
	break;
#ifdef SHORT_PACKETS
    case SP_S_YOU:
      fatp = &fat_s_you;
      break;
    case SP_S_PLANET:
      fat_s_planet.pkt_size = sizes[SP_S_PLANET];
      fatp = &fat_s_planet;
      break;
    case SP_S_PHASER:   /* S_P2 */
      phps = (struct phaser_s_spacket *) packet;
      idx = (int) (phps->pnum & 0x3f); /* only 6 bits */
      fatp = &fat_s_phaser[idx];
      fatp->pkt_size = sizes[SP_S_PHASER]; /* there are 3 types */
      break; 
    case SP_S_KILLS:		/* S_P2 */
      fat_s_kills.pkt_size = sizes[SP_S_KILLS];
      fatp = &fat_s_kills;
      break;
#endif
    default:
	ERROR(1,("Fat error: bad semi-critical type in updateFat\n"));
	return;
    }

    if (fatp->packet != (PTR) packet) {
	ERROR(1,( "Fat error: fatp->packet=0x%.8lx, packet=0x%.8lx\n",
		fatp->packet, packet));
	return;
    }
    
    /* step 2 : move this dude to temporary list 0 */
    dequeue(fatp);
    enqueue(fatp, 0);
}

/*
 * This fattens up the transmission, adding up to MAX_FAT_DATA bytes.  The
 * packets which get added will be moved to a higher queue, giving them less
 * priority for next time.  Note that they are added to a parallel temporary
 * list (otherwise they'd could be sent several times in the same transmission
 * as the algorithm steps through the queues), and merged later on.
 *
 * Packets are assigned from head to tail, on a first-fit basis.  If a
 * semi-critical packet is larger than MAX_FAT_DATA, this routine will never
 * send it, but it will skip around it.
 *
 * This routine is called from flushSockBuf, before the transmission is sent.
 *
 * A possible improvement is to have certain packets "expire", never to be
 * seen again.  This way we don't keep resending torp packets for players who
 * are long dead.  This raises the possibility that the dead player's torps
 * will never go away though.
 */
fatten()
{
    int bytesleft;
    FAT_NODE *fatp, *nextfatp;
    int list;

#ifdef FATDIAG
    ERROR(1,("--- fattening\n"));
#endif
    bytesleft = MAX_FAT_DATA;
    for (list = 0; list < MAX_FAT_LIST; list++) {
	fatp = fatlist[list].head;
	while (fatp != NULL) {
	    nextfatp = fatp->prev;	/* move toward tail */
#ifdef FATDIAG
	    if (nextfatp == fatp) {
		ERROR(1,("Hey!  nextfatp == fatp!\n"));
		kill(getpid(), 15);
	    }
#endif
	    if (fatp->pkt_size < bytesleft) {
		/* got one! */
		MCOPY(fatp->packet, udpbufptr, fatp->pkt_size);
		udpbufptr += fatp->pkt_size;
		bytesleft -= fatp->pkt_size;

#ifdef PING     /* counts as a udp packet sent */
                packets_sent ++;
#endif

		/* move the packet to a higher queue (if there is one) */
		dequeue(fatp);
		if (list+1 == MAX_FAT_LIST) {
		    /*enqueue(fatp, list);*/	/* keep packets on high queue?*/
		} else {
		    enqueue(fatp, list+1);
		}

		/* done yet? */
		if (bytesleft < MAX_NONFAT)
		    goto done;		/* don't waste time searching anymore */
	    }

	    fatp = nextfatp;
	}
    }

done:
    /* at this point, we either filled with fat or ran out of queued packets */
#ifdef FATDIAG
    ERROR(1,("--- done\n"));
#endif
    V_UDPDIAG(("- Added %d grams of fat\n", MAX_FAT_DATA - bytesleft));
    return (0);		/* some compilers need something after a goto */
}


/*
 * Remove a FAT_NODE from a queue.  If it's at the head or the tail of a
 * list, then we need to figure out which list it's in and update the head
 * or tail pointer.  It's easier to go searching than to maintain a queue
 * number in every FAT_NODE.
 *
 * This routine looks too complex... there must be a simpler way to do this.
 */
dequeue(fatp)
FAT_NODE *fatp;
{
    int i;

#ifdef V_FATDIAG
    for (i = 0; i < MAX_FAT_LIST; i++) {
	ERROR(1,("fatlist[i].head = 0x%.8lx tail = 0x%.8lx\n",
		fatlist[i].head, fatlist[i].tail));
	if ((fatlist[i].head == NULL && fatlist[i].tail != NULL) ||
	    (fatlist[i].head != NULL && fatlist[i].tail == NULL)) {
	    ERROR(1,("before!\n"));
	    kill(getpid(), 15);
	}
    }
#endif


    if (fatp->next == NULL) {
	/* it's at the head or not in a queue */
	for (i = 0; i < MAX_FAT_LIST; i++) {
	    if (fatlist[i].head == fatp) {
		fatlist[i].head = fatp->prev;		/* move head back */
		if (fatlist[i].head != NULL)
		    (fatlist[i].head)->next = NULL;	/* amputate */
		break;
	    }
	}
    } else {
	/* it's not at the head */
	if (fatp->prev != NULL)
	    fatp->prev->next = fatp->next;
    }

    if (fatp->prev == NULL) {
	/* it's at the tail or not in a queue */
	for (i = 0; i < MAX_FAT_LIST; i++) {
	    if (fatlist[i].tail == fatp) {
		fatlist[i].tail = fatp->next;		/* move head fwd */
		if (fatlist[i].tail != NULL)
		    (fatlist[i].tail)->prev = NULL;	/* amputate */
		break;
	    }
	}
    } else {
	/* it's not at the tail */
	if (fatp->next != NULL)
	    fatp->next->prev = fatp->prev;
    }

#ifdef FATDIAG
    ERROR(1,("Removed 0x%.8lx...", fatp));		/* FATDIAG */
    for (i = 0; i < MAX_FAT_LIST; i++) {
	if ((fatlist[i].head == NULL && fatlist[i].tail != NULL) ||
	    (fatlist[i].head != NULL && fatlist[i].tail == NULL)) {
	    ERROR(1,("after: %d %.8lx %.8lx\n", i, fatlist[i].head, fatlist[i].tail));
	    kill(getpid(), 15);
	}
    }
#endif
    fatp->prev = NULL;
    fatp->next = NULL;
}

/*
 * Add a FAT_NODE to the tail of a temporary queue.  The merge() routine
 * merges the temporary queues with the fatlists once the transmission is
 * sent.
 */
enqueue(fatp, list)
FAT_NODE *fatp;
int list;
{
#ifdef FATDIAG
    ERROR(1,("added to tmplist %d\n", list));		/* FATDIAG */
#endif

    if (tmplist[list].tail == NULL) {
	/* list was empty */
	tmplist[list].tail = tmplist[list].head = fatp;
    } else {
	/* list wasn't empty */
	fatp->next = tmplist[list].tail;
	fatp->next->prev = fatp;
	tmplist[list].tail = fatp;
    }
}

/*
 * This gets called from flushSockBuf after the transmission is sent.  It
 * appends all the packets sitting in temporary queues to the corresponding
 * fat queues, where they will be eligible for fattening next transmission.
 */
fatMerge()
{
    int i;

    for (i = 0; i < MAX_FAT_LIST; i++) {
	if (tmplist[i].head == NULL)
	    continue;		/* temp list is empty, nothing to do */

	if (fatlist[i].head == NULL) {
	    /* fatlist is empty; just copy pointers */
	    fatlist[i].head = tmplist[i].head;
	    fatlist[i].tail = tmplist[i].tail;

	} else {
	    /* stuff in both */
	    (tmplist[i].head)->next = fatlist[i].tail;	/* fwd pointer */
	    (fatlist[i].tail)->prev = tmplist[i].head;	/* back pointer */
	    fatlist[i].tail = tmplist[i].tail;		/* move tail back */
	    tmplist[i].head = tmplist[i].tail = NULL;	/* clear the tmp list */
	}
	tmplist[i].head = tmplist[i].tail = NULL;
#ifdef FATDIAG
	ERROR(1,("merged list %d: %.8lx %.8lx\n", i, fatlist[i].head,
		fatlist[i].tail));
#endif
    }
}

#ifdef CHECKMESG
check_mesgs(packet)

   struct mesg_cpacket  *packet;
{
   static char buf[256];
   static char  addrb[10];
   /*
    * if 'logall' log message
    * if 'loggod' log messages to god or messages preceeded by "god:" ("GOD:")
    */

   if(logall| loggod){
      time_t 	t = time(NULL);
      char      tbuf[40];
      struct tm *tmv = localtime(&t);

#ifdef STRFTIME
      sprintf(tbuf, "%02d/%02d %d/%d", tmv->tm_hour,tmv->tm_min,
      tmv->tm_mday,tmv->tm_mon);
#else
      strftime(tbuf, 39, "%R %D", tmv);
#endif
      sprintf(buf, "%s %s@%s \"%s\"", tbuf, me->p_login, me->p_monitor,
         packet->mesg);
      if(logall){
         if(!mlog) {
            ERROR(1,( "ntserv: ERROR, null mlog file descriptor\n"));
            return 0;
         }
         fprintf(mlog, "%s\n", buf);
         fflush(mlog);
      }
      if(loggod && ((strncasecmp(packet->mesg, "god:", 4) == 0) ||
		    (packet->group & MGOD))){
         if(!glog) {
            ERROR(1,( "ntserv: ERROR, null glog file descriptor\n"));
            return 0;
         }
         fprintf(glog, "%s\n", buf);
         fflush(glog);
         /* response */
         sprintf(addrb, "GOD->%c%c", teamlet[me->p_team], shipnos[me->p_no]);
         pmessage(me->p_no, MINDIV, addrb, "Thank you, %s, for your comments.", 
		me->p_login);
         pmessage(me->p_no, MINDIV, addrb, "Your message has been logged.");
      }
   }
   return 0;    /* continue processing all messages */
}

/* added for clue checking    NBT */

check_clue(packet)

   struct mesg_cpacket  *packet;
{
   char player[10];

   if ((packet->group == MINDIV) && (packet->indiv == me->p_no)) {
       if (!strcmp(packet->mesg,clueString)) {
           sprintf(player, "GOD->%c%c", teamlet[me->p_team], shipnos[me->p_no]);
           clueVerified=1;
	   clueFuse=0;
	   clueCount=0;
           pmessage(me->p_no, MINDIV, player, 
		"Thank you, %s, you have been verified as possible clue.",
               me->p_name);
       }
   }
}
#endif /* CHECKMESG */


/* return true if you should eat message */

int parseIgnore(packet)
struct mesg_cpacket *packet;
{
    char *s;
    int who;
    int what;
    int noneflag;
    extern int ignored[];


/*    if (packet->indiv != me->p_no) return FALSE;*/

    s = packet->mesg;

    who = packet->indiv;
    if ((*s != ':') && (strcmp(s, "     ") != 0)) return FALSE;
    if ((who == me->p_no) && (*(s+1) == 'D')) {		/* NBT */
	if (dooshignore) {
	    new_warning(UNDEF,"Accepting Doosh messages.");
	    dooshignore=0;
	} else  {
	    new_warning(UNDEF,"Ignoring Doosh messages.");
	    dooshignore=1;
	}
	return TRUE;
    }
    else if ((who == me->p_no) && (*(s+1) == 'M')) {         /* NBT */
        if (macroignore) {
            new_warning(UNDEF,"Accepting multi-line macros.");
            macroignore=0;
        } else  {
            new_warning(UNDEF,"Ignoring multi-line macros.");
            macroignore=1;
        }
        return TRUE;
    }
    if ((who == me->p_no) || (*s == ' ')) { /* check for borg call 4/6/92 TC */
	if (binconfirm)
	    new_warning(UNDEF,"No cyborgs allowed in the game at this time.");
	else {
	    char buf[MSG_LEN]; char buf2[5];
	    int i; int cybflag = 0;

	    strcpy(buf, "Possible cyborgs: ");
	    for (i = 0; i < MAXPLAYER; i++)
		if ((players[i].p_status != PFREE) &&
		    (players[i].p_stats.st_flags & ST_CYBORG)) {
		    sprintf(buf2, "%s ", players[i].p_mapchars);
		    strcat(buf, buf2);
		    cybflag = 1;
		}
	    if (!cybflag) strcat(buf, "None");
	    new_warning(UNDEF,buf);
	}
	if (*s != ' ')		/* if not a borg call, eat msg 4/6/92 TC */
	    return TRUE;
	else
	    return FALSE;	/* otherwise, send it 4/6/92 TC */
    }

    if (packet->group != MINDIV) return FALSE; /* below is for indiv only */

    do {
	what = 0;
	switch (*(++s)) {
	  case 'a':
	  case 'A':
	    what = MALL;
	    break;
	  case 't':
	  case 'T':
	    what = MTEAM;
	    break;
	  case 'i':
	  case 'I':
	    what = MINDIV;
	    break;
	  case '\0':
	    what = 0;
	    break;
	  default:
	    what = 0;
	    break;
	}
	ignored[who] ^= what;
    } while (what != 0);

    strcpy(buf, "Ignore status for this player: ");
    noneflag = TRUE;
    if (ignored[who] & MALL) {
	strcat(buf, "All "); noneflag = FALSE;
    }
    if (ignored[who] & MTEAM) {
	strcat(buf, "Team "); noneflag = FALSE;
    }
    if (ignored[who] & MINDIV) {
	strcat(buf, "Indiv "); noneflag = FALSE;
    }
    if (noneflag)
	strcat(buf, "None");
    new_warning(UNDEF,buf);
    return TRUE;
}

/* give session stats if you send yourself a '?' 2/27/92 TC */
/* return true if you should eat message */

int parseQuery(packet)
struct mesg_spacket *packet;	/* was cpacket 4/17/92 TC */
{
   
    /* 0-8 for address, 9 is space */
    char *cchar;
    extern int bounceSessionStats();
    extern int bouncePingStats();
    extern int bounceRSAClientType();
    extern int bounceSBStats();

#ifdef SHORT_PACKETS
    if (packet->type == SP_S_MESSAGE)
      cchar = &packet->mesg[2];
    else
#endif
      cchar = &packet->mesg[10];

    if (*cchar == '?' && *(cchar+1) == '\0') 
      return bounceSessionStats(packet->m_from);
#ifdef PING
    if (*cchar == '!' && *(cchar+1) == '\0')
      return bouncePingStats(packet->m_from);
#endif
#ifdef RSA
    if (*cchar == '#' && *(cchar+1) == '\0')
      return bounceRSAClientType(packet->m_from);
#endif
    if (*cchar == '^' && *(cchar+1) == '\0')
      return bounceSBStats(packet->m_from);


    /* DEBUG */
    if (strcmp(cchar, "debug")==0){
      char      buf[80];
      sprintf(buf, "dumpshmem > %s.dump.%d", login, getpid());
      if(fork() == 0){
         signal(SIGALRM, SIG_DFL);
         execl("/bin/sh", "sh", "-c", buf, NULL);
         perror("execl");
	 ERROR(1,("Another socket exit"));
         _exit(1);
      }
    }
    return FALSE;
}

#ifdef SHORT_PACKETS

handleShortReq(packet)

   struct shortreq_cpacket	*packet;
{
   struct shortreply_spacket	resp;

   switch(packet->req){
      case SPK_VOFF:
	 send_short = 0;
	 new_warning(UNDEF,"Not sending variable and short packets.  Back to default.");
	 if (udpSock >= 0 && udpMode == MODE_FAT) forceUpdate();
	 break;

      case SPK_VON:
	  if ( packet->version != (char)SHORTVERSION && 
	       packet->version != (char)OLDSHORTVERSION){
		new_warning(UNDEF,"Your SHORT Protocol Version is not right!");
		packet->req = SPK_VOFF;
		break;		
	}
	 if (!send_short) 
		new_warning(UNDEF,"Sending variable and short packets. "); /* sned only firsttime */
         if (packet->version == (char) SHORTVERSION)
        	send_short = 2;  /* New packets are sent S_P2 */
         else
	 	send_short = 1; /* S_P version 1.0 used */
	 resp.winside = htons(WINSIDE);
	 resp.gwidth  = htonl(GWIDTH);
	 break;

      case SPK_MOFF:
	 send_mesg = 1;
	 new_warning(UNDEF,"Obsolete!");
	 packet->req = SPK_MON;
	 break;
      
      case SPK_MON:
	 send_mesg = 1;
	 new_warning(UNDEF,"All messages sent.");
	 break;

      case SPK_M_KILLS:
	 send_kmesg = 1;
	 new_warning(UNDEF,"Kill messages sent");
	 break;
      
      case SPK_M_NOKILLS:
	 send_kmesg = 1;
	 new_warning(UNDEF,"Obsolete!");
                 packet->req = SPK_M_KILLS;
	 break;
      
      case SPK_M_WARN:
	 send_warn = 1;
	 new_warning(UNDEF,"Warn messages sent");
	 break;
      
      case SPK_M_NOWARN:
	 send_warn = 1;
	 new_warning(UNDEF,"Obsolete!");
                 packet->req = SPK_M_WARN;	
	 break;

      case SPK_SALL:
	if(send_short){
	spk_update_sall = 1;
	spk_update_all = 0;
	forceUpdate();
	}
	else 
	    new_warning(UNDEF,"Activate SHORT Packets first!");
	return;

      case SPK_ALL:
	if(send_short){
	spk_update_sall = 0;
	spk_update_all = 1;
	forceUpdate();
	}
	else 
	    new_warning(UNDEF,"Activate SHORT Packets first!");
	return;
	 
      default:
	 new_warning(UNDEF,"Unknown short packet code");
	 return;
   }
   
   resp.type = SP_S_REPLY;
   resp.repl = (char) packet->req;

   sendClientPacket((struct player_spacket *) &resp);
}

handleThresh(packet)
   
   struct threshold_cpacket	*packet;
{
   send_threshold = packet->thresh;
#ifdef SHORT_THRESHOLD
   if (  send_threshold == 0) {
	actual_threshold = 0;
   	new_warning(UNDEF,"Threshold test deactivated.");
   }
   else {
	actual_threshold = send_threshold / numupdates;
	if ( actual_threshold < 60 ) { /* my low value */
		actual_threshold = 60; /* means: 1 SP_S_PLAYER+SP_S_YOU + 16 bytes */
		new_warning(UNDEF, "Threshold set to %d .  %d / Update(Server limit!)", numupdates * 60, 60);
	}
	else {
		new_warning(UNDEF, "Threshold set to %d .  %d / Update",send_threshold , actual_threshold);
	}
   }
#else
	new_warning(UNDEF,"Server is compiled without Thresholdtesting!");
#endif
}

updateVPlayer(p)

   struct player_spacket *p;
{
   register int		i = clientVPlayerCount; 
#if MAXPLAYER > 32
   register int		j = clientVXPlayerCount;
#endif
   register U_LONG	x = ntohl(p->x), y = ntohl(p->y);
   register int 	dir,dx,dy;
   int     		view = SCALE * WINSIDE / 2;

	if (p->pnum == me->p_no){
		if ( send_short > 1) { /* S_P2 */
		struct player_s2_spacket  *pl = (struct player_s2_spacket  *) &clientVPlayers[0];
		pl->x= htons((x+SCALE/2)/SCALE) /* p->x*/ ;
		pl->y = htons((y+SCALE/2)/SCALE) /*p->y*/;
		pl->speed = p->speed;
		pl->dir = p->dir;
		big = 1; /* Flag to remind me that we must send big header */
		}
		else {
		struct player_s_spacket  *pl = (struct player_s_spacket  *) &clientVPlayers[0];
		pl->x= htonl(x)/* p->x*/ ;
		pl->y = htonl(y) /*p->y*/;
		pl->speed = p->speed;
		pl->dir = p->dir;
		big = 1; /* Flag to remind me that we must send big header */
		}
	}
	else if ( p->pnum < 32){
		clientVPlayers[1] += 1; /* Another packet... */
		/* Where is the ship? On local or galactic map. */
		dx = x - me->p_x;
		dy = y - me->p_y;
		if (dx > view || dx < -view || dy > view || dy < -view) { /* Galactic */
		    dx = x * WINSIDE / GWIDTH; /* Orig. */
		    dy = y * WINSIDE / GWIDTH;
		    if ( dx < 0 || dy < 0 || dx >= WINSIDE || dy >= WINSIDE) {
			dx = 501; /* clipped */
			dy = 501;
		    }
		    dir = rosette (p->dir);
		    clientVPlayers[i++] = (u_char) ((p->pnum)|((dx  & 256) >> 2)|((dy & 256)>>1) | 32);
		    clientVPlayers[i++] = ( u_char)(( p->speed & 0x0f)|(dir << 4));
		    clientVPlayers[i++] = (u_char) (dx & 0xff);
		    clientVPlayers[i++] = (u_char) ( dy & 0xff);
		    clientVPlayerCount = i;		    
		}
		else {   /* Local */
		    /*
		    dx = dx / SCALE + WINSIDE / 2;
		    dy = dy / SCALE + WINSIDE / 2;
                    */

		    /* NEW -- fix precision loss -tsh */
		    dx = x/SCALE - me->p_x/SCALE + WINSIDE/2;
		    dy = y/SCALE - me->p_y/SCALE + WINSIDE/2;

		    dir = rosette (p->dir);
		    clientVPlayers[i++] = (u_char) ((p->pnum)|((dx  & 256) >> 2)|((dy & 256)>>1) );
		    clientVPlayers[i++] = ( u_char)(( p->speed & 0x0f)|(dir << 4));
		    clientVPlayers[i++] = (u_char) (dx & 0xff);
		    clientVPlayers[i++] = (u_char) ( dy & 0xff);
		    clientVPlayerCount = i;		
		}
	}
#if MAXPLAYER > 32
	else if ( p->pnum < 64){
		clientVXPlayers[1] += 1;
		/* Where is the ship? On local or galactic map. */
		dx = x - me->p_x;
		dy = y - me->p_y;
		if (dx > view || dx < -view || dy > view || dy < -view) { /* Galactic */
		    dx = x * WINSIDE / GWIDTH; /* Orig. */
		    dy = y * WINSIDE / GWIDTH;
		    if ( dx < 0 || dy < 0 || dx >= WINSIDE || dy >= WINSIDE) {
			dx = 501; /* clipped */
			dy = 501;
		    }
		    dir = rosette (p->dir);
		    clientVXPlayers[j++] = (u_char) ((p->pnum - 32)|((dx  & 256) >> 2)|((dy & 256)>>1) | 32);
		    clientVXPlayers[j++] = (u_char)(( p->speed & 0x0f)|(dir << 4));
		    clientVXPlayers[j++] = (u_char) (dx & 0xff);
		    clientVXPlayers[j++] = (u_char) ( dy & 0xff);
		    clientVXPlayerCount = j;		    
		}
		else {   /* Local */
		    dx = dx / SCALE + WINSIDE / 2;
		    dy = dy / SCALE + WINSIDE / 2;
		    dir = rosette (p->dir);
		    clientVXPlayers[j++] = (u_char) ((p->pnum-32)|((dx  & 256) >> 2)|((dy & 256)>>1) );
		    clientVXPlayers[j++] = (u_char)(( p->speed & 0x0f)|(dir << 4));
		    clientVXPlayers[j++] = (u_char) (dx & 0xff);
		    clientVXPlayers[j++] = (u_char) ( dy & 0xff);
		    clientVXPlayerCount = j;
		}
	}
#endif
	else  sendClientPacket((struct player_spacket *) p);

}

sendVPlayers()
{
	unsigned int size,i,j,new;
    register struct player *pl;
   /* initialize type & length fields */
   clientVPlayers[0] = (u_char) SP_S_PLAYER;
#if MAXPLAYER > 32
   clientVXPlayers[0] = (u_char) SP_S_PLAYER;
#endif


	if ( big != 0) {	/* Big Packet */
		if ( send_short > 1) { /* S_P2 */
		struct player_s2_spacket  *pa = (struct player_s2_spacket  *) &clientVPlayers[0];
		pa->flags = new = 0;
		
		for(i=0,j=0; i < 16 && i < MAXPLAYER;i++ , j += 2) {
		    new = new | ((unsigned int)n_flags[i] << j); 	
		} /* for */
		    pa->flags = htonl(new);
		    size = 12+(clientVPlayers[1]*4);
		sizes[SP_S_PLAYER] = size;
	        sendClientPacket((struct player_spacket *) clientVPlayers);	
		if(highest_active_player > 15 && highest_active_player < 32) {
              	    struct top_flags *ptr = (struct top_flags *) &clientVPlayers[4];
		    new = 0;
                  for(i=16,j=0; i < 32 && i < MAXPLAYER; i++, j +=2) {
                        new = new | ((unsigned int)n_flags[i] << j);
                  } /* for */
                 ptr->tflags = htonl(new);
                 ptr->type   = (u_char) SP_S_PLAYER;
                 ptr->packets = (unsigned char) 64;
                 ptr->numflags = 1;
                 ptr->index    = 1;
                 size = 8; 
                 sizes[SP_S_PLAYER] = size;
                 sendClientPacket((struct player_spacket *) &clientVPlayers[4]);
		}
		} /* if */
		else {
		size = 12+( clientVPlayers[1]*4);
		sizes[SP_S_PLAYER] = size;
		sendClientPacket((struct player_spacket *) clientVPlayers);
		}
	}
	else if ( clientVPlayerCount > 12){ /* Small packet */

		if (send_short > 1) { /* S_P2 */
		if(highest_active_player > 15 && highest_active_player < 32) {
                struct top_flags *ptr = (struct top_flags *) &clientVPlayers[0];
		new = 0;
                size = 12 + ( clientVPlayers[1]*4);
                  for(i=16,j=0; i < 32 && i < MAXPLAYER; i++, j +=2) {
                        new = new | ((unsigned int)n_flags[i] << j);
                  } /* for */
		ptr->tflags = htonl(new);
		new=0;
                for(i=0,j=0; i < 16 && i < MAXPLAYER;i++ , j += 2) {
                    new = new | ((unsigned int)n_flags[i] << j);
                } /* for */
                 ptr->tflags2 = htonl(new);
                 ptr->type   = (u_char) SP_S_PLAYER;
                 ptr->packets = (unsigned char) clientVPlayers[1] | 64;
                 ptr->numflags = 2;
                 ptr->index    = 1;
                 sizes[SP_S_PLAYER] = size;
                 sendClientPacket((struct player_spacket *) &clientVPlayers[0]);
		}
		else { /* No players > 15 */
               struct top_flags *ptr = (struct top_flags *) &clientVPlayers[4];
               new = 0;
                 size = 8 + ( clientVPlayers[1]*4);
                  for(i=0,j=0; i < 16 && i < MAXPLAYER; i++, j +=2) {
                        new = new | ((unsigned int)n_flags[i] << j);
                  } /* for */
                 ptr->tflags = htonl(new);
                 ptr->type   = (u_char) SP_S_PLAYER;
                 ptr->packets = (unsigned char) clientVPlayers[1] | 64;
                 ptr->numflags = 1;
                 ptr->index    = 0;
                 sizes[SP_S_PLAYER] = size;
                 sendClientPacket((struct player_spacket *) &clientVPlayers[4]);
		} /* active player if */ 
		} /* S_P2 */
		else { 
		size = 4 + ( clientVPlayers[1]*4);
		sizes[SP_S_PLAYER] = size;
		clientVPlayers[8] = (u_char) SP_S_PLAYER;
		clientVPlayers[9] = clientVPlayers[1] | 64;
		clientVPlayers[10]=0;
		clientVPlayers[11]=0;
		sendClientPacket((struct player_spacket *) &clientVPlayers[8]);
		} 
	}

#if MAXPLAYER > 32
	if ( clientVXPlayerCount > 4) { /* Small + extended Packet */
		sizes[SP_S_PLAYER] = (clientVXPlayers[1]*4)+4;
		clientVXPlayers[1] |= 128; /* To mark it as a short header + Extended */
		sendClientPacket((struct player_spacket *) clientVXPlayers);
	}
#endif
}

/* New Torp Routine */
SupdateTorps()
{
    register struct torp *torp;
    register int i,j;
    register struct torp_info_spacket *tpi;
    register struct torp_spacket *tp;
    register u_char  *tinfo; 	/* TorpInfo data   */
    register u_char  *torp_xy; /* The torp coord. */
    register u_char  infobitset, torpbitset;
    int numtorps ; /* How many torps ? */

int shift; /* Shift for 9 bit xy coordinate */
register int dx,dy; 
int     view = SCALE * WINSIDE / 2;
mustsend = 0;

    for (   j=0, torp=torps, tpi=clientTorpsInfo, tp=clientTorps; 
	    j<	(MAXPLAYER * MAXTORP)/8;
	    j++) {

	shift = 0;
	numtorps = 0;
	infobitset 	= 0;
	torpbitset = 0;
	torp_xy = &clientVTorps[4];
	*torp_xy = 0; /* Clear first data byte */
	tinfo = & clientVTorpsInfo[0];

          for (i=0;  i<8;  i++, torp++, tpi++, tp++) {

	if (torp->t_owner==me->p_no) {
	    if (torp->t_war!=tpi->war ||
		torp->t_status!=tpi->status) {
		if (torp->t_war == tpi->war){ 
			/* Don't send TMOVE/TFREE . It's encoded in the SP_S_TORP bitset. */
			switch(torp->t_status){
 			    case TFREE: 	
				if (tpi->status == TEXPLODE){ /* Send real TFREE if torp exploded */
                                     infobitset = infobitset | ( 01  << i);   /* set bit for torp */
                                     *tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) torp->t_status << 4);
                                }
                                else
                                        mustsend = 1;
				tpi->status=torp->t_status;
				/* Don't forget... */
				tp->x=htonl(torp->t_x);
				tp->y=htonl(torp->t_y);
				tp->dir=torp->t_dir;									
				continue; /* We continue , because server sends obsolete data otherwise */
			    case TMOVE:
				tpi->status=torp->t_status;
				break;				
			    default:
				tpi->status=torp->t_status; 
				infobitset = infobitset | ( 01  << i);   /* set bit for torp */
				*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
				break;
			}
		}
		else {
		tpi->war=torp->t_war;
		tpi->status=torp->t_status; 
		infobitset = infobitset | ( 01  << i);   /* set bit for torp */
		*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
		}
	    }
	    if (tp->x!=htonl(torp->t_x) ||
		tp->y!=htonl(torp->t_y) ||
                ((torp->t_gspeed == 0) && (torp->t_status != TFREE))) { /* Hadley */
		tp->type=SP_TORP;
		tp->x=htonl(torp->t_x);
		tp->y=htonl(torp->t_y);
		tp->dir=torp->t_dir;
	/* Now the fun begins .. */
		 torpbitset = torpbitset | ( 01  << i);   /* set bit for torp */
		 numtorps++;
	/* Now the Coordi. */
	    dx = torp->t_x - me->p_x;
	    dy = torp->t_y - me->p_y;
	    if (dx > view || dx < -view || dy > view || dy < -view) {
		/* any torps off screen appear at 501 = clipped */
		dx = dy = 501;
		dx <<= shift;
		*torp_xy++  |= ( dx & 255);
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){ 
			shift = 0;
			torp_xy++;
			*torp_xy = 0;
		}	     	
		continue;
	    }
	    /*
	    dx = dx / SCALE + WINSIDE / 2;
	    dy = dy / SCALE + WINSIDE / 2;
	    */
            /* NEW: fix precision loss -tsh */
            dx = torp->t_x/SCALE - me->p_x/SCALE + WINSIDE/2;
            dy = torp->t_y/SCALE - me->p_y/SCALE + WINSIDE/2;

		dx <<= shift;
		*torp_xy++  |= (dx & 0xff );
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){
			 shift = 0;	     	
			 torp_xy++;
			*torp_xy = 0;
		}
	    }
	} else {	/* Someone else's torp... */
	    if (torp->t_y > me->p_y + SCALE*WINSIDE/2 ||
		torp->t_x > me->p_x + SCALE*WINSIDE/2 ||
		torp->t_x < me->p_x - SCALE*WINSIDE/2 ||
		torp->t_y < me->p_y - SCALE*WINSIDE/2 ||
		torp->t_status==TFREE) {
		if (torp->t_status==TFREE && tpi->status==TEXPLODE) {
		    tpi->status=TFREE;
		    continue;
		}
		if (tpi->status!=TFREE) {
		    tpi->status=TFREE;
		     mustsend = 1;
		}
	    } else {	/* in view */
		if (torp->t_status!=tpi->status ||
		    (torp->t_war & me->p_team)!=tpi->war) {
		   if ( (torp->t_war & me->p_team) == tpi->war){
			/* Don't send TMOVE/TFREE . It's encoded in the SP_S_TORP bitset. */
			switch(torp->t_status){
 			    case TFREE: 	/* We continue , because server sends obsolete data otherwise */
				tpi->status=torp->t_status;
				mustsend = 1;
				/* Don't forget... */
		    		tp->x=htonl(torp->t_x);
		    		tp->y=htonl(torp->t_y);
		    		tp->dir=torp->t_dir;
				continue;
			    case TMOVE:
				tpi->status=torp->t_status;
				break;				
			    default:
				tpi->status=torp->t_status; 
				infobitset = infobitset | ( 01  << i);   /* set bit for torp */
				*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
				break;
			}
		   }
		   else {
		    /* Let the client fade away the explosion on its own */
		    tpi->war=torp->t_war & me->p_team;
		    tpi->status=torp->t_status;
		    infobitset = infobitset | ( 01  << i);   /* set bit for torp */
		    *tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
		    }
		}
		if (tp->x!=htonl(torp->t_x) ||
		    tp->y!=htonl(torp->t_y) ||
                    (torp->t_gspeed == 0 && torp->t_status != TFREE)) {/*Hadley*/
		    tp->x=htonl(torp->t_x);
		    tp->y=htonl(torp->t_y);
		    tp->dir=torp->t_dir;
	/* Now the fun begins .. */
		 torpbitset = torpbitset | ( 01  << i);   /* set bit for torp */
		 numtorps++;
	/* Now the Coordi. */
	    /*
	    dx = torp->t_x - me->p_x;
	    dx = dx / SCALE + WINSIDE / 2;
	    dy = torp->t_y - me->p_y;
	    dy = dy / SCALE + WINSIDE / 2;
	    */
            /* NEW: fix precision loss -tsh */
            dx = torp->t_x/SCALE - me->p_x/SCALE + WINSIDE/2;
            dy = torp->t_y/SCALE - me->p_y/SCALE + WINSIDE/2;

		dx <<= shift;
		*torp_xy++  |= (dx & 255);
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){ 
			shift = 0;
			torp_xy++;
			*torp_xy = 0;
		}	     		     	     
		} /* second if */
	    } 
	} 
         }	/* 2. for */

/* Is there something to send ? */
	if ( infobitset != 0) {
		int size = tinfo -clientVTorpsInfo;
		u_char *dest = &clientVTorps[4+ vtdata[numtorps]];
		u_char *source  =  &clientVTorpsInfo[0];

		/* I must copy the Torpinfo behind the Torpdata */
		while ( --size >= 0) { /* size doubly used */
			*dest++ = *source++;
		}
		size = 4 +  vtdata[numtorps]+ (tinfo -clientVTorpsInfo);
                                if((size % 4) != 0)
                                   	size += (4 - (size % 4));
		
		sizes[SP_S_TORP_INFO] = size;
		clientVTorps[0] = SP_S_TORP_INFO; /* The type */
		clientVTorps[1] = (u_char) torpbitset;
		clientVTorps[2] = (u_char) j; /* which torps */
		clientVTorps[3] = (u_char) infobitset;
		sendClientPacket((struct torp_spacket *) &clientVTorps[0]);
		mustsend = 0;
	}	
	else if ( torpbitset != 0){ /* NO TorpInfo */
		mustsend = 0; 
		if ( numtorps == 8) { /* we do not need bitset */
			clientVTorps[2] = SP_S_8_TORP; /* The type */
			clientVTorps[3] = (u_char) j; /* which torps */
			sendClientPacket((struct torp_spacket *) &clientVTorps[2]);
		}
		else {
		sizes[SP_S_TORP] = vtsize[numtorps];
		clientVTorps[1] = SP_S_TORP; /* The type */
		clientVTorps[2] = (u_char) torpbitset;
		clientVTorps[3] = (u_char) j; /* which torps */
		sendClientPacket((struct torp_spacket *) &clientVTorps[1]);
		}
	}
	else if (mustsend != 0){ /* TFREE's */
		mustsend = 0; 
		sizes[SP_S_TORP] = 4;
		clientVTorps[1] = SP_S_TORP; /* The type */
		clientVTorps[2] = (u_char)torpbitset; 
		clientVTorps[3] = (u_char) j; /* which torps */
		sendClientPacket((struct torp_spacket *) &clientVTorps[1]);	
	}
    } /* auesseres for */

/*  That's the main loop */
	/* Now we must process the rest torps */
	/* I know this is very ugly */
#if (MAXPLAYER*MAXTORP) % 8
if ( (MAXPLAYER*MAXTORP) % 8){ /* Rest torps */
	
	shift = 0;
	numtorps = 0;
	infobitset 	= 0;
	torpbitset = 0;
	torp_xy = &clientVTorps[4];
	*torp_xy = 0; /* Clear first data byte */
	tinfo = & clientVTorpsInfo[0];
	mustsend = 0;

          for (i=8*j;  i< MAXPLAYER*MAXTORP;  i++, torp++, tpi++, tp++) {

	if (torp->t_owner==me->p_no) {
	    if (torp->t_war!=tpi->war ||
		torp->t_status!=tpi->status) {
		if ( torp->t_war == tpi->war){
			/* Don't send TMOVE/TFREE . It's encoded in the SP_S_TORP bitset. */
			switch(torp->t_status){
 			    case TFREE: 	/* We continue , because server sends obsolete data */
				tpi->status=torp->t_status;
				mustsend = 1;
				/* Don't forget... */
				tp->x=htonl(torp->t_x);
				tp->y=htonl(torp->t_y);
				tp->dir=torp->t_dir;									
				continue;
			    case TMOVE:
				tpi->status=torp->t_status;
				break;				
			    case TSTRAIGHT:
				tpi->status=torp->t_status;
				break;				
			    default:
				tpi->status=torp->t_status; 
				infobitset = infobitset | ( 01  << i);   /* set bit for torp */
				*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
				break;
			}
		}
		else {
		tpi->war=torp->t_war;
		tpi->status=torp->t_status;
		infobitset = infobitset | ( 01  << i);   /* set bit for torp */
		*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
		}
	    }
	    if (tp->x!=htonl(torp->t_x) ||
		tp->y!=htonl(torp->t_y)) {
		tp->type=SP_TORP;
		tp->x=htonl(torp->t_x);
		tp->y=htonl(torp->t_y);
		tp->dir=torp->t_dir;
	/* Now the fun begins .. */
		 torpbitset = torpbitset | ( 01  << i);   /* set bit for torp */
		 numtorps++;
	/* Now the Coordi. */
	    dx = torp->t_x - me->p_x;
	    dy = torp->t_y - me->p_y;
	    if (dx > view || dx < -view || dy > view || dy < -view) {
		/* any torps off screen appear at 501 = clipped */
		dx = dy = 501;
		dx <<= shift;
		*torp_xy++  |= ( dx & 255);
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){ 
			shift = 0;
			torp_xy++;
			*torp_xy = 0;
		}	     	
		continue;
	    }
	    /*
	    dx = dx / SCALE + WINSIDE / 2;
	    dy = dy / SCALE + WINSIDE / 2;
	    */
            /* NEW: fix precision loss -tsh */
              dx = torp->t_x/SCALE - me->p_x/SCALE + WINSIDE/2;
              dy = torp->t_y/SCALE - me->p_y/SCALE + WINSIDE/2;
		dx <<= shift;
		*torp_xy++  |= (dx & 0xff );
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){
			 shift = 0;	     	
			 torp_xy++;
			*torp_xy = 0;
		}
	    }
	} else {	/* Someone else's torp... */
	    if (torp->t_y > me->p_y + SCALE*WINSIDE/2 ||
		torp->t_x > me->p_x + SCALE*WINSIDE/2 ||
		torp->t_x < me->p_x - SCALE*WINSIDE/2 ||
		torp->t_y < me->p_y - SCALE*WINSIDE/2 ||
		torp->t_status==TFREE) {
		if (torp->t_status==TFREE && tpi->status==TEXPLODE) {
		    tpi->status=TFREE;
		    continue;
		}
		if (tpi->status!=TFREE) {
		    tpi->status=TFREE;
		     mustsend = 1;
		}
	    } else {	/* in view */
		if (torp->t_status!=tpi->status ||
		    (torp->t_war & me->p_team)!=tpi->war) {
		   if ((torp->t_war & me->p_team) == tpi->war){
			/* Don't send TMOVE/TFREE . It's encoded in the SP_S_TORP bitset. */
			switch(torp->t_status){
 			    case TFREE: 	/* We continue , because server sends obsolete data */
				tpi->status=torp->t_status;
				mustsend = 1;
				/* Don't forget... */
		    		tp->x=htonl(torp->t_x);
		    		tp->y=htonl(torp->t_y);
		    		tp->dir=torp->t_dir;
				continue;
			    case TMOVE:
				tpi->status=torp->t_status;
				break;				
			    case TSTRAIGHT:
				tpi->status=torp->t_status;
				break;				
			    default:
				tpi->status=torp->t_status; 
				infobitset = infobitset | ( 01  << i);   /* set bit for torp */
				*tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
				break;
			}
		   }
		   else {
		    /* Let the client fade away the explosion on its own */
		    tpi->war=torp->t_war & me->p_team;
		    tpi->status=torp->t_status;
		    infobitset = infobitset | ( 01  << i);   /* set bit for torp */
		    *tinfo++ = ((u_char)tpi->war & 0x0f)|((u_char) tpi->status << 4);
		    }
		}
		if (tp->x!=htonl(torp->t_x) ||
		    tp->y!=htonl(torp->t_y) ||
                    (torp->t_gspeed == 0 && torp->t_status != TFREE)) {/*Hadley*/
		    tp->x=htonl(torp->t_x);
		    tp->y=htonl(torp->t_y);
		    tp->dir=torp->t_dir;
	/* Now the fun begins .. */
		 torpbitset = torpbitset | ( 01  << i);   /* set bit for torp */
		 numtorps++;
	/* Now the Coordi. */
	    /*
	    dx = torp->t_x - me->p_x;
	    dx = dx / SCALE + WINSIDE / 2;
	    dy = torp->t_y - me->p_y;
	    dy = dy / SCALE + WINSIDE / 2;
	    */
            /* NEW: fix precision loss -tsh */
              dx = torp->t_x/SCALE - me->p_x/SCALE + WINSIDE/2;
              dy = torp->t_y/SCALE - me->p_y/SCALE + WINSIDE/2;
		dx <<= shift;
		*torp_xy++  |= (dx & 255);
		*torp_xy = (((u_int) dx >> 8) & 255);
		shift++;
		dy <<= shift;
		*torp_xy++  |= (dy & 255);
		*torp_xy = (((u_int) dy >> 8) & 255);
		shift++;
	     	if ( shift == 8){ 
			shift = 0;
			torp_xy++;
			*torp_xy = 0;
		}	     		     	     
		}
	    }
	}
         }	/* 2. for */

/* Is there something to send ? */
	if ( infobitset != 0) {
		int size = tinfo - clientVTorpsInfo;
		u_char *dest = &clientVTorps[4+ vtdata[numtorps]];
		u_char *source  =  &clientVTorpsInfo[0];

		/* I must copy the Torpinfo behind the Torpdata */
		while ( --size >= 0) { /* size doubly used */
			*dest++ = *source++;
		}
		size = 4 +  vtdata[numtorps]+ (tinfo - clientVTorpsInfo);
                                if((size % 4) != 0)
                                   	size += (4 - (size % 4));
		
		sizes[SP_S_TORP_INFO] = size;
		clientVTorps[0] = SP_S_TORP_INFO; /* The type */
		clientVTorps[1] = (u_char) torpbitset;
		clientVTorps[2] = (u_char) j; /* which torps */
		clientVTorps[3] = (u_char) infobitset;
		sendClientPacket((struct torp_spacket *) &clientVTorps[0]);					
	}	
	else if ( torpbitset != 0){ /* NO TorpInfo */
		mustsend = 0; 
		if ( numtorps == 8) { /* we do not need bitset */
			clientVTorps[2] = SP_S_8_TORP; /* The type */
			clientVTorps[3] = (u_char) j; /* which torps */
			sendClientPacket((struct torp_spacket *) &clientVTorps[2]);
		}
		else {
		sizes[SP_S_TORP] = vtsize[numtorps];
		clientVTorps[1] = SP_S_TORP; /* The type */
		clientVTorps[2] = (u_char) torpbitset;
		clientVTorps[3] = (u_char) j; /* which torps */
		sendClientPacket((struct torp_spacket *) &clientVTorps[1]);
		}
	}
	else if (mustsend != 0){ /* TFREE's */
		mustsend = 0; 
		sizes[SP_S_TORP] = 4;
		clientVTorps[1] = SP_S_TORP; /* The type */
		clientVTorps[2] = (u_char)torpbitset; 
		clientVTorps[3] = (u_char) j; /* which torps */
		sendClientPacket((struct torp_spacket *) &clientVTorps[1]);	
	}
} /* last if */
#endif
} /* Function */

#endif /* SHORT_PACKETS */


void
forceShutdown (s)

     int s;
{
  SIGNAL (SIGALRM, SIG_IGN);
  ERROR(1,("forceShutdown for %s@%s: %d\n", login, host,s));
  shutdown (sock, 2);
  close (sock);
  if (udpSock >= 0)
    {
      shutdown (udpSock, 2);
      close (udpSock);
    }

  if (me->p_status != PFREE) freeslot(me); /* Clear slot if needed */
  exit (1);
}

#ifdef SHORT_PACKETS
updatePlayerStats()
{
   register struct stats_spacket        *stats;
   register struct player               *pl;
   register                             i = efticks(5);
   register struct stats_s_spacket *s_stats = &singleStats;

   if(repCount % i)
      return;

   i = (repCount/i) % MAXPLAYER;

   pl = &players[i];
   if(!pl->p_status) return;

   stats = &clientStats[i];

   /*
    * Send stat packets once per five updates. But, only send one.  We
    * will cycle through them all eventually.
    */
   if (  stats->tkills != htonl(pl->p_stats.st_tkills) ||
         stats->tlosses != htonl(pl->p_stats.st_tlosses) ||
         stats->kills != htonl(pl->p_stats.st_kills) ||
         stats->losses != htonl(pl->p_stats.st_losses) ||
         stats->tticks != htonl(pl->p_stats.st_tticks) ||
         stats->tplanets != htonl(pl->p_stats.st_tplanets) ||
         stats->tarmies != htonl(pl->p_stats.st_tarmsbomb) ||
         stats->sbkills != htonl(pl->p_stats.st_sbkills) ||
         stats->sblosses != htonl(pl->p_stats.st_sblosses) ||
         stats->armies != htonl(pl->p_stats.st_armsbomb) ||
         stats->planets != htonl(pl->p_stats.st_planets)) {

      stats->tkills = htonl(pl->p_stats.st_tkills);
      stats->tlosses = htonl(pl->p_stats.st_tlosses);
      stats->kills = htonl(pl->p_stats.st_kills);
      stats->losses = htonl(pl->p_stats.st_losses);
      stats->tticks = htonl(pl->p_stats.st_tticks);
      stats->tplanets = htonl(pl->p_stats.st_tplanets);
      stats->tarmies = htonl(pl->p_stats.st_tarmsbomb);
      stats->sbkills = htonl(pl->p_stats.st_sbkills);
      stats->sblosses = htonl(pl->p_stats.st_sblosses);
      stats->armies = htonl(pl->p_stats.st_armsbomb);
      stats->planets = htonl(pl->p_stats.st_planets);
      if ((pl->p_ship.s_type == STARBASE) && (SBhours)) {
                stats->maxkills=htonl((int) (pl->p_stats.st_sbticks));
     } else {
                stats->maxkills=htonl((int) (pl->p_stats.st_maxkills * 100));
      }
      stats->sbmaxkills = htonl((int) (pl->p_stats.st_sbmaxkills * 100));
      stats->type = SP_STATS;
      stats->pnum = i;
      if (send_short > 1 &&
                pl->p_stats.st_tkills < 0xffff &&
                pl->p_stats.st_tlosses < 0xffff &&
                pl->p_stats.st_losses < 0xffff &&
                pl->p_stats.st_tplanets < 0xffff &&
                pl->p_stats.st_sbkills < 0xffff &&
                pl->p_stats.st_sblosses < 0xffff &&
                pl->p_stats.st_planets < 0xffff ) {
            s_stats->tkills = htons(pl->p_stats.st_tkills);
            s_stats->tlosses = htons(pl->p_stats.st_tlosses);
            s_stats->kills = htons(pl->p_stats.st_kills);
            s_stats->losses = htons(pl->p_stats.st_losses);
            s_stats->tticks = htonl(pl->p_stats.st_tticks);
            s_stats->tplanets = htons(pl->p_stats.st_tplanets);
            s_stats->tarmies = htonl(pl->p_stats.st_tarmsbomb);
            s_stats->sbkills = htons(pl->p_stats.st_sbkills);
            s_stats->sblosses = htons(pl->p_stats.st_sblosses);
            s_stats->armies = htons(pl->p_stats.st_armsbomb);
            s_stats->planets = htons(pl->p_stats.st_planets);
            if ((pl->p_ship.s_type == STARBASE) && (SBhours)) {
                s_stats->maxkills=htonl((int) (pl->p_stats.st_sbticks));
            } else {
                s_stats->maxkills=htonl((int) (pl->p_stats.st_maxkills * 100));
            }
            s_stats->sbmaxkills=htonl((int) (pl->p_stats.st_sbmaxkills * 100));
            s_stats->type=SP_S_STATS;
            s_stats->pnum=i;
            sendClientPacket(s_stats);
            }
            else
     		 sendClientPacket((CVOID) stats);
   }
}
#else
updatePlayerStats()
{
   register struct stats_spacket        *stats;
   register struct player               *pl;
   register                             i = efticks(5);

   if(repCount % i)
      return;

   i = (repCount/i) % MAXPLAYER;

   pl = &players[i];
   if(!pl->p_status) return;

   stats = &clientStats[i];

   /*
    * Send stat packets once per five updates. But, only send one.  We
    * will cycle through them all eventually.
    */
   if (  stats->tkills != htonl(pl->p_stats.st_tkills) ||
         stats->tlosses != htonl(pl->p_stats.st_tlosses) ||
         stats->kills != htonl(pl->p_stats.st_kills) ||
         stats->losses != htonl(pl->p_stats.st_losses) ||
         stats->tticks != htonl(pl->p_stats.st_tticks) ||
         stats->tplanets != htonl(pl->p_stats.st_tplanets) ||
         stats->tarmies != htonl(pl->p_stats.st_tarmsbomb) ||
         stats->sbkills != htonl(pl->p_stats.st_sbkills) ||
         stats->sblosses != htonl(pl->p_stats.st_sblosses) ||
         stats->armies != htonl(pl->p_stats.st_armsbomb) ||
         stats->planets != htonl(pl->p_stats.st_planets)) {

      stats->tkills = htonl(pl->p_stats.st_tkills);
      stats->tlosses = htonl(pl->p_stats.st_tlosses);
      stats->kills = htonl(pl->p_stats.st_kills);
      stats->losses = htonl(pl->p_stats.st_losses);
      stats->tticks = htonl(pl->p_stats.st_tticks);
      stats->tplanets = htonl(pl->p_stats.st_tplanets);
      stats->tarmies = htonl(pl->p_stats.st_tarmsbomb);
      stats->sbkills = htonl(pl->p_stats.st_sbkills);
      stats->sblosses = htonl(pl->p_stats.st_sblosses);
      stats->armies = htonl(pl->p_stats.st_armsbomb);
      stats->planets = htonl(pl->p_stats.st_planets);
      if ((pl->p_ship.s_type == STARBASE) && (SBhours)) {
                stats->maxkills=htonl((int) (pl->p_stats.st_sbticks));
      } else {
                stats->maxkills=htonl((int) (pl->p_stats.st_maxkills * 100));
      }
      stats->sbmaxkills = htonl((int) (pl->p_stats.st_sbmaxkills * 100));
      stats->type = SP_STATS;
      stats->pnum = i;
      sendClientPacket((CVOID) stats);
   }
}
#endif
