/*
 * This file implements all of the goo on the server side for handling 
 * clients.  It's got a bunch of global variables for keeping track of 
 * each of the clients. 
 *
 * Note:  All functions that are used to process data from the client
 * have the prototype of (char *data, int datalen, int client_num).  This
 * way, we can use one dispatch table.
 *
 * esrv_map_new starts updating the map
 *
 * esrv_map_setbelow allows filling in all of the faces for the map.
 * if a face has not already been sent to the client, it is sent now.
 *
 * mapcellchanged, compactlayer, compactstack, perform the map compressing
 * operations
 *
 * esrv_map_doneredraw finishes the map update, and ships across the
 * map updates. 
 *
 * esrv_map_scroll tells the client to scroll the map, and does similarily
 * for the locally cached copy.
 */

#include <global.h>
#include <sproto.h>

#ifdef ERIC_SERVER
#include <newclient.h>
#include <newserver.h>

/* This block is basically taken from socket.c - I assume if it works there,
 * it should work here.
 */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#if defined(hpux) || defined(SVR4)
#include <unistd.h>
#endif
#if defined(_IBMR2) || defined(___IBMR2)
#include <sys/select.h>
#endif
#if defined(__sun__) && !defined(SVR4)
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
#endif


typedef unsigned char uchar;

#define ESRV_VERSION 1003



typedef struct __FaceInfo {
  char *name;	/* name of the image, including component path names (ie,
		 * ./arch/system/bug.111)
		 */
  char *data;	/* xpm data */
  uint16 datalen;   /* length of the xpm data */
  char bitmapdata[3*24];    /* 24x24 bitmaps always are the same size */
} FaceInfo;

/*************************
 * Globals for this file *
 *************************/

NewSocket *cs_sockets;
static int nconns;
static int totalclients = 0;
static int allocatedclients = 0;
static FaceInfo faces[MAXFACENUM];
static struct timeval timeout;

/* Either keep this near the start or end of the file so it is
 * at least reasonablye easy to find.
 */

typedef void (*CmdProc)(uchar*, int, int);
struct CmdMapping {
    char *cmdname;
    CmdProc cmdproc;
};

/* This writes data to the socket.  we precede the len information on the
 * packet.  Len needs to be passed here because buf could be binary
 * data
 */
static void Write_To_Socket(int cnum, unsigned char *buf, int len)
{
    int amt=0;
    unsigned char *pos=buf;

    fprintf(stderr,"Trying to write %d bytes to socket %d\n", len, cnum);
    if (cs_sockets[cnum].status == Ns_Dead || !buf)
	return;
    /* If we manage to write more than we wanted, take it as a bonus */
    while (len>0) {
	amt=write(cs_sockets[cnum].fd, pos, len);
	if (amt < 0) { /* We got an error */
	    if (errno != EWOULDBLOCK) {
		LOG(llevError,"New socket %d write failed (%d: %s).\n", cnum,
		    errno, strerror_local(errno));
		cs_sockets[cnum].status=Ns_Dead;
		return;
	    }
	    else { /* It would block - run another select (0 timeout should
		    * should already be set.)
		    */
		fd_set tmpwrite;
		FD_ZERO(&tmpwrite);
		FD_SET(cs_sockets[cnum].fd,&tmpwrite);
		select(cs_sockets[cnum].fd,NULL,&tmpwrite,NULL,&timeout);
		/* So len & pos processing below are correct */
		amt=0;
	    }
	}
	/* amt gets set to 0 above in blocking code, so we do this as
	 * an else if to make sure we don't reprocess it.
	 */
	else if (amt==0) {
	    LOG(llevError,"Write_To_Socket: No data written out.\n");
	}
	len =- amt;
	pos += amt;
    }
}

/* Send With Handling - cnum is the client number, msg is what we want
 * to send.
 */
void Send_With_Handling(int cnum,SockList  *msg)
{
    unsigned char sbuf[4];

    if (cs_sockets[cnum].status == Ns_Dead || !msg)
	return;

    sbuf[0] = ((uint32)(msg->len) >> 8) & 0xFF;
    sbuf[1] = ((uint32)(msg->len)) & 0xFF;
    Write_To_Socket(cnum, sbuf, 2);
    Write_To_Socket(cnum, msg->buf, msg->len);
}

/* Takes a string of data, and writes it out to the socket. A very handy
 * shortcut function.
 */
void Write_String_To_Socket(int cnum, char *buf, int len)
{
    SockList sl;

    sl.len = len;
    sl.buf = (uchar*)buf;
    Send_With_Handling(cnum, &sl);
}

/* Initializes a connection - really, it just sets up the data structure,
 * socket setup is handled elsewhere.  We do send a version to the
 * client.
 */
static void InitConnection(int cnum, uint32 from)
{
    SockList sl;
    unsigned char buf[256];
    int	bufsize=65536;	/*Supposed absolute upper limit */
    int oldbufsize,buflen=sizeof(int);

    if (fcntl(cs_sockets[cnum].fd, F_SETFL, O_NDELAY)==-1) {
	LOG(llevError,"InitConnection:  Error on fcntl.\n");
    }
    if (getsockopt(cs_sockets[cnum].fd,SOL_SOCKET,SO_SNDBUF, (char*)&oldbufsize, &buflen)==-1)
	oldbufsize=0;
    if (oldbufsize<bufsize) {
	LOG(llevDebug, "Default buffer size was %d bytes, will reset it to %d\n", oldbufsize, bufsize);
	if(setsockopt(cs_sockets[cnum].fd,SOL_SOCKET,SO_SNDBUF, (char*)&bufsize, sizeof(&bufsize))) {
	    LOG(llevError,"InitConnection: setsockopt unable to set output buf size to %d\n", bufsize);
	}
    }
    buflen=sizeof(int);
    getsockopt(cs_sockets[cnum].fd,SOL_SOCKET,SO_SNDBUF, (char*)&oldbufsize, &buflen);
    LOG(llevDebug, "Socket buffer size now %d bytes\n", oldbufsize);

    cs_sockets[cnum].status = Ns_Init;
    cs_sockets[cnum].facemode = Send_Face_Pixmap;

    /* we should really do some checking here - if total clients overflows
     * we need to do something more intelligent, because client id's will start
     * duplicating (not likely in normal cases, but malicous attacks that
     * just open and close connections could get this total up.
     */
    totalclients++;
    cs_sockets[cnum].client_id = totalclients;
    cs_sockets[cnum].inbuf.len=0;
    cs_sockets[cnum].inbuf.buf=malloc(MAXSOCKBUF);
    memset(&cs_sockets[cnum].lastmap,0,sizeof(struct Map));
    memset(&cs_sockets[cnum].faces_sent,0,sizeof(cs_sockets[cnum].faces_sent));
    memset(&cs_sockets[cnum].stats,0,sizeof(struct statsinfo));
    cs_sockets[cnum].status = Ns_Add;
    cs_sockets[cnum].sent_scroll=0;
    sprintf((char*)buf,"%d.%d.%d.%d",
          (from>>24)&255, (from>>16)&255, (from>>8)&255, from&255);
    cs_sockets[cnum].host=strdup_local((char*)buf);
    sprintf((char*)buf, "version %d", ESRV_VERSION);
    sl.buf=buf;
    sl.len=strlen((char*)buf);
    Send_With_Handling(cnum, &sl);
}

