/*
 * static char *rcsid_init_c =
 *    "$Id: request.c,v 1.33 2001/12/31 08:36:04 mwedel Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 2001 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to crossfire-devel@real-time.com
*/

/*
 * 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>

#include <newclient.h>
#include <newserver.h>
#include <living.h>
#include <commands.h>

/* This block is basically taken from socket.c - I assume if it works there,
 * it should work here.
 */
#ifndef WIN32 /* ---win32 exclude unix headers */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif /* win32 */

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "sounds.h"

/* This table translates the attack numbers as used within the
 * program to the value we use when sending STATS command to the
 * client.  IF a value is -1, then we don't send that to the
 * client.
 */
short atnr_cs_stat[NROFATTACKS] = {CS_STAT_RES_PHYS,
    CS_STAT_RES_MAG,CS_STAT_RES_FIRE, CS_STAT_RES_ELEC,
    CS_STAT_RES_COLD, CS_STAT_RES_CONF, CS_STAT_RES_ACID,
    CS_STAT_RES_DRAIN, -1 /* weaponmagic */,
    CS_STAT_RES_GHOSTHIT, CS_STAT_RES_POISON, 
    CS_STAT_RES_SLOW, CS_STAT_RES_PARA, CS_STAT_TURN_UNDEAD,
    CS_STAT_RES_FEAR, -1 /* Cancellation */,
    CS_STAT_RES_DEPLETE, CS_STAT_RES_DEATH,
    -1 /* Chaos */, -1 /* Counterspell */,
    -1 /* Godpower */, CS_STAT_RES_HOLYWORD,
    CS_STAT_RES_BLIND,    -1 /* Blind */
};

/* This is the Setup cmd - easy first implementation */
void SetUp(char *buf, int len, NewSocket *ns)
{
    int s;
    char *cmd, *param, cmdback[HUGE_BUF];

    /* run through the cmds of setup
     * syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ...
     *
     * we send the status of the cmd back, or a FALSE is the cmd is the server unknown
     * The client then must sort this out
     */

    LOG(llevInfo,"Get SetupCmd:: %s\n", buf);
    strcpy(cmdback,"setup");
    for(s=0;s<len; ) {

	cmd = &buf[s];

	/* find the next space, and put a null there */
	for(;buf[s] && buf[s] != ' ';s++) ;
	buf[s++]=0;
	while (buf[s] == ' ') s++;

	if(s>=len)
	    break;

	param = &buf[s];

	for(;buf[s] && buf[s] != ' ';s++) ;
	buf[s++]=0;
	while (buf[s] == ' ') s++;
		
	strcat(cmdback, " ");
	strcat(cmdback, cmd);
	strcat(cmdback, " ");

	
	if (!strcmp(cmd,"newanim")) {
	    ns->newanim = atoi(param);
	    strcat(cmdback, param);
	}
	else if (!strcmp(cmd,"sound")) {
	    ns->sound = atoi(param);
	    strcat(cmdback, param);
	}
	else if (!strcmp(cmd,"ext2")) {
	    ns->ext2 = atoi(param);
	    strcat(cmdback, param);
	}
	else if (!strcmp(cmd,"sexp")) {
	    ns->skillexp = atoi(param);
	    strcat(cmdback, param);
	} else if (!strcmp(cmd,"darkness")) {
	    ns->darkness = atoi(param);
	    strcat(cmdback, param);
	} else if (!strcmp(cmd,"map1cmd")) {
	    ns->map1cmd = atoi(param);
	    /* if beyond this size, need to use map1cmd no matter what */
	    if (ns->mapx>11 || ns->mapy>11) ns->map1cmd=1;
	    strcat(cmdback, ns->map1cmd?"1":"0");
        } else if (!strcmp(cmd,"newmapcmd")) {
            ns->newmapcmd= atoi(param);
            strcat(cmdback, param);
        } else if (!strcmp(cmd,"map2cmd")) {
            ns->map2cmd = atoi(param);
            /* if beyond this size, need to use map2cmd no matter what */
            if (ns->mapx>11 || ns->mapy>11) ns->map2cmd=1;
            strcat(cmdback, ns->map2cmd?"1":"0");
            if(ns->map2cmd)ns->map1cmd=0;
	} else if (!strcmp(cmd,"facecache")) {
	    ns->facecache = atoi(param);
            strcat(cmdback, param);
	} else if (!strcmp(cmd,"faceset")) {
	    char tmpbuf[20];
	    int q = atoi(param);

	    if (is_valid_faceset(q))
		ns->faceset=q;
	    sprintf(tmpbuf,"%d", ns->faceset);
	    strcat(cmdback, tmpbuf);
	    /* if the client is using faceset, it knows about image2 command */
	    ns->image2=1;
        } else if (!strcmp(cmd,"mapsize")) {
	    int x, y=0;
	    char tmpbuf[MAX_BUF], *cp;

	    x = atoi(param);
	    for (cp = param; *cp!=0; cp++)
		if (*cp == 'x' || *cp == 'X') {
		    y = atoi(cp+1);
		    break;
		}
	    if (x < 9 || y < 9 || x>MAP_CLIENT_X || y > MAP_CLIENT_Y) {
		sprintf(tmpbuf," %dx%d", MAP_CLIENT_X, MAP_CLIENT_Y);
		strcat(cmdback, tmpbuf);
	    } else {
		ns->mapx = x;
		ns->mapy = y;
		/* better to send back what we are really using and not the
		 * param as given to us in case it gets parsed differently.
		 */
		sprintf(tmpbuf,"%dx%d", x,y);
		strcat(cmdback, tmpbuf);
		/* If beyond this size and not map2cmd used, will use map1cmd */
		if ((x>11 || y>11) && !ns->map2cmd) ns->map1cmd=1;
	    }
	} else {
	    /* Didn't get a setup command we understood -
	     * report a failure to the client.
	     */
	    strcat(cmdback, "FALSE");
	}
    } /* for processing all the setup commands */
    LOG(llevInfo,"SendBack SetupCmd:: %s\n", cmdback);
    Write_String_To_Socket(ns, cmdback, strlen(cmdback));
}

