view src/daemonII.c @ 2:2719a89505ba

First entry of Paradise Server 2.9 patch 10 Beta
author darius
date Sat, 06 Dec 1997 04:37:01 +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
--------------------------------------------------------------------------*/
char binary[] = "@(#)daemonII";

#define DAEMONII 1		/* to tell daemonII.h we are in this file */

#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/file.h>
#include <setjmp.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#ifndef ULTRIX
#include <sys/fcntl.h>
#endif
#include <string.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <math.h>
#include <errno.h>

#include "defs.h"
#include "struct.h"
#include "data.h"
#include "planets.h"
#include "terrain.h"
#include "conquer.h"
#include "daemonII.h"
#include "getship.h"
#include "weapons.h"
#include "player.h"
#include "misc.h"
#include "shmem.h"
#include "path.h"

#define TellERR(x)     fprintf(stderr, "!  %s: %s\n", argv0, x)
#define TellERRf(x, y) { \
                         sprintf(buf, x, y); \
                         fprintf(stderr, "!  %s: %s\n", argv0, buf); \
                       }
/*--------------------------FUNCTION PROTOTYPES---------------------------*/
#ifndef FreeBSD
long lseek();
#endif

typedef void sig_ret_t;

void move();
sig_ret_t reaper();
sig_ret_t setflag();
sig_ret_t freemem();
char *getenv();
char *twoletters();
void starttimer();
void stoptimer();
void printdaemonIIUsage();
void check_load();
void rescue();
void teamtimers();
void shipbuild_timers();
extern void (*r_signal()) ();

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







/*---------------------------MODULE VARIABLES-----------------------------*/

int dietime = -1;		/* to decide whether the deamon has been */
/* inactive for one minute.  Set to -1 so */
/* the deamon will not immediately quit */

int ticks = 0;			/* counting ticks for game timing */

jmp_buf env;			/* to hold long jump back into main */

static int debug = 0;		/* set if an arg is passed to main on the
				 * command line, this var gets set to 1 and
				 * debuf info is printed.  */

static int doMove;		/* indicates whether it's time to call move() */

int plfd;			/* for the planet file */
int glfd;			/* for the status file */

/* The name of the four teams */

/* The verbage to use in sentences such as 'the XXXXX have/has' */
char *teamVerbage[9] = {"", "has", "have", "", "have", "", "", "", "have"};

int tourntimestamp = 0;		/* ticks since t-mode started */


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








/*----------------------------------MAIN-----------------------------------*/
/*
 * Well, this is it.  The big Kahuna.  The main function of daemonII.  If an
 * arg is passed to main, then debug info is printed.  What is passed in does
 * not matter.  Personally, I am fond of running it with: daemonII fungus.
 * But maybe that's just me. Important:  An environment variable is read in
 * from the user.  It is called NETREKDIR.  This variable needs to be a path
 * to where the  '.' (dot) files are to be found.  Kurt added this to make
 * things a hell of a lot easier.
 */

