ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/loop.C
(Generate patch)

Comparing deliantra/server/socket/loop.C (file contents):
Revision 1.6 by root, Sun Sep 10 13:43:33 2006 UTC vs.
Revision 1.12 by root, Thu Dec 14 01:12:35 2006 UTC

1
2/*
3 * static char *rcsid_loop_c =
4 * "$Id: loop.C,v 1.6 2006/09/10 13:43:33 root Exp $";
5 */
6
7/* 1/*
8 CrossFire, A Multiplayer game for X-windows 2 CrossFire, A Multiplayer game for X-windows
9 3
10 Copyright (C) 2002-2003 Mark Wedel & The Crossfire Development Team 4 Copyright (C) 2002-2003 Mark Wedel & The Crossfire Development Team
11 Copyright (C) 1992 Frank Tore Johansen 5 Copyright (C) 1992 Frank Tore Johansen
22 16
23 You should have received a copy of the GNU General Public License 17 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software 18 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 20
27 The author can be reached via e-mail to crossfire-devel@real-time.com 21 The author can be reached via e-mail to <crossfire@schmorp.de>
28*/ 22*/
29 23
30/** 24/**
31 * \file 25 * \file
32 * Main client/server loops. 26 * Main client/server loops.
38 * The reading of data is handled in ericserver.c 32 * The reading of data is handled in ericserver.c
39 */ 33 */
40 34
41 35
42#include <global.h> 36#include <global.h>
43#ifndef __CEXTRACT__
44# include <sproto.h> 37#include <sproto.h>
45# include <sockproto.h> 38#include <sockproto.h>
46#endif
47 39
48#ifndef WIN32 /* ---win32 exclude unix headers */
49# include <sys/types.h> 40#include <sys/types.h>
50# include <sys/time.h> 41#include <sys/time.h>
51# include <sys/socket.h> 42#include <sys/socket.h>
52# include <netinet/in.h> 43#include <netinet/in.h>
53# include <netdb.h> 44#include <netdb.h>
54#endif /* end win32 */
55 45
56#ifdef HAVE_UNISTD_H 46#ifdef HAVE_UNISTD_H
57# include <unistd.h> 47# include <unistd.h>
58#endif 48#endif
59 49
81 71
82struct NsCmdMapping 72struct NsCmdMapping
83{ 73{
84 const char *cmdname; 74 const char *cmdname;
85 func_uint8_int_ns cmdproc; 75 func_uint8_int_ns cmdproc;
76 uint8 flag;
86}; 77};
87 78
88typedef void (*func_uint8_int_pl) (char *, int, player *); 79typedef void (*func_uint8_int_pl) (char *, int, player *);
89struct PlCmdMapping 80struct PlCmdMapping
90{ 81{
117 {"lock", (func_uint8_int_pl) LockItem, 1}, 108 {"lock", (func_uint8_int_pl) LockItem, 1},
118 {"mark", (func_uint8_int_pl) MarkItem, 1}, 109 {"mark", (func_uint8_int_pl) MarkItem, 1},
119 {"mapredraw", MapRedrawCmd, 0}, /* Added: phil */ 110 {"mapredraw", MapRedrawCmd, 0}, /* Added: phil */
120 {"mapinfo", MapInfoCmd, 2}, /* CF+ */ 111 {"mapinfo", MapInfoCmd, 2}, /* CF+ */
121 {"ext", ExtCmd, 2}, /* CF+ */ 112 {"ext", ExtCmd, 2}, /* CF+ */
122 {NULL, NULL, 0} /* terminator */ 113 { 0 } /* terminator */
123}; 114};
124 115
125/** Face-related commands */ 116/** Face-related commands */
126static struct NsCmdMapping nscommands[] = { 117static struct NsCmdMapping nscommands[] = {
127 {"addme", AddMeCmd}, 118 {"addme", AddMeCmd, 2},
128 {"askface", SendFaceCmd}, /* Added: phil */ 119 {"askface", SendFaceCmd}, /* Added: phil */
129 {"requestinfo", RequestInfo}, 120 {"requestinfo", RequestInfo},
130 {"setfacemode", SetFaceMode}, 121 {"setfacemode", SetFaceMode, 2},
131 {"setsound", SetSound}, 122 {"setsound", SetSound, 2},
132 {"setup", SetUp}, 123 {"setup", SetUp, 2},
133 {"version", VersionCmd}, 124 {"version", VersionCmd, 2},
134 {"toggleextendedinfos", ToggleExtendedInfos}, /*Added: tchize */ 125 {"toggleextendedinfos", ToggleExtendedInfos, 2}, /*Added: tchize */
135 {"toggleextendedtext", ToggleExtendedText}, /*Added: tchize */ 126 {"toggleextendedtext", ToggleExtendedText, 2}, /*Added: tchize */
136 {"asksmooth", AskSmooth}, /*Added: tchize (smoothing technologies) */ 127 {"asksmooth", AskSmooth}, /*Added: tchize (smoothing technologies) */
137 {NULL, NULL} /* terminator (I, II & III) */ 128 { 0 } /* terminator (I, II & III) */
138}; 129};
139 130
140/** 131/**
141 * RequestInfo is sort of a meta command. There is some specific 132 * RequestInfo is sort of a meta command. There is some specific
142 * request of information, but we call other functions to provide 133 * request of information, but we call other functions to provide
166 { 157 {
167 *cp = '\0'; 158 *cp = '\0';
168 params = cp + 1; 159 params = cp + 1;
169 break; 160 break;
170 } 161 }
162
171 if (!strcmp (buf, "image_info")) 163 if (!strcmp (buf, "image_info"))
172 send_image_info (ns, params); 164 send_image_info (ns, params);
173 else if (!strcmp (buf, "image_sums")) 165 else if (!strcmp (buf, "image_sums"))
174 send_image_sums (ns, params); 166 send_image_sums (ns, params);
175 else if (!strcmp (buf, "skill_info")) 167 else if (!strcmp (buf, "skill_info"))
176 send_skill_info (ns, params); 168 send_skill_info (ns, params);
177 else if (!strcmp (buf, "spell_paths")) 169 else if (!strcmp (buf, "spell_paths"))
178 send_spell_paths (ns, params); 170 send_spell_paths (ns, params);
179 else 171 else
180 Write_String_To_Socket (ns, bigbuf, len); 172 ns->send_packet (bigbuf, len);
181} 173}
182 174
183/** 175/**
184 * Handle client input. 176 * Handle client input.
185 * 177 *
189 * with this socket, null if no player (one of the init_sockets for just 181 * with this socket, null if no player (one of the init_sockets for just
190 * starting a connection) 182 * starting a connection)
191 */ 183 */
192 184
193void 185void
194HandleClient (NewSocket * ns, player *pl) 186HandleClient (NewSocket *ns, player *pl)
195{ 187{
196 int len = 0, i, cnt;
197 char *data;
198
199 /* Loop through this - maybe we have several complete packets here. */ 188 /* Loop through this - maybe we have several complete packets here. */
200 // limit to a few commands only, though, as to not monopolise the server 189 // limit to a few commands only, though, as to not monopolise the server
201 for (cnt = 16; cnt--;) 190 for (int repeat = 16; repeat--;)
202 { 191 {
203 /* If it is a player, and they don't have any speed left, we 192 /* If it is a player, and they don't have any speed left, we
204 * return, and will read in the data when they do have time. 193 * return, and will read in the data when they do have time.
205 */ 194 */
206 if (pl && pl->state == ST_PLAYING && pl->ob != NULL && pl->ob->speed_left < 0) 195 if (pl && pl->state == ST_PLAYING && pl->ob && pl->ob->speed_left < 0)
207 {
208 return; 196 return;
197
198 int pkt_len = ns->read_packet ();
199
200 if (pkt_len < 0)
209 } 201 {
210 202 LOG (llevError, "read error on player %s\n", &pl->ob->name);
211 i = SockList_ReadPacket (ns->fd, &ns->inbuf, MAXSOCKBUF - 1);
212
213 if (i < 0)
214 {
215#ifdef ESRV_DEBUG
216 LOG (llevDebug, "HandleClient: Read error on connection player %s\n", (pl ? pl->ob->name : "None"));
217#endif
218 /* Caller will take care of cleaning this up */ 203 /* Caller will take care of cleaning this up */
219 ns->status = Ns_Dead; 204 ns->status = Ns_Dead;
220 return; 205 return;
221 } 206 }
207 else if (pkt_len == 0)
222 /* Still dont have a full packet */ 208 /* Still dont have a full packet */
223 if (i == 0)
224 return; 209 return;
225
226// //D//TODO//temporarily log long commands
227// if (ns->inbuf.len >= 40 && pl && pl->ob)
228// LOG (llevDebug, "HandleClient: long comamnd from <%s,%s> %d<%s>\n", pl->ob->name, ns->host, ns->inbuf.len, ns->inbuf.buf + 2);
229 210
230 /* First, break out beginning word. There are at least 211 /* First, break out beginning word. There are at least
231 * a few commands that do not have any paremeters. If 212 * a few commands that do not have any paremeters. If
232 * we get such a command, don't worry about trying 213 * we get such a command, don't worry about trying
233 * to break it up. 214 * to break it up.
234 */ 215 */
216 int datalen;
235 data = (char *) strchr ((char *) ns->inbuf.buf + 2, ' '); 217 char *data = strchr ((char *)ns->inbuf + 2, ' ');
218
236 if (data) 219 if (data)
237 { 220 {
238 *data = '\0'; 221 *data++ = 0;
239 data++; 222 datalen = pkt_len - (data - (char *)ns->inbuf);
240 len = ns->inbuf.len - (data - (char *) ns->inbuf.buf);
241 } 223 }
242 else 224 else
225 {
226 data = (char *)ns->inbuf + 2; // better read garbage than segfault
243 len = 0; 227 datalen = 0;
244
245 ns->inbuf.buf[ns->inbuf.len] = '\0'; /* Terminate buffer - useful for string data */
246 for (i = 0; nscommands[i].cmdname != NULL; i++)
247 { 228 }
229
230 ns->inbuf [pkt_len] = 0; /* Terminate buffer - useful for string data */
231
232 for (int i = 0; nscommands[i].cmdname; i++)
233 {
248 if (strcmp ((char *) ns->inbuf.buf + 2, nscommands[i].cmdname) == 0) 234 if (!strcmp ((char *)ns->inbuf + 2, nscommands[i].cmdname))
249 { 235 {
250 nscommands[i].cmdproc ((char *) data, len, ns); 236 nscommands[i].cmdproc (data, datalen, ns);
251 ns->inbuf.len = 0; 237 ns->skip_packet (pkt_len);
252 return; //D// not doing this causes random memory corruption 238 //D// not doing this causes random memory corruption
239 if (nscommands[i].flag & 2)
253 goto next_packet; 240 goto next_packet;
254 } 241 return;
255 } 242 }
243 }
244
256 /* Player must be in the playing state or the flag on the 245 /* Player must be in the playing state or the flag on the
257 * the command must be zero for the user to use the command - 246 * the command must be zero for the user to use the command -
258 * otherwise, a player cam save, be in the play_again state, and 247 * otherwise, a player cam save, be in the play_again state, and
259 * the map they were on gets swapped out, yet things that try to look 248 * the map they were on gets swapped out, yet things that try to look
260 * at the map causes a crash. If the command is valid, but 249 * at the map causes a crash. If the command is valid, but
261 * one they can't use, we still swallow it up. 250 * one they can't use, we still swallow it up.
262 */ 251 */
263 if (pl) 252 if (pl)
264 for (i = 0; plcommands[i].cmdname != NULL; i++) 253 for (int i = 0; plcommands[i].cmdname; i++)
265 { 254 {
266 if (strcmp ((char *) ns->inbuf.buf + 2, plcommands[i].cmdname) == 0) 255 if (!strcmp ((char *)ns->inbuf + 2, plcommands[i].cmdname))
267 { 256 {
268 if (pl->state == ST_PLAYING || !(plcommands[i].flag & 1)) 257 if (pl->state == ST_PLAYING || !(plcommands[i].flag & 1))
269 plcommands[i].cmdproc ((char *) data, len, pl); 258 plcommands[i].cmdproc ((char *) data, datalen, pl);
270 ns->inbuf.len = 0; 259
260 ns->skip_packet (pkt_len);
271 //D// not doing this causes random memory corruption 261 //D// not doing this causes random memory corruption
272 if (plcommands[i].flag & 2) 262 if (plcommands[i].flag & 2)
273 goto next_packet; 263 goto next_packet;
264
274 return; 265 return;
275 } 266 }
276 } 267 }
268
269 printf ("BP34n");//D
277 /* If we get here, we didn't find a valid command. Logging 270 /* If we get here, we didn't find a valid command. Logging
278 * this might be questionable, because a broken client/malicious 271 * this might be questionable, because a broken client/malicious
279 * user could certainly send a whole bunch of invalid commands. 272 * user could certainly send a whole bunch of invalid commands.
280 */ 273 */
281 LOG (llevDebug, "Bad command from client (%s)\n", ns->inbuf.buf + 2); 274 LOG (llevDebug, "Bad command from client (%s)\n", ns->inbuf + 2);
275 ns->skip_packet (pkt_len);
282 next_packet: 276 next_packet:
283 ; 277 ;
284 } 278 }
285} 279}
286 280
332 if (pl->socket.status != Ns_Dead) 326 if (pl->socket.status != Ns_Dead)
333 Socket_Flush (&pl->socket); 327 Socket_Flush (&pl->socket);
334} 328}
335 329
336/** 330/**
337 * This checks the sockets for input and exceptions, does the right thing. 331 * This checks the sockets for input, does the right thing.
338 * 332 *
339 * A bit of this code is grabbed out of socket.c 333 * A bit of this code is grabbed out of socket.c
340 * There are 2 lists we need to look through - init_sockets is a list 334 * There are 2 lists we need to look through - init_sockets is a list
341 * 335 *
342 */ 336 */
343void 337void
344doeric_server (void) 338doeric_server (void)
345{ 339{
346 int i, pollret; 340 int i, pollret;
347 fd_set tmp_read, tmp_exceptions, tmp_write; 341 fd_set tmp_read, tmp_write;
348 struct sockaddr_in addr; 342 struct sockaddr_in addr;
349 socklen_t addrlen = sizeof (struct sockaddr); 343 socklen_t addrlen = sizeof (struct sockaddr);
350 player *pl, *next; 344 player *pl, *next;
351 345
352#ifdef CS_LOGSTATS 346#ifdef CS_LOGSTATS
354 write_cs_stats (); 348 write_cs_stats ();
355#endif 349#endif
356 350
357 FD_ZERO (&tmp_read); 351 FD_ZERO (&tmp_read);
358 FD_ZERO (&tmp_write); 352 FD_ZERO (&tmp_write);
359 FD_ZERO (&tmp_exceptions);
360 353
361 for (i = 0; i < socket_info.allocated_sockets; i++) 354 for (i = 0; i < socket_info.allocated_sockets; i++)
362 { 355 {
363 if (init_sockets[i].status == Ns_Dead) 356 if (init_sockets[i].status == Ns_Dead)
364 { 357 {
366 init_sockets[i].status = Ns_Avail; 359 init_sockets[i].status = Ns_Avail;
367 socket_info.nconns--; 360 socket_info.nconns--;
368 } 361 }
369 else if (init_sockets[i].status != Ns_Avail) 362 else if (init_sockets[i].status != Ns_Avail)
370 { 363 {
371 FD_SET ((uint32) init_sockets[i].fd, &tmp_read); 364 FD_SET (init_sockets[i].fd, &tmp_read);
372 FD_SET ((uint32) init_sockets[i].fd, &tmp_write); 365 FD_SET (init_sockets[i].fd, &tmp_write);
373 FD_SET ((uint32) init_sockets[i].fd, &tmp_exceptions);
374 } 366 }
375 } 367 }
376 368
377 /* Go through the players. Let the loop set the next pl value, 369 /* Go through the players. Let the loop set the next pl value,
378 * since we may remove some 370 * since we may remove some
382 if (pl->socket.status == Ns_Dead) 374 if (pl->socket.status == Ns_Dead)
383 { 375 {
384 player *npl = pl->next; 376 player *npl = pl->next;
385 377
386 save_player (pl->ob, 0); 378 save_player (pl->ob, 0);
379
387 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED)) 380 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
388 { 381 {
389 terminate_all_pets (pl->ob); 382 terminate_all_pets (pl->ob);
390 remove_ob (pl->ob); 383 pl->ob->remove ();
391 } 384 }
385
392 leave (pl, 1); 386 leave (pl, 1);
393 final_free_player (pl); 387 final_free_player (pl);
394 pl = npl; 388 pl = npl;
395 } 389 }
396 else 390 else
397 { 391 {
398 FD_SET ((uint32) pl->socket.fd, &tmp_read); 392 FD_SET ((uint32) pl->socket.fd, &tmp_read);
399 FD_SET ((uint32) pl->socket.fd, &tmp_write); 393 FD_SET ((uint32) pl->socket.fd, &tmp_write);
400 FD_SET ((uint32) pl->socket.fd, &tmp_exceptions);
401 pl = pl->next; 394 pl = pl->next;
402 } 395 }
403 } 396 }
404 397
405 /* Reset timeout each time, since some OS's will change the values on 398 /* Reset timeout each time, since some OS's will change the values on
406 * the return from select. 399 * the return from select.
407 */ 400 */
408 socket_info.timeout.tv_sec = 0; 401 socket_info.timeout.tv_sec = 0;
409 socket_info.timeout.tv_usec = 0; 402 socket_info.timeout.tv_usec = 0;
410 403
411 pollret = select (socket_info.max_filedescriptor, &tmp_read, &tmp_write, &tmp_exceptions, &socket_info.timeout); 404 pollret = select (socket_info.max_filedescriptor,
405 &tmp_read, &tmp_write, 0,
406 &socket_info.timeout);
412 407
413 if (pollret == -1) 408 if (pollret == -1)
414 { 409 {
415 LOG (llevError, "select failed: %s\n", strerror (errno)); 410 LOG (llevError, "select failed: %s\n", strerror (errno));
416 return; 411 return;
451 { 446 {
452 newsocknum = j; 447 newsocknum = j;
453 break; 448 break;
454 } 449 }
455 } 450 }
451
456 init_sockets[newsocknum].fd = accept (init_sockets[0].fd, (struct sockaddr *) &addr, &addrlen); 452 init_sockets[newsocknum].fd = accept (init_sockets[0].fd, (struct sockaddr *) &addr, &addrlen);
453
457 if (init_sockets[newsocknum].fd == -1) 454 if (init_sockets[newsocknum].fd == -1)
458 {
459 LOG (llevError, "accept failed: %s\n", strerror (errno)); 455 LOG (llevError, "accept failed: %s\n", strerror (errno));
460 }
461 else 456 else
462 { 457 {
463 char buf[MAX_BUF]; 458 char buf[MAX_BUF];
464 long ip; 459 long ip;
465 NewSocket *ns; 460 NewSocket *ns;
481 socket_info.nconns++; 476 socket_info.nconns++;
482 } 477 }
483 } 478 }
484 } 479 }
485 480
486 /* Check for any exceptions/input on the sockets */ 481 /* Check for any input on the sockets */
487 if (pollret) 482 if (pollret)
488 for (i = 1; i < socket_info.allocated_sockets; i++) 483 for (i = 1; i < socket_info.allocated_sockets; i++)
489 { 484 {
490 if (init_sockets[i].status == Ns_Avail) 485 if (init_sockets[i].status == Ns_Avail)
491 continue; 486 continue;
492 if (FD_ISSET (init_sockets[i].fd, &tmp_exceptions)) 487
493 { 488 //TODO: disassociate handleclient from socket readin
494 free_newsocket (&init_sockets[i]);
495 init_sockets[i].status = Ns_Avail;
496 socket_info.nconns--;
497 continue;
498 }
499 if (FD_ISSET (init_sockets[i].fd, &tmp_read)) 489 if (init_sockets[i].inbuf_len || FD_ISSET (init_sockets[i].fd, &tmp_read))
500 {
501 HandleClient (&init_sockets[i], NULL); 490 HandleClient (&init_sockets[i], NULL);
502 } 491
503 if (FD_ISSET (init_sockets[i].fd, &tmp_write)) 492 if (FD_ISSET (init_sockets[i].fd, &tmp_write))
504 {
505 init_sockets[i].can_write = 1; 493 init_sockets[i].can_write = 1;
506 }
507 } 494 }
508 495
509 /* This does roughly the same thing, but for the players now */ 496 /* This does roughly the same thing, but for the players now */
510 for (pl = first_player; pl != NULL; pl = next) 497 for (pl = first_player; pl != NULL; pl = next)
511 { 498 {
522 LOG (llevDebug, "Player %s socket now write enabled\n", pl->ob->name); 509 LOG (llevDebug, "Player %s socket now write enabled\n", pl->ob->name);
523#endif 510#endif
524 pl->socket.can_write = 1; 511 pl->socket.can_write = 1;
525 write_socket_buffer (&pl->socket); 512 write_socket_buffer (&pl->socket);
526 } 513 }
514
527 /* if we get an error on the write_socket buffer, no reason to 515 /* if we get an error on the write_socket buffer, no reason to
528 * continue on this socket. 516 * continue on this socket.
529 */ 517 */
530 if (pl->socket.status == Ns_Dead) 518 if (pl->socket.status == Ns_Dead)
531 continue; 519 continue;
532 } 520 }
533 else 521 else
534 pl->socket.can_write = 0; 522 pl->socket.can_write = 0;
535 523
536 if (FD_ISSET (pl->socket.fd, &tmp_exceptions)) 524 HandleClient (&pl->socket, pl);
525 /* If the player has left the game, then the socket status
526 * will be set to this be the leave function. We don't
527 * need to call leave again, as it has already been called
528 * once.
529 */
530 if (pl->socket.status == Ns_Dead)
537 { 531 {
538 save_player (pl->ob, 0); 532 save_player (pl->ob, 0);
533
539 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED)) 534 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
540 { 535 {
541 terminate_all_pets (pl->ob); 536 terminate_all_pets (pl->ob);
542 remove_ob (pl->ob); 537 pl->ob->remove ();
543 } 538 }
539
544 leave (pl, 1); 540 leave (pl, 1);
545 final_free_player (pl); 541 final_free_player (pl);
546 } 542 }
547 else 543 else
548 { 544 {
549 HandleClient (&pl->socket, pl); 545 /* Update the players stats once per tick. More efficient than
550 /* If the player has left the game, then the socket status 546 * sending them whenever they change, and probably just as useful
551 * will be set to this be the leave function. We don't
552 * need to call leave again, as it has already been called
553 * once.
554 */ 547 */
555 if (pl->socket.status == Ns_Dead)
556 {
557 save_player (pl->ob, 0);
558 if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
559 {
560 terminate_all_pets (pl->ob);
561 remove_ob (pl->ob);
562 }
563 leave (pl, 1);
564 final_free_player (pl);
565 }
566 else
567 {
568
569 /* Update the players stats once per tick. More efficient than
570 * sending them whenever they change, and probably just as useful
571 */
572 esrv_update_stats (pl); 548 esrv_update_stats (pl);
573 if (pl->last_weight != -1 && pl->last_weight != WEIGHT (pl->ob)) 549 if (pl->last_weight != -1 && pl->last_weight != WEIGHT (pl->ob))
574 { 550 {
575 esrv_update_item (UPD_WEIGHT, pl->ob, pl->ob); 551 esrv_update_item (UPD_WEIGHT, pl->ob, pl->ob);
576 if (pl->last_weight != WEIGHT (pl->ob)) 552 if (pl->last_weight != WEIGHT (pl->ob))
577 LOG (llevError, "esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n", 553 LOG (llevError, "esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n",
578 (unsigned long) pl->last_weight, WEIGHT (pl->ob)); 554 (unsigned long) pl->last_weight, WEIGHT (pl->ob));
579 } 555 }
556
580 /* draw_client_map does sanity checking that map is 557 /* draw_client_map does sanity checking that map is
581 * valid, so don't do it here. 558 * valid, so don't do it here.
582 */ 559 */
583 draw_client_map (pl->ob); 560 draw_client_map (pl->ob);
584 if (pl->socket.update_look) 561 if (pl->socket.update_look)
585 esrv_draw_look (pl->ob); 562 esrv_draw_look (pl->ob);
586 }
587 } 563 }
588 } 564 }
589} 565}
566

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines