view src/socket.c @ 6:8c6d5731234d

First entry of Paradise Server 2.9 patch 10 Beta
author darius
date Sat, 06 Dec 1997 04:37:04 +0000
parents
children
line wrap: on
line source

/*--------------------------------------------------------------------------
NETREK II -- Paradise

Permission to use, copy, modify, and distribute this software and its
documentation, or any derivative works thereof, for any NON-COMMERCIAL
purpose and without fee is hereby granted, provided that this copyright
notice appear in all copies.  No representations are made about the
suitability of this software for any purpose.  This software is provided
"as is" without express or implied warranty.

    Xtrek Copyright 1986                            Chris Guthrie
    Netrek (Xtrek II) Copyright 1989                Kevin P. Smith
                                                    Scott Silvey
    Paradise II (Netrek II) Copyright 1993          Larry Denys
                                                    Kurt Olsen
                                                    Brandon Gillespie
--------------------------------------------------------------------------*/


#include "config.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <math.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <zlib.h>
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "packets.h"
#include "shmem.h"
#include "path.h"
#include "gppackets.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 */

extern void (*r_signal()) ();

/* #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 */

void handleTorpReq(), handlePhasReq(), handleSpeedReq();
void handleDirReq(), handleShieldReq(), handleRepairReq(), handleOrbitReq();
void handlePractrReq(), handleBombReq(), handleBeamReq(), handleCloakReq();
void handleDetTReq(), handleCopilotReq();
void handleOutfit(), handleLoginReq();
void handlePlasmaReq(), handleWarReq(), handlePlanlockReq();
void handlePlaylockReq(), handleDetMReq();
void handleTractorReq(), handleRepressReq();
void handleCoupReq(), handleRefitReq(), handleMessageReq();
void handleQuitReq(), handleOptionsPacket();
void handleSocketReq(), handleByeReq();
void handleDockingReq(), handleReset();
void handleUpdatesReq(), handleReserved();
void handleScan();
void handleUdpReq(), handleSequence();
void handleAskMOTD();
void handleRSAKey();
void handlePingResponse();
#ifdef SHORT_PACKETS
void handleShortReq(), handleThresh(), handleSMessageReq();
#endif
#ifdef FEATURE
void handleFeature(), sendFeature();	/* in feature.c */
#endif

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

struct packet_handler handlers[] = {
  {0},				/* record 0 */
  {handleMessageReq},		/* CP_MESSAGE */
  {handleSpeedReq},		/* CP_SPEED */
  {handleDirReq},		/* CP_DIRECTION */
  {handlePhasReq},		/* CP_PHASER */
  {handlePlasmaReq},		/* CP_PLASMA */
  {handleTorpReq},		/* CP_TORP */
  {handleQuitReq},		/* CP_QUIT */
  {handleLoginReq},		/* CP_LOGIN */
  {handleOutfit},		/* CP_OUTFIT */
  {handleWarReq},		/* CP_WAR */
  {handlePractrReq},		/* CP_PRACTR */
  {handleShieldReq},		/* CP_SHIELD */
  {handleRepairReq},		/* CP_REPAIR */
  {handleOrbitReq},		/* CP_ORBIT */
  {handlePlanlockReq},		/* CP_PLANLOCK */
  {handlePlaylockReq},		/* CP_PLAYLOCK */
  {handleBombReq},		/* CP_BOMB */
  {handleBeamReq},		/* CP_BEAM */
  {handleCloakReq},		/* CP_CLOAK */
  {handleDetTReq},		/* CP_DET_TORPS */
  {handleDetMReq},		/* CP_DET_MYTORP */
  {handleCopilotReq},		/* CP_COPLIOT */
  {handleRefitReq},		/* CP_REFIT */
  {handleTractorReq},		/* CP_TRACTOR */
  {handleRepressReq},		/* CP_REPRESS */
  {handleCoupReq},		/* CP_COUP */
  {handleSocketReq},		/* CP_SOCKET */
  {handleOptionsPacket},	/* CP_OPTIONS */
  {handleByeReq},		/* CP_BYE */
  {handleDockingReq},		/* CP_DOCKPERM */
  {handleUpdatesReq},		/* CP_UPDATES */
  {handleReset},		/* CP_RESETSTATS */
  {handleReserved},		/* CP_RESERVED */
  {handleScan},			/* CP_SCAN (ATM) */
  {handleUdpReq},		/* CP_UDP_REQ */
  {handleSequence},		/* CP_SEQUENCE */
  {handleRSAKey},		/* CP_RSA_KEY */
  {handleAskMOTD},		/* CP_ASK_MOTD */
  {0},
  {0},
  {0},
  {handlePingResponse},		/* CP_PING_RESPONSE */
#ifdef SHORT_PACKETS
  {handleShortReq},		/* CP_S_REQ */
  {handleThresh},		/* CP_S_THRS */
  {handleSMessageReq},		/* CP_S_MESSAGE */
  {0},				/* CP_S_RESERVED */
  {0},				/* CP_S_DUMMY */
#else
  {0},
  {0},
  {0},
  {0},
  {0},
#endif
  {0},				/* 48 */
  {0},				/* 49 */
  {0},				/* 50 */
  {0},				/* 51 */
  {0},				/* 52 */
  {0},				/* 53 */
  {0},				/* 54 */
  {0},				/* 55 */
  {0},				/* 56 */
  {0},				/* 57 */
  {0},				/* 58 */
  {0},				/* 59 */
  {handleFeature},		/* CP_FEATURE */
  {0}
};
#define NUM_PACKETS (sizeof(handlers) / sizeof(*handlers) - 1)

int size_of_spacket();
int size_of_cpacket();

int packetsReceived[256] = {0};
int packetsSent[256] = {0};

int clientDead = 0;

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

/* 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 */
extern FAT_NODE fat_kills[MAXPLAYER];
extern FAT_NODE fat_torp_info[MAXPLAYER * MAXTORP];
extern FAT_NODE fat_thingy_info[TOTALTHINGIES];
extern FAT_NODE fat_phaser[MAXPLAYER];
extern FAT_NODE fat_plasma_info[MAXPLAYER * MAXPLASMA];
extern FAT_NODE fat_you;
#if 0
extern FAT_NODE fat_status;
extern FAT_NODE fat_planet[MAXPLANETS];
#else
extern FAT_NODE fat_status2;
extern FAT_NODE fat_planet2[MAXPLANETS];
#endif
extern FAT_NODE fat_flags[MAXPLAYER];
extern FAT_NODE fat_hostile[MAXPLAYER];

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 thingy_info_spacket clientThingysInfo[TOTALTHINGIES];
struct thingy_spacket clientThingys[TOTALTHINGIES];
int clientThingyStatus[TOTALTHINGIES];
struct phaser_spacket clientPhasers[MAXPLAYER];
struct you_spacket clientSelf;
struct pe1_num_missiles_spacket clientMissiles;
#if 0
struct status_spacket clientStatus;
struct planet_spacket clientPlanets[MAXPLANETS];
#endif
struct planet_loc_spacket clientPlanetLocs[MAXPLANETS];
struct plasma_info_spacket clientPlasmasInfo[MAXPLAYER * MAXPLASMA];
struct plasma_spacket clientPlasmas[MAXPLAYER * MAXPLASMA];
int mustUpdate[MAXPLAYER];
struct status_spacket2 clientStatus2;	/* new stats packets */
struct stats_spacket2 clientStats2[MAXPLAYER];
struct planet_spacket2 clientPlanets2[MAXPLANETS];

#ifdef SHORT_PACKETS

struct youss_spacket clientSelfShip;
struct youshort_spacket clientSelfShort;

/* HW */
static unsigned 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 unsigned char clientVTorps[40];

static unsigned char clientVTorpsInfo[16];

static unsigned char clientVPlayers[MAXPLAYER * VPLAYER_SIZE + 16];
#if MAXPLAYER > 32
static unsigned char clientVXPlayers[33 * 4];
#endif
static int clientVPlayerCount;
static int clientVXPlayerCount;
static int big, small;

static int send_threshold = 0;	/* infinity */
static int send_short = 0;
static int send_mesg = 1;
static int send_kmesg = 1;
static int send_warn = 1;
static int numupdates = 5;	/* For threshold */
static int actual_threshold = 0;/* == send_threshold / numupdates */
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


extern long unsigned int inet_addr( /* ??? */ );
void initClientData();
void updateTorpInfos();
void short_updateTorps(), updateTorps();
void updateMissiles();
void updateThingies();
void updatePlasmas();
void updateStatus();
void updateSelf();
void updatePhasers();
void updateShips();
void updatePlanets();
void updateTerrain();
void updateMessages();
void sendMissileNum();
extern void updateWarnings();	/* warning.c */
void sendClientSizedPacket();
void sendClientPacket();
void flushSockBuf();
void updateMOTD();
int parseQuery();
void bounce();
int gwrite();
void printUdpInfo();
int closeUdpConn();
void updateFat();
int fatten();
void fatMerge();
int doRead();
extern int read();
extern void perror();
void logmessage();
extern int ntorp();
extern int phaser();
extern int set_speed();
extern int set_course();
extern int shield_up();
extern int shield_down();
extern int repair();
extern int orbit();
extern pid_t fork();
extern int execl();
extern int bomb_planet();
extern int beam_up();
extern int beam_down();
extern int cloak_on();
extern int cloak_off();
extern void detothers();
extern void fire_missile_dir();
extern int nplasmatorp();
extern int lock_planet();
extern int lock_player();
extern int do_refit();
extern int pmessage2();
extern int parse_command_mess();
extern int write();
extern int setitimer();
extern int gethostname();
extern int decryptRSAPacket();
extern int makeRSAPacket();
extern int encryptReservedPacket();
void forceUpdate();
int connUdpConn();
extern int sendMotd();
void dequeue();
void enqueue();
extern int pmessage();

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

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

  if ((ns = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    printf("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)
    {
      printf("I cannot get host name\n");
      close(ns);
      exit(1);
    }
    else
    {
#if 1
      memcpy((char *) &addr.sin_addr, hp->h_addr, hp->h_length);
#else
      addr.sin_addr.s_addr = *(long *) hp->h_addr;
#endif
    }
  }
  remoteaddr = addr.sin_addr.s_addr;

  if (connect(ns, (struct sockaddr *) & addr, sizeof(addr)) < 0)
  {
    /* printf("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.
 */
void
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;
}

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

  clientDead = 0;
  clientMissiles.num = 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;
    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;
    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;

    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 < TOTALTHINGIES; i++)
  {
    clientThingysInfo[i].shape = htons(-1);
    clientThingysInfo[i].owner = htons(i / NPTHINGIES);
    clientThingys[i].x = htonl(-1);

    fat_thingy_info[i].packet = (PTR) & clientThingysInfo[i];
    fat_thingy_info[i].pkt_size = sizeof(struct torp_info_spacket);
    fat_thingy_info[i].prev = fat_thingy_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_plasma_info[i].next = (FAT_NODE *) NULL;
  }
  for (i = 0; i < MAXPLANETS; i++)
  {
    clientPlanets2[i].armies = htonl(-1);
    clientPlanetLocs[i].x = htonl(-1);

#if 0
    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;
#else
    fat_planet2[i].packet = (PTR) & clientPlanets2[i];
    fat_planet2[i].pkt_size = sizeof(struct planet_spacket2);
    fat_planet2[i].prev = fat_planet2[i].next = (FAT_NODE *) 0;
#endif
  }
  msgCurrent = (mctl->mc_current + 1) % MAXMESSAGE;
  clientSelf.pnum = -1;

  fat_you.packet = (PTR) & clientSelf;
  fat_you.pkt_size = sizeof(struct you_spacket);
  fat_you.prev = fat_you.next = (FAT_NODE *) NULL;
#if 0
  fat_status.packet = (PTR) & clientStatus;
  fat_status.pkt_size = sizeof(struct status_spacket);
  fat_status.prev = fat_status.next = (FAT_NODE *) NULL;
#else
  fat_status2.packet = (PTR) & clientStatus2;
  fat_status2.pkt_size = sizeof(struct status_spacket2);
  fat_status2.prev = fat_status2.next = (FAT_NODE *) 0;
#endif

  reset_fat_list();
}

int
isClientDead()
{
  return (clientDead);
}