/* 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.
 * I am not sure if this file is the best place for this function.  however,
 * it either has to be here or init_sockets needs to be exported.
 */
void AddMeCmd(char *buf, int len, NewSocket *ns)
{
    Settings oldsettings;
    oldsettings=settings;
    if (ns->status != Ns_Add || add_player(ns)) {		
	Write_String_To_Socket(ns, "addme_failed",12);
    } else {
	/* Basically, the add_player copies the socket structure into
	 * the player structure, so this one (which is from init_sockets)
	 * is not needed anymore.  The write below should still work, as the
	 * stuff in ns is still relevant.
	 */
	Write_String_To_Socket(ns, "addme_success",13);
	socket_info.nconns--;
	ns->status = Ns_Avail;
    }
    settings=oldsettings;	
}


/* This handles the general commands from the client (ie, north, fire, cast,
 * etc.)
 */
void PlayerCmd(char *buf, int len, player *pl)
{

    /* 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->state!=ST_PLAYING) {
	new_draw_info_format(NDI_UNIQUE, 0,pl->ob,
	    "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->count=atoi((char*)buf);
	pl->count_left=0;
	buf=strchr(buf,' ');	/* advance beyond the numbers */
	if (!buf) {
#ifdef ESRV_DEBUG
	    LOG(llevDebug,"PlayerCmd: Got count but no command.");
#endif
	    return;
	}
	buf++;
    }
    pl->idle=0;
    /* This should not happen anymore.    */
    if (pl->ob->speed_left<-1.0) {
	LOG(llevError,"Player has negative time - shouldn't do command.\n");
    }
    /* In c_new.c */
    execute_newserver_command(pl->ob, (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->count_left=0;
    pl->count=0;

}


/* This handles the general commands from the client (ie, north, fire, cast,
 * etc.)  It is a lot like PlayerCmd above, but is called with the
 * 'ncom' method which gives more information back to the client so it
 * can throttle.
 */
void NewPlayerCmd(uint8 *buf, int len, player *pl)
{
    int packet, time,repeat;
    char    command[MAX_BUF];
    SockList sl;

    if (len < 7) {
	LOG(llevDebug,"Corrupt ncom command - not long enough - discarding\n");
	return;
    }

    packet = GetShort_String(buf);
    repeat = GetInt_String(buf+2);
    /* -1 is special - no repeat, but don't update */
    if (repeat!=-1) {
	pl->count=repeat;
	pl->count_left=0;
    }
    if ((len-4) >= MAX_BUF) len=MAX_BUF-5;

    strncpy(command, (char*)buf+6, len-4);
    command[len-4]='\0';

    /* 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->state!=ST_PLAYING) {
	new_draw_info_format(NDI_UNIQUE, 0,pl->ob,
	    "You can not issue commands - state is not ST_PLAYING (%s)", buf);
	return;
    }

    pl->idle=0;

    /* This should not happen anymore.    */
    if (pl->ob->speed_left<-1.0) {
	LOG(llevError,"Player has negative time - shouldn't do command.\n");
    }
    /* In c_new.c */
    execute_newserver_command(pl->ob, command);
    /* 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->count_left=0;
    pl->count=0;

    /* Send confirmation of command execution now */
    sl.buf = (uint8*)command;
    strcpy((char*)sl.buf,"comc ");
    sl.len=5;
    SockList_AddShort(&sl,packet);
    if (FABS(pl->ob->speed) < 0.001) time=MAX_TIME * 100;
    else
	time = MAX_TIME/ FABS(pl->ob->speed);
    SockList_AddInt(&sl,time);
    Send_With_Handling(&pl->socket, &sl);
}


/* This is a reply to a previous query. */
void ReplyCmd(char *buf, int len, player *pl)
{
    /* 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->write_buf,":%s", (char*)buf);

    pl->socket.ext_title_flag = 2; /* funny hack: we want the class info in old title function */

    /* this avoids any hacking here */
    
    switch (pl->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->ob, buf[0]);
	    break;

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

	case ST_CHANGE_CLASS:

        key_change_class(pl->ob, buf[0]);
        break;

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

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

	case ST_GET_NAME:
	    receive_player_name(pl->ob,13);
	    break;

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

	case ST_GET_PARTY_PASSWORD:        /* Get password for party */
	receive_party_password(pl->ob,13);
	    break;

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

/* 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.
 */
void VersionCmd(char *buf, int len,NewSocket *ns)
{
    char *cp;
    char version_warning[256];
		
    if (!buf) {
	LOG(llevError, "CS: received corrupted version command\n");
	return;
    }

    ns->cs_version = atoi(buf);
    ns->sc_version =  ns->cs_version;
    if (VERSION_CS !=  ns->cs_version) {
#ifdef ESRV_DEBUG
	LOG(llevDebug, "CS: csversion mismatch (%d,%d)\n", VERSION_CS,ns->cs_version);
#endif
    }
    cp = strchr(buf+1,' ');
    if (!cp) return;
    ns->sc_version = atoi(cp);
    if (VERSION_SC != ns->sc_version) {
#ifdef ESRV_DEBUG
	LOG(llevDebug, "CS: scversion mismatch (%d,%d)\n",VERSION_SC,ns->sc_version);
#endif
    }
    cp = strchr(cp+1, ' ');
    if (cp) {
	LOG(llevDebug,"CS: connection from client of type <%s>\n", cp);

	/* This is first implementation - i skip all beta DX clients with it 
	 * Add later stuff here for other clients 
	 */

	if(!strcmp(" CF DX CLIENT", cp)) /* these are old dxclients */
	{
	    sprintf(version_warning,"drawinfo %d %s", NDI_RED, "**** VERSION WARNING ****\n**** CLIENT IS TO OLD!! UPDATE THE CLIENT!! ****");
	    Write_String_To_Socket(ns, version_warning, strlen(version_warning));
	}
    }
}

/* sound related functions. */
 
void SetSound(char *buf, int len, NewSocket *ns)
{
    ns->sound = atoi(buf);
}

/* client wants the map resent */

void MapRedrawCmd(char *buff, int len, player *pl)
{
    /* Okay, this is MAJOR UGLY. but the only way I know how to
     * clear the "cache"
     */
    memset(&pl->socket.lastmap, 0, sizeof(struct Map));
    draw_client_map(pl->ob);
}

void MapNewmapCmd( player *pl)
{
    if( pl->socket.newmapcmd == 1)
        Write_String_To_Socket( &pl->socket, "newmap", 6);
}



/* Moves and object (typically, container to inventory
 * move <to> <tag> <nrof> 
 */
void MoveCmd(char *buf, int len,player *pl)
{
    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);

/*    LOG(llevDebug,"Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);*/
    esrv_move_object(pl->ob,vals[0], vals[1], vals[2]);
}



/******************************************************************************
 *
 * Start of commands the server sends to the client.
 *
 ******************************************************************************/

/*
 * 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(NewSocket *ns, uint8 flags, char *text)
{
    char buf[MAX_BUF];

    sprintf(buf,"query %d %s", flags, text?text:"");
    Write_String_To_Socket(ns, 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(player *pl)
{
    SockList sl;
    char buf[MAX_BUF];
    uint16 flags;

    sl.buf=malloc(MAX_BUF);
    strcpy((char*)sl.buf,"stats ");
    sl.len=strlen((char*)sl.buf);
    
    if(pl->ob != NULL)
    {
        AddIfShort(pl->last_stats.hp, pl->ob->stats.hp, CS_STAT_HP);
        AddIfShort(pl->last_stats.maxhp, pl->ob->stats.maxhp, CS_STAT_MAXHP);
        AddIfShort(pl->last_stats.sp, pl->ob->stats.sp, CS_STAT_SP);
        AddIfShort(pl->last_stats.maxsp, pl->ob->stats.maxsp, CS_STAT_MAXSP);
        AddIfShort(pl->last_stats.grace, pl->ob->stats.grace, CS_STAT_GRACE);
        AddIfShort(pl->last_stats.maxgrace, pl->ob->stats.maxgrace, CS_STAT_MAXGRACE);
        AddIfShort(pl->last_stats.Str, pl->ob->stats.Str, CS_STAT_STR);
        AddIfShort(pl->last_stats.Int, pl->ob->stats.Int, CS_STAT_INT);
        AddIfShort(pl->last_stats.Pow, pl->ob->stats.Pow, CS_STAT_POW);
        AddIfShort(pl->last_stats.Wis, pl->ob->stats.Wis, CS_STAT_WIS);
        AddIfShort(pl->last_stats.Dex, pl->ob->stats.Dex, CS_STAT_DEX);
        AddIfShort(pl->last_stats.Con, pl->ob->stats.Con, CS_STAT_CON);
        AddIfShort(pl->last_stats.Cha, pl->ob->stats.Cha, CS_STAT_CHA);
    }
    if(pl->last_stats.exp != pl->ob->stats.exp && pl->socket.skillexp) 
    {
	int s;
	for(s=0;s<pl->last_skill_index;s++)
        {
        AddIfInt(pl->last_skill_exp[s],pl->last_skill_ob[s]->stats.exp , pl->last_skill_id[s]);
        AddIfShort(pl->last_skill_level[s],pl->last_skill_ob[s]->level , pl->last_skill_id[s]+1);
    }
    }
    AddIfInt(pl->last_stats.exp, pl->ob->stats.exp, CS_STAT_EXP);
    AddIfShort(pl->last_level, pl->ob->level, CS_STAT_LEVEL);
    AddIfShort(pl->last_stats.wc, pl->ob->stats.wc, CS_STAT_WC);
    AddIfShort(pl->last_stats.ac, pl->ob->stats.ac, CS_STAT_AC);
    AddIfShort(pl->last_stats.dam, pl->ob->stats.dam, CS_STAT_DAM);
    AddIfFloat(pl->last_speed, pl->ob->speed, CS_STAT_SPEED);
    AddIfShort(pl->last_stats.food, pl->ob->stats.food, CS_STAT_FOOD);
    AddIfFloat(pl->last_weapon_sp, pl->weapon_sp, CS_STAT_WEAP_SP);
    AddIfInt(pl->last_weight_limit, weight_limit[pl->ob->stats.Str], CS_STAT_WEIGHT_LIM);
    flags=0;
    if (pl->fire_on) flags |=SF_FIREON;
    if (pl->run_on) flags |= SF_RUNON;

    AddIfShort(pl->last_flags, flags, CS_STAT_FLAGS);
    if (pl->socket.sc_version<1025) {
	AddIfShort(pl->last_resist[ATNR_PHYSICAL], pl->ob->resist[ATNR_PHYSICAL], CS_STAT_ARMOUR);
    } else {
	int i;

	for (i=0; i<NROFATTACKS; i++) {
	    /* Skip ones we won't send */
	    if (atnr_cs_stat[i]==-1) continue;
	    AddIfShort(pl->last_resist[i], pl->ob->resist[i], atnr_cs_stat[i]);
	}
    }
 
    /* the user set title is nothing we don't use in new client.
     * If so, pack it behind name 
     */
    if(pl->socket.ext2) /* for the ext2 client, we send complex title  and no range info*/
    {
        if(pl->socket.ext_title_flag)
        {
            generate_ext_title(pl);
            AddIfString(pl->socket.stats.ext_title , pl->ext_title, CS_STAT_EXT_TITLE);
            
            /* ugly little sucker, but so we got the arch name in class select of the
             * client and we don't must change above anything */
            if(pl->socket.ext_title_flag==2)
            {
                set_title(pl->ob, buf);
                AddIfString(pl->socket.stats.title, buf, CS_STAT_TITLE);
            }
            pl->socket.ext_title_flag = 0;
        }
    }
    else /* for old clients, the normal range & title cmd */
    {    
	rangetostring(pl->ob, buf); /* we want use the new fire & run system in new client */
        AddIfString(pl->socket.stats.range, buf, CS_STAT_RANGE);
        set_title(pl->ob, buf);
        AddIfString(pl->socket.stats.title, buf, CS_STAT_TITLE);
    }

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


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

