/*****************************************************************************
 *                                                                           *
 *  Copyright (c) 1993, 1994, 1995, Elan Feingold (feingold@zko.dec.com)     *
 *                                                                           *
 *     PERMISSION TO USE, COPY, MODIFY, AND TO DISTRIBUTE THIS SOFTWARE      *
 *     AND ITS DOCUMENTATION FOR ANY PURPOSE IS HEREBY GRANTED WITHOUT       *
 *     FEE, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE APPEAR IN ALL           *
 *     COPIES AND MODIFIED COPIES AND THAT BOTH THAT COPYRIGHT NOTICE AND    *
 *     THIS PERMISSION NOTICE APPEAR IN SUPPORTING DOCUMENTATION.  THERE     *
 *     IS NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR      *
 *     ANY PURPOSE.  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS       *
 *     OR IMPLIED WARRANTY.                                                  *
 *                                                                           *
 *****************************************************************************/

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#include "types.h"
#include "riskgame.h"
#include "network.h"
#include "server.h"
#include "deck.h"
#include "debug.h"
#include "version.h"

/* Move this to the Imakefile!! */
#ifdef __hpux
#define FDSET int 
#else
#define FDSET fd_set
#endif

/* This may be a static value (i.e. OPEN_MAX), or a dynamic value,
 * as in OSF/1, accessed through sysconf().  I'm not sure that all 
 * systems have these, so we'll just guess.  I don't think this is
 * exceptionally evil, since if we run out of descriptors, the socket
 * or accept calls will fail.
 */

#define MAX_DESCRIPTORS 128

Int32      iServerCommLink;
Int32      iState;
fd_set     fdSet, fdBackup;
Deck      *pPlayerDeck = NULL;
Deck      *pCardDeck;
Int32      iReply, iAllClients;
Int32      iMaxFileDescUsed = -1;
Char       strScratch[256];
Int32      iServerMode = SERVER_REGISTERING;
Flag       fGameReset = TRUE;
Int32      iNumClientsStarted = 0;
Int32      iTurn;

/* Private server data that doesn't get replicated. */
typedef struct _Client
{
  Int32     iCommLink;
  CString   strAddress;
  Int32     iState;
} Client;

/* Structure to track failures */
typedef struct _Failure
{
  CString   strReason;
  Int32     iCount;
} Failure;

Failure   pFailures[MAX_CLIENTS];
Client    pClients[MAX_CLIENTS];
Int32     iNumClients=0, iNumFailures=0;

/* Private functions */
void     SRV_ReplicateRegistrationData(Int32 iCommLinkDest);
void     SRV_ReplicateAllData(Int32 iCommLinkDest);
void     SRV_DistributeCountries(void);
void     SRV_SetInitialArmiesOfPlayers(void);
void     SRV_NotifyClientsOfTurn(Int32 iTurn);
Int32    SRV_CommLinkToClient(Int32 iCommLink);
void     SRV_HandleRegistration(Int32 iCommLink);
void     SRV_HandleSignals(Int32 iParam);
void     UTIL_ExitProgram(Int32 iExitValue);
Int32    SRV_IterateTurn(void);

/* Very private functions */
Client *_SRV_GetNthClient(Int32 iNumClient);
 
/* Server message handlers */
void SRV_HandleEXIT(void);
void SRV_HandleALLOCPLAYER(Int32 iClient);
void SRV_HandleFREEPLAYER(void *pvMessage);
void SRV_HandleREPLYPACKET(void *pvMessage);
void SRV_HandleMESSAGEPACKET(Int32 iClient, void *pvMessage);
void SRV_HandleENTERSTATE(void *pvMessage);
void SRV_HandleDEREGISTERCLIENT(Int32 iClient);


/************************************************************************ 
 *  FUNCTION: SRV_Init
 *  HISTORY: 
 *     01.23.94  ESF  Created.
 *     02.22.94  ESF  Cleaned up a bit, removing warnings.
 *     05.08.94  ESF  Fixed MSG_MESSAGEPACKET handling.
 *     05.10.94  ESF  Fixed, not registering needed callback with DistObj.
 *     05.12.94  ESF  Added fd usage map.
 *     05.19.94  ESF  Fixed bug, passing &pvMessage instead of pvMessage.
 *     08.16.94  ESF  Cleaned up.
 *     10.01.94  ESF  Added initialization of failures.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_Init(void)
{
  struct sockaddr_in   server;
  Int32                i;

  /* Print an informative message */
  printf("SERVER: Starting %s\n", FRISK_VERSION);

  /* Catch signals so that we can clean up upon termination */
  signal(SIGINT,  SRV_HandleSignals);
  signal(SIGBUS,  SRV_HandleSignals);
  signal(SIGFPE,  SRV_HandleSignals);
  signal(SIGHUP,  SRV_HandleSignals);
  signal(SIGQUIT, SRV_HandleSignals);
  signal(SIGILL,  SRV_HandleSignals);
  signal(SIGKILL, SRV_HandleSignals);
  signal(SIGQUIT, SRV_HandleSignals);
  signal(SIGTRAP, SRV_HandleSignals);
  signal(SIGTERM, SRV_HandleSignals);

  /* We want to ignore this signal, because we don't want a I/O
   * failure to terminate the program.
   */

  signal(SIGPIPE, SIG_IGN);

  /* Init. clients and players */
  SRV_SetNumClients(0);

  /* Here we use the internals, not good */
  for (i=0; i!=MAX_CLIENTS; i++)
    {
      pClients[i].iState = FALSE;
      pFailures[i].strReason = NULL;
      pFailures[i].iCount = 0;
    }

  /* Initialize the pool of free players */
  pPlayerDeck = DECK_Create(MAX_PLAYERS);

  /* Create server socket */
  if ((iServerCommLink = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
      perror("Creating CommLink");
      UTIL_ExitProgram(1);
    }
  
  /* Update the max */
  iMaxFileDescUsed = MAX(iMaxFileDescUsed, iServerCommLink);
  
  /* Name sockets using wildcards */
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(RISK_PORT);
  
  /* Bind the socket to the port */
  if (bind(iServerCommLink, (struct sockaddr *)&server, sizeof(server)))
    {
      printf("SERVER: The Frisk port (%d), is already in use.  Perhaps a\n"
	     "        server is already running, or else a server crashed\n"
	     "        badly, in which case you will probably have to wait\n"
	     "        a few minutes till the port clears.  I'm exiting.\n", 
	     RISK_PORT);
      UTIL_ExitProgram(1);
    }

  SRV_PlayGame();
}