int
main(argc, argv)
  int argc;
  char **argv;
{
  register int i;		/* looping var */
  int jjk;			/* looping var */
  char buf[255];		/* Temp buffer */
  char *paths;			/* to form path with */
  char *ptr;			/* get get path env var */

  int x = 0;			/* for delay in debugging messages */
#ifdef LEAGUE_SUPPORT
  int configleague = 0;		/* if nonzero, set configvals->league to 1 */
#endif
  int attach = 0;
  int nogo = 0;			/* for the usage flag/case */

  argv0 = argv[0];

  i = 1;
  while (argv[i])
  {
    if (argv[i][0] == '-')
    {
      ptr = &argv[i][1];
      while (*ptr)
      {
	switch (*ptr)
	{
	 case 'l':
#ifdef LEAGUE_SUPPORT
	  configleague = 1;
#else
	  TellERR("daemon not compiled with league support.");
	  TellERR("Edit config.h and reinstall.");
	  TellERR("Continuing anyway.");
#endif
	  break;
	 case 'd':
	  debug = 1;
	  break;
	 case 'a':
	  attach = 1;
	  break;
	 case 'h':
	 case 'u':		/* for old times sake */
	 case '-':		/* this allows for --help people */
	  nogo++;
	  break;
	 case 'v':		/* version, what the hell */
	  fprintf(stderr, "-- NetrekII (Paradise), %s --\n", PARAVERS);
	  exit(0);
	  break;
	 default:
	  TellERRf("Unknown flag '%c'.", *ptr);
	  nogo++;
	  /* fprintf(stderr, "Unknown flag '%c'\n", *ptr); */
	  break;
	}
	ptr++;
      }
    }
#if 0				/* this is goofy, just use -d */
    else
    {
      TelLERR("Backward compatibility: activating debugging mode.");
      debug = 1;
      break;
    }
#endif
    else
    {
      TellERRf("Invalid option format '%s'.", argv[i]);
      nogo++;
      break;
    }
    i++;
  }

  if (nogo)
  {
    printdaemonIIUsage(argv0);
  }
  else
  {
    /* log the PID */
    char *fname;
    FILE *fptr;

    fname = build_path("logs/daemonII.pid");
    fptr = fopen(fname, "w+");
    fprintf(fptr, "%d", getpid());
    fclose(fptr);
  }

  fprintf(stderr, "Daemon says 'hello!'\n");	/* say hi */
  srand48(getpid());		/* seed random # gen */

  openmem(attach ? 0 : 2, 0);	/* create shared memory */

  /* my daemonII has been dumping core a lot in readsysdefaults */
  if (!debug)
  {				/* setup signals if not debugging */
    for (i = 0; i < NSIG; i++)
      r_signal(i, freemem);
    r_signal(SIGSTOP, SIG_DFL);	/* accept SIGSTOP? 3/6/92 TC */
    r_signal(SIGTSTP, SIG_DFL);	/* accept SIGTSTP? 3/6/92 TC */
    r_signal(SIGCONT, SIG_DFL);	/* accept SIGCONT? 3/6/92 TC */
  }

#ifdef LEAGUE_SUPPORT
  if (configleague && !attach)
  {
    status2->league = 1;	/* configure for league play */
    /* .sysdef will be ignored */

    /* haven't chosen teams yet */
    status2->home.index = status2->away.index = -1;

    /* haven't chosen captains either */
    status2->home.captain = status2->away.captain = -1;

    /* no names for the team */
    status2->home.name[0] = status2->away.name[0] = 0;

    status2->home.ready = status2->away.ready = 0;

    status2->home.desirepause = status2->away.desirepause = 0;

    /* away has NOT passed team choice */
    status2->awaypassed = 0;

    status2->paused = 0;

    /* clear out the temporary player file */
    paths = build_path(PLAYERFILE);
    if (0 != unlink(paths) && errno != ENOENT)
    {
      perror("zeroing tourney temporary player file");
    }

    status2->home.desired.galaxyreset = status2->away.desired.galaxyreset
      = 0;
    status2->home.desired.restart = status2->away.desired.restart
      = 0;
  }
#endif

  if (!attach)
    readsysdefaults();		/* go get sysdefaults */

#ifdef LEAGUE_SUPPORT
  if (configleague && !attach)
  {
    status2->home.timeouts_left = status2->away.timeouts_left =
      configvals->timeouts;

    status2->home.desired.regulation = status2->away.desired.regulation
      = configvals->regulation_minutes;
    status2->home.desired.overtime = status2->away.desired.overtime
      = configvals->overtime_minutes;
    status2->home.desired.maxplayers = status2->away.desired.maxplayers
      = configvals->playersperteam;
  }
#endif


  if (!attach)
  {
    for (i = 0; i < MAXPLAYER; i++)
    {				/* go through all players */
      players[i].p_status = PFREE;	/* set slot free */
      players[i].p_no = i;	/* set his player number */
      players[i].p_ntspid = 0;
    }
    status2->nontteamlock = ALLTEAM;
    status2->starttourn = 0;
  }				/* !attach */

  paths = build_path(PLFILE);
  plfd = open(paths, O_RDWR, 0744);	/* open planets file */
  if (!attach)
  {
#if 1
    gen_planets();		/* generate a new galaxy every time */
    status->time = 0;
#else
    if (plfd < 0)
    {				/* oopen failed? */
      fprintf(stderr, "No planet file.  Restarting galaxy\n");
      gen_planets();		/* yup, go to it */
    }
    else
    {				/* try to read in planets */
      if (read(plfd, (char *) planets, sizeof(struct planet) * MAXPLANETS) !=
	  sizeof(struct planet) * MAXPLANETS)
      {				/* if wrong size */
	fprintf(stderr, "Planet file wrong size.  Restarting galaxy\n");
	gen_planets();		/* then regenerate galaxy */
      }
    }
#endif
  }				/* !attach */
  paths = build_path(GLOBAL);

  glfd = open(paths, O_RDWR, 0744);	/* try to open file */

  if (!attach)
  {
    if (glfd < 0)
    {				/* if could not open */
      fprintf(stderr, "No global file.  Resetting all stats\n");
      memset((char *) status, 0, sizeof(struct status));
      glfd = open(paths, O_RDWR | O_CREAT, 0744);	/* try to create file */
    }
    else
    {
      if (read(glfd, (char *) status, sizeof(struct status)) !=
	  sizeof(struct status))
      {				/* try to read file */
	fprintf(stderr, "Global file wrong size.  Resetting all stats\n");
	memset((char *) status, 0, sizeof(struct status));
      }
    }
    if (status->time == 0)
    {				/* do stats need resetting */
      status->dooshes = 1500;	/* yup, then reset them */
      status->armsbomb = 4000;	/* set them to something other than */
      status->resbomb = 1200;	/* zeroes and ones so that the */
      status->planets = 1000;	/* globals are not totally whacked */
      status->kills = 1;	/* when we first start */
      status->losses = 1;
      status->genocides = 10;
      status->sbkills = 1200;
      status->sblosses = 30;
      status->sbtime = 720000;
      status->wbkills = 1200;
      status->wblosses = 40;
      status->wbtime = 360000;
      status->jsplanets = 400;
      status->jstime = 240000;
      status->time = 1;
      status->timeprod = 1;
    }

    /* wait queue stuff */
    status->wait = 0;		/* invocation of the */
    status->count = 0;		/* daemon */
    status->request = 0;

  }				/* !attach */
  status->active = 0;		/* set stats that deal with this */
  status->gameup = 1;
  status->nukegame = getpid();
  status->timeprod = 0;

  setjmp(env);			/* set the loooong jump */

  r_signal(SIGCHLD, reaper);	/* set reaper and setflag signal */
  r_signal(SIGALRM, setflag);	/* handlers */

  if (!attach)
  {
    for (i = 0; i <= MAXTEAM; i++)
    {				/* reset some team vars */
      teams[i].s_surrender = 0;	/* reset surrender timers */
      for (jjk = 0; jjk < NUM_TYPES; jjk++)
	teams[i].s_turns[jjk] = 0;	/* reset all ship construction timers */
    }
  }				/* !attach */

  status2->newgalaxy = 0;
  for (i = 0; i < MAXPLAYER; i++)
  {
    galaxyValid[i] = 0;
  }

  check_load();			/* check the load on machine */

  starttimer();			/* start interval timer */
  doMove = 0;

  while (1)
  {				/* do forever */
    if (!doMove)
      pause();			/* wait for signal */

    if (doMove)
    {				/* if it's time */
      doMove = 0;		/* reset the flag */
      move();			/* then do the update */

      if (debug)
      {				/* if in debug mode */
	if (!(++x % 50))	/* print 'mark' as we wait */
	  printf("Mark %d\n", x);
      }
    }
  }
}
/*---------------------[ prints the usage of daemonII ]---------------------*/

