--- deliantra/server/socket/request.C 2006/08/13 17:16:06 1.1 +++ deliantra/server/socket/request.C 2006/12/22 16:34:00 1.51 @@ -1,9 +1,4 @@ /* - * static char *rcsid_init_c = - * "$Id: request.C,v 1.1 2006/08/13 17:16:06 elmex Exp $"; - */ - -/* CrossFire, A Multiplayer game for X-windows Copyright (C) 2001 Mark Wedel @@ -23,7 +18,7 @@ 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 + The author can be reached via e-mail to */ /** @@ -45,42 +40,25 @@ * 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 #include -#include -#include #include #include /* 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 #include #include #include #include -#endif /* win32 */ -#ifdef HAVE_UNISTD_H #include -#endif - -#ifdef HAVE_SYS_TIME_H #include -#endif #include "sounds.h" @@ -90,68 +68,205 @@ * 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, /* Internal */ - -1, /* life stealing */ - -1 /* Disease - not fully done yet */ +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, /* Internal */ + -1, /* life stealing */ + -1 /* Disease - not fully done yet */ }; +static void +socket_map_scroll (client *ns, int dx, int dy) +{ + struct Map newmap; + int x, y, mx, my; + + ns->send_packet_printf ("map_scroll %d %d", dx, dy); + + /* If we are using the Map1aCmd, we may in fact send + * head information that is outside the viewable map. + * So set the mx,my to the max value we want to + * look for. Removed code to do so - it caused extra + * complexities for the client, and probably doesn't make + * that much difference in bandwidth. + */ + mx = ns->mapx; + my = ns->mapy; + + if (ns->mapmode == Map1aCmd) + { + mx += MAX_HEAD_OFFSET; + my += MAX_HEAD_OFFSET; + } + + /* 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 this reason, + * if the destination x or y coordinate is outside the viewable + * area, we clear the values - otherwise, the old values + * are preserved, and the check_head thinks it needs to clear them. + */ + for (x = 0; x < mx; x++) + { + for (y = 0; y < my; y++) + { + if (x >= ns->mapx || y >= ns->mapy) + /* clear cells outside the viewable area */ + memset (&newmap.cells[x][y], 0, sizeof (struct MapCell)); + else if ((x + dx) < 0 || (x + dx) >= ns->mapx || (y + dy) < 0 || (y + dy) >= ns->mapy) + /* clear newly visible tiles within the viewable area */ + memset (&(newmap.cells[x][y]), 0, sizeof (struct MapCell)); + else + memcpy (&(newmap.cells[x][y]), &(ns->lastmap.cells[x + dx][y + dy]), sizeof (struct MapCell)); + } + } + + memcpy (&(ns->lastmap), &newmap, sizeof (struct Map)); + + /* Make sure that the next "map1" command will be sent (even if it is + * empty). + */ + ns->sent_scroll = 1; +} + +static void +clear_map (player *pl) +{ + memset (&pl->ns->lastmap, 0, sizeof (pl->ns->lastmap)); + + if (pl->ns->newmapcmd == 1) + pl->ns->send_packet ("newmap"); + + pl->ns->floorbox_reset (); +} + /** check for map change and send new map data */ static void check_map_change (player *pl) { - char buf[MAX_BUF]; /* eauugggh */ - + client &socket = *pl->ns; object *ob = pl->ob; + char buf[MAX_BUF]; /* eauugggh */ - if (!pl->socket.mapinfocmd) - return; - - if (pl->socket.current_map != ob->map) + if (socket.current_map != ob->map) { - pl->socket.current_map = ob->map; + socket.current_map = ob->map; + + clear_map (pl); - if (ob->map && ob->map->path [0]) + if (socket.mapinfocmd) { - int flags = 0; + if (ob->map && ob->map->path[0]) + { + int flags = 0; - if (ob->map->tile_path [0]) flags |= 1; - if (ob->map->tile_path [1]) flags |= 2; - if (ob->map->tile_path [2]) flags |= 4; - if (ob->map->tile_path [3]) flags |= 8; - - snprintf (buf, MAX_BUF, "mapinfo - spatial %d %d %d %d %d %s", - flags, pl->socket.mapx / 2 - ob->x, pl->socket.mapy / 2 - ob->y, - ob->map->width, ob->map->height, ob->map->path); + if (ob->map->tile_path[0]) + flags |= 1; + if (ob->map->tile_path[1]) + flags |= 2; + if (ob->map->tile_path[2]) + flags |= 4; + if (ob->map->tile_path[3]) + flags |= 8; + + snprintf (buf, MAX_BUF, "mapinfo - spatial %d %d %d %d %d %s", + flags, socket.mapx / 2 - ob->x, socket.mapy / 2 - ob->y, ob->map->width, ob->map->height, ob->map->path); + } + else + snprintf (buf, MAX_BUF, "mapinfo current"); + + socket.send_packet (buf); } - else - snprintf (buf, MAX_BUF, "mapinfo current"); + } + else if (socket.current_x != ob->x || socket.current_y != ob->y) + { + int dx = ob->x - socket.current_x; + int dy = ob->y - socket.current_y; - Write_String_To_Socket (&pl->socket, buf, strlen (buf)); + if (socket.buggy_mapscroll && (abs (dx) > 8 || abs (dy) > 8)) + clear_map (pl); // current (<= 1.9.1) clients have unchecked buffer overflows + else + { + socket_map_scroll (&socket, ob->x - socket.current_x, ob->y - socket.current_y); + socket.floorbox_reset (); + } } + + socket.current_x = ob->x; + socket.current_y = ob->y; } -void ExtCmd (char *buf, int len, player *pl) +/** + * RequestInfo is sort of a meta command. There is some specific + * request of information, but we call other functions to provide + * that information. + */ +void +RequestInfo (char *buf, int len, client * ns) +{ + char *params = NULL, *cp; + + /* No match */ + char bigbuf[MAX_BUF]; + int slen; + + /* Set up replyinfo before we modify any of the buffers - this is used + * if we don't find a match. + */ + strcpy (bigbuf, "replyinfo "); + slen = strlen (bigbuf); + safe_strcat (bigbuf, buf, &slen, MAX_BUF); + + /* find the first space, make it null, and update the + * params pointer. + */ + for (cp = buf; *cp != '\0'; cp++) + if (*cp == ' ') + { + *cp = '\0'; + params = cp + 1; + break; + } + + if (!strcmp (buf, "image_info")) + send_image_info (ns, params); + else if (!strcmp (buf, "image_sums")) + send_image_sums (ns, params); + else if (!strcmp (buf, "skill_info")) + send_skill_info (ns, params); + else if (!strcmp (buf, "spell_paths")) + send_spell_paths (ns, params); + else + ns->send_packet (bigbuf, len); +} + +void +ExtCmd (char *buf, int len, player *pl) +{ + INVOKE_PLAYER (EXTCMD, pl, ARG_DATA (buf, len)); +} + +void +ExtiCmd (char *buf, int len, client *ns) { - execute_global_event (EVENT_EXTCMD, pl, buf, len); + INVOKE_CLIENT (EXTICMD, ns, ARG_DATA (buf, len)); } -void MapInfoCmd (char *buf, int len, player *pl) +void +MapInfoCmd (char *buf, int len, player *pl) { // mapinfo tag spatial flags x y w h hash - - char bigbuf[MAX_BUF], *cp, *token; + + char bigbuf[MAX_BUF], *token; token = buf; // copy token @@ -165,11 +280,11 @@ buf += 8; // initial map and its origin - mapstruct *map = pl->ob->map; + maptile *map = pl->ob->map; sint16 dx, dy; - int mapx = pl->socket.mapx / 2 - pl->ob->x; - int mapy = pl->socket.mapy / 2 - pl->ob->y; - int max_distance = 8; // limit maximum path length to something generous + int mapx = pl->ns->mapx / 2 - pl->ob->x; + int mapy = pl->ns->mapy / 2 - pl->ob->y; + int max_distance = 8; // limit maximum path length to something generous while (*buf && map && max_distance) { @@ -177,22 +292,30 @@ switch (dir) { - case '1': - dx = 0; dy = -1; map = get_map_from_coord (map, &dx, &dy); - map && (mapy -= map->height); - break; - case '2': - mapx += map->width; - dx = map->width; dy = 0; map = get_map_from_coord (map, &dx, &dy); - break; - case '3': - mapy += map->height; - dx = 0; dy = map->height; map = get_map_from_coord (map, &dx, &dy); - break; - case '4': - dx = -1; dy = 0; map = get_map_from_coord (map, &dx, &dy); - map && (mapx -= map->width); - break; + case '1': + dx = 0; + dy = -1; + map = get_map_from_coord (map, &dx, &dy); + map && (mapy -= map->height); + break; + case '2': + mapx += map->width; + dx = map->width; + dy = 0; + map = get_map_from_coord (map, &dx, &dy); + break; + case '3': + mapy += map->height; + dx = 0; + dy = map->height; + map = get_map_from_coord (map, &dx, &dy); + break; + case '4': + dx = -1; + dy = 0; + map = get_map_from_coord (map, &dx, &dy); + map && (mapx -= map->width); + break; } --max_distance; @@ -200,181 +323,243 @@ if (!max_distance) snprintf (bigbuf, MAX_BUF, "mapinfo %s error", token); - else if (map && map->path [0]) + else if (map && map->path[0]) { int flags = 0; - if (map->tile_path [0]) flags |= 1; - if (map->tile_path [1]) flags |= 2; - if (map->tile_path [2]) flags |= 4; - if (map->tile_path [3]) flags |= 8; - - snprintf (bigbuf, MAX_BUF, "mapinfo %s spatial %d %d %d %d %d %s", - token, flags, mapx, mapy, - map->width, map->height, map->path); + if (map->tile_path[0]) + flags |= 1; + if (map->tile_path[1]) + flags |= 2; + if (map->tile_path[2]) + flags |= 4; + if (map->tile_path[3]) + flags |= 8; + + snprintf (bigbuf, MAX_BUF, "mapinfo %s spatial %d %d %d %d %d %s", token, flags, mapx, mapy, map->width, map->height, map->path); } else snprintf (bigbuf, MAX_BUF, "mapinfo %s nomap", token); } else snprintf (bigbuf, MAX_BUF, "mapinfo %s unsupported", token); - - Write_String_To_Socket (&pl->socket, bigbuf, strlen (bigbuf)); + + pl->ns->send_packet (bigbuf); } /** This is the Setup cmd - easy first implementation */ -void SetUp(char *buf, int len, NewSocket *ns) +void +SetUp (char *buf, int len, client * ns) { - int s, slen; - char *cmd, *param, cmdback[HUGE_BUF]; + int s, slen; + char *cmd, *param, cmdback[HUGE_BUF]; + + /* run through the cmds of setup + * syntax is setup ... + * + * 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++; + + slen = strlen (cmdback); + safe_strcat (cmdback, " ", &slen, HUGE_BUF); + safe_strcat (cmdback, cmd, &slen, HUGE_BUF); + safe_strcat (cmdback, " ", &slen, HUGE_BUF); - /* run through the cmds of setup - * syntax is setup ... - * - * 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) - break; - - param = &buf[s]; - - for(;buf[s] && buf[s] != ' ';s++) ; - buf[s++]=0; - while (buf[s] == ' ') s++; - - slen = strlen(cmdback); - safe_strcat(cmdback, " ", &slen, HUGE_BUF); - safe_strcat(cmdback, cmd, &slen, HUGE_BUF); - safe_strcat(cmdback, " ", &slen, HUGE_BUF); - - if (!strcmp(cmd,"sound")) { - ns->sound = atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); - } - else if (!strcmp(cmd,"exp64")) { - ns->exp64 = atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); - } else if (!strcmp(cmd, "spellmon")) { - ns->monitor_spells = atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); - } else if (!strcmp(cmd,"darkness")) { - ns->darkness = atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); - } else if (!strcmp(cmd,"map1cmd")) { - if (atoi(param)) ns->mapmode = Map1Cmd; - /* if beyond this size, need to use map1cmd no matter what */ - if (ns->mapx>11 || ns->mapy>11) ns->mapmode = Map1Cmd; - safe_strcat(cmdback, ns->mapmode == Map1Cmd?"1":"0", &slen, HUGE_BUF); - } else if (!strcmp(cmd,"map1acmd")) { - if (atoi(param)) ns->mapmode = Map1aCmd; - /* if beyond this size, need to use map1acmd no matter what */ - if (ns->mapx>11 || ns->mapy>11) ns->mapmode = Map1aCmd; - safe_strcat(cmdback, ns->mapmode == Map1aCmd?"1":"0", &slen, HUGE_BUF); - } else if (!strcmp(cmd,"newmapcmd")) { - ns->newmapcmd= atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); + if (!strcmp (cmd, "sound")) + { + ns->sound = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "exp64")) + { + ns->exp64 = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "spellmon")) + { + ns->monitor_spells = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "darkness")) + { + ns->darkness = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "map1cmd")) + { + if (atoi (param)) + ns->mapmode = Map1Cmd; + /* if beyond this size, need to use map1cmd no matter what */ + if (ns->mapx > 11 || ns->mapy > 11) + ns->mapmode = Map1Cmd; + safe_strcat (cmdback, ns->mapmode == Map1Cmd ? "1" : "0", &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "map1acmd")) + { + if (atoi (param)) + ns->mapmode = Map1aCmd; + /* if beyond this size, need to use map1acmd no matter what */ + if (ns->mapx > 11 || ns->mapy > 11) + ns->mapmode = Map1aCmd; + safe_strcat (cmdback, ns->mapmode == Map1aCmd ? "1" : "0", &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "newmapcmd")) + { + ns->newmapcmd = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); // } else if (!strcmp(cmd,"plugincmd")) { // ns->plugincmd = atoi(param); -// safe_strcat(cmdback, param, &slen, HUGE_BUF); - } else if (!strcmp(cmd,"mapinfocmd")) { - ns->mapinfocmd = atoi(param); - safe_strcat(cmdback, "1", &slen, HUGE_BUF); - } else if (!strcmp(cmd,"extcmd")) { - ns->extcmd = atoi(param); - safe_strcat(cmdback, "1", &slen, HUGE_BUF); - } else if (!strcmp(cmd,"facecache")) { - ns->facecache = atoi(param); - safe_strcat(cmdback, param, &slen, HUGE_BUF); - } 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); - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - /* if the client is using faceset, it knows about image2 command */ - ns->image2=1; - } else if (!strcmp(cmd,"itemcmd")) { - /* Version of the item protocol command to use. Currently, - * only supported versions are 1 and 2. Using a numeric - * value will make it very easy to extend this in the future. - */ - char tmpbuf[20]; - int q = atoi(param); - if (q<1 || q>2) { - strcpy(tmpbuf,"FALSE"); - } else { - ns->itemcmd = q; - sprintf(tmpbuf,"%d", ns->itemcmd); - } - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - } 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); - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - } 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); - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - /* If beyond this size and still using orig map command, need to - * go to map1cmd. - */ - if ((x>11 || y>11) && ns->mapmode == Map0Cmd) ns->mapmode = Map1Cmd; - } - } else if (!strcmp(cmd,"extendedMapInfos")) { - /* Added by tchize - * prepare to use the mapextended command - */ - char tmpbuf[20]; - ns->ext_mapinfos = (atoi(param)); - sprintf(tmpbuf,"%d", ns->ext_mapinfos); - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - } else if (!strcmp(cmd,"extendedTextInfos")) { - /* Added by tchize - * prepare to use the extended text commands - * Client toggle this to non zero to get exttext - */ - char tmpbuf[20]; - - ns->has_readable_type = (atoi(param)); - sprintf(tmpbuf,"%d", ns->has_readable_type); - safe_strcat(cmdback, tmpbuf, &slen, HUGE_BUF); - } else { - /* Didn't get a setup command we understood - - * report a failure to the client. - */ - safe_strcat(cmdback, "FALSE", &slen, HUGE_BUF); - } - } /* for processing all the setup commands */ - LOG(llevInfo,"SendBack SetupCmd:: %s\n", cmdback); - Write_String_To_Socket(ns, cmdback, strlen(cmdback)); +// safe_strcat(cmdback, param, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "mapinfocmd")) + { + ns->mapinfocmd = atoi (param); + safe_strcat (cmdback, "1", &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "extcmd")) + { + ns->extcmd = atoi (param); + safe_strcat (cmdback, "1", &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "extmap")) + { + ns->extmap = atoi (param); + safe_strcat (cmdback, "1", &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "facecache")) + { + ns->facecache = atoi (param); + safe_strcat (cmdback, param, &slen, HUGE_BUF); + } + 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); + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + /* if the client is using faceset, it knows about image2 command */ + ns->image2 = 1; + } + else if (!strcmp (cmd, "itemcmd")) + { + /* Version of the item protocol command to use. Currently, + * only supported versions are 1 and 2. Using a numeric + * value will make it very easy to extend this in the future. + */ + char tmpbuf[20]; + int q = atoi (param); + + if (q < 1 || q > 2) + { + strcpy (tmpbuf, "FALSE"); + } + else + { + ns->itemcmd = q; + sprintf (tmpbuf, "%d", ns->itemcmd); + } + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + } + 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); + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + } + 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); + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + /* If beyond this size and still using orig map command, need to + * go to map1cmd. + */ + if ((x > 11 || y > 11) && ns->mapmode == Map0Cmd) + ns->mapmode = Map1Cmd; + } + } + else if (!strcmp (cmd, "extendedMapInfos")) + { + /* Added by tchize + * prepare to use the mapextended command + */ + char tmpbuf[20]; + + ns->ext_mapinfos = (atoi (param)); + sprintf (tmpbuf, "%d", ns->ext_mapinfos); + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + } + else if (!strcmp (cmd, "extendedTextInfos")) + { + /* Added by tchize + * prepare to use the extended text commands + * Client toggle this to non zero to get exttext + */ + char tmpbuf[20]; + + ns->has_readable_type = (atoi (param)); + sprintf (tmpbuf, "%d", ns->has_readable_type); + safe_strcat (cmdback, tmpbuf, &slen, HUGE_BUF); + } + else + { + /* Didn't get a setup command we understood - + * report a failure to the client. + */ + safe_strcat (cmdback, "FALSE", &slen, HUGE_BUF); + } + } /* for processing all the setup commands */ + + LOG (llevInfo, "SendBack SetupCmd:: %s\n", cmdback); + ns->send_packet (cmdback); } /** @@ -383,61 +568,65 @@ * 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) +void +AddMeCmd (char *buf, int len, client * 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; + INVOKE_CLIENT (ADDME, ns, ARG_DATA (buf, len)); } /** Reply to ExtendedInfos command */ -void ToggleExtendedInfos (char *buf, int len, NewSocket *ns){ - char cmdback[MAX_BUF]; - char command[50]; - int info,nextinfo; - cmdback[0]='\0'; - nextinfo=0; - while (1){ - /* 1. Extract an info*/ - info=nextinfo; - while ( (info=len) - break; - nextinfo=info+1; - while ( (nextinfo=49) /*Erroneous info asked*/ - continue; - strncpy (command,&(buf[info]),nextinfo-info); - command[nextinfo-info]='\0'; - /* 2. Interpret info*/ - if (!strcmp("smooth",command)){ - /* Toggle smoothing*/ - ns->EMI_smooth=!ns->EMI_smooth; - }else{ - /*bad value*/ - } - /*3. Next info*/ - } - strcpy (cmdback,"ExtendedInfoSet"); - if (ns->EMI_smooth){ - strcat (cmdback," "); - strcat (cmdback,"smoothing"); - } - Write_String_To_Socket(ns, cmdback,strlen(cmdback)); +void +ToggleExtendedInfos (char *buf, int len, client * ns) +{ + char cmdback[MAX_BUF]; + char command[50]; + int info, nextinfo; + + cmdback[0] = '\0'; + nextinfo = 0; + + while (1) + { + /* 1. Extract an info */ + info = nextinfo; + + while ((info < len) && (buf[info] == ' ')) + info++; + + if (info >= len) + break; + + nextinfo = info + 1; + + while ((nextinfo < len) && (buf[nextinfo] != ' ')) + nextinfo++; + + if (nextinfo - info >= 49) /*Erroneous info asked */ + continue; + + strncpy (command, &(buf[info]), nextinfo - info); + + /* 2. Interpret info */ + if (!strcmp ("smooth", command)) + /* Toggle smoothing */ + ns->EMI_smooth = !ns->EMI_smooth; + else + /*bad value */; + + /*3. Next info */ + } + + strcpy (cmdback, "ExtendedInfoSet"); + + if (ns->EMI_smooth) + { + strcat (cmdback, " "); + strcat (cmdback, "smoothing"); + } + + ns->send_packet (cmdback); } + /* #define MSG_TYPE_BOOK 1 #define MSG_TYPE_CARD 2 @@ -445,42 +634,60 @@ #define MSG_TYPE_SIGN 4 #define MSG_TYPE_MONUMENT 5 #define MSG_TYPE_SCRIPTED_DIALOG 6*/ + /** Reply to ExtendedInfos command */ -void ToggleExtendedText (char *buf, int len, NewSocket *ns){ - char cmdback[MAX_BUF]; - char temp[10]; - char command[50]; - int info,nextinfo,i,flag; - cmdback[0]='\0'; - nextinfo=0; - while (1){ - /* 1. Extract an info*/ - info=nextinfo; - while ( (info=len) - break; - nextinfo=info+1; - while ( (nextinfo=49) /*Erroneous info asked*/ - continue; - strncpy (command,&(buf[info]),nextinfo-info); - command[nextinfo-info]='\0'; - /* 2. Interpret info*/ - i = sscanf(command,"%d",&flag); - if ( (i==1) && (flag>0) && (flag<=MSG_TYPE_LAST)) - ns->supported_readables|=(1<supported_readables &(1<= len) + break; + + nextinfo = info + 1; + + while ((nextinfo < len) && (buf[nextinfo] != ' ')) + nextinfo++; + + if (nextinfo - info >= 49) /*Erroneous info asked */ + continue; + + strncpy (command, &(buf[info]), nextinfo - info); + command[nextinfo - info] = '\0'; + /* 2. Interpret info */ + i = sscanf (command, "%d", &flag); + + if ((i == 1) && (flag > 0) && (flag <= MSG_TYPE_LAST)) + ns->supported_readables |= (1 << flag); + /*3. Next info */ + } + + /* Send resulting state */ + strcpy (cmdback, "ExtendedTextSet"); + + for (i = 0; i <= MSG_TYPE_LAST; i++) + if (ns->supported_readables & (1 << i)) + { + strcat (cmdback, " "); + snprintf (temp, sizeof (temp), "%d", i); + strcat (cmdback, temp); + } + + ns->send_packet (cmdback); } /** @@ -490,95 +697,74 @@ * if we know the client wants it, might as well push it to the * client. */ -static void SendSmooth(NewSocket *ns, uint16 face) { - uint16 smoothface; - unsigned char reply[MAX_BUF]; - SockList sl; - - /* If we can't find a face, return and set it so we won't try to send this - * again. - */ - if ((!FindSmooth (face, &smoothface)) && - (!FindSmooth ( smooth_face->number, &smoothface))) { - - LOG(llevError,"could not findsmooth for %d. Neither default (%s)\n",face,smooth_face->name); - ns->faces_sent[face] |= NS_FACESENT_SMOOTH; - return; - } - - if (!(ns->faces_sent[smoothface] & NS_FACESENT_FACE)) - esrv_send_face(ns, smoothface, 0); - - ns->faces_sent[face] |= NS_FACESENT_SMOOTH; - - sl.buf=reply; - strcpy((char*)sl.buf,"smooth "); - sl.len=strlen((char*)sl.buf); - SockList_AddShort(&sl, face); - SockList_AddShort(&sl, smoothface); - Send_With_Handling(ns, &sl); +static void +SendSmooth (client *ns, uint16 face) +{ + uint16 smoothface; + + /* If we can't find a face, return and set it so we won't try to send this + * again. + */ + if ((!FindSmooth (face, &smoothface)) && (!FindSmooth (smooth_face->number, &smoothface))) + { + + LOG (llevError, "could not findsmooth for %d. Neither default (%s)\n", face, &smooth_face->name); + ns->faces_sent[face] |= NS_FACESENT_SMOOTH; + return; + } + + if (!(ns->faces_sent[smoothface] & NS_FACESENT_FACE)) + esrv_send_face (ns, smoothface, 0); + + ns->faces_sent[face] |= NS_FACESENT_SMOOTH; + + packet sl ("smooth"); + + sl << uint16 (face) + << uint16 (smoothface); + + ns->send_packet (sl); } /** * Tells client the picture it has to use * to smooth a picture number given as argument. */ -void AskSmooth (char *buf, int len, NewSocket *ns){ - uint16 facenbr; - - facenbr=atoi (buf); - SendSmooth(ns, facenbr); +void +AskSmooth (char *buf, int len, client *ns) +{ + SendSmooth (ns, atoi (buf)); } - - - - /** * This handles the general commands from the client (ie, north, fire, cast, * etc.) */ -void PlayerCmd(char *buf, int len, player *pl) +void +PlayerCmd (char *buf, int len, player *pl) { + /* 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); + + buf = strchr (buf, ' '); /* advance beyond the numbers */ + if (!buf) + return; - /* 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); - buf=strchr(buf,' '); /* advance beyond the numbers */ - if (!buf) { -#ifdef ESRV_DEBUG - LOG(llevDebug,"PlayerCmd: Got count but no command.\n"); -#endif - return; - } - buf++; - } - /* 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. - */ + buf++; + } - pl->count=0; + 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 = 0; } @@ -588,126 +774,92 @@ * 'ncom' method which gives more information back to the client so it * can throttle. */ -void NewPlayerCmd(uint8 *buf, int len, player *pl) +void +NewPlayerCmd (char *buf, int len, player *pl) { - int time,repeat; - short packet; - unsigned char command[MAX_BUF]; - SockList sl; - - if (len < 7) { - LOG(llevDebug,"Corrupt ncom command <%s> not long enough - discarding\n", buf); - 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; - } - if ((len-4) >= MAX_BUF) len=MAX_BUF-5; - - strncpy((char*)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; - } - - /* 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*)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=0; - - /* Send confirmation of command execution now */ - sl.buf = 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 = ( int )( MAX_TIME/ FABS(pl->ob->speed) ); - SockList_AddInt(&sl,time); - Send_With_Handling(&pl->socket, &sl); -} + if (len <= 6) + { + LOG (llevDebug, "Corrupt ncom command <%s> not long enough - discarding\n", buf); + return; + } + + uint16 cmdid = net_uint16 ((uint8 *)buf); + sint32 repeat = net_sint32 ((uint8 *)buf + 2); + + /* -1 is special - no repeat, but don't update */ + if (repeat != -1) + pl->count = repeat; + + buf += 6; //len -= 6; + execute_newserver_command (pl->ob, 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 = 0; + + //TODO: schmorp thinks whatever this calculates, it makes no sense at all + int time = FABS (pl->ob->speed) < 0.001 + ? time = MAX_TIME * 100 + : time = (int) (MAX_TIME / FABS (pl->ob->speed)); + + /* Send confirmation of command execution now */ + packet sl ("comc"); + sl << uint16 (cmdid) << uint32 (time); + pl->ns->send_packet (sl); +} /** This is a reply to a previous query. */ -void ReplyCmd(char *buf, int len, player *pl) +void +ReplyCmd (char *buf, int len, client *ns) { - /* 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. - */ - snprintf(pl->write_buf, sizeof(pl->write_buf), ":%s", buf); - - /* 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; + if (ns->state == ST_CUSTOM) + { + INVOKE_CLIENT (REPLY, ns, ARG_DATA (buf, len)); + return; + } + + if (!ns->pl) + return; //TODO: depends on the exact reply we are after + //TODO: but right now, we always have a ns->pl + + player *pl = ns->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. + */ + snprintf (pl->write_buf, sizeof (pl->write_buf), ":%s", buf); + + /* this avoids any hacking here */ - case ST_CHANGE_CLASS: + switch (ns->state) + { + case ST_PLAYING: + LOG (llevError, "Got reply message with ST_PLAYING input state\n"); + break; - key_change_class(pl->ob, buf[0]); + 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; + case ST_CONFIRM_QUIT: + key_confirm_quit (pl->ob, buf[0]); + break; - default: - LOG(llevError,"Unknown input state: %d\n", pl->state); + case ST_GET_PARTY_PASSWORD: /* Get password for party */ + receive_party_password (pl->ob, 13); + break; + + default: + LOG (llevError, "Unknown input state: %d\n", ns->state); } } @@ -718,119 +870,99 @@ * backwards compatible, having it be a later version should not be a * problem. */ -void VersionCmd(char *buf, int len,NewSocket *ns) +void +VersionCmd (char *buf, int len, client * 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>, ip %s\n", cp, ns->host); - - /* This is first implementation - i skip all beta DX clients with it - * Add later stuff here for other clients - */ - - /* these are old dxclients */ - /* Version 1024 added support for singular + plural name values - - * requiing this minimal value reduces complexity of that code, and it - * has been around for a long time. - */ - if(!strcmp(" CF DX CLIENT", cp) || ns->sc_version < 1024 ) - { - sprintf(version_warning,"drawinfo %d %s", NDI_RED, "**** VERSION WARNING ****\n**** CLIENT IS TOO OLD!! UPDATE THE CLIENT!! ****"); - Write_String_To_Socket(ns, version_warning, strlen(version_warning)); - } + if (!buf) + { + LOG (llevError, "CS: received corrupted version command\n"); + return; + } + ns->cs_version = atoi (buf); + ns->sc_version = ns->cs_version; + + LOG (llevDebug, "connection from client <%s>\n", buf); + + + //TODO: should log here just for statistics + + //if (VERSION_CS != ns->cs_version) + // unchecked; + + char *cp = strchr (buf + 1, ' '); + if (!cp) + return; + + ns->sc_version = atoi (cp); + + //if (VERSION_SC != ns->sc_version) + // unchecked; + + cp = strchr (cp + 1, ' '); + + if (cp) + { + assign (ns->version, cp + 1); + + if (ns->sc_version < 1026) + ns->send_packet_printf ("drawinfo %d %s", NDI_RED, + "**** VERSION WARNING ****\n**** CLIENT IS TOO OLD!! UPDATE THE CLIENT!! ****"); } } /** sound related functions. */ - -void SetSound(char *buf, int len, NewSocket *ns) +void +SetSound (char *buf, int len, client * ns) { - ns->sound = atoi(buf); + ns->sound = atoi (buf); } /** client wants the map resent */ -void MapRedrawCmd(char *buf, int len, player *pl) +void +MapRedrawCmd (char *buf, int len, player *pl) { /* This function is currently disabled; just clearing the map state results in * display errors. It should clear the cache and send a newmap command. * Unfortunately this solution does not work because some client versions send * a mapredraw command after receiving a newmap command. */ -#if 0 - /* 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); -#endif -} - -/** Newmap command */ -void MapNewmapCmd( player *pl) -{ - if( pl->socket.newmapcmd == 1) { - memset(&pl->socket.lastmap, 0, sizeof(pl->socket.lastmap)); - Write_String_To_Socket( &pl->socket, "newmap", 6); - } - pl->socket.current_map = 0; } - - /** * Moves an object (typically, container to inventory). * syntax is: move (to) (tag) (nrof) */ -void MoveCmd(char *buf, int len,player *pl) +void +MoveCmd (char *buf, int len, player *pl) { - int vals[3], i; + 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++; + /* A little funky here. We only cycle for 2 records, because + * we obviously are 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); + + 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]); + esrv_move_object (pl->ob, vals[0], vals[1], vals[2]); } - - /****************************************************************************** * * Start of commands the server sends to the client. @@ -841,46 +973,36 @@ * 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) +void +send_query (client *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)); + ns->send_packet_printf ("query %d %s", flags, text ? text : ""); } #define AddIfInt64(Old,New,Type) if (Old != New) {\ - Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt64(&sl, New); \ - } + Old = New; \ + sl << uint8 (Type) << uint64 (New); \ + } #define AddIfInt(Old,New,Type) if (Old != New) {\ - Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt(&sl, New); \ - } + Old = New; \ + sl << uint8 (Type) << uint32 (New); \ + } #define AddIfShort(Old,New,Type) if (Old != New) {\ - Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddShort(&sl, New); \ - } + Old = New; \ + sl << uint8 (Type) << uint16 (New); \ + } #define AddIfFloat(Old,New,Type) if (Old != New) {\ - Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt(&sl,(long)(New*FLOAT_MULTI));\ - } + Old = New; \ + sl << uint8 (Type) << uint32 (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, ( char )strlen(New)); \ - strcpy((char*)sl.buf + sl.len, New); \ - sl.len += strlen(New); \ - } + free(Old); Old = strdup (New);\ + sl << uint8 (Type) << data8 (New); \ + } /** * Sends a statistics update. We look at the old values, @@ -888,125 +1010,116 @@ * Since this gets sent a lot, this is actually one of the few binary * commands for now. */ -void esrv_update_stats(player *pl) +void +esrv_update_stats (player *pl) { - SockList sl; - char buf[MAX_BUF]; - uint16 flags; - - sl.buf=(unsigned char*)malloc(MAXSOCKBUF); - 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->socket.exp64) { - uint8 s; - for(s=0;slast_skill_ob[s] && - pl->last_skill_exp[s] != pl->last_skill_ob[s]->stats.exp) { - - /* Always send along the level if exp changes. This is only - * 1 extra byte, but keeps processing simpler. - */ - SockList_AddChar(&sl, ( char )( s + CS_STAT_SKILLINFO )); - SockList_AddChar(&sl, ( char )pl->last_skill_ob[s]->level); - SockList_AddInt64(&sl, pl->last_skill_ob[s]->stats.exp); - pl->last_skill_exp[s] = pl->last_skill_ob[s]->stats.exp; - } - } - } - if (pl->socket.exp64) { - AddIfInt64(pl->last_stats.exp, pl->ob->stats.exp, CS_STAT_EXP64); - } else { - AddIfInt(pl->last_stats.exp, ( int )pl->ob->stats.exp, CS_STAT_EXP); - } - AddIfShort(pl->last_level, ( char )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, (sint32)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; ilast_resist[i], pl->ob->resist[i], ( char )atnr_cs_stat[i]); - } - } - if (pl->socket.monitor_spells) { - AddIfInt(pl->last_path_attuned, pl->ob->path_attuned, CS_STAT_SPELL_ATTUNE); - AddIfInt(pl->last_path_repelled, pl->ob->path_repelled, CS_STAT_SPELL_REPEL); - AddIfInt(pl->last_path_denied, pl->ob->path_denied, CS_STAT_SPELL_DENY); - } - 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); + char buf[MAX_BUF]; + uint16 flags; + + packet sl ("stats"); + + if (pl->ob) + { + 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.Dex, pl->ob->stats.Dex, CS_STAT_DEX); + AddIfShort (pl->last_stats.Con, pl->ob->stats.Con, CS_STAT_CON); + AddIfShort (pl->last_stats.Int, pl->ob->stats.Int, CS_STAT_INT); + AddIfShort (pl->last_stats.Wis, pl->ob->stats.Wis, CS_STAT_WIS); + AddIfShort (pl->last_stats.Pow, pl->ob->stats.Pow, CS_STAT_POW); + AddIfShort (pl->last_stats.Cha, pl->ob->stats.Cha, CS_STAT_CHA); + } + + if (pl->ns->exp64) + for (int s = 0; s < NUM_SKILLS; s++) + if (pl->last_skill_ob[s] && pl->last_skill_exp[s] != pl->last_skill_ob[s]->stats.exp) + { + /* Always send along the level if exp changes. This is only + * 1 extra byte, but keeps processing simpler. + */ + sl << uint8 (s + CS_STAT_SKILLINFO) + << uint8 (pl->last_skill_ob[s]->level) + << uint64 (pl->last_skill_ob[s]->stats.exp); + + pl->last_skill_exp[s] = pl->last_skill_ob[s]->stats.exp; + } + + if (pl->ns->exp64) + { AddIfInt64 (pl->last_stats.exp, pl->ob->stats.exp, CS_STAT_EXP64) } + else + { AddIfInt (pl->last_stats.exp, (int) pl->ob->stats.exp, CS_STAT_EXP) } + + AddIfShort (pl->last_level, (char) 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, (sint32) 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->ns->sc_version < 1025) + { AddIfShort (pl->last_resist[ATNR_PHYSICAL], pl->ob->resist[ATNR_PHYSICAL], CS_STAT_ARMOUR) } + else + for (int 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], (char) atnr_cs_stat[i]); + } + + if (pl->ns->monitor_spells) + { + AddIfInt (pl->last_path_attuned, pl->ob->path_attuned, CS_STAT_SPELL_ATTUNE); + AddIfInt (pl->last_path_repelled, pl->ob->path_repelled, CS_STAT_SPELL_REPEL); + AddIfInt (pl->last_path_denied, pl->ob->path_denied, CS_STAT_SPELL_DENY); } - free(sl.buf); -} + rangetostring (pl->ob, buf); /* we want use the new fire & run system in new client */ + AddIfString (pl->ns->stats.range, buf, CS_STAT_RANGE); + set_title (pl->ob, buf); + AddIfString (pl->ns->stats.title, buf, CS_STAT_TITLE); + + /* Only send it away if we have some actual data */ + if (sl.length () > 6) + pl->ns->send_packet (sl); +} /** * Tells the client that here is a player it should start using. */ -void esrv_new_player(player *pl, uint32 weight) +void +esrv_new_player (player *pl, uint32 weight) { - SockList sl; + pl->last_weight = weight; - pl->last_weight = weight; + packet sl ("player"); - sl.buf=(unsigned char*)malloc(MAXSOCKBUF); + sl << uint32 (pl->ob->count) + << uint32 (weight) + << uint32 (pl->ob->face->number) + << data8 (pl->ob->name); - 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, ( char )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); + pl->ns->send_packet (sl); + 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 @@ -1014,36 +1127,38 @@ * 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) +void +esrv_send_animation (client * 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; + } + + packet sl ("anim"); + + sl << uint16 (anim_num) + << uint16 (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 (int i = 0; i < animations[anim_num].num_animations; i++) + { + if (!(ns->faces_sent[animations[anim_num].faces[i]] & NS_FACESENT_FACE)) + esrv_send_face (ns, animations[anim_num].faces[i], 0); + + sl << uint16 (animations[anim_num].faces[i]); /* flags - not used right now */ + } + + ns->send_packet (sl); - /* 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 = (unsigned char*) 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; ifaces_sent[animations[anim_num].faces[i]] & NS_FACESENT_FACE)) - 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; + ns->anims_sent[anim_num] = 1; } @@ -1057,157 +1172,35 @@ * 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(newmap->cells[x][y].count >= MAP_LAYERS) { - LOG(llevError,"Too many faces in map cell %d %d\n",x,y); - return; - abort(); - } - newmap->cells[x][y].faces[newmap->cells[x][y].count] = face_num; - newmap->cells[x][y].count ++; - if (!(ns->faces_sent[face_num] & NS_FACESENT_FACE)) - 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]; -}; - -/** Checkes if map cells have changed */ -static int mapcellchanged(NewSocket *ns,int i,int j, struct Map *newmap) +static void +esrv_map_setbelow (client * ns, int x, int y, short face_num, struct Map *newmap) { - int k; - - if (ns->lastmap.cells[i][j].count != newmap->cells[i][j].count) - return 1; - for(k=0;kcells[i][j].count;k++) { - if (ns->lastmap.cells[i][j].faces[k] != - newmap->cells[i][j].faces[k]) { - return 1; + if (newmap->cells[x][y].count >= MAP_LAYERS) + { + //TODO: one or the other, can't both have abort and return, verify and act + LOG (llevError, "Too many faces in map cell %d %d\n", x, y); + return; + abort (); } - } - return 0; -} -/** - * Basically, what this does is pack the data into layers. - * 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. - */ -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[MAP_LAYERS]; - - for(k = 0;kmapx;x++) { - for(y=0;ymapy;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;kcells[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> 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( int )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); -} + newmap->cells[x][y].faces[newmap->cells[x][y].count] = face_num; + newmap->cells[x][y].count++; + if (!(ns->faces_sent[face_num] & NS_FACESENT_FACE)) + esrv_send_face (ns, face_num, 0); +} /** Clears a map cell */ -static void map_clearcell(struct MapCell *cell, int face0, int face1, int face2, int count) +static void +map_clearcell (struct MapCell *cell, int face0, int face1, int face2, int count) { - cell->count=count; - cell->faces[0] = face0; - cell->faces[1] = face1; - cell->faces[2] = face2; + cell->faces[0] = face0; + cell->faces[1] = face1; + cell->faces[2] = face2; + cell->count = count; + cell->stat_hp = 0; + cell->flags = 0; + cell->player = 0; } #define MAX_HEAD_POS MAX(MAX_CLIENT_X, MAX_CLIENT_Y) @@ -1216,51 +1209,55 @@ /* Using a global really isn't a good approach, but saves the over head of * allocating and deallocating such a block of data each time run through, * and saves the space of allocating this in the socket object when we only - * need it for this cycle. If the serve is ever threaded, this needs to be + * need it for this cycle. If the server is ever threaded, this needs to be * re-examined. */ - -static object *heads[MAX_HEAD_POS * MAX_HEAD_POS * MAX_LAYERS]; +static object *heads[MAX_HEAD_POS * MAX_HEAD_POS * MAX_LAYERS]; /** * Returns true if any of the heads for this * space is set. Returns false if all are blank - this is used * for empty space checking. */ -static inline int have_head(int ax, int ay) { +static inline int +have_head (int ax, int ay) +{ + if (heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS] || + heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + 1] || heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + 2]) + return 1; - if (heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS] || - heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + 1] || - heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + 2]) return 1; - return 0; + return 0; } /** * check_head is a bit simplistic version of update_space below. * basically, it only checks the that the head on space ax,ay at layer * needs to get sent - if so, it adds the data, sending the head - * if needed, and returning 1. If this no data needs to get + * if needed, and returning 1. If this no data needs to get * sent, it returns zero. */ -static inline int check_head(SockList *sl, NewSocket *ns, int ax, int ay, int layer) +static int +check_head (packet &sl, client &ns, int ax, int ay, int layer) { - short face_num; + short face_num; - if (heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer]) - face_num = heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer]->face->number; - else - face_num = 0; + if (heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer]) + face_num = heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer]->face->number; + else + face_num = 0; - if (face_num != ns->lastmap.cells[ax][ay].faces[layer]) { - SockList_AddShort(sl, face_num); - if (face_num && !(ns->faces_sent[face_num] & NS_FACESENT_FACE)) - esrv_send_face(ns, face_num, 0); - heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer] = NULL; - ns->lastmap.cells[ax][ay].faces[layer] = face_num; - return 1; + if (face_num != ns.lastmap.cells[ax][ay].faces[layer]) + { + sl << uint16 (face_num); + if (face_num && !(ns.faces_sent[face_num] & NS_FACESENT_FACE)) + esrv_send_face (&ns, face_num, 0); + + heads[(ay * MAX_HEAD_POS + ax) * MAX_LAYERS + layer] = NULL; + ns.lastmap.cells[ax][ay].faces[layer] = face_num; + return 1; } - return 0; /* No change */ + return 0; /* No change */ } /** @@ -1282,185 +1279,208 @@ * the case, it seems to make more sense to have these layer values * actually match. */ - -static inline int update_space(SockList *sl, NewSocket *ns, mapstruct *mp, int mx, int my, int sx, int sy, int layer) +static int +update_space (packet &sl, client &ns, maptile *mp, int mx, int my, int sx, int sy, int layer) { - object *ob, *head; - uint16 face_num; - int bx, by,i; - - /* If there is a multipart object stored away, treat that as more important. - * If not, then do the normal processing. - */ - - head = heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer]; - - /* Check to see if this head is part of the set of objects - * we would normally send for this space. If so, then - * don't use the head value. We need to do the check - * here and not when setting up the heads[] value for two reasons - - * 1) the heads[] values will get used even if the space is not visible. - * 2) its possible the head is not on the same map as a part, and I'd - * rather not need to do the map translation overhead. - * 3) We need to do some extra checking to make sure that we will - * otherwise send the image as this layer, eg, either it matches - * the head value, or is not multipart. - */ - if (head && !head->more) { - for (i=0; ihead) ob=ob->head; - - if (ob == head) { - heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer] = NULL; - head = NULL; - break; - } - } - } - - ob = head; - if (!ob) ob = GET_MAP_FACE_OBJ(mp, mx, my, layer); - - /* If there is no object for this space, or if the face for the object - * is the blank face, set the face number to zero. - * else if we have the stored head object for this space, that takes - * precedence over the other object for this space. - * otherwise, we do special head processing - */ - if (!ob || ob->face == blank_face) face_num=0; - else if (head){ - /* if this is a head that had previously been stored */ - face_num = ob->face->number; - } else { - /* if the faces for the different parts of a multipart object - * are the same, we only want to send the bottom right most - * portion of the object. That info is in the tail_.. values - * of the head. Note that for the head itself, ob->head will - * be null, so we only do this block if we are working on - * a tail piece. - */ - - /* tail_x and tail_y will only be set in the head object. If - * this is the head object and these are set, we proceed - * with logic to only send bottom right. Similarly, if - * this is one of the more parts but the head has those values - * set, we want to do the processing. There can be cases where - * the head is not visible but one of its parts is, so we just - * can always expect that ob->arch->tail_x will be true for all - * object we may want to display. - */ - if ((ob->arch->tail_x || ob->arch->tail_y) || - (ob->head && (ob->head->arch->tail_x || ob->head->arch->tail_y))) { - - if (ob->head) head = ob->head; - else head = ob; - - /* Basically figure out where the offset is from where we are right - * now. the ob->arch->clone.{x,y} values hold the offset that this current - * piece is from the head, and the tail is where the tail is from the - * head. Note that bx and by will equal sx and sy if we are already working - * on the bottom right corner. If ob is the head, the clone values - * will be zero, so the right thing will still happen. - */ - bx = sx + head->arch->tail_x - ob->arch->clone.x; - by = sy + head->arch->tail_y - ob->arch->clone.y; - - /* I don't think this can ever happen, but better to check for it just - * in case. - */ - if (bx < sx || by < sy) { - LOG(llevError,"update_space: bx (%d) or by (%d) is less than sx (%d) or sy (%d)\n", - bx, by, sx, sy); - face_num = 0; - } - /* single part object, multipart object with non merged faces, - * of multipart object already at lower right. - */ - else if (bx == sx && by == sy) { - face_num = ob->face->number; - - /* if this face matches one stored away, clear that one away. - * this code relies on the fact that the map1 commands - * goes from 2 down to 0. - */ - for (i=0; iface->number == face_num) - heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + i] = NULL; - } - else { - /* If this head is stored away, clear it - otherwise, - * there can be cases where a object is on multiple layers - - * we only want to send it once. - */ - face_num = head->face->number; - for (i=0; iface->number == face_num) - heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] = NULL; - - /* First, try to put the new head on the same layer. If that is used up, - * then find another layer. - */ - if (heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + layer] == NULL) { - heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + layer] = head; - } else for (i=0; iface->number; - - /* clear out any head entries that have the same face as this one */ - for (bx=0; bxface->number == face_num) - heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + bx] = NULL; - } - } /* else not already head object or blank face */ - - /* This is a real hack. Basically, if we have nothing to send for this layer, - * but there is a head on the next layer, send that instead. - * Without this, what happens is you can get the case where the player stands - * on the same space as the head. However, if you have overlapping big objects - * of the same type, what happens then is it doesn't think it needs to send - * This tends to make stacking also work/look better. - */ - if (!face_num && layer > 0 && heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer -1]) { - face_num = heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer -1]->face->number; - heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer -1] = NULL; - } - - /* Another hack - because of heads and whatnot, this face may match one - * we already sent for a lower layer. In that case, don't send - * this one. - */ - if (face_num && layer+1lastmap.cells[sx][sy].faces[layer+1] == face_num) { - face_num = 0; - } - - /* We've gotten what face we want to use for the object. Now see if - * if it has changed since we last sent it to the client. - */ - if (ns->lastmap.cells[sx][sy].faces[layer] != face_num) { - ns->lastmap.cells[sx][sy].faces[layer] = face_num; - if (!(ns->faces_sent[face_num] & NS_FACESENT_FACE)) - esrv_send_face(ns, face_num, 0); - SockList_AddShort(sl, face_num); - return 1; + object *ob, *head; + uint16 face_num; + int bx, by, i; + + /* If there is a multipart object stored away, treat that as more important. + * If not, then do the normal processing. + */ + head = heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer]; + + /* Check to see if this head is part of the set of objects + * we would normally send for this space. If so, then + * don't use the head value. We need to do the check + * here and not when setting up the heads[] value for two reasons - + * 1) the heads[] values will get used even if the space is not visible. + * 2) its possible the head is not on the same map as a part, and I'd + * rather not need to do the map translation overhead. + * 3) We need to do some extra checking to make sure that we will + * otherwise send the image as this layer, eg, either it matches + * the head value, or is not multipart. + */ + if (head && !head->more) + { + for (i = 0; i < MAP_LAYERS; i++) + { + ob = GET_MAP_FACE_OBJ (mp, mx, my, i); + if (!ob) + continue; + + if (ob->head) + ob = ob->head; + + if (ob == head) + { + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer] = NULL; + head = NULL; + break; + } + } + } + + ob = head; + if (!ob) + ob = GET_MAP_FACE_OBJ (mp, mx, my, layer); + + /* If there is no object for this space, or if the face for the object + * is the blank face, set the face number to zero. + * else if we have the stored head object for this space, that takes + * precedence over the other object for this space. + * otherwise, we do special head processing + */ + if (!ob || ob->face == blank_face) + face_num = 0; + else if (head) + { + /* if this is a head that had previously been stored */ + face_num = ob->face->number; } - /* Nothing changed */ - return 0; + else + { + /* if the faces for the different parts of a multipart object + * are the same, we only want to send the bottom right most + * portion of the object. That info is in the tail_.. values + * of the head. Note that for the head itself, ob->head will + * be null, so we only do this block if we are working on + * a tail piece. + */ + + /* tail_x and tail_y will only be set in the head object. If + * this is the head object and these are set, we proceed + * with logic to only send bottom right. Similarly, if + * this is one of the more parts but the head has those values + * set, we want to do the processing. There can be cases where + * the head is not visible but one of its parts is, so we just + * can always expect that ob->arch->tail_x will be true for all + * object we may want to display. + */ + if ((ob->arch->tail_x || ob->arch->tail_y) || (ob->head && (ob->head->arch->tail_x || ob->head->arch->tail_y))) + { + + if (ob->head) + head = ob->head; + else + head = ob; + + /* Basically figure out where the offset is from where we are right + * now. the ob->arch->clone.{x,y} values hold the offset that this current + * piece is from the head, and the tail is where the tail is from the + * head. Note that bx and by will equal sx and sy if we are already working + * on the bottom right corner. If ob is the head, the clone values + * will be zero, so the right thing will still happen. + */ + bx = sx + head->arch->tail_x - ob->arch->clone.x; + by = sy + head->arch->tail_y - ob->arch->clone.y; + + /* I don't think this can ever happen, but better to check for it just + * in case. + */ + if (bx < sx || by < sy) + { + LOG (llevError, "update_space: bx (%d) or by (%d) is less than sx (%d) or sy (%d)\n", bx, by, sx, sy); + face_num = 0; + } + /* single part object, multipart object with non merged faces, + * of multipart object already at lower right. + */ + else if (bx == sx && by == sy) + { + face_num = ob->face->number; + + /* if this face matches one stored away, clear that one away. + * this code relies on the fact that the map1 commands + * goes from 2 down to 0. + */ + for (i = 0; i < MAP_LAYERS; i++) + if (heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + i] && + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + i]->face->number == face_num) + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + i] = NULL; + } + else + { + /* If this head is stored away, clear it - otherwise, + * there can be cases where a object is on multiple layers - + * we only want to send it once. + */ + face_num = head->face->number; + for (i = 0; i < MAP_LAYERS; i++) + if (heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] && + heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i]->face->number == face_num) + heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] = NULL; + + /* First, try to put the new head on the same layer. If that is used up, + * then find another layer. + */ + if (heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + layer] == NULL) + { + heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + layer] = head; + } + else + for (i = 0; i < MAX_LAYERS; i++) + { + if (heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] == NULL || + heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] == head) + { + heads[(by * MAX_HEAD_POS + bx) * MAX_LAYERS + i] = head; + } + } + face_num = 0; /* Don't send this object - we'll send the head later */ + } + } + else + { + /* In this case, we are already at the lower right or single part object, + * so nothing special + */ + face_num = ob->face->number; + + /* clear out any head entries that have the same face as this one */ + for (bx = 0; bx < layer; bx++) + if (heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + bx] && + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + bx]->face->number == face_num) + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + bx] = NULL; + } + } /* else not already head object or blank face */ + + /* This is a real hack. Basically, if we have nothing to send for this layer, + * but there is a head on the next layer, send that instead. + * Without this, what happens is you can get the case where the player stands + * on the same space as the head. However, if you have overlapping big objects + * of the same type, what happens then is it doesn't think it needs to send + * This tends to make stacking also work/look better. + */ + if (!face_num && layer > 0 && heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer - 1]) + { + face_num = heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer - 1]->face->number; + heads[(sy * MAX_HEAD_POS + sx) * MAX_LAYERS + layer - 1] = NULL; + } + + /* Another hack - because of heads and whatnot, this face may match one + * we already sent for a lower layer. In that case, don't send + * this one. + */ + if (face_num && layer + 1 < MAP_LAYERS && ns.lastmap.cells[sx][sy].faces[layer + 1] == face_num) + face_num = 0; + + /* We've gotten what face we want to use for the object. Now see if + * if it has changed since we last sent it to the client. + */ + if (ns.lastmap.cells[sx][sy].faces[layer] != face_num) + { + ns.lastmap.cells[sx][sy].faces[layer] = face_num; + if (!(ns.faces_sent[face_num] & NS_FACESENT_FACE)) + esrv_send_face (&ns, face_num, 0); + + sl << uint16 (face_num); + return 1; + } + + /* Nothing changed */ + return 0; } /** @@ -1482,37 +1502,43 @@ * take. */ -static inline int update_smooth(SockList *sl, NewSocket *ns, mapstruct *mp, int mx, int my, int sx, int sy, int layer) +static inline int +update_smooth (packet &sl, client &ns, maptile *mp, int mx, int my, int sx, int sy, int layer) { - object *ob; - int smoothlevel; /* old face_num;*/ + object *ob; + int smoothlevel; /* old face_num; */ - ob = GET_MAP_FACE_OBJ(mp, mx, my, layer); + ob = GET_MAP_FACE_OBJ (mp, mx, my, layer); - /* If there is no object for this space, or if the face for the object - * is the blank face, set the smoothlevel to zero. - */ - if (!ob || ob->face == blank_face || MAP_NOSMOOTH(mp)) smoothlevel=0; - else { - smoothlevel = ob->smoothlevel; - if (smoothlevel && !(ns->faces_sent[ob->face->number] & NS_FACESENT_SMOOTH)) - SendSmooth(ns, ob->face->number); - } /* else not already head object or blank face */ - - /* We've gotten what face we want to use for the object. Now see if - * if it has changed since we last sent it to the client. - */ - if (smoothlevel>255) - smoothlevel=255; - else if (smoothlevel<0) - smoothlevel=0; - if (ns->lastmap.cells[sx][sy].smooth[layer] != smoothlevel) { - ns->lastmap.cells[sx][sy].smooth[layer] = smoothlevel; - SockList_AddChar(sl, (uint8) (smoothlevel&0xFF)); - return 1; + /* If there is no object for this space, or if the face for the object + * is the blank face, set the smoothlevel to zero. + */ + if (!ob || ob->face == blank_face || MAP_NOSMOOTH (mp)) + smoothlevel = 0; + else + { + smoothlevel = ob->smoothlevel; + if (smoothlevel && !(ns.faces_sent[ob->face->number] & NS_FACESENT_SMOOTH)) + SendSmooth (&ns, ob->face->number); + } /* else not already head object or blank face */ + + /* We've gotten what face we want to use for the object. Now see if + * if it has changed since we last sent it to the client. + */ + if (smoothlevel > 255) + smoothlevel = 255; + else if (smoothlevel < 0) + smoothlevel = 0; + + if (ns.lastmap.cells[sx][sy].smooth[layer] != smoothlevel) + { + ns.lastmap.cells[sx][sy].smooth[layer] = smoothlevel; + sl << uint8 (smoothlevel); + return 1; } - /* Nothing changed */ - return 0; + + /* Nothing changed */ + return 0; } /** @@ -1520,14 +1546,20 @@ * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries * available. */ -int getExtendedMapInfoSize(NewSocket* ns){ - int result=0; - if (ns->ext_mapinfos){ - if (ns->EMI_smooth) - result+=1; /*One byte for smoothlevel*/ +int +getExtendedMapInfoSize (client * ns) +{ + int result = 0; + + if (ns->ext_mapinfos) + { + if (ns->EMI_smooth) + result += 1; /*One byte for smoothlevel */ } - return result; + + return result; } + /** * This function uses the new map1 protocol command to send the map * to the client. It is necessary because the old map command supports @@ -1541,7 +1573,7 @@ * 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. + * the ns->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. @@ -1550,702 +1582,686 @@ * we use faces[0] faces[1] faces[2] to hold what the three layers * look like. */ -void draw_client_map1(object *pl) +void +draw_client_map1 (object *pl) { - int x,y,ax, ay, d, startlen, max_x, max_y, oldlen; - sint16 nx, ny; - int estartlen, eoldlen; - SockList sl; - SockList esl; /*For extended Map info*/ - uint16 mask,emask; - uint8 eentrysize; - uint16 ewhatstart,ewhatflag; - uint8 extendedinfos; - mapstruct *m; - - sl.buf=(unsigned char*)malloc(MAXSOCKBUF); - if (pl->contr->socket.mapmode == Map1Cmd) - strcpy((char*)sl.buf,"map1 "); - else - strcpy((char*)sl.buf,"map1a "); - sl.len=strlen((char*)sl.buf); - startlen = sl.len; - /*Extendedmapinfo structure initialisation*/ - if (pl->contr->socket.ext_mapinfos){ - esl.buf=(unsigned char*)malloc(MAXSOCKBUF); - strcpy((char*)esl.buf,"mapextended "); - esl.len=strlen((char*)esl.buf); - extendedinfos=EMI_NOREDRAW; - if (pl->contr->socket.EMI_smooth) - extendedinfos|=EMI_SMOOTH; - ewhatstart=esl.len; - ewhatflag=extendedinfos; /*The EMI_NOREDRAW bit - could need to be taken away*/ - SockList_AddChar(&esl, extendedinfos); - eentrysize=getExtendedMapInfoSize(&(pl->contr->socket)); - SockList_AddChar(&esl, eentrysize); - estartlen = esl.len; - } else { - /* suppress compiler warnings */ - ewhatstart = 0; - ewhatflag = 0; - estartlen = 0; - } - /* Init data to zero */ - memset(heads, 0, sizeof(object *) * MAX_HEAD_POS * MAX_HEAD_POS * MAX_LAYERS); - - /* x,y are the real map locations. ax, ay are viewport relative - * locations. - */ - ay=0; - - /* We could do this logic as conditionals in the if statement, - * but that started to get a bit messy to look at. - */ - max_x = pl->x+(pl->contr->socket.mapx+1)/2; - max_y = pl->y+(pl->contr->socket.mapy+1)/2; - if (pl->contr->socket.mapmode == Map1aCmd) { - max_x += MAX_HEAD_OFFSET; - max_y += MAX_HEAD_OFFSET; - } - - for(y=pl->y-pl->contr->socket.mapy/2; yx-pl->contr->socket.mapx/2;x= pl->contr->socket.mapx || ay >= pl->contr->socket.mapy) { - int i, got_one; - - oldlen = sl.len; - - - SockList_AddShort(&sl, mask); - - if (check_head(&sl, &pl->contr->socket, ax, ay, 2)) - mask |= 0x4; - if (check_head(&sl, &pl->contr->socket, ax, ay, 1)) - mask |= 0x2; - if (check_head(&sl, &pl->contr->socket, ax, ay, 0)) - mask |= 0x1; - - /* If all we are doing is sending 0 (blank) faces, we don't - * actually need to send that - just the coordinates - * with no faces tells the client to blank out the - * space. - */ - got_one=0; - for (i=oldlen+2; icontr->blocked_los[ax][ay]; - - /* 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 the space is out - * of the map, it shouldn't have a head - */ - if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) { - SockList_AddShort(&sl, mask); - map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay],0,0,0,-1); - } - } else if (d>3) { - int need_send=0, count; - /* This block deals with spaces that are not visible for whatever - * reason. Still may need to send the head for this space. - */ - - oldlen = sl.len; -#if 0 - /* First thing we do is blank out this space (clear it) - * if not already done. If the client is using darkness, and - * this space is at the edge, we also include the darkness. - */ - if (d==4) { - if (pl->contr->socket.darkness && pl->contr->socket.lastmap.cells[ax][ay].count != d) { - mask |= 8; - SockList_AddShort(&sl, mask); - SockList_AddChar(&sl, 0); - } - count = d; - } else -#endif - { - SockList_AddShort(&sl, mask); - if (pl->contr->socket.lastmap.cells[ax][ay].count != -1) need_send=1; - count = -1; - } - - if (pl->contr->socket.mapmode == Map1aCmd && have_head(ax, ay)) { - /* Now check to see if any heads need to be sent */ - - if (check_head(&sl, &pl->contr->socket, ax, ay, 2)) - mask |= 0x4; - if (check_head(&sl, &pl->contr->socket, ax, ay, 1)) - mask |= 0x2; - if (check_head(&sl, &pl->contr->socket, ax, ay, 0)) - mask |= 0x1; - pl->contr->socket.lastmap.cells[ax][ay].count = count; - - } else { - struct MapCell *cell = &pl->contr->socket.lastmap.cells[ax][ay]; - /* properly clear a previously sent big face */ - if(cell->faces[0] != 0 - || cell->faces[1] != 0 - || cell->faces[2] != 0) - need_send = 1; - map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay], 0, 0, 0, count); - } - - if ((mask & 0xf) || need_send) { - sl.buf[oldlen+1] = mask & 0xff; - } else { - sl.len = oldlen; - } - } else { - /* In this block, the space is visible or there are head objects - * we need to send. - */ - - /* 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. - */ - oldlen = sl.len; - mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; - eoldlen = esl.len; - emask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; - SockList_AddShort(&sl, mask); - - if (pl->contr->socket.ext_mapinfos) - SockList_AddShort(&esl, emask); - - /* 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; - } - - /* Floor face */ - if (update_space(&sl, &pl->contr->socket, m, nx, ny, ax, ay, 2)) - mask |= 0x4; - - if (pl->contr->socket.EMI_smooth) - if (update_smooth(&esl, &pl->contr->socket, m, nx, ny, ax, ay, 2)){ - emask |= 0x4; - } - - /* Middle face */ - if (update_space(&sl, &pl->contr->socket, m, nx, ny, ax, ay, 1)) - mask |= 0x2; - - if (pl->contr->socket.EMI_smooth) - if (update_smooth(&esl, &pl->contr->socket, m, nx, ny, ax, ay, 1)){ - emask |= 0x2; - } - - - if(nx == pl->x && ny == pl->y && pl->invisible & (pl->invisible < 50 ? 4 : 1)) { - if (pl->contr->socket.lastmap.cells[ax][ay].faces[0] != pl->face->number) { - pl->contr->socket.lastmap.cells[ax][ay].faces[0] = pl->face->number; - mask |= 0x1; - if (!(pl->contr->socket.faces_sent[pl->face->number] &NS_FACESENT_FACE)) - esrv_send_face(&pl->contr->socket, pl->face->number, 0); - SockList_AddShort(&sl, pl->face->number); - } - } - /* Top face */ - else { - if (update_space(&sl, &pl->contr->socket, m, nx, ny, ax, ay, 0)) - mask |= 0x1; - if (pl->contr->socket.EMI_smooth) - if (update_smooth(&esl, &pl->contr->socket, m, nx, ny, ax, ay, 0)){ - emask |= 0x1; - } - } - /* Check to see if we are in fact sending anything for this - * space by checking the mask. If so, update the mask. - * if not, reset the len to that from before adding the mask - * value, so we don't send those bits. - */ - if (mask & 0xf) { - sl.buf[oldlen+1] = mask & 0xff; - } else { - sl.len = oldlen; - } - if (emask & 0xf) { - esl.buf[eoldlen+1] = emask & 0xff; - } else { - esl.len = eoldlen; - } - } /* else this is a viewable space */ - } /* for x loop */ - } /* for y loop */ - - /* Verify that we in fact do need to send this */ - if (pl->contr->socket.ext_mapinfos){ - if (!(sl.len>startlen || pl->contr->socket.sent_scroll)){ - /* No map data will follow, so don't say the client - * it doesn't need draw! - */ - ewhatflag&=(~EMI_NOREDRAW); - esl.buf[ewhatstart+1] = ewhatflag & 0xff; - } - if (esl.len>estartlen) { - Send_With_Handling(&pl->contr->socket, &esl); - } - free(esl.buf); - } - if (sl.len>startlen || pl->contr->socket.sent_scroll) { - Send_With_Handling(&pl->contr->socket, &sl); - pl->contr->socket.sent_scroll = 0; + int x, y, ax, ay, d, startlen, max_x, max_y, oldlen; + sint16 nx, ny; + int estartlen, eoldlen; + uint16 mask, emask; + uint8 eentrysize; + uint16 ewhatstart, ewhatflag; + uint8 extendedinfos; + maptile *m; + + client &socket = *pl->contr->ns; + + check_map_change (pl->contr); + + packet sl (socket.mapmode == Map1Cmd ? "map1" : "map1a"); + packet esl; + + startlen = sl.length (); + + /*Extendedmapinfo structure initialisation */ + if (socket.ext_mapinfos) + { + extendedinfos = EMI_NOREDRAW; + + if (socket.EMI_smooth) + extendedinfos |= EMI_SMOOTH; + + ewhatstart = esl.length (); + ewhatflag = extendedinfos; /*The EMI_NOREDRAW bit + could need to be taken away */ + eentrysize = getExtendedMapInfoSize (&socket); + esl << "mapextended " + << uint8 (extendedinfos) + << uint8 (eentrysize); + + estartlen = esl.length (); + } + else + { + /* suppress compiler warnings */ + ewhatstart = 0; + ewhatflag = 0; + estartlen = 0; + } + + /* Init data to zero */ + memset (heads, 0, sizeof (object *) * MAX_HEAD_POS * MAX_HEAD_POS * MAX_LAYERS); + + /* x,y are the real map locations. ax, ay are viewport relative + * locations. + */ + ay = 0; + + /* We could do this logic as conditionals in the if statement, + * but that started to get a bit messy to look at. + */ + max_x = pl->x + (socket.mapx + 1) / 2; + max_y = pl->y + (socket.mapy + 1) / 2; + + if (socket.mapmode == Map1aCmd) + { + max_x += MAX_HEAD_OFFSET; + max_y += MAX_HEAD_OFFSET; + } + + for (y = pl->y - socket.mapy / 2; y < max_y; y++, ay++) + { + ax = 0; + for (x = pl->x - socket.mapx / 2; x < max_x; x++, ax++) + { + emask = mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; + + /* If this space is out of the normal viewable area, we only check + * the heads value ax or ay will only be greater than what + * the client wants if using the map1a command - this is because + * if the map1a command is not used, max_x and max_y will be + * set to lower values. + */ + if (ax >= socket.mapx || ay >= socket.mapy) + { + int i, got_one; + + oldlen = sl.length (); + + sl << uint16 (mask); + + if (check_head (sl, socket, ax, ay, 2)) mask |= 0x4; + if (check_head (sl, socket, ax, ay, 1)) mask |= 0x2; + if (check_head (sl, socket, ax, ay, 0)) mask |= 0x1; + + /* If all we are doing is sending 0 (blank) faces, we don't + * actually need to send that - just the coordinates + * with no faces tells the client to blank out the + * space. + */ + got_one = 0; + for (i = oldlen + 2; i < sl.length (); i++) + if (sl[i]) + got_one = 1; + + if (got_one && (mask & 0xf)) + sl[oldlen + 1] = mask & 0xff; + else + { /*either all faces blank, either no face at all */ + if (mask & 0xf) /*at least 1 face, we know it's blank, only send coordinates */ + sl.reset (oldlen + 2); + else + sl.reset (oldlen); + } + + /*What concerns extendinfos, nothing to be done for now + * (perhaps effects layer later) + */ + continue; /* don't do processing below */ + } + + MapCell &lastcell = socket.lastmap.cells[ax][ay]; + + d = pl->contr->blocked_los[ax][ay]; + + /* 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 the space is out + * of the map, it shouldn't have a head + */ + if (lastcell.count != -1) + { + sl << uint16 (mask); + map_clearcell (&lastcell, 0, 0, 0, -1); + } + } + else if (d > 3) + { + int need_send = 0, count; + + /* This block deals with spaces that are not visible for whatever + * reason. Still may need to send the head for this space. + */ + + oldlen = sl.length (); + + sl << uint16 (mask); + + if (lastcell.count != -1) + need_send = 1; + + count = -1; + + if (socket.mapmode == Map1aCmd && have_head (ax, ay)) + { + /* Now check to see if any heads need to be sent */ + + if (check_head (sl, socket, ax, ay, 2)) mask |= 0x4; + if (check_head (sl, socket, ax, ay, 1)) mask |= 0x2; + if (check_head (sl, socket, ax, ay, 0)) mask |= 0x1; + + lastcell.count = count; + } + else + { + /* properly clear a previously sent big face */ + if (lastcell.faces[0] != 0 || lastcell.faces[1] != 0 || lastcell.faces[2] != 0 + || lastcell.stat_hp || lastcell.flags || lastcell.player) + need_send = 1; + + map_clearcell (&lastcell, 0, 0, 0, count); + } + + if ((mask & 0xf) || need_send) + sl[oldlen + 1] = mask & 0xff; + else + sl.reset (oldlen); + } + else + { + /* In this block, the space is visible or there are head objects + * we need to send. + */ + + /* 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.length () 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. + */ + oldlen = sl.length (); + mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; + eoldlen = esl.length (); + emask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; + sl << uint16 (mask); + + if (socket.ext_mapinfos) + esl << uint16 (emask); + + unsigned char dummy; + unsigned char *last_ext = &dummy; + + /* Darkness changed */ + if (lastcell.count != d && socket.darkness) + { + mask |= 0x8; + + if (socket.extmap) + { + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + sl << uint8 (d); + } + else + sl << uint8 (255 - 64 * d); + } + + lastcell.count = d; + + if (socket.extmap) + { + uint8 stat_hp = 0; + uint8 stat_width = 0; + uint8 flags = 0; + UUID player = 0; + + // send hp information, if applicable + if (object *op = GET_MAP_FACE_OBJ (m, nx, ny, 0)) + { + if (op->head || op->invisible) + ; // do not show + else if (op->type == PLAYER + || QUERY_FLAG (op, FLAG_MONSTER) || QUERY_FLAG (op, FLAG_ALIVE) || QUERY_FLAG (op, FLAG_GENERATOR)) + { + if (op->stats.maxhp > 0 && (unsigned) op->stats.maxhp > (unsigned) op->stats.hp) + { + stat_hp = 255 - (op->stats.hp * 255 + 254) / op->stats.maxhp; + stat_width = op->arch->tail_x; + } + } + + if (op->msg && op->msg[0] == '@') + flags |= 1; + + if (op->type == PLAYER && op != pl) + player = op->count; + } + + if (lastcell.stat_hp != stat_hp) + { + lastcell.stat_hp = stat_hp; + + mask |= 0x8; + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + + sl << uint8 (5) << uint8 (stat_hp); + + if (stat_width > 1) + { + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + + sl << uint8 (6) << uint8 (stat_width); + } + } + + if (lastcell.player != player) + { + lastcell.player = player; + + mask |= 0x8; + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + + sl << uint8 (0x47) << uint8 (8) << (uint64)player; + } + + if (lastcell.flags != flags) + { + lastcell.flags = flags; + + mask |= 0x8; + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + + sl << uint8 (8) << uint8 (flags); + } + } + + /* Floor face */ + if (update_space (sl, socket, m, nx, ny, ax, ay, 2)) + mask |= 0x4; + + if (socket.EMI_smooth) + if (update_smooth (esl, socket, m, nx, ny, ax, ay, 2)) + emask |= 0x4; + + /* Middle face */ + if (update_space (sl, socket, m, nx, ny, ax, ay, 1)) + mask |= 0x2; + + if (socket.EMI_smooth) + if (update_smooth (esl, socket, m, nx, ny, ax, ay, 1)) + emask |= 0x2; + + if (nx == pl->x && ny == pl->y && pl->invisible & (pl->invisible < 50 ? 4 : 1)) + { + if (lastcell.faces[0] != pl->face->number) + { + lastcell.faces[0] = pl->face->number; + mask |= 0x1; + + if (!(socket.faces_sent[pl->face->number] & NS_FACESENT_FACE)) + esrv_send_face (&socket, pl->face->number, 0); + + sl << uint16 (pl->face->number); + } + } + else + { + /* Top face */ + if (update_space (sl, socket, m, nx, ny, ax, ay, 0)) + mask |= 0x1; + + if (socket.EMI_smooth) + if (update_smooth (esl, socket, m, nx, ny, ax, ay, 0)) + emask |= 0x1; + } + + /* Check to see if we are in fact sending anything for this + * space by checking the mask. If so, update the mask. + * if not, reset the len to that from before adding the mask + * value, so we don't send those bits. + */ + if (mask & 0xf) + sl[oldlen + 1] = mask & 0xff; + else + sl.reset (oldlen); + + if (emask & 0xf) + esl[eoldlen + 1] = emask & 0xff; + else + esl.reset (eoldlen); + } /* else this is a viewable space */ + } /* for x loop */ + } /* for y loop */ + + /* Verify that we in fact do need to send this */ + if (socket.ext_mapinfos) + { + if (!(sl.length () > startlen || socket.sent_scroll)) + { + /* No map data will follow, so don't say the client + * it doesn't need draw! + */ + ewhatflag &= (~EMI_NOREDRAW); + esl[ewhatstart + 1] = ewhatflag & 0xff; + } + + if (esl.length () > estartlen) + socket.send_packet (esl); } - free(sl.buf); - check_map_change (pl->contr); + if (sl.length () > startlen || socket.sent_scroll) + { + socket.send_packet (sl); + socket.sent_scroll = 0; + } } /** * Draws client map. */ -void draw_client_map(object *pl) +void +draw_client_map (object *pl) { - int i,j; - sint16 ax, ay, nx, ny;/* ax and ay goes from 0 to max-size of arrays */ - New_Face *face,*floor; - New_Face *floor2; - int d, mflags; - struct Map newmap; - mapstruct *m, *pm; - - if (pl->type != PLAYER) { - LOG(llevError,"draw_client_map called with non player/non eric-server\n"); - return; - } - - if (pl->contr->transport) { - pm = pl->contr->transport->map; - } - else - pm = pl->map; - - /* 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 (pm==NULL || pm->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 = pm; - mflags = get_map_flags(m, &m, ax, ay, &ax, &ay); - if (mflags & P_OUT_OF_MAP) - continue; - if (mflags & P_NEED_UPDATE) - update_position(m, ax, ay); - /* If a map is visible to the player, we don't want to swap it out - * just to reload it. This should really call something like - * swap_map, but this is much more efficient and 'good enough' - */ - if (mflags & P_NEW_MAP) - m->timeout = 50; - } - } - /* do LOS after calls to update_position */ - if(pl->contr->do_los) { - update_los(pl); - pl->contr->do_los = 0; - } - - if (pl->contr->socket.mapmode == Map1Cmd || pl->contr->socket.mapmode == Map1aCmd) { - /* Big maps need a different drawing mechanism to work */ - draw_client_map1(pl); - return; + int i, j; + sint16 ax, ay, nx, ny; /* ax and ay goes from 0 to max-size of arrays */ + New_Face *face, *floor; + New_Face *floor2; + int d, mflags; + struct Map newmap; + maptile *m, *pm; + + if (pl->type != PLAYER) + { + LOG (llevError, "draw_client_map called with non player/non eric-server\n"); + 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. - * Subtract 1 from the max values so that we properly handle cases where - * player has specified an even map. Otherwise, we try to send them too - * much, ie, if mapx is 10, we would try to send from -5 to 5, which is actually - * 11 spaces. Now, we would send from -5 to 4, which is properly. If mapx is - * odd, this still works fine. - */ - ay=0; - for(j=pl->y-pl->contr->socket.mapy/2; j<=pl->y+(pl->contr->socket.mapy-1)/2;j++, ay++) { - ax=0; - for(i=pl->x-pl->contr->socket.mapx/2;i<=pl->x+(pl->contr->socket.mapx-1)/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(pm, &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); - - check_map_change (pl->contr); -} - - -void esrv_map_scroll(NewSocket *ns,int dx,int dy) -{ - struct Map newmap; - int x,y, mx, my; - char buf[MAXSOCKBUF]; - - sprintf(buf,"map_scroll %d %d", dx, dy); - Write_String_To_Socket(ns, buf, strlen(buf)); - - /* If we are using the Map1aCmd, we may in fact send - * head information that is outside the viewable map. - * So set the mx,my to the max value we want to - * look for. Removed code to do so - it caused extra - * complexities for the client, and probably doesn't make - * that much difference in bandwidth. - */ - mx = ns->mapx; - my = ns->mapy; - - if (ns->mapmode == Map1aCmd) { - mx += MAX_HEAD_OFFSET; - my += MAX_HEAD_OFFSET; - } - - /* 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 this reason, - * if the destination x or y coordinate is outside the viewable - * area, we clear the values - otherwise, the old values - * are preserved, and the check_head thinks it needs to clear them. - */ - for(x=0; x= ns->mapx || y >= ns->mapy) { - /* clear cells outside the viewable area */ - memset(&newmap.cells[x][y], 0, sizeof(struct MapCell)); - } - else if ((x+dx) < 0 || (x+dx) >= ns->mapx || (y+dy) < 0 || (y + dy) >= ns->mapy) { - /* clear newly visible tiles within the viewable area */ - memset(&(newmap.cells[x][y]), 0, sizeof(struct MapCell)); - } - else { - memcpy(&(newmap.cells[x][y]), - &(ns->lastmap.cells[x+dx][y+dy]),sizeof(struct MapCell)); - } - } - } - - memcpy(&(ns->lastmap), &newmap,sizeof(struct Map)); - - /* Make sure that the next "map1" command will be sent (even if it is - * empty). - */ - ns->sent_scroll = 1; + + pm = pl->map; + + /* 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 (pm == NULL || pm->in_memory != MAP_IN_MEMORY) + return; + + memset (&newmap, 0, sizeof (struct Map)); + + for (j = (pl->y - pl->contr->ns->mapy / 2); j < (pl->y + (pl->contr->ns->mapy + 1) / 2); j++) + for (i = (pl->x - pl->contr->ns->mapx / 2); i < (pl->x + (pl->contr->ns->mapx + 1) / 2); i++) + { + ax = i; + ay = j; + m = pm; + mflags = get_map_flags (m, &m, ax, ay, &ax, &ay); + + if (mflags & P_OUT_OF_MAP) + continue; + + /* If a map is visible to the player, we don't want to swap it out + * just to reload it. This should really call something like + * swap_map, but this is much more efficient and 'good enough' + */ + if (mflags & P_NEW_MAP) + m->timeout = 50; + } + + /* do LOS after calls to update_position */ + if (pl->contr->do_los) + { + update_los (pl); + pl->contr->do_los = 0; + } + + /* Big maps need a different drawing mechanism to work */ + draw_client_map1 (pl); } + /*****************************************************************************/ + /* 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) +void +send_plugin_custom_message (object *pl, char *buf) { - cs_write_string(&pl->contr->socket,buf,strlen(buf)); + pl->contr->ns->send_packet (buf); } /** * This sends the skill number to name mapping. We ignore * the params - we always send the same info no matter what. */ -void send_skill_info(NewSocket *ns, char *params) +void +send_skill_info (client *ns, char *params) { - SockList sl; - int i; + packet sl; + sl << "replyinfo skill_info\n"; + + for (int i = 1; i < NUM_SKILLS; i++) + sl.printf ("%d:%s\n", i + CS_STAT_SKILLINFO, &skill_names[i]); - sl.buf = (unsigned char*) malloc(MAXSOCKBUF); - strcpy((char*)sl.buf,"replyinfo skill_info\n"); - for (i=1; i< NUM_SKILLS; i++) { - sprintf((char*)sl.buf + strlen((char*)sl.buf), "%d:%s\n", i + CS_STAT_SKILLINFO, - skill_names[i]); - } - sl.len = strlen((char*)sl.buf); - if (sl.len >= MAXSOCKBUF) { - LOG(llevError,"Buffer overflow in send_skill_info!\n"); - fatal(0); + if (sl.length () >= MAXSOCKBUF) + { + LOG (llevError, "Buffer overflow in send_skill_info!\n"); + fatal (0); } - Send_With_Handling(ns, &sl); - free(sl.buf); + + ns->send_packet (sl); } /** * This sends the spell path to name mapping. We ignore * the params - we always send the same info no matter what. */ -void send_spell_paths (NewSocket *ns, char *params) { - SockList sl; - int i; - - sl.buf = (unsigned char*) malloc(MAXSOCKBUF); - strcpy((char*)sl.buf,"replyinfo spell_paths\n"); - for(i=0; i= MAXSOCKBUF) { - LOG(llevError,"Buffer overflow in send_spell_paths!\n"); - fatal(0); +void +send_spell_paths (client * ns, char *params) +{ + packet sl; + + sl << "replyinfo spell_paths\n"; + + for (int i = 0; i < NRSPELLPATHS; i++) + sl.printf ("%d:%s\n", 1 << i, spellpathnames[i]); + + if (sl.length () >= MAXSOCKBUF) + { + LOG (llevError, "Buffer overflow in send_spell_paths!\n"); + fatal (0); } - Send_With_Handling(ns, &sl); - free(sl.buf); + + ns->send_packet (sl); } /** * This looks for any spells the player may have that have changed their stats. * it then sends an updspell packet for each spell that has changed in this way */ -void esrv_update_spells(player *pl) { - SockList sl; - int flags=0; - object *spell; - if (!pl->socket.monitor_spells) return; - for (spell=pl->ob->inv; spell!=NULL; spell=spell->below) { - if (spell->type == SPELL) { - /* check if we need to update it*/ - if (spell->last_sp != SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA)) { - spell->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA); - flags |= UPD_SP_MANA; - } - if (spell->last_grace != SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE)) { - spell->last_grace = SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE); - flags |= UPD_SP_GRACE; - } - if (spell->last_eat != spell->stats.dam+SP_level_dam_adjust(pl->ob, spell)) { - spell->last_eat = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell); - flags |= UPD_SP_DAMAGE; - } - if (flags !=0) { - sl.buf =(unsigned char*) malloc(MAXSOCKBUF); - strcpy((char*)sl.buf,"updspell "); - sl.len=strlen((char*)sl.buf); - SockList_AddChar(&sl, flags); - SockList_AddInt(&sl, spell->count); - if (flags & UPD_SP_MANA) SockList_AddShort(&sl, spell->last_sp); - if (flags & UPD_SP_GRACE) SockList_AddShort(&sl, spell->last_grace); - if (flags & UPD_SP_DAMAGE) SockList_AddShort(&sl, spell->last_eat); - flags = 0; - Send_With_Handling(&pl->socket, &sl); - free(sl.buf); - } - } +void +esrv_update_spells (player *pl) +{ + if (!pl->ns->monitor_spells) + return; + + for (object *spell = pl->ob->inv; spell; spell = spell->below) + { + if (spell->type == SPELL) + { + int flags = 0; + + /* check if we need to update it */ + if (spell->last_sp != SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA)) + { + spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA); + flags |= UPD_SP_MANA; + } + + if (spell->last_grace != SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE)) + { + spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE); + flags |= UPD_SP_GRACE; + } + + if (spell->last_eat != spell->stats.dam + SP_level_dam_adjust (pl->ob, spell)) + { + spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell); + flags |= UPD_SP_DAMAGE; + } + + if (flags) + { + packet sl; + + sl << "updspell " + << uint8 (flags) + << uint32 (spell->count); + + if (flags & UPD_SP_MANA ) sl << uint16 (spell->last_sp); + if (flags & UPD_SP_GRACE ) sl << uint16 (spell->last_grace); + if (flags & UPD_SP_DAMAGE) sl << uint16 (spell->last_eat); + + pl->ns->send_packet (sl); + } + } } } -void esrv_remove_spell(player *pl, object *spell) { - SockList sl; - - if (!pl->socket.monitor_spells) return; - if (!pl || !spell || spell->env != pl->ob) { - LOG(llevError, "Invalid call to esrv_remove_spell"); - return; - } - sl.buf = (unsigned char*) malloc(MAXSOCKBUF); - strcpy((char*)sl.buf,"delspell "); - sl.len=strlen((char*)sl.buf); - SockList_AddInt(&sl, spell->count); - Send_With_Handling(&pl->socket, &sl); - free(sl.buf); +void +esrv_remove_spell (player *pl, object *spell) +{ + if (!pl->ns->monitor_spells) + return; + + if (!pl || !spell || spell->env != pl->ob) + { + LOG (llevError, "Invalid call to esrv_remove_spell"); + return; + } + + packet sl ("delspell"); + + sl << uint32 (spell->count); + + pl->ns->send_packet (sl); } /* appends the spell *spell to the Socklist we will send the data to. */ -static void append_spell (player *pl, SockList *sl, object *spell) { - int len, i, skill=0; +static void +append_spell (player *pl, packet &sl, object *spell) +{ + int len, i, skill = 0; - if (!(spell->name)) { - LOG(llevError, "item number %d is a spell with no name.\n", spell->count); - return; + if (!(spell->name)) + { + LOG (llevError, "item number %d is a spell with no name.\n", spell->count); + return; } - SockList_AddInt(sl, spell->count); - SockList_AddShort(sl, spell->level); - SockList_AddShort(sl, spell->casting_time); - /* store costs and damage in the object struct, to compare to later */ - spell->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA); - spell->last_grace = SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE); - spell->last_eat = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell); - /* send the current values */ - SockList_AddShort(sl, spell->last_sp); - SockList_AddShort(sl, spell->last_grace); - SockList_AddShort(sl, spell->last_eat); - - /* figure out which skill it uses, if it uses one */ - if (spell->skill) { - for (i=1; i< NUM_SKILLS; i++) - if (!strcmp(spell->skill, skill_names[i])) { - skill = i+CS_STAT_SKILLINFO; - break; - } - } - SockList_AddChar(sl, skill); - - SockList_AddInt(sl, spell->path_attuned); - SockList_AddInt(sl, (spell->face)?spell->face->number:0); - - len = strlen(spell->name); - SockList_AddChar(sl, (char)len); - memcpy(sl->buf+sl->len, spell->name, len); - sl->len+=len; - - if (!spell->msg) { - SockList_AddShort(sl, 0); - } - else { - len = strlen(spell->msg); - SockList_AddShort(sl, len); - memcpy(sl->buf+sl->len, spell->msg, len); - sl->len+=len; + + /* store costs and damage in the object struct, to compare to later */ + spell->last_sp = SP_level_spellpoint_cost (pl->ob, spell, SPELL_MANA); + spell->last_grace = SP_level_spellpoint_cost (pl->ob, spell, SPELL_GRACE); + spell->last_eat = spell->stats.dam + SP_level_dam_adjust (pl->ob, spell); + + /* figure out which skill it uses, if it uses one */ + if (spell->skill) + { + for (i = 1; i < NUM_SKILLS; i++) + if (!strcmp (spell->skill, skill_names[i])) + { + skill = i + CS_STAT_SKILLINFO; + break; + } } + + /* send the current values */ + sl << uint32 (spell->count) + << uint16 (spell->level) + << uint16 (spell->casting_time) + << uint16 (spell->last_sp) + << uint16 (spell->last_grace) + << uint16 (spell->last_eat) + << uint8 (skill) + << uint32 (spell->path_attuned) + << uint32 (spell->face ? spell->face->number : 0) + << data8 (spell->name) + << data16 (spell->msg); } /** * This tells the client to add the spell *ob, if *ob is NULL, then add * all spells in the player's inventory. */ -void esrv_add_spells(player *pl, object *spell) { - SockList sl; - if (!pl) { - LOG(llevError, "esrv_add_spells, tried to add a spell to a NULL player"); - return; - } - if (!pl->socket.monitor_spells) return; - sl.buf = (unsigned char*) malloc(MAXSOCKBUF); - strcpy((char*)sl.buf,"addspell "); - sl.len=strlen((char*)sl.buf); - if (!spell) { - for (spell=pl->ob->inv; spell!=NULL; spell=spell->below) { - /* were we to simply keep appending data here, we could exceed - * MAXSOCKBUF if the player has enough spells to add, we know that - * append_spells will always append 19 data bytes, plus 4 length - * bytes and 3 strings (because that is the spec) so we need to - * check that the length of those 3 strings, plus the 23 bytes, - * won't take us over the length limit for the socket, if it does, - * we need to send what we already have, and restart packet formation - */ - /* Seeing crashes by overflowed buffers. Quick arithemetic seems - * to show add_spell is 26 bytes + 2 strings. However, the overun - * is hundreds of bytes off, so correcting 22 vs 26 doesn't seem - * like it will fix this - */ - if (spell->type != SPELL) continue; - if (sl.len >= (MAXSOCKBUF - (26 + strlen(spell->name) + - (spell->msg?strlen(spell->msg):0)))) { - Send_With_Handling(&pl->socket, &sl); - strcpy((char*)sl.buf,"addspell "); - sl.len=strlen((char*)sl.buf); - } - append_spell(pl, &sl, spell); - } - } - else if (spell->type != SPELL) { - LOG(llevError, "Asked to send a non-spell object as a spell"); - return; - } - else append_spell(pl, &sl, spell); - if (sl.len >= MAXSOCKBUF) { - LOG(llevError,"Buffer overflow in esrv_add_spells!\n"); - fatal(0); - } - /* finally, we can send the packet */ - Send_With_Handling(&pl->socket, &sl); - free(sl.buf); +void +esrv_add_spells (player *pl, object *spell) +{ + if (!pl) + { + LOG (llevError, "esrv_add_spells, tried to add a spell to a NULL player"); + return; + } + + if (!pl->ns->monitor_spells) + return; + + packet sl ("addspell"); + + if (!spell) + { + for (spell = pl->ob->inv; spell != NULL; spell = spell->below) + { + /* were we to simply keep appending data here, we could exceed + * MAXSOCKBUF if the player has enough spells to add, we know that + * append_spells will always append 19 data bytes, plus 4 length + * bytes and 3 strings (because that is the spec) so we need to + * check that the length of those 3 strings, plus the 23 bytes, + * won't take us over the length limit for the socket, if it does, + * we need to send what we already have, and restart packet formation + */ + /* Seeing crashes by overflowed buffers. Quick arithemetic seems + * to show add_spell is 26 bytes + 2 strings. However, the overun + * is hundreds of bytes off, so correcting 22 vs 26 doesn't seem + * like it will fix this + */ + if (spell->type != SPELL) + continue; + + if (sl.length () >= (MAXSOCKBUF - (26 + strlen (spell->name) + (spell->msg ? strlen (spell->msg) : 0)))) + { + pl->ns->send_packet (sl); + + sl.reset (); + sl << "addspell "; + } + + append_spell (pl, sl, spell); + } + } + else if (spell->type != SPELL) + { + LOG (llevError, "Asked to send a non-spell object as a spell"); + return; + } + else + append_spell (pl, sl, spell); + + if (sl.length () >= MAXSOCKBUF) + { + LOG (llevError, "Buffer overflow in esrv_add_spells!\n"); + fatal (0); + } + + /* finally, we can send the packet */ + pl->ns->send_packet (sl); }