/* 
   This file is part of Practical Distributed Processing
   Copyright (C) 2006-2007 Phillip J. Brooke and Richard F. Paige
*/

#include "netstr.h"
#include "ngcommon.h"
#include "ngcommon2.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

/* Head of the linked list of map servers. */
msnode *mshead = NULL;

/* A structure for passing connections to pthreads. */
typedef struct new_conn {
  int                s;
  struct sockaddr_in *a;
} newconn;

/* A structure for spawning new players. */
typedef struct new_spawn {
  char *name;
} newspawn;

/* The UDP port that our admin server primarily uses for comms. */
int su;
/* And the TCP port for map registrations and player logins. */
int st;

/* Flag indicating that a shutdown is desired. */
int terminate = 0;

/* Tile assignment. */
msnode* tiles[N_TILES][N_TILES];

/* Subprogram prototypes. */
void open_ports(void);
void wait_for_start(void);
void assign_tiles(void);
void running(void);
void *handle_new_player(void *param);
void spawn_player (pnode *pcurr);
void *spawn_player2 (void *param);
int from_map_server(struct sockaddr_in *a);

/* Main function and subprograms. */

int main (int argc, char *argv[]) {
  pthread_mutexattr_t mattr;

  printf("Admin server starting, protocol version %d...\n", PROTOCOL_VERSION);
  read_command_line(argc, argv);
  if (!secret_keyword) {
    printf("Need secret keyword supplying with `-s'!\n");
    exit(EXIT_FAILURE);
  }
  open_ports();
  wait_for_start();
  if (!terminate) {
    /* Start issuing tiles to map servers. */
    if (!mshead) {
      printf("No map servers registered!  Cannot assign tiles.\n");
      exit(EXIT_FAILURE);
    }
    assign_tiles();
  }
  if (!terminate) {
    /* Create player list mutex. */
    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); 
    if (pthread_mutex_init(&pmutex, &mattr) < 0) 
    {
        perror("pthread_mutex_init failed");
        exit(EXIT_FAILURE);
    }
    /* Normal running!. */
    running();
  }
  printf("Admin server shutting down.\n");
  close(su);
  exit(EXIT_SUCCESS);
}