void
printdaemonIIUsage(char *myname)
{
  int x;
  char message[][255] = {
    "\n\t'%s [options]'\n\n",
    "Options:\n",
    "\t-h   help (this usage message)\n",
    "\t-l   configures as a League server (usually run by listen)\n",
    "\t-d   debug\n",
    "\t-a   attach to a crashed daemon's memory segment\n",
    "\nNOTE: %s is designed to be launched by the startup process.\n\n",
    "\0"
  };

  fprintf(stderr, "-- NetrekII (Paradise), %s --\n", PARAVERS);
  for (x = 0; *message[x] != '\0'; x++)
    fprintf(stderr, message[x], myname);

  exit(1);
}

/*--------------------------[ printdaemonIIUsage ]--------------------------*/

/* signal handler for SIGALRM */
sig_ret_t
setflag()
{
  doMove = 1;
}

void
starttimer()
{
  struct itimerval udt;

  udt.it_interval.tv_sec = 0;
  udt.it_interval.tv_usec = UPDATE;
  udt.it_value.tv_sec = 0;
  udt.it_value.tv_usec = UPDATE;
  setitimer(ITIMER_REAL, &udt, (struct itimerval *) 0);
}

void
stoptimer()
{
  struct itimerval udt;

  udt.it_interval.tv_sec = 0;
  udt.it_interval.tv_usec = 0;
  udt.it_value.tv_sec = 0;
  udt.it_value.tv_usec = 0;
  setitimer(ITIMER_REAL, &udt, (struct itimerval *) 0);
}