/* This handles the general commands from the client (ie, north, fire, cast,
 * etc.)
 */
static void PlayerCmd(char *buf, int len, int cnum)
{
    object *pl=esrv_getopfromcid(cs_sockets[cnum].client_id);

    /* The following should never happen with a proper or honest client.
     * Therefore, the error message doesn't have to be too clear - if 
     * someone is playing with a hacked/non working client, this gives them
     * an idea of the problem, but they deserve what they get
     */
    if (pl->contr->state!=ST_PLAYING) {
	new_draw_info_format(NDI_UNIQUE, 0,pl,
	    "You can not issue commands - state is not ST_PLAYING (%s)", buf);
	return;
    }
    /* Check if there is a count.  In theory, a zero count could also be
     * sent, so check for that also.
     */
    if (atoi(buf) || buf[0]=='0') {
	pl->contr->count=atoi((char*)buf);
	pl->contr->count_left=0;
	buf=strchr(buf,' ');	/* advance beyond the numbers */
	if (!buf) {
	    LOG(llevDebug,"PlayerCmd: Got count but no command.");
	    return;
	}
	buf++;
    }
    pl->contr->idle=0;
    /* This should not happen anymore.    */
    if (pl->speed_left<-1.0) {
	LOG(llevError,"Player has negative time - shouldn't do command.\n");
    }
    /* In c_new.c */
    execute_newserver_command(pl, (char*)buf);
    /* Perhaps something better should be done with a left over count.
     * Cleaning up the input should probably be done first - all actions
     * for the command that issued the count should be done before any other
     * commands.
     */

    pl->contr->count_left=0;
    pl->contr->count=0;

}


/* This is a reply to a previous query. */
static void ReplyCmd(uchar *buf, int len, int cnum)
{
    object *pl=esrv_getopfromcid(cs_sockets[cnum].client_id);

    LOG(llevDebug,"In ReplyCmd: got reply '%s'\n", buf);

    /* This is to synthesize how the data would be stored if it
     * was normally entered.  A bit of a hack, and should be cleaned up
     * once all the X11 code is removed from the server.
     *
     * We pass 13 to many of the functions because this way they
     * think it was the carriage return that was entered, and the
     * function then does not try to do additional input.
     */
    sprintf(pl->contr->write_buf,":%s", (char*)buf);
    pl->contr->writing = strlen(pl->contr->write_buf);

    switch (pl->contr->state) {
	case ST_PLAYING:
	    LOG(llevError,"Got reply message with ST_PLAYING input state\n");
	    break;

	case ST_PLAY_AGAIN:
	    /* We can check this for return value (2==quit).  Maybe we
	     * should, and do something appropriate?
	     */
	    receive_play_again(pl, buf[0]);
	    break;

	case ST_ROLL_STAT:
	    key_roll_stat(pl,buf[0]);
	    break;

	case ST_CHANGE_CLASS:
	    key_change_class(pl, buf[0]);
	    break;

	case ST_CONFIRM_QUIT:
	    key_confirm_quit(pl, buf[0]);
	    break;

	case ST_CONFIGURE:
	    LOG(llevError,"In client input handling, but into configure state\n");
	    pl->contr->state = ST_PLAYING;
	    break;

	case ST_GET_NAME:
	    receive_player_name(pl,13);
	    break;

	case ST_GET_PASSWORD:
	case ST_CONFIRM_PASSWORD:
	    receive_player_password(pl,13);
	    break;

	/* Pausing should really be done by the client, not the server.
	 * but this is easy to handle here.
	 */
	case ST_MENU_MORE:
	    shop_listing_more(pl);
	    break;

#ifdef SIMPLE_PARTY_SYSTEM
	case ST_GET_PARTY_PASSWORD:        /* Get password for party */
	receive_party_password(pl,13);
	    break;
#endif /* SIMPLE_PARTY_SYSTEM */

	default:
	    LOG(llevError,"Unknown input state: %d\n", pl->contr->state);
    }
}

/* Given a client id, we then get the client number. */
int getcnum(uint32 client_id) 
{
  int i;

  for(i=1;i<nconns;i++)
    if (cs_sockets[i].client_id == client_id)
      return i;
  
  return -1;
}

/* Client tells its its version.  If there is a mismatch, we close the
 * socket.  In real life, all we should care about is the client having
 * something older than the server.  If we assume the client will be
 * backwards compatible, having it be a later version should not be a 
 * problem.
 */
static void VersionCmd(char *buf, int len,int cnum)
{
  if (ESRV_VERSION != atoi(buf)) {
    LOG(llevDebug, "Server, Client have different versions (%d,%d)\n",
	   ESRV_VERSION,atoi(buf));
    cs_sockets[cnum].status = Ns_Dead;
  }
}

/* The client has requested to be added to the game.  This is what
 * takes care of it.  We tell the client how things worked out.
 */
static void AddMeCmd(char *buf, int len,int cnum)
{
    Settings oldsettings=settings;

    settings.displaymode=Dm_Pixmap;
    if (cs_sockets[cnum].status != Ns_Add || add_player(cs_sockets[cnum].host,
		 "someone",NULL,cs_sockets[cnum].client_id)) {
	Write_String_To_Socket(cnum, "addme_failed",12);
    } else {
	Write_String_To_Socket(cnum, "addme_success",13);
    }
    settings=oldsettings;
}


