ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/loop.C
Revision: 1.7
Committed: Thu Sep 14 22:34:05 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.6: +1 -7 lines
Log Message:
indent

File Contents

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