/************************************************************************ 
 *  FUNCTION: SRV_BroadcastTextMessage
 *  HISTORY: 
 *     01.26.94  ESF  Created.
 *     01.15.95  ESF  Initialized .iTo field to avoid uninitialized memory.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_BroadcastTextMessage(CString strMessage)
{
  MsgMessagePacket   msgMess;

  msgMess.strMessage  = strMessage;
  msgMess.strFrom     = "Server";
  msgMess.iTo         = DST_ALLPLAYERS;

  /* Send the message out to all clients */
  SRV_BroadcastMessage(MSG_MESSAGEPACKET, &msgMess);
}


/************************************************************************ 
 *  FUNCTION: SRV_PlayGame
 *  HISTORY: 
 *     02.04.94  ESF  Created.
 *     02.05.94  ESF  Fixed broadcast loop bug. 
 *     02.05.94  ESF  Fixed message receive bug.
 *     03.03.94  ESF  Changed to send _UPDATE to all clients but sender.
 *     03.28.94  ESF  Added _DEADPLAYER & _ENDOFGAME.
 *     03.29.94  ESF  Added _REQUESTCARD.
 *     04.01.94  ESF  Fixed card exchange to work right with jokers.
 *     04.11.94  ESF  Fixed CARDPACKET to broadcast the card.
 *     05.05.94  ESF  Added MSG_OBJ* msgs.
 *     05.06.94  ESF  Factored out dealing cards code.
 *     05.15.94  ESF  Added MSG_[ALLOC|FREE]PLAYER.
 *     05.15.94  ESF  Added MSG_REPLYPACKET.
 *     05.17.94  ESF  Added MSG_NETMESSAGE.
 *     06.24.94  ESF  Fixed memory leak bug.
 *     07.27.94  ESF  Completely revamped, combined with CollectPlayers().
 *     09.31.94  ESF  Fixed so that a new deck is created upon reset.
 *     10.01.94  ESF  Fixed MSG_ENDOFGAME to pass message and not NULL.
 *     10.02.94  ESF  Fixed so in case of bogus message, client is killed.
 *     10.03.94  ESF  Fixed bug, excessive processing of MSG_DEREGISTERCLIENT.
 *     10.08.94  ESF  Added SERVER_FORTIFYING mode to fix a bug.
 *     10.29.94  ESF  Added handling for MSG_DICEROLL.
 *     10.30.94  ESF  Fixed serious bug: SERVER[_REGISTERING -> _FORTIFYING]. 
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_PlayGame(void)
{
  Int32             i, n, iMessageType;
  void             *pvMessage;

  /* Create the card deck */
  pCardDeck = DECK_Create(NUM_COUNTRIES + 2);

  /* Add the initial fd to keep an eye on -- the connect socket */
  FD_ZERO(&fdBackup);
  FD_SET(iServerCommLink, &fdBackup);
  
  /* Start accepting connections */
  listen(iServerCommLink, 5);
  
  /* Loop for the entirety of the game */
  for(;;)
    {
      fdSet = fdBackup;

      /* If there have been any failures, then deal with them */
      SRV_RecoverFailures();
      
      /* Wait for a message to come in */
      if (select(iMaxFileDescUsed+1, (FDSET *)&fdSet, (FDSET *)0, (FDSET *)0, 
		 NULL) < 0)
	perror("Select");
      
      /* Two things might have happened here.  Either an existing client
       * sent a message to the server, or a new client sent a message to
       * the connect port, trying to join the game.  If the former occurred
       * process it normally.  If the latter occurred, let the client
       * connect, and add its fd to the fd map.  If we are in the middle
       * of a game, send it a message saying this, and then perform a 
       * RISK_SelectiveReplicate() so as to get the new client in the
       * same state as the others.  Also send a message to the other
       * clients telling them what is happening.
       */
      
      if (FD_ISSET(iServerCommLink, &fdSet))
	{
	  Int32 iNewCommLink;

	  /* Try to accept the new connection */
	  if ((iNewCommLink = accept(iServerCommLink, 0, 0)) < 0)
	    {
	      /* Couldn't do it, go back to top */
	      printf("SERVER: Connect to client failed, its loss...\n");
	      continue;
	    }
	  else /* connection went fine */
	    {
	      /* Does Frisk have enough resources to hold this client? */
	      if (SRV_GetNumClients() >= MAX_CLIENTS ||
		  iNewCommLink >= MAX_DESCRIPTORS)
		{
		  (void)RISK_SendMessage(iNewCommLink, MSG_EXIT, NULL);
		  close(iNewCommLink);
		  continue;
		}
	    }

	  /* Assert: At this point, connection is complete and we have
	   * enough resources to keep it around.  Begin connection protocol.
	   */

	  SRV_HandleRegistration(iNewCommLink);
	}
      else
	{
	  Flag fHandledMessage = FALSE;

	  /* There is a client trying to send a message */
	  for (i=0; i!=MAX_CLIENTS && !fHandledMessage; i++)
	    if (SRV_GetStateOfClient(i) == TRUE &&
		FD_ISSET(SRV_GetCommLinkOfClient(i), &fdSet))
	      {
		/* We got the message we were looking */
		fHandledMessage = TRUE;
		
		/* Actually pick it up, skip it if the was failure */
		if (!RISK_ReceiveMessage(SRV_GetCommLinkOfClient(i), 
					 &iMessageType, &pvMessage))
		  continue;

		/* Depending on the message type, dispatch it to one
		 * of the handlers.  They usually take just a message,
		 * but depending on what they do they could take the
		 * and/or the client index.
		 */

		switch (iMessageType)
		  {
		  case MSG_NETMESSAGE:
		  case MSG_DICEROLL:
		  case MSG_PLACENOTIFY:
		  case MSG_ATTACKNOTIFY:
		  case MSG_MOVENOTIFY:
		    SRV_CompleteBroadcast(i, iMessageType, pvMessage);
		    break;

		  case MSG_ENDOFGAME:
		    {
		      /* Let everyone else know */
		      SRV_BroadcastMessage(MSG_ENDOFGAME, pvMessage);
		      iServerMode = SERVER_REGISTERING;
		    }
		    break;
		    
		  case MSG_ALLOCPLAYER:
		    SRV_HandleALLOCPLAYER(i);
		    break;
		    
		  case MSG_FREEPLAYER:
		    SRV_HandleFREEPLAYER(pvMessage);
		    break;
		    
		  case MSG_REPLYPACKET:
		    SRV_HandleREPLYPACKET(pvMessage);
		    break;
		    
		  case MSG_MESSAGEPACKET:
		    SRV_HandleMESSAGEPACKET(i, pvMessage);
		    break;
		    
		    /* These next two messages imply that the client is going
		     * to register players, and will eventually send a 
		     * MSG_STARTGAME when it is done (or a MSG_DEREGISTERCLIENT
		     * if it decides to abort.
		     */

		  case MSG_GAMENEWPLAYERS:
		  case MSG_GAMEMOREPLAYERS:
		    
		    /* Set the message to send */
		    if (iMessageType == MSG_GAMENEWPLAYERS)
		      sprintf(strScratch, "%s is registering new players.",
			      SRV_GetAddressOfClient(i));
		    else /* ((iMessageType == MSG_GAMEMOREPLAYERS) */
		      sprintf(strScratch, "%s is registering additional "
			      "players.", SRV_GetAddressOfClient(i));

		    /* Set the source and send it */
		    printf("SERVER: %s\n", strScratch);
		    SRV_BroadcastTextMessage(strScratch);

		    /* Reset the game if it has not been already reset */
		    if (!fGameReset)
		      {
			RISK_ResetGame();
			fGameReset = TRUE;

			/* Get a new deck, destroy the old one. */
			DECK_Destroy(pCardDeck);
			pCardDeck = DECK_Create(NUM_COUNTRIES + 2);
		      }

		    break;
		    
		    /* These messages are all ones that imply the following
		     * postcondition:  We are one client closer to being
		     * able to start a game.  Either the client deregisters,
		     * or specifies how it wants to be involved in the next
		     * game (and won't need to register players), or specifies 
		     * that it has finished registering players.
		     */
		    
		  case MSG_STARTGAME:
		  case MSG_SAMEPLAYERS:
		  case MSG_DEREGISTERCLIENT:
		    {
		      /* Send out an appropriate message */ 
		      if (iMessageType == MSG_SAMEPLAYERS)
			{
			  sprintf(strScratch, 
				  "%s is playing again with the same players.",
				  SRV_GetAddressOfClient(i));
			}
		      else if (iMessageType == MSG_STARTGAME)
			{
			  sprintf(strScratch, 
				  "%s has finished registering players.", 
				  SRV_GetAddressOfClient(i));
			}
		      else if (iMessageType == MSG_DEREGISTERCLIENT)
			{
			  /* Do the actual deregistration */
			  SRV_HandleDEREGISTERCLIENT(i);

			  sprintf(strScratch, 
				  "%s has deregistered.",
				  SRV_GetAddressOfClient(i));

			  /* If this was the last client, then jump into
			   * registration mode.
			   */
			  
			  if (SRV_GetNumClients() == 0)
			    {
			      D_Assert(RISK_GetNumLivePlayers() == 0, 
				       "Bogus number of live players!");
			      D_Assert(RISK_GetNumPlayers() == 0, 
				       "Bogus number of players!");
			      iServerMode = SERVER_REGISTERING;
			    }
			  else if (iServerMode == SERVER_PLAYING ||
				   iServerMode == SERVER_FORTIFYING)
			    {
			      /* Break out of processing this message */
			      SRV_BroadcastTextMessage(strScratch); 
			      break;
			    }
			}

		      printf("SERVER: %s\n", strScratch);
		      SRV_BroadcastTextMessage(strScratch); 

		      /* We have another client ready to go.  If the
		       * number of all the clients have sent this message,
		       * then we want to start a new game.  First, get
		       * the initial number of armies that each client
		       * gets, and then dole out the countries to the
		       * players.  Lastly, pick a random player, and send
		       * that player's client a MSG_TURNNOTIFY to let 
		       * the client know that it is that player's turn.
		       * If this was the first client, and the map is
		       * dirty, then reset the distributed object.
		       */
		       
		      D_Assert(iServerMode == SERVER_REGISTERING, 
			       "Something's wierd!");

		      /* Reset the game if it hasn't already been reset */
		      if (!fGameReset)
			{
			  RISK_ResetGame();
			  fGameReset = TRUE;

			  /* Get a new deck, destroy the old one. */
			  DECK_Destroy(pCardDeck);
			  pCardDeck = DECK_Create(NUM_COUNTRIES + 2);
			}
		      
		      /* As long as the client wasn't deregistering, 
		       * then we have one more client ready to register.
		       * (A client deregistering gets taken care of by
		       * lowering the number of clients by one).
		       */

		      if (iMessageType != MSG_DEREGISTERCLIENT)
			iNumClientsStarted++;

		      /* If there are enough players to play, then
		       * start, otherwise, print a warning -- other
		       * clients may join later.  The fundamental 
		       * check here is that all of the clients
		       * have started.
		       */

		      if (iNumClientsStarted == SRV_GetNumClients() &&
			  RISK_GetNumPlayers() >= 2)
			{
			  Int32 iIndex;
			  
			  printf("SERVER: Clients have finished " 
				 "registering, beginning game.\n");
			  iServerMode = SERVER_FORTIFYING;
			  
			  SRV_SetInitialArmiesOfPlayers();
			  
			  /* We're going to need to reset it before
			   * playing again, so mark this.
			   */
			  
			  fGameReset = FALSE;
			  
			  /* All of the players start out alive */
			  RISK_SetNumLivePlayers(RISK_GetNumPlayers());
			
			  /* Dole out the countries */
			  SRV_DistributeCountries();
  
			  /* Compute a random player and notify clients */
			  D_Assert(RISK_GetNumLivePlayers(), 
				   "Modulo by zero!");
			  
			  iIndex = rand() % RISK_GetNumLivePlayers();
			  iTurn = RISK_GetNthLivePlayer(iIndex);
			  SRV_NotifyClientsOfTurn(iTurn);
			  
			  /* Reset this for the next time */
			  iNumClientsStarted = 0;
			}
		    }
		    break;
		    
		  case MSG_OBJSTRUPDATE:
		  case MSG_OBJINTUPDATE:
		    RISK_ProcessMessage(iMessageType, pvMessage, i);
		    break;

		  case MSG_ENDTURN:
		    {
		      Int32 iPlayer;

		      /* Sanity check */
		      D_Assert(RISK_GetNumLivePlayers(), "Modulo by zero!");

  		      if (iServerMode == SERVER_FORTIFYING)
  			{
  			  Int32  i;
  			  Flag   fFoundPlayer;
  
  			  /* If noone has any more armies, then move to
  			   * SERVER_PLAYING mode.  Otherwise, go along 
  			   * the list of players, looking for a player who 
  			   * still has armies to place.
  			   */
  
  			  for (i=0, fFoundPlayer=FALSE; 
  			       i!=RISK_GetNumLivePlayers() && !fFoundPlayer; 
  			       i++)
  			    {
  			      /* The next player who's turn it is */ 
  			      iPlayer = SRV_IterateTurn();
  
  			      /* Is there a player with armies left? */
  			      if (RISK_GetNumArmiesOfPlayer(iPlayer) != 0)
  				{
  				  SRV_NotifyClientsOfTurn(iPlayer);
  				  fFoundPlayer = TRUE;
  				}
  			    }
  
  			  /* If we have not found a player with armies, 
  			   * then there are no longer any players with any 
  			   * armies to fortify with.  Let the game begin!!  
  			   * Theorem (proof left to reader):  When some 
  			   * players have more armies to to fortify with 
  			   * than others (because they got less countries), 
  			   * then the last player to fortify here will be 
  			   * the last player in relation to the first player 
  			   * who fortified.  So if we move to the next 
  			   * player, it will be the player who fortified 
  			   * first, which is what we want.
  			   */
  			  
  			  if (!fFoundPlayer)
  			    {
  			      iServerMode = SERVER_PLAYING;
  			      iPlayer = SRV_IterateTurn();
  			      SRV_NotifyClientsOfTurn(iPlayer);
  			    }
  			}
  		      else /* iServerMode == SERVER_PLAYING */
  			{
  			  /* Get the next player */
			  iPlayer = SRV_IterateTurn();
			  SRV_NotifyClientsOfTurn(iPlayer);
  			} 
		    }
		    break;
		    
		  case MSG_ENTERSTATE:
		    SRV_HandleENTERSTATE(pvMessage);
		    break;
		    
		  case MSG_EXCHANGECARDS:
		    {
		      MsgExchangeCards *pMess = (MsgExchangeCards *)pvMessage;
		      MsgReplyPacket    msgMess;
		      Int32             iNumJokers;
		      
		      /* Put cards back on the deck and change them to type */
		      for (n=iNumJokers=0; n!=3; n++)
			{
			  DECK_PutCard(pCardDeck, pMess->piCards[n]);
			  
			  if (pMess->piCards[n] < NUM_COUNTRIES)
			    pMess->piCards[n] %= 3;
			  else
			    pMess->piCards[n] = -1, iNumJokers++;
			}
		      
		      /* Find out how many armies the player gets in 
		       * exchange for the cards and send them to him or
		       * her, in an _UPDATEARMIES message.  Right now
		       * the only option is fixed return values for card
		       * exchanges.
		       */
		      
		      /* Do we have one of each (possibly with jokers)? */
		      if ((pMess->piCards[0] != pMess->piCards[1] &&
			   pMess->piCards[1] != pMess->piCards[2] &&
			   pMess->piCards[0] != pMess->piCards[2]) ||
			  iNumJokers >= 2)
			{
			  msgMess.iReply = 10;
			}
		      else if (pMess->piCards[0]==0 ||
			       pMess->piCards[1]==0 ||
			       pMess->piCards[2]==0)
			{
			  msgMess.iReply = 8;  
			}
		      else if (pMess->piCards[0]==1 ||
			       pMess->piCards[1]==1 ||
			       pMess->piCards[2]==1)
			{
			  msgMess.iReply = 6;  
			}
		      else 
			{
			  msgMess.iReply = 4;  
			}
		      
		      (void)RISK_SendMessage(SRV_GetCommLinkOfClient(i), 
					     MSG_REPLYPACKET, &msgMess);
		    }
		    break;
		    
		  case MSG_REQUESTCARD:
		    {
		      MsgRequestCard *pMess = (MsgRequestCard *)pvMessage;
		      
		      RISK_SetCardOfPlayer(pMess->iPlayer, 
					   RISK_GetNumCardsOfPlayer
					   (pMess->iPlayer),
					   DECK_GetCard(pCardDeck));
		      RISK_SetNumCardsOfPlayer(pMess->iPlayer,
					       RISK_GetNumCardsOfPlayer
					       (pMess->iPlayer)+1);
		    }
		    break;
		    
		  case MSG_EXIT:
		    SRV_HandleEXIT();
		    break;
		  
		  default:
		    {
		      MsgNetPopup msg;

		      /* Assume that client is messed up.  Consider it 
		       * a failure and kill the client.
		       */
		      
		      printf("SERVER: Considering client %s dead (it send a"
			     " bogus message.)\n", SRV_GetAddressOfClient(i));

		      /* In case the client is alive and well, send it a 
		       * message to inform it why it is being axed.
		       */
		      
		      sprintf(strScratch, 
			    "The server received an invalid message from me.");
		      msg.strMessage = strScratch;
		      (void)RISK_SendMessage(SRV_GetCommLinkOfClient(i), 
					     MSG_NETPOPUP, &msg);
		      
		      /* Log the failure */
		      SRV_LogFailure("Sent bogus message", 
				     SRV_GetCommLinkOfClient(i));
		    }
		  }
		
		/* Free up the memory the message was taking */
		NET_DeleteMessage(iMessageType, pvMessage);

		D_Assert(i != MAX_CLIENTS && fHandledMessage == TRUE, 
			 "Message received from unknown source!!");
	      }
	}
    }
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.27.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_NotifyClientsOfTurn(Int32 iPlayer)
{
  MsgTurnNotify msgTurnNotify;

  /* Let all clients know whos turn it is */
  msgTurnNotify.iPlayer = iPlayer;
  msgTurnNotify.iClient = RISK_GetClientOfPlayer(iPlayer);
  SRV_BroadcastMessage(MSG_TURNNOTIFY, &msgTurnNotify);
}
  