#ifdef LEAGUE_SUPPORT
static void
handle_pause_goop()
{
  if (status2->paused)
  {
    if (!status2->home.desirepause && !status2->away.desirepause)
    {
      /* countdown to game resumption */
      status2->paused--;
      if (status2->paused)
      {
	if (status2->paused % TICKSPERSEC == 0)
	{
	  char buf[80];
	  sprintf(buf, "Game will resume in %d seconds",
		  status2->paused / TICKSPERSEC);
	  pmessage(buf, -1, MALL, UMPIRE);
	}
      }
      else
      {
	pmessage("Let the carnage resume!", -1, MALL, UMPIRE);
      }
    }
    else
    {
      status2->pausemsgfuse++;
      if (status2->pausemsgfuse > SECONDS(15))
      {
	status2->pausemsgfuse = 0;
	pmessage("Game is PAUSEd.  Captains `LEAGUE CONTINUE' to resume play.",
		 -1, MALL, UMPIRE);
	if (!status2->home.desirepause)
	  pmessage("The home team wishes to CONTINUE the game.",
		   -1, MALL, UMPIRE);
	if (!status2->away.desirepause)
	  pmessage("The away team wishes to CONTINUE the game.",
		   -1, MALL, UMPIRE);
      }
    }
  }
  else
  {
    if (!status2->home.desirepause && !status2->away.desirepause)
      return;

    status2->pausemsgfuse++;
    if (status2->pausemsgfuse > SECONDS(15))
    {
      char buf[80];
      status2->pausemsgfuse = 0;
      sprintf(buf, "The %s team wishes to PAUSE the game!",
	      status2->home.desirepause ? "home" : "away");
      pmessage(buf, -1, MALL, UMPIRE);
    }
  }
}
#endif

/*---------------------------------MOVE-----------------------------------*/
/*
 * This is the main loop for the program.  It is called every 1/10th of a
 * second.  It decides which  functions of the deamon need to be run.
 */