/* Client wants to examine some object.  So lets do so. */
static void ExamineCmd(char *buf, int len,int cnum)
{
  long tag = atoi(buf);

  esrv_examine_object(esrv_getopfromcid(cs_sockets[cnum].client_id), tag);
}

/* Client wants to apply some object.  Lets do so. */
static void ApplyCmd(char *buf, int len,int cnum)
{
  long tag = atoi(buf);

  esrv_apply_object(esrv_getopfromcid(cs_sockets[cnum].client_id), tag);
}

static void SetFaceMode(char *buf, int len, int cnum)
{
    int mode = atoi(buf);

    if (mode==CF_FACE_NONE) {
	cs_sockets[cnum].facemode=Send_Face_None;
    }
    else if (mode==CF_FACE_BITMAP) {
	cs_sockets[cnum].facemode=Send_Face_Bitmap;
    } else if (mode==CF_FACE_XPM) {
	cs_sockets[cnum].facemode=Send_Face_Pixmap;
    } else {
	LOG(llevDebug,"SetFaceMode: Invalid mode from client: %d\n", mode);
    }
}

/* client has requested pixmap that it somehow missed getting */
void MapRedrawCmd(uchar *buff, int len, int clientnum)
{
	object *playerob;
	buff=NULL;
	len=0; /* stupid thing to get rid of gcc complaints*/

	playerob=esrv_getopfromcid(cs_sockets[clientnum].client_id);

	/* Okay, this is MAJOR UGLY. but the only way I know how to
	 * clear the "cache"
	 */
	memset(&cs_sockets[clientnum].lastmap, 0, sizeof(struct Map));

	draw_client_map(playerob);
}

/* client has requested pixmap that it somehow missed getting */
void SendFaceCmd(uchar *buff, int len, int clientnum)
{
	long tmpnum = atoi(buff);
	short facenum=tmpnum & 0xffff;

	/* esrv_send_face should really be changed to take clientnum
	 * directly.
	 */
	if(facenum!=0)
		esrv_send_face(clientnum, facenum);
}

/* Moves and object (typically, container to inventory
 * move <to> <tag> <nrof> 
 */
