view src/main.c @ 4:aa38447a4b21

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

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <setjmp.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <unistd.h>

#include "defs.h"
#include "struct.h"
#include "data.h"
#include "packets.h"
#include "shmem.h"
#include "path.h"

#if 0
#define D(s) do { s } while(0)
#else
#define D(s)
#endif


#if 0
jmp_buf env;
#endif

int startTkills, startTlosses, startTarms, startTplanets, startTticks;
char start_login[16];		/* change 1/25/91 TC */
char start_name[16];		/* change 1/25/91 TC */
int goAway = 0;			/* change 4/14/91 TC */
int ignored[MAXPLAYER];		/* change 7/24/91 TC */

int overload = 0;		/* global 7/31/91 TC */
/* overload indicates a request for a reserved slot */

int indie = 0;			/* always be indie 8/28/91 TC */


extern void load_time_access();
void printntservUsage();
extern pid_t getpid();
extern int connectToClient();
extern int checkSocket();
extern int initClientData();
extern int socketPause();
extern int readFromClient();
extern int checkVersion();
extern int findslot();
extern void updateSelf();
extern void updateShips();
extern void updatePlanets();
extern void updateTerrain();
extern void flushSockBuf();
extern int getname();
extern int sendShipCap();
extern int logEntry();
extern int getEntry();
void printStats();
void exitGame();
#if defined(sparc) && !defined(SVR4)
int atexitfunc( /* int, caddr_t */ );
#else
void atexitfunc();
#endif
extern void (*r_signal()) ();
extern int enter();
extern int input();
extern int setitimer();
extern int pmessage2();
extern int savestats();
extern void move_player();
extern int sendMotdLine();
extern int time_access();
void doMotdPics();
extern int numPlanets();
extern int sendMotdPic();
extern int ParseXbmFile();