void
move()
{
  static int oldtourn = 0;	/* are we in t-mode or not */
  int i, j;			/* looping vars */
  struct planet *pl;

  if (++ticks == dietime)
  {				/* no player for 1 minute. kill self */
    if (debug)			/* do not quit if debug mode */
      fprintf(stderr, "Ho hum.  1 minute, no activity...\n");
    else
    {				/* quit if not debug mode */
      fprintf(stderr, "Self-destructing the daemon!\n");
      freemem(0);
    }
  }

  if ((FUSE(300)) && update_sys_defaults())	/* check to load system
						 * defualts */
    /* This message tells players that new defaults have been */
    /* loaded and the message triggers the ntserv processes */
    /* to check new defaults.  */
    pmessage("Loading new server configuration.", 0, MALL, MSERVA);

  if (FUSE(SECONDS(1)))
  {
    if (tournamentMode())
    {				/* are we in tournament mode */
      if (!oldtourn)
      {				/* is this a new condition */
	if (!status2->starttourn)
	{			/* fresh t-mode */
	  if (configvals->gamestartnuke)
	    explode_everyone(KTOURNSTART, 20);
	}
	status2->starttourn = configvals->nottimeout ? configvals->nottimeout : -1;
	warmessage();		/* go print war message */
	for (i = 0, pl = &planets[i]; i < NUMPLANETS; i++, pl++)
	  for (j = 0; j < MAXTEAM + 1; j++)
	    pl->pl_tinfo[j].timestamp = 0;
	status->clock = 0;
	tourntimestamp = ticks;	/* take a timestamp */
      }
      oldtourn = 1;		/* record that we have printed warmsg */
      status->tourn = 1;	/* set the game status to t-mode */
      status->time++;		/* inc time in t-mode */
    }
    else
    {				/* else we are not in t-mode */
      if (oldtourn)
      {				/* if previously in t-mode */
	tourntimestamp = ticks;	/* record t-mode ending */
	peacemessage();		/* send peace message */
      }
      else
      {
	static fuse = 0;
	fuse++;
	if (fuse > 60 && status2->starttourn > 0)
	{
	  fuse = 0;
	  status2->starttourn--;
	  switch (status2->starttourn)
	  {
	   case 0:
	    status2->newgalaxy = 1;
	    break;
	   case 1:
	   case 3:
	   case 5:
	   case 15:
	    {
	      static char buf[120];
	      sprintf(buf, "Warning!!  Galaxy will be reset in %d minute%s due to inactivity.", status2->starttourn, (status2->starttourn == 1) ? "" : "s");
	      pmessage(buf, 0, MALL, MSERVA);
	    }
	    break;
	    pmessage("Warning!!  Galaxy will be reset in one minute due to inactivity.", 0, MALL, MSERVA);
	    break;
	  }
	}
      }
      oldtourn = 0;		/* set we are not in t-mode */
      status->tourn = 0;	/* record in stats */
    }
  }

#if 0
  if (status->nukegame)
  {				/* if daemon should die then */
    freemem(0);			/* nuke shared memory */
    exit(0);			/* kill daemon */
  }
#endif

  parse_godmessages();		/* log any messages to god */

#ifdef LEAGUE_SUPPORT
  handle_pause_goop();		/* print any messages related to pausing the
				 * game */
#endif

#ifdef LEAGUE_SUPPORT
  if (!status2->paused)
#endif
  {
    if (FUSE(PLAYERFUSE))	/* time to update players? */
      udplayers();

    if (FUSE(TORPFUSE))		/* time to update torps? */
      udtorps();
    if (FUSE(MISSILEFUSE))	/* time to update missiles? */
      udmissiles();
    if (FUSE(PLASMAFUSE))	/* time to update plasma? */
      udplasmatorps();
    if (FUSE(PHASERFUSE))	/* time to update phasers? */
      udphaser();


    if (FUSE(CLOAKFUSE))	/* time to update cloaking? */
      udcloak();

    if (FUSE(TEAMFUSE))		/* time to update team timers? */
      teamtimers();

    if (FUSE(PLFIGHTFUSE))	/* time to update planets? */
      plfight();

    if (FUSE(TERRAINFUSE))	/* time to do terrain effects? */
      doTerrainEffects();

    if (FUSE(BEAMFUSE))		/* time to update beaming */
      beam();

    if (FUSE(SYNCFUSE))		/* time to save planets? */
      save_planets();


    if (FUSE(topgun ? HOSEFUSE2 : HOSEFUSE)
#if !defined(AEDILE) || !defined(IGGY_IN_T)
	&& status->tourn != 1	/* no Iggy during T-mode */
#endif
#ifdef LEAGUE_SUPPORT
	&& status2->league == 0
#endif
      )				/* no Iggy during league games */
      rescue(HUNTERKILLER, 0, -1);	/* send in iggy-- no team, no target */

    if (status->tourn)
    {
      {
	static int spinner = 0;

	for (spinner += configvals->popspeed; spinner >= 100; spinner -= 100)
	  popplanets();		/* increase population */
      }

      if (FUSE(PLANETFUSE))	/* time to grow resources */
	growplanets();

      {
	/*
	 * check for revolts.  Each planet is checked on average once every
	 * PLANETFUSE.
	 */
	static int spinner = 0;
	for (spinner += configvals->numplanets;
	     spinner >= PLANETFUSE;
	     spinner -= PLANETFUSE)
	{
	  check_revolt();
	}
      }
    }
    /* planet moving */
    if (configvals->planupdspd > 0 && FUSE(4))
      moveplanets();

    if (FUSE(MINUTEFUSE) && status->tourn)
    {

      shipbuild_timers();

#ifdef LEAGUE_SUPPORT
      if (!status2->league)
#endif
	udsurrend();		/* update surrender every minute unless
				 * playing league */

      status->clock++;		/* increment the timestamp clock */

    }
    /* update the tournament clock, maybe print messages */
#ifdef LEAGUE_SUPPORT
    udtourny();
#endif
  }				/* end if !paused */

  if (FUSE(MINUTEFUSE))
  {
    int i, c;
    c = 0;
    for (i = 0; i < MAXPLAYER; i++)
    {
      if (players[i].p_status != PFREE)
	c++;
    }
#ifdef COUNTFILENAME
    if (c)
    {
      char *paths;
      FILE *logfile;
      paths = build_path(COUNTFILENAME);
      logfile = fopen(paths, "a");
      if (logfile)
      {
	struct tm *tp;
	char buf[50];
	time_t cal;

	cal = time(0);
	tp = localtime(&cal);
	strftime(buf, 50, "%m/%d %H:%M", tp);

	fprintf(logfile, "%s : %2d ", buf, c);
	for (i = 0; i < c; i++)
	{
	  putc('*', logfile);
	}
	putc('\n', logfile);
	fclose(logfile);
      }
    }
#else
#ifdef UFL
    {
      char *paths;
      FILE *logfile;
      paths = build_path(LOGFILENAME);
      logfile = fopen(paths, "a");
      if (logfile)
      {
	fprintf(logfile, "Count: %d players\n", c);
	fclose(logfile);
      }
    }
#endif
#endif
  }

#if 0
  /* well, this may cause blocked pipes if too many */
  /* processes, the file said before I hacked it. */
  /* So this is disabled.  */
  if (FUSE(CHECKLOADFUSE))	/* time to check load? */
    check_load();
#endif


  if (status2->newgalaxy)
  {

    /* Disable the game timer. It'll be set again after the longjmp() */
    stoptimer();

    status2->nontteamlock = ALLTEAM;	/* allow all teams again */
    status2->starttourn = 0;	/* fresh galaxy */

    gen_planets();

    for (i = 0; i < MAXPLAYER; i++)
    {
      galaxyValid[i] = 0;	/* force download of new galaxy map */
    }
    longjmp(env, 0);
  }
}


