ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.35
Committed: Wed Mar 14 00:04:59 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.34: +6 -0 lines
Log Message:
- rewrote smooth face handling, as a side-effect, smoothing seems to work
  again and smooth faces can be reloaded.
- the server now sends the full animation for an object the first time
  it is seen, this uses slightly more bandwidth initially, but avoids
  the flickering for objects change their face later.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game for X-windows
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 1992 Frank Tore Johansen
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * The author can be reached via e-mail to mark@pyramid.com
22 */
23
24 /**
25 * \file
26 * Low-level socket-related functions.
27 *
28 * \date 2003-12-02
29 *
30 * Contains some base functions that both the client and server
31 * can use. As such, depending what we are being compiled for will
32 * determine what we can include. the client is designed have
33 * CFCLIENT defined as part of its compile flags.
34 */
35
36 using namespace std;
37
38 #include <global.h>
39 #include <sproto.h>
40 #include <cstdarg>
41
42 #ifdef __linux__
43 # include <sys/types.h>
44 # include <sys/socket.h>
45 # include <netinet/in.h>
46 # include <netinet/tcp.h>
47 #endif
48
49 // use a really low timeout, as it doesn't cost any bandwidth, and you can
50 // easily die in 20 seconds...
51 #define SOCKET_TIMEOUT1 10 * 1000
52 #define SOCKET_TIMEOUT2 20 * 1000
53
54 void
55 client::flush ()
56 {
57 if (destroyed ())
58 return;
59
60 #ifdef __linux__
61 // check time of last ack, and, if too old, kill connection
62 struct tcp_info tcpi;
63 socklen_t len = sizeof (tcpi);
64
65 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
66 {
67 unsigned int diff = tcpi.tcpi_last_ack_recv - tcpi.tcpi_last_data_sent;
68
69 rtt = tcpi.tcpi_rtt;
70 rttvar = tcpi.tcpi_rttvar;
71
72 if (tcpi.tcpi_unacked && SOCKET_TIMEOUT1 < diff && diff < 0x80000000UL // ack delayed for 20s
73 && SOCKET_TIMEOUT2 < tcpi.tcpi_last_data_sent) // no data sent for 10s
74 {
75 LOG (llevDebug, "Connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
76 (unsigned) tcpi.tcpi_last_ack_recv, (unsigned) tcpi.tcpi_last_data_sent, (unsigned) tcpi.tcpi_unacked);
77 destroy ();
78 }
79 }
80 #endif
81
82 /**
83 * Writes data to socket.
84 *
85 * When the socket is clear to write, and we have backlogged data, this
86 * is called to write it out.
87 */
88
89 if (!outputbuffer.len || socket_ev.poll () & PE_W)
90 return;
91
92 write_outputbuffer ();
93 }
94
95 void
96 client::write_outputbuffer ()
97 {
98 while (outputbuffer.len)
99 {
100 int res = write (fd, outputbuffer.data + outputbuffer.start,
101 min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
102
103 if (res > 0)
104 {
105 outputbuffer.start += res;
106 /* wrap back to start of buffer */
107 if (outputbuffer.start == SOCKETBUFSIZE)
108 outputbuffer.start = 0;
109
110 outputbuffer.len -= res;
111 #ifdef CS_LOGSTATS
112 cst_tot.obytes += res;
113 cst_lst.obytes += res;
114 #endif
115 }
116 else if (res == 0)
117 {
118 LOG (llevError, "socket write failed, connection closed.\n");
119 destroy ();
120 return;
121 }
122 else if (errno == EINTR)
123 {
124 // just retry
125 }
126 else if (errno == EAGAIN)
127 {
128 // delay till ready
129 socket_ev.poll (socket_ev.poll () | PE_W);
130 socket_ev.start ();
131 return;
132 }
133 else
134 {
135 LOG (llevError, "socket write failed: %s\n", strerror (errno));
136 destroy ();
137 return;
138 }
139 }
140
141 socket_ev.poll (socket_ev.poll () & ~PE_W);
142 }
143
144 /******************************************************************************
145 *
146 * Start of read routines.
147 *
148 ******************************************************************************/
149
150 int
151 client::next_packet ()
152 {
153 if (inbuf_len >= 2)
154 {
155 int pkt_len = (inbuf [0] << 8) | inbuf [1];
156
157 if (inbuf_len >= 2 + pkt_len)
158 return 2 + pkt_len;
159
160 if (inbuf_len == sizeof (inbuf))
161 {
162 send_packet_printf ("drawinfo %d input buffer overflow - closing connection.", NDI_RED);
163 destroy ();
164 return -1;
165 }
166 }
167
168 return 0;
169 }
170
171 void
172 client::skip_packet (int len)
173 {
174 inbuf_len -= len;
175 memmove (inbuf, inbuf + len, inbuf_len);
176 }
177
178 /*****************************************************************************
179 * Start of command dispatch area.
180 * The commands here are protocol commands.
181 ****************************************************************************/
182
183 // SocketCommand, PlayingCommand, should not exist with those ugly casts
184 #define SC(cb) (void *)static_cast<void (*)(char *, int, client *)>(cb),
185 #define PC(cb) (void *)static_cast<void (*)(char *, int, player *)>(cb), PF_PLAYER |
186
187 /**
188 * Dispatch table for the server.
189 */
190 static struct packet_type packets[] = {
191 {"ncom", PC(NewPlayerCmd) PF_PLAYING | PF_COMMAND6 },
192 {"command", PC(PlayerCmd) PF_PLAYING | PF_COMMAND0 },
193
194 {"examine", PC(ExamineCmd) PF_PLAYING },
195 {"apply", PC(ApplyCmd) PF_PLAYING },
196 {"lookat", PC(LookAt) PF_PLAYING },
197 {"lock", PC(LockItem) PF_PLAYING },
198 {"mark", PC(MarkItem) PF_PLAYING },
199 {"move", PC(MoveCmd) PF_PLAYING },
200 {"ext", PC(ExtCmd) 0 }, /* CF+ */
201 {"mapredraw", PC(MapRedrawCmd) 0 }, /* Added: phil */
202 {"mapinfo", PC(MapInfoCmd) 0 }, /* CF+ */
203
204 {"reply", SC(ReplyCmd) 0 },
205 {"exti", SC(ExtiCmd) 0 }, /* CF+ */
206 {"addme", SC(AddMeCmd) 0 },
207 {"askface", SC(SendFaceCmd) 0 }, /* Added: phil */
208 {"requestinfo", SC(RequestInfo) 0 },
209 {"setfacemode", SC(SetFaceMode) 0 },
210 {"setsound", SC(SetSound) 0 },
211 {"setup", SC(SetUp) 0 },
212 {"version", SC(VersionCmd) 0 },
213 {"toggleextendedinfos", SC(ToggleExtendedInfos) 0 }, /*Added: tchize */
214 {"toggleextendedtext", SC(ToggleExtendedText) 0 }, /*Added: tchize */
215 {"asksmooth", SC(AskSmooth) 0 }, /*Added: tchize (smoothing technologies) */
216 };
217
218 bool
219 client::may_execute (const packet_type *pkt) const
220 {
221 return (!(pkt->flags & PF_PLAYER) || pl)
222 && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING);
223 }
224
225 // HACK: some commands currently should be executed
226 // even when the player is frozen. this hack detects
227 // those commands. it should be folded into may_execute,
228 // but kept seperate to emphasise the hack aspect, i.e.
229 // do it better, then remove.
230 static bool
231 always_immediate (const client *ns, const packet_type *pkt, const char *data, int len)
232 {
233 if (!(pkt->flags & (PF_COMMAND0 | PF_COMMAND6)))
234 return false;
235
236 if (!ns->pl || !ns->pl->ob || !ns->pl->ob->map)
237 return false;
238
239 if (pkt->flags & PF_COMMAND6)
240 {
241 data += 6;
242 len -= 6;
243 }
244
245 if (len > 4 && !strncmp (data, "say " , 4))
246 return true;
247 if (len > 5 && !strncmp (data, "chat ", 5))
248 return true;
249
250 return false;
251 }
252
253 void
254 client::execute (const packet_type *pkt, char *data, int datalen)
255 {
256 if (may_execute (pkt) || always_immediate (this, pkt, data, datalen))
257 {
258 //TODO: only one format
259 if (pkt->flags & PF_PLAYER)
260 ((void (*)(char *, int, player *))pkt->cb)((char *)data, datalen, pl);
261 else
262 ((void (*)(char *, int, client *))pkt->cb)((char *)data, datalen, this);
263 }
264 else
265 send_packet_printf ("drawinfo %d ERROR: you cannot execute '%s' now.", NDI_RED, pkt->name);
266 }
267
268 bool
269 client::handle_packet ()
270 {
271 int pkt_len = next_packet ();
272
273 if (!pkt_len)
274 return false;
275 else if (pkt_len < 0)
276 {
277 LOG (llevError, "read error on player %s\n",
278 pl && pl->ob ? &pl->ob->name : "[anonymous]");
279 destroy ();
280 return false;
281 }
282
283 inbuf [pkt_len] = 0; /* Terminate buffer - useful for string data */
284
285 /* First, break out beginning word. There are at least
286 * a few commands that do not have any paremeters. If
287 * we get such a command, don't worry about trying
288 * to break it up.
289 */
290 int datalen;
291 char *data = strchr ((char *)inbuf + 2, ' ');
292
293 if (data)
294 {
295 *data++ = 0;
296 datalen = pkt_len - (data - (char *)inbuf);
297 }
298 else
299 {
300 data = (char *)inbuf + 2; // better read garbage than segfault
301 datalen = 0;
302 }
303
304 for (packet_type *pkt = packets; pkt < packets + (sizeof (packets) / sizeof (packets[0])); ++pkt)
305 if (!strcmp ((char *)inbuf + 2, pkt->name))
306 {
307 if (pkt->flags & PF_PLAYER && !always_immediate (this, pkt, data, datalen))
308 queue_command (pkt, data, datalen);
309 else
310 execute (pkt, data, datalen);
311
312 goto next_packet;
313 }
314
315 // If we get here, we didn't find a valid command.
316 send_packet_printf ("drawinfo %d ERROR: command '%s' not supported.", NDI_RED, (char *)inbuf + 2);
317 next_packet:
318 skip_packet (pkt_len);
319
320 // input buffer has space again
321 socket_ev.poll (socket_ev.poll () | PE_R);
322
323 return true;
324 }
325
326 // callback called when socket is either readable or writable
327 void
328 client::socket_cb (iow &w, int got)
329 {
330 //TODO remove when we have better socket cleanup logic
331 if (destroyed ())
332 {
333 socket_ev.poll (0);
334 return;
335 }
336
337 if (got & PE_W)
338 {
339 write_outputbuffer ();
340
341 if (!outputbuffer.len)
342 socket_ev.poll (socket_ev.poll () & ~PE_W);
343 }
344
345 if (got & PE_R)
346 {
347 //TODO: rate-limit tcp connection in better ways, important
348
349 int amount = sizeof (inbuf) - inbuf_len;
350
351 if (!amount)
352 {
353 // input buffer full
354 socket_ev.poll (socket_ev.poll () & ~PE_R);
355 return;
356 }
357
358 amount = read (fd, inbuf + inbuf_len, amount);
359
360 if (!amount)
361 {
362 destroy ();
363 return;
364 }
365 else if (amount < 0)
366 {
367 if (errno != EAGAIN && errno != EINTR)
368 {
369 LOG (llevError, "read error: %s\n", strerror (errno));
370 destroy ();
371 return;
372 }
373
374 // should not be here, normally
375 }
376 else
377 {
378 inbuf_len += amount;
379
380 cst_tot.ibytes += amount;
381 cst_lst.ibytes += amount;
382
383 cmd_ev.start ();
384 }
385 }
386 }
387
388 // called whenever we have additional commands to process
389 void
390 client::cmd_cb (iw &w)
391 {
392 if (handle_packet ())
393 w.start ();
394 else
395 flush ();
396 }
397
398 /*******************************************************************************
399 *
400 * Start of write related routines.
401 *
402 ******************************************************************************/
403
404 /**
405 * Adds data to a socket buffer for whatever reason.
406 *
407 * ns is the socket we are adding the data to, buf is the start of the
408 * data, and len is the number of bytes to add.
409 */
410 void
411 client::send (void *buf_, int len)
412 {
413 char *buf = (char *)buf_;
414
415 if (destroyed () || !buf)
416 return;
417
418 if (len + outputbuffer.len > SOCKETBUFSIZE)
419 {
420 LOG (llevDebug, "socket on fd %d has overrun internal buffer - marking as dead\n", fd);
421 destroy ();
422 return;
423 }
424
425 int avail, end;
426
427 /* data + end is where we start putting the new data. The last byte
428 * currently in use is actually data + end -1
429 */
430 end = outputbuffer.start + outputbuffer.len;
431 /* The buffer is already in a wrapped state, so adjust end */
432 if (end >= SOCKETBUFSIZE)
433 end -= SOCKETBUFSIZE;
434
435 avail = SOCKETBUFSIZE - end;
436
437 /* We can all fit it behind the current data without wrapping */
438 if (avail >= len)
439 memcpy (outputbuffer.data + end, buf, len);
440 else
441 {
442 memcpy (outputbuffer.data + end, buf, avail);
443 memcpy (outputbuffer.data, buf + avail, len - avail);
444 }
445
446 outputbuffer.len += len;
447 }
448
449 /**
450 * Takes a string of data, and writes it out to the socket. A very handy
451 * shortcut function.
452 */
453 void
454 client::send_packet (packet &sl)
455 {
456 if (destroyed ())
457 return;
458
459 if (sl.length () >= MAXSOCKBUF)
460 {
461 LOG (llevError, "Trying to send a buffer beyond properly size, len =%d\n", sl.length ());
462 /* Almost certainly we've overflowed a buffer, so quit now to make
463 * it easier to debug.
464 */
465 abort ();
466 }
467
468 if (!sl.length ())
469 return;
470
471 assert (sl.hdrlen == 2);
472
473 sl.buf_ [0] = sl.length () >> 8;
474 sl.buf_ [1] = sl.length () ;
475
476 send (sl.buf_, sl.length () + sl.hdrlen);
477 }
478
479 void
480 client::send_packet (const char *buf, int len)
481 {
482 packet sl;
483
484 sl << data (buf, len);
485 send_packet (sl);
486 }
487
488 void
489 client::send_packet (const char *buf)
490 {
491 send_packet (buf, strlen (buf));
492 }
493
494 void
495 client::send_packet_printf (const char *format, ...)
496 {
497 packet sl;
498
499 va_list ap;
500 va_start (ap, format);
501 sl.vprintf (format, ap);
502 va_end (ap);
503
504 send_packet (sl);
505 }
506
507 void
508 client::send_drawinfo (const char *msg, int flags)
509 {
510 send_packet_printf ("drawinfo %d %s", flags, msg);
511 }
512
513 /***********************************************************************
514 *
515 * packet functions/utilities
516 *
517 **********************************************************************/
518
519 packet::packet (const char *name)
520 {
521 reset ();
522
523 int len = strlen (name);
524 memcpy (cur, name, len); cur += len;
525 *cur++ = ' ';
526 }
527
528 packet &packet::operator <<(const data &v)
529 {
530 if (room () < v.len)
531 reset ();
532 else
533 {
534 if (v.len)
535 {
536 memcpy (cur, v.ptr, v.len);
537 cur += v.len;
538 }
539 }
540
541 return *this;
542 }
543
544 packet &packet::operator <<(const data8 &v)
545 {
546 unsigned int len = min (v.len, 0x00FF);
547 return *this << uint8 (len) << data (v.ptr, len);
548 }
549
550 packet &packet::operator <<(const data16 &v)
551 {
552 unsigned int len = min (v.len, 0xFFFF);
553 return *this << uint16 (len) << data (v.ptr, len);
554 }
555
556 packet &packet::operator <<(const char *v)
557 {
558 return *this << data (v, strlen (v ? v : 0));
559 }
560
561 void
562 packet::vprintf (const char *format, va_list ap)
563 {
564 int size = room ();
565
566 int len = vsnprintf ((char *)cur, size, format, ap);
567
568 if (len >= size)
569 return reset ();
570
571 cur += len;
572 }
573
574 /******************************************************************************
575 *
576 * statistics logging functions.
577 *
578 ******************************************************************************/
579
580 #ifdef CS_LOGSTATS
581
582 /* cst_tot is for the life of the server, cst_last is for the last series of
583 * stats
584 */
585 CS_Stats cst_tot, cst_lst;
586
587 /**
588 * Writes out the gathered stats. We clear cst_lst.
589 */
590 void
591 write_cs_stats (void)
592 {
593 time_t now = time (NULL);
594
595 /* If no connections recently, don't both to log anything */
596 if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
597 return;
598
599 /* CSSTAT is put in so scripts can easily find the line */
600 LOG (llevInfo, "CSSTAT: %.16s tot %d %d %d %d inc %d %d %d %d\n",
601 ctime (&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
602 now - cst_tot.time_start, cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, now - cst_lst.time_start);
603 cst_lst.ibytes = 0;
604 cst_lst.obytes = 0;
605 cst_lst.time_start = now;
606 }
607 #endif
608