void
updateClient()
{
  int i;

  for (i = 0; i < MAXPLAYER; i++)
  {
    mustUpdate[i] = 0;
  }
  updateShips();
#ifdef SHORT_PACKETS
  if (send_short)
  {
    short_updateTorps();
  }
  else
#endif
  {
    updateTorps();
    updateTorpInfos();
  }

  if (weaponsallowed[WP_MISSILE] ||
      weaponsallowed[WP_FIGHTER])
    updateMissiles();
  updateThingies();
  sendMissileNum(me->p_ship.s_missilestored);
  updatePlasmas();
  updateStatus();
  updateSelf();
  updatePhasers();
  updatePlanets();
  updateTerrain();		/* for galaxy reset */
  updateMessages();
  updateWarnings();		/* warning.c */

  /*
   * 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
   */
  /* Also, don't ping while in verification stage */

  if (ping
      && (repCount % efticks(5 * configvals->ping_period) == 0)
      && me->p_status != PDEAD
      && me->p_status != POUTFIT
  /* && me->p_status != PTQUEUE */
#ifdef AUTHORIZE
      && testtime <= 0
#endif
    )
    sendClientPing();		/* ping.c */

  if (buffersEmpty())
  {
    /* We sent nothing!  We better send something to wake him */
    sendClientPacket((struct player_spacket *) & clientSelf);
  }
  flushSockBuf();
  repCount++;
}

void
briefUpdateClient()
{
  updateMessages();
  updateMOTD();
  updateWarnings();

  flushSockBuf();
  repCount++;
}

void
updateStatus()
{
  if (repCount % efticks(75) == 0 &&
      ((ntohl(clientStatus2.timeprod) != status->timeprod) ||
       (clientStatus2.tourn != status->tourn)))
  {
    clientStatus2.type = SP_STATUS2;
    clientStatus2.tourn = status->tourn;
    clientStatus2.dooshes = htonl(status->dooshes);
    clientStatus2.armsbomb = htonl(status->armsbomb);
    clientStatus2.resbomb = htonl(status->resbomb);
    clientStatus2.planets = htonl(status->planets);
    clientStatus2.kills = htonl(status->kills);
    clientStatus2.losses = htonl(status->losses);
    clientStatus2.sbkills = htonl(status->sbkills);
    clientStatus2.sblosses = htonl(status->sblosses);
    clientStatus2.sbtime = htonl(status->sbtime);
    clientStatus2.wbkills = htonl(status->wbkills);
    clientStatus2.wblosses = htonl(status->wblosses);
    clientStatus2.wbtime = htonl(status->wbtime);
    clientStatus2.jsplanets = htonl(status->jsplanets);
    clientStatus2.jstime = htonl(status->jstime);
    clientStatus2.time = htonl(status->time);
    clientStatus2.timeprod = htonl(status->timeprod);
    sendClientPacket((struct player_spacket *) & clientStatus2);
  }
}

struct player *
maybe_watching(p)
  struct player *p;
{
  struct player *tg = &players[me->p_playerl];
  return (p == me
	  && me->p_status == POBSERVE
	  && (me->p_flags & PFPLOCK)
	  && (me->p_teamspy & tg->p_team)
	  && tg->p_spyable) ?
    tg : p;
}

struct planet *
maybe_watching_planet()
{
  return (me->p_status == POBSERVE && (me->p_flags & PFPLLOCK)) ?
  &planets[me->p_planet] : 0;
}

void
updateSelf()
{
  struct player *watched = maybe_watching(me);
  int armies = maybe_watching_planet()
  ? maybe_watching_planet()->pl_armies
  : watched->p_armies;
  int damage;

  damage = watched->p_damage
    + shipvals[watched->p_ship.s_type].s_maxdamage
    - watched->p_ship.s_maxdamage;

  if (ntohl(clientSelf.fuel) != watched->p_fuel ||
      ntohl(clientSelf.shield) != watched->p_shield ||
      ntohl(clientSelf.damage) != damage ||
      ntohs(clientSelf.etemp) != watched->p_etemp ||
      ntohs(clientSelf.wtemp) != watched->p_wtemp ||
      ntohl(clientSelf.flags) != watched->p_flags ||
      clientSelf.armies != armies ||
      clientSelf.swar != watched->p_swar ||
      ntohs(clientSelf.whydead) != watched->p_whydead ||
      ntohs(clientSelf.whodead) != watched->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) != watched->p_flags ||
	  clientSelf.armies != armies ||
	  clientSelf.swar != watched->p_swar)
      {
	clientSelf.type = SP_YOU | 0x40;	/* mark as semi-critical */
      }
      if (ntohs(clientSelf.whydead) != watched->p_whydead ||
	  ntohs(clientSelf.whodead) != watched->p_whodead ||
	  clientSelf.pnum != me->p_no)
      {
	clientSelf.type = SP_YOU | 0x80;	/* mark as critical */
      }
    }
    clientSelf.pnum = me->p_no;
    clientSelf.flags = htonl(watched->p_flags);
    clientSelf.swar = watched->p_swar;
    clientSelf.hostile = watched->p_hostile;
    clientSelf.armies = armies;
    clientSelf.shield = htonl(watched->p_shield);
    clientSelf.fuel = htonl(watched->p_fuel);
    clientSelf.etemp = htons(watched->p_etemp);
    clientSelf.wtemp = htons(watched->p_wtemp);
    clientSelf.whydead = htons(watched->p_whydead);
    clientSelf.whodead = htons(watched->p_whodead);
    clientSelf.damage = htonl(damage);
    /* ATM - visible tractor */
    clientSelf.tractor = (char) watched->p_tractor | 0x40;

    clientSelf.pad2 = (unsigned char) (status->clock & 0xFF);
    clientSelf.pad3 = (unsigned char) ((status->clock & 0xFF00) >> 8);
    sendClientPacket((struct player_spacket *) & clientSelf);
  }
}

extern int ignored[];

void
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;
#if 0
  struct stats_spacket ms_stats;
#endif
  register struct stats_spacket2 *stats;
  int update;
  int x, y;
  /*
   * #define FLAGMASK
   * (PFSHIELD|PFBOMB|PFORBIT|PFCLOAK|PFROBOT|PFBEAMUP|PFBEAMDOWN|PFPRACTR|PFD
   * OCK|PFTRACT|PFPRESS|PFDOCKOK) atm mask
   */

  /*
   * #define FLAGMASK
   * (PFSHIELD|PFBOMB|PFORBIT|PFCLOAK|PFROBOT|PFBEAMUP|PFBEAMDOWN|PFPRACTR|PFD
   * OCK|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)

  /* 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 = clientStats2;
       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, 15);
      strncpy(login->monitor, pl->p_monitor, 15);
      strncpy(login->login, pl->p_login, 15);
      login->name[15] = 0;
      login->monitor[15] = 0;
      login->login[15] = 0;
      login->type = SP_PL_LOGIN;
      login->pnum = i;
      login->rank = pl->p_stats.st_rank;
      sendClientPacket((struct player_spacket *) 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((struct player_spacket *) hostile);
    }
    /*
     * Send stat packets once per five updates. But, only send one.  We will
     * cycle through them all eventually.
     */
    /*
     * Also, update if status chages (i.e., entered game, died, tq'ed, etc)
     */

    if (pl->p_status != pstatus->status ||
	(repCount % (MAXPLAYER * efticks(5)) == i * efticks(5) &&
	 (stats->di != htonl(pl->p_stats.st_di) ||
	  stats->kills != htonl(pl->p_stats.st_tkills) ||
	  stats->losses != htonl(pl->p_stats.st_tlosses) ||
	  stats->armsbomb != htonl(pl->p_stats.st_tarmsbomb) ||
	  stats->resbomb != htonl(pl->p_stats.st_tresbomb) ||
	  stats->dooshes != htonl(pl->p_stats.st_tdooshes) ||
	  stats->planets != htonl(pl->p_stats.st_tplanets) ||
	  stats->sbkills != htonl(pl->p_stats.st_sbkills) ||
	  stats->sblosses != htonl(pl->p_stats.st_sblosses) ||
	  stats->wbkills != htonl(pl->p_stats.st_wbkills) ||
	  stats->wblosses != htonl(pl->p_stats.st_wblosses) ||
	  stats->jsplanets != htonl(pl->p_stats.st_jsplanets) ||
	  stats->rank != htonl(pl->p_stats.st_rank) ||
	  stats->royal != htonl(pl->p_stats.st_royal))))
    {

      stats->genocides = htonl(pl->p_stats.st_genocides);
      stats->maxkills = htonl((int) (pl->p_stats.st_tmaxkills * 100));
      stats->di = htonl((int) (pl->p_stats.st_di * 100));
      stats->kills = htonl(pl->p_stats.st_tkills);
      stats->losses = htonl(pl->p_stats.st_tlosses);
      stats->armsbomb = htonl(pl->p_stats.st_tarmsbomb);
      stats->resbomb = htonl(pl->p_stats.st_tresbomb);
      stats->dooshes = htonl(pl->p_stats.st_tdooshes);
      stats->planets = htonl(pl->p_stats.st_tplanets);
      stats->tticks = htonl(pl->p_stats.st_tticks);
      stats->sbkills = htonl(pl->p_stats.st_sbkills);
      stats->sblosses = htonl(pl->p_stats.st_sblosses);
      stats->sbticks = htonl(pl->p_stats.st_sbticks);
      stats->sbmaxkills = htonl((int) (pl->p_stats.st_sbmaxkills * 100));
      stats->wbkills = htonl(pl->p_stats.st_wbkills);
      stats->wblosses = htonl(pl->p_stats.st_wblosses);
      stats->wbticks = htonl(pl->p_stats.st_wbticks);
      stats->wbmaxkills = htonl((int) (pl->p_stats.st_wbmaxkills * 100));
      stats->jsplanets = htonl(pl->p_stats.st_jsplanets);
      stats->jsticks = htonl(pl->p_stats.st_jsticks);
      stats->rank = htonl(pl->p_stats.st_rank);
      stats->royal = htonl(pl->p_stats.st_royal);
      stats->type = SP_STATS2;
      stats->pnum = i;
      /* if (blk_metaserver == 0) */
      sendClientPacket((struct player_spacket *) stats);
#if 0
      else
      {
	ms_stats.type = SP_STATS;
	ms_stats.pnum = i;
	ms_stats.tkills = htonl(pl->p_stats.st_tkills);
	ms_stats.tlosses = htonl(pl->p_stats.st_tlosses);
	ms_stats.kills = htonl(1);
	ms_stats.losses = htonl(1);
	ms_stats.tticks = htonl(pl->p_stats.st_tticks);
	ms_stats.tplanets = htonl(pl->p_stats.st_tplanets);
	ms_stats.tarmies = htonl(pl->p_stats.st_tarmsbomb);
	ms_stats.sbkills = htonl(pl->p_stats.st_sbkills);
	ms_stats.sblosses = htonl(pl->p_stats.st_sblosses);
	ms_stats.armies = htonl(1);
	ms_stats.planets = htonl(1);
	ms_stats.maxkills = htonl((long) (pl->p_stats.st_tmaxkills * 100));
	ms_stats.sbmaxkills = htonl((long) (pl->p_stats.st_sbmaxkills * 100));
	sendClientPacket((struct player_spacket *) & ms_stats);
      }
#endif
    }
    if (maybe_watching(pl)->p_ship.s_type != cpli->shiptype ||
	pl->p_team != cpli->team)
    {
      cpli->type = SP_PLAYER_INFO;
      cpli->pnum = i;
      cpli->shiptype = maybe_watching(pl)->p_ship.s_type;
      cpli->team = pl->p_team;
      sendClientPacket((struct player_spacket *) cpli);
      /*
       * if (!blk_flag) cpli->shiptype=pl->p_ship.s_type;
       */
    }
    if (kills->kills != htonl((int) (maybe_watching(pl)->p_kills * 100)))
    {
      kills->type = SP_KILLS;
      kills->pnum = i;
      kills->kills = htonl((int) (maybe_watching(pl)->p_kills * 100));
      sendClientPacket((struct player_spacket *) kills);
    }
    {
      int plstat = pl->p_status;

#ifdef LEAGUE_SUPPORT
      if (status2->paused && pl->p_team != me->p_team)
	plstat = PFREE;		/* enemies are invisible when the game is
				 * paused */
#endif

      if (pstatus->status != plstat)
      {
	/*
	 * We update the location of people whose status has changed. (like
	 * if they just re-entered...)
	 */
	update = 1;
	pstatus->type = SP_PSTATUS;
	pstatus->pnum = i;
	pstatus->status = plstat;
	if (pl->p_status == PFREE)
	{
	  /*
	   * I think this will turn off ignores for players that leave.
	   * 7/24/91 TC
	   */
	  ignored[i] = 0;
	}
	sendClientPacket((struct player_spacket *) pstatus);
      }
    }

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

    if (!configvals->hiddenenemy || pl->p_team == me->p_team ||
	(maybe_watching(pl)->p_flags & PFSEEN) || (status->tourn == 0) ||
	(maybe_watching(pl)->p_flags & PFROBOT))
    {				/* a bot never has its PFSEEN bit set */

      x = maybe_watching(pl)->p_x;
      y = maybe_watching(pl)->p_y;
      if (me != pl && flags->flags !=
	  htonl(FLAGMASK & maybe_watching(pl)->p_flags))
      {
	flags->type = SP_FLAGS;
	flags->pnum = i;
	flags->flags = htonl(FLAGMASK & maybe_watching(pl)->p_flags);
	flags->tractor = (char) maybe_watching(pl)->p_tractor | 0x40;	/* ATM - visible tractor */
	sendClientPacket((struct player_spacket *) 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);
	sendClientPacket((struct player_spacket *) flags);
      }
    }
    if (x != ntohl(cpl->x) || y != ntohl(cpl->y) ||
	maybe_watching(pl)->p_dir != cpl->dir ||
	maybe_watching(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)) &&
	  (maybe_watching(me) != pl) && !mustUpdate[i])
      {
	if (repCount % efticks(9) != 0)
	  continue;
	cpl->type = SP_PLAYER;
	cpl->pnum = i;
	cpl->x = htonl(x + (lrand48() % 2000) - 1000);
	cpl->y = htonl(y + (lrand48() % 2000) - 1000);
	sendClientPacket(cpl);
	continue;
      }
      cpl->type = SP_PLAYER;
      cpl->pnum = i;
      cpl->x = htonl(x);
      cpl->y = htonl(y);
      cpl->speed = maybe_watching(pl)->p_speed;
      cpl->dir = maybe_watching(pl)->p_dir;
      sendClientPacket(cpl);
    }
  }
}