static void MoveCmd(char *buf, int len,int cnum)
{
    int vals[3], i;

    /* A little funky here.  We only cycle for 2 records, because
     * we obviously am not going to find a space after the third
     * record.  Perhaps we should just replace this with a
     * sscanf?
     */
    for (i=0; i<2; i++) {
	vals[i]=atoi(buf);
	if (!(buf = strchr(buf, ' '))) {
	    LOG(llevError,"Incomplete move command: %s\n", buf);
	    return;
	}
	buf++;
    }
    vals[2]=atoi(buf);

    printf ("Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);
    esrv_move_object(esrv_getopfromcid(cs_sockets[cnum].client_id), 
	vals[0], vals[1], vals[2]);
}


/* A throway function.  it reads a line, makes sure it is null terminated,
 * and strips off the newline.
 */
static void readbufline(char *buf,int size,FILE *in)
{
  buf[0] = '\0';
  fgets(buf,size,in);
  buf[size-1] = '\0';
  if (buf[0] == '\0')
    return;
  if (buf[strlen(buf)-1] != '\n') {
    fprintf(stderr,"whoa, line '%s' not newline terminated??\n",buf);
    abort();
  }
}

/* basically, all we need to do here is free all data structures that
 * might be associated with the socket.
 */

static void dropconnection(int which)
{
    int j;

    LOG(llevDebug,"Closing connection %d\n", which);
    if (close(cs_sockets[which].fd)) {
	LOG(llevDebug,"Error closing socket %d\n", which);
    }
    if (cs_sockets[which].stats.range)
	free(cs_sockets[which].stats.range);
    if (cs_sockets[which].stats.title)
	free(cs_sockets[which].stats.title);
    free(cs_sockets[which].host);
    free(cs_sockets[which].inbuf.buf);
    nconns--;
    for(j=which;j<nconns;j++) {
	cs_sockets[j]=cs_sockets[j+1];
    }
}

/* read_client_images loads the xpm and pixmas file into memory.  This 
 * way, we can easily send them to the client.  We should really do something
 * better than abort on any errors - on the other hand, these are all fatal
 * to the server (can't work around them), but the abort just seems a bit
 * messy (exit would probably be better.)
 */

/* Eric had set up things to use a method of malloc/realloc to always make
 * sure he had a large enough buffer - this made the code slightly more
 * complicated and harder to read.  It makes more sense to me to just
 * set up a large buffer that should be able to handle any image.  being
 * that most xpm images are less than 1K, a 10,000 byte buffer should easily
 * cover us.
 */

/* Couple of notes:  We assume that the faces are in a continous block.
 * This works fine for now, but this could perhaps change in the future
 * (If clients do image caching, it would be handy for servers to perhaps
 * use different blocks for their new images..
 */
#define XPM_BUF 10000
void read_client_images()
{
    char filename[400];
    char buf[500];
    char databuf[XPM_BUF],*cur;
    FILE *infile;
    int num,len,compressed;

    /* Read in the pixmaps file */
    sprintf(filename,"%s/esrv_xpm.eric",LIBDIR);
    if ((infile = open_and_uncompress(filename,0,&compressed))==NULL) {
	LOG(llevError,"Unable to open %s\n", filename);
	abort();
    }
    while(1) {
	readbufline(buf,500,infile);
	if (*buf == '\0')
	    break;
	if(strncmp(buf,"ESRV_XPM ",9)!=0 ||
	   buf[14] != ' ') {
	    fprintf(stderr,"whoa, bad esrv_xpm line; not ESRV_XPM ...\n%s",buf);
	    abort();
	}
	num = atoi(buf+9);
	if (num<0 || num>=MAXFACENUM) {
	    LOG(llevError,"whoa, pixmap num %d \\not\\in 0..%d\n%s",
		    num,MAXFACENUM,buf);
	    abort();
	}
	buf[strlen(buf)-1] = '\0';
	if (faces[num].name != NULL) {
	    fprintf(stderr,"whoa, pixmap #%d duplicated??\n",num);
	    abort();
	}
	faces[num].name = malloc(strlen(buf+15)+1);
	strcpy(faces[num].name,buf+15);

	cur = databuf;
	/* Collect all the data for this pixmap */
	while(1) {
	    readbufline(buf,500,infile);
	    if (*buf == '\0') {
		fprintf(stderr,"whoa, pixmap #%d not terminated??\n",num);
		abort();
	    }
	    if (strcmp(buf,"ESRV_XPM_END\n")==0)
		break;
	    len = strlen(buf);
	    if (cur+len > databuf+XPM_BUF) {
		LOG(llevError,"Overflow of XPM_BUF in read_client_images, image %s\n",
		    faces[num].name);
		abort();
	    }
	    strcpy(cur,buf);
	    cur += len;
	}
	/* Collected all the data, put it into the pixmap buffer */
	faces[num].data = malloc(cur-databuf+1);
	faces[num].datalen = cur-databuf+1;
	memcpy(faces[num].data, databuf,cur-databuf);
	faces[num].data[cur-databuf] = '\0';
    }
    close_and_delete(infile,compressed);

    /* Assume bitmap information has same number as pixmap information */
    sprintf(filename,"%s/%s.cfb",LIBDIR,FONTNAME);
    if ((infile = open_and_uncompress(filename,0,&compressed))==NULL) {
	LOG(llevError,"Can't open %s file",filename);
	abort();
    }
    for(num=0;num<MAXFACENUM;num++) {
	if (faces[num].name == NULL)
	    break; /* Last one -- assumes pixmaps are contiguous. */
	if (fread(faces[num].bitmapdata, 24 * 3, 1, infile) != 1) {
	    printf("Unable to read bitmap data for face #%d\n",num);
	    abort();
	}
    }
    while(num<MAXFACENUM) {
	if (faces[num].name != NULL) {
	    printf("Non-contiguous faces, %d sits in middle of nowhere.\n",num);
	    abort();
	}
	num++;
    }
    close_and_delete(infile, compressed);
}
/* This sets up the socket and reads all the image information into memory. */
void init_ericserver()
{
    struct sockaddr_in	insock;
    struct protoent  *protox;
    struct linger linger_opt;

    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    LOG(llevDebug,"Initialize new client/server data\n");
    nconns = 1;
    cs_sockets = malloc(sizeof(NewSocket));

    protox = getprotobyname("tcp");
    if (protox==NULL) {
	LOG(llevError,"init_ericserver: Error getting protox");
	return;
    }
    cs_sockets[0].fd = socket(PF_INET, SOCK_STREAM, protox->p_proto);
    if (cs_sockets[0].fd == -1) {
	perror("Error create new client server socket.");
	LOG(llevError, "Error creating socket on port\n");
	exit(-1);
    }
    insock.sin_family = AF_INET;
    insock.sin_port = htons(settings.csport);
    insock.sin_addr.s_addr = htonl(INADDR_ANY);

    linger_opt.l_onoff = 0;
    linger_opt.l_linger = 0;
    if(setsockopt(cs_sockets[0].fd,SOL_SOCKET,SO_LINGER,(char *) &linger_opt,
       sizeof(struct linger))) {
	perror("error on setsockopt LINGER");
	LOG(llevError, "Error on setsockopt LINGER\n");
    }
#if defined(__osf__) || defined(hpux) || defined(sgi) || defined(NeXT) || \
        defined(__sun__) || defined(linux) || defined(SVR4)
    {
	char tmp =1;

	if(setsockopt(cs_sockets[0].fd,SOL_SOCKET,SO_REUSEADDR, &tmp, sizeof(&tmp))) {
	    perror("error on setsockopt REUSEADDR");
	    LOG(llevError,"error on setsockopt REUSEADDR\n");
	}
    }
#else
    if(setsockopt(cs_sockets[0].fd,SOL_SOCKET,SO_REUSEADDR,(char *)NULL,0)) {
	perror("error on setsockopt REUSEADDR");
	LOG(llevError,"error on setsockopt REUSEADDR\n");
    }
#endif

    if (bind(cs_sockets[0].fd,(struct sockaddr *)&insock,sizeof(insock)) == (-1)) {
	perror("error on bind command");
	LOG(llevError,"error on bind command\n");
	close(cs_sockets[0].fd);
	exit(-1);
    }
    if (listen(cs_sockets[0].fd,5) == (-1))  {
	perror("error on listen");
	LOG(llevError,"error on listen\n");
	close(cs_sockets[0].fd);
	exit(-1);
    }
    read_client_images();
}


/*
 * CmdMapping is the dispatch table for the server, used in HandleClient,
 * which gets called when the client has input.  All commands called here 
 * use the same parameter form (char* data, int len, int clientnum.
 * We do implicit casts, because the data that is being passed is
 * unsigned (pretty much needs to be for binary data), however, most
 * of these treat it only as strings, so it makes things easier
 * to cast it here instead of a bunch of times in the function itself.
 */
static struct CmdMapping commands[] = {
   { "version", (CmdProc)VersionCmd },
   { "addme", (CmdProc)AddMeCmd },
   { "examine", (CmdProc)ExamineCmd },
   { "apply", (CmdProc)ApplyCmd },
   { "move", (CmdProc)MoveCmd },
   { "reply", ReplyCmd},
   { "command", (CmdProc)PlayerCmd},
   { "setfacemode", (CmdProc)SetFaceMode},
   { "askface", SendFaceCmd},	/* Added: phil */
   { "mapredraw", MapRedrawCmd}	/* Added: phil */
};


#define NCOMMANDS (sizeof(commands)/sizeof(struct CmdMapping))

static void HandleClient(int cnum)
{
    NewSocket *sock=&cs_sockets[cnum];
    int len,i,firstrun=1;
    object *pl=esrv_getopfromcid(cs_sockets[cnum].client_id);
    unsigned char *data;

/*    LOG(llevDebug,"HandleClient called for connection %d\n", cnum);*/
    /* Loop through this - maybe we have several complete packets here. */
    while (1) {
	if (pl && pl->contr->state==ST_PLAYING && pl->speed_left < 0) {
	    /* Commented out, as this will happen almost all the
	     * time unless the player is really fast (player does
	     * action, uses up his time, and we end up here as we
	     * we see if he has more actions.)
	     */
/*	    LOG(llevDebug,"Player has no speed (%f), not reading from the socket.\n",pl->speed_left);*/
	    return;
	}
	i=SockList_ReadPacket(sock->fd, &sock->inbuf, MAXSOCKBUF-1);
	if (i<0) {
	    LOG(llevDebug,"HandleClient: Read error on connection %d\n", cnum);
	    cs_sockets[cnum].status = Ns_Dead;
	    return;
	}
	/* Still dont have a full packet */
	if (i==0) return;
	firstrun=0;
	/* First, break out beginning word.  There are at least
	 * a few commands that do not have any paremeters.  If
	 * we get such a command, don't worry about trying
	 * to break it up.
	 */
	data = (unsigned char *)strchr((char*)sock->inbuf.buf +2, ' ');
	if (data) {
	    *data='\0';
	    data++;
	    len = sock->inbuf.len - (data - sock->inbuf.buf);
	}
	sock->inbuf.buf[sock->inbuf.len]='\0';  /* Terminate buffer - useful for string data */
	LOG(llevDebug,"Handle_Client: %d Got command %s, data len=%d, slen=%d\n", 
	    cnum, sock->inbuf.buf+2, len, sock->inbuf.len);
	for(i=0;i < NCOMMANDS;i++) {
	    if (strcmp((char*)sock->inbuf.buf+2,commands[i].cmdname)==0) {
		commands[i].cmdproc(data,len,cnum);
		break;
	    }
	}
	if (i==NCOMMANDS) 
	    LOG(llevError,"Bad command from client (%s)\n",sock->inbuf.buf+2);

	/* So on the next reads, in knows we have no data */
	sock->inbuf.len=0;
    }
}

extern int max_filedescriptors;

/* This checks the sockets for input and exceptions, does the right thing.  A bit
 * of this code is grabbed out of socket.c
 */
void doeric_server()
{
    int i, pollret;
    long cid;
    fd_set tmp_read, tmp_exceptions;
    struct sockaddr_in addr;
    int addrlen=sizeof(struct sockaddr);

/* Start at connection 1, being that 0 is the what listens on the port. */
    for(i=1;i<nconns;i++) {
	if (cs_sockets[i].status == Ns_Dead) {
	    cid = cs_sockets[i].client_id;
	    dropconnection(i);
	    esrv_quit_player(cid);
	    i--;
	}
    }
    FD_ZERO(&tmp_read);
    FD_ZERO(&tmp_exceptions);
    for (i=0; i<nconns; i++) {
	FD_SET(cs_sockets[i].fd, &tmp_read);
	FD_SET(cs_sockets[i].fd, &tmp_exceptions);
    }
    pollret= select(max_filedescriptors, &tmp_read, NULL, &tmp_exceptions, &timeout);
    if (pollret==-1) {
	perror("doeric_serover: error on select");
	LOG(llevError,"doeric_server: error on select\n");
	return;
    }
    if (!pollret) return;
/*    LOG(llevDebug,"select found %d\n", pollret);*/
    if (FD_ISSET(cs_sockets[0].fd, &tmp_read)) {
	LOG(llevDebug,"doeric_server: New Connection\n");
	if (allocatedclients <= nconns) {
	    cs_sockets = realloc(cs_sockets,sizeof(NewSocket)*(nconns+1));
	    allocatedclients++;
	}
	cs_sockets[nconns].fd=accept(cs_sockets[0].fd, (struct sockaddr *)&addr, &addrlen);
	if (cs_sockets[nconns].fd==-1) {
	    perror("doeric_server: error on accept");
	    LOG(llevError,"doeric_server: error on accept\n");
	}
	else {
	    nconns++;
	    InitConnection(nconns-1,ntohl(addr.sin_addr.s_addr));
	}
    }
    for(i=1;i<nconns;i++) {
	if (FD_ISSET(cs_sockets[i].fd,&tmp_exceptions)) {
	    LOG(llevDebug,"Connection %d detected an exception.", i);
	    cid = cs_sockets[i].client_id;
	    dropconnection(i);
	    esrv_quit_player(cid);
	    i--;
	    continue;
	}
	if (FD_ISSET(cs_sockets[i].fd, &tmp_read)) {
	    HandleClient(i);
	}
    }
}

/* If run in server mode, the game blocks until new connection.  That is handled
 * in socket.c.  However, it needs to know what the fd over here is, so it can
 * get connections on this side.  Thus just returns it.  When the old server/X11
 * code goes away, it is likely this can also go away.
 */
int ericfd()
{
    return cs_sockets[0].fd;
}

/*
 * esrv_remove_player removes a player (called from xfire sources).  It basically just
 * sets the socket to dead, so that other code closes it out.
 */
void esrv_remove_player(long client_id)
{
    int i;
  
    for(i=0;i<nconns;i++) {
	if (cs_sockets[i].client_id == client_id) {
	    cs_sockets[i].status = Ns_Dead;
	    break;
	}
    }
}
/*
 * send_query asks the client to query the user.  This way, the client knows
 * it needs to send something back (vs just printing out a message
 */
void send_query(long client_id, uint8 flags, char *text)
{
    int cnum;
    char buf[MAX_BUF];

    if ((cnum = getcnum(client_id))<0) {
	LOG(llevDebug,"client %ld is gone.\n",client_id);
	return;
    }

    sprintf(buf,"query %d %s", flags, text?text:"");
    Write_String_To_Socket(cnum, buf, strlen(buf));
}

/*
 * esrv_print_msg draws a normal message on the client.  It is pretty 
 * much the same thing as the draw_info above, but takes a color
 * parameter.  the esrv_drawinfo functions should probably be
 * replaced with this, just using black as the color.
 */
void esrv_print_msg(uint32 client_id,int color, const char *str)
{
    int cnum;
    char buf[HUGE_BUF];

    if ((cnum = getcnum(client_id))<0) {
	LOG(llevDebug,"client %ld is gone.\n",client_id);
	return;
    }
    sprintf(buf,"drawinfo %d %s", color, str);
/*    LOG(llevDebug,"sending %s to socket, len=%d", buf, strlen(buf));*/
    Write_String_To_Socket(cnum, buf, strlen(buf));
}


/* Sends the stats to the client - only sends them if they have changed */

#define AddIfInt(Old,New,Type) if (Old != New) {\
			Old = New; \
			SockList_AddChar(&sl, Type); \
			SockList_AddInt(&sl, New); \
		       }