void esrv_new_player(player *pl, uint32 weight)
{
    SockList	sl;

    sl.buf=malloc(MAXSOCKBUF);

    strcpy((char*)sl.buf,"player ");
    sl.len=strlen((char*)sl.buf);
    SockList_AddInt(&sl, pl->ob->count);
    SockList_AddInt(&sl, weight);
    SockList_AddInt(&sl, pl->ob->face->number);

    SockList_AddChar(&sl, strlen(pl->ob->name));
    strcpy((char*)sl.buf+sl.len, pl->ob->name);
    sl.len += strlen(pl->ob->name);
       
    Send_With_Handling(&pl->socket, &sl);
    free(sl.buf);
    SET_FLAG(pl->ob, FLAG_CLIENT_SENT);
}


/* Need to send an animation sequence to the client.
 * We will send appropriate face commands to the client if we haven't
 * sent them the face yet (this can become quite costly in terms of
 * how much we are sending - on the other hand, this should only happen
 * when the player logs in and picks stuff up.
 */
void esrv_send_animation(NewSocket *ns, short anim_num)
{
    SockList sl;
    int i;

    /* Do some checking on the anim_num we got.  Note that the animations
     * are added in contigous order, so if the number is in the valid
     * range, it must be a valid animation.
     */
    if (anim_num < 0 || anim_num >= num_animations) {
	LOG(llevError,"esrv_send_anim (%d) out of bounds??\n",anim_num);
	return;
    }

    sl.buf = malloc(MAXSOCKBUF);
    strcpy((char*)sl.buf, "anim ");
    sl.len=5;
    SockList_AddShort(&sl, anim_num);
    SockList_AddShort(&sl, 0);  /* flags - not used right now */
    /* Build up the list of faces.  Also, send any information (ie, the
     * the face itself) down to the client.
     */
    for (i=0; i<animations[anim_num].num_animations; i++) {
	if (ns->faces_sent[animations[anim_num].faces[i]] == 0)
	    esrv_send_face(ns,animations[anim_num].faces[i],0);
	SockList_AddShort(&sl, animations[anim_num].faces[i]);  /* flags - not used right now */
    }
    Send_With_Handling(ns, &sl);
    free(sl.buf);
    ns->anims_sent[anim_num] = 1;
}