void
updateTorpInfos()
{
  register struct torp *torp;
  register int i;
  register struct torp_info_spacket *tpi;

  for (i = 0, torp = torps, tpi = clientTorpsInfo;
       i < MAXPLAYER * MAXTORP;
       i++, torp++, tpi++)
  {
    if (torp->t_owner == me->p_no)
    {
      if (torp->t_war != tpi->war ||
	  torp->t_status != tpi->status)
      {
	tpi->type = SP_TORP_INFO;
	tpi->war = torp->t_war;
	tpi->status = torp->t_status;
	tpi->tnum = htons(i);
	sendClientPacket((struct player_spacket *) tpi);
      }
    }
    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;
	  tpi->tnum = htons(i);
	  tpi->type = SP_TORP_INFO;
	  sendClientPacket((struct player_spacket *) tpi);
	}
      }
      else
      {				/* in view */
	enum torp_status_e tstatus = torp->t_status;

#ifdef LEAGUE_SUPPORT
	if (status2->paused
	    && players[torp->t_owner].p_team != me->p_team)
	  tstatus = TFREE;	/* enemy torps are invisible during game
				 * pause */
#endif

	if (tstatus != tpi->status ||
	    (torp->t_war & me->p_team) != tpi->war)
	{
	  /* Let the client fade away the explosion on its own */
	  tpi->war = torp->t_war & me->p_team;
	  tpi->type = SP_TORP_INFO;
	  tpi->tnum = htons(i);
	  tpi->status = tstatus;
	  sendClientPacket((struct player_spacket *) tpi);
	}
      }
    }
  }
}



void
updateTorps()
{
  register struct torp *torp;
  register int i;
  register struct torp_spacket *tp;

  for (i = 0, torp = torps, tp = clientTorps;
       i < MAXPLAYER * MAXTORP;
       i++, torp++, tp++)
  {
    if (torp->t_owner == me->p_no)
    {

      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;
	tp->tnum = htons(i);
	sendClientPacket((struct player_spacket *) tp);
      }
    }
    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)
      {
	/* do nothing */
      }
      else
      {				/* in view */
	enum torp_status_e tstatus = torp->t_status;

#ifdef LEAGUE_SUPPORT
	if (status2->paused
	    && players[torp->t_owner].p_team != me->p_team)
	  tstatus = TFREE;	/* enemy torps are invisible during game
				 * pause */
#endif

	if (tstatus == TFREE)
	  continue;		/* no need to transmit position */

	if (tp->x != htonl(torp->t_x) ||
	    tp->y != htonl(torp->t_y))
	{
	  tp->x = htonl(torp->t_x);
	  tp->y = htonl(torp->t_y);
	  tp->dir = torp->t_dir;
	  tp->tnum = htons(i);
	  tp->type = SP_TORP;
	  sendClientPacket((struct player_spacket *) tp);
	}
      }
    }
  }
}

#ifdef SHORT_PACKETS

#define NIBBLE()	*(*data)++ = (torp->t_war & 0xf) | (torp->t_status << 4)

int 
encode_torp_status(torp, pnum, data, tpi, tp, mustsend)
  struct torp *torp;
  int pnum;
  char **data;
  struct torp_info_spacket *tpi;
  struct torp_spacket *tp;
  int *mustsend;
{
  if (pnum != me->p_no)
  {
    int dx, dy;
    int i = SCALE * WINSIDE / 2;
    dx = me->p_x - torp->t_x;
    dy = me->p_y - torp->t_y;
    if (dx < -i || dx > i || dy < -i || dy > i || torp->t_status == TFREE)
    {
      if (torp->t_status == TFREE && tpi->status == TEXPLODE)
	tpi->status = TFREE;
      else if (tpi->status != TFREE)
      {
	tpi->status = TFREE;
	*mustsend = 1;
      }
      return 0;
    }
  }

  if (torp->t_war != tpi->war)
  {
    tpi->war = torp->t_war;
    tpi->status = torp->t_status;
    NIBBLE();
    return 1;
  }
  else if (torp->t_status != tpi->status)
  {
    switch (torp->t_status)
    {
     case TFREE:
      {
	int rval = 0;
	if (tpi->status == TEXPLODE)
	{
	  NIBBLE();
	  rval = 1;
	}
	else
	  *mustsend = 1;
	tpi->status = torp->t_status;
	tp->x = htonl(torp->t_x);
	tp->y = htonl(torp->t_y);
	return rval;
      }
      break;
     case TMOVE:
     case TSTRAIGHT:
      tpi->status = torp->t_status;
      break;
     default:
      NIBBLE();
      tpi->status = torp->t_status;
      return 1;
      break;
    }
  }
  return 0;
}

#define	TORP_INVISIBLE(tstatus) ( \
				 (tstatus)== TFREE || \
				 (tstatus) == TOFF || \
				 (tstatus) == TLAND)

int 
encode_torp_position(torp, pnum, data, shift, cache)
  struct torp *torp;
  int pnum;
  char **data;
  int *shift;
  struct torp_spacket *cache;
{
  int x, y;

  if (htonl(torp->t_x) == cache->x &&
      htonl(torp->t_y) == cache->y)
    return 0;

  cache->x = htonl(torp->t_x);
  cache->y = htonl(torp->t_y);

  if (TORP_INVISIBLE(torp->t_status)
#ifdef LEAGUE_SUPPORT
      || (status2->paused &&
	  players[pnum].p_team != me->p_team)
#endif
    )
    return 0;

  x = torp->t_x / SCALE - me->p_x / SCALE + WINSIDE / 2;
  y = torp->t_y / SCALE - me->p_y / SCALE + WINSIDE / 2;

  if (x < 0 || x >= WINSIDE ||
      y < 0 || y >= WINSIDE)
  {
    if (pnum != me->p_no)
      return 0;
    x = y = 501;
  }

  **data |= x << *shift;
  *(++(*data)) = (0x1ff & x) >> (8 - *shift);
  (*shift)++;
  **data |= y << *shift;
  *(++(*data)) = (0x1ff & y) >> (8 - *shift);
  (*shift)++;
  if (*shift == 8)
  {
    *shift = 0;
    *(++(*data)) = 0;
  }
  return 1;
}

void
short_updateTorps()
{
  register int i;

  for (i = 0; i <= (MAXPLAYER * MAXTORP - 1) / 8; i++)
  {
    struct torp *torp = &torps[i * 8];
    int j;
    char packet[2		/* packet type and player number */
		+   1		/* torp mask */
		+   1		/* torp info mask */
		+   18		/* 2*8 9-bit numbers */
		+   8		/* 8 torp info bytes */
    ];
    char *data = packet + 4;
    char info[8];
    char *ip = info;
    int shift = 0;
    int torppos_mask = 0;
    int torpinfo_mask = 0;
    int mustsend;

    /* encode screen x and y coords */
    data[0] = 0;
#define TIDX	(j+i*8)
#define PNUM	((int)((j+i*8)/MAXTORP))
    for (j = 0; j < 8 && TIDX < MAXPLAYER * MAXTORP; j++)
    {
      torpinfo_mask |= encode_torp_status
	(&torp[j], PNUM, &ip, &clientTorpsInfo[TIDX], &clientTorps[TIDX], &mustsend) << j;
      torppos_mask |= encode_torp_position
	(&torp[j], PNUM, &data, &shift, &clientTorps[TIDX]) << j;
    }

    /*
     * if (!torppos_mask) continue;
     */

    if (torpinfo_mask)
    {
      if (shift)
	data++;
      for (j = 0; j < 8 && &info[j] < ip; j++)
	data[j] = info[j];
      packet[0] = SP_S_TORP_INFO;
      packet[1] = torppos_mask;
      packet[2] = i;
      packet[3] = torpinfo_mask;
      sendClientSizedPacket(packet, (data - packet + j));
    }
    else if (torppos_mask == 0xff)
    {
      /* what a disgusting hack */
      packet[2] = SP_S_8_TORP;
      packet[3] = i;
      sendClientSizedPacket(packet + 2, 20);
    }
    else if (mustsend || torppos_mask != 0)
    {
      packet[1] = SP_S_TORP;
      packet[2] = torppos_mask & 0xff;
      packet[3] = i;
      sendClientSizedPacket(packet + 1, (data - (packet + 1) + (shift != 0)));
    }
  }
#undef PNUM
#undef TIDX
}


#endif


void
updateMissiles()
{
  register struct missile *missile;
  register int i;
  register struct thingy_info_spacket *dpi;
  register struct thingy_spacket *dp;

  for (i = 0, missile = missiles, dpi = clientThingysInfo, dp = clientThingys;
       i < MAXPLAYER * NPTHINGIES;
       i++, missile++, dpi++, dp++)
  {
    enum torp_status_e msstatus = missile->ms_status;

#ifdef LEAGUE_SUPPORT
    if (status2->paused &&
	players[missile->ms_owner].p_team != me->p_team)
      msstatus = TFREE;		/* enemy torps are invisible during game
				 * pause */
#endif

    switch (msstatus)
    {
     case TFREE:
     case TLAND:
     case TOFF:
      dpi->shape = htons(SHP_BLANK);
      break;
     case TMOVE:
     case TRETURN:
     case TSTRAIGHT:
      dpi->shape = htons((missile->ms_type == FIGHTERTHINGY)
			 ? SHP_FIGHTER
			 : SHP_MISSILE);
      break;
     case TEXPLODE:
     case TDET:
      dpi->shape = htons(SHP_PBOOM);
      break;
    }

    dpi->type = SP_THINGY_INFO;
    dpi->tnum = htons(i);

    dp->type = SP_THINGY;
    dp->tnum = htons(i);
    dp->dir = missile->ms_dir;

    if (missile->ms_owner == me->p_no)
    {
      if (missile->ms_war != dpi->war ||
	  msstatus != clientThingyStatus[i])
      {
	dpi->war = missile->ms_war;
	clientThingyStatus[i] = msstatus;
	sendClientPacket((struct player_spacket *) dpi);
      }
      if (dp->x != htonl(missile->ms_x) ||
	  dp->y != htonl(missile->ms_y))
      {
	dp->x = htonl(missile->ms_x);
	dp->y = htonl(missile->ms_y);
	/* printf("missile at %d,%d\n", dp->x, dp->y); */
	sendClientPacket((struct player_spacket *) dp);
      }
    }
    else
    {				/* Someone else's missile... */
      if (msstatus == TFREE ||
	  missile->ms_y > me->p_y + SCALE * WINSIDE / 2 ||
	  missile->ms_x > me->p_x + SCALE * WINSIDE / 2 ||
	  missile->ms_x < me->p_x - SCALE * WINSIDE / 2 ||
	  missile->ms_y < me->p_y - SCALE * WINSIDE / 2)
      {
	if (msstatus == TFREE && clientThingyStatus[i] == TEXPLODE)
	{
	  clientThingyStatus[i] = TFREE;
	  continue;
	}
	if (clientThingyStatus[i] != TFREE)
	{
	  clientThingyStatus[i] = TFREE;
	  dpi->shape = htons(SHP_BLANK);
	  sendClientPacket((struct player_spacket *) dpi);
	}
      }
      else
      {				/* in view */
	if (dp->x != htonl(missile->ms_x) ||
	    dp->y != htonl(missile->ms_y))
	{
	  dp->x = htonl(missile->ms_x);
	  dp->y = htonl(missile->ms_y);
	  sendClientPacket((struct player_spacket *) dp);
	}
	if (msstatus != clientThingyStatus[i] ||
	    (missile->ms_war & me->p_team) != dpi->war)
	{
	  /* Let the client fade away the explosion on its own */
	  dpi->war = missile->ms_war & me->p_team;
	  clientThingyStatus[i] = msstatus;
	  sendClientPacket((struct player_spacket *) dpi);
	}
      }
    }
  }
}