#define AddIfShort(Old,New,Type) if (Old != New) {\
			Old = New; \
			SockList_AddChar(&sl, Type); \
			SockList_AddShort(&sl, New); \
		       }

#define AddIfFloat(Old,New,Type) if (Old != New) {\
			Old = New; \
			SockList_AddChar(&sl, Type); \
			SockList_AddInt(&sl,(long)(New*FLOAT_MULTI));\
			}

#define AddIfString(Old,New,Type) if (Old == NULL || strcmp(Old,New)) {\
			if (Old) free(Old);\
	                Old = strdup_local(New);\
			SockList_AddChar(&sl, Type); \
			SockList_AddChar(&sl, strlen(New)); \
			strcpy((char*)sl.buf + sl.len, New); \
			sl.len += strlen(New); \
			}

/*
 * esrv_update_stats sends a statistics update.  We look at the old values,
 * and only send what has changed.  Stat mapping values are in newclient.h
 * Since this gets sent a lot, this is actually one of the few binary
 * commands for now.
 */
void esrv_update_stats(long client_id, object *pl)
{
    int cnum;
    SockList sl;
    char buf[MAX_BUF];

    if ((cnum = getcnum(client_id))<0) {
	LOG(llevDebug,"update_stats to not there person?\n");
	return;
    }
    sl.buf=malloc(MAX_BUF);
    strcpy((char*)sl.buf,"stats ");
    sl.len=strlen((char*)sl.buf);

    AddIfShort(pl->contr->last_stats.hp, pl->stats.hp, CS_STAT_HP);
    AddIfShort(pl->contr->last_stats.maxhp, pl->stats.maxhp, CS_STAT_MAXHP);
    AddIfShort(pl->contr->last_stats.sp, pl->stats.sp, CS_STAT_SP);
    AddIfShort(pl->contr->last_stats.maxsp, pl->stats.maxsp, CS_STAT_MAXSP);
    AddIfShort(pl->contr->last_stats.grace, pl->stats.grace, CS_STAT_GRACE);
    AddIfShort(pl->contr->last_stats.maxgrace, pl->stats.maxgrace, CS_STAT_MAXGRACE);
    AddIfShort(pl->contr->last_stats.Str, pl->stats.Str, CS_STAT_STR);
    AddIfShort(pl->contr->last_stats.Int, pl->stats.Int, CS_STAT_INT);
/* added this to allow Pow stat - b.t. */
    AddIfShort(pl->contr->last_stats.Pow, pl->stats.Pow, CS_STAT_POW);
    AddIfShort(pl->contr->last_stats.Wis, pl->stats.Wis, CS_STAT_WIS);
    AddIfShort(pl->contr->last_stats.Dex, pl->stats.Dex, CS_STAT_DEX);
    AddIfShort(pl->contr->last_stats.Con, pl->stats.Con, CS_STAT_CON);
    AddIfShort(pl->contr->last_stats.Cha, pl->stats.Cha, CS_STAT_CHA);
    AddIfInt(pl->contr->last_stats.exp, pl->stats.exp, CS_STAT_EXP);
    AddIfShort(pl->contr->last_level, pl->level, CS_STAT_LEVEL);
    AddIfShort(pl->contr->last_stats.wc, pl->stats.wc, CS_STAT_WC);
    AddIfShort(pl->contr->last_stats.ac, pl->stats.ac, CS_STAT_AC);
    AddIfShort(pl->contr->last_stats.dam, pl->stats.dam, CS_STAT_DAM);
    AddIfShort(pl->contr->last_armour, pl->armour, CS_STAT_ARMOUR);
    AddIfFloat(pl->contr->last_speed, pl->speed, CS_STAT_SPEED);
    AddIfShort(pl->contr->last_stats.food, pl->stats.food, CS_STAT_FOOD);
    AddIfFloat(pl->contr->last_weapon_sp, pl->contr->weapon_sp, CS_STAT_WEAP_SP);

    rangetostring(pl, buf);
    AddIfString(cs_sockets[cnum].stats.range, buf, CS_STAT_RANGE);
    set_title(pl, buf);
    AddIfString(cs_sockets[cnum].stats.title, buf, CS_STAT_TITLE);

    /* Only send it away if we have some actual data */
    if (sl.len>6) {
/*	LOG(llevDebug,"Sending stats command, %d bytes long.\n", sl.len);*/
	Send_With_Handling(cnum, &sl);
    }
    free(sl.buf);
}