/******************************************************************************
 *
 * Start of map related commands.
 *
 ******************************************************************************/

/* 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(NewSocket *ns, int x,int y,
			      short face_num, struct Map *newmap)
{

    if (x<0 || x>ns->mapx-1 || 
	y<0 || y>ns->mapy-1 || 
	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 (ns->faces_sent[face_num] == 0)
	esrv_send_face(ns,face_num,0);
}

struct LayerCell {
  uint16 xy;
  short face;
};

struct MapLayer {
  int count;
  struct LayerCell lcells[MAP_CLIENT_X * MAP_CLIENT_Y];
};

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

  if (ns->lastmap.cells[i][j].count != newmap->cells[i][j].count)
    return 1;
  for(k=0;k<newmap->cells[i][j].count;k++) {
    if (ns->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 uint8 *compactlayer(NewSocket *ns, unsigned char *cur, int numlayers, 
			   struct Map *newmap)
{
    int x,y,k;
    int face;
    unsigned char *fcur;
    struct MapLayer layers[MAXMAPCELLFACES];
  
    for(k = 0;k<MAXMAPCELLFACES;k++)
	layers[k].count = 0;
    fcur = cur;
    for(x=0;x<ns->mapx;x++) {
	for(y=0;y<ns->mapy;y++) {
	    if (!mapcellchanged(ns,x,y,newmap))
		continue;
	    if (newmap->cells[x][y].count == 0) {
		*cur = x*ns->mapy+y;	    /* mark empty space */
		cur++;
		continue;
	    }
	    for(k=0;k<newmap->cells[x][y].count;k++) {
		layers[k].lcells[layers[k].count].xy = x*ns->mapy+y;
		layers[k].lcells[layers[k].count].face = 
		    newmap->cells[x][y].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(x=0;x<layers[k].count;) {
	    fcur = cur;
	    *cur = layers[k].lcells[x].face >> 8;
	    cur++;
	    *cur = layers[k].lcells[x].face & 0xFF;
	    cur++;
	    face = layers[k].lcells[x].face;
	    /* Now, we back the redundant data into 1 byte xy pairings */
	    for(y=x;y<layers[k].count;y++) {
		if (layers[k].lcells[y].face == face) {
		    *cur = layers[k].lcells[y].xy;
		    cur++;
		    layers[k].lcells[y].face = -1;
		}
	    }
	    *(cur-1) = *(cur-1) | 128; /* mark for end of xy's; 11*11 < 128 */
	    /* forward over the now redundant data */
	    while(x < layers[k].count &&
		  layers[k].lcells[x].face == -1)
		x++;
	}
	*fcur = *fcur | 128; /* mark for end of faces at this layer */
    }
    return cur;
}