sig_ret_t
freemem(sig)
  int sig;
{
  register int i;
  register struct player *j;

  if (sig)
  {
    fprintf(stderr, "Daemon: Caught signal %d\n", sig);
    /* U_STACK_TRACE(); */
  }

  /* Blow players out of the game */
  for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++)
  {
    j->p_status = POUTFIT;
    j->p_whydead = KDAEMON;
    j->p_ntorp = 0;
    j->p_nplasmatorp = 0;
    j->p_explode = 600 / PLAYERFUSE;	/* ghost buster was leaving players
					 * in */
  }
  /* Kill waiting players */
  status->gameup = 0;		/* say goodbye to xsg et. al. 4/10/92 TC */
  status->count = 0;
  save_planets();
  sleep(2);
  blast_shmem();
  exit(0);
}


void
check_load()
{
#ifndef hpux			/* breaks under hpux... it's fixable, though */
  FILE *fp, *popen();
  char buf[100];
  char *s;
  float load;

#if defined(SYSV) || defined(Linux) || defined(FreeBSD)
#if defined(sgi)
  fp = popen("/usr/bsd/uptime", "r");	/* sigh. */
#else
  fp = popen("/usr/bin/uptime", "r");
#endif
#else
  fp = popen("/usr/ucb/uptime", "r");
#endif
  if (fp == NULL)
  {
    /* status->gameup=0; */
    return;
  }
  fgets(buf, 99, fp);
  s = strrchr(buf, ':');
  if (s == NULL)
  {
    /* status->gameup=0; */
    pclose(fp);
    return;
  }
  if (sscanf(s + 1, " %f", &load) == 1)
  {
    sprintf(buf, "NetrekII (Paradise), %s", PARAVERS);
    pmessage(buf, 0, MALL, MSERVA);
    if (load >= configvals->maxload && status->gameup == 1)
    {
      status->gameup = 0;
      sprintf(buf, "The load is %f, this game is going down", load);
      pmessage(buf, 0, MALL, MSERVA);
    }
    else if (load < configvals->maxload && status->gameup == 0)
    {
      status->gameup = 1;
      sprintf(buf, "The load is %f, this game is coming up", load);
      pmessage(buf, 0, MALL, MSERVA);
    }
    else
    {
      sprintf(buf, "Load check: %-7.2f", load);
      buf[strlen(buf) - 1] = '\0';
      pmessage(buf, 0, MALL, MSERVA);
    }
  }
  else
  {
    /* status->gameup=0; */
  }
  r_signal(SIGCHLD, SIG_DFL);
  pclose(fp);
  r_signal(SIGCHLD, reaper);
#endif
}

