ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/main.C
Revision: 1.112
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.111: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2001-2003,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 #include <global.h>
25 #include <object.h>
26 #include <tod.h>
27
28 #include <sproto.h>
29 #include <time.h>
30
31 #include <../random_maps/random_map.h>
32 #include <../random_maps/rproto.h>
33 #include "path.h"
34
35 static char days[7][4] = {
36 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
37 };
38
39 void
40 version (object *op)
41 {
42 new_draw_info_format (NDI_UNIQUE, 0, op, "This is Crossfire+ v%s", VERSION);
43
44 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to this program:");
45 new_draw_info (NDI_UNIQUE, 0, op, "Marc A. Lehmann <pcg@goof.com>");
46 new_draw_info (NDI_UNIQUE, 0, op, "Robin Redeker <elmex@x-paste.de>");
47 new_draw_info (NDI_UNIQUE, 0, op, "Pippijn van Steenhoven <pip88nl@gmail.com>");
48 new_draw_info (NDI_UNIQUE, 0, op, "");
49 new_draw_info (NDI_UNIQUE, 0, op, "Authors and contributors to the original Crossfire:");
50 new_draw_info (NDI_UNIQUE, 0, op, "Mark Wedel <mwedel@sonic.net>");
51 new_draw_info (NDI_UNIQUE, 0, op, "Frank Tore Johansen <frankj@ifi.uio.no>");
52 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Torgrim Homme <kjetilho@ifi.uio.no>");
53 new_draw_info (NDI_UNIQUE, 0, op, "Tyler Van Gorder <tvangod@ecst.csuchico.edu>");
54 new_draw_info (NDI_UNIQUE, 0, op, "Tony Elmroth <elmroth@cd.chalmers.se>");
55 new_draw_info (NDI_UNIQUE, 0, op, "Dougal Scott <dougal.scott@fcit.monasu.edu.au>");
56 new_draw_info (NDI_UNIQUE, 0, op, "William <wchuang@athena.mit.edu>");
57 new_draw_info (NDI_UNIQUE, 0, op, "Geoff Bailey <ftww@cs.su.oz.au>");
58 new_draw_info (NDI_UNIQUE, 0, op, "Kjetil Wiekhorst Jørgensen <jorgens@flipper.pvv.unit.no>");
59 new_draw_info (NDI_UNIQUE, 0, op, "Cameron Blackwood <c.blackwood@rdt.monash.edu.au>");
60 new_draw_info (NDI_UNIQUE, 0, op, "Joseph L. Traub <jtraub+@cmu.edu>");
61 new_draw_info (NDI_UNIQUE, 0, op, "Rupert G. Goldie <rgg@aaii.oz.au>");
62 new_draw_info (NDI_UNIQUE, 0, op, "Eric A. Anderson <eanders+@cmu.edu>");
63 new_draw_info (NDI_UNIQUE, 0, op, "Rickard Eneqvist <eneq@Prag.DoCS.UU.SE>");
64 new_draw_info (NDI_UNIQUE, 0, op, "Jarkko Sonninen <Jarkko.Sonninen@lut.fi>");
65 new_draw_info (NDI_UNIQUE, 0, op, "Karl Holland <kholland@sunlab.cit.cornell.du>");
66 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Lundgren <vick@bern.docs.uu.se>");
67 new_draw_info (NDI_UNIQUE, 0, op, "Mikael Olsson <mol@meryl.csd.uu.se>");
68 new_draw_info (NDI_UNIQUE, 0, op, "Tero Haatanen <Tero.Haatanen@lut.fi>");
69 new_draw_info (NDI_UNIQUE, 0, op, "Lasse Ylitalo <ylitalo@student.docs.uu.se>");
70 new_draw_info (NDI_UNIQUE, 0, op, "Niilo Neuvo <anipa@guru.magic.fi>");
71 new_draw_info (NDI_UNIQUE, 0, op, "Markku J{rvinen <mta@modeemi.cs.tut.fi>");
72 new_draw_info (NDI_UNIQUE, 0, op, "Sylvain Meunier <meunier@inf.enst.fr>");
73 new_draw_info (NDI_UNIQUE, 0, op, "Jason Fosback <jfosback@darmok.uoregon.edu>");
74 new_draw_info (NDI_UNIQUE, 0, op, "Carl Edman <cedman@capitalist.princeton.edu>");
75 new_draw_info (NDI_UNIQUE, 0, op, "Charles Henrich <henrich@crh.cl.msu.edu>");
76 new_draw_info (NDI_UNIQUE, 0, op, "Gregor Schmid <schmid@fb3-s7.math.tu-berlin.de>");
77 new_draw_info (NDI_UNIQUE, 0, op, "Raphael Quinet <quinet@montefiore.ulg.ac.be>");
78 new_draw_info (NDI_UNIQUE, 0, op, "Jari Vanhala <jam@modeemi.cs.tut.fi>");
79 new_draw_info (NDI_UNIQUE, 0, op, "Tero Kivinen <kivinen@joker.cs.hut.fi>");
80 new_draw_info (NDI_UNIQUE, 0, op, "Peter Mardahl <peterm@soda.berkeley.edu>");
81 new_draw_info (NDI_UNIQUE, 0, op, "Matthew Zeher <matt@cs.odu.edu>");
82 new_draw_info (NDI_UNIQUE, 0, op, "Scott R. Turner <srt@sun-dimas.aero.org>");
83 new_draw_info (NDI_UNIQUE, 0, op, "Ben Fennema <huma@netcom.com>");
84 new_draw_info (NDI_UNIQUE, 0, op, "Nick Williams <njw@cs.city.ac.uk>");
85 new_draw_info (NDI_UNIQUE, 0, op, "Laurent Wacrenier <Wacren@Gin.ObsPM.Fr>");
86 new_draw_info (NDI_UNIQUE, 0, op, "Brian Thomas <thomas@astro.psu.edu>");
87 new_draw_info (NDI_UNIQUE, 0, op, "John Steven Moerk <jsm@axon.ksc.nasa.gov>");
88 new_draw_info (NDI_UNIQUE, 0, op, "Delbecq David <david.delbecq@mailandnews.com>");
89 new_draw_info (NDI_UNIQUE, 0, op, "Chachkoff Yann <yann.chachkoff@mailandnews.com>\n");
90 new_draw_info (NDI_UNIQUE, 0, op, "");
91 new_draw_info (NDI_UNIQUE, 0, op, "Images and art:");
92 new_draw_info (NDI_UNIQUE, 0, op, "Peter Gardner");
93 new_draw_info (NDI_UNIQUE, 0, op, "David Gervais <david_eg@mail.com>");
94 new_draw_info (NDI_UNIQUE, 0, op, "Mitsuhiro Itakura <ita@gold.koma.jaeri.go.jp>");
95 new_draw_info (NDI_UNIQUE, 0, op, "Hansjoerg Malthaner <hansjoerg.malthaner@danet.de>");
96 new_draw_info (NDI_UNIQUE, 0, op, "Mårten Woxberg <maxmc@telia.com>");
97 new_draw_info (NDI_UNIQUE, 0, op, "");
98 new_draw_info (NDI_UNIQUE, 0, op, "And many more!");
99 }
100
101 /* This is a basic little function to put the player back to his
102 * savebed. We do some error checking - its possible that the
103 * savebed map may no longer exist, so we make sure the player
104 * goes someplace.
105 */
106 void
107 enter_player_savebed (object *op)
108 {
109 object *tmp = object::create ();
110 EXIT_PATH (tmp) = op->contr->savebed_map;
111 EXIT_X (tmp) = op->contr->bed_x;
112 EXIT_Y (tmp) = op->contr->bed_y;
113 op->enter_exit (tmp);
114 tmp->destroy ();
115 }
116
117 /*
118 * enter_map(): Moves the player and pets from current map (if any) to
119 * new map. map, x, y must be set. map is the map we are moving the
120 * player to - it could be the map he just came from if the load failed for
121 * whatever reason. If default map coordinates are to be used, then
122 * the function that calls this should figure them out.
123 */
124 void
125 object::enter_map (maptile *newmap, int x, int y)
126 {
127 if (destroyed () || !newmap || newmap->in_memory != MAP_IN_MEMORY)
128 return;
129
130 if (out_of_map (newmap, x, y))
131 {
132 LOG (llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", &newmap->path, x, y);
133 x = newmap->enter_x;
134 y = newmap->enter_y;
135 if (out_of_map (newmap, x, y))
136 {
137 LOG (llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n",
138 &newmap->path, x, y, newmap->width, newmap->height);
139 new_draw_info (NDI_UNIQUE, 0, this, "The exit is closed");
140 return;
141 }
142 }
143
144 if (contr && map != newmap && map)
145 if (INVOKE_MAP (LEAVE, map, ARG_PLAYER (contr)))
146 return;
147
148 // remove, so stupid ob_locked does not trigger a failure
149 remove ();
150
151 /* try to find a spot for the player */
152 if (ob_blocked (this, newmap, x, y))
153 { /* First choice blocked */
154 /* We try to find a spot for the player, starting closest in.
155 * We could use find_first_free_spot, but that doesn't randomize it at all,
156 * So for example, if the north space is free, you would always end up there even
157 * if other spaces around are available.
158 * Note that for the second and third calls, we could start at a position other
159 * than one, but then we could end up on the other side of walls and so forth.
160 */
161 int i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE1 + 1);
162
163 if (i == -1)
164 {
165 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE2 + 1);
166 if (i == -1)
167 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE);
168 }
169
170 if (i != -1)
171 {
172 x += freearr_x[i];
173 y += freearr_y[i];
174 }
175 else
176 /* not much we can do in this case. */
177 LOG (llevInfo, "enter_map: Could not find free spot for player - will dump on top of object (%s: %d, %d)\n", &newmap->path, x, y);
178 }
179
180 if (contr && map != newmap)
181 {
182 if (INVOKE_PLAYER (MAP_CHANGE, contr, ARG_MAP (newmap), ARG_INT (x), ARG_INT (y)))
183 return;
184
185 if (INVOKE_MAP (ENTER, newmap, ARG_PLAYER (contr), ARG_INT (x), ARG_INT (y)))
186 return;
187 }
188
189 this->x = x;
190 this->y = y;
191 map = newmap;
192
193 insert_ob_in_map (this, map, 0, INS_NO_WALK_ON);
194
195 enemy = 0;
196
197 if (contr)
198 {
199 contr->maplevel = newmap->path;
200 contr->count = 0;
201 }
202
203 /* Update any golems */
204 if (type == PLAYER)
205 if (object *golem = contr->golem)
206 {
207 int i = find_free_spot (golem, newmap, x, y, 1, SIZEOFFREE);
208
209 if (i < 0)
210 golem->destroy ();
211 else
212 {
213 newmap->insert (golem, x + freearr_x[i], y + freearr_y[i]);
214 golem->direction = find_dir_2 (x - golem->x, y - golem->y);
215 }
216 }
217
218 /* since the players map is already loaded, we don't need to worry
219 * about pending objects.
220 */
221 remove_all_pets (newmap);
222 }
223
224 /* process_players1 and process_players2 do all the player related stuff.
225 * I moved it out of process events and process_map. This was to some
226 * extent for debugging as well as to get a better idea of the time used
227 * by the various functions. process_players1() does the processing before
228 * objects have been updated, process_players2() does the processing that
229 * is needed after the players have been updated.
230 */
231 static void
232 process_players1 ()
233 {
234 /* Basically, we keep looping until all the players have done their actions. */
235 for (int flag = 1; flag != 0;)
236 {
237 flag = 0;
238 for_all_players (pl)
239 {
240 pl->refcnt_chk ();
241
242 if (expect_false (!pl->ob || !pl->ns || !pl->ob->active))
243 continue;
244
245 if (handle_newcs_player (pl->ob))
246 flag = 1;
247 }
248 }
249
250 for_all_players (pl)
251 {
252 object *ob = pl->ob;
253
254 if (expect_false (!ob || !pl->ns || !ob->active))
255 continue;
256
257 do_some_living (ob);
258 }
259 }
260
261 static void
262 process_players2 ()
263 {
264 /* Then check if any players should use weapon-speed instead of speed */
265 for_all_players (pl)
266 {
267 pl->weapon_sp_left = min (pl->weapon_sp, pl->weapon_sp_left + pl->weapon_sp);
268 pl->ob->speed_left = min (pl->ob->speed, pl->ob->speed_left + pl->ob->speed);
269 }
270 }
271
272 void
273 process_events ()
274 {
275 process_players1 ();
276
277 for_all_actives (op)
278 {
279 /* Now process op */
280 if (expect_false (QUERY_FLAG (op, FLAG_FREED)))
281 {
282 LOG (llevError, "BUG: process_events(): Free object on list\n");
283 op->set_speed (0);
284 continue;
285 }
286
287 if (expect_false (!op->has_active_speed ()))
288 {
289 LOG (llevError, "BUG: process_events(): Object %s has no speed (%f), "
290 "but is on active list\n", op->debug_desc (), op->speed);
291 op->set_speed (0);
292 continue;
293 }
294
295 if (expect_false (op->flag [FLAG_REMOVED]))
296 {
297 LOG (llevError, "BUG: process_events(): removed object is on active list: %s\n",
298 op->debug_desc ());
299 op->set_speed (0);
300 continue;
301 }
302
303 /* Animate the object. Bug or feature that anim_speed
304 * is based on ticks, and not the creatures speed?
305 */
306 if (op->anim_speed && op->last_anim >= op->anim_speed)
307 {
308 animate_object (op, op->contr ? op->facing : op->direction);
309 op->last_anim = 1;
310 }
311 else
312 ++op->last_anim;
313
314 if (op->speed_left > 0.f)
315 {
316 --op->speed_left;
317 process_object (op);
318
319 if (op->destroyed ())
320 continue;
321 }
322
323 if (!op->contr)
324 op->speed_left = min (abs (op->speed), op->speed_left + abs (op->speed));
325 }
326
327 process_players2 ();
328 }
329
330 /* clean up everything before exiting */
331 void
332 emergency_save ()
333 {
334 LOG (llevDebug, "emergency save begin.\n");
335
336 cfperl_emergency_save ();
337
338 LOG (llevDebug, "saving book archive.\n");
339 write_book_archive ();
340
341 LOG (llevDebug, "emergency save done.\n");
342 }
343
344 // send all clients some informational text
345 static void
346 cleanup_inform (const char *cause, bool make_core)
347 {
348 int flags = NDI_UNIQUE | NDI_ALL | (make_core ? NDI_RED : NDI_GREEN);
349
350 new_draw_info_format (flags, 0, 0, "The server will now shutdown.");
351 new_draw_info_format (flags, 0, 0, "Cause for this shutdown: %s", cause);
352
353 if (make_core)
354 new_draw_info_format (flags, 0, 0, "This is considered a crash, but all maps and players have been saved.");
355 else
356 new_draw_info_format (flags, 0, 0, "This is considered to be a clean shutdown, and all maps and players will be saved now.");
357
358 new_draw_info_format (flags, 0, 0, "%s", CLEANUP_MESSAGE);
359
360 flush_sockets ();
361 }
362
363 /* clean up everything before exiting */
364 void
365 cleanup (const char *cause, bool make_core)
366 {
367 if (make_core)
368 fork_abort (cause);
369
370 LOG (llevError, "cleanup cause: %s\n", cause);
371
372 if (!make_core)
373 cleanup_inform (cause, make_core);
374
375 LOG (llevDebug, "cleanup begin.\n");
376
377 if (init_done && !in_cleanup)
378 {
379 in_cleanup = true;
380 emergency_save ();
381 }
382 else
383 in_cleanup = true;
384
385 LOG (llevDebug, "running cleanup handlers.\n");
386 INVOKE_GLOBAL (CLEANUP);
387
388 LOG (llevDebug, "cleanup done.\n");
389
390 if (make_core)
391 {
392 cleanup_inform (cause, make_core);
393 cfperl_cleanup (make_core);
394 _exit (1);
395 }
396 else
397 {
398 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "Maps and players successfully saved, exiting.");
399 new_draw_info_format (NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "And again: " CLEANUP_MESSAGE);
400 flush_sockets ();
401 cfperl_cleanup (make_core);
402 _exit (0);
403 }
404 }
405
406 int
407 forbid_play (void)
408 {
409 #if !defined(_IBMR2) && !defined(___IBMR2) && defined(PERM_FILE)
410 char buf[MAX_BUF], day[MAX_BUF];
411 FILE *fp;
412 time_t clock;
413 struct tm *tm;
414 int i, start, stop, forbit = 0, comp;
415
416 clock = time (NULL);
417 tm = (struct tm *) localtime (&clock);
418
419 sprintf (buf, "%s/%s", settings.confdir, PERM_FILE);
420 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
421 return 0;
422
423 while (fgets (buf, MAX_BUF, fp))
424 {
425 if (buf[0] == '#')
426 continue;
427
428 if (!strncmp (buf, "msg", 3))
429 {
430 if (forbit)
431 while (fgets (buf, MAX_BUF, fp)) /* print message */
432 fputs (buf, logfile);
433 break;
434 }
435 else if (sscanf (buf, "%s %d%*c%d\n", day, &start, &stop) != 3)
436 {
437 LOG (llevDebug, "Warning: Incomplete line in permission file ignored.\n");
438 continue;
439 }
440
441 for (i = 0; i < 7; i++)
442 {
443 if (!strncmp (buf, days[i], 3) && (tm->tm_wday == i) && (tm->tm_hour >= start) && (tm->tm_hour < stop))
444 forbit = 1;
445 }
446 }
447
448 close_and_delete (fp, comp);
449
450 return forbit;
451 #else
452 return 0;
453 #endif
454 }
455
456 /*
457 * do_specials() is a collection of functions to call from time to time.
458 * Modified 2000-1-14 MSW to use the global pticks count to determine how
459 * often to do things. This will allow us to spred them out more often.
460 * I use prime numbers for the factor count - in that way, it is less likely
461 * these actions will fall on the same tick (compared to say using 500/2500/15000
462 * which would mean on that 15,000 tick count a whole bunch of stuff gets
463 * done). Of course, there can still be times where multiple specials are
464 * done on the same tick, but that will happen very infrequently
465 *
466 * I also think this code makes it easier to see how often we really are
467 * doing the various things.
468 */
469 void
470 do_specials (void)
471 {
472 if (expect_false (!(pticks % TICKS_PER_HOUR)))
473 adjust_daylight ();
474
475 if (expect_false (!(pticks % 7)))
476 shstr::gc ();
477
478 if (expect_false (!(pticks % 2503)))
479 fix_weight (); /* Hack to fix weightproblems caused by bugs */
480
481 if (expect_false (!(pticks % 5003)))
482 write_book_archive ();
483
484 if (expect_false (!(pticks % 5009)))
485 clean_friendly_list ();
486
487 if (expect_false (!(pticks % 5011)))
488 obsolete_parties ();
489
490 if (expect_false (!(pticks % 12503)))
491 fix_luck ();
492 }
493
494 void
495 server_tick ()
496 {
497 // first do the user visible stuff
498 doeric_server ();
499 INVOKE_GLOBAL (CLOCK);
500 process_events (); /* "do" something with objects with speed */
501 flush_sockets ();
502
503 // then do some bookkeeping, should not really be here
504 do_specials (); /* Routines called from time to time. */
505 attachable::check_mortals ();
506
507 ++pticks;
508 }
509
510 #if 0
511 // used fro benchmarking (x86/amd64-specific)
512 typedef unsigned long tval;
513 typedef unsigned long long stamp64;
514
515 extern inline tval
516 stamp (void)
517 {
518 tval tsc;
519 asm volatile ("rdtsc":"=a" (tsc)::"edx");
520
521 return tsc;
522 }
523
524 extern inline tval
525 measure (tval t)
526 {
527 tval tsc;
528 asm volatile ("rdtsc":"=a" (tsc)::"edx");
529
530 if (tsc > t)
531 return tsc - t;
532 else
533 return t - tsc;
534 }
535
536 int
537 main (int argc, char **argv)
538 {
539 rand_gen rg(0);
540 tval fastest = 0x7fffffff;
541 for (int loop = 10000; loop--; )
542 {
543 tval s = stamp ();
544 volatile int i = rg.get_int(25);
545 fastest = min (fastest, measure (s));
546 }
547
548 //printf ("fastest %d\n", fastest);
549 for (int i = 0; i < 1024*1024*3; ++i)
550 {
551 char c = rg.get_int (256);
552 write (2, &c, 1);
553 }
554 }
555
556 #else
557
558 // normal main
559 int
560 main (int argc, char **argv)
561 {
562 settings.argc = argc;
563 settings.argv = argv;
564
565 init (argc, argv);
566
567 initPlugins ();
568
569 for (;;)
570 cfperl_main ();
571 }
572 #endif
573