static void esrv_map_doneredraw(NewSocket *ns, struct Map *newmap)
{
    static long frames,bytes,tbytes,tframes;
    uint8 *cur;
    SockList sl;


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

    cur = compactlayer(ns,sl.buf+sl.len,MAXMAPCELLFACES,newmap);
    sl.len=cur-sl.buf;

/*    LOG(llevDebug, "Sending map command.\n");*/

    if (sl.len>strlen("map ") || ns->sent_scroll) {
	/* All of this is just accounting stuff */
	if (tframes>100) {
	    tframes = tbytes = 0;
	}
	tframes++;
	frames++;
	tbytes += sl.len;
	bytes += sl.len;
	memcpy(&ns->lastmap,newmap,sizeof(struct Map));
	Send_With_Handling(ns, &sl);
	ns->sent_scroll = 0;
    }
    free(sl.buf);
}


/* Clears a map cell */
static void map_clearcell(struct MapCell *cell)
{
    cell->count=-1;
    cell->faces[0] = 0;
    cell->faces[0] = 0;
    cell->faces[1] = 0;
    cell->faces[2] = 0;
}


/* this function uses the new map1 protocol command to send the map
 * to the client.  It is necessary because the old map command supports
 * a maximum map size of 15x15.
 * This function is much simpler than the old one.  This is because
 * the old function optimized to send as few face identifiers as possible,
 * at the expense of sending more coordinate location (coordinates were
 * only 1 byte, faces 2 bytes, so this was a worthwhile savings).  Since
 * we need 2 bytes for coordinates and 2 bytes for faces, such a trade off
 * maps no sense.  Instead, we actually really only use 12 bits for coordinates,
 * and use the other 4 bits for other informatiion.   For full documentation
 * of what we send, see the doc/Protocol file.
 * I will describe internally what we do:
 * the socket->lastmap shows how the map last looked when sent to the client.
 * in the lastmap structure, there is a cells array, which is set to the
 * maximum viewable size (As set in config.h).
 * in the cells, there are faces and a count value.
 * we use the count value to hold the darkness value.  If -1, then this space
 *   is not viewable.
 * we use faces[0] faces[1] faces[2] to hold what the three layers
 * look like.
 */