/* Tells the client that here is a player it should start using. */

void esrv_new_player(long client_id, long tag, char *name, long weight, 
	long face)
{
    SockList	sl;
    int	client_num;

    if ((client_num=getcnum(client_id))<0) return;
    sl.buf=malloc(MAXSOCKBUF);

    strcpy((char*)sl.buf,"player ");
    sl.len=strlen((char*)sl.buf);
    SockList_AddInt(&sl, tag);
    SockList_AddInt(&sl, weight);
    SockList_AddInt(&sl, face);
    SockList_AddChar(&sl, strlen(name));
    strcpy((char*)sl.buf+sl.len, name);
    sl.len += strlen(name);
    Send_With_Handling(client_num, &sl);
    free(sl.buf);
}


/* Tells the client to delete an item.  Uses the item
 * command with a -1 location.
 */

void esrv_del_item(int client_id, int tag)
{
    int client_num;
    SockList sl;

    if ((client_num=getcnum(client_id))<0) return;
    sl.buf=malloc(MAXSOCKBUF);

    strcpy((char*)sl.buf,"delitem ");
    sl.len=strlen((char*)sl.buf);
    SockList_AddInt(&sl, tag);

    Send_With_Handling(client_num, &sl);
    free(sl.buf);
}

/*
 * esrv_send_face sends a face to a client if they are in pixmap mode
 * nothing gets sent in bitmap mode. 
 */

