--- deliantra/server/socket/request.C 2006/09/07 20:03:21 1.15 +++ deliantra/server/socket/request.C 2007/08/26 04:21:30 1.122 @@ -1,31 +1,26 @@ /* - * static char *rcsid_init_c = - * "$Id: request.C,v 1.15 2006/09/07 20:03:21 pippijn Exp $"; + * This file is part of Crossfire TRT, the Roguelike Realtime MORPG. + * + * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team + * Copyright (©) 2001,2007 Mark Wedel + * Copyright (©) 1992,2007 Frank Tore Johansen + * + * Crossfire TRT is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * The authors can be reached via e-mail to */ -/* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2001 Mark Wedel - Copyright (C) 1992 Frank Tore Johansen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - The author can be reached via e-mail to crossfire-devel@real-time.com -*/ - /** * \file * Client handling. @@ -42,45 +37,25 @@ * * esrv_map_new starts updating the map * - * esrv_map_setbelow allows filling in all of the faces for the map. - * if a face has not already been sent to the client, it is sent now. - * - * mapcellchanged, compactlayer, compactstack, perform the map compressing - * operations - * - * esrv_map_doneredraw finishes the map update, and ships across the - * map updates. - * - * esrv_map_scroll tells the client to scroll the map, and does similarily - * for the locally cached copy. */ #include #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,129 +65,114 @@ * 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 (NewSocket *ns, int dx, int dy) +socket_map_scroll (client *ns, int dx, int dy) { - struct Map newmap; - int x,y, mx, my; + struct Map newmap; + int x, y, mx, my; - { - char buf[MAXSOCKBUF]; + ns->send_packet_printf ("map_scroll %d %d", dx, dy); - 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)); - } + /* 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; + + /* 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)); + 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; + /* 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) { - NewSocket &socket = pl->socket; + pl->ns->mapinfo_queue_clear (); + + memset (&pl->ns->lastmap, 0, sizeof (pl->ns->lastmap)); - memset (&socket.lastmap, 0, sizeof (socket.lastmap)); + pl->ns->force_newmap = false; - if (socket.newmapcmd == 1) - Write_String_To_Socket (&socket, "newmap", 6); + if (pl->ns->newmapcmd == 1) + pl->ns->send_packet ("newmap"); - socket.update_look = 1; - socket.look_position = 0; + pl->ns->floorbox_reset (); } -/** check for map change and send new map data */ +/** check for map/region change and send new map data */ static void check_map_change (player *pl) { - NewSocket &socket = pl->socket; - object *ob = pl->ob; - char buf[MAX_BUF]; /* eauugggh */ + client &socket = *pl->ns; + object *ob = pl->observe; - if (socket.current_map != ob->map) + if (socket.current_map != ob->map || socket.force_newmap) { - socket.current_map = ob->map; - clear_map (pl); + socket.current_map = ob->map; if (socket.mapinfocmd) { - if (ob->map && ob->map->path [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, socket.mapx / 2 - ob->x, 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; + + socket.send_packet_printf ("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"); - - Write_String_To_Socket (&socket, buf, strlen (buf)); - } + socket.send_packet ("mapinfo current"); + } } else if (socket.current_x != ob->x || socket.current_y != ob->y) { @@ -220,256 +180,194 @@ int dy = ob->y - socket.current_y; if (socket.buggy_mapscroll && (abs (dx) > 8 || abs (dy) > 8)) - clear_map (pl); // current (<= 1.9.1) clients have unchecked buffer overflows + 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.update_look = 1; - socket.look_position = 0; + socket.floorbox_reset (); } } socket.current_x = ob->x; socket.current_y = ob->y; + + region *reg = ob->region (); + if (socket.current_region != reg) + { + INVOKE_PLAYER (REGION_CHANGE, pl, ARG_REGION (reg), ARG_REGION (socket.current_region)); + socket.current_region = reg; + } +} + +/** + * 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; + + /* find the first space, make it null, and update the + * params pointer. + */ + for (params = buf; *params; params++) + if (*params == ' ') + { + *params++ = 0; + 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 + { + // undo tokenisation above and send replyinfo with the request unchanged + if (*params) + *--params = ' '; + + ns->send_packet_printf ("replyinfo %s", buf); + } } -void ExtCmd (char *buf, int len, player *pl) +void +ExtCmd (char *buf, int len, player *pl) { INVOKE_PLAYER (EXTCMD, pl, ARG_DATA (buf, len)); } -void MapInfoCmd (char *buf, int len, player *pl) +void +ExtiCmd (char *buf, int len, client *ns) { - // mapinfo tag spatial flags x y w h hash - - char bigbuf[MAX_BUF], *token; + INVOKE_CLIENT (EXTICMD, ns, ARG_DATA (buf, len)); +} - token = buf; - // copy token - if (!(buf = strchr (buf, ' '))) - return; +void +client::mapinfo_queue_clear () +{ + for (auto (i, mapinfo_queue.begin ()); i != mapinfo_queue.end (); ++i) + free (*i); - *buf++ = 0; + mapinfo_queue.clear (); +} - if (!strncmp (buf, "spatial ", 8)) - { - buf += 8; +bool +client::mapinfo_try (char *buf) +{ + char *token = buf; + buf += strlen (buf) + 9; - // initial map and its origin - mapstruct *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 + // initial map and its origin + maptile *map = pl->observe->map; + int mapx = pl->ns->mapx / 2 - pl->observe->x; + int mapy = pl->ns->mapy / 2 - pl->observe->y; + int max_distance = 8; // limit maximum path length to something generous - while (*buf && map && max_distance) - { - int dir = *buf++; + while (*buf && map && max_distance) + { + int dir = *buf++ - '1'; - switch (dir) + if (dir >= 0 && dir <= 3) + { + if (!map->tile_path [dir]) + map = 0; + else if (map->tile_available (dir, false)) { - 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; - } + maptile *neigh = map->tile_map [dir]; - --max_distance; - } - - if (!max_distance) - snprintf (bigbuf, MAX_BUF, "mapinfo %s error", token); - else if (map && map->path [0]) - { - int flags = 0; + switch (dir) + { + case 0: mapy -= neigh->height; break; + case 2: mapy += map ->height; break; + case 3: mapx -= neigh->width ; break; + case 1: mapx += map ->width ; break; + } - 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); + map = neigh; + --max_distance; + } + else + return 0; } else - snprintf (bigbuf, MAX_BUF, "mapinfo %s nomap", token); + max_distance = 0; } + + if (!max_distance) + send_packet_printf ("mapinfo %s error", token); + else if (!map || !map->path) + send_packet_printf ("mapinfo %s nomap", token); else - snprintf (bigbuf, MAX_BUF, "mapinfo %s unsupported", token); - - Write_String_To_Socket (&pl->socket, bigbuf, strlen (bigbuf)); + { + 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; + + send_packet_printf ("mapinfo %s spatial %d %d %d %d %d %s", token, flags, mapx, mapy, map->width, map->height, &map->path); + } + + return 1; } -/** This is the Setup cmd - easy first implementation */ -void SetUp(char *buf, int len, NewSocket *ns) +void +client::mapinfo_queue_run () { - int s, slen; - char *cmd, *param, cmdback[HUGE_BUF]; + if (mapinfo_queue.empty () || !pl) + return; - /* 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 - */ + for (int i = 0; i < mapinfo_queue.size (); ++i) + if (mapinfo_try (mapinfo_queue [i])) + { + free (mapinfo_queue [i]); + mapinfo_queue.erase (i); + } + else + ++i; +} - LOG(llevInfo,"Get SetupCmd:: %s\n", buf); - strcpy(cmdback,"setup"); - for(s=0;smapinfo tag spatial flags x y w h hash - cmd = &buf[s]; + char *token = buf; - /* find the next space, and put a null there */ - for(;buf[s] && buf[s] != ' ';s++) ; - buf[s++]=0; - while (buf[s] == ' ') s++; + if (!(buf = strchr (buf, ' '))) + return; - if(s>=len) - break; + if (!strncmp (buf, " spatial ", 9)) + { + char *copy = strdup (token); + copy [buf - token] = 0; - param = &buf[s]; +#if 0 + // this makes only sense when we flush the buffer immediately + if (pl->ns->mapinfo_try (copy)) + free (copy); + else +#endif + pl->ns->mapinfo_queue.push_back (copy); + } + else + pl->ns->send_packet_printf ("mapinfo %s unsupported", token); +} - 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); -// } 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,"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); - Write_String_To_Socket(ns, cmdback, strlen(cmdback)); +/** This is the Setup cmd */ +void +SetUp (char *buf, int len, client * ns) +{ + INVOKE_CLIENT (SETUP, ns, ARG_DATA (buf, len)); } /** @@ -478,61 +376,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 @@ -540,269 +442,177 @@ #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<number, &smoothface))) { + cmdback[0] = '\0'; - LOG(llevError,"could not findsmooth for %d. Neither default (%s)\n", face, &smooth_face->name); - ns->faces_sent[face] |= NS_FACESENT_SMOOTH; - return; - } + nextinfo = 0; + while (1) + { + /* 1. Extract an info */ + info = nextinfo; - if (!(ns->faces_sent[smoothface] & NS_FACESENT_FACE)) - esrv_send_face(ns, smoothface, 0); + while ((info < len) && (buf[info] == ' ')) + info++; - ns->faces_sent[face] |= NS_FACESENT_SMOOTH; + if (info >= len) + break; - 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); -} - - /** - * 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; + nextinfo = info + 1; - facenbr=atoi (buf); - SendSmooth(ns, facenbr); -} + 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); +} /** * 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); - /* 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); + buf = strchr (buf, ' '); /* advance beyond the numbers */ + if (!buf) return; + + buf++; } - /* 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. - */ - 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; } - /** * This handles the general commands from the client (ie, north, fire, cast, * etc.). It is a lot like PlayerCmd above, but is called with the * 'ncom' method which gives more information back to the client so it * can throttle. */ -void NewPlayerCmd(uint8 *buf, int len, player *pl) +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; + if (len <= 6) + { + LOG (llevDebug, "%s: corrupt ncom command <%s>: not long enough (%d) - discarding\n", pl->ns->host, buf, len); + 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; - } + uint16 cmdid = net_uint16 ((uint8 *)buf); + sint32 repeat = net_sint32 ((uint8 *)buf + 2); - /* 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); -} + /* -1 is special - no repeat, but don't update */ + if (repeat != -1) + pl->count = repeat; + buf += 6; //len -= 6; -/** This is a reply to a previous query. */ -void ReplyCmd(char *buf, int len, player *pl) -{ - /* This is to synthesize how the data would be stored if it - * was normally entered. A bit of a hack, and should be cleaned up - * once all the X11 code is removed from the server. - * - * We pass 13 to many of the functions because this way they - * think it was the carriage return that was entered, and the - * function then does not try to do additional input. - */ - 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; + execute_newserver_command (pl->ob, buf); - 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; + /* 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; - case ST_ROLL_STAT: - key_roll_stat(pl->ob,buf[0]); - break; + //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)); - case ST_CHANGE_CLASS: + /* Send confirmation of command execution now */ + packet sl ("comc"); + sl << uint16 (cmdid) << uint32 (time); + pl->ns->send_packet (sl); +} - key_change_class(pl->ob, buf[0]); - break; +/** This is a reply to a previous query. */ +void +ReplyCmd (char *buf, int len, client *ns) +{ + if (ns->state == ST_CUSTOM) + { + INVOKE_CLIENT (REPLY, ns, ARG_DATA (buf, len)); + return; + } - case ST_CONFIRM_QUIT: - key_confirm_quit(pl->ob, buf[0]); - break; + if (!ns->pl) + return; //TODO: depends on the exact reply we are after + //TODO: but right now, we always have a ns->pl - case ST_CONFIGURE: - LOG(llevError,"In client input handling, but into configure state\n"); - pl->state = ST_PLAYING; - break; + player *pl = ns->pl; - case ST_GET_NAME: - receive_player_name(pl->ob,13); - break; + /* 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); - case ST_GET_PASSWORD: - case ST_CONFIRM_PASSWORD: - receive_player_password(pl->ob,13); - break; + /* this avoids any hacking here */ - case ST_GET_PARTY_PASSWORD: /* Get password for party */ - receive_party_password(pl->ob,13); - break; + switch (ns->state) + { + case ST_PLAYING: + LOG (llevError, "Got reply message with ST_PLAYING input state\n"); + break; + + case ST_GET_PARTY_PASSWORD: /* Get password for party */ + receive_party_password (pl->ob, 13); + break; - default: - LOG(llevError,"Unknown input state: %d\n", pl->state); + default: + LOG (llevError, "Unknown input state: %d\n", ns->state); } } @@ -813,109 +623,97 @@ * 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; + 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); - - snprintf (ns->client, sizeof (ns->client), "%s", cp + 1); - - /* 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)); - } + 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) + { + 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 } /** * 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; + /* 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++; + + 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. @@ -926,45 +724,63 @@ * 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, const char *text) { - char buf[MAX_BUF]; + ns->send_packet_printf ("query %d %s", flags, text ? text : ""); +} + +/** + * Get player's current range attack in obuf. + */ +static void +rangetostring (player *pl, char *obuf) +{ + dynbuf_text buf; + + if (pl->ranged_ob) + buf << " Range" << (pl->ob->current_weapon == pl->ranged_ob ? "*" : "") << ": " << pl->ranged_ob->name; + + if (pl->combat_ob) + buf << " Combat" << (pl->ob->current_weapon == pl->combat_ob ? "*" : "") << ": " << pl->combat_ob->name; + +#if 0 + //TODO: remove this when slot system is working, this is only for debugging + if (pl->ob->chosen_skill) + buf << " Skill*: " << pl->ob->chosen_skill->name; +#endif - sprintf(buf,"query %d %s", flags, text?text:""); - Write_String_To_Socket(ns, buf, strlen(buf)); + //TODO: maybe golem should become the current_weapon, quite simply? + if (pl->golem) + buf << " Golem*: " << pl->golem->name; + + buf << '\0'; + buf.linearise (obuf); } #define AddIfInt64(Old,New,Type) if (Old != New) {\ Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt64(&sl, New); \ + sl << uint8 (Type) << uint64 (New); \ } #define AddIfInt(Old,New,Type) if (Old != New) {\ Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt(&sl, New); \ + sl << uint8 (Type) << uint32 (New); \ } #define AddIfShort(Old,New,Type) if (Old != New) {\ Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddShort(&sl, New); \ + sl << uint8 (Type) << uint16 (New); \ } -#define AddIfFloat(Old,New,Type) if (Old != New) {\ +#define AddIfFloat(Old,New,Type,mult) if (Old != New) {\ Old = New; \ - SockList_AddChar(&sl, Type); \ - SockList_AddInt(&sl,(long)(New*FLOAT_MULTI));\ + sl << uint8 (Type) << uint32 (New*FLOAT_MULTI*mult); \ } #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); \ } /** @@ -973,383 +789,143 @@ * 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); - } - free(sl.buf); -} + char buf[MAX_BUF]; + uint16 flags; + client *ns = pl->ns; + if (!ns) + return; -/** - * Tells the client that here is a player it should start using. - */ -void esrv_new_player(player *pl, uint32 weight) -{ - SockList sl; + object *ob = pl->observe; + if (!ob) + return; - pl->last_weight = weight; + player *opl = ob->contr ? static_cast(ob->contr) : pl; - sl.buf=(unsigned char*)malloc(MAXSOCKBUF); + packet sl ("stats"); - 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); -} + AddIfShort (ns->last_stats.hp, ob->stats.hp, CS_STAT_HP); + AddIfShort (ns->last_stats.maxhp, ob->stats.maxhp, CS_STAT_MAXHP); + AddIfShort (ns->last_stats.sp, ob->stats.sp, CS_STAT_SP); + AddIfShort (ns->last_stats.maxsp, ob->stats.maxsp, CS_STAT_MAXSP); + AddIfShort (ns->last_stats.grace, ob->stats.grace, CS_STAT_GRACE); + AddIfShort (ns->last_stats.maxgrace, ob->stats.maxgrace, CS_STAT_MAXGRACE); + AddIfShort (ns->last_stats.Str, ob->stats.Str, CS_STAT_STR); + AddIfShort (ns->last_stats.Dex, ob->stats.Dex, CS_STAT_DEX); + AddIfShort (ns->last_stats.Con, ob->stats.Con, CS_STAT_CON); + AddIfShort (ns->last_stats.Int, ob->stats.Int, CS_STAT_INT); + AddIfShort (ns->last_stats.Wis, ob->stats.Wis, CS_STAT_WIS); + AddIfShort (ns->last_stats.Pow, ob->stats.Pow, CS_STAT_POW); + AddIfShort (ns->last_stats.Cha, ob->stats.Cha, CS_STAT_CHA); + + for (int s = 0; s < NUM_SKILLS; s++) + if (object *skill = opl->last_skill_ob[s]) + if (skill->stats.exp != ns->last_skill_exp [s]) + { + ns->last_skill_exp [s] = skill->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 (skill->level) + << uint64 (skill->stats.exp); + } + + AddIfInt64 (ns->last_stats.exp, ob->stats.exp, CS_STAT_EXP64); + AddIfShort (ns->last_level, ob->level, CS_STAT_LEVEL); + AddIfShort (ns->last_stats.wc, ob->stats.wc, CS_STAT_WC); + AddIfShort (ns->last_stats.ac, ob->stats.ac, CS_STAT_AC); + AddIfShort (ns->last_stats.dam, ob->stats.dam, CS_STAT_DAM); + AddIfFloat (ns->last_speed, ob->speed, CS_STAT_SPEED, 1.f/TICK); + AddIfShort (ns->last_stats.food, ob->stats.food, CS_STAT_FOOD); + AddIfFloat (ns->last_weapon_sp, pl->weapon_sp, CS_STAT_WEAP_SP, 1.f/TICK); + AddIfInt (ns->last_weight_limit, weight_limit[ob->stats.Str], CS_STAT_WEIGHT_LIM); + + flags = 0; + + if (opl->fire_on) + flags |= SF_FIREON; -/** - * Need to send an animation sequence to the client. - * We will send appropriate face commands to the client if we haven't - * sent them the face yet (this can become quite costly in terms of - * how much we are sending - on the other hand, this should only happen - * when the player logs in and picks stuff up. - */ -void esrv_send_animation(NewSocket *ns, short anim_num) -{ - SockList sl; - int i; - - /* Do some checking on the anim_num we got. Note that the animations - * are added in contigous order, so if the number is in the valid - * range, it must be a valid animation. - */ - if (anim_num < 0 || anim_num > num_animations) { - LOG(llevError,"esrv_send_anim (%d) out of bounds??\n",anim_num); - return; - } + if (opl->run_on) + flags |= SF_RUNON; - 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; -} + AddIfShort (ns->last_flags, flags, CS_STAT_FLAGS); + if (ns->sc_version < 1025) + { AddIfShort (ns->last_resist[ATNR_PHYSICAL], 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; -/****************************************************************************** - * - * Start of map related commands. - * - ******************************************************************************/ + AddIfShort (ns->last_resist[i], ob->resist[i], atnr_cs_stat[i]); + } -/** - * 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(); + if (pl->ns->monitor_spells) + { + AddIfInt (ns->last_path_attuned, ob->path_attuned, CS_STAT_SPELL_ATTUNE); + AddIfInt (ns->last_path_repelled, ob->path_repelled, CS_STAT_SPELL_REPEL); + AddIfInt (ns->last_path_denied, ob->path_denied, CS_STAT_SPELL_DENY); } - 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) -{ - 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; - } - } - return 0; + rangetostring (opl, buf); /* we want use the new fire & run system in new client */ + AddIfString (ns->stats.range, buf, CS_STAT_RANGE); + set_title (ob, buf); + AddIfString (ns->stats.title, buf, CS_STAT_TITLE); + + /* Only send it away if we have some actual data */ + if (sl.length () > 6) + ns->send_packet (sl); } /** - * 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); + sl << uint32 (pl->ob->count) + << uint32 (weight) + << uint32 (pl->ob->face) + << data8 (pl->ob->name); + + pl->ns->last_weight = weight; + pl->ns->send_packet (sl); + SET_FLAG (pl->ob, FLAG_CLIENT_SENT); } +/****************************************************************************** + * + * Start of map related commands. + * + ******************************************************************************/ /** 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 count) { - cell->faces[0] = face0; - cell->faces[1] = face1; - cell->faces[2] = face2; - cell->count = count; - cell->stat_hp = 0; - cell->player = 0; + cell->faces[0] = 0; + cell->faces[1] = 0; + cell->faces[2] = 0; + cell->smooth[0] = 0; + cell->smooth[1] = 0; + cell->smooth[2] = 0; + cell->count = count; + cell->stat_hp = 0; + cell->flags = 0; + cell->player = 0; } -#define MAX_HEAD_POS MAX(MAX_CLIENT_X, MAX_CLIENT_Y) #define MAX_LAYERS 3 -/* 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 - * re-examined. - */ - -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) { - - 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; -} - -/** - * 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 - * sent, it returns zero. - */ -static int check_head (SockList &sl, NewSocket &ns, int ax, int ay, int layer) -{ - 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 (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; - } - - return 0; /* No change */ -} - /** * Removes the need to replicate the same code for each layer. * this returns true if this space is now in fact different than @@ -1358,9 +934,6 @@ * ns is the socket we are working on - all the info we care * about is in this socket structure, so now need not pass the * entire player object. - * mx and my are map coordinate offsets for map mp - * sx and sy are the offsets into the socket structure that - * holds the old values. * layer is the layer to update, with 2 being the floor and 0 the * top layer (this matches what the GET_MAP_FACE and GET_MAP_FACE_OBJ) * take. Interesting to note that before this function, the map1 function @@ -1369,944 +942,711 @@ * the case, it seems to make more sense to have these layer values * actually match. */ - -static 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, mapspace &ms, MapCell &lastcell, 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; - } - } - } + object *ob = ms.faces_obj [layer]; - 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 + */ + uint16 face_num = ob && ob->face != blank_face ? ob->face : 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 (lastcell.faces[layer] != face_num) + { + lastcell.faces[layer] = face_num; - /* 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 */ + if (!ns.faces_sent[face_num]) + if (ob) + ns.send_faces (ob); + else + ns.send_face (face_num, 10); - /* 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; + sl << uint16 (face_num); + return 1; } - /* Nothing changed */ - return 0; + + /* Nothing changed */ + return 0; } /** - * This function is mainly a copy of update_space, - * except it handles update of the smoothing updates, - * not the face updates. - * Removes the need to replicate the same code for each layer. - * this returns true if this smooth is now in fact different - * than it was. - * sl is the socklist this data is going into. - * ns is the socket we are working on - all the info we care - * about is in this socket structure, so know need to pass the - * entire player object. - * mx and my are map coordinate offsets for map mp - * sx and sy are the offsets into the socket structure that - * holds the old values. - * layer is the layer to update, with 2 being the floor and 0 the - * top layer (this matches what the GET_MAP_FACE and GET_MAP_FACE_OBJ - * take. + * Returns the size of a data for a map square as returned by + * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries + * available. */ - -static inline int update_smooth(SockList *sl, NewSocket *ns, mapstruct *mp, int mx, int my, int sx, int sy, int layer) +int +getExtendedMapInfoSize (client * ns) { - object *ob; - int smoothlevel; /* old face_num;*/ + int result = 0; - 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 (ns->ext_mapinfos) + { + if (ns->EMI_smooth) + result += 1; /*One byte for smoothlevel */ } - /* Nothing changed */ - return 0; + + return result; +} + +// prefetch (and touch) all maps within a specific distancd +static void +prefetch_surrounding_maps (maptile *map, int distance) +{ + map->last_access = runtime; + + if (--distance) + for (int dir = 4; --dir; ) + if (const shstr &path = map->tile_path [dir]) + if (maptile *&neigh = map->tile_map [dir]) + prefetch_surrounding_maps (neigh, distance); + else + neigh = maptile::find_async (path, map); +} + +// prefetch a generous area around the player +static void +prefetch_surrounding_maps (object *op) +{ + prefetch_surrounding_maps (op->map, 3); } /** - * Returns the size of a data for a map square as returned by - * mapextended. There are CLIENTMAPX*CLIENTMAPY*LAYERS entries - * available. + * Draws client map. */ -int getExtendedMapInfoSize(NewSocket* ns){ - int result=0; - if (ns->ext_mapinfos){ - if (ns->EMI_smooth) - result+=1; /*One byte for smoothlevel*/ +void +draw_client_map (player *pl) +{ + object *ob = pl->observe; + if (!ob->active) + return; + + maptile *plmap = ob->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 (!plmap || plmap->in_memory != MAP_IN_MEMORY) + return; + + int x, y, ax, ay, startlen, max_x, max_y, oldlen; + int estartlen, eoldlen; + uint8 eentrysize; + uint16 ewhatstart, ewhatflag; + uint8 extendedinfos; + + check_map_change (pl); + prefetch_surrounding_maps (pl->ob); + + /* do LOS after calls to update_position */ + if (ob != pl->ob) + clear_los (pl); + else if (pl->do_los) + { + update_los (ob); + pl->do_los = 0; } - 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 - * a maximum map size of 15x15. - * This function is much simpler than the old one. This is because - * the old function optimized to send as few face identifiers as possible, - * at the expense of sending more coordinate location (coordinates were - * only 1 byte, faces 2 bytes, so this was a worthwhile savings). Since - * we need 2 bytes for coordinates and 2 bytes for faces, such a trade off - * maps no sense. Instead, we actually really only use 12 bits for coordinates, - * and use the other 4 bits for other informatiion. For full documentation - * of what we send, see the doc/Protocol file. - * I will describe internally what we do: - * the socket->lastmap shows how the map last looked when sent to the client. - * in the lastmap structure, there is a cells array, which is set to the - * maximum viewable size (As set in config.h). - * in the cells, there are faces and a count value. - * we use the count value to hold the darkness value. If -1, then this space - * is not viewable. - * we use faces[0] faces[1] faces[2] to hold what the three layers - * look like. - */ -void draw_client_map1(object *pl) -{ - int x,y,ax, ay, d, 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; - NewSocket &socket = pl->contr->socket; - - check_map_change (pl->contr); - - sl.buf=(unsigned char*)malloc(MAXSOCKBUF); - if (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 (socket.ext_mapinfos){ - esl.buf=(unsigned char*)malloc(MAXSOCKBUF); - strcpy((char*)esl.buf,"mapextended "); - esl.len=strlen((char*)esl.buf); - extendedinfos=EMI_NOREDRAW; - if (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(&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+(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; yx-socket.mapx/2;x= socket.mapx || ay >= socket.mapy) { - int i, got_one; - - oldlen = sl.len; - - SockList_AddShort(&sl, 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; ilastmap shows how the map last looked when sent to the client. + * in the lastmap structure, there is a cells array, which is set to the + * maximum viewable size (As set in config.h). + * in the cells, there are faces and a count value. + * we use the count value to hold the darkness value. If -1, then this space + * is not viewable. + * we use faces[0] faces[1] faces[2] to hold what the three layers + * look like. + */ + + client &socket = *pl->ns; + + 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 (); + } + + /* 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 = ob->x + (socket.mapx + 1) / 2; + max_y = ob->y + (socket.mapy + 1) / 2; + + for (y = ob->y - socket.mapy / 2; y < max_y; y++, ay++) + { + sint16 nx, ny; + maptile *m = 0; + + ax = 0; + for (x = ob->x - socket.mapx / 2; x < max_x; x++, ax++) + { + // check to see if we can simply go one right quickly + ++nx; + if (m && nx >= m->width) + m = 0; + + if (!m) + { + nx = x; ny = y; m = plmap; + + if (xy_normalise (m, nx, ny)) + m->touch (); + else + m = 0; } - MapCell &lastcell = socket.lastmap.cells[ax][ay]; + int emask, mask; + emask = mask = (ax & 0x3f) << 10 | (ay & 0x3f) << 4; - d = pl->contr->blocked_los[ax][ay]; + MapCell &lastcell = socket.lastmap.cells[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) { - SockList_AddShort(&sl, 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.len; - - SockList_AddShort(&sl, 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 { - struct MapCell *cell = &lastcell; - /* 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(&lastcell, 0, 0, 0, count); + /* If the coordinates are not valid, or it is too dark to see, + * we tell the client as such + */ + 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, -1); } - 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 (socket.ext_mapinfos) - SockList_AddShort(&esl, 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.buf + sl.len; SockList_AddChar (&sl, d); - } - else - SockList_AddChar (&sl, 255 - 64 * d); - } + continue; + } + + int d = pl->blocked_los[ax][ay]; + + 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; + + /* properly clear a previously sent big face */ + if (lastcell.faces[0] || lastcell.faces[1] || lastcell.faces[2] + || lastcell.stat_hp || lastcell.flags || lastcell.player) + need_send = 1; + + map_clearcell (&lastcell, count); - lastcell.count = d; + if ((mask & 0xf) || need_send) + sl[oldlen + 1] = mask & 0xff; + else + sl.reset (oldlen); + } + else + { + /* In this block, the space is visible. + */ - if (socket.extmap) - { - uint8 stat_hp = 0; - uint8 stat_width = 0; - tag_t 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->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.buf + sl.len; SockList_AddChar (&sl, 5); - SockList_AddChar (&sl, stat_hp); - - if (stat_width > 1) - { - *last_ext |= 0x80; last_ext = sl.buf + sl.len; SockList_AddChar (&sl, 6); - SockList_AddChar (&sl, stat_width); - } - } - - if (lastcell.player != player) - { - lastcell.player = player; - mask |= 0x8; - *last_ext |= 0x80; last_ext = sl.buf + sl.len; SockList_AddChar (&sl, 0x47); - SockList_AddChar (&sl, 4); - SockList_AddInt (&sl, player); - } - } - - /* 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); - SockList_AddShort(&sl, pl->face->number); + /* 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 (); + eoldlen = esl.length (); + + sl << uint16 (mask); + + 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); } - /* Top face */ - else { - 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; + + lastcell.count = d; + + mapspace &ms = m->at (nx, ny); + ms.update (); + + if (socket.extmap) + { + uint8 stat_hp = 0; + uint8 stat_width = 0; + uint8 flags = 0; + tag_t player = 0; + + // send hp information, if applicable + if (object *op = ms.faces_obj [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->max_x - op->arch->x; //TODO: should be upper-left edge + } } + + if (op->msg && op->msg[0] == '@') + flags |= 1; + + if (op->type == PLAYER && op != ob) + 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 (4) << (uint32)player; + } + + if (lastcell.flags != flags) + { + lastcell.flags = flags; + + mask |= 0x8; + *last_ext |= 0x80; + last_ext = &sl[sl.length ()]; + + sl << uint8 (8) << uint8 (flags); + } } - /* 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 (socket.ext_mapinfos){ - if (!(sl.len>startlen || 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(&socket, &esl); - } - free(esl.buf); - } - if (sl.len>startlen || socket.sent_scroll) { - Send_With_Handling(&socket, &sl); - socket.sent_scroll = 0; - } - free(sl.buf); -} -/** - * Draws client map. - */ -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; + /* Floor face */ + if (update_space (sl, socket, ms, lastcell, 2)) + mask |= 0x4; + + /* Middle face */ + if (update_space (sl, socket, ms, lastcell, 1)) + mask |= 0x2; + + if (ms.player () == ob + && (ob->invisible & (ob->invisible < 50 ? 1 : 7))) + { + // force player to be visible to himself if invisible + if (lastcell.faces[0] != ob->face) + { + lastcell.faces[0] = ob->face; - if (pl->type != PLAYER) { - LOG(llevError,"draw_client_map called with non player/non eric-server\n"); - return; - } + mask |= 0x1; + sl << uint16 (ob->face); - pm = pl->map; + socket.send_faces (ob); + } + } + /* Top face */ + else if (update_space (sl, socket, ms, lastcell, 0)) + mask |= 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 (socket.ext_mapinfos) + esl << uint16 (emask); + + if (socket.EMI_smooth) + { + for (int layer = 2+1; layer--; ) + { + object *ob = ms.faces_obj [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. + int smoothlevel = ob && ob->face != blank_face ? ob->smoothlevel : 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 (lastcell.smooth[layer] != smoothlevel) + { + lastcell.smooth[layer] = smoothlevel; + esl << uint8 (smoothlevel); + emask |= 1 << layer; + } + } - /* 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 (emask & 0xf) + esl[eoldlen + 1] = emask & 0xff; + else + esl.reset (eoldlen); + } + } /* else this is a viewable space */ + } /* for x loop */ + } /* for y loop */ - 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; - } - - 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 */ + socket.flush_fx (); + + /* 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); } - esrv_map_doneredraw(&pl->contr->socket, &newmap); - check_map_change (pl->contr); + if (sl.length () > startlen || socket.sent_scroll) + { + socket.send_packet (sl); + socket.sent_scroll = 0; + } } - /*****************************************************************************/ /* 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"; - 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); + for (int i = 1; i < NUM_SKILLS; i++) + sl.printf ("%d:%s\n", i + CS_STAT_SKILLINFO, &skill_names[i]); + + 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; +void +esrv_update_spells (player *pl) +{ + if (!pl->ns) + return; + + 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_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 (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); + + 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; +void +esrv_remove_spell (player *pl, object *spell) +{ + if (!pl->ns->monitor_spells) + return; - if (!pl->socket.monitor_spells) return; - if (!pl || !spell || spell->env != pl->ob) { - LOG(llevError, "Invalid call to esrv_remove_spell"); - 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); + + 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 i, skill = 0; - 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; - } + if (!(spell->name)) + { + LOG (llevError, "item number %d is a spell with no name.\n", spell->count); + return; } - SockList_AddChar(sl, skill); - SockList_AddInt(sl, spell->path_attuned); - SockList_AddInt(sl, (spell->face)?spell->face->number:0); + /* 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; + } + } - 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; + // spells better have a face + if (!spell->face) + { + LOG (llevError, "%s: spell has no face, but face is mandatory.\n", &spell->name); + spell->face = face_find ("burnout.x11", blank_face); } + + pl->ns->send_face (spell->face); + + /* 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) + << 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; +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->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); + + if (!pl->ns->monitor_spells) + return; + + packet sl ("addspell"); + + if (!spell) + { + for (spell = pl->ob->inv; spell; 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 + */ + if (spell->type != SPELL) + continue; + + /* 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 (sl.length () > (MAXSOCKBUF - (26 + strlen (spell->name) + (spell->msg ? strlen (spell->msg) : 0)))) + { + pl->ns->flush_fx (); + pl->ns->send_packet (sl); + + sl.reset (); + sl << "addspell "; } - append_spell(pl, &sl, spell); + + append_spell (pl, sl, spell); } } - else if (spell->type != SPELL) { - LOG(llevError, "Asked to send a non-spell object as a spell"); - return; + 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); + 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->flush_fx (); + pl->ns->send_packet (sl); }