void draw_client_map1(object *pl)
{
    int x,y,ax, ay, d, face_num1,face_num2,face_num3, nx,ny;
    SockList sl;
    uint16  mask;
    New_Face	*face;
    mapstruct *m;

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


    /* x,y are the real map locations.  ax, ay are viewport relative
     * locations.
     */
    ay=0;
    for(y=pl->y-pl->contr->socket.mapy/2; y<pl->y+(pl->contr->socket.mapy+1)/2;y++,ay++) {
	ax=0;
	for(x=pl->x-pl->contr->socket.mapx/2;x<pl->x+(pl->contr->socket.mapx+1)/2;x++,ax++) {
	    d =  pl->contr->blocked_los[ax][ay];
	    mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4;

	    /* If the coordinates are not valid, or it is too dark to see,
	     * we tell the client as such
	     */
	    nx=x;
	    ny=y;
	    m = get_map_from_coord(pl->map, &nx, &ny);
	    if (!m) {
		/* space is out of map.  Update space and clear values
		 * if this hasn't already been done.
		 */
		if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) {
		    SockList_AddShort(&sl, mask);
		    map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
		}
	    } else if ( d > 3 ) {
		/* space is 'blocked' by darkness */
		if (d==4 && pl->contr->socket.darkness) {
		    /* this is the first spot where darkness becomes too dark to see.
		     * only need to update this if it is different from what we 
		     * last sent
		     */
		    if (pl->contr->socket.lastmap.cells[ax][ay].count != d) {
			map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
			SockList_AddShort(&sl, mask);
			mask |= 8;  /* add darkness */
			SockList_AddShort(&sl, mask);
			SockList_AddChar(&sl, 0);
			pl->contr->socket.lastmap.cells[ax][ay].count = d;
		    }
		} else if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) {
		    SockList_AddShort(&sl, mask);
		    map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
		}
	    }
	    else { /* this space is viewable */
		/* Rather than try to figure out what everything that we might
		 * need to send is, then form the packet after that,
		 * we presume that we will in fact form a packet, and update
		 * the bits by what we do actually send.  If we send nothing,
		 * we just back out sl.len to the old value, and no harm
		 * is done.
		 * I think this is simpler than doing a bunch of checks to see
		 * what if anything we need to send, setting the bits, then
		 * doing those checks again to add the real data.
		 */
		int oldlen = sl.len;
		mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4;
		SockList_AddShort(&sl, mask);

		/* Darkness changed */
		if (pl->contr->socket.lastmap.cells[ax][ay].count != d && pl->contr->socket.darkness) {
		    pl->contr->socket.lastmap.cells[ax][ay].count = d;
		    mask |= 0x8;    /* darkness bit */

		    /* Protocol defines 255 full bright, 0 full dark.
		     * We currently don't have that many darkness ranges,
		     * so we current what limited values we do have.
		     */
		    if (d==0) SockList_AddChar(&sl, 255);
		    else if (d==1) SockList_AddChar(&sl, 191);
		    else if (d==2) SockList_AddChar(&sl, 127);
		    else if (d==3) SockList_AddChar(&sl, 63);
		}
		else
		    /* need to reset from -1 so that if it does become blocked again,
		     * the code that deals with that can detect that it needs to tell
		     * the client that this space is now blocked.
		     */
		    pl->contr->socket.lastmap.cells[ax][ay].count = d;

		/* Check to see if floor face has changed */
		face = GET_MAP_FACE(m, nx,ny,2);
		if (!face || face == blank_face) face_num1=0;
		else face_num1 = face->number;

		if (pl->contr->socket.lastmap.cells[ax][ay].faces[0] != face_num1) {
		    mask |= 0x4;    /* floor bit */
		    pl->contr->socket.lastmap.cells[ax][ay].faces[0] = face_num1;
		    SockList_AddShort(&sl, face_num1);
		    if (pl->contr->socket.faces_sent[face_num1] == 0)
			esrv_send_face(&pl->contr->socket,face_num1,0);
		}

		face = GET_MAP_FACE(m, nx,ny,1);
		if (!face || face == blank_face) face_num2=0;
		else face_num2 = face->number;

		if (pl->contr->socket.lastmap.cells[ax][ay].faces[1] != face_num2) {
		    mask |= 0x2;    /* middle bit */
		    pl->contr->socket.lastmap.cells[ax][ay].faces[1] = face_num2;
		    SockList_AddShort(&sl, face_num2);
		    if (pl->contr->socket.faces_sent[face_num2] == 0)
			esrv_send_face(&pl->contr->socket,face_num2,0);
		}

		face = GET_MAP_FACE(m, nx,ny,0);
		if (!face || face == blank_face) face_num3=0;
		else face_num3 = face->number;

		if (pl->contr->socket.lastmap.cells[ax][ay].faces[2] != face_num3) {
		    mask |= 0x1;    /* top bit */
		    pl->contr->socket.lastmap.cells[ax][ay].faces[2] = face_num3;
		    SockList_AddShort(&sl, face_num3);
		    if (pl->contr->socket.faces_sent[face_num3] == 0)
			esrv_send_face(&pl->contr->socket,face_num3,0);
		}
		if ((face_num1 == face_num2 && face_num1 !=0) || 
		    (face_num2 == face_num3 && face_num2 !=0) || 
		    (face_num1 == face_num3 && face_num1 !=0)) {
		    /* This shouldn't happen - update_position should already
		     * check for this.
		     */
		    LOG(llevDebug,"faces match: %d %d %d\n", face_num1, face_num2, face_num3);
		}

		/* Lets see if the mask is in fact different.  If, we need to
		 * update it, if not, we back it out
		 */
		if (mask & 0xf) {
		    /* yep - different. Only need to update the second byte
		     * because that is the only one that changed.
		     */
		    sl.buf[oldlen+1] = mask & 0xff;
		} else {
		    /* no change, so just reset the len */
		    sl.len = oldlen;
		}
	    }
	} /* for x loop */
    } /* for y loop */
		    
    /* Verify that we in fact do need to send this */
    if (sl.len>strlen("map1 ") || pl->contr->socket.sent_scroll) {
	Send_With_Handling(&pl->contr->socket, &sl);
	pl->contr->socket.sent_scroll = 0;
    }
    free(sl.buf);
}

/* this is the map2 command, which sends only the head of a multi tile
 * or a animation tag instead of a face (comes later).
 * I reduce for this the x/y bit size to 5. 31x31 map are real big. There should
 * not send bigger or even so big maps (game play problems & performance problems!).
 * The first of the free bits is unused, the second is used as a flag byte marker.
 * If it set, there is a byte with flags in front of the face & darkness data.
 * the first 3 bits fo the byte are used as multi head flags.
 * if set, the face layer comparing to it is the head picture of a multi arch
 * and the next 2 bytes
 * after this layer hold the x/y offset of the part tile relativ to the head.
 * Depending on the physical tile size can then the client blit & clip the picture.
 * Because we send so only 1 picture/data for a multi tile arch, we will get back the
 * overhead of bytes we send for the 1st one.
 */
#define NO_FACE_SEND        (-1)
#define FACE_SEND_NORMAL    0
 