/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *     07.25.94  ESF  Changed to support TEST_GAMEs.
 *     10.30.94  ESF  Changed to support TEST_GAMEs better.
 *     10.30.94  ESF  Changed to refer to LivePlayer instead of Player.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_DistributeCountries(void)
{
  Deck   *pCountryDeck = DECK_Create(NUM_COUNTRIES);
  Int32   iCountry, iPlayer, i;

  /* Dole them out starting with the first player */
  iTurn = RISK_GetNthLivePlayer(0);

  /* Dole out the countries */
  for(i=0; i!=NUM_COUNTRIES; i++)
    {
      /* Pick a country, any country */
      iCountry = DECK_GetCard(pCountryDeck);

#ifdef TEST_GAME
      /* Give countries to the first player, leave one for the rest */
      iPlayer = (i <= NUM_COUNTRIES-RISK_GetNumLivePlayers()) 
	  ? RISK_GetNthLivePlayer(0) 
	  : RISK_GetNthLivePlayer(i-(NUM_COUNTRIES-RISK_GetNumLivePlayers()));
#else      
      iPlayer = iTurn;
#endif

      /* Update the game object */
      RISK_SetOwnerOfCountry(iCountry, iPlayer);
      RISK_SetNumCountriesOfPlayer(iPlayer, 
				   RISK_GetNumCountriesOfPlayer(iPlayer)+1);
      RISK_SetNumArmiesOfCountry(iCountry, 1);
      RISK_SetNumArmiesOfPlayer(iPlayer, RISK_GetNumArmiesOfPlayer(iPlayer)-1);

      /* Iterate to next player */
      (void)SRV_IterateTurn();
    }

#ifdef TEST_GAME
  /* Set the number of armies to be small, to let the game start soon. */
  for (i=0; i!=RISK_GetNumLivePlayers(); i++)
    RISK_SetNumArmiesOfPlayer(RISK_GetNthLivePlayer(i), 2);
#endif
  
  DECK_Destroy(pCountryDeck);  
}