static void
fill_thingy_info_packet(thing, packet)
  struct thingy *thing;
  struct thingy_info_spacket *packet;
{
  switch (thing->type)
  {
   case TT_NONE:
    packet->war = 0;
    packet->shape = htons(SHP_BLANK);
    packet->owner = 0;
    break;
   case TT_WARP_BEACON:
    packet->war = 0;
    if (thing->u.wbeacon.owner == me->p_team)
    {
      packet->shape = htons(SHP_WARP_BEACON);
      packet->owner = htons(thing->u.wbeacon.owner);
    }
    else
    {
      packet->shape = htons(SHP_BLANK);
      packet->owner = 0;
    }
    break;
   default:
    printf("Unknown thingy type: %d\n", (int) thing->type);
    break;
  }
}

static void
fill_thingy_packet(thing, packet)
  struct thingy *thing;
  struct thingy_spacket *packet;
{
  switch (thing->type)
  {
   case TT_NONE:
    packet->dir = 0;
    packet->x = packet->y = htonl(0);
    break;
   case TT_WARP_BEACON:
    packet->dir = 0;
    if (thing->u.wbeacon.owner == me->p_team)
    {
      packet->x = htonl(thing->u.wbeacon.x);
      packet->y = htonl(thing->u.wbeacon.y);
    }
    else
    {
      packet->x = packet->y = htonl(0);
    }
    break;
   default:
    printf("Unknown thingy type: %d\n", (int) thing->type);
    break;
  }
}

void
updateThingies()
{
  struct thingy *thing;
  struct thingy_info_spacket *tip;
  struct thingy_spacket *tp;
  int i;

  for (i = 0; i < NGTHINGIES; i++)
  {
    struct thingy_info_spacket ti1;
    struct thingy_spacket t2;

    thing = &thingies[i];
    tip = &clientThingysInfo[i + MAXPLAYER * NPTHINGIES];
    tp = &clientThingys[i + MAXPLAYER * NPTHINGIES];

    ti1.type = SP_THINGY_INFO;
    ti1.tnum = htons(i + MAXPLAYER * NPTHINGIES);
    fill_thingy_info_packet(thing, &ti1);

    if (0 != memcmp((char *) tip, (char *) &ti1, sizeof(ti1)))
    {
      memcpy(tip, &ti1, sizeof(ti1));
      sendClientPacket((struct player_spacket *) tip);
    }

    if (tip->shape != htons(SHP_BLANK))
    {
      t2.type = SP_THINGY;
      t2.tnum = htons(i + MAXPLAYER * NPTHINGIES);
      fill_thingy_packet(thing, &t2);

      if (0 != memcmp(tp, &t2, sizeof(t2)))
      {
	memcpy(tp, &t2, sizeof(t2));
	sendClientPacket((struct player_spacket *) tp);
      }
    }
  }

}

void
updatePlasmas()
{
  register struct plasmatorp *torp;
  register int i;
  register struct plasma_info_spacket *tpi;
  register struct plasma_spacket *tp;

  for (i = 0, torp = plasmatorps, tpi = clientPlasmasInfo, tp = clientPlasmas;
       i < MAXPLAYER * MAXPLASMA;
       i++, torp++, tpi++, tp++)
  {
    if (torp->pt_owner == me->p_no)
    {
      if (torp->pt_war != tpi->war ||
	  torp->pt_status != tpi->status)
      {
	tpi->type = SP_PLASMA_INFO;
	tpi->war = torp->pt_war;
	tpi->status = torp->pt_status;
	tpi->pnum = htons(i);
	sendClientPacket((struct player_spacket *) tpi);
      }
      if (tp->x != htonl(torp->pt_x) ||
	  tp->y != htonl(torp->pt_y))
      {
	tp->type = SP_PLASMA;
	tp->x = htonl(torp->pt_x);
	tp->y = htonl(torp->pt_y);
	tp->pnum = htons(i);
	sendClientPacket((struct player_spacket *) tp);
      }
    }
    else
    {				/* Someone else's torp... */
      enum torp_status_e ptstatus = torp->pt_status;

#ifdef LEAGUE_SUPPORT
      if (status2->paused &&
	  players[torp->pt_owner].p_team != me->p_team)
	ptstatus = TFREE;	/* enemy torps are invisible during game
				 * pause */
#endif
      if (torp->pt_y > me->p_y + SCALE * WINSIDE / 2 ||
	  torp->pt_x > me->p_x + SCALE * WINSIDE / 2 ||
	  torp->pt_x < me->p_x - SCALE * WINSIDE / 2 ||
	  torp->pt_y < me->p_y - SCALE * WINSIDE / 2 ||
	  ptstatus == PTFREE)
      {
	if (ptstatus == PTFREE && tpi->status == PTEXPLODE)
	{
	  tpi->status = PTFREE;
	  continue;
	}
	if (tpi->status != PTFREE)
	{
	  tpi->status = PTFREE;
	  tpi->pnum = htons(i);
	  tpi->type = SP_PLASMA_INFO;
	  sendClientPacket((struct player_spacket *) tpi);
	}
      }
      else
      {				/* in view */
	/* Send torp (we assume it moved) */
	tp->x = htonl(torp->pt_x);
	tp->y = htonl(torp->pt_y);
	tp->pnum = htons(i);
	tp->type = SP_PLASMA;
	sendClientPacket((struct player_spacket *) tp);
	if (ptstatus != tpi->status ||
	    (torp->pt_war & me->p_team) != tpi->war)
	{
	  tpi->war = torp->pt_war & me->p_team;
	  tpi->type = SP_PLASMA_INFO;
	  tpi->pnum = htons(i);
	  tpi->status = ptstatus;
	  sendClientPacket((struct player_spacket *) tpi);
	}
      }
    }
  }
}

void
updatePhasers()
{
  register int i;
  register struct phaser_spacket *ph;
  register struct phaser *phase;
  register struct player *pl;

  for (i = 0, ph = clientPhasers, phase = phasers, pl = players;
       i < MAXPLAYER; i++, ph++, phase++, pl++)
  {
    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;
	sendClientPacket((struct player_spacket *) 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);
	sendClientPacket((struct player_spacket *) ph);
      }
    }
  }
}


#define PLFLAGMASK (PLRESMASK|PLATMASK|PLSURMASK|PLPARADISE|PLTYPEMASK)

void
updatePlanets()
{
  register int i;
  register struct planet *plan;
  register struct planet_loc_spacket *pll;
#if 0
  register struct planet_spacket *mspl;
#endif
  register struct planet_spacket2 *pl;
  int dx, dy;
  int d2x, d2y;
  char *name;

  for (i = 0, pl = clientPlanets2, 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_hinfo & me->p_team) == 0) && (pl->info & me->p_team))
    {
      pl->type = SP_PLANET2;
      pl->pnum = i;
      pl->info = 0;
      pl->flags = PLPARADISE;
      sendClientPacket((struct player_spacket *) pl);
    }
    else
    {
      struct teaminfo temp, *ptr;
      if (configvals->durablescouting)
      {
	temp.owner = plan->pl_owner;
	temp.armies = plan->pl_armies;
	temp.flags = plan->pl_flags;
	temp.timestamp = status->clock;
	ptr = &temp;
      }
      else
	ptr = &plan->pl_tinfo[me->p_team];

      if (pl->info != plan->pl_hinfo ||
	  pl->armies != htonl(ptr->armies) ||
	  pl->owner != ptr->owner ||
	  pl->flags != htonl(ptr->flags & PLFLAGMASK)
	  || ((pl->timestamp != htonl(ptr->timestamp))
	      && (me->p_team != plan->pl_owner)))
      {
	pl->type = SP_PLANET2;
	pl->pnum = (char) i;
	pl->info = (char) plan->pl_hinfo;
	pl->flags = htonl(ptr->flags & PLFLAGMASK);
	pl->armies = htonl(ptr->armies);
	pl->owner = (char) ptr->owner;
	pl->timestamp = htonl(ptr->timestamp);
	sendClientPacket((struct player_spacket *) pl);
      }
    }
    /* Assume that the planet only needs to be updated once... */

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

    dx = ntohl(pll->x) - plan->pl_x;
    if (dx < 0)
      dx = -dx;
    dy = ntohl(pll->y) - plan->pl_y;
    if (dy < 0)
      dy = -dy;

    d2x = plan->pl_x - me->p_x;
    d2y = plan->pl_y - me->p_y;
    if (d2x < 0)
      d2x = -d2x;
    if (d2y < 0)
      d2y = -d2y;

    if (1 || plan->pl_hinfo & me->p_team)
      name = plan->pl_name;
    else
      name = "   ";
    /*
     * if ((pll->x != htonl(plan->pl_x)) || (pll->y != htonl(plan->pl_y))) {
     */
    if (strcmp(pll->name, name) ||
	((dx > 400 || dy > 400) ||
	 ((dx >= 20 || dy >= 20) && (d2x < 10000 && d2y < 10000))))
    {
      pll->x = htonl(plan->pl_x);
      pll->y = htonl(plan->pl_y);
      pll->pnum = i;
      if (plan->pl_system == 0)
	pll->pad2 = 255;
      else
	pll->pad2 = (char) stars[plan->pl_system];
      strcpy(pll->name, name);
      pll->type = SP_PLANET_LOC;
      sendClientPacket((struct player_spacket *) pll);
    }
  }
}

void
updateMessages()
{
  int i;
  struct message *cur;
  struct mesg_spacket msg;

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

    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) ||
	 (cur->m_flags & MGOD && cur->m_from == me->p_no)))
    {
      msg.type = SP_MESSAGE;
      strncpy(msg.mesg, cur->m_data, 80);
      msg.mesg[79] = '\0';
      msg.m_flags = cur->m_flags;
      msg.m_recpt = cur->m_recpt;
      msg.m_from = cur->m_from;

      if ((cur->m_from < 0)
	  || (cur->m_from > MAXPLAYER)
	  || (cur->m_flags & MGOD && cur->m_from == me->p_no)
	  || (cur->m_flags & MALL && !(ignored[cur->m_from] & MALL))
	  || (cur->m_flags & MTEAM && !(ignored[cur->m_from] & MTEAM)))
	sendClientPacket((struct player_spacket *) & msg);
      else if (cur->m_flags & MINDIV)
      {

	/* session stats now parsed here.  parseQuery == true */
	/* means eat message 4/17/92 TC */

	if (!parseQuery(&msg))
	  if (ignored[cur->m_from] & MINDIV)
	    bounce("That player is currently ignoring you.",
		   cur->m_from);
	  else
	    sendClientPacket((struct player_spacket *) & msg);
      }
    }
    msgCurrent = (msgCurrent + 1) % MAXMESSAGE;
  }
}

/* Asteroid/Nebulae socket code (5/16/95 rpg) */

#define DIM (MAX_GWIDTH/TGRID_GRANULARITY)