void draw_client_map2(object *pl)
{
    int x,y,ax, ay, d, face_num1,face_num2,face_num3, nx,ny;
    SockList sl;
    uint16  mask;
    New_Face	*face;
    mapstruct *m;
    object *tmp;
    int dark, face1, face2, face3;
    int quick_pos_1,quick_pos_2,quick_pos_3; 

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



    /* x,y are the real map locations.  ax, ay are viewport relative
     * locations.
     */
    ay=0;
    for(y=pl->y-pl->contr->socket.mapy/2; y<pl->y+(pl->contr->socket.mapy+1)/2;y++,ay++) {
	ax=0;
	for(x=pl->x-pl->contr->socket.mapx/2;x<pl->x+(pl->contr->socket.mapx+1)/2;x++,ax++) {
	    d =  pl->contr->blocked_los[ax][ay];
	    mask = (ax & 0x1f) << 11 | (ay & 0x1f) << 5;

	    /* If the coordinates are not valid, or it is too dark to see,
	     * we tell the client as such
	     */
	    nx=x;
	    ny=y;
	    m = get_map_from_coord(pl->map, &nx, &ny);
	    if (!m) {
		/* space is out of map.  Update space and clear values
		 * if this hasn't already been done.
		 */
		if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) {
		    SockList_AddShort(&sl, mask);
		    map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
		}
	    } else if ( d > 3 ) {
		/* space is 'blocked' by darkness */
		if (d==4 && pl->contr->socket.darkness) {
		    /* this is the first spot where darkness becomes too dark to see.
		     * only need to update this if it is different from what we 
		     * last sent
		     */
		    if (pl->contr->socket.lastmap.cells[ax][ay].count != d) {
			map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
			SockList_AddShort(&sl, mask);
			mask |= 8;  /* add darkness */
			SockList_AddShort(&sl, mask);
			SockList_AddChar(&sl, 0);
			pl->contr->socket.lastmap.cells[ax][ay].count = d;
		    }
		} else if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) {
		    SockList_AddShort(&sl, mask);
		    map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay]);
		}
	    }
	    else { /* this space is viewable */

        int ext_flag = 0, oldlen = sl.len;
        dark = NO_FACE_SEND;
        face1 = face2 = face3 = NO_FACE_SEND;
		mask = (ax & 0x1f) << 11 | (ay & 0x1f) << 5;

		/* Darkness changed */
		if (pl->contr->socket.lastmap.cells[ax][ay].count != d && pl->contr->socket.darkness) {
		    pl->contr->socket.lastmap.cells[ax][ay].count = d;
		    mask |= 0x8;    /* darkness bit */

		    /* Protocol defines 255 full bright, 0 full dark.
		     * We currently don't have that many darkness ranges,
		     * so we current what limited values we do have.
		     */
            if (d==0) dark = 255;
            else if (d==1) dark = 191;
            else if (d==2) dark = 127;
            else if (d==3) dark = 63;
        }
		else
		    /* need to reset from -1 so that if it does become blocked again,
		     * the code that deals with that can detect that it needs to tell
		     * the client that this space is now blocked.
		     */
		    pl->contr->socket.lastmap.cells[ax][ay].count = d;

		/* Check to see if floor face has changed */
		face = GET_MAP_FACE(m, nx,ny,2);

        tmp = GET_MAP_FACE_OBJ(m, nx,ny,2); 
        if(tmp)
        {
            quick_pos_1=tmp->quick_pos;
            if(quick_pos_1 && quick_pos_1!=255)
                face = tmp->head->face;
        }
         else
            quick_pos_1 = 0;
         if (!face || face == blank_face) face_num1=0;
         else face_num1 = face->number;

         if (pl->contr->socket.lastmap.cells[ax][ay].faces[0] != face_num1 ||
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[0] != quick_pos_1) {
            
		    mask |= 0x4;    /* floor bit */
		    pl->contr->socket.lastmap.cells[ax][ay].faces[0] = face_num1;
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[0] = quick_pos_1;
            if(quick_pos_1) /* if a multi arch */
            {
                mask |= 0x10;    /* mark ext flag as valid */
                ext_flag |= 0x4; /* mark multi arch */
            }
            
            face1 = FACE_SEND_NORMAL;
            if (pl->contr->socket.faces_sent[face_num1] == 0)
			esrv_send_face(&pl->contr->socket,face_num1,0);
		}

		face = GET_MAP_FACE(m, nx,ny,1);
        
        tmp = GET_MAP_FACE_OBJ(m, nx,ny,1);
        if(tmp)
        {
            quick_pos_2=tmp->quick_pos;
            if(quick_pos_2 && quick_pos_2!=255)
               face = tmp->head->face;
        }
        else
            quick_pos_2 = 0;
        if (!face || face == blank_face) face_num2=0;
        else face_num2 = face->number;

        if (pl->contr->socket.lastmap.cells[ax][ay].faces[1] != face_num2||
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[1] != quick_pos_2) {
            
		    mask |= 0x2;    /* middle bit */
		    pl->contr->socket.lastmap.cells[ax][ay].faces[1] = face_num2;
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[1] = quick_pos_2;
            if(quick_pos_2) /* if a multi arch */
            {
                mask |= 0x10;    /* mark ext flag as valid */
                ext_flag |= 0x2;
            }
            face2 = FACE_SEND_NORMAL;
		    if (pl->contr->socket.faces_sent[face_num2] == 0)
			esrv_send_face(&pl->contr->socket,face_num2,0);
		}

		face = GET_MAP_FACE(m, nx,ny,0);
        
        tmp = GET_MAP_FACE_OBJ(m, nx,ny,0);
        if(tmp)
        {
            quick_pos_3=tmp->quick_pos;
            if(quick_pos_3 && quick_pos_3 != 255)
              face = tmp->head->face;
        }
        else
            quick_pos_3 = 0;
        if (!face || face == blank_face) face_num3=0;
        else face_num3 = face->number;
   
        if (pl->contr->socket.lastmap.cells[ax][ay].faces[2] != face_num3 ||
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[2] != quick_pos_3) {
		    mask |= 0x1;    /* top bit */
            if(quick_pos_3) /* if a multi arch */
            {
                mask |= 0x10;    /* mark ext flag as valid */
                ext_flag |= 0x1;
            }
            face3 = FACE_SEND_NORMAL;
            pl->contr->socket.lastmap.cells[ax][ay].faces[2] = face_num3;
            pl->contr->socket.lastmap.cells[ax][ay].quick_pos[2] = quick_pos_3;
            if (pl->contr->socket.faces_sent[face_num3] == 0)
			esrv_send_face(&pl->contr->socket,face_num3,0);
		}

        SockList_AddShort(&sl, mask);
        if(mask & 0x10) /* we have a ext. flag byte here */
            SockList_AddChar(&sl, ext_flag);
        if(dark != NO_FACE_SEND)
            SockList_AddChar(&sl, dark);
        if(face1 != NO_FACE_SEND)
        {
            SockList_AddShort(&sl, face_num1);
            if(ext_flag & 0x4)
                SockList_AddChar(&sl, quick_pos_1);
        }
        if(face2 != NO_FACE_SEND)
        {
            SockList_AddShort(&sl, face_num2);
            if(ext_flag & 0x2)
                SockList_AddChar(&sl, quick_pos_2);
        }
        if(face3 != NO_FACE_SEND)
        {
            SockList_AddShort(&sl, face_num3);
            if(ext_flag & 0x1)
                SockList_AddChar(&sl, quick_pos_3);
        }

       if (!(mask & 0x1f))
        sl.len = oldlen;

	    }
	} /* for x loop */
    } /* for y loop */
		    
    /* Verify that we in fact do need to send this */
    if (sl.len>strlen("map2 ") || pl->contr->socket.sent_scroll) {
	Send_With_Handling(&pl->contr->socket, &sl);
	pl->contr->socket.sent_scroll = 0;
    }
    free(sl.buf);
}




