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 |
root |
1.10 |
#include <sproto.h> |
38 |
|
|
#include <sockproto.h> |
39 |
elmex |
1.1 |
|
40 |
pippijn |
1.8 |
#include <sys/types.h> |
41 |
|
|
#include <sys/time.h> |
42 |
|
|
#include <sys/socket.h> |
43 |
|
|
#include <netinet/in.h> |
44 |
|
|
#include <netdb.h> |
45 |
elmex |
1.1 |
|
46 |
root |
1.16 |
#include <unistd.h> |
47 |
|
|
#include <arpa/inet.h> |
48 |
elmex |
1.1 |
|
49 |
|
|
#include <loader.h> |
50 |
|
|
#include <newserver.h> |
51 |
|
|
|
52 |
|
|
/***************************************************************************** |
53 |
|
|
* Start of command dispatch area. |
54 |
|
|
* The commands here are protocol commands. |
55 |
|
|
****************************************************************************/ |
56 |
|
|
|
57 |
|
|
/* Either keep this near the start or end of the file so it is |
58 |
|
|
* at least reasonablye easy to find. |
59 |
|
|
* There are really 2 commands - those which are sent/received |
60 |
|
|
* before player joins, and those happen after the player has joined. |
61 |
|
|
* As such, we have function types that might be called, so |
62 |
|
|
* we end up having 2 tables. |
63 |
|
|
*/ |
64 |
|
|
|
65 |
root |
1.13 |
typedef void (*func_uint8_int_ns) (char *, int, client_socket *); |
66 |
elmex |
1.1 |
|
67 |
root |
1.6 |
struct NsCmdMapping |
68 |
|
|
{ |
69 |
|
|
const char *cmdname; |
70 |
|
|
func_uint8_int_ns cmdproc; |
71 |
root |
1.11 |
uint8 flag; |
72 |
elmex |
1.1 |
}; |
73 |
|
|
|
74 |
root |
1.6 |
typedef void (*func_uint8_int_pl) (char *, int, player *); |
75 |
|
|
struct PlCmdMapping |
76 |
|
|
{ |
77 |
|
|
const char *cmdname; |
78 |
|
|
func_uint8_int_pl cmdproc; |
79 |
|
|
uint8 flag; |
80 |
elmex |
1.1 |
}; |
81 |
|
|
|
82 |
|
|
/** |
83 |
|
|
* Dispatch table for the server. |
84 |
|
|
* |
85 |
|
|
* CmdMapping is the dispatch table for the server, used in HandleClient, |
86 |
|
|
* which gets called when the client has input. All commands called here |
87 |
|
|
* use the same parameter form (char* data, int len, int clientnum. |
88 |
|
|
* We do implicit casts, because the data that is being passed is |
89 |
|
|
* unsigned (pretty much needs to be for binary data), however, most |
90 |
|
|
* of these treat it only as strings, so it makes things easier |
91 |
|
|
* to cast it here instead of a bunch of times in the function itself. |
92 |
|
|
* flag is 1 if the player must be in the playing state to issue the |
93 |
|
|
* command, 0 if they can issue it at any time. |
94 |
|
|
*/ |
95 |
|
|
static struct PlCmdMapping plcommands[] = { |
96 |
root |
1.6 |
{"examine", ExamineCmd, 1}, |
97 |
|
|
{"apply", ApplyCmd, 1}, |
98 |
|
|
{"move", MoveCmd, 1}, |
99 |
|
|
{"reply", ReplyCmd, 0}, |
100 |
|
|
{"command", PlayerCmd, 1}, |
101 |
|
|
{"ncom", (func_uint8_int_pl) NewPlayerCmd, 1}, |
102 |
|
|
{"lookat", LookAt, 1}, |
103 |
|
|
{"lock", (func_uint8_int_pl) LockItem, 1}, |
104 |
|
|
{"mark", (func_uint8_int_pl) MarkItem, 1}, |
105 |
|
|
{"mapredraw", MapRedrawCmd, 0}, /* Added: phil */ |
106 |
|
|
{"mapinfo", MapInfoCmd, 2}, /* CF+ */ |
107 |
|
|
{"ext", ExtCmd, 2}, /* CF+ */ |
108 |
root |
1.11 |
{ 0 } /* terminator */ |
109 |
elmex |
1.1 |
}; |
110 |
|
|
|
111 |
|
|
/** Face-related commands */ |
112 |
|
|
static struct NsCmdMapping nscommands[] = { |
113 |
root |
1.11 |
{"addme", AddMeCmd, 2}, |
114 |
root |
1.6 |
{"askface", SendFaceCmd}, /* Added: phil */ |
115 |
|
|
{"requestinfo", RequestInfo}, |
116 |
root |
1.11 |
{"setfacemode", SetFaceMode, 2}, |
117 |
|
|
{"setsound", SetSound, 2}, |
118 |
|
|
{"setup", SetUp, 2}, |
119 |
|
|
{"version", VersionCmd, 2}, |
120 |
|
|
{"toggleextendedinfos", ToggleExtendedInfos, 2}, /*Added: tchize */ |
121 |
|
|
{"toggleextendedtext", ToggleExtendedText, 2}, /*Added: tchize */ |
122 |
root |
1.6 |
{"asksmooth", AskSmooth}, /*Added: tchize (smoothing technologies) */ |
123 |
root |
1.11 |
{ 0 } /* terminator (I, II & III) */ |
124 |
elmex |
1.1 |
}; |
125 |
|
|
|
126 |
|
|
/** |
127 |
|
|
* RequestInfo is sort of a meta command. There is some specific |
128 |
|
|
* request of information, but we call other functions to provide |
129 |
|
|
* that information. |
130 |
|
|
*/ |
131 |
root |
1.6 |
void |
132 |
root |
1.13 |
RequestInfo (char *buf, int len, client_socket * ns) |
133 |
elmex |
1.1 |
{ |
134 |
root |
1.6 |
char *params = NULL, *cp; |
135 |
|
|
|
136 |
|
|
/* No match */ |
137 |
|
|
char bigbuf[MAX_BUF]; |
138 |
|
|
int slen; |
139 |
|
|
|
140 |
|
|
/* Set up replyinfo before we modify any of the buffers - this is used |
141 |
|
|
* if we don't find a match. |
142 |
|
|
*/ |
143 |
|
|
strcpy (bigbuf, "replyinfo "); |
144 |
|
|
slen = strlen (bigbuf); |
145 |
|
|
safe_strcat (bigbuf, buf, &slen, MAX_BUF); |
146 |
|
|
|
147 |
|
|
/* find the first space, make it null, and update the |
148 |
|
|
* params pointer. |
149 |
|
|
*/ |
150 |
|
|
for (cp = buf; *cp != '\0'; cp++) |
151 |
|
|
if (*cp == ' ') |
152 |
|
|
{ |
153 |
|
|
*cp = '\0'; |
154 |
|
|
params = cp + 1; |
155 |
|
|
break; |
156 |
|
|
} |
157 |
root |
1.10 |
|
158 |
root |
1.6 |
if (!strcmp (buf, "image_info")) |
159 |
|
|
send_image_info (ns, params); |
160 |
|
|
else if (!strcmp (buf, "image_sums")) |
161 |
|
|
send_image_sums (ns, params); |
162 |
|
|
else if (!strcmp (buf, "skill_info")) |
163 |
|
|
send_skill_info (ns, params); |
164 |
|
|
else if (!strcmp (buf, "spell_paths")) |
165 |
|
|
send_spell_paths (ns, params); |
166 |
|
|
else |
167 |
root |
1.12 |
ns->send_packet (bigbuf, len); |
168 |
elmex |
1.1 |
} |
169 |
|
|
|
170 |
|
|
/** |
171 |
|
|
* Handle client input. |
172 |
|
|
* |
173 |
|
|
* HandleClient is actually not named really well - we only get here once |
174 |
|
|
* there is input, so we don't do exception or other stuff here. |
175 |
|
|
* sock is the output socket information. pl is the player associated |
176 |
|
|
* with this socket, null if no player (one of the init_sockets for just |
177 |
|
|
* starting a connection) |
178 |
|
|
*/ |
179 |
|
|
|
180 |
root |
1.6 |
void |
181 |
root |
1.13 |
HandleClient (client_socket *ns, player *pl) |
182 |
elmex |
1.1 |
{ |
183 |
root |
1.6 |
/* Loop through this - maybe we have several complete packets here. */ |
184 |
|
|
// limit to a few commands only, though, as to not monopolise the server |
185 |
root |
1.11 |
for (int repeat = 16; repeat--;) |
186 |
root |
1.6 |
{ |
187 |
|
|
/* If it is a player, and they don't have any speed left, we |
188 |
|
|
* return, and will read in the data when they do have time. |
189 |
|
|
*/ |
190 |
root |
1.11 |
if (pl && pl->state == ST_PLAYING && pl->ob && pl->ob->speed_left < 0) |
191 |
|
|
return; |
192 |
elmex |
1.1 |
|
193 |
root |
1.11 |
int pkt_len = ns->read_packet (); |
194 |
root |
1.6 |
|
195 |
root |
1.11 |
if (pkt_len < 0) |
196 |
root |
1.6 |
{ |
197 |
root |
1.11 |
LOG (llevError, "read error on player %s\n", &pl->ob->name); |
198 |
root |
1.6 |
/* Caller will take care of cleaning this up */ |
199 |
|
|
ns->status = Ns_Dead; |
200 |
|
|
return; |
201 |
root |
1.3 |
} |
202 |
root |
1.11 |
else if (pkt_len == 0) |
203 |
|
|
/* Still dont have a full packet */ |
204 |
root |
1.6 |
return; |
205 |
elmex |
1.1 |
|
206 |
root |
1.6 |
/* First, break out beginning word. There are at least |
207 |
|
|
* a few commands that do not have any paremeters. If |
208 |
|
|
* we get such a command, don't worry about trying |
209 |
|
|
* to break it up. |
210 |
|
|
*/ |
211 |
root |
1.11 |
int datalen; |
212 |
|
|
char *data = strchr ((char *)ns->inbuf + 2, ' '); |
213 |
|
|
|
214 |
root |
1.6 |
if (data) |
215 |
|
|
{ |
216 |
root |
1.11 |
*data++ = 0; |
217 |
|
|
datalen = pkt_len - (data - (char *)ns->inbuf); |
218 |
root |
1.6 |
} |
219 |
|
|
else |
220 |
root |
1.11 |
{ |
221 |
|
|
data = (char *)ns->inbuf + 2; // better read garbage than segfault |
222 |
|
|
datalen = 0; |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
ns->inbuf [pkt_len] = 0; /* Terminate buffer - useful for string data */ |
226 |
root |
1.6 |
|
227 |
root |
1.11 |
for (int i = 0; nscommands[i].cmdname; i++) |
228 |
root |
1.6 |
{ |
229 |
root |
1.11 |
if (!strcmp ((char *)ns->inbuf + 2, nscommands[i].cmdname)) |
230 |
root |
1.6 |
{ |
231 |
root |
1.11 |
nscommands[i].cmdproc (data, datalen, ns); |
232 |
|
|
ns->skip_packet (pkt_len); |
233 |
|
|
//D// not doing this causes random memory corruption |
234 |
|
|
if (nscommands[i].flag & 2) |
235 |
|
|
goto next_packet; |
236 |
|
|
return; |
237 |
root |
1.3 |
} |
238 |
|
|
} |
239 |
root |
1.11 |
|
240 |
root |
1.6 |
/* Player must be in the playing state or the flag on the |
241 |
|
|
* the command must be zero for the user to use the command - |
242 |
|
|
* otherwise, a player cam save, be in the play_again state, and |
243 |
|
|
* the map they were on gets swapped out, yet things that try to look |
244 |
|
|
* at the map causes a crash. If the command is valid, but |
245 |
|
|
* one they can't use, we still swallow it up. |
246 |
|
|
*/ |
247 |
|
|
if (pl) |
248 |
root |
1.11 |
for (int i = 0; plcommands[i].cmdname; i++) |
249 |
root |
1.6 |
{ |
250 |
root |
1.11 |
if (!strcmp ((char *)ns->inbuf + 2, plcommands[i].cmdname)) |
251 |
root |
1.6 |
{ |
252 |
root |
1.3 |
if (pl->state == ST_PLAYING || !(plcommands[i].flag & 1)) |
253 |
root |
1.11 |
plcommands[i].cmdproc ((char *) data, datalen, pl); |
254 |
|
|
|
255 |
|
|
ns->skip_packet (pkt_len); |
256 |
elmex |
1.1 |
//D// not doing this causes random memory corruption |
257 |
|
|
if (plcommands[i].flag & 2) |
258 |
|
|
goto next_packet; |
259 |
root |
1.11 |
|
260 |
elmex |
1.1 |
return; |
261 |
root |
1.6 |
} |
262 |
|
|
} |
263 |
root |
1.11 |
|
264 |
root |
1.6 |
/* If we get here, we didn't find a valid command. Logging |
265 |
|
|
* this might be questionable, because a broken client/malicious |
266 |
|
|
* user could certainly send a whole bunch of invalid commands. |
267 |
|
|
*/ |
268 |
root |
1.11 |
LOG (llevDebug, "Bad command from client (%s)\n", ns->inbuf + 2); |
269 |
|
|
ns->skip_packet (pkt_len); |
270 |
elmex |
1.1 |
next_packet: |
271 |
root |
1.6 |
; |
272 |
elmex |
1.1 |
} |
273 |
|
|
} |
274 |
|
|
|
275 |
root |
1.6 |
void |
276 |
|
|
flush_sockets (void) |
277 |
elmex |
1.1 |
{ |
278 |
root |
1.15 |
for (sockvec::iterator i = client_sockets.begin (); i != client_sockets.end (); ++i) |
279 |
|
|
if ((*i)->status != Ns_Dead) |
280 |
|
|
(*i)->flush (); |
281 |
elmex |
1.1 |
} |
282 |
|
|
|
283 |
|
|
/** |
284 |
root |
1.10 |
* This checks the sockets for input, does the right thing. |
285 |
elmex |
1.1 |
* |
286 |
|
|
* A bit of this code is grabbed out of socket.c |
287 |
|
|
* There are 2 lists we need to look through - init_sockets is a list |
288 |
|
|
* |
289 |
|
|
*/ |
290 |
root |
1.6 |
void |
291 |
|
|
doeric_server (void) |
292 |
elmex |
1.1 |
{ |
293 |
root |
1.6 |
int i, pollret; |
294 |
root |
1.15 |
fd_set tmp_read; |
295 |
root |
1.6 |
struct sockaddr_in addr; |
296 |
|
|
socklen_t addrlen = sizeof (struct sockaddr); |
297 |
|
|
player *pl, *next; |
298 |
root |
1.14 |
int maxfd = 0; |
299 |
elmex |
1.1 |
|
300 |
|
|
#ifdef CS_LOGSTATS |
301 |
root |
1.6 |
if ((time (NULL) - cst_lst.time_start) >= CS_LOGTIME) |
302 |
|
|
write_cs_stats (); |
303 |
elmex |
1.1 |
#endif |
304 |
|
|
|
305 |
root |
1.6 |
/* Go through the players. Let the loop set the next pl value, |
306 |
|
|
* since we may remove some |
307 |
|
|
*/ |
308 |
root |
1.14 |
//TODO: must be handled cleanly elsewhere |
309 |
|
|
for (pl = first_player; pl; ) |
310 |
root |
1.6 |
{ |
311 |
root |
1.14 |
player *npl = pl->next; |
312 |
|
|
|
313 |
|
|
//TODO: must be handled cleanly elsewhere |
314 |
|
|
if (pl->socket->status == Ns_Dead) |
315 |
root |
1.6 |
{ |
316 |
|
|
save_player (pl->ob, 0); |
317 |
root |
1.10 |
|
318 |
root |
1.6 |
if (!QUERY_FLAG (pl->ob, FLAG_REMOVED)) |
319 |
|
|
{ |
320 |
|
|
terminate_all_pets (pl->ob); |
321 |
root |
1.9 |
pl->ob->remove (); |
322 |
root |
1.3 |
} |
323 |
root |
1.10 |
|
324 |
root |
1.6 |
leave (pl, 1); |
325 |
|
|
final_free_player (pl); |
326 |
root |
1.3 |
} |
327 |
elmex |
1.1 |
|
328 |
root |
1.14 |
pl = npl; |
329 |
elmex |
1.1 |
} |
330 |
|
|
|
331 |
root |
1.14 |
FD_ZERO (&tmp_read); |
332 |
elmex |
1.1 |
|
333 |
root |
1.14 |
for (sockvec::iterator i = client_sockets.begin (); i != client_sockets.end (); ) |
334 |
root |
1.6 |
{ |
335 |
root |
1.14 |
client_socket *s = *i; |
336 |
elmex |
1.1 |
|
337 |
root |
1.14 |
if (s->status == Ns_Dead) |
338 |
root |
1.6 |
{ |
339 |
root |
1.14 |
client_sockets.erase (i); |
340 |
|
|
delete s; |
341 |
root |
1.6 |
} |
342 |
|
|
else |
343 |
|
|
{ |
344 |
root |
1.14 |
if (s->fd > maxfd) maxfd = s->fd; |
345 |
|
|
|
346 |
|
|
FD_SET (s->fd, &tmp_read); |
347 |
root |
1.6 |
|
348 |
root |
1.14 |
++i; |
349 |
root |
1.6 |
} |
350 |
root |
1.14 |
} |
351 |
root |
1.10 |
|
352 |
root |
1.14 |
struct timeval timeout; |
353 |
root |
1.6 |
|
354 |
root |
1.14 |
timeout.tv_sec = 0; |
355 |
|
|
timeout.tv_usec = 0; |
356 |
root |
1.6 |
|
357 |
root |
1.14 |
pollret = select (maxfd + 1, |
358 |
root |
1.15 |
&tmp_read, 0, 0, |
359 |
root |
1.14 |
&timeout); |
360 |
root |
1.6 |
|
361 |
root |
1.14 |
if (pollret == -1) |
362 |
|
|
{ |
363 |
|
|
LOG (llevError, "select failed: %s\n", strerror (errno)); |
364 |
|
|
return; |
365 |
elmex |
1.1 |
} |
366 |
|
|
|
367 |
root |
1.14 |
/* We need to do some of the processing below regardless */ |
368 |
|
|
|
369 |
root |
1.10 |
/* Check for any input on the sockets */ |
370 |
root |
1.14 |
for (sockvec::iterator i = client_sockets.begin (); i != client_sockets.end (); ++i) |
371 |
root |
1.6 |
{ |
372 |
root |
1.14 |
client_socket *s = *i; |
373 |
|
|
player *pl = s->pl; |
374 |
elmex |
1.1 |
|
375 |
root |
1.14 |
//TODO: disassociate handleclient from socket readin |
376 |
|
|
if (s->inbuf_len || FD_ISSET (s->fd, &tmp_read)) |
377 |
|
|
HandleClient (s, pl); |
378 |
root |
1.10 |
|
379 |
root |
1.14 |
//TODO: should not be done here, either |
380 |
|
|
if (s->status != Ns_Dead && pl) |
381 |
root |
1.6 |
{ |
382 |
root |
1.10 |
/* Update the players stats once per tick. More efficient than |
383 |
|
|
* sending them whenever they change, and probably just as useful |
384 |
root |
1.6 |
*/ |
385 |
root |
1.10 |
esrv_update_stats (pl); |
386 |
|
|
if (pl->last_weight != -1 && pl->last_weight != WEIGHT (pl->ob)) |
387 |
root |
1.6 |
{ |
388 |
root |
1.10 |
esrv_update_item (UPD_WEIGHT, pl->ob, pl->ob); |
389 |
|
|
if (pl->last_weight != WEIGHT (pl->ob)) |
390 |
|
|
LOG (llevError, "esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n", |
391 |
|
|
(unsigned long) pl->last_weight, WEIGHT (pl->ob)); |
392 |
root |
1.6 |
} |
393 |
|
|
|
394 |
root |
1.10 |
/* draw_client_map does sanity checking that map is |
395 |
|
|
* valid, so don't do it here. |
396 |
|
|
*/ |
397 |
|
|
draw_client_map (pl->ob); |
398 |
root |
1.14 |
if (s->update_look) |
399 |
root |
1.10 |
esrv_draw_look (pl->ob); |
400 |
root |
1.3 |
} |
401 |
elmex |
1.1 |
} |
402 |
|
|
} |
403 |
root |
1.10 |
|