void
updateTerrain()
{
  int i;
  int j, maxfor;
  int status;
  int npkts;
  struct terrain_packet2 tpkt;
  struct terrain_info_packet2 tinfo_pkt;
  unsigned long olen = DIM * DIM, dlen = DIM * DIM / 1000 + DIM * DIM + 13;
#if defined(T_DIAG) || defined(T_DIAG2)
  char buf[80];
#endif
  unsigned char origBuf[DIM * DIM];
  unsigned char gzipBuf[DIM * DIM / 1000 +
			    DIM * DIM + 13];
  /*
   * Don't ask me.  The compression libs need (original size + 0.1% + 12
   * bytes).
   */
  /*
   * Note - this will have to be RADICALLY changed if alt1/alt2 are sent as
   * well.
   */

  /* check to see if client can handle the terrain data first */
  if (F_terrain)
  {
    if (galaxyValid[me->p_no])
      return;

#if defined(T_DIAG) || defined(T_DIAG2)
    {
      char buf[80];
      sprintf(buf, "pno: %d gIsense: %d", me->p_no, galaxyValid[me->p_no]);
      pmessage(buf, 0, MALL, MSERVA);
    }
#endif
    galaxyValid[me->p_no] = 1;
    /* Send initial packet. */
    tinfo_pkt.type = SP_TERRAIN_INFO2;
    tinfo_pkt.xdim = htons(DIM);
    tinfo_pkt.ydim = htons(DIM);
    sendClientPacket((struct player_spacket *) & tinfo_pkt);
    for (i = 0; i < DIM * DIM; i++)
    {
      origBuf[i] = terrain_grid[i].types;	/* pack types field into
						 * array */
    }
#if defined(T_DIAG) || defined(T_DIAG2)
    status = compress(gzipBuf, &dlen, origBuf, olen);
    if (status != Z_OK)
    {
      pmessage("TERRAIN: Cannot gzip terrain grid.", 0, MALL, MSERVA);
      return;
    }
    else
    {
      sprintf(buf, "TERRAIN: Original length %d, compressed length %d", DIM * DIM, dlen);
      pmessage(buf, 0, MALL, MSERVA);
    }
#else
    compress(gzipBuf, &dlen, origBuf, olen);
#endif
    npkts = (dlen >> LOG2NTERRAIN);
    if (dlen & TERRAIN_MASK)
    {
      npkts++;			/* require a partial packet */
    }
    for (i = 1; i <= npkts; i++)
    {
      tpkt.type = SP_TERRAIN2;
      tpkt.sequence = (unsigned char) i;
      tpkt.total_pkts = (unsigned char) npkts;
      if (i < npkts)
      {
	maxfor = tpkt.length = NTERRAIN;
      }
      else
      {
	maxfor = tpkt.length = (dlen & TERRAIN_MASK);
      }
      for (j = 0; j < maxfor; j++)
      {
	tpkt.terrain_type[j] = gzipBuf[((i - 1) << LOG2NTERRAIN) + j];
      }
      /* ok, packet is filled in, send it */
#ifdef T_DIAG2
      sprintf(buf, "Sending terrain packet %d of %d", tpkt.sequence, tpkt.total_pkts);
      pmessage(buf, 0, MALL, MSERVA);
#endif
      sendClientPacket((struct player_spacket *) & tpkt);
    }
  }
#ifdef FEATURE_DIAG
  else
  {
    pmessage("Mis-timed terrain data (F_terrain = 0)!", 0, MALL, MSERVA);
  }
#endif
}

void
updateMOTD()
{
  static int spinner = 0;

  if (--spinner < 0)
  {
    static struct stat oldstat;
    static int firsttime = 1;

    char *path;
    struct stat newstat;
    struct obvious_packet pkt;

    spinner = 10;

    if (!firsttime)
    {
      path = build_path(MOTD);
      stat(path, &newstat);
      if (newstat.st_ino == oldstat.st_ino &&
	  newstat.st_mtime == oldstat.st_mtime)
	return;
      oldstat.st_ino = newstat.st_ino;
      oldstat.st_mtime = newstat.st_mtime;

      pkt.type = SP_NEW_MOTD;
      sendClientPacket((struct player_spacket *) & pkt);
    }
    else
    {
      sendMotd();		/* can't build_path before this */
      path = build_path(MOTD);
      stat(path, &oldstat);
      /* printf("%s: %d, %d\n", path, oldstat.st_ino, oldstat.st_mtime); */
      firsttime = 0;
    }
  }
}

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

  qPacket.type = SP_QUEUE;
  qPacket.pos = htons(pos);
  sendClientPacket((struct player_spacket *) & qPacket);
  flushSockBuf();
}

void
sendClientPacket(packet)
  struct player_spacket *packet;
{
  sendClientSizedPacket(packet, -1);
}

void
sendClientSizedPacket(packet, size)
/* Pick a random type for the packet */
  struct player_spacket *packet;
  int size;
{
  int orig_type;
  int issc;
  static int oldStatus = POUTFIT;

#if 0
  if (blk_metaserver)
#endif
#ifdef SHOW_PACKETS
  {
    FILE *logfile;
    char *paths;
    paths = build_path("logs/metaserver.log");
    logfile = fopen(paths, "a");
    if (logfile)
    {
      fprintf(logfile, "Sending packet type %d\n", (int) packet->type);
      fclose(logfile);
    }

  }
#endif

#ifdef T_DIAG2
  if ((packet->type == SP_TERRAIN2) || (packet->type == SP_TERRAIN_INFO2))
  {
    pmessage("Sending TERRAIN packet\n", 0, MALL, MSERVA);
  }
#endif

  orig_type = packet->type;
#if 0
  packet->type &= ~(0x40 | 0x80);	/* clear special flags */
#else
  packet->type &= (char) 0x3f;	/* above doesn't work? 4/18/92 TC */
#endif
#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 we're not given the size, calculate it */
  if (size < 0)
  {
    if (size_of_spacket(packet) == 0)
    {
      printf("Attempt to send strange packet %d\n", packet->type);
      return;
    }
    size = size_of_spacket(packet);
  }
  else
  {
    /* pad to 32-bits */
    size = ((size - 1) / 4 + 1) * 4;
  }

  packetsSent[packet->type]++;


  if (commMode == COMM_TCP
      || (commMode == COMM_UDP && udpMode == MODE_TCP))
  {
    switch (orig_type)
    {
     case SP_MOTD:
     case SP_MOTD_PIC:
      /* these can afford to be delayed */
      sendTCPdeferred((void *) packet, size);
      break;

     default:
      /* business as usual, TCP */
      sendTCPbuffered((void *) packet, size);
      break;
    }
  }
  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_THINGY_INFO:
     case SP_PHASER:
     case SP_PLASMA_INFO:
     case SP_YOU | 0x40:	/* ??? what is this? */
     case SP_STATUS:
     case SP_STATUS2:
     case SP_PLANET:
     case SP_PLANET2:
     case SP_FLAGS:
     case SP_HOSTILE:
      /*
       * these are semi-critical; flag as semi-critical and fall through
       */
      issc = 1;

     case SP_PLAYER:
     case SP_TORP:
#ifdef SHORT_PACKETS
     case SP_S_TORP:
     case SP_S_8_TORP:
#endif
     case SP_THINGY:
     case SP_YOU:
     case SP_PLASMA:
     case SP_STATS:
     case SP_STATS2:
      /* case SP_SCAN: */
     case SP_PING:
     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));
      packets_sent++;
      sendUDPbuffered(issc, (void *) packet, size);
      break;

     case SP_MOTD:
     case SP_MOTD_PIC:
      sendTCPdeferred((void *) packet, size);
      break;

     default:
      sendTCPbuffered((void *) packet, size);
      break;
    }
  }

  if (me != NULL)
    oldStatus = me->p_status;
}

/*
 * flushSockBuf, socketPause, socketWait were here
 */

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

  if (clientDead)
    return (0);

  timeout.tv_sec = 0;
  timeout.tv_usec = 0;

  build_select_masks(&readfds, &writefds);

  if (select(32, &readfds, &writefds, (fd_set *) 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);
    }
    if (retval == 0 &&		/* no other traffic */
	FD_ISSET(sock, &writefds))
    {
      flushDeferred();		/* we have an eye in the packet hurricane */
    }
  }
  return (retval != 0);		/* convert to 1/0 */
}

static int
input_allowed(packettype)
  int packettype;
{
  switch (packettype)
  {
   case CP_MESSAGE:
   case CP_SOCKET:
   case CP_OPTIONS:
   case CP_BYE:
   case CP_UPDATES:
   case CP_RESETSTATS:
   case CP_RESERVED:
   case CP_RSA_KEY:
   case CP_ASK_MOTD:
   case CP_PING_RESPONSE:
   case CP_UDP_REQ:
#ifdef FEATURE
   case CP_FEATURE:
#endif
#ifdef SHORT_PACKETS
   case CP_S_MESSAGE:
#endif
    return 1;
   default:
    if (!me)
      return;

    if (me->p_status == PTQUEUE)
      return (packettype == CP_OUTFIT
	);
    else if (me->p_status == POBSERVE)
      return (packettype == CP_QUIT
	      || packettype == CP_PLANLOCK
	      || packettype == CP_PLAYLOCK
	      || packettype == CP_SCAN
	      || packettype == CP_SEQUENCE	/* whatever this is */
	);

    if (inputMask >= 0 && inputMask != packettype)
      return 0;
    if (me == NULL)
      return 1;
    if (!(me->p_flags & (PFWAR | PFREFITTING)))
      return 1;

    return 0;
  }
}

static int rsock;		/* ping stuff */

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

  rsock = asock;		/* need the socket in the ping handler
				 * routine */

  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)
  {
#if DEBUG
    /* (this happens when the client hits 'Q') */
    fprintf(stderr, "1) read() failed in doRead (%d, error %d)\n",
	    count, errno);
    fprintf(stderr, "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, (struct sockaddr *) & 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;
      return (0);
    }
    clientDead = 1;
    return (0);
  }
  bufptr = buf;
  while (bufptr < buf + count)
  {
    if (*bufptr < 1 ||
	(unsigned char) *bufptr > NUM_PACKETS ||
	size_of_cpacket((void *) bufptr) == 0)
    {
      printf("Unknown packet type: %d, aborting...\n", *bufptr);
      return (0);
    }
    size = size_of_cpacket(bufptr);
    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...");
	fprintf(stderr, "1a) read() failed (%d, error %d)\n",
		count, errno);
	clientDead = 1;
	return (0);
      }
      temp = read(asock, buf + count, size - (count + (buf - bufptr)));
      if (temp <= 0)
      {
	if (errno != EINTR)
	{
	  sprintf(buf, "Died in second read(), return=%d", temp);
	  logmessage(buf);
	  fprintf(stderr, "2) read() failed (%d, error %d)\n",
		  count, errno);
	  clientDead = 1;
	  return (0);
	}
      }
      else
	count += temp;
    }
    /*
     * 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.
     */
#if 0
    {
      FILE *logfile;
      char *paths;
      if (blk_metaserver)
      {
	paths = build_path("logs/metaserver.log");
	logfile = fopen(paths, "a");
	if (logfile)
	{
	  fprintf(logfile, "Receiving packet type %d\n", (int) *bufptr);
	  fclose(logfile);
	}
      }
    }
