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

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.32 * 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 elmex 1.1 /**
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 root 1.11 #include <cstdarg>
41 elmex 1.1
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 root 1.26 #define SOCKET_TIMEOUT1 10 * 1000
52     #define SOCKET_TIMEOUT2 20 * 1000
53 elmex 1.1
54 root 1.5 void
55 root 1.22 client::flush ()
56 elmex 1.1 {
57 root 1.29 if (destroyed ())
58     return;
59    
60 elmex 1.1 #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 root 1.20 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
66 elmex 1.1 {
67     unsigned int diff = tcpi.tcpi_last_ack_recv - tcpi.tcpi_last_data_sent;
68    
69 root 1.21 rtt = tcpi.tcpi_rtt;
70     rttvar = tcpi.tcpi_rttvar;
71    
72 root 1.26 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 elmex 1.1 {
75 root 1.20 LOG (llevDebug, "Connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
76 root 1.5 (unsigned) tcpi.tcpi_last_ack_recv, (unsigned) tcpi.tcpi_last_data_sent, (unsigned) tcpi.tcpi_unacked);
77 root 1.23 destroy ();
78 elmex 1.1 }
79     }
80 root 1.20 #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 elmex 1.1
95 root 1.20 void
96 root 1.22 client::write_outputbuffer ()
97 root 1.20 {
98     while (outputbuffer.len)
99     {
100     int res = write (fd, outputbuffer.data + outputbuffer.start,
101     min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
102 root 1.5
103 root 1.20 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 elmex 1.1 #endif
115 root 1.20 }
116     else if (res == 0)
117     {
118     LOG (llevError, "socket write failed, connection closed.\n");
119 root 1.23 destroy ();
120 root 1.20 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 root 1.23 destroy ();
137 root 1.20 return;
138     }
139     }
140    
141     socket_ev.poll (socket_ev.poll () & ~PE_W);
142 elmex 1.1 }
143    
144 root 1.23 /******************************************************************************
145 elmex 1.1 *
146 root 1.23 * Start of read routines.
147 elmex 1.1 *
148 root 1.23 ******************************************************************************/
149 elmex 1.1
150 root 1.23 int
151     client::next_packet ()
152 elmex 1.1 {
153 root 1.23 if (inbuf_len >= 2)
154 root 1.11 {
155 root 1.23 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 root 1.15 {
162 root 1.23 send_packet_printf ("drawinfo %d input buffer overflow - closing connection.", NDI_RED);
163     destroy ();
164     return -1;
165 root 1.15 }
166 root 1.11 }
167 root 1.9
168 root 1.23 return 0;
169 root 1.11 }
170 root 1.9
171 root 1.23 void
172     client::skip_packet (int len)
173 root 1.11 {
174 root 1.23 inbuf_len -= len;
175     memmove (inbuf, inbuf + len, inbuf_len);
176 elmex 1.1 }
177    
178 root 1.23 /*****************************************************************************
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 root 1.33 {"ncom", PC(NewPlayerCmd) PF_PLAYING | PF_COMMAND6 },
192     {"command", PC(PlayerCmd) PF_PLAYING | PF_COMMAND0 },
193 root 1.23
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 root 1.26 {"mapredraw", PC(MapRedrawCmd) 0 }, /* Added: phil */
202 root 1.23 {"mapinfo", PC(MapInfoCmd) 0 }, /* CF+ */
203    
204 root 1.28 {"reply", SC(ReplyCmd) 0 },
205 root 1.26 {"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 root 1.23 };
217    
218     bool
219     client::may_execute (const packet_type *pkt) const
220 elmex 1.1 {
221 root 1.23 return (!(pkt->flags & PF_PLAYER) || pl)
222 root 1.29 && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING);
223 root 1.11 }
224 root 1.9
225 root 1.33 // 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 root 1.23 void
254     client::execute (const packet_type *pkt, char *data, int datalen)
255 root 1.11 {
256 root 1.33 if (may_execute (pkt) || always_immediate (this, pkt, data, datalen))
257 root 1.23 {
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 elmex 1.1 }
267    
268 root 1.23 bool
269     client::handle_packet ()
270 root 1.10 {
271 root 1.23 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 root 1.15
304 root 1.23 for (packet_type *pkt = packets; pkt < packets + (sizeof (packets) / sizeof (packets[0])); ++pkt)
305     if (!strcmp ((char *)inbuf + 2, pkt->name))
306     {
307 root 1.33 if (pkt->flags & PF_PLAYER && !always_immediate (this, pkt, data, datalen))
308 root 1.26 queue_command (pkt, data, datalen);
309     else
310 root 1.23 execute (pkt, data, datalen);
311    
312     goto next_packet;
313     }
314    
315 root 1.26 // If we get here, we didn't find a valid command.
316 root 1.23 send_packet_printf ("drawinfo %d ERROR: command '%s' not supported.", NDI_RED, (char *)inbuf + 2);
317     next_packet:
318     skip_packet (pkt_len);
319 root 1.11
320 root 1.23 // input buffer has space again
321     socket_ev.poll (socket_ev.poll () | PE_R);
322 root 1.10
323 root 1.23 return true;
324 root 1.10 }
325    
326 root 1.23 // 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 root 1.29 if (destroyed ())
332 root 1.23 {
333     socket_ev.poll (0);
334     return;
335     }
336 elmex 1.1
337 root 1.23 if (got & PE_W)
338 root 1.5 {
339 root 1.23 write_outputbuffer ();
340    
341     if (!outputbuffer.len)
342     socket_ev.poll (socket_ev.poll () & ~PE_W);
343     }
344 root 1.12
345 root 1.23 if (got & PE_R)
346     {
347     //TODO: rate-limit tcp connection in better ways, important
348 pippijn 1.8
349 root 1.12 int amount = sizeof (inbuf) - inbuf_len;
350    
351 root 1.23 if (!amount)
352 root 1.5 {
353 root 1.23 // input buffer full
354     socket_ev.poll (socket_ev.poll () & ~PE_R);
355     return;
356 root 1.3 }
357 elmex 1.1
358 root 1.12 amount = read (fd, inbuf + inbuf_len, amount);
359    
360     if (!amount)
361 root 1.5 {
362 root 1.23 destroy ();
363     return;
364 root 1.5 }
365 root 1.12 else if (amount < 0)
366 root 1.5 {
367 root 1.12 if (errno != EAGAIN && errno != EINTR)
368 root 1.5 {
369 root 1.20 LOG (llevError, "read error: %s\n", strerror (errno));
370 root 1.23 destroy ();
371     return;
372 root 1.3 }
373 root 1.12
374 root 1.23 // should not be here, normally
375 root 1.3 }
376 root 1.23 else
377     {
378     inbuf_len += amount;
379 root 1.12
380 root 1.23 cst_tot.ibytes += amount;
381     cst_lst.ibytes += amount;
382 root 1.12
383 root 1.23 cmd_ev.start ();
384     }
385 root 1.5 }
386 root 1.12 }
387    
388 root 1.23 // called whenever we have additional commands to process
389 root 1.12 void
390 root 1.23 client::cmd_cb (iw &w)
391 root 1.12 {
392 root 1.26 if (handle_packet ())
393 root 1.23 w.start ();
394     else
395     flush ();
396 elmex 1.1 }
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 root 1.20 void
411 root 1.22 client::send (void *buf_, int len)
412 root 1.20 {
413     char *buf = (char *)buf_;
414 elmex 1.1
415 root 1.29 if (destroyed () || !buf)
416 root 1.23 return;
417 elmex 1.1
418 root 1.31 if (len + outputbuffer.len > SOCKETBUFSIZE)
419 root 1.5 {
420 root 1.23 LOG (llevDebug, "socket on fd %d has overrun internal buffer - marking as dead\n", fd);
421     destroy ();
422 root 1.5 return;
423     }
424    
425 root 1.20 int avail, end;
426    
427 root 1.5 /* data + end is where we start putting the new data. The last byte
428     * currently in use is actually data + end -1
429     */
430 root 1.20 end = outputbuffer.start + outputbuffer.len;
431 root 1.5 /* The buffer is already in a wrapped state, so adjust end */
432     if (end >= SOCKETBUFSIZE)
433     end -= SOCKETBUFSIZE;
434 root 1.7
435 root 1.5 avail = SOCKETBUFSIZE - end;
436 elmex 1.1
437 root 1.5 /* We can all fit it behind the current data without wrapping */
438     if (avail >= len)
439 root 1.20 memcpy (outputbuffer.data + end, buf, len);
440 root 1.5 else
441     {
442 root 1.20 memcpy (outputbuffer.data + end, buf, avail);
443     memcpy (outputbuffer.data, buf + avail, len - avail);
444 elmex 1.1 }
445 root 1.7
446 root 1.20 outputbuffer.len += len;
447 elmex 1.1 }
448    
449     /**
450     * Takes a string of data, and writes it out to the socket. A very handy
451     * shortcut function.
452     */
453 root 1.13 void
454 root 1.22 client::send_packet (packet &sl)
455 root 1.13 {
456 root 1.29 if (destroyed ())
457 root 1.19 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 root 1.13 }
478    
479 root 1.5 void
480 root 1.22 client::send_packet (const char *buf, int len)
481 elmex 1.1 {
482 root 1.14 packet sl;
483 elmex 1.1
484 root 1.13 sl << data (buf, len);
485     send_packet (sl);
486 elmex 1.1 }
487    
488 root 1.13 void
489 root 1.22 client::send_packet (const char *buf)
490 root 1.13 {
491     send_packet (buf, strlen (buf));
492     }
493 elmex 1.1
494 root 1.23 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 root 1.35 void
508     client::send_drawinfo (const char *msg, int flags)
509     {
510     send_packet_printf ("drawinfo %d %s", flags, msg);
511     }
512    
513 root 1.23 /***********************************************************************
514     *
515     * packet functions/utilities
516     *
517     **********************************************************************/
518    
519 root 1.27 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 root 1.23 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 elmex 1.1 /******************************************************************************
575     *
576     * statistics logging functions.
577     *
578     ******************************************************************************/
579    
580     #ifdef CS_LOGSTATS
581 root 1.5
582 elmex 1.1 /* 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 root 1.5 void
591     write_cs_stats (void)
592 elmex 1.1 {
593 root 1.5 time_t now = time (NULL);
594 elmex 1.1
595 root 1.5 /* 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 elmex 1.1 }
607     #endif
608 root 1.9