void
ghostmess(victim)
  struct player *victim;
{
  char buf[80];
  static float ghostkills = 0.0;
  int i, k;

  ghostkills += 1.0 + victim->p_armies * 0.1 + victim->p_kills * 0.1;
  sprintf(buf, "%s (%s) was kill %0.2f for the GhostBusters",
	  victim->p_name, twoletters(victim),
	  ghostkills);
  pmessage(buf, 0, MALL, MSERVA);
#if 1
  if (victim->p_armies > 0)
  {
    k = 10 * (remap[victim->p_team] - 1);
    if (k >= 0 && k <= 30)
      for (i = 0; i < 10; i++)
      {
	if (planets[i + k].pl_owner == victim->p_team)
	{
	  planets[i + k].pl_armies += victim->p_armies;
	  sprintf(buf, "%s's %d armies placed on %s",
		  victim->p_name, victim->p_armies, planets[k + i].pl_name);
	  pmessage(buf, 0, MALL | MGHOST, MSERVA);
	  break;
	}
      }
  }
#else
  if (victim->p_armies > 0)
    PlaceLostArmies(victim);	/* not working yet */
#endif
}

void
saveplayer(victim)
  struct player *victim;
{
  int fd;
  char *paths;

  if (victim->p_pos < 0)
    return;
  if (victim->p_stats.st_lastlogin == 0)
    return;
#ifndef ROBOTSTATS
  if (victim->p_flags & PFROBOT)
    return;
#endif

  paths = build_path(PLAYERFILE);
  fd = open(paths, O_WRONLY, 0644);
  if (fd >= 0)
  {
    lseek(fd, 32 + victim->p_pos * sizeof(struct statentry), 0);
    write(fd, (char *) &victim->p_stats, sizeof(struct stats));
    close(fd);
  }
}


/* Send in a robot to avenge the aggrieved team */
/* -1 for HK, -2 for Terminator, -3 for sticky Terminator */

/* if team in { FED, ROM, KLI, ORI }, a nonzero target means "fleet" mode */
/* CRD feature: number (or -1) for starting planet - MAK,  2-Jun-93 */