#endif
    packetsReceived[(unsigned char) *bufptr]++;

    if (asock == udpSock)
      packets_received++;

    if (handlers[(unsigned char) *bufptr].handler != NULL)
    {
      if (input_allowed(*bufptr))
      {
	if (me && me->p_flags & PFSELFDEST
	    && *bufptr != CP_PING_RESPONSE)
	{
	  me->p_flags &= ~PFSELFDEST;
	  warning("Self Destruct has been canceled");
	}
	(*(handlers[(unsigned char) *bufptr].handler)) (bufptr);
      }
      /* Otherwise we ignore the request */
    }
    else
    {
      printf("Handler for packet %d not installed...\n", *bufptr);
    }
    bufptr += size;
    if (bufptr > buf + BUFSIZ)
    {
      memcpy(buf, buf + BUFSIZ, 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);
	    fprintf(stderr, "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);
}

void
handleTorpReq(packet)
  struct torp_cpacket *packet;
{
  ntorp((CARD8) packet->dir, (int) TMOVE);
}

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

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

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

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

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

void
handleOrbitReq(packet)
  struct orbit_cpacket *packet;
{
  if (packet->state)
  {
    orbit();
  }
  else
  {
    me->p_flags &= ~PFORBIT;
#if 0
    planets[me->p_planet].pl_torbit &= ~me->p_team;
#endif
    if (me->p_flags & PFDOCK)
    {
      if (players[me->p_docked].p_speed > 4)
      {
	warning("It's unsafe to disengage from bases while over warp 4.");
	return;
      }
      else
	undock_player(me);
    }
  }
}

/*-----------------------------PRACTICE_ROBOT-----------------------------*/
/*
 * Send in a practice robot.  You can only bring i a practice robot if no
 * other players are in the game.
 */

static void
practice_robo()
{
  char *paths;			/* to hold dot dir path */
  char *arg1;
  register int i;		/* looping var */
  register struct player *j;	/* to point to players */
  static struct timeval space = {0, 0};

  if (!temporally_spaced(&space, 1000000))	/* damn auto-repeating... */
    return;

  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
  {
    if (j->p_status != PALIVE)	/* if payer not alive, that's ok */
      continue;
    if (j == me)		/* ignore myself */
      continue;
    warning("Can't send in practice robot with other players in the game.");
    return;			/* another player discovered--out of here */
  }

  if (fork() == 0)
  {				/* do if fork successful */
    (void) r_signal(SIGALRM, SIG_DFL);
    (void) close(0);
    (void) close(1);
    (void) close(2);
    switch (me->p_team)
    {				/* decide which teaem robot is on */
     case FED:
      arg1 = "-Tf";		/* fed option */
      break;
     case ROM:
      arg1 = "-Tr";		/* rom option */
      break;
     case KLI:
      arg1 = "-Tk";		/* klingon option */
      break;
     case ORI:
      arg1 = "-To";		/* orion option */
      break;
     default:
      arg1 = "-Ti";		/* in case something screwy happens */
      break;
    }

    paths = build_path(ROBOT);

    execl(paths, "robot", arg1, "-p", "-f", "-h", 0);
    _exit(1);			/* failure :(  died at birth */
  }
}

/*--------------------------------------------------------------------------*/

/* ARGSUSED */
void
handlePractrReq(packet)
  struct practr_cpacket *packet;
{
  practice_robo();
}

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

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

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

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

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

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

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

  pickPack.type = SP_PICKOK;
  pickPack.state = state;
  sendClientPacket((struct player_spacket *) & pickPack);
}

void
handleLoginReq(packet)
  struct login_cpacket *packet;
{
  if (packet->pad2 == 0x69)
  {
    if (packet->pad3 == 0x42)
      blk_flag = 1;		/* added 1/19/93 KAO */
    if (packet->pad3 == 0x43)
      blk_flag = 2;
  }
  strncpy(namePick, packet->name, 16);
  namePick[15] = 0;
  strncpy(passPick, packet->password, 16);
  passPick[15] = 0;
  /* Is this a name query or a login? */
  if (packet->query)
  {
    passPick[15] = 1;
  }
  strncpy(login, packet->login, 16);
  login[15] = 0;
}

void
sendClientLogin(stats)
  struct stats *stats;
{
  struct login_spacket logPacket;
  logPacket.pad2 = 69;
  if (configvals->galaxygenerator == 4)
    logPacket.pad3 = 88;
  else
    logPacket.pad3 = 42;
  logPacket.type = SP_LOGIN;
  if (stats == NULL)
  {
    logPacket.accept = 0;
  }
  else
  {
    logPacket.accept = 1;
    logPacket.flags = htonl(stats->st_flags);
  }
  sendClientPacket((struct player_spacket *) & logPacket);
}

void
handlePlasmaReq(packet)
  struct plasma_cpacket *packet;
{
  if (me->p_specweap & SFNHASMISSILE)
  {
    fire_missile_dir(packet->dir);
  }
  else if (me->p_specweap & SFNPLASMAARMED)
  {
    nplasmatorp(packet->dir, PTMOVE);
  }
  else
  {
    warning("This ship is armed with no special weapons");
  }
}

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

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

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

void
handleDetMReq(packet)
  struct det_mytorp_cpacket *packet;
{
  struct torp *atorp;
  short t;