/* Create TCP and UDP ports. */
void open_ports(void) {
  int                on              = 1;
  struct sockaddr_in a;

  /* Open a TCP port. */
    if ((st = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
      perror("Could not create TCP socket");
      exit(EXIT_FAILURE);
    }

  if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
      perror("Problem setting TCP socket option");
      exit(EXIT_FAILURE);
    }

  a.sin_family = AF_INET;
  a.sin_addr.s_addr = INADDR_ANY;
  a.sin_port = htons(ADMIN_PORT);

  if (bind(st, (struct sockaddr *) &a, sizeof(a)) != 0)
    {
      perror("Could not bind TCP socket");
      exit(EXIT_FAILURE);
    }

  if (listen(st, 5) != 0)
    {
      perror("Problem listening on TCP socket");
      exit(EXIT_FAILURE);
    }

  /* Create our UDP socket. */
  if ((su = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("Could not create socket");
    exit(EXIT_FAILURE);
  }

  if (setsockopt(su, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
      perror("Problem setting socket option");
      exit(EXIT_FAILURE);
    }

  a.sin_family = AF_INET;
  a.sin_addr.s_addr = INADDR_ANY;
  a.sin_port = htons(ADMIN_PORT);

  if (bind(su, (struct sockaddr *) &a, sizeof(a)) != 0)
    {
      perror("Could not bind socket");
      exit(EXIT_FAILURE);
    }
}

/* Accept TCP registrations from map servers until stdin gets `start'. */
void wait_for_start(void) {
  char *             buffer          = malloc(BUFFER_SIZE);
  char *             expected        = malloc(BUFFER_SIZE);
  char *             expected_token;
  fd_set             rd;
  int                acceptServer;
  int                drop;
  int                exit_flag       = 0;
  int                nonce;
  int                s;
  int                tcp_port, udp_port;
  msnode *           mscurr;
  socklen_t          sockaddr_in_len;
  struct sockaddr_in a;

  sockaddr_in_len = sizeof(struct sockaddr_in);
  printf("Accepting map server connections while waiting for `start' or `stop' command...\n");
  /* Now loop until stdin says `start' or `stop'. */
  while (!exit_flag) {

    FD_ZERO(&rd);
    FD_SET(0, &rd); /* Stdin. */
    FD_SET(st, &rd); /* The socket we're listening to. */
    
    if (select(st+1, &rd, NULL, NULL, NULL) == -1) {
      perror("Problem with select!");
      exit(EXIT_FAILURE);
    }
    /* Is it stdin that has new data?  */
    if (FD_ISSET(0, &rd)) {
      if (fgets(buffer, BUFFER_SIZE, stdin) == NULL) {
	/* End of file.  Our admin has gone!  Die. */
	printf("Stdin closed!  Will quit.\n");
	exit_flag = 1;
	terminate = 1;
      } else {
	/* Is the string `start' or `stop'? */
	if (!strncmp(buffer, "start", 5)) {
	  exit_flag = 1;
	} else
	if (!strncmp(buffer, "stop", 4)) {
	  exit_flag = 1;
	  terminate = 1;
	} else {
	  printf("\nType `start' to assign tiles and start the game running.\nType `stop' to stop the servers.\n\n");
	}
      }
    }
    /* or is it our TCP socket? */
    if (FD_ISSET(st, &rd)) {
      drop = 0;
      /* Accept a connection.  We deal with this here rather than in a
	 separate thread so that we do not have race issues with our
	 list of map servers (either when adding others, or when
	 starting the game. */
      /* Accept the connection and send a banner. */
      if ((s = accept(st, (struct sockaddr *) &a, &sockaddr_in_len)) > 0) {
	snprintf(buffer, BUFFER_SIZE, PROTOCOL_LN " STARTUP");
	if (send(s, buffer, strlen(buffer), 0) < 0) {
	  perror("Problem with send of initial message");
	  drop = 1;
	}
	/* Start reading... */
	if (!drop) {
	  if (NSrecv(s, buffer, BUFFER_SIZE, 0) <= 0) {
	    printf("Warning!  problem with recv (initial).\n");
	    drop = 1;
	  }
	}
	if (!drop) {
	  /* It should be something like `REGISTER MAP SERVER' and the
	     two ports. */
	  if (sscanf(buffer, "REGISTER MAP SERVER\nUDP-PORT %d TCP-PORT %d",
		     &udp_port, &tcp_port) != 2) {
	    printf("Unexpected message.\n");
	    drop = 1;
	  }
	}
	if (!drop) {
	  /* Create nonce, send it. */
	  nonce = random() % INT_MAX;
	  snprintf(buffer, BUFFER_SIZE,
		   "CHALLENGE %d", nonce);
	  if (send(s, buffer, strlen(buffer), 0) < 0) {
	    perror("Problem with sending challenge");
	    drop = 1;
	  }
	}
	/* Wait for response. */
	if (!drop) {
	  if (NSrecv(s, buffer, BUFFER_SIZE, 0) <= 0) {
	    printf("Warning!  problem with recv (response).\n");
	    drop = 1;
	  }
	}
	/* Is the response okay? */
	if (!drop) {
	  expected_token = make_token("REGISTER", nonce);
	  snprintf(expected, BUFFER_SIZE, "RESPONSE %s", expected_token);
	  free(expected_token);
	  if (strncmp(expected, buffer, strlen(expected))) {
	    printf("Challenge/response failed.\n");
	    acceptServer = 0;
	  } else {
	    acceptServer = 1;
	    /* Register this map server. */
	    printf("Registering map server on %s:%d.\n", 
		   inet_ntoa(a.sin_addr), ntohs(a.sin_port));
	    /* Add it to the linked list. */
	    mscurr = malloc(sizeof(msnode));
	    mscurr->udp = a;
	    mscurr->tcp = a;
	    mscurr->tcp.sin_port = htons(tcp_port);
	    mscurr->udp.sin_port = htons(udp_port);
	    mscurr->next = mshead;
	    mshead = mscurr;
	  }
	}
	if (!drop) {
	  /* Generate a suitable message. */
	  snprintf(buffer, BUFFER_SIZE, "%s MAP SERVER",
		   ((acceptServer) ? "ACCEPT" : "REJECT"));
	  if (send(s, buffer, strlen(buffer), 0) < 0) {
	    perror("Problem with send of reply.");
	  }
	}
	close(s);
      } else {
	printf("Problem with accept...\n");
      }
    } /* FD_ISSET(st, ...) */
  } /* While loop. */
};

/* Assign the tiles in a round-robin manner. */
void assign_tiles(void) {
  char *              buffer          = malloc(BUFFER_SIZE);
  char *              ok              = malloc(BUFFER_SIZE);
  char *              reply           = malloc(BUFFER_SIZE);
  int                 s;
  int                 x,y;
  msnode *            mscurr;

  snprintf(ok, BUFFER_SIZE, "OK");

  /* Actually assign the tiles. */
  mscurr = mshead;
  for (x = 0; x < N_TILES; x++) {
    for (y = 0; y < N_TILES; y++) {
      tiles[x][y] = mscurr;
      mscurr = mscurr->next;
      if (!mscurr) { mscurr = mshead; }
    }
  }

  /* Now walk through the linked list once, telling each server what
     it's got. */
  printf("Assigning tiles... ");
  mscurr = mshead;
  while (mscurr) {
    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
      {
	perror("Could not create socket to connect to map server");
	exit(EXIT_FAILURE);
      }
    if (connect(s, (struct sockaddr *) &(mscurr->tcp), sizeof(mscurr->tcp)) != 0)
      {
	perror("Could not connect to map server");
	exit(EXIT_FAILURE);
      }
    /* Prepare the reply we expect. */
    snprintf(reply, BUFFER_SIZE, PROTOCOL_LN "\nMAP");
    /* Read.... */
    if (NSrecv(s, buffer, BUFFER_SIZE, 0) <= 0) {
      printf("Problem with recv -- expecting reply `%s'.\n", reply);
      exit(EXIT_FAILURE);
    }
    /* Does it match? */
    if (strncmp(buffer, reply, strlen(reply))) {
      /* Oh dear, this is most unhelpful. */
      printf("Aiee, unexpected reply from map server.\n");
      exit(EXIT_FAILURE);
    }
    /* Send our header. */
    snprintf(buffer, BUFFER_SIZE, PROTOCOL_LN " ADMIN TILES");
    if (send(s, buffer, strlen(buffer), 0) < 0) {
      perror("Problem sending ADMIN TILES message");
      exit(EXIT_FAILURE);
    }
    /* Expect OK. */
    if (NSrecv(s, buffer, BUFFER_SIZE, 0) <= 0) {
      printf("Problem with recv -- expecting reply `%s'.\n", ok);
      exit(EXIT_FAILURE);
    }
    /* Does it match? */
    if (strncmp(buffer, ok, strlen(ok))) {
      /* Oh dear, this is most unhelpful. */
      printf("Aiee, unexpected reply from map server.\n");
      exit(EXIT_FAILURE);
    }

    for (x = 0; x < N_TILES; x++) {
      for (y = 0; y < N_TILES; y++) {
	if (tiles[x][y] == mscurr) {
	  snprintf(buffer, BUFFER_SIZE, "TILE\nXT %d\nYT %d", x, y);
	  if (send(s, buffer, strlen(buffer), 0) < 0) {
	    perror("Problem sending tile assignment");
	    exit(EXIT_FAILURE);
	  }
	  /* Wait for OK. */
	  if (NSrecv(s, buffer, BUFFER_SIZE, 0) <= 0) {
	    perror("Problem with recv");
	    printf("Was expecting reply `%s'.\n", ok);
	    exit(EXIT_FAILURE);
	  }
	  /* Does it match? */
	  if (strncmp(buffer, ok, strlen(ok))) {
	    /* Oh dear, this is most unhelpful. */
	    printf("Aiee, unexpected reply from map server.\n");
	    exit(EXIT_FAILURE);
	  }
	}
      }
    }
    /* Send DONE. */
    snprintf(buffer, BUFFER_SIZE, "DONE");
    if (send(s, buffer, strlen(buffer), 0) < 0) {
      perror("Problem sending DONE message on tile assignment");
      exit(EXIT_FAILURE);
    }

    close(s);
    /* And onto the next. */
    mscurr = mscurr -> next;
  }
  free(buffer);
  free(ok);
  free(reply);
  printf("done.\n");
}

void running(void) {
  char *             buffer          = malloc(BUFFER_SIZE);
  char *             username        = malloc(BUFFER_SIZE);
  fd_set             rd;
  int                exit_flag       = 0;
  int                s;
  int                xt, yt;
  int                xt2, yt2;
  int                xti, yti;
  msnode *           mscurr;
  newconn *          nc;
  pnode *            pcurr;
  pthread_attr_t     attr;
  pthread_t          threadid; 
  socklen_t          sockaddr_in_len;
  struct sockaddr_in a;

  sockaddr_in_len = sizeof(struct sockaddr_in);

  printf("Ready to accept player login.\n");

  while (!exit_flag) {
    /* We now have to listen to stdin (for a stop message), the TCP
       socket st for a new player, and the UDP socket for other
       messages. */
    FD_ZERO(&rd);
    FD_SET(0, &rd); /* Stdin. */
    FD_SET(su, &rd); /* The UDP socket we're listening to. */
    FD_SET(st, &rd); /* The TCP socket we're listening to. */
    if (select((su>st ? su+1 : st+1), &rd, NULL, NULL, NULL) == -1) {
      perror("Problem with select!");
      exit(EXIT_FAILURE);
    }
    
    /* Is it stdin that has new data?  */
    if (FD_ISSET(0, &rd)) {
      if (fgets(buffer, BUFFER_SIZE, stdin) == NULL) {
	/* End of file.  Our admin has gone!  Die. */
	printf("Stdin closed!  Will quit.\n");
	exit_flag = 1;
      } else {
	/* Is the string `stop'? */
	if (!strncmp(buffer, "stop", 4)) {
	  exit_flag = 1;
	} else {
	  printf("\nType `stop' to stop the servers.\n\n");
	}
      }
    } /* end: stdin had new data */

    /* or is it our TCP socket? */
    if (FD_ISSET(st, &rd)) {
      /* Accept the connection. */
      s = accept(st, (struct sockaddr *) &a, &sockaddr_in_len);
      /* Set up a thread to handle this connection. */
      pthread_attr_init(&attr);
      /* We never want to collect any values for this -- it's entirely
	 the thread's problem. */
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
      nc = malloc(sizeof(newconn));
      nc->s = s;
      nc->a = malloc(sizeof(struct sockaddr_in));
      memcpy(nc->a, &a, sizeof(struct sockaddr_in));
      pthread_create(&threadid, &attr, handle_new_player, nc);
    }/* end: TCP socket had new data */

    /* or is it our UDP socket? */
    if (FD_ISSET(su, &rd)) {
      if (NSrecvfrom(su, buffer, BUFFER_SIZE, 0,
		     (struct sockaddr *) &a, &sockaddr_in_len) <= 0) {
	/* Ignore it. */
	printf("Ignoring broken message from %s:%d.\n",
	       inet_ntoa(a.sin_addr), ntohs(a.sin_port));
      } else if (!from_map_server(&a)) {
	printf("Ignoring non-map-server message from %s:%d.\n",
	     inet_ntoa(a.sin_addr), ntohs(a.sin_port));
      } else {
	/* Handle a UDP message. */
	/* So what messages can the map server's UDP port accept? */
	/* From a map server... */
	/* MAP HEARTBEAT */
	if (!strcmp(buffer, PROTOCOL_LN "\nMAP HEARTBEAT")) {
	  printf("Heartbeat from server on %s:%d.\n",
		 inet_ntoa(a.sin_addr), ntohs(a.sin_port));
	  /* NOTE: should update something that we watch. */
	} 
	/* PLAYER UP */
	else if ((sscanf(buffer, PROTOCOL_LN "\nPLAYER UP %" Xstr(SHORT_BUFFER) "s", username) == 1)) {
	  pthread_mutex_lock(&pmutex);
	  pcurr = match_player(username);
	  if (pcurr) {
	    printf("Server on %s:%d accepted player %s.\n",
		   inet_ntoa(a.sin_addr), ntohs(a.sin_port), username);
	    snprintf(buffer, BUFFER_SIZE,
		     PROTOCOL_LN "\nPLAYER UP %d %d %f %f %s %d",
		     pcurr->xt, pcurr->yt, pcurr->xi, pcurr->yi, 
		     inet_ntoa(tiles[pcurr->xt][pcurr->yt]->udp.sin_addr), 
		     ntohs(tiles[pcurr->xt][pcurr->yt]->udp.sin_port));
	    if (sendto(su, buffer, strlen(buffer), 0,
		       (struct sockaddr *) &(pcurr->udp),
		       sizeof(pcurr->udp)) < 0) {
	      perror("Problem sending info");
	    }
	  } else {
	    printf("Server on %s:%d accepted bogus player!\n",
		   inet_ntoa(a.sin_addr), ntohs(a.sin_port));
	  }
	  pthread_mutex_unlock(&pmutex);
	}
	/* NEIGHBOURS */
	else if (sscanf(buffer, PROTOCOL_LN "\nNEIGHBOURS %d %d", 
			&xt, &yt) == 2) {
	  if (check_tile(&xt, &yt)) {
	    /* For each of the eight neighbours, send a HANDLER message. */
	    for (xti=-1; xti<=1; xti++) {
	      for (yti=-1; yti<=1; yti++) {
		if (xti!=0 || yti!=0) {
		  xt2 = (xt + xti + N_TILES) % N_TILES;
		  yt2 = (yt + yti + N_TILES) % N_TILES;
		  snprintf(buffer, BUFFER_SIZE,
			   PROTOCOL_LN "\nHANDLER %d %d %s %d",
			   xt2, yt2,
			   inet_ntoa(tiles[xt2][yt2]->udp.sin_addr), 
			   ntohs(tiles[xt2][yt2]->udp.sin_port));
		  if (sendto(su, buffer, strlen(buffer), 0,
			     (struct sockaddr *) &a,
			     sizeof(a)) < 0) {
		    perror("Problem sending handler message");
		  }
		}
	      }
	    }
	  }
	}
	/* REMOVE PLAYER */
	else if (sscanf(buffer, PROTOCOL_LN "\nREMOVE PLAYER %" Xstr(SHORT_BUFFER) "s %d %d", username, &xt, &yt) == 3) {
	  /* Just find player in the list and remove them. */
	  pthread_mutex_lock(&pmutex);
	  pcurr = match_player(username);
	  if (pcurr) {
	    delete_player(pcurr);
	    printf("Deleted player %s.\n", username);
	  } else {
	    printf("Could not delete player %s (no match).\n", username);
	  }	    
	  pthread_mutex_unlock(&pmutex);
	}
	/* PLAYER DIED */
	else if (sscanf(buffer, PROTOCOL_LN "\nPLAYER DIED %" Xstr(SHORT_BUFFER) "s %d %d", username, &xt, &yt) == 3) {
	  pthread_mutex_lock(&pmutex);
	  pcurr = match_player(username);
	  if (pcurr) {
	    spawn_player(pcurr);
	  } else {
	    printf("Could not respawn dead player %s (no match).\n", username);
	  }
	  pthread_mutex_unlock(&pmutex);
	}
	/* SHUTDOWN ACK */
	else if (!strcmp(buffer, PROTOCOL_LN "\nSHUTDOWN ACK")) {
	  printf("Server on %s:%d acknowledges shutdown.\n",
		 inet_ntoa(a.sin_addr), ntohs(a.sin_port));
	  /* NOTE: Should deal with those that don't ack. */
	} 
	/* Only the map servers will send stuff back to the admin server
	   via UDP. */
	/* No match? */
	else {
	  printf("Ignoring unmatched message from %s:%d.\n",
		 inet_ntoa(a.sin_addr), ntohs(a.sin_port));
	  printf("  Message: `%s'\n", buffer);
	}
      } /* end: if recv */
      
    }/* end: UDP socket had new data */

    
  } /* while */
  close(st);
  /* Connect to each map server and say shutdown. */
  printf("Shutting down map servers...\n");
  snprintf(buffer, BUFFER_SIZE,
	   PROTOCOL_LN "\nSHUTDOWN");
  mscurr = mshead;
  while (mscurr) {
    if (sendto(su, buffer, strlen(buffer), 0, 
		      (struct sockaddr *) &(mscurr->udp), 
		      sizeof(mscurr->udp)) < 0) {
      perror("Problem sending shutdown command to map server");
    }
    mscurr = mscurr->next;
  }
  /* Also send it to all the clients, just in case some don't get it. */
  pcurr = phead;
  while (pcurr) {
    if (sendto(su, buffer, strlen(buffer), 0, 
		      (struct sockaddr *) &(pcurr->udp), 
		      sizeof(pcurr->udp)) < 0) {
      perror("Problem sending shutdown command to player client");
    }
    pcurr = pcurr->next;
  }
}

void *handle_new_player(void *param) {
  char    *buffer       = malloc(BUFFER_SIZE);
  char    *name         = malloc(BUFFER_SIZE);
  char    *ok           = malloc(BUFFER_SIZE);
  char    *reply        = malloc(BUFFER_SIZE);
  int      drop         = 0;
  int      dupe_name;
  int      exit_flag    = 0;
  int      udp_port;
  newconn *nc;
  pnode   *pcurr;
  
  nc = (newconn *) param;
  printf("Thread %ld handling connection on socket %d from %s:%d.\n",
	 (long int) pthread_self(), nc->s, 
	 inet_ntoa(nc->a->sin_addr), ntohs(nc->a->sin_port));
  snprintf(ok, BUFFER_SIZE, "OK");
  /* Send a message as given in the book. */
  snprintf(buffer, BUFFER_SIZE, PROTOCOL_LN " ADMIN");
  if (send(nc->s, buffer, strlen(buffer), 0) < 0) {
    perror("Problem with send of initial message");
    exit(EXIT_FAILURE);
  }
  
  /* A client should reply.  We know what they should say. */
  /* Wait for a message.  We know what it should be.... */
  snprintf(reply, BUFFER_SIZE, PROTOCOL_LN " CLIENT");
  if (NSrecv(nc->s, buffer, BUFFER_SIZE, 0) <= 0) {
    perror("Problem with recv, dropping connection");
    drop = 1;
  }
  if (!drop) {
    if (strncmp(reply, buffer, strlen(reply))) {
      printf("Bogus string, dropping connection.\n");
      drop = 1;

    }
  }
  if (!drop) {
    /* Say OK. */
    if (send(nc->s, ok, strlen(ok), 0) < 0) {
      perror("Problem with send");
      drop = 1;
    }
  }
  if (!drop) {
    while (!exit_flag) {
      /* Wait for login message. */
      if (NSrecv(nc->s, buffer, BUFFER_SIZE, 0) < 0) {
	perror("Problem with recv, dropping connection (1)");
	drop = 1;
	exit_flag = 1;
      }
      if (!drop) {
	/* Parse login line. */
	if (sscanf(buffer, "LOGIN %" Xstr(SHORT_BUFFER) "s", name) == 1) {
	  printf("Attempt to login with player name `%s'...\n", name);
	  /* Does it already exist? */
	  /* We must lock this data structure! */
	  pthread_mutex_lock(&pmutex);
	  pcurr = phead;
	  dupe_name = 0;
	  while (pcurr) {
	    if (!strcmp(pcurr->name, name)) {
	      dupe_name = 1;
	    }
	    pcurr = pcurr->next;
	  }
	  if (dupe_name) {
	    /* Reject it. */
	    printf("Rejecting attempt (duplicate name)\n");
	    snprintf(buffer, BUFFER_SIZE, "NOT-OK");
	  } else {
	    /* Accept it. */
	    snprintf(buffer, BUFFER_SIZE, "OK");
	    /* Update the data structure. */
	    pcurr = malloc(sizeof(pnode));
	    pcurr->tcp = *(nc->a);
	    pcurr->name = name;
	    pcurr->next = phead;
	    phead = pcurr;
	    /* We add the UDP port (via pcurr, which should still be
	       valid) later. */
	    /* Fall out of the loop. */
	    exit_flag = 1;
	  }
	  /* Release the data structure. */
	  pthread_mutex_unlock(&pmutex);
	  /* Send a response. */
	  if (send(nc->s, buffer, strlen(buffer), 0) < 0) {
	    perror("Problem with send");
	    drop = 1;
	  }
	} else {
	  printf("Bogus message, dropping connection.\n");
	  drop = 1;
	  exit_flag = 1;
	}
      }
    } /* while */
  } /* if (!drop) */
  if (!drop) {
    /* If we're here, then we've registered pcurr as the new player.
       Get the UDP port number. */
    if (NSrecv(nc->s, buffer, BUFFER_SIZE, 0) <= 0) {
      perror("Problem with recv, dropping connection (2)");
      drop = 1;
    }
  }
  if (!drop) {
    if (sscanf(buffer, "UDP-PORT %d", &udp_port) == 1) {
      printf("Got UDP port (%d) okay.\n", udp_port);
      pcurr->udp = pcurr->tcp;
      pcurr->udp.sin_port = htons(udp_port);
    } else {
      printf("Problem reading UDP port from message `%s'.\n", buffer);
      drop = 1;
      delete_player(pcurr);
    }
  } 
  if (!drop) {
    spawn_player(pcurr);
  }
  close(nc->s);
  /* Done with the new connection structure. */
  free(nc->a);
  free(nc); 
  /* Other stuff to free.  Don't free name! */
  free(buffer);
  free(ok);
  free(reply);
  pthread_exit(NULL);
  return 0; /* Never reach this line. */
}

/* Let's hope that pmutex is already locked by the same thread. */
void spawn_player (pnode *pcurr) {
  pthread_attr_t     attr;
  pthread_t          threadid; 
  newspawn          *ns;

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  pthread_mutex_lock(&pmutex);
  ns = malloc(sizeof(newspawn));
  ns->name = strdup(pcurr->name);
  printf("Deferring spawn of player %s...\n", ns->name);
  pthread_create(&threadid, &attr, spawn_player2, ns);
  pthread_mutex_unlock(&pmutex);
}

void *spawn_player2 (void *param) {
  char     *buffer       = malloc(BUFFER_SIZE);
  pnode    *pcurr;
  newspawn *ns;

  ns = (newspawn *) param;
  /* How long will we wait? */
  usleep(SPAWNDELAY);
  printf("Spawning player %s...\n", ns->name);
  pthread_mutex_lock(&pmutex);
  pcurr = match_player(ns->name);
  /* Generate starting point, get map server.  Send this stuff to the map server */
  pcurr->xt = random()%N_TILES;
  pcurr->yt = random()%N_TILES;
  pcurr->xi = (float) (random()%TILE_LENGTH);
  pcurr->yi = (float) (random()%TILE_LENGTH);
  printf("Allocating start location on tile (%d,%d), position (%f,%f).\n",
	 pcurr->xt, pcurr->yt, pcurr->xi, pcurr->yi);
  /* Tell map server from tiles[xt][yt] that it's got a new player. */
  snprintf(buffer, BUFFER_SIZE,
	   PROTOCOL_LN "\nNEW PLAYER %s %d %d %f %f %s %d",
	   pcurr->name, pcurr->xt, pcurr->yt, pcurr->xi, pcurr->yi, 
	   inet_ntoa(pcurr->udp.sin_addr), ntohs(pcurr->udp.sin_port));
  if (sendto(su, buffer, strlen(buffer), 0, 
	     (struct sockaddr *) &(tiles[pcurr->xt][pcurr->yt]->udp), 
	     sizeof(tiles[pcurr->xt][pcurr->yt]->udp)) < 0) {
    perror("Problem sending new player message to map server");
  }
  /* Need to wait for an ack from the map server.  This is UDP.
     We'll handle it via the normal UDP loop in running. */
  pthread_mutex_unlock(&pmutex);
  free(buffer);
  /* Done with the new spawn structure. */
  free(ns->name);
  free(ns); 
  pthread_exit(NULL);
  return 0; /* Never reach this line. */
}

int from_map_server(struct sockaddr_in *a) {
  msnode * mscurr;

  mscurr = mshead;
  while (mscurr) {
    if ((a->sin_addr.s_addr==mscurr->udp.sin_addr.s_addr)
	&& (ntohs(a->sin_port)==ntohs(mscurr->udp.sin_port))) {
      return 1;
    }
    mscurr = mscurr->next;
  }
  return 0;
}
