ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/loop.C
Revision: 1.6
Committed: Sun Sep 10 13:43:33 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +372 -306 lines
Log Message:
indent

File Contents

# User Rev Content
1 elmex 1.1
2     /*
3     * static char *rcsid_loop_c =
4 root 1.6 * "$Id: loop.C,v 1.5 2006-09-09 21:48:29 root Exp $";
5 elmex 1.1 */
6    
7     /*
8     CrossFire, A Multiplayer game for X-windows
9    
10     Copyright (C) 2002-2003 Mark Wedel & The Crossfire Development Team
11     Copyright (C) 1992 Frank Tore Johansen
12    
13     This program is free software; you can redistribute it and/or modify
14     it under the terms of the GNU General Public License as published by
15     the Free Software Foundation; either version 2 of the License, or
16     (at your option) any later version.
17    
18     This program is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     GNU General Public License for more details.
22    
23     You should have received a copy of the GNU General Public License
24     along with this program; if not, write to the Free Software
25     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26    
27     The author can be reached via e-mail to crossfire-devel@real-time.com
28     */
29    
30     /**
31     * \file
32     * Main client/server loops.
33     *
34     * \date 2003-12-02
35     *
36     * loop.c mainly deals with initialization and higher level socket
37     * maintenance (checking for lost connections and if data has arrived.)
38     * The reading of data is handled in ericserver.c
39     */
40    
41    
42     #include <global.h>
43     #ifndef __CEXTRACT__
44 root 1.6 # include <sproto.h>
45     # include <sockproto.h>
46 elmex 1.1 #endif
47    
48 root 1.6 #ifndef WIN32 /* ---win32 exclude unix headers */
49     # include <sys/types.h>
50     # include <sys/time.h>
51     # include <sys/socket.h>
52     # include <netinet/in.h>
53     # include <netdb.h>
54 elmex 1.1 #endif /* end win32 */
55    
56     #ifdef HAVE_UNISTD_H
57 root 1.6 # include <unistd.h>
58 elmex 1.1 #endif
59    
60     #ifdef HAVE_ARPA_INET_H
61 root 1.6 # include <arpa/inet.h>
62 elmex 1.1 #endif
63    
64     #include <loader.h>
65     #include <newserver.h>
66    
67     /*****************************************************************************
68     * Start of command dispatch area.
69     * The commands here are protocol commands.
70     ****************************************************************************/
71    
72     /* Either keep this near the start or end of the file so it is
73     * at least reasonablye easy to find.
74     * There are really 2 commands - those which are sent/received
75     * before player joins, and those happen after the player has joined.
76     * As such, we have function types that might be called, so
77     * we end up having 2 tables.
78     */
79    
80 root 1.6 typedef void (*func_uint8_int_ns) (char *, int, NewSocket *);
81 elmex 1.1
82 root 1.6 struct NsCmdMapping
83     {
84     const char *cmdname;
85     func_uint8_int_ns cmdproc;
86 elmex 1.1 };
87    
88 root 1.6 typedef void (*func_uint8_int_pl) (char *, int, player *);
89     struct PlCmdMapping
90     {
91     const char *cmdname;
92     func_uint8_int_pl cmdproc;
93     uint8 flag;
94 elmex 1.1 };
95    
96     /**
97     * Dispatch table for the server.
98     *
99     * CmdMapping is the dispatch table for the server, used in HandleClient,
100     * which gets called when the client has input. All commands called here
101     * use the same parameter form (char* data, int len, int clientnum.
102     * We do implicit casts, because the data that is being passed is
103     * unsigned (pretty much needs to be for binary data), however, most
104     * of these treat it only as strings, so it makes things easier
105     * to cast it here instead of a bunch of times in the function itself.
106     * flag is 1 if the player must be in the playing state to issue the
107     * command, 0 if they can issue it at any time.
108     */
109     static struct PlCmdMapping plcommands[] = {
110 root 1.6 {"examine", ExamineCmd, 1},
111     {"apply", ApplyCmd, 1},
112     {"move", MoveCmd, 1},
113     {"reply", ReplyCmd, 0},
114     {"command", PlayerCmd, 1},
115     {"ncom", (func_uint8_int_pl) NewPlayerCmd, 1},
116     {"lookat", LookAt, 1},
117     {"lock", (func_uint8_int_pl) LockItem, 1},
118     {"mark", (func_uint8_int_pl) MarkItem, 1},
119     {"mapredraw", MapRedrawCmd, 0}, /* Added: phil */
120     {"mapinfo", MapInfoCmd, 2}, /* CF+ */
121     {"ext", ExtCmd, 2}, /* CF+ */
122     {NULL, NULL, 0} /* terminator */
123 elmex 1.1 };
124    
125     /** Face-related commands */
126     static struct NsCmdMapping nscommands[] = {
127 root 1.6 {"addme", AddMeCmd},
128     {"askface", SendFaceCmd}, /* Added: phil */
129     {"requestinfo", RequestInfo},
130     {"setfacemode", SetFaceMode},
131     {"setsound", SetSound},
132     {"setup", SetUp},
133     {"version", VersionCmd},
134     {"toggleextendedinfos", ToggleExtendedInfos}, /*Added: tchize */
135     {"toggleextendedtext", ToggleExtendedText}, /*Added: tchize */
136     {"asksmooth", AskSmooth}, /*Added: tchize (smoothing technologies) */
137     {NULL, NULL} /* terminator (I, II & III) */
138 elmex 1.1 };
139    
140     /**
141     * RequestInfo is sort of a meta command. There is some specific
142     * request of information, but we call other functions to provide
143     * that information.
144     */
145 root 1.6 void
146     RequestInfo (char *buf, int len, NewSocket * ns)
147 elmex 1.1 {
148 root 1.6 char *params = NULL, *cp;
149    
150     /* No match */
151     char bigbuf[MAX_BUF];
152     int slen;
153    
154     /* Set up replyinfo before we modify any of the buffers - this is used
155     * if we don't find a match.
156     */
157     strcpy (bigbuf, "replyinfo ");
158     slen = strlen (bigbuf);
159     safe_strcat (bigbuf, buf, &slen, MAX_BUF);
160    
161     /* find the first space, make it null, and update the
162     * params pointer.
163     */
164     for (cp = buf; *cp != '\0'; cp++)
165     if (*cp == ' ')
166     {
167     *cp = '\0';
168     params = cp + 1;
169     break;
170     }
171     if (!strcmp (buf, "image_info"))
172     send_image_info (ns, params);
173     else if (!strcmp (buf, "image_sums"))
174     send_image_sums (ns, params);
175     else if (!strcmp (buf, "skill_info"))
176     send_skill_info (ns, params);
177     else if (!strcmp (buf, "spell_paths"))
178     send_spell_paths (ns, params);
179     else
180     Write_String_To_Socket (ns, bigbuf, len);
181 elmex 1.1 }
182    
183     /**
184     * Handle client input.
185     *
186     * HandleClient is actually not named really well - we only get here once
187     * there is input, so we don't do exception or other stuff here.
188     * sock is the output socket information. pl is the player associated
189     * with this socket, null if no player (one of the init_sockets for just
190     * starting a connection)
191     */
192    
193 root 1.6 void
194     HandleClient (NewSocket * ns, player *pl)
195 elmex 1.1 {
196 root 1.6 int len = 0, i, cnt;
197     char *data;
198 elmex 1.1
199 root 1.6 /* Loop through this - maybe we have several complete packets here. */
200     // limit to a few commands only, though, as to not monopolise the server
201     for (cnt = 16; cnt--;)
202     {
203     /* 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.
205     */
206     if (pl && pl->state == ST_PLAYING && pl->ob != NULL && pl->ob->speed_left < 0)
207     {
208     return;
209 root 1.3 }
210 elmex 1.1
211 root 1.6 i = SockList_ReadPacket (ns->fd, &ns->inbuf, MAXSOCKBUF - 1);
212    
213     if (i < 0)
214     {
215 elmex 1.1 #ifdef ESRV_DEBUG
216 root 1.6 LOG (llevDebug, "HandleClient: Read error on connection player %s\n", (pl ? pl->ob->name : "None"));
217 elmex 1.1 #endif
218 root 1.6 /* Caller will take care of cleaning this up */
219     ns->status = Ns_Dead;
220     return;
221 root 1.3 }
222 root 1.6 /* Still dont have a full packet */
223     if (i == 0)
224     return;
225 elmex 1.1
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    
230 root 1.6 /* First, break out beginning word. There are at least
231     * a few commands that do not have any paremeters. If
232     * we get such a command, don't worry about trying
233     * to break it up.
234     */
235     data = (char *) strchr ((char *) ns->inbuf.buf + 2, ' ');
236     if (data)
237     {
238     *data = '\0';
239     data++;
240     len = ns->inbuf.len - (data - (char *) ns->inbuf.buf);
241     }
242     else
243     len = 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     {
248     if (strcmp ((char *) ns->inbuf.buf + 2, nscommands[i].cmdname) == 0)
249     {
250     nscommands[i].cmdproc ((char *) data, len, ns);
251     ns->inbuf.len = 0;
252     return; //D// not doing this causes random memory corruption
253     goto next_packet;
254 root 1.3 }
255     }
256 root 1.6 /* 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 -
258     * 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
260     * at the map causes a crash. If the command is valid, but
261     * one they can't use, we still swallow it up.
262     */
263     if (pl)
264     for (i = 0; plcommands[i].cmdname != NULL; i++)
265     {
266     if (strcmp ((char *) ns->inbuf.buf + 2, plcommands[i].cmdname) == 0)
267     {
268 root 1.3 if (pl->state == ST_PLAYING || !(plcommands[i].flag & 1))
269 root 1.6 plcommands[i].cmdproc ((char *) data, len, pl);
270     ns->inbuf.len = 0;
271 elmex 1.1 //D// not doing this causes random memory corruption
272     if (plcommands[i].flag & 2)
273     goto next_packet;
274     return;
275 root 1.6 }
276     }
277     /* If we get here, we didn't find a valid command. Logging
278     * this might be questionable, because a broken client/malicious
279     * user could certainly send a whole bunch of invalid commands.
280     */
281     LOG (llevDebug, "Bad command from client (%s)\n", ns->inbuf.buf + 2);
282 elmex 1.1 next_packet:
283 root 1.6 ;
284 elmex 1.1 }
285     }
286    
287    
288     /*****************************************************************************
289     *
290     * Low level socket looping - select calls and watchdog udp packet
291     * sending.
292     *
293     ******************************************************************************/
294    
295     #ifdef WATCHDOG
296 root 1.6
297 elmex 1.1 /**
298     * Tell watchdog that we are still alive
299     *
300     * I put the function here since we should hopefully already be getting
301     * all the needed include files for socket support
302     */
303    
304 root 1.6 void
305     watchdog (void)
306 elmex 1.1 {
307 root 1.6 static int fd = -1;
308 elmex 1.1 static struct sockaddr_in insock;
309    
310 root 1.6 if (fd == -1)
311 elmex 1.1 {
312     struct protoent *protoent;
313    
314 root 1.6 if ((protoent = getprotobyname ("udp")) == NULL || (fd = socket (PF_INET, SOCK_DGRAM, protoent->p_proto)) == -1)
315     {
316     return;
317     }
318     insock.sin_family = AF_INET;
319     insock.sin_port = htons ((unsigned short) 13325);
320     insock.sin_addr.s_addr = inet_addr ("127.0.0.1");
321 elmex 1.1 }
322 root 1.6 sendto (fd, (void *) &fd, 1, 0, (struct sockaddr *) &insock, sizeof (insock));
323 elmex 1.1 }
324     #endif
325    
326 root 1.6 void
327     flush_sockets (void)
328 elmex 1.1 {
329     player *pl;
330    
331     for (pl = first_player; pl != NULL; pl = pl->next)
332     if (pl->socket.status != Ns_Dead)
333     Socket_Flush (&pl->socket);
334     }
335    
336     /**
337     * This checks the sockets for input and exceptions, does the right thing.
338     *
339     * 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
341     *
342     */
343 root 1.6 void
344     doeric_server (void)
345 elmex 1.1 {
346 root 1.6 int i, pollret;
347     fd_set tmp_read, tmp_exceptions, tmp_write;
348     struct sockaddr_in addr;
349     socklen_t addrlen = sizeof (struct sockaddr);
350     player *pl, *next;
351 elmex 1.1
352     #ifdef CS_LOGSTATS
353 root 1.6 if ((time (NULL) - cst_lst.time_start) >= CS_LOGTIME)
354     write_cs_stats ();
355 elmex 1.1 #endif
356    
357 root 1.6 FD_ZERO (&tmp_read);
358     FD_ZERO (&tmp_write);
359     FD_ZERO (&tmp_exceptions);
360    
361     for (i = 0; i < socket_info.allocated_sockets; i++)
362     {
363     if (init_sockets[i].status == Ns_Dead)
364     {
365     free_newsocket (&init_sockets[i]);
366     init_sockets[i].status = Ns_Avail;
367     socket_info.nconns--;
368     }
369     else if (init_sockets[i].status != Ns_Avail)
370     {
371     FD_SET ((uint32) init_sockets[i].fd, &tmp_read);
372     FD_SET ((uint32) init_sockets[i].fd, &tmp_write);
373     FD_SET ((uint32) init_sockets[i].fd, &tmp_exceptions);
374 root 1.3 }
375 elmex 1.1 }
376    
377 root 1.6 /* Go through the players. Let the loop set the next pl value,
378     * since we may remove some
379     */
380     for (pl = first_player; pl != NULL;)
381     {
382     if (pl->socket.status == Ns_Dead)
383     {
384     player *npl = pl->next;
385    
386     save_player (pl->ob, 0);
387     if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
388     {
389     terminate_all_pets (pl->ob);
390     remove_ob (pl->ob);
391 root 1.3 }
392 root 1.6 leave (pl, 1);
393     final_free_player (pl);
394     pl = npl;
395     }
396     else
397     {
398     FD_SET ((uint32) pl->socket.fd, &tmp_read);
399     FD_SET ((uint32) pl->socket.fd, &tmp_write);
400     FD_SET ((uint32) pl->socket.fd, &tmp_exceptions);
401     pl = pl->next;
402 root 1.3 }
403 elmex 1.1 }
404    
405 root 1.6 /* Reset timeout each time, since some OS's will change the values on
406     * the return from select.
407     */
408     socket_info.timeout.tv_sec = 0;
409     socket_info.timeout.tv_usec = 0;
410 elmex 1.1
411 root 1.6 pollret = select (socket_info.max_filedescriptor, &tmp_read, &tmp_write, &tmp_exceptions, &socket_info.timeout);
412 elmex 1.1
413 root 1.6 if (pollret == -1)
414     {
415     LOG (llevError, "select failed: %s\n", strerror (errno));
416     return;
417 elmex 1.1 }
418    
419 root 1.6 /* We need to do some of the processing below regardless */
420    
421 elmex 1.1 /* if (!pollret) return;*/
422    
423 root 1.6 /* Following adds a new connection */
424     if (pollret && FD_ISSET (init_sockets[0].fd, &tmp_read))
425     {
426     int newsocknum = 0;
427 elmex 1.1
428     #ifdef ESRV_DEBUG
429 root 1.6 LOG (llevDebug, "doeric_server: New Connection\n");
430 elmex 1.1 #endif
431 root 1.6 /* If this is the case, all sockets currently in used */
432     if (socket_info.allocated_sockets <= socket_info.nconns)
433     {
434     init_sockets = (NewSocket *) realloc (init_sockets, sizeof (NewSocket) * (socket_info.nconns + 1));
435     if (!init_sockets)
436     fatal (OUT_OF_MEMORY);
437     newsocknum = socket_info.allocated_sockets;
438     socket_info.allocated_sockets++;
439     init_sockets[newsocknum].faces_sent_len = nrofpixmaps;
440     init_sockets[newsocknum].faces_sent = (uint8 *) malloc (nrofpixmaps * sizeof (*init_sockets[newsocknum].faces_sent));
441     if (!init_sockets[newsocknum].faces_sent)
442     fatal (OUT_OF_MEMORY);
443     init_sockets[newsocknum].status = Ns_Avail;
444     }
445     else
446     {
447     int j;
448    
449     for (j = 1; j < socket_info.allocated_sockets; j++)
450     if (init_sockets[j].status == Ns_Avail)
451     {
452     newsocknum = j;
453     break;
454     }
455     }
456     init_sockets[newsocknum].fd = accept (init_sockets[0].fd, (struct sockaddr *) &addr, &addrlen);
457     if (init_sockets[newsocknum].fd == -1)
458     {
459     LOG (llevError, "accept failed: %s\n", strerror (errno));
460     }
461     else
462     {
463     char buf[MAX_BUF];
464     long ip;
465     NewSocket *ns;
466    
467     ns = &init_sockets[newsocknum];
468    
469     ip = ntohl (addr.sin_addr.s_addr);
470     sprintf (buf, "%ld.%ld.%ld.%ld", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
471    
472     if (checkbanned (NULL, buf))
473     {
474     LOG (llevInfo, "Banned host tried to connect: [%s]\n", buf);
475     close (init_sockets[newsocknum].fd);
476     init_sockets[newsocknum].fd = -1;
477 root 1.3 }
478 root 1.6 else
479     {
480     InitConnection (ns, buf);
481     socket_info.nconns++;
482 root 1.3 }
483     }
484 elmex 1.1 }
485    
486 root 1.6 /* Check for any exceptions/input on the sockets */
487     if (pollret)
488     for (i = 1; i < socket_info.allocated_sockets; i++)
489     {
490     if (init_sockets[i].status == Ns_Avail)
491     continue;
492     if (FD_ISSET (init_sockets[i].fd, &tmp_exceptions))
493     {
494     free_newsocket (&init_sockets[i]);
495 root 1.3 init_sockets[i].status = Ns_Avail;
496     socket_info.nconns--;
497     continue;
498 root 1.6 }
499     if (FD_ISSET (init_sockets[i].fd, &tmp_read))
500     {
501     HandleClient (&init_sockets[i], NULL);
502     }
503     if (FD_ISSET (init_sockets[i].fd, &tmp_write))
504     {
505     init_sockets[i].can_write = 1;
506     }
507     }
508 elmex 1.1
509 root 1.6 /* This does roughly the same thing, but for the players now */
510     for (pl = first_player; pl != NULL; pl = next)
511     {
512 elmex 1.1
513 root 1.6 next = pl->next;
514     if (pl->socket.status == Ns_Dead)
515     continue;
516    
517     if (FD_ISSET (pl->socket.fd, &tmp_write))
518     {
519     if (!pl->socket.can_write)
520     {
521 elmex 1.1 #if 0
522 root 1.6 LOG (llevDebug, "Player %s socket now write enabled\n", pl->ob->name);
523 elmex 1.1 #endif
524 root 1.6 pl->socket.can_write = 1;
525     write_socket_buffer (&pl->socket);
526 root 1.3 }
527 root 1.6 /* if we get an error on the write_socket buffer, no reason to
528     * continue on this socket.
529     */
530     if (pl->socket.status == Ns_Dead)
531     continue;
532     }
533     else
534     pl->socket.can_write = 0;
535    
536     if (FD_ISSET (pl->socket.fd, &tmp_exceptions))
537     {
538     save_player (pl->ob, 0);
539     if (!QUERY_FLAG (pl->ob, FLAG_REMOVED))
540     {
541     terminate_all_pets (pl->ob);
542     remove_ob (pl->ob);
543 root 1.3 }
544 root 1.6 leave (pl, 1);
545     final_free_player (pl);
546 root 1.3 }
547 root 1.6 else
548     {
549     HandleClient (&pl->socket, pl);
550     /* If the player has left the game, then the socket status
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     */
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 root 1.3 }
563 root 1.6 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);
573     if (pl->last_weight != -1 && pl->last_weight != WEIGHT (pl->ob))
574     {
575     esrv_update_item (UPD_WEIGHT, pl->ob, pl->ob);
576     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",
578     (unsigned long) pl->last_weight, WEIGHT (pl->ob));
579 root 1.3 }
580 root 1.6 /* draw_client_map does sanity checking that map is
581     * valid, so don't do it here.
582     */
583     draw_client_map (pl->ob);
584     if (pl->socket.update_look)
585     esrv_draw_look (pl->ob);
586 root 1.3 }
587     }
588 elmex 1.1 }
589     }