void
rescue(team, target, planet)
  int team;
  int target;
  int planet;
{
  char *arg1, argp[5];
  int pid;
  char *paths;			/* added 1/18/93 KAO */

#ifdef LEAGUE_SUPPORT
  if (status2->league)
    return;			/* no robots during league play */
#endif

  sprintf(argp, "-S%d", planet);

  if ((pid = fork()) == 0)
  {
    /* underscore is just a place holder */
    static char termbuf[] = "-Tt_";
    if (!debug)
    {
      close(0);
      close(1);
      close(2);
    }
    r_signal(SIGALRM, SIG_DFL);
    paths = build_path(ROBOT);
    switch (team)
    {
     case FED:
      arg1 = "-Tf";
      break;
     case ROM:
      arg1 = "-Tr";
      break;
     case KLI:
      arg1 = "-Tk";
      break;
     case ORI:
      arg1 = "-To";
      break;
     case HUNTERKILLER:
      arg1 = "-Ti";		/* -1 means independent robot */
      if (!debug)
      {
	execl(paths, "robot", arg1, "-P", argp, 0);
      }
      else
      {
	execl(paths, "robot", arg1, "-P", argp, "-d", 0);
      }
      break;
     case TERMINATOR:		/* Terminator */
      arg1 = termbuf;
      arg1[3] = twoletters(&players[target])[1];
      break;
     case STERMINATOR:		/* sticky Terminator */
      arg1 = termbuf;
      arg1[3] = twoletters(&players[target])[1];
      if (!debug)
	execl(paths, "robot", arg1, "-s", argp, 0);
      else
	execl(paths, "robot", arg1, "-s", argp, "-d", 0);
      break;
     default:
      arg1 = "-Ti";
      break;
    }
    if (target > 0)		/* be fleet 8/28/91 TC */
      execl(paths, "robot", arg1, "-f", argp, 0);
    else if (!debug)
    {				/* Make these fleet, too - MAK,  4-Jun-93 */
      execl(paths, "snake", arg1, argp, 0);
      /* execl (paths, "robot", arg1, "-f", argp, 0); */
    }
    else
    {				/* Make these fleet, too - MAK,  4-Jun-93 */
      execl(paths, "snake", arg1, argp, "-d", 0);
      /* execl (paths, "snake", arg1, "-f", argp, "-d", 0); */
    }
    /* If we get here, we are hosed anyway */
    fprintf(stderr, "Failed to exec robot %s.\n", paths);
    exit(1);
  }
  else
  {
    if (debug)
    {
      fprintf(stderr, "Forking robot: pid is %d\n", pid);
    }
  }
}

#include <sys/resource.h>

/* ARGSUSED */

/* Don't fear the ... */

sig_ret_t
reaper(sig)
{
  static int status;
  static int pid;

#ifndef SVR4
  while ((pid = wait3((union wait *) & status, WNOHANG, (struct rusage *) 0)) > 0)
  {
#else				/* note: no status info */
  while ((pid = waitpid(0, 0, WNOHANG)) > 0)
  {
#endif				/* SVR4 */
    if (debug)
    {
      fprintf(stderr, "Reaping: pid is %d (status: %X)\n",
	      pid, status);
    }
  }
}

unsigned char
getcourse(x, y, xme, yme)
  int x, y, xme, yme;
{
  return ((unsigned char) (int) (atan2((double) (x - xme),
				     (double) (yme - y)) / 3.14159 * 128.));
}

int tm_robots[MAXTEAM + 1];	/* To limit the number of robots */

void
teamtimers()
{
  register int i;
  for (i = 0; i <= MAXTEAM; i++)
  {
    if (tm_robots[i] > 0)
      tm_robots[i]--;
  }
}

void
shipbuild_timers()
{
  int i, t;

  for (i = 0; i <= MAXTEAM; i++)/* go through all teams */
    for (t = 0; t < NUM_TYPES; t++)	/* and each ship type */
      if (teams[i].s_turns[t] > 0)	/* and if need be, then dec */
	teams[i].s_turns[t]--;	/* the construction timer */
}


#undef D