int
main(argc, argv)
  int argc;
  char **argv;
{
  int intrupt();
  char *getenv();
  int team, s_type;
  int pno;
  int usage = 0;		/* Flag saying tell usage */
  int errorc = 0;		/* Error count */
  char *errorv[5];		/* Error Vector (cannot have more than 5) */
  char *name, *ptr;
  int reaper();
  int callHost = 0;
  long starttime;
  enum HomeAway homeaway = NEITHER;
  int observer = 0;

  argv0 = argv[0];

  pno = time(NULL);


  /* load_time_acess - read in the hours file, Larry's. */

  load_time_access();

  name = *argv++;
  argc--;
  if ((ptr = strrchr(name, '/')) != NULL)
    name = ptr + 1;
  while (*argv)
  {
    if (**argv == '-')
      ++* argv;
    else
      break;

    argc--;
    ptr = *argv++;
    while (*ptr)
    {
      switch (*ptr)
      {
       case 'u':		/* for old times sake */
       case '-':		/* this will help the --help people */
       case 'h':
	usage++;
	break;
       case 'i':
	indie++;
	break;			/* 8/28/91 TC */
       case 'R':
	if (getuid() == geteuid())
	  overload++;
	break;
       case 's':
	xtrekPort = atoi(*argv);
	callHost = 1;
	argv++;
	argc--;
	break;
       case 'M':
	blk_metaserver = 1;
	break;
       case 'd':
	host = *argv;
	argc--;
	argv++;
	break;
	/* netrek league stuff */
       case 'O':
	observer = 1;
	break;
       case 'H':
	homeaway = HOME;
	break;
       case 'A':
	homeaway = AWAY;
	break;
       default:
	{
	  char buffer[100];
	  sprintf(buffer, "Unknown option '%c'\n", *ptr);
	  errorv[errorc++] = buffer;
	  break;
	}
      }
      if (usage)
	break;
      ptr++;
    }
  }

  if (usage || errorc)
  {
    int x;
    char message[][255] = {
      "\n\t'%s [options] -s <socket number> <display address>'\n\n",
      "Options:\n",
      "\t-h   Help (this usage message)\n",
      "\t-i   Team independant\n",
      "\t-R   Reserved slot\n",
      "\t-s   Socket number\n",
      "\t-M   Metaserver\n",
      "\t-d   Display\n",
      "\t-O   Observer\n",
      "\t-H   Home (League Play)\n",
      "\t-A   Away (League Play)\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; x < errorc; x++)
      fprintf(stderr, "\n%s: %s", argv0, errorv[x]);
    for (x = 0; *message[x] != '\0'; x++)
      fprintf(stderr, message[x], argv0);

    exit(1);
  }

  openmem(1, homeaway != NEITHER);

  /* compatability */
  if (argc > 0)
    host = argv[0];
  srand48(getpid() + time((time_t *) 0));
  /* this finds the shared memory information */

#if 0
  if (blk_metaserver)
  {
    FILE *ptr;
    char *buf;
    buf = build_path("logs/metaserver.log");
    ptr = fopen(buf, "a");
    fprintf(ptr, "Connection from meta-server\n");
    fclose(ptr);
  }
#endif


  me = NULL;			/* UDP fix (?) */
  if (callHost)
  {
    if (!connectToClient(host, xtrekPort))
    {
      exit(0);
    }
  }
  else
  {
    sock = 0;			/* Because we were forked by inetd! */
    checkSocket();
    initClientData();		/* "normally" called by connectToClient() */
  }

  starttime = time(NULL);
  while (userVersion == 0)
  {
    /*
     * Waiting for user to send his version number. We give him ten seconds
     * to do so...
     */
    if (starttime + 10 < time(NULL))
    {
      exit(1);
    }
    socketPause();
    readFromClient();
  }
  if (!checkVersion())
    exit(1);

  pno = findslot(overload, homeaway);
  if (pno < 0)
  {
    /* print some appropriate message */
    exit(1);
  }

#if defined(sparc) && !defined(SVR4)
  on_exit(atexitfunc, (caddr_t) 0);
#else
  atexit(atexitfunc);		/* register a function to execute at exit */
#endif

  me = &players[pno];
  me->p_no = pno;
  me->p_team = NOBODY;
  me->p_stats.st_royal = 0;
#ifdef	RC_DISTRESS
  me->gen_distress = 0;		/* default to RCD off */
#endif
  me->p_ntspid = getpid();
  myship = &me->p_ship;
  mystats = &me->p_stats;
  lastm = mctl->mc_current;
  me->p_lastrefit = -1;
  me->p_spyable = 1;
  me->p_teamspy = ~0;
#if 0
  me->p_planfrac = 0;		/* reset fractional parts */
  me->p_bombfrac = 0;		/* reset fractional parts */
#endif

  /* --------------------------[ CLUECHECK stuff ]-------------------------- */
#ifdef CLUECHECK1
  me->p_cluedelay = 10;
  me->p_cluecountdown = 0;
#endif

#ifdef CLUECHECK2
  me->p_cluedelay = lrand48() % 1000;	/* so it doesn't ask them immediately */
  me->p_cluecountdown = 0;
#endif
  /* ----------------------------------------------------------------------- */

#ifndef AUTHORIZE
  strcpy(RSA_client_type, "server doesn't support RSA");
#endif

  (void) r_signal(SIGINT, SIG_IGN);
  (void) r_signal(SIGCHLD, reaper);

  /*
   * We set these so we won't bother updating him on the location of the
   * other players in the galaxy which he is not near.  There is no real harm
   * to doing this, except that he would then get more information than he
   * deserves. It is kind of a hack, but should be harmless.
   */
  me->p_x = -100000;
  me->p_y = -100000;
  me->p_homeaway = homeaway;
  me->p_observer = observer;

#if 0
  updateGameparams();
#endif

  updateSelf();			/* so he gets info on who he is */
  updateShips();		/* put this back so maybe something will work */
  /* with Andy's meta-server */

  if (!blk_metaserver)
  {
    updateStatus();
    updatePlanets();
    updateTerrain();
  }
#if 1
  updateGameparams();
#endif

  flushSockBuf();

  /* Get login name */

#if 0
  if ((pwent = getpwuid(getuid())) != NULL)
    (void) strncpy(login, pwent->pw_name, sizeof(login));
  else
#endif
    (void) strncpy(login, "Bozo", sizeof(login));
  login[sizeof(login) - 1] = '\0';

  strcpy(pseudo, "Guest");

  strcpy(me->p_name, pseudo);
  me->p_team = ALLTEAM;
  getname();
  if (me->p_stats.st_rank >= NUMRANKS)
    me->p_stats.st_rank = NUMRANKS - 1;
  if (me->p_stats.st_royal >= NUMROYALRANKS)
    me->p_stats.st_royal = NUMROYALRANKS - 1;
  strcpy(pseudo, me->p_name);
  strcpy(start_name, me->p_name);	/* change 1/25/91 TC */

  sendShipCap();		/* KAO 1/25/93 */

  keeppeace = (me->p_stats.st_flags & ST_KEEPPEACE) == ST_KEEPPEACE;

  /*
   * Set p_hostile to hostile, so if keeppeace is on, the guy starts off
   * hating everyone (like a good fighter should)
   */
  me->p_hostile = (FED | ROM | KLI | ORI);
  s_type = CRUISER;
  me->p_planets = 0;
  me->p_armsbomb = 0;
  me->p_dooshes = 0;
  me->p_resbomb = 0;
  /* Set up a reasonable default */
  me->p_whydead = KQUIT;

  (void) strncpy(me->p_login, login, sizeof(me->p_login));
  me->p_login[sizeof(me->p_login) - 1] = '\0';
  strcpy(start_login, login);	/* change 1/25/91 TC */
  {
    int i;
    for (i = 0; i < MAXPLAYER; i++)
      ignored[i] = 0;
  }

  (void) strncpy(me->p_monitor, host, sizeof(me->p_monitor));
  me->p_monitor[sizeof(me->p_monitor) - 1] = '\0';

  /* assume this is only place p_monitor is set, and mirror accordingly */
  /* 4/13/92 TC */
  (void) strncpy(me->p_full_hostname, host, sizeof(me->p_full_hostname));
  me->p_full_hostname[sizeof(me->p_full_hostname) - 1] = '\0';

  logEntry();			/* moved down to get login/monitor 2/12/92
				 * TMC */

  me->p_avrt = -1;		/* ping stats */
  me->p_stdv = -1;
  me->p_pkls = -1;

  startTkills = me->p_stats.st_tkills;
  startTlosses = me->p_stats.st_tlosses;
  startTarms = me->p_stats.st_tarmsbomb;
  startTplanets = me->p_stats.st_tplanets;
  startTticks = me->p_stats.st_tticks;

  r_signal(SIGHUP, exitGame);	/* allows use of HUP to force a clean exit */



  me->p_status = POUTFIT;
  repCount = 0;

  while (1)
  {
    switch (me->p_status)
    {
     case POUTFIT:
     case PTQUEUE:
      /* give the player the motd and find out which team he wants */
      if (me->p_status != PALIVE)
      {
	me->p_x = -100000;
	me->p_y = -100000;
	updateSelf();
	updateShips();
	teamPick = -1;
	flushSockBuf();
	getEntry(&team, &s_type);
      }
      if (goAway)
      {				/* change 4/14/91 TC */
	printStats();
	exit(0);
      }
      if (team == -1)
      {
	exitGame();
      }

      if (indie)
	team = 4;		/* force to independent 8/28/91 TC */
      inputMask = -1;		/* Allow all input now */
      enter(team, 0, pno, s_type, -1);
      /* for (i = 0; i < NSIG; i++) { r_signal(i, SIG_IGN); } */

      me->p_status = me->p_observer ? POBSERVE : PALIVE;	/* Put player in game */
      me->p_ghostbuster = 0;
      break;
     case PALIVE:
     case PEXPLODE:
     case PDEAD:
     case POBSERVE:
      /* Get input until the player quits or dies */


      input();
      break;
     default:
      if (tmpPick != PATROL)
      {
	printf("player status = %d.  exiting\n", me->p_status);
	exitGame();
      }
    }
  }

  /* NOTREACHED */
  return 1;
}

extern int setflag();		/* input.c */

int interrupting = 0;

void
stop_interruptor()
{
  struct itimerval udt;

  if (!interrupting)
    return;

  r_signal(SIGALRM, SIG_IGN);	/* set up signals */
  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, 0);

  interrupting = 0;
}

