diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/daemonII.c	Sat Dec 06 04:37:01 1997 +0000
@@ -0,0 +1,1162 @@
+/*--------------------------------------------------------------------------
+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