/************************************************************************ 
 *  FUNCTION: SRV_SetInitialArmiesOfPlayers
 *  HISTORY: 
 *     08.27.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_SetInitialArmiesOfPlayers(void)
{
  Int32 i, iPlayer, iNumArmies;

  /* Calculate the number of armies. */
  iNumArmies = MAX(NUM_COUNTRIES/RISK_GetNumPlayers() + 5,
		   40 - RISK_GetNumPlayers());
  
  /* Set the initial number of armies for all the players */
  for (i=0; i!=RISK_GetNumPlayers(); i++)
    {
      iPlayer = RISK_GetNthPlayer(i);
      RISK_SetNumArmiesOfPlayer(iPlayer, iNumArmies);

      /* Sanity check */
      D_Assert(RISK_GetNumArmiesOfPlayer(iPlayer) > 0,
	       "Number of armies is negative!");
    }
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     02.05.94  ESF  Created.
 *     08.16.94  ESF  Rewrote a bit.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_BroadcastMessage(Int32 iMessType, void *pvMessage)
{
  Int32 i;
  
  /* If there are no clients, then do nothing */
  if (SRV_GetNumClients() == 0)
    return;

  /* Loop through and send the message to each active client */
  for (i=0; i!=MAX_CLIENTS; i++)
    if (SRV_GetStateOfClient(i) == TRUE)
      (void)RISK_SendMessage(SRV_GetCommLinkOfClient(i), iMessType, pvMessage);
}