void
start_interruptor()
{
  struct itimerval udt;

  if (interrupting)
    return;

  {
    int min_delay = me->p_observer
    ? configvals->min_observer_upd_delay
    : configvals->min_upd_delay;

    if (timerDelay < min_delay)
      timerDelay = min_delay;
  }

  r_signal(SIGALRM, SIG_IGN);	/* set up signals */
  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);

  r_signal(SIGALRM, setflag);

  interrupting = 1;
}

#if 0

/*
 * this is TOTALLY untested.  It probably doesn't belong in this file,
 * either.  When I figure out all the mechanisms, this will replace the while
 * loop in main().  RF
 */

void
inputloop()
{
  static int switching = -1;	/* is the player changing teams? */

  int status = me->p_status;

  while (1)
  {
    switch (status)
    {
     case PFREE:
      status = me->p_status = PDEAD;
      me->p_explode = 600;
      stop_interruptor();
      break;

     case POUTFIT:
     case PTQUEUE:
      updateSelf();
      updateShips();
      sendMaskPacket(tournamentMask(me->p_team));
      briefUpdateClient();
      teamPick = -1;
      socketPause();
      break;

     case PEXPLODE:
     case PDEAD:
      inputMask = 0;
     case PALIVE:
      socketWait();
      delay_interrupt();	/* don't let client read be interrupted by
				 * the interval timer */
      /*
       * ^-- won't work.. delay_interrupt doesn't stop ALRMs from happening
       * (HAK 9/21)
       */
      break;
    }

    /* me->p_status could change in here. */
    readFromClient();

    if (isClientDead())
    {
      if (!reconnect())
      {
	/* me->p_status=PFREE; should this be done?  RF */
	exit(0);
      }
    }
    switch (status)
    {
     case POUTFIT:
     case PTQUEUE:
      if (teamPick == -1)
	break;

      if (teamPick < 0 || teamPick > 3 /* XXX */ )
      {
	warning("That is not a valid team.");
	sendPickokPacket(0);
      }
      else if (!(tournamentMask(me->p_team) & (1 << teamPick)))
      {
	warning("I cannot allow that.  Pick another team");
	sendPickokPacket(0);
      }
      else if (((1 << teamPick) != me->p_team) &&
	       (me->p_team != ALLTEAM) &&
	       switching != teamPick &&
	       me->p_whydead != KGENOCIDE)
      {				/* switching teams */
	switching = teamPick;
	warning("Please confirm change of teams.  Select the new team again.");
	sendPickokPacket(0);
      }
      else if (shipPick < 0 || shipPick >= NUM_TYPES)
      {
	/* His team choice is ok. */
	warning("That is an illegal ship type.  Try again.");
	sendPickokPacket(0);
      }
      else if (!allowed_ship(1 << teamPick, mystats->st_rank, mystats->st_royal, shipPick))
      {
	sendPickokPacket(0);
      }
      else
      {

	if (goAway)
	{			/* what does this do ? */
	  printStats();
	  exit(0);
	}
	if (indie)
	  me->p_team = 4;

	inputMask = -1;

	enter(teamPick, 0, me->p_no, shipPick, -1);

	status = me->p_status = me->p_observer ? POBSERVER : PALIVE;
	start_interruptor();	/* since we're alive, we need regular
				 * interrupts now */

	me->p_ghostbuster = 0;

	repCount = 0;

      }
      break;
     case PALIVE:
     case PEXPLODE:
     case PDEAD:
      if (0 /* sendflag */ )
      {				/* this is still busted, ugh */
	check_authentication();
	if (me->p_status == PFREE)
	{
	  me->p_ghostbuster = 0;
	  me->p_status = PDEAD;
	}
	if ((me->p_status == PDEAD || me->p_status == POUTFIT)
	    && (me->p_ntorp <= 0)
	    && (me->p_nplasmatorp <= 0))
	{
	  stop_interruptor();
	  death();
	  status = POUTFIT;
	}
	auto_features();
	updateClient();

	/* sendflag=0;   ugh, broke */
      }
      reenable_interrupt();
    }
  }
}