void draw_client_map(object *pl)
{
    int i,j,ax,ay,nx,ny; /* ax and ay goes from 0 to max-size of arrays */
    New_Face	*face,*floor;
    New_Face	*floor2;
    int d;
    struct Map	newmap;
    mapstruct	*m;

    if (pl->type != PLAYER) {
	LOG(llevError,"draw_client_map called with non player/non eric-server\n");
	return;
    }

    /* IF player is just joining the game, he isn't here yet, so the map
     * can get swapped out.  If so, don't try to send them a map.  All will
     * be OK once they really log in.
     */
    if (pl->map->in_memory!=MAP_IN_MEMORY) return;
    memset(&newmap, 0, sizeof(struct Map));

    for(j = (pl->y - pl->contr->socket.mapy/2) ; j < (pl->y + (pl->contr->socket.mapy+1)/2); j++) {
        for(i = (pl->x - pl->contr->socket.mapx/2) ; i < (pl->x + (pl->contr->socket.mapx+1)/2); i++) {
	    ax=i;
	    ay=j;
	    m = get_map_from_coord(pl->map, &ax, &ay);
	    if (m && (GET_MAP_FLAGS(m,ax,ay) & P_NEED_UPDATE))
		update_position(m, ax, ay);
	}
    }
    /* do LOS after calls to update_position */
    if(pl->contr->do_los) {
        update_los(pl);
        pl->contr->do_los = 0;
    }

    if (pl->contr->socket.map1cmd) {
        /* Big maps need a different drawing mechanism to work */
        draw_client_map1(pl);
        return;
    }
    
    if (pl->contr->socket.map2cmd) {
        /* Big maps need a different drawing mechanism to work */
        /* And we want only the head of a multi arch */
        draw_client_map2(pl);
        return;
    }

    if(pl->invisible & (pl->invisible < 50 ? 4 : 1)) {
	esrv_map_setbelow(&pl->contr->socket,pl->contr->socket.mapx/2,
			  pl->contr->socket.mapy/2,pl->face->number,&newmap);
    }

    /* j and i are the y and x coordinates of the real map (which is 
     * basically some number of spaces around the player)
     * ax and ay are values from within the viewport (ie, 0, 0 is upper
     * left corner) and are thus disconnected from the map values.
     */
    ay=0;
    for(j=pl->y-pl->contr->socket.mapy/2; j<=pl->y+pl->contr->socket.mapy/2;j++, ay++) {
	ax=0;
	for(i=pl->x-pl->contr->socket.mapx/2;i<=pl->x+pl->contr->socket.mapx/2;i++, ax++) {

	    d =  pl->contr->blocked_los[ax][ay];
	    /* note the out_of_map and d>3 checks are both within the same
	     * negation check.
	     */
	    nx = i;
	    ny = j;
	    m = get_map_from_coord(pl->map, &nx, &ny);
	    if (m && d<4) {
		face = GET_MAP_FACE(m, nx, ny,0);
		floor2 = GET_MAP_FACE(m, nx, ny,1);
		floor = GET_MAP_FACE(m, nx, ny,2);

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

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


void esrv_map_scroll(NewSocket *ns,int dx,int dy)
{
    struct Map newmap;
    int x,y;
    char buf[MAXSOCKBUF];

    sprintf(buf,"map_scroll %d %d", dx, dy);
    Write_String_To_Socket(ns, 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<ns->mapx;x++) {
	for(y=0;y<ns->mapy;y++) {
	    if (x+dx < 0 || x+dx >= ns->mapx || y+dy < 0 || y+dy >= ns->mapy) {
		memset(&(newmap.cells[x][y]), 0, sizeof(struct MapCell));
		continue;
	    }
	    memcpy(&(newmap.cells[x][y]),
		   &(ns->lastmap.cells[x+dx][y+dy]),sizeof(struct MapCell));
	}
    }
    memcpy(&(ns->lastmap), &newmap,sizeof(struct Map));
    ns->sent_scroll = 1;
}
/*****************************************************************************/
/* GROS: The following one is used to allow a plugin to send a generic cmd to*/
/* a player. Of course, the client need to know the command to be able to    */
/* manage it !                                                               */
/*****************************************************************************/
void send_plugin_custom_message(object *pl, char *buf)
{
    cs_write_string(&pl->contr->socket,buf,strlen(buf));
}