void esrv_send_face(uint32 client_num,short face_num)
{
    SockList sl;

    if (client_num<0) {
	LOG(llevDebug,"esrv_send_face:send_face to non existent socket.\n");
	return;
    }

    /* if face mode none, we just send the facename instead */
/*    if (cs_sockets[client_num].facemode == Send_Face_None) return;*/

    if (face_num < 0 || face_num >= MAXFACENUM) {
	LOG(llevError,"esrv_send_face (%d) out of bounds??\n",face_num);
	return;
    }
    if (faces[face_num].data == NULL) {
	LOG(llevError,"esrv_send_face: faces[%d].data == NULL\n",face_num);
	return;
    }
    sl.buf = malloc(MAXSOCKBUF);

    if (cs_sockets[client_num].facemode == Send_Face_Pixmap) {
	strcpy((char*)sl.buf, "pixmap ");
	sl.len=strlen((char*)sl.buf);
	SockList_AddInt(&sl, face_num);
	SockList_AddInt(&sl, faces[face_num].datalen);
	memcpy(sl.buf+sl.len, faces[face_num].data, faces[face_num].datalen);
	sl.len += faces[face_num].datalen;
/*	LOG(llevDebug,"sending pixmap %d, len %d\n", face_num, faces[face_num].datalen);*/
	Send_With_Handling(client_num, &sl);
    } else if (cs_sockets[client_num].facemode == Send_Face_Bitmap) {
	strcpy((char*)sl.buf, "bitmap ");
	sl.len=strlen((char*)sl.buf);
	SockList_AddInt(&sl, face_num);
	SockList_AddChar(&sl, new_faces[face_num].fg);
	SockList_AddChar(&sl, new_faces[face_num].bg);
	memcpy(sl.buf+sl.len, faces[face_num].bitmapdata, 3*24);
	sl.len += 3*24;
	Send_With_Handling(client_num, &sl);
    } else if (cs_sockets[client_num].facemode == Send_Face_None) {
	strcpy((char*)sl.buf, "face ");
	sl.len=strlen((char*)sl.buf);
	SockList_AddShort(&sl, face_num);
	strcpy(sl.buf+sl.len, faces[face_num].name);
	sl.len +=strlen(faces[face_num].name);
	Send_With_Handling(client_num, &sl);
    } else {
	LOG(llevError,"Invalid face send mode on client_num #%d\n",client_num);
    }
    cs_sockets[client_num].faces_sent[face_num] = 1;
    free(sl.buf);
}


/* This adds face_num to a map cell at x,y.  If the client doesn't have
 * the face yet, we will also send it.
 */
static void esrv_map_setbelow(uint32 client_id, uint32 cnum, int x,int y,
			      short face_num, struct Map *newmap)
{
  if (x<0||x>10 ||y<0 ||y>10 || face_num < 0 || face_num > MAXFACENUM) {
    LOG(llevError,"bad user x/y/facenum not in 0..10,0..10,0..%d\n",
	    MAXFACENUM-1);
    abort();
  }
  if(newmap->cells[x][y].count >= MAXMAPCELLFACES) {
    LOG(llevError,"Too many faces in map cell %d %d\n",x,y);
    abort();
  }
  newmap->cells[x][y].faces[newmap->cells[x][y].count] = face_num;
  newmap->cells[x][y].count ++;
  if (cnum>0 && cs_sockets[cnum].faces_sent[face_num] == 0)
    esrv_send_face(cnum,face_num);
}

struct LayerCell {
  char xy;
  short face;
};

struct MapLayer {
  int count;
  struct LayerCell lcells[121];
};

static int mapcellchanged(int cnum,int i,int j, struct Map *newmap)
{
  int k;

  if (cs_sockets[cnum].lastmap.cells[i][j].count != newmap->cells[i][j].count)
    return 1;
  for(k=0;k<newmap->cells[i][j].count;k++) {
    if (cs_sockets[cnum].lastmap.cells[i][j].faces[k] !=
	newmap->cells[i][j].faces[k]) {
      return 1;
    }
  }
  return 0;
}


/* cnum is the client number, cur is the the buffer we put all of
 * this data into.  we return the end of the data.  layers is
 * how many layers of data we should back.
 * Basically, what this does is pack the data into layers.
 */  
static uchar *compactlayer(int cnum, unsigned char *cur, int numlayers, 
			   struct Map *newmap)
{
    int i,j,k;
    int face;
    unsigned char *fcur;
    struct MapLayer layers[MAXMAPCELLFACES];
  
    for(k = 0;k<MAXMAPCELLFACES;k++)
	layers[k].count = 0;
    fcur = cur;
    for(i=0;i<11;i++) {
	for(j=0;j<11;j++) {
	    if (!mapcellchanged(cnum,i,j,newmap))
		continue;
	    if (newmap->cells[i][j].count == 0) {
		*cur = i*11+j;	    /* mark empty space */
		cur++;
		continue;
	    }
	    for(k=0;k<newmap->cells[i][j].count;k++) {
		layers[k].lcells[layers[k].count].xy = i*11+j;
		layers[k].lcells[layers[k].count].face = 
		    newmap->cells[i][j].faces[k];
		layers[k].count++;
	    }
	}
    }
    /* If no data, return now. */
    if (fcur == cur && layers[0].count == 0)
	return cur;
    *cur = 255; /* mark end of explicitly cleared cells */
    cur++;
    /* First pack by layers. */
    for(k=0;k<numlayers;k++) {
	if (layers[k].count == 0)
	    break; /* once a layer is entirely empty, no layer below it can
		have anything in it either */
	/* Pack by entries in thie layer */
	for(i=0;i<layers[k].count;) {
	    fcur = cur;
	    *cur = layers[k].lcells[i].face >> 8;
	    cur++;
	    *cur = layers[k].lcells[i].face & 0xFF;
	    cur++;
	    face = layers[k].lcells[i].face;
	    /* Now, we back the redundant data into 1 byte xy pairings */
	    for(j=i;j<layers[k].count;j++) {
		if (layers[k].lcells[j].face == face) {
		    *cur = layers[k].lcells[j].xy;
		    cur++;
		    layers[k].lcells[j].face = -1;
		}
	    }
	    *(cur-1) = *(cur-1) | 128; /* mark for end of xy's; 11*11 < 128 */
	    /* forward over the now redundant data */
	    while(i < layers[k].count &&
		  layers[k].lcells[i].face == -1)
		i++;
	}
	*fcur = *fcur | 128; /* mark for end of faces at this layer */
    }
    return cur;
}

static void esrv_map_doneredraw(long client_id, uint32 cnum, 
				struct Map *newmap)
{
    static long frames,bytes,tbytes,tframes;
    uchar *cur;
    SockList sl;


    sl.buf=malloc(MAXSOCKBUF);
    strcpy((char*)sl.buf,"map ");
    sl.len=strlen((char*)sl.buf);