#endif


void
exitGame()
{
  char buf[80];
  char addrbuf[20];

  if (me != NULL && me->p_team != ALLTEAM)
  {
    sprintf(buf, "%s %s (%s) leaving game (%.16s@%.32s)",
	    ((me->p_stats.st_royal) ? royal[me->p_stats.st_royal].name
	     : ranks[me->p_stats.st_rank].name),
	    me->p_name,
	    twoletters(me),
	    me->p_login,
	    me->p_full_hostname
      );
    sprintf(addrbuf, " %s->ALL", twoletters(me));
    pmessage2(buf, 0, MALL | MLEAVE, addrbuf, me->p_no);
    me->p_stats.st_flags &= ~ST_CYBORG;	/* clear this flag 8/27/91 TC */
    savestats();
    printStats();
  }
  me->p_status = PFREE;
  move_player(me->p_no, -1, -1, 1);
  exit(0);
}

#if defined(sparc) && !defined(SVR4)
int
atexitfunc(status, arg)
  int status;
  caddr_t arg;
#else
void
atexitfunc()
#endif
{
  me->p_ntspid = 0;
  me->p_status = PFREE;
}

#define	PLURAL(n)	(((n)==1)?"":"s")

static char *weapon_types[WP_MAX] = {
  "Plasma torpedos",
  "Tractors",
  "Missiles",
  "Fighters",
};