/************************************************************************ 
 *  FUNCTION:
 *  HISTORY: 
 *     03.28.94  ESF  Created.
 *     08.16.94  ESF  Rewrote a bit.
 *     10.02.94  ESF  Fixed a heinous bug, was going 'till NumClients.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_CompleteBroadcast(Int32 iClientExclude, Int32 iMessageType, 
			   void *pvMess)
{
  Int32 i;

  for (i=0; i!=MAX_CLIENTS; i++)
    if (i!=iClientExclude && SRV_GetStateOfClient(i) == TRUE)
      (void)RISK_SendMessage(SRV_GetCommLinkOfClient(i), iMessageType, pvMess);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.18.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_ObjectMessage(Int32 iMessType, void *pvMess, Int32 iType, Int32 iSrc)
{
  if (iType == MESS_OUTGOING)
    SRV_BroadcastMessage(iMessType, pvMess);
  else /* (iType == MESS_INCOMING) */
    SRV_CompleteBroadcast(iSrc, iMessType, pvMess);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleEXIT(void)
{
  Int32 n;

  /* close the listening socket */
  close(iServerCommLink);

  /* close all of the player sockets */
  for(n=0; n!=MAX_CLIENTS; n++)
    if (SRV_GetStateOfClient(n) == TRUE)
      close(SRV_GetCommLinkOfClient(n));

  UTIL_ExitProgram(0);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *     10.04.94  ESF  Fixed a bug, creating player not a transaction.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleALLOCPLAYER(Int32 iClient)
{
  MsgReplyPacket mess;
  
  /* Get a player and return it, or -1 if none available */
  mess.iReply = DECK_GetCard(pPlayerDeck);
  
  /* If there was a valid player, make a note */
  if (mess.iReply != -1)
    {
      RISK_SetNumPlayers(RISK_GetNumPlayers()+1);
      RISK_SetNumLivePlayers(RISK_GetNumLivePlayers()+1);

      /* We need to assure that the player is now valid, since
       * we have increased the number of players.  The was this
       * is done is by assigning the player a client that is
       * not -1.  This way if the client crashes before it is
       * able to do this, then we have a valid client.  This
       * is in the notion of creating a transaction, an atomic
       * series of operations.  We wish to create a valid player
       * through a series of operations, and so to ensure that the
       * player is valid, we assign to it the client here, although
       * the client should set this itself.  Eventually maybe this
       * should be done just here...
       */

      RISK_SetClientOfPlayer(mess.iReply, iClient);
    }
  
  RISK_SendMessage(SRV_GetCommLinkOfClient(iClient), MSG_REPLYPACKET, 
		   &mess);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleREPLYPACKET(void *pvMessage)
{
  iReply = ((MsgReplyPacket *)pvMessage)->iReply; 
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *     10.15.94  ESF  Fixed a bug, only Set LivePlayers if player is alive.
 *     11.06.94  ESF  Fixed a bug, return player's cards to server.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleFREEPLAYER(void *pvMessage)
{
  Int32 iPlayer, i;
  
  /* Put the player ID back onto pool of free players */
  iPlayer = ((MsgFreePlayer *)(pvMessage))->iPlayer;
  DECK_PutCard(pPlayerDeck, iPlayer);

  /* If the player has cards at this point, take them away and put
   * them back onto the card deck.
   */

  for (i=0; i!=RISK_GetNumCardsOfPlayer(iPlayer); i++)
    DECK_PutCard(pCardDeck, RISK_GetCardOfPlayer(iPlayer, i));
  
  /* If the player was alive, then subtract it. */
  if (RISK_GetStateOfPlayer(iPlayer) == TRUE)
    RISK_SetNumLivePlayers(RISK_GetNumLivePlayers()-1);
  
  /* Details, details... */
  RISK_SetClientOfPlayer(iPlayer, -1);
  RISK_SetNumPlayers(RISK_GetNumPlayers()-1);

  /* If the number of live players went down to 0 by any chance, then
   * we must go back to registering mode.  Kind of moot, but safe...
   */

  if (RISK_GetNumLivePlayers() == 0)
    iServerMode = SERVER_REGISTERING;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleMESSAGEPACKET(Int32 iClient, void *pvMessage)
{
  MsgMessagePacket *pMess = (MsgMessagePacket *)pvMessage;
  
  if (pMess->iTo == DST_ALLPLAYERS)
    SRV_BroadcastMessage(MSG_MESSAGEPACKET, pvMessage);
  else if (pMess->iTo == DST_ALLBUTME)
    SRV_CompleteBroadcast(iClient, MSG_MESSAGEPACKET, pvMessage);
  else
    (void)RISK_SendMessage(SRV_GetCommLinkOfClient(pMess->iTo), 
			   MSG_MESSAGEPACKET, pvMessage);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleENTERSTATE(void *pvMessage)
{
  iState = ((MsgEnterState *)pvMessage)->iState; 
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *     08.31.94  ESF  Changed to use only one socket.
 *     09.28.94  ESF  Fixed to handle errant clients correctly (free 'em).
 *     09.28.94  ESF  Fixed the process of filling in the new clients.
 *     09.31.94  ESF  Added notification of current turn for new clients.
 *     10.01.94  ESF  Fixed to check for failure of ReceiveMessage.
 *     10.15.94  ESF  Fixed, was freeing client that I wasn't allocating.
 *     10.30.94  ESF  Added quotes to the "A new client..." message.
 *     01.01.95  ESF  Fixed a bug in notifying clients of current player.
 *     01.15.95  ESF  Initilized iTo field to avoid uninitialized memory.
 *     01.15.95  ESF  Fixed memory leak.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleRegistration(Int32 iCommLink)
{
  Int32                 n, iMessType;
  MsgRegisterClient    *pmsgRegisterClient;
  MsgClientIdent        msgClientIdent;

  /* It will send a MSG_REGISTERCLIENT, then we send it a MSG_CLIENTIDENT */
  if (!RISK_ReceiveMessage(iCommLink, &iMessType, 
			   (void **)&pmsgRegisterClient))
    {
      NET_DeleteMessage(MSG_REGISTERCLIENT, pmsgRegisterClient);
      return;
    }
  
  if (iMessType == MSG_REGISTERCLIENT)
    {
      Int32 iNewClient;

      /* Get the handle to a new client */
      iNewClient = SRV_AllocClient();

      /* Did the allocation fail? */
      if (iNewClient == -1)
	{
	  printf("SERVER: Unable to allocate room for new client!");
	  close (iCommLink);
 	  NET_DeleteMessage(MSG_REGISTERCLIENT, pmsgRegisterClient);
	  return;
	}

      /* Send its ID */
      msgClientIdent.iClientID = iNewClient;
      (void)RISK_SendMessage(iCommLink, MSG_CLIENTIDENT, &msgClientIdent);
      
      printf("SERVER: A new client, \"%s\", has registered.\n",
	     pmsgRegisterClient->strClientAddress);

      /* Set up its fields */
      SRV_SetCommLinkOfClient(iNewClient, iCommLink);
      SRV_SetAddressOfClient(iNewClient, pmsgRegisterClient->strClientAddress);

      /* Remember to look for messages from it with select(), 
       * and update the maximum file descriptor.
       */

      FD_SET(iCommLink, &fdBackup);
      iMaxFileDescUsed = MAX(iMaxFileDescUsed, iCommLink);

      /* Let the new client know about old clients, if needed */
      if (SRV_GetNumClients() > 1)
	{
	  MsgMessagePacket  msgMess;
	  
	  /* Let the new client know about old clients */
	  msgMess.strMessage = strScratch;
	  msgMess.strFrom = "Server";
	  msgMess.iTo = DST_OTHER;
	  
	  /* Send info about everybody but the new client. */
	  for (n=0; n!=MAX_CLIENTS; n++)
	    if (SRV_GetStateOfClient(n) == TRUE &&
		SRV_GetCommLinkOfClient(n) != iCommLink)
	      {
		sprintf(strScratch, "The client \"%s\", is registered.",
			SRV_GetAddressOfClient(n));

		(void)RISK_SendMessage(iCommLink, MSG_MESSAGEPACKET, &msgMess);
	      }
	}
      
      /* Let all clients know about it */
      sprintf(strScratch, "A new client, %s, has registered.",
	      SRV_GetAddressOfClient(iNewClient));
      SRV_BroadcastTextMessage(strScratch);

      /* If there are any players, let the new client 
       * know about them, so that it can set up the 
       * colors and other things.
       */
      
      if (RISK_GetNumPlayers())
	{
	  /* Let the clients know the situation */
	  sprintf(strScratch, "The client %s is being updated.",
		  SRV_GetAddressOfClient(iNewClient));
	  SRV_BroadcastTextMessage(strScratch);

	  if (iServerMode == SERVER_REGISTERING)
	    SRV_ReplicateRegistrationData(iCommLink);
	  else if (iServerMode == SERVER_PLAYING ||
		   iServerMode == SERVER_FORTIFYING)
	    {
	      MsgTurnNotify  msgTurnNotify;

	      SRV_ReplicateAllData(iCommLink);

	      /* Let the client know who's turn it is currently */
	      msgTurnNotify.iPlayer = iTurn;
	      msgTurnNotify.iClient = 
		RISK_GetClientOfPlayer(msgTurnNotify.iPlayer);
	      (void)RISK_SendMessage(iCommLink, MSG_TURNNOTIFY, 
				     &msgTurnNotify);
	    }
	}      

      /* Tell the new client to pop up its registration box, if we
       * are not in game playing mode.
       */
      
      if (iServerMode == SERVER_REGISTERING)
	(void)RISK_SendMessage(iCommLink, MSG_POPUPREGISTERBOX, NULL);
    }
  else /* client not obeying protocol */
    {
      printf("SERVER: Connecting client is not obeying protocol!\n");
      FD_CLR(iCommLink, &fdBackup);
      close(iCommLink);
    }
  
  /* Don't need this anymore */
  NET_DeleteMessage(MSG_REGISTERCLIENT, pmsgRegisterClient);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.10.94  ESF  Created.
 *     08.31.94  ESF  Revamped, made more sophisticated.
 *     09.30.94  ESF  Fixed bug, freeing player before MSG_DELETEMSGDST.
 *     09.31.94  ESF  Fixed so that FreeClient is called last.
 *     10.01.94  ESF  Fixed so that it doesn't return too soon.
 *     01.15.95  ESF  Removed MSG_DELETEMSGDST stuff.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleDEREGISTERCLIENT(Int32 iClient)
{
  D_Assert(iClient>=0 && iClient<MAX_CLIENTS, "Bogus client!");

  if (RISK_GetNumLivePlayersOfClient(iClient) == 0)
    ; /* Let it go away... */
  else if (iServerMode == SERVER_PLAYING ||
	   iServerMode == SERVER_FORTIFYING)
    {
      MsgNetPopup   msgNetPopup;
      MsgEndOfGame  msgEndOfGame;

      /* We need to end the game, let other clients know
       * that some client was killed or exited or something.
       */
      
      sprintf(strScratch, "Game is over, because %s exited or failed.",
	      SRV_GetAddressOfClient(iClient));
      msgNetPopup.strMessage = strScratch;
      
      SRV_CompleteBroadcast(iClient, MSG_NETPOPUP, &msgNetPopup);

      /* There was no winner */
      msgEndOfGame.iWinner = -1;
      SRV_CompleteBroadcast(iClient, MSG_ENDOFGAME, &msgEndOfGame);
      
      /* Server invariant:  Before the next game begins, some client
       * will send a message to the server that will cause 
       * RISK_ResetGame() to be called.  Formal proof left to the 
       * reader as an exercise -- a lot easier than the proofs we
       * were assigned in CS280, let me tell you...
       */

      iServerMode = SERVER_REGISTERING;
    }

  /* We need to free the client */
  SRV_FreeClient(iClient);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     06.16.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void UTIL_ExitProgram(Int32 iExitValue)
{
  MEM_TheEnd();

  /* Don't let any more data be received on this socket */
  shutdown(iServerCommLink, 0);

  exit(iExitValue);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     07.31.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_HandleSignals(Int32 iParam)
{
  Int32          i;
  MsgNetPopup    msg;

  /* Send a last message to the clients to inform them */
  sprintf(strScratch, "Server was killed by a signal!");
  msg.strMessage = strScratch;
  SRV_BroadcastMessage(MSG_NETPOPUP, &msg);

  /* Close all of the open file descriptors */
  printf("Ouch, I've been killed...closing file descriptors.\n");
  
  for (i=0; i!=MAX_CLIENTS; i++)
    if (SRV_GetStateOfClient(i) == TRUE)
      close(SRV_GetCommLinkOfClient(i));
  
  UTIL_ExitProgram(-1);
}


/************************************************************************ 
 *  FUNCTION: SRV_AllocClient
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *     08.08.94  ESF  Moved here from server.c
 *     08.21.94  ESF  Fixed to work.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 SRV_AllocClient(void)
{
  Int32 i;

  /* Since we wouldn't have accepted the connection if there weren't
   * a slot available, this call cannot fail.
   */

  D_Assert(SRV_GetNumClients() < MAX_CLIENTS, "Not good!");
  SRV_SetNumClients(SRV_GetNumClients()+1);
  
  /* Find an available client */
  for (i=0; i!=MAX_CLIENTS; i++)
    if (pClients[i].iState == FALSE)
      {
	pClients[i].iState = TRUE;
	return (i);
      }

  D_Assert(FALSE, "Something wierd happened!");
  
  /* For the compiler */
  return (0);
}


/************************************************************************ 
 *  FUNCTION: SRV_FreeClient
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *     08.08.94  ESF  Moved here from server.c.
 *     08.08.94  ESF  Fixed to delete players at client.
 *     09.09.94  ESF  Fixed to take into consideration side effects.
 *     09.13.94  ESF  Fixed loop, was doing i<=0, changed to i>=0 :)
 *     09.14.94  ESF  Fixed to deal with iNumClientsStarted.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_FreeClient(Int32 iClient)
{
  Int32            i, iNumPlayers;
  MsgFreePlayer    msgFreePlayer;

  /* Get rid of the client.  We do this first so that broadcasts
   * won't include this client.  This would normally result in
   * a SIGPIPE, if the exiting client had already shut down its
   * sockets, but since we ignore SIGPIPE, it would result in
   * a failed broadcast, which is technically wrong.
   */

  pClients[iClient].iState = FALSE;
  SRV_SetNumClients(SRV_GetNumClients()-1);

  /* If iNumClientsStarted is non-zero, then this client is 
   * deregistering when the game hasn't started yet.  Decrease
   * the number, as it won't be participating in the game.
   */

  if (iNumClientsStarted)
    {
      D_Assert(iServerMode == SERVER_REGISTERING, "Bogus iNumClientsStarted!");
      iNumClientsStarted--;
    }

  /* Reset the fields, close the sockets */
  close(SRV_GetCommLinkOfClient(iClient));

  /* Remove the socket from the fd_set */
  FD_CLR(SRV_GetCommLinkOfClient(iClient), &fdBackup);

  /* Free all of the players at the client that is being nixed.
   * The client would call CLNT_FreePlayer to do this, which
   * is an RPC type call that sends MSG_FREEPLAYER to the
   * server (that's me!)  So what we do here is manufacture a
   * MSG_FREEPLAYER and call the handler for it.  A bit roundabout,
   * perhaps, but nevertheless a consistant way of dealing with
   * freeing players.  Could probably be improved.  Notice that
   * we get the Nth player, and _not_ the Nth live player.
   * Note also, that freeing a player has the side effect of
   * changing the value of RISK_GetNumPlayers().
   */

  iNumPlayers = RISK_GetNumPlayers();

  for (i=iNumPlayers-1; i>=0; i--)
    if (RISK_GetClientOfPlayer(RISK_GetNthPlayer(i)) == iClient)
      {
	msgFreePlayer.iPlayer = RISK_GetNthPlayer(i);
	SRV_HandleFREEPLAYER((void *)&msgFreePlayer);
      }
}



/***************************************/
void SRV_SetAddressOfClient(Int32 iNumClient, CString strAddress)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  if (pClients[iNumClient].strAddress != NULL)
    MEM_Free(pClients[iNumClient].strAddress);
  pClients[iNumClient].strAddress = 
    (CString)MEM_Alloc(strlen(strAddress)+1);
  strcpy(pClients[iNumClient].strAddress, 
	 strAddress);
}

/***************************************/
void SRV_SetStateOfClient(Int32 iNumClient, Int32 iState)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  pClients[iNumClient].iState = iState;
}

/***************************************/
void SRV_SetCommLinkOfClient(Int32 iNumClient, Int32 iCommLink)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");
  
  pClients[iNumClient].iCommLink = iCommLink;
}


/***************************************/
void SRV_SetNumClients(Int32 iClients)
{
  iNumClients = iClients;
}

/***************************************/
CString SRV_GetAddressOfClient(Int32 iNumClient)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  return pClients[iNumClient].strAddress;
}

/***************************************/
Int32 SRV_GetCommLinkOfClient(Int32 iNumClient)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  return pClients[iNumClient].iCommLink;
}


/***************************************/
Int32 SRV_GetStateOfClient(Int32 iNumClient)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  return pClients[iNumClient].iState;
}


/***************************************/
Int32 SRV_GetNumClients(void)
{
  return iNumClients;
}


/************************************************************************ 
 *  FUNCTION: SRV_CommLinkToClient
 *  HISTORY: 
 *     09.01.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 SRV_CommLinkToClient(Int32 iCommLink)
{
  Int32 i;

  D_Assert(iCommLink>=0 && iCommLink<MAX_DESCRIPTORS, "Bogus CommLink!");

  for (i=0; i!=MAX_CLIENTS; i++)
    if ((SRV_GetStateOfClient(i) == TRUE) &&
	(SRV_GetCommLinkOfClient(i) == iCommLink))
      return (i);

  return (-1);
}


/************************************************************************ 
 *  FUNCTION: _SRV_GetNthClient
 *  HISTORY: 
 *     08.21.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Client *_SRV_GetNthClient(Int32 iIndex)
{
  Int32 i, iNumFound;

  /* Find the nth client and return it.  It is an error to call
   * this function with an index that is out of range.
   */

  D_Assert(iIndex>=0 && iIndex<MAX_CLIENTS,
	   "_SRV_GetNthClient called with bogus parameter!");

  for (i=0, iNumFound=-1; i!=MAX_CLIENTS && iNumFound!=iIndex ; i++)
    if (pClients[i].iState == TRUE)
      iNumFound++;
  
  if (iNumFound==iIndex)
    return &pClients[i-1];

  D_Assert(FALSE, "Bogus index!");

  /* For compiler */
  return NULL;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.28.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_ReplicateRegistrationData(Int32 iCommLinkDest)
{
  Int32 i, iPlayer;

  RISK_SelectiveReplicate(iCommLinkDest, GEN_NUMPLAYERS, 0, 0);
  RISK_SelectiveReplicate(iCommLinkDest, GEN_NUMLIVEPLAYERS, 0, 0);
  
  for (i=0; i!=RISK_GetNumPlayers(); i++)
    {
      iPlayer = RISK_GetNthPlayer(i);
      
      RISK_SelectiveReplicate(iCommLinkDest, PLR_COLORSTRING, iPlayer, 0); 
      RISK_SelectiveReplicate(iCommLinkDest, PLR_NAME, iPlayer, 0);
      RISK_SelectiveReplicate(iCommLinkDest, PLR_SPECIES, iPlayer, 0);
      RISK_SelectiveReplicate(iCommLinkDest, PLR_CLIENT, iPlayer, 0);
    }
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.28.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_ReplicateAllData(Int32 iCommLinkDest)
{
  Int32 i, j, iPlayer;

  /* First replicate the registration data */
  SRV_ReplicateRegistrationData(iCommLinkDest);

  /* Now everything else */
  for (i=0; i!=RISK_GetNumPlayers(); i++)
    {
      iPlayer = RISK_GetNthPlayer(i);
      
      RISK_SelectiveReplicate(iCommLinkDest, PLR_STATE, iPlayer, 0);
      RISK_SelectiveReplicate(iCommLinkDest, PLR_NUMCOUNTRIES, iPlayer, 0);
      RISK_SelectiveReplicate(iCommLinkDest, PLR_NUMARMIES, iPlayer, 0);
      RISK_SelectiveReplicate(iCommLinkDest, PLR_NUMCARDS, iPlayer, 0);
      
      for (j=0; j!=RISK_GetNumCardsOfPlayer(iPlayer); j++)
	RISK_SelectiveReplicate(iCommLinkDest, PLR_CARD, iPlayer, j);
    }

  for (i=0; i!=NUM_COUNTRIES; i++)
    {
      RISK_SelectiveReplicate(iCommLinkDest, CNT_OWNER, i, 0);
      RISK_SelectiveReplicate(iCommLinkDest, CNT_NUMARMIES, i, 0);
    }
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     10.01.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_LogFailure(CString strReason, Int32 iCommLink)
{
  const Int32 iClient = SRV_CommLinkToClient(iCommLink);

  D_Assert(iClient >= -1 && iClient < MAX_CLIENTS, "Bogus client...");

  /* This stops any more bogus messages from coming in from the 
   * dead client, which probably has nothing interesting to say
   * anyway...
   */
  
  FD_CLR(iCommLink, &fdBackup);

  /* If this is -1, then it is not a known client */
  if (iClient == -1)
    {
      /* This shouldn't happen much */
      printf("SERVER: CommLink operation failed, recovering.\n");
      close(iCommLink);
    }
  else
    {
      /* Log the failure, to be dealt with later. */
      pFailures[iClient].strReason = strReason;
      pFailures[iClient].iCount++; 
      iNumFailures++;
    }
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.28.94  ESF  Created.
 *     08.31.94  ESF  Revised to do something.
 *     09.29.94  ESF  Fixed to deregister client in case of failure.
 *     09.30.94  ESF  Fixed to call the right deregistration function.
 *     09.31.94  ESF  Added MSG_NETMESSAGE to be sent also.
 *     10.01.94  ESF  Changed to reflect new failure handling.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void SRV_RecoverFailures(void)
{
  Int32 iClient;

  /* An optimization */
  if (iNumFailures == 0)
    return;

  /* Go through all of the failures and handle them */
  for (iClient=0; iClient!=MAX_CLIENTS; iClient++)
    if (pFailures[iClient].iCount > 0)
      {
	MsgNetPopup       msgNetPopup;
	MsgMessagePacket  msgMessagePacket;
	
	/* Information */
	sprintf(strScratch, "%s has failed (%s) (%d failure%s)", 
		SRV_GetAddressOfClient(iClient), 
		pFailures[iClient].strReason, 
		pFailures[iClient].iCount, pFailures[iClient].iCount>1 ? 
		"s" : "");
	printf("SERVER: %s\n", pFailures[iClient].strReason);
	
	/* It is in the list of clients, so we need to destroy it.
	 * Make as if we received a MSG_DEREGISTERCLIENT.
	 */
	
	SRV_HandleDEREGISTERCLIENT(iClient);
	
	/* Tell the clients what's happening (N.B. this broadcast call
	 * has to be made AFTER the FreeClient call, otherwise, the
	 * broadcast will try to send a message to the client that
	 * just failed, and we will enter a recursive situation.)
	 */
	
	msgNetPopup.strMessage = strScratch;
	SRV_BroadcastMessage(MSG_NETPOPUP, &msgNetPopup);
	
	msgMessagePacket.strFrom = "Server";
	msgMessagePacket.strMessage = strScratch;
	SRV_BroadcastMessage(MSG_MESSAGEPACKET, &msgMessagePacket);
	
	/* Pheww... Finished. */
	printf("SERVER: Successfully handled client failure.\n");

	pFailures[iClient].iCount = 0;
	pFailures[iClient].strReason = 0;
      }

  /* Reset this */
  iNumFailures = 0;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     10.29.94  ESF  Created.
 *     11.27.94  ESF  Fixed a really stupid and serious bug.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 SRV_IterateTurn(void)
{
  /* It may be that the player who's turn it just was killed a few
   * players, and the number of live players has gone down.  The way
   * players are organized is as if they were stacked on one top of
   * the next, with the 0th live player at the bottom.  In other words,
   * if the 2nd live player gets killed, then the 3rd live player becomes
   * the 2nd live player, etc.  This is done so that one can loop from the
   * [0th -- NumLivePlayer()th] live player.  However, when calculating 
   * turns, this may screw us up.  If is the 2nd live player's turn, and
   * during this turn the 1st live player gets killed, then the next turn
   * belongs to the 2nd live player (who used to be the 3rd live player).
   * So to calculate the next turn, we iterate through the players, NOT
   * the live players.  We simply search for the next live player.  In
   * case this sounds obvious, it probably is, but the code use to read:
   * "iTurn = (iTurn+1) % RISK_GetNumLivePlayers()" which is obviously wrong.
   */

  do 
    {
      /* Go to the next player, wrap-around if we're at the end */
      iTurn = (iTurn+1) % MAX_PLAYERS;
    } 
  while (RISK_GetStateOfPlayer(iTurn) == FALSE ||
	 RISK_GetClientOfPlayer(iTurn) == -1);

  /* Return the player who's turn it is */
  return iTurn;
}