    cur = compactlayer(cnum,sl.buf+sl.len,MAXMAPCELLFACES,newmap);
    sl.len=cur-sl.buf;
    if (sl.len>strlen("map ") || cs_sockets[cnum].sent_scroll) {
	/* All of this is just accounting stuff */
	if (tframes>100) {
	    tframes = tbytes = 0;
	}
	tframes++;
	frames++;
	tbytes += sl.len;
	bytes += sl.len;
#if 0      
    printf("ts%d,a%d;lf%ld,la%d\n",
	   cur-obuf,(int)((double)bytes/(double)frames +0.5),
	   tframes,(int)((double)tbytes/(double)tframes + 0.5));
#endif
	memcpy(&cs_sockets[cnum].lastmap,newmap,sizeof(struct Map));
	Send_With_Handling(cnum, &sl);
	cs_sockets[cnum].sent_scroll = 0;
    }
    free(sl.buf);
}



/* grabbed out of xio.c - it makes more sense to put it over here. */

void draw_client_map(object *pl)
{
    int i,j,ax,ay; /* ax and ay goes from 0 to max-size of arrays */
    uint32 cnum;
    New_Face	*face,*floor;
#ifdef DOUBLE_FLOOR
    New_Face	*floor2;
#endif
    int d;
    struct Map	newmap;

    if (pl->type != PLAYER || pl->contr->eric_server==0) {
	LOG(llevError,"draw_client_map called with non player/non eric-server\n");
	return;
    }
    if ((cnum = getcnum(pl->contr->eric_server))<0) {
	LOG(llevDebug,"client %ld is gone.\n",pl->contr->eric_server);
	return;
    }
    memset(&newmap, 0, sizeof(struct Map));

    if(pl->invisible & (pl->invisible < 50 ? 4 : 1)) {
	esrv_map_setbelow(pl->contr->eric_server,cnum,5,5,pl->face->number,&newmap);
    }

    for(j=pl->y+WINUPPER;j<=pl->y+WINLOWER;j++) {
	ay=j-pl->y-WINUPPER;
	for(i=pl->x+WINLEFT;i<=pl->x+WINRIGHT;i++) {
	    ax=i-pl->x-WINLEFT;
	    d =  pl->contr->blocked_los[i+5-pl->x][j+5-pl->y];
	    if (!(out_of_map(pl->map,i,j) || d>3)) {
		face = get_map(pl->map, i, j)->face;
		floor = get_map_floor(pl->map, i,j)->face;
#ifdef DOUBLE_FLOOR
		floor2 = get_map_floor2(pl->map, i,j)->face;
#endif

		/* If all is blank, send a blank face. */
		if (face == blank_face &&
#ifdef DOUBLE_FLOOR
		    floor2 == blank_face &&
#endif
		    floor == blank_face) {
			esrv_map_setbelow(pl->contr->eric_server,cnum,ax,ay,
				blank_face->number,&newmap);
		} else { /* actually have something interesting */
		  /* send the darkness mask, if any */
		  if (d ) 
		    esrv_map_setbelow(pl->contr->eric_server,cnum,ax,ay,
						  dark_faces[d-1]->number,&newmap);

		    if (face != blank_face)
			esrv_map_setbelow(pl->contr->eric_server,cnum,ax,ay,
					  face->number,&newmap);
#ifdef DOUBLE_FLOOR
		    if (floor2 != blank_face)
			esrv_map_setbelow(pl->contr->eric_server,cnum,ax,ay,
				floor2->number,&newmap);
#endif
		    if (floor != blank_face)
			esrv_map_setbelow(pl->contr->eric_server,cnum,ax,ay,
					  floor->number,&newmap);
		} 
	    } /* Is a valid space */
	}
    }
    esrv_map_doneredraw(pl->contr->eric_server, cnum, &newmap);
}


void esrv_map_scroll(long client_id,int dx,int dy)
{
    int cnum;
    struct Map newmap;
    int x,y;
    char buf[MAXSOCKBUF];

    if ((cnum = getcnum(client_id))<0) {
	LOG(llevError,"map_scroll: updating map for non-existant client\n");
	return;
    }
    sprintf(buf,"map_scroll %d %d", dx, dy);
    Write_String_To_Socket(cnum, buf, strlen(buf));
    /* the x and y here are coordinates for the new map, i.e. if we moved
     (dx,dy), newmap[x][y] = oldmap[x-dx][y-dy] */
    for(x=0;x<11;x++) {
	for(y=0;y<11;y++) {
	    newmap.cells[x][y].count = 0;
	    if (x+dx < 0 || x+dx >= 11)
		continue;
	    if (y+dy < 0 || y+dy >= 11)
		continue;
	    memcpy(&(newmap.cells[x][y]),
		   &(cs_sockets[cnum].lastmap.cells[x+dx][y+dy]),sizeof(struct MapCell));
	}
    }
    memcpy(&(cs_sockets[cnum].lastmap), &newmap,sizeof(struct Map));
    cs_sockets[cnum].sent_scroll = 1;
}

/* Free's all the memory that ericserver allocates. */
void free_all_ericserver()
{  
    int num;

    LOG(llevDebug,"Freeing all ericserver information.\n");
    for(num=0;num<MAXFACENUM;num++) {
	if (faces[num].name) {
	    free(faces[num].name);
	    free(faces[num].data);
	}
    }
    free(cs_sockets);
}


#else

/* Instead of having a bunch if #ifdefs to comment out active code
 * in the functions, it is easier to just make a seperate section
 * (here) of the functions that just do nothing.
 */
void esrv_del_item(int client_id, int tag) {}
void init_ericserver() {}
void esrv_new_player(long client_id, long tag, char *name, long weight, long face) {}
void esrv_remove_player(long client_id){}
void doeric_server() {}
void send_query(long client_id, uint8 flags, char *text) {}
int getcnum(uint32 client_id)  {return -1;}
void esrv_send_face(uint32 client_num,short face_num){}
void Send_With_Handling(int cnum,SockList  *msg){}
int ericfd(){ return -1;}
void esrv_update_stats(long client_id, object *pl) {}
void esrv_print_msg(uint32 client_id,int color, const char *str) {}
void draw_client_map(object *pl) {} 
void esrv_map_scroll(long client_id,int dx,int dy) {}
void free_all_ericserver() {}
#endif