void
sendSysDefs()
{
  char buf[200], buf2[200];
  int i;

  sendMotdLine("\t@@@");

  if (!time_access())
  {
    sendMotdLine("** WE ARE CLOSED, CHECK HOURS **");
    sendMotdLine("");
  }

  sendMotdLine("Available ship types:");
  buf[0] = 0;
  for (i = 0; i < NUM_TYPES; i++)
  {
    struct ship *s = &shipvals[i];
    if (!shipsallowed[i])
      continue;
    sprintf(buf2, "   %c) %s/%c%c", s->s_letter, s->s_name,
	    s->s_desig1, s->s_desig2);
    if (strlen(buf) + strlen(buf2) > 80)
    {
      sendMotdLine(buf);
      strcpy(buf, buf2);
    }
    else
    {
      strcat(buf, buf2);
    }
  }
  /* guaranteed to have stuff here */
  sendMotdLine(buf);
  sendMotdLine("");
  sendMotdLine(
	       "SHIP         REQUIRED RANK   BUILD TIME    LIMIT     NUM PLYRS  NUM PLNTS");

  for (i = 0; i < NUM_TYPES; i++)
  {
    struct ship *s = &shipvals[i];
    if (!shipsallowed[i])
      continue;
    buf2[0] = 0;
    if (s->s_rank > 0 || s->s_timer > 0 || s->s_maxnum < 16 ||
	s->s_numdefn || s->s_numplan)
    {
      sprintf(buf2, "%-13s%-16s", s->s_name,
	      s->s_rank ? ranks[s->s_rank].name : "none");
      if (s->s_timer > 0)
	sprintf(buf, "%d minutes", s->s_timer);
      else
	strcpy(buf, "none");
      sprintf(buf2 + strlen(buf2), "%-14s", buf);

      if (s->s_maxnum < 16)
	sprintf(buf, "%d/team", s->s_maxnum);
      else
	strcpy(buf, "none");
      sprintf(buf2 + strlen(buf2), "%-12s  %-3d        %-3d", buf,
	      s->s_numdefn, s->s_numplan);
      sendMotdLine(buf2);
    }
  }
  sendMotdLine("");


  buf2[0] = 0;
  for (i = 0; i < WP_MAX; i++)
  {
    if (weaponsallowed[i])
    {
      if (buf2[0])
	strcat(buf2, ", ");
      strcat(buf2, weapon_types[i]);
    }
  }
  sprintf(buf, "Special weapons enabled: %s", buf2[0] ? buf2 : "none");
  sendMotdLine(buf);

  if (weaponsallowed[WP_PLASMA])
  {
    sprintf(buf, "You need %.1f kill%s to get plasma torpedos",
	    configvals->plkills, PLURAL(configvals->plkills));
    sendMotdLine(buf);
  }
  if (weaponsallowed[WP_MISSILE])
  {
    sprintf(buf, "You need %.1f kill%s to get missiles",
	    configvals->mskills, PLURAL(configvals->mskills));
    sendMotdLine(buf);
  }
  sendMotdLine("");

  sprintf(buf, "Tournament mode requires %d player%s per team",
	  configvals->tournplayers, PLURAL(configvals->tournplayers));
  sendMotdLine(buf);
  /* sendMotdLine(""); */

  /* We don't blab about newturn */
#if 0
  sendMotdLine(configvals->hiddenenemy ?
	       "Visibility is restricted during T-mode" :
	       "Visibility is unlimited all the time");
#endif
  sendMotdLine(configvals->binconfirm ?
	       "Only authorized binaries are allowed" :
	       "Non-authorized binaries are detected but not rejected");

  if (configvals->planetlimittype)
  {
    sprintf(buf, "Independent planets may be taken if your team has fewer than %d planets", configvals->planetsinplay);
    sendMotdLine(buf);
  }
  else
  {
    sprintf(buf, "Only %d planets can be in play at once",
	    configvals->planetsinplay);
    sendMotdLine(buf);
  }

  if (configvals->planupdspd == 0)
  {
    sendMotdLine("Planets do not orbit their stars");
  }
  else
  {
    sprintf(buf, "Planets orbit their stars at a rate of %g",
	    configvals->planupdspd);
    sendMotdLine(buf);
  }
  sendMotdLine(configvals->warpdecel ?
	       "New warp deceleration code is in effect" :
	       "Old-style instant warp deceleration is in effect");
  sprintf(buf, "The next galaxy will be generated using method #%d",
	  configvals->galaxygenerator);
  sendMotdLine(buf);
  sendMotdLine
    (configvals->affect_shiptimers_outside_T ?
     "Ship deaths outside tournament mode affect construction timers" :
     "Construction timers are not affected by ship deaths outside tournament mode");

  sprintf(buf, "Cloaking during warp prep is %sallowed",
	  configvals->cloakduringwarpprep ? "" : "not ");
  sendMotdLine(buf);

  sprintf(buf, "Cloaking during warp is %sallowed",
	  configvals->cloakwhilewarping ? "" : "not ");
  sendMotdLine(buf);

  sprintf(buf, "Variable warp speed is %sabled",
	  configvals->variable_warp ? "en" : "dis");
  sendMotdLine(buf);

  sprintf(buf, "Warp prep suspension is %sallowed",
	  configvals->warpprep_suspendable ? "" : "not ");
  sendMotdLine(buf);

  switch (configvals->warpprepstyle)
  {
   case WPS_NOTRACT:
    sendMotdLine("Tractors do not affect warp prep");
    break;
   case WPS_TABORT:
    sendMotdLine("Tractors make warp fail to engage");
    break;
   case WPS_TPREVENT:
    sendMotdLine("Tractors prevent entering warp");
    break;
   case WPS_TABORTNOW:
    sendMotdLine("Tractors abort warp prep countdown");
    break;
   case WPS_TSUSPEND:
    sendMotdLine("Tractors suspend warp prep countdown");
    break;
  }

  sprintf(buf, "There is a %d%% chance that you'll orbit a planet CCW",
	  (int) ((1.0 - configvals->orbitdirprob) * 100));
  sendMotdLine(buf);

  sprintf(buf, "Army growth: %d.  Pop choice: %d.  Pop speed: %d%%.",
	configvals->popscheme, configvals->popchoice, configvals->popspeed);
  sendMotdLine(buf);

  if (configvals->warpzone)
  {
    sprintf(buf, "Warp zones are enabled with radius %d.",
	    configvals->warpzone);
  }
  else
  {
    sprintf(buf, "Warp zones are disabled.");
  }
  sendMotdLine(buf);
}