  /* you can det individual torps */
  t = ntohs(packet->tnum);
  if (t < 0)
  {
    struct missile *dr;
    int i, any;

    any = 0;

    for (i = 0; i < MAXTORP; i++)
    {
      atorp = &torps[me->p_no * MAXTORP + i];
      if (atorp->t_status == TMOVE || atorp->t_status == TSTRAIGHT)
      {
	atorp->t_status = TOFF;
	any = 1;
      }
    }

    if (any)
      return;

    for (i = 0; i < NPTHINGIES; i++)
    {
      dr = &missiles[me->p_no * NPTHINGIES + i];
      if (dr->ms_status == TMOVE || dr->ms_status == TSTRAIGHT)
      {
	switch (dr->ms_type)
	{
	 case MISSILETHINGY:
	  dr->ms_status = TOFF;
	  break;
	 case FIGHTERTHINGY:
	  dr->ms_status = TRETURN;
	  break;
	}
	any = 1;
      }
    }

    /* any ? */
  }
  else
  {

    if (t < 0 || t >= MAXPLAYER * MAXTORP)
      return;
    atorp = &torps[t];

    if (atorp->t_owner != me->p_no)
      return;
    if (atorp->t_status == TMOVE || atorp->t_status == TSTRAIGHT)
    {
      atorp->t_status = TOFF;
    }
  }
}

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

  if (weaponsallowed[WP_TRACTOR] == 0)
  {
    warning("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)
  {
    warning("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 (me->p_flags & PFDOCK && players[me->p_docked].p_speed > 4)
  {
    warning("It's unsafe to tractor while docked and moving at a warp greater then 4.");
    return;
  }
  if (ihypot(me->p_x - player->p_x, me->p_y - player->p_y) <
      (TRACTDIST) * me->p_ship.s_tractrng)
  {
    undock_player(me);
    me->p_flags &= ~PFORBIT;
#if 0
    undock_player(player);	/* harmless if they're not docked */

#if 0
    if (player->p_flags & PFORBIT)
      planets[player->p_planet].pl_torbit &= ~player->p_team;
    if (me->p_flags & PFORBIT)
      planets[me->p_planet].pl_torbit &= ~me->p_team;
#endif
    player->p_flags &= ~(PFORBIT | PFDOCK);
    me->p_flags &= ~(PFORBIT | PFDOCK);
#endif
    me->p_tractor = target;
    me->p_flags |= PFTRACT;

  }
  else
  {
    warning("Weapon's Officer:  Vessel is out of range of our tractor beam.");
  }
}

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

  if (weaponsallowed[WP_TRACTOR] == 0)
  {
    warning("Pressor 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)
  {
    warning("Weapons's Officer:  Cannot pressor while cloaked, sir!");
    return;
  }
  if (target < 0 || target >= MAXPLAYER || target == me->p_no)
    return;
  player = &players[target];
  if (player->p_flags & PFCLOAK)
    return;
  if (me->p_flags & PFDOCK && players[me->p_docked].p_speed > 4)
  {
    warning("It's unsafe to pressor while docked and moving at a warp greater then 4.");
    return;
  }
  if (ihypot(me->p_x - player->p_x, me->p_y - player->p_y) <
      (TRACTDIST) * me->p_ship.s_tractrng)
  {
    undock_player(me);
    me->p_flags &= ~PFORBIT;
#if 0
    undock_player(player);

#if 0
    if (player->p_flags & PFORBIT)
      planets[player->p_planet].pl_torbit &= ~player->p_team;
    if (me->p_flags & PFORBIT)
      planets[me->p_planet].pl_torbit &= ~me->p_team;
#endif
    player->p_flags &= ~(PFORBIT | PFDOCK);
    me->p_flags &= ~(PFORBIT | PFDOCK);
#endif
    me->p_tractor = target;
    me->p_flags |= (PFTRACT | PFPRESS);
  }
  else
  {
    warning("Weapon's Officer:  Vessel is out of range of our pressor beam.");
  }
}

void
sendMotdLine(line)
  char *line;
{
  struct motd_spacket motdPacket;

  motdPacket.type = SP_MOTD;
  strncpy(motdPacket.line, line, 80);
  motdPacket.line[79] = '\0';
  sendClientPacket((struct player_spacket *) & motdPacket);
}

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

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

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

  int isCensured();		/* "shut up and play" code 7/21/91 TC */
  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)
  {
    warning("Be quiet");
    balance += 3;
    if (balance > time(NULL) + 60)
    {
      balance = time(NULL) + 60;
    }
    return;
  }
  balance += 3;
  /* packet->mesg[69] = '\0'; */
  sprintf(addrbuf, " %s->", twoletters(me));
  if (parseIgnore(packet))
    return;			/* moved this up 4/6/92 TC */
  if (packet->group & MGOD)
  {
    strcpy(addrbuf + 5, "GOD");
  }
  else if (packet->group & MALL)
  {
    sprintf(addrbuf + 5, "ALL");
    if (isCensured(me->p_login))
    {
      warning("You are censured.  Message was not sent.");
      return;
    }
  }
  else if (packet->group & MTEAM)
  {
    if (packet->indiv != FED && packet->indiv != ROM &&
	packet->indiv != KLI && packet->indiv != ORI)
      return;
    if (isCensured(me->p_login) && (packet->indiv != me->p_team))
    {
      warning("You are censured.  Message was not sent.");
      return;
    }
    sprintf(addrbuf + 5, teams[packet->indiv].shortname);
  }
  else if (packet->group & MINDIV)
  {
    if (packet->indiv < 0 || packet->indiv >= MAXPLAYER)
      return;
    if (players[packet->indiv].p_status == PFREE)
      return;
    if (isCensured(me->p_login) && (players[packet->indiv].p_team != me->p_team))
    {
      warning("You are censured.  Message was not sent.");
      return;
    }
    if (ignored[packet->indiv] & MINDIV)
    {
      warning("You are ignoring that player.  Message was not sent.");
      return;
    }
    if ((me->p_team != players[packet->indiv].p_team) &&
	(isCensured(players[packet->indiv].p_login)))
    {
      warning("That player is censured.  Message was not sent.");
      return;
    }
    sprintf(addrbuf + 5, "%s ", twoletters(&players[packet->indiv]));
  }
  else
  {
    return;
  }
#if 0
  if ((packet->group == MGOD
       || me->p_no == packet->indiv && packet->group == MINDIV)
      && parse_command_mess(packet->mesg, me->p_no))
  {
    /* message parsed. Eat it */
  }
  else
  {
    pmessage2(packet->mesg, packet->indiv, packet->group, addrbuf, me->p_no);
  }
#else
  /* don't eat the parsed messages */
  pmessage2(packet->mesg, packet->indiv, packet->group, addrbuf, me->p_no);
  if (me->p_no == packet->indiv && packet->group == MINDIV)
  {
    char tmpbuf[sizeof(packet->mesg) + 1];
    strncpy(tmpbuf, packet->mesg, sizeof(packet->mesg));
    tmpbuf[sizeof(packet->mesg)] = 0;
    parse_command_mess(tmpbuf, me->p_no);
  }
#endif

  /*
   * Blech !!!
   * 
   * Don't do this:
   * 
   * if (me->p_no == packet->indiv && packet->mesg[0] == '_') me = &players[0];
   */
}

/* ARGSUSED */
void
handleQuitReq(packet)
  struct quit_cpacket *packet;
{
  if (me->p_status == POBSERVE)
  {
    me->p_status = PTQUEUE;
    return;
  }
  me->p_flags |= PFSELFDEST;

  switch (me->p_ship.s_type)
  {
   case STARBASE:
   case WARBASE:
    selfdest = 60;
    break;
   default:
    selfdest = 10;
  }

  selfdest = me->p_updates + selfdest * 10;

  warning("Self destruct initiated");
}

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

  maskPacket.type = SP_MASK;
  maskPacket.mask = mask;
  sendClientPacket((struct player_spacket *) & maskPacket);
}

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

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

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

int
checkVersion()
{
  struct badversion_spacket packet;

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

void
logEntry()
{
  FILE *logfile;
  int curtime;
  char *paths;

  paths = build_path(LOGFILENAME);
  logfile = fopen(paths, "a");
  if (!logfile)
    return;
  curtime = time(NULL);

#ifdef LOG_LONG_INFO		/*-[ prints out long info to the log files ]-*/

  fprintf(logfile, "Joining: %s, (%c) <%s@%s> %s", me->p_name,
	  shipnos[me->p_no],
	  me->p_login,		/* debug 2/21/92 TMC */

#else

  fprintf(logfile, "Joining: %s <%s@%s> %s", me->p_name, me->p_login,

#endif				/*-[ LOG_LONG_INFO ]-*/

	  me->p_full_hostname,
	  ctime((time_t *) & curtime));

  fclose(logfile);
}

/* gwrite was here */

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

  if (allows_docking(me->p_ship))
  {
    if (me->p_speed > 4 && me->p_docked)
    {
      warning("It's unsafe to disengage other ships while over warp 4.");
      return;
    }
    else
    {
      for (i = 0; i < me->p_ship.s_numports; i++)
	base_undock(me, i);
      me->p_docked = 0;

      if (packet->state)
	me->p_flags |= PFDOCKOK;
      else
	me->p_flags &= ~PFDOCKOK;
    }
  }
}

void
handleReset(packet)
  struct resetstats_cpacket *packet;
{
  extern int startTkills, startTlosses, startTarms, startTplanets, startTticks;

  if (packet->verify != 'Y')
    return;

  /* Gee, they seem to want to reset their stats!  Here goes... */
#if 0
  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;

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

  mystats->st_genocides = 0;
  mystats->st_tmaxkills = 0.0;
  mystats->st_di = 0.0;
  mystats->st_tkills = 0;
  mystats->st_tlosses = 0;
  mystats->st_tarmsbomb = 0;
  mystats->st_tresbomb = 0;
  mystats->st_tdooshes = 0;
  mystats->st_tplanets = 0;
  mystats->st_tticks = 1;
  mystats->st_sbkills = 0;
  mystats->st_sblosses = 0;
  mystats->st_sbmaxkills = 0.0;
  mystats->st_sbticks = 1;
  mystats->st_wbkills = 0;
  mystats->st_wblosses = 0;
  mystats->st_wbmaxkills = 0.0;
  mystats->st_wbticks = 1;
  mystats->st_jsplanets = 0;
  mystats->st_jsticks = 1;
  mystats->st_rank = 0;
  mystats->st_royal = 0;

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

void
handleUpdatesReq(packet)
  struct updates_cpacket *packet;
{
  struct itimerval udt;
  extern int interrupting;	/* main.c */
  int min_delay = me->p_observer
  ? configvals->min_observer_upd_delay
  : configvals->min_upd_delay;

  timerDelay = ntohl(packet->usecs);
  if (timerDelay < min_delay)
    timerDelay = min_delay;
  if (timerDelay >= 1000000)
    timerDelay = 999999;

  if (interrupting)
  {				/* only setitimer if the ntserv is configured
				 * to handle it.  It's NOT configured to
				 * handle it in the outfit loop... */
    udt.it_interval.tv_sec = 0;
    udt.it_interval.tv_usec = timerDelay;
    udt.it_value.tv_sec = 0;
    udt.it_value.tv_usec = timerDelay;
    setitimer(ITIMER_REAL, &udt, 0);
  }

}

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

  paths = build_path(LOGFILENAME);

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

#if 0
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 (memcmp(packet->data, testdata, 16) != 0)
  {
    testtime = 1;
    return;
  }
  memcpy(mysp.data, testdata, 16);
  if (gethostname(serverName, 64))
    fprintf(stderr, "gethostname() error\n");	/* 8/2/92 TC */
  encryptReservedPacket(&mysp, &mycp, serverName, me->p_no);
  if (memcmp(packet->resp, mycp.resp, 16) != 0)
  {
    fprintf(stderr, "User verified incorrectly.\n");
    testtime = 1;
    return;
  }
  testtime = 0;
}

#ifdef ATM_STUFF
void 
dummy_()
{
  if ((configvals->binconfirm == 2) &&
      !strcmp(packet->resp, "Cyborg"))
  {
    testtime = 0;		/* accept */
    cyborg = 1;
    if (me->p_name[0] != '+')
    {
      temp[0] = '+';		/* indicate cyborg */
      strcpy(temp + 1, me->p_name);	/* this happens AFTER entry, */
      temp[15] = '\0';		/* so changing enter() isn't */
      strcpy(me->p_name, temp);	/* sufficient */
    }
    return;
  }

  if (memcmp(packet->resp, mycp.resp, 16) != 0)
  {
    fprintf(stderr, "User verified incorrectly.\n");
    testtime = 1;
    return;
  }

  testtime = 0;
}

#endif
#else

void
handleRSAKey(packet)
  struct rsa_key_cpacket *packet;
{
#ifdef AUTHORIZE
  struct rsa_key_spacket mysp;
  char serverName[64];

  if (testtime == 1)
    return;
  if (RSA_Client != 1)
    return;
  memcpy(mysp.data, testdata, KEY_SIZE);
  if (gethostname(serverName, 64))
    fprintf(stderr, "gethostname() error\n");
  if (decryptRSAPacket(&mysp, packet, serverName))
  {
    fprintf(stderr, "User verified incorrectly.\n");
    testtime = 1;
    return;
  }
  testtime = 0;
#endif				/* AUTHORIZE */
}

void
handleReserved(packet)
  struct reserved_cpacket *packet;
{
#ifdef AUTHORIZE
  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 (memcmp(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;
    warning(RSA_VERSION);
    if (!strncmp(packet->resp, RSA_VERSION, strlen("RSA v??")))
    {
      /* This is the right major version */
      RSA_Client = 1;
      makeRSAPacket(&rsp);
      memcpy(testdata, rsp.data, KEY_SIZE);
      sendClientPacket((struct player_spacket *) & rsp);
      return;
    }
    testtime = 1;
    return;
  }
  memcpy(mysp.data, testdata, RESERVED_SIZE);
  if (gethostname(serverName, 64))
    fprintf(stderr, "gethostname() error\n");	/* 8/2/92 TC */
  encryptReservedPacket(&mysp, &mycp, serverName, me->p_no);
  if (memcmp(packet->resp, mycp.resp, RESERVED_SIZE) != 0)
  {
    fprintf(stderr, "User verified incorrectly.\n");
    testtime = 1;
    return;
  }
  /* Use .sysdef CONFIRM flag to allow old style clients. */
  if (configvals->binconfirm == 2)
    testtime = 0;
  else
    testtime = 1;

#endif				/* AUTHORIZE */
}

#endif

void
handleScan(packet)		/* ATM */
  struct scan_cpacket *packet;
{
#if 0
  struct scan_spacket response;
  struct player *pp;

  memset(&response, 0, sizeof(struct scan_spacket));
  response.type = SP_SCAN;
  response.pnum = packet->pnum;
  if (!weaponsallowed[WP_SCANNER])
  {
    warning("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((struct player_spacket *) & response);
#endif
}


void
handlePingResponse(packet)
  struct ping_cpacket *packet;
{
  char buf[80];
  /* client requests pings by sending pingme == 1 on TCP socket */

  if (rsock == sock)
  {
    if (!ping && packet->pingme == 1)
    {
      ping = 1;
      sprintf(buf, "Server sending ping packets at %d second intervals",
	      configvals->ping_period);
      warning(buf);
      return;
    }
    /* client says stop */
    else if (ping && !packet->pingme)
    {
      ping = 0;
      warning("Server no longer sending ping packets.");
      return;
    }
  }
  pingResponse(packet);		/* ping.c */
}

#ifdef SHORT_PACKETS

void 
handleShortReq(packet)
  struct shortreq_cpacket *packet;
{
  struct shortreply_spacket resp;

  switch (packet->req)
  {
   case SPK_VOFF:
    send_short = 0;
    warning("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)
    {
      warning("Your SHORT Protocol Version is not right!");
      packet->req = SPK_VOFF;
      break;
    }
    if (!send_short)
      warning("Sending variable and short packets. ");	/* send only firsttime */
    send_short = 1;
    resp.winside = ntohs(WINSIDE);
    resp.gwidth = ntohl(GWIDTH);
    break;

   case SPK_MOFF:
    send_mesg = 1;
    warning("Obsolete!");
    packet->req = SPK_MON;
    break;

   case SPK_MON:
    send_mesg = 1;
    warning("All messages sent.");
    break;

   case SPK_M_KILLS:
    send_kmesg = 1;
    warning("Kill messages sent");
    break;

   case SPK_M_NOKILLS:
    send_kmesg = 1;
    warning("Obsolete!");
    packet->req = SPK_M_KILLS;
    break;

   case SPK_M_WARN:
    send_warn = 1;
    warning("Warn messages sent");
    break;

   case SPK_M_NOWARN:
    send_warn = 1;
    warning("Obsolete!");
    packet->req = SPK_M_WARN;
    break;

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

   case SPK_ALL:
    if (send_short)
    {
      spk_update_sall = 0;
      spk_update_all = 1;
      forceUpdate();
    }
    else
      warning("Activate SHORT Packets first!");
    return;

   default:
    warning("Unknown short packet code");
    return;
  }

  resp.type = SP_S_REPLY;
  resp.repl = (char) packet->req;

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

void 
handleThresh(packet)
  struct threshold_cpacket *packet;
{
  send_threshold = packet->thresh;
#ifdef SHORT_THRESHOLD
  if (send_threshold == 0)
  {
    actual_threshold = 0;
    warning("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 */
      sprintf(buf, "Threshold set to %d .  %d / Update(Server limit!)",
	      numupdates * 60, 60);
      warning(buf);
    }
    else
    {
      sprintf(buf, "Threshold set to %d .  %d / Update", send_threshold, actual_threshold);
      warning(buf);
    }
  }
#else
  warning("Server is compiled without Thresholdtesting!");
#endif
}

void 
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;
  strcpy(mesPacket.mesg, packet->mesg);
  handleMessageReq(&mesPacket);
  /* I hope this was it */
}


#endif

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

void
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
    warning("WARNING: BROKEN mode is enabled");
#endif

    resetUDPsequence();		/* 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)
    {
      warning("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:
	warning("Server will send with TCP only");
	break;
       case MODE_SIMPLE:
	warning("Server will send with simple UDP");
	break;
       case MODE_FAT:
	warning("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:
	warning("Server will send with double UDP");
	scbufptr = scbuf + sizeof(struct sc_sequence_spacket);
	break;
#else
       case MODE_DOUBLE:
	warning("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((struct player_spacket *) & 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 (!configvals->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)
      {
	char buf[80];
	sprintf(buf, "Server UDP is v%.1f, client is v%.1f",
		(float) UDPVERSION / 10.0,
		(float) userUdpVersion / 10.0);
	warning(buf);
	UDPDIAG(("%s (rejected %s)\n", buf, 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() */
	int t = sizeof(response);
	if (gwrite(udpSock, (char *) &response, sizeof(response)) != t)
	{
	  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
  {
    fprintf(stderr, "ntserv: got weird UDP request (%d)\n",
	    packet->request);
    return;
  }
send:
  sendClientPacket((struct player_spacket *) & response);
}


int
connUdpConn()
{
  struct sockaddr_in addr;
  int len;

  if (udpSock > 0)
  {
    fprintf(stderr, "ntserv: tried to open udpSock twice\n");
    return (0);			/* pretend we succeeded (this could be bad) */
  }
  resetUDPbuffer();
  if ((udpSock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    perror("ntserv: unable to create DGRAM socket");
    return (-1);
  }
  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, (struct sockaddr *) & addr, sizeof(addr)) < 0)
  {
    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, (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);

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

  return (0);
}

int
closeUdpConn()
{
  V_UDPDIAG(("Closing UDP socket\n"));
  if (udpSock < 0)
  {
    fprintf(stderr, "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 */
void
printUdpInfo()
{
  struct sockaddr_in addr;
  int len;

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

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

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

void
handleAskMOTD()
{
  sendMotd();
}


#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.
 */
void
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;
  if ((cc = gwrite(udpSock, scbuf, scbufptr - scbuf)) != scbufptr - scbuf)
  {
    fprintf(stderr, "UDP sc gwrite failed (%d, error %d)\n", cc, errno);
    UDPDIAG(("*** UDP diSConnected 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...)
 */
void
forceUpdate()
{
  static time_t lastone = 0;
  time_t now;
  int i;

  now = time(0);
  if (now - lastone < UDP_UPDATE_WAIT)
  {
    warning("Update request DENIED (chill out!)");
    return;
  }
  lastone = now;

  /* clientDead=0; */
  for (i = 0; i < MAXPLAYER; i++)
  {
    clientHostile[i].hostile = -1;
    clientStats[i].losses = -1;	/* (non-critical, but nice) */
    /* clientLogin[i].rank= -1;		(critical) */
    /* clientPlayersInfo[i].shiptype= -1;	(critical) */
    /* clientPStatus[i].status= -1;		(critical) */
    clientPlayers[i].x = htonl(-1);	/* (non-critical, but nice) */
    clientPhasers[i].status = -1;
    clientKills[i].kills = htonl(-1);	/* (non-critical, but nice) */
    clientFlags[i].flags = htonl(-1);
    mustUpdate[i] = 0;
  }
  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) */
  }
  for (i = 0; i < TOTALTHINGIES; i++)
  {

    clientThingysInfo[i].shape = htons(-1);
    /* clientThingys[i].x= -1;			(non-critical) */
  }
  for (i = 0; i < MAXPLANETS; i++)
  {
    clientPlanets2[i].armies = htonl(-2);
    /* clientPlanetLocs[i].x= htonl(-1);	(critical) */
  }
  /* msgCurrent=(mctl->mc_current+1) % MAXMESSAGE; */
  clientSelf.pnum = -1;
}

int
isCensured(s)			/* return true if cannot message opponents */
  char *s;
{
  return (
#if 0
	  (strncmp(s, "am4m", 4) == 0) ||	/* 7/21/91 TC */
	  (strncmp(s, "dm3e", 4) == 0) ||	/* 7/21/91 TC */
	  (strncmp(s, "gusciora", 8) == 0) ||	/* 7/25/91 TC */
	  (strncmp(s, "flan", 4) == 0) ||	/* 4/2/91 TC */
	  (strncmp(s, "kc3b", 4) == 0) ||	/* 4/4/91 TC */
	  (strncmp(s, "windom", 6) == 0) ||	/* 7/20/92 TC */
#endif
	  0
    );
}

/* return true if you should eat message */

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

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

  s = packet->mesg;

  who = packet->indiv;
  if ((*s != ':') && (strncmp(s, "     ", 5) != 0))
    return 0;
  if ((who == me->p_no) || (*s == ' '))
  {				/* check for borg call 4/6/92 TC */
    if (configvals->binconfirm)
      warning("No cyborgs allowed in the game at this time.");
    else
    {
      char buf[80];
      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 ", twoletters(&players[i]));
	  strcat(buf, buf2);
	  cybflag = 1;
	}
      if (!cybflag)
	strcat(buf, "None");
      warning(buf);
    }
    if (*s != ' ')		/* if not a borg call, eat msg 4/6/92 TC */
      return 1;
    else
      return 0;			/* otherwise, send it 4/6/92 TC */
  }
  if (packet->group != MINDIV)
    return 0;			/* 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 = 1;
  if (ignored[who] & MALL)
  {
    strcat(buf, "All ");
    noneflag = 0;
  }
  if (ignored[who] & MTEAM)
  {
    strcat(buf, "Team ");
    noneflag = 0;
  }
  if (ignored[who] & MINDIV)
  {
    strcat(buf, "Indiv ");
    noneflag = 0;
  }
  if (noneflag)
    strcat(buf, "None");
  warning(buf);
  return 1;
}

/* give session stats if you send yourself a '?' 2/27/92 TC */
/* or '!' for ping stats (HAK) */
/* merged RSA query '#' here, too (HAK) */
/* return true if you should eat message */

int
parseQuery(packet)
  struct mesg_spacket *packet;	/* was cpacket 4/17/92 TC */
{
  char buf[80];
  float sessionBombing, sessionPlanets, sessionOffense, sessionDefense;
  int deltaArmies, deltaPlanets, deltaKills, deltaLosses, deltaTicks;

  extern int startTkills, startTlosses, startTarms, startTplanets, startTticks;

  /* 0-8 for address, 9 is space */

  if (packet->mesg[11] != '\0')	/* one character only */
    return 0;

  switch (packet->mesg[10])
  {
   case '!':
    return bouncePingStats(packet);
   case '#':
    sprintf(buf, "Client: %s", RSA_client_type);
    bounce(buf, packet->m_from);
    return 1;
   case '?':
    deltaPlanets = me->p_stats.st_tplanets - startTplanets;
    deltaArmies = me->p_stats.st_tarmsbomb - startTarms;
    deltaKills = me->p_stats.st_tkills - startTkills;
    deltaLosses = me->p_stats.st_tlosses - startTlosses;
    deltaTicks = me->p_stats.st_tticks - startTticks;

    if (deltaTicks == 0)
      return 1;			/* can happen if no tmode */

    sessionPlanets = (float) deltaPlanets *status->timeprod /
        ((float) deltaTicks * status->planets);

    sessionBombing = (float) deltaArmies *status->timeprod /
        ((float) deltaTicks * status->armsbomb);

    sessionOffense = (float) deltaKills *status->timeprod /
        ((float) deltaTicks * status->kills);

    sessionDefense = (float) deltaTicks *status->losses /
        (deltaLosses != 0 ?
	     ((float) deltaLosses * status->timeprod) :
	     (status->timeprod));

    sprintf(buf, "%2s stats: %d planets and %d armies. %d wins/%d losses. %5.2f hours.",
	    twoletters(me),
	    deltaPlanets,
	    deltaArmies,
	    deltaKills,
	    deltaLosses,
	    (float) deltaTicks / 36000.0);
    bounce(buf, packet->m_from);
    sprintf(buf, "Ratings: Pla: %5.2f  Bom: %5.2f  Off: %5.2f  Def: %5.2f  Ratio: %4.2f",
	    sessionPlanets,
	    sessionBombing,
	    sessionOffense,
	    sessionDefense,
	    (float) deltaKills /
	    (float) ((deltaLosses == 0) ? 1 : deltaLosses));
    bounce(buf, packet->m_from);
    return 1;
   default:
    return 0;
  }
  /* NOTREACHED */
}

int
bouncePingStats(packet)
  struct mesg_spacket *packet;
{
  char buf[80];

  if (me->p_avrt == -1)
  {
    /* client doesn't support it or server not pinging */
    sprintf(buf, "No ping stats available for %s",
	    twoletters(me));
  }
  else
  {
    sprintf(buf, "%s ping stats: Average: %d ms, Stdv: %d ms, Loss: %d%%",
	    twoletters(me),
	    me->p_avrt,
	    me->p_stdv,
	    me->p_pkls);
  }
  bounce(buf, packet->m_from);

  return 1;
}

/* new code, sends bouncemsg to bounceto from GOD 4/17/92 TC */
void
bounce(bouncemsg, bounceto)
  char *bouncemsg;
  int bounceto;
{
  char buf[10];

  sprintf(buf, "GOD->%s", twoletters(&players[bounceto]));
  pmessage(bouncemsg, bounceto, MINDIV, buf);
}


/*
 */

void
sendShipCap()
{
  struct ship_cap_spacket temppack;
  struct ship ship;
  int i;

  if (!blk_flag)
    return;
  for (i = 0; i < NUM_TYPES; i++)
  {
    getship(&ship, i);
    temppack.type = SP_SHIP_CAP;
    temppack.operation = 0;
    temppack.s_type = htons(ship.s_type);
    temppack.s_torpspeed = htons(ship.s_torp.speed);
#if 1
    temppack.s_phaserrange = htons(ship.s_phaser.speed);
#else
    temppack.s_phaserrange = htons(ship.s_phaser.damage);
#endif
    temppack.s_maxspeed = htonl(ship.s_imp.maxspeed);
    temppack.s_maxfuel = htonl(ship.s_maxfuel);
    temppack.s_maxshield = htonl(ship.s_maxshield);
    temppack.s_maxdamage = htonl(ship.s_maxdamage);
    temppack.s_maxwpntemp = htonl(ship.s_maxwpntemp);
    temppack.s_maxegntemp = htonl(ship.s_maxegntemp);
    temppack.s_width = htons(ship.s_width);
    temppack.s_height = htons(ship.s_height);
    temppack.s_maxarmies = htons(ship.s_maxarmies);
    temppack.s_letter = ship.s_letter;
    temppack.s_desig1 = ship.s_desig1;
    temppack.s_desig2 = ship.s_desig2;
    if (blk_flag == 1)
      temppack.s_bitmap = htons(ship.s_alttype);
    else
      temppack.s_bitmap = htons(ship.s_bitmap);
    sendClientPacket((struct player_spacket *) & temppack);
  }
}

void
sendMotdPic(x, y, bits, page, width, height)
  int x;
  int y;
  char *bits;
  int page;
  int width;
  int height;
{
  struct motd_pic_spacket temppack;
  short sx, sy, sp, sw, sh;
  int size;

  size = (width / 8 + (width % 8 != 0)) * height;
  sx = x;
  sy = y;
  sp = page;
  sw = width;
  sh = height;
  temppack.type = SP_MOTD_PIC;
  temppack.x = htons(sx);
  temppack.y = htons(sy);
  temppack.width = htons(sw);
  temppack.height = htons(sh);
  temppack.page = htons(sp);
  memcpy(temppack.bits, bits, size);

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


void
sendMotdNopic(x, y, page, width, height)
  int x;
  int y;
  int page;
  int width;
  int height;
{
  struct pe1_missing_bitmap_spacket temppack;

  temppack.type = SP_PARADISE_EXT1;
  temppack.subtype = SP_PE1_MISSING_BITMAP;
  temppack.page = htons((short) page);
  temppack.x = htons((short) x);
  temppack.y = htons((short) y);
  temppack.width = htons((short) width);
  temppack.height = htons((short) height);

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

/* tells the client how many missiles carried [BDyess] */
void
sendMissileNum(num)
  int num;
{

  /* remove the 1 || to enable missile updates [BDyess] */
  if (clientMissiles.num == htons(num))
    return;

  clientMissiles.type = SP_PARADISE_EXT1;
  clientMissiles.subtype = SP_PE1_NUM_MISSILES;
  clientMissiles.num = htons(num);

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

#ifdef RSA_EXEMPTION_FILE

/*
 * this code was copied from
 * 
 * portname.c, part of faucet and hose: network pipe utilities Copyright (C)
 * 1992 Robert Forsman
 * 
 * He has granted the Paradise project permission to use this code for
 * non-profit purposes.
 * 
 */

int 
convert_hostname(char *name, struct in_addr * addr)
{
  struct hostent *hp;
  int len;

  hp = gethostbyname(name);
  if (hp != NULL)
    memcpy(addr, hp->h_addr, hp->h_length);
  else
  {
    int count;
    unsigned int a1, a2, a3, a4;

    count = sscanf(name, "%i.%i.%i.%i%n", &a1, &a2, &a3, &a4, &len);

    if (4 != count || 0 != name[len])
      return 0;

    addr->s_addr = (((((a1 << 8) | a2) << 8) | a3) << 8) | a4;
  }
  return 1;
}

/*
 * figure out if our client is exempt from RSA authentication.
 * 
 * The host name resolution above doesn't handle gateways, which can have more
 * than one internet address :/
 */

int 
site_rsa_exempt()
{
  FILE *fp;
  char buf[256];

  if (remoteaddr == -1)
  {
    printf("remote address is not yet available?!\n");
    return 0;			/* weird */
  }

  /* hopefully we've got the remote address at this point */

  fp = fopen(build_path(RSA_EXEMPTION_FILE), "r");

  if (!fp)
    return 0;			/* nobody is exempt */

  while (fgets(buf, sizeof(buf), fp))
  {
    char hostname[256];
    char *playername;
    int len;
    int i;
    struct in_addr addr;

    len = strlen(buf);

    if (buf[len - 1] == '\n')
      buf[len - 1] = 0;

    for (i = 0; buf[i] && !isspace(buf[i]); i++)
      hostname[i] = buf[i];

    hostname[i] = 0;		/* hostname is copied to buffer */

    while (buf[i] && isspace(buf[i]))
      i++;

    playername = buf + i;	/* player name is stuff after hostname */

    if (!(*playername == 0 || strcmp(playername, me->p_name) == 0))
      continue;			/* name didn't match */

    /*
     * shit, I gotta parse this crap myself.  I'll steal this code from hose
     * - RF
     */
    if (!convert_hostname(hostname, &addr))
    {
      printf("address in %s unparseable `%s'\n",
	     RSA_EXEMPTION_FILE, hostname);
      continue;
    }

    if (addr.s_addr == remoteaddr)
      return 1;
  }				/* while (line in rsa-exempt file) */

  fclose(fp);

  return 0;
}

#endif				/* RSA_EXEMPTION_FILE */