void 
sendMotd()
{
  FILE *motd;
  char buf[100], buf2[30];	/* big enough... */
  char *paths;

  time_t curtime;
  struct tm *tmstruct;
  int hour, tacc;

  time(&curtime);
  tmstruct = localtime(&curtime);
  if (!(hour = tmstruct->tm_hour % 12))
    hour = 12;
  sprintf(buf, "Netrek II (Paradise) server %s, connection established at %d:%02d%s.",
	  PARAVERS,
	  hour,
	  tmstruct->tm_min,
	  tmstruct->tm_hour >= 12 ? "pm" : "am");

  /*
   * if (!(tacc = time_access())) strcat(buf, "  WE'RE CLOSED, CHECK HOURS");
   */
  sendMotdLine(buf);
  sendMotdLine(" ");

  if (!blk_flag)
  {
    paths = build_path(WCMOTD);	/* Wrong client message */
    if ((motd = fopen(paths, "r")) != NULL)
    {
      while (fgets(buf, sizeof(buf), motd) != NULL)
      {
	buf[strlen(buf) - 1] = '\0';
	sendMotdLine(buf);
      }
      fclose(motd);
    }
    else
    {				/* default message */
      sendMotdLine(" ");
      sendMotdLine(
		   "      ****************************************************************");
      sendMotdLine(" ");

      sendMotdLine(
		   "       This is a Paradise server; you need a Paradise client to play!");
      sendMotdLine(" ");
      sendMotdLine(
		   "       Paradise clients can be had from");
      sendMotdLine(
		   "          ftp.cis.ufl.edu    pub/netrek.paradise/");
      sendMotdLine(
		   "          ftp.reed.edu       mirrors/netrek.paradise/");
      sendMotdLine(" ");
      sendMotdLine(
		   "      ****************************************************************");
    }
    return;
  }

   /* if (blk_flag) */ 
  {				/* added 1/19/93 KAO */
    int i, first = 1;

    strcpy(buf, "BLK: REFIT ");

    for (i = 0; i < NUM_TYPES; i++)
    {
      struct ship *s = &shipvals[i];
      if (!shipsallowed[i])
	continue;

      if (!first)
	strcat(buf, ", ");
      else
	first = 0;

      sprintf(buf2, "%c) %c%c", s->s_letter, s->s_desig1, s->s_desig2);
      strcat(buf, buf2);
    }
    sendMotdLine(buf);
  }
  /* the following will read a motd */
  if (!time_access())
  {
    paths = build_path(CLOSEDMOTD);
    if ((motd = fopen(paths, "r")) == NULL)
    {
      paths = build_path(MOTD);
      motd = fopen(paths, "r");
    }
  }
  else
  {
    paths = build_path(MOTD);
    motd = fopen(paths, "r");
  }
  if (motd != NULL)
  {
#ifdef CLUECHECK1
    init_motdbuf(paths);
#endif
    while (fgets(buf, sizeof(buf), motd) != NULL)
    {
      buf[strlen(buf) - 1] = '\0';
      sendMotdLine(buf);
    }
    (void) fclose(motd);
  }
  sendSysDefs();

  /* wait till the end for the pictures */
  if (!blk_metaserver)
    doMotdPics();
}

int 
reaper(sig)
  int sig;
{
#ifndef SVR4
  while (wait3((union wait *) 0, WNOHANG, (struct rusage *) 0) > 0);
#else
  while (waitpid(0, NULL, WNOHANG) > 0);
#endif				/* SVR4 */
  return 0;
}

void 
printStats()
{
  FILE *logfile;
#if defined(SVR4) || defined(sparc)
  time_t curtime;
#else
  int curtime;
#endif				/* SVR4 */
  char *paths;			/* added 1/18/93 KAO */

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

#ifdef LOG_LONG_INFO		/*-[  Long info printed to logfiles and startup.log ]-*/
  fprintf(logfile, "Leaving: %-16s (%s) %3dP %3dA %3dW/%3dL %3dmin %drtt %dsdv %dls %dplan <%s@%s> %s",
	  me->p_name,
	  twoletters(me),
	  me->p_stats.st_tplanets - startTplanets,
	  me->p_stats.st_tarmsbomb - startTarms,
	  me->p_stats.st_tkills - startTkills,
	  me->p_stats.st_tlosses - startTlosses,
	  (me->p_stats.st_tticks - startTticks) / 600,
	  me->p_avrt, me->p_stdv, me->p_pkls,
	  numPlanets(me->p_team),

#else

  fprintf(logfile, "Leaving: %s <%s@%s> %s",
	  me->p_name,
#endif
	  me->p_login,
	  me->p_full_hostname,
	  ctime(&curtime));

  /* #endif /*-[ LOG_LONG_INFO ]- */

  if (goAway)
    fprintf(logfile, "^^^ 2 players/1 slot.  was %s (%s)\n",
	    start_name, start_login);
  fclose(logfile);
}


/*
 * .pics file format:
 * 
 * name x y page name x y page etc
 */

void
doMotdPics()
{
  FILE *ptr, *ftemp;
  char buf[128], fname[128];
  unsigned char *bits;
  char *result;
  int x, y, page, w, h;
  int bytesperline;		/* pad the width to a byte */
  int linesperblock;		/* how many lines in 1016 bytes? */
  int i;
  char *paths;

  paths = build_path(PICS);
  ptr = fopen(paths, "r");
  if (ptr == NULL)
    return;
  while (1)
  {
    result = fgets(fname, 125, ptr);
    if (result == NULL)
      /* must fclose ptr */
      break;

    if (fname[strlen(fname) - 1] == '\n')
      fname[strlen(fname) - 1] = 0;

    paths = build_path(fname);
    ftemp = fopen(paths, "r");
    bits = 0;
    if (ftemp == 0)
    {
      fprintf(stderr, "ntserv: couldn't open file %s.  skipping\n", paths);
    }
    else
    {
      ParseXbmFile(ftemp, &w, &h, &bits);	/* parsexbm.c */

      bytesperline = (w - 1) / 8 + 1;
      linesperblock = 1016 /* packets.h */ / bytesperline;
    }

    fgets(buf, 125, ptr);

    if (3 != sscanf(buf, "%d %d %d", &x, &y, &page))
    {
      printf("Format error in .pics file\n");
      if (bits)
	free(bits);
      bits = NULL;
    }

    if (bits)
    {
      if (me != 0 && (me->p_stats.st_flags & ST_NOBITMAPS))
      {
	sendMotdNopic(x, y, page, w, h);
      }
      else
	for (i = 0; i * linesperblock < h; i++)
	{
	  int nlines;
	  if ((i + 1) * linesperblock > h)
	    nlines = h - i * linesperblock;
	  else
	    nlines = linesperblock;
#if 0
	  printf("Sending MotdPics: %s %d %d %d %d %d\n",
		 fname, x, y + i * linesperblock, page, w, nlines);
#endif
	  sendMotdPic(x, y + i * linesperblock,
		      bits + bytesperline * linesperblock * i,
		      page, w, nlines);
	}
      free(bits);
    }
  }
  fclose(ptr);
}


#undef D