ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/main.C
Revision: 1.181
Committed: Wed Dec 5 19:03:27 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.180: +12 -12 lines
Log Message:
some bugfixes

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 * Copyright (©) 2001-2003 Mark Wedel & Crossfire Development Team
7 * Copyright (©) 1992 Frank Tore Johansen
8 *
9 * Deliantra is free software: you can redistribute it and/or modify it under
10 * the terms of the Affero GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the Affero GNU General Public License
20 * and the GNU General Public License along with this program. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * The authors can be reached via e-mail to <support@deliantra.net>
24 */
25
26 #include <global.h>
27 #include <object.h>
28 #include <tod.h>
29
30 #include <sproto.h>
31 #include <time.h>
32
33 #include <glib.h>
34
35 #include <rmg.h>
36 #include <rproto.h>
37 #include "path.h"
38
39 void
40 version (object *op)
41 {
42 new_draw_info_format (NDI_UNIQUE, 0, op, "This is Deliantra v%s", VERSION);
43 }
44
45 /* This is a basic little function to put the player back to his
46 * savebed.
47 */
48 void
49 enter_player_savebed (object *op)
50 {
51 op->player_goto (op->contr->savebed_map, op->contr->bed_x, op->contr->bed_y);
52 }
53
54 /*
55 * enter_map(): Moves the player and pets from current map (if any) to
56 * new map. map, x, y must be set. map is the map we are moving the
57 * player to - it could be the map he just came from if the load failed for
58 * whatever reason. If default map coordinates are to be used, then
59 * the function that calls this should figure them out.
60 */
61 bool
62 object::enter_map (maptile *newmap, int x, int y)
63 {
64 if (destroyed () || !newmap || !newmap->linkable ())
65 return false;
66
67 if (out_of_map (newmap, x, y))
68 {
69 LOG (llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", &newmap->path, x, y);
70 x = newmap->enter_x;
71 y = newmap->enter_y;
72 if (out_of_map (newmap, x, y))
73 {
74 LOG (llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n",
75 &newmap->path, x, y, newmap->width, newmap->height);
76 new_draw_info (NDI_UNIQUE, 0, this, "The exit is closed");
77 return false;
78 }
79 }
80
81 if (contr && map != newmap && map)
82 if (INVOKE_MAP (LEAVE, map, ARG_PLAYER (contr)))
83 return false;
84
85 // remove, so stupid blocked does not trigger a failure
86 remove ();
87
88 /* try to find a spot for the player */
89 if (blocked (newmap, x, y))
90 { /* First choice blocked */
91 /* We try to find a spot for the player, starting closest in.
92 * We could use find_first_free_spot, but that doesn't randomize it at all,
93 * So for example, if the north space is free, you would always end up there even
94 * if other spaces around are available.
95 * Note that for the second and third calls, we could start at a position other
96 * than one, but then we could end up on the other side of walls and so forth.
97 */
98 int i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE1 + 1);
99
100 if (i < 0)
101 {
102 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE2 + 1);
103 if (i < 0)
104 i = find_free_spot (this, newmap, x, y, 1, SIZEOFFREE);
105 }
106
107 if (i >= 0)
108 {
109 maptile *m = newmap;
110 sint16 nx = x + DIRX (i);
111 sint16 ny = y + DIRY (i);
112
113 if (xy_normalise (m, nx, ny))
114 {
115 newmap = m;
116 x = nx;
117 y = ny;
118 }
119 }
120 else
121 /* not much we can do in this case. */
122 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);
123 }
124
125 if (contr && map != newmap)
126 {
127 if (INVOKE_PLAYER (MAP_CHANGE, contr, ARG_MAP (newmap), ARG_INT (x), ARG_INT (y)))
128 return false;
129
130 if (INVOKE_MAP (ENTER, newmap, ARG_PLAYER (contr), ARG_INT (x), ARG_INT (y)))
131 return false;
132 }
133
134 enemy = 0;
135
136 newmap->activate (); // workaround for activate activating everyhing on the map, includign the palyer on the {link} map
137 //newmap->insert (this, x, y);
138 newmap->insert (this, x, y, 0, INS_NO_AUTO_EXIT);
139 prefetch_surrounding_maps ();
140
141 if (map == newmap) // see if we actually arrived there - insert might trigger a teleport
142 {
143 if (contr)
144 {
145 contr->maplevel = newmap->path;
146 contr->count = 0;
147
148 /* Update any golems */
149 if (object *golem = contr->golem)
150 {
151 int i = find_free_spot (golem, newmap, x, y, 1, SIZEOFFREE);
152
153 if (i < 0)
154 golem->drop_and_destroy ();
155 else
156 {
157 newmap->insert (golem, x + DIRX (i), y + DIRY (i));
158 golem->direction = find_dir_2 (golem->x - x, golem->y - y);
159 }
160 }
161 }
162
163 /* since the players map is already loaded, we don't need to worry
164 * about pending objects.
165 */
166 move_all_pets ();
167
168 return true;
169 }
170
171 return false;
172 }
173
174 /* process_players1 and process_players2 do all the player related stuff.
175 * I moved it out of process events and process_map. This was to some
176 * extent for debugging as well as to get a better idea of the time used
177 * by the various functions. process_players1() does the processing before
178 * objects have been updated, process_players2() does the processing that
179 * is needed after the players have been updated.
180 */
181 static void
182 process_players1 ()
183 {
184 /* Basically, we keep looping until all the players have done their actions. */
185 for (int flag = 1; flag != 0;)
186 {
187 flag = 0;
188 for_all_players (pl)
189 {
190 pl->refcnt_chk ();
191
192 if (ecb_expect_false (!pl->ob || !pl->ns || !pl->ob->active))
193 continue;
194
195 if (handle_newcs_player (pl->ob))
196 flag = 1;
197 }
198 }
199
200 for_all_players (pl)
201 {
202 object *ob = pl->ob;
203
204 // process_objects destroys the speed_left value
205 pl->speed_left_save = ob->speed_left;
206
207 if (ecb_expect_false (!ob || !pl->ns || !ob->active))
208 continue;
209
210 do_some_living (ob);
211 }
212 }
213
214 static void
215 process_players2 ()
216 {
217 /* Then check if any players should use weapon-speed instead of speed */
218 for_all_players (pl)
219 {
220 // restore speed_left value saved by process_players1
221 pl->ob->speed_left = pl->speed_left_save;
222
223 pl->weapon_sp_left = min (pl->weapon_sp, pl->weapon_sp_left + pl->weapon_sp);
224 pl->ob->speed_left = min (pl->ob->speed, pl->ob->speed_left + pl->ob->speed);
225 }
226 }
227
228 static void
229 process_events ()
230 {
231 process_players1 ();
232
233 for_all_actives (op)
234 {
235 // try to prefetch some stuff we expect to need
236 // obviously, it should be grouped into the same cacheline.
237 // preliminary results indicate that this gives halves the speed
238 // used for the inner loop
239 if (_i < actives.size ()) // HACK, rely on _i :/
240 {
241 object *next = actives [_i + 1];
242
243 ecb_prefetch (&next->flag , 0, 1);
244 ecb_prefetch (&next->speed , 0, 1);
245 ecb_prefetch (&next->anim_speed, 0, 1);
246 ecb_prefetch (&next->contr , 0, 1);
247 }
248
249 /* Now process op */
250 if (ecb_expect_false (op->flag [FLAG_FREED]))
251 {
252 LOG (llevError, "BUG: process_events(): Free object on list (%s)\n", op->debug_desc ());
253 op->set_speed (0);
254 continue;
255 }
256
257 if (ecb_expect_false (!op->has_active_speed ()))
258 {
259 LOG (llevError, "BUG: process_events(): Object %s has no speed (%f), "
260 "but is on active list\n", op->debug_desc (), op->speed);
261 op->set_speed (0);
262 continue;
263 }
264
265 if (ecb_expect_false (op->flag [FLAG_REMOVED]))
266 {
267 LOG (llevError, "BUG: process_events(): removed object is on active list: %s\n",
268 op->debug_desc ());
269 op->set_speed (0);
270 continue;
271 }
272
273 /* Animate the object. Bug or feature that anim_speed
274 * is based on ticks, and not the creatures speed?
275 */
276 if (op->anim_speed && op->last_anim >= op->anim_speed)
277 {
278 animate_object (op, op->contr ? op->facing : op->direction);
279 op->last_anim = 1;
280 }
281 else
282 ++op->last_anim;
283
284 if (ecb_expect_false (op->speed_left > 0.f))
285 {
286 --op->speed_left;
287 process_object (op);
288 }
289
290 // this will destroy the speed_left value for players, but
291 // process_players1 and ..2 save/restore the real value,
292 // so we can avoid a costly test here.
293 op->speed_left = min (op->speed, op->speed_left + op->speed);
294 }
295
296 process_players2 ();
297 }
298
299 /* clean up everything before exiting */
300 void
301 emergency_save ()
302 {
303 LOG (llevInfo, "emergency_save: enter\n");
304
305 LOG (llevInfo, "emergency_save: saving book archive\n");
306 write_book_archive ();
307
308 cfperl_emergency_save ();
309
310 LOG (llevInfo, "emergency_save: leave\n");
311 }
312
313 // send all clients some informational text
314 static void
315 cleanup_inform (const char *cause, bool make_core)
316 {
317 int flags = NDI_REPLY | NDI_UNIQUE | NDI_ALL | (make_core ? NDI_RED : NDI_GREEN);
318
319 new_draw_info_format (flags, 0, 0, "The server will now shutdown.");
320 new_draw_info_format (flags, 0, 0, "Cause for this shutdown: %s", cause);
321
322 if (make_core)
323 new_draw_info_format (flags, 0, 0, "This is considered a crash, but all maps and players have been saved.");
324 else
325 new_draw_info_format (flags, 0, 0, "This is considered to be a clean shutdown, and all maps and players will be saved now.");
326
327 new_draw_info_format (flags, 0, 0, "%s", CLEANUP_MESSAGE);
328
329 client::flush_sockets ();
330 }
331
332 /* clean up everything before exiting */
333 void
334 cleanup (const char *cause, bool make_core)
335 {
336 if (make_core)
337 fork_abort (cause);
338
339 LOG (make_core ? llevError : llevInfo, "cleanup cause: %s\n", cause);
340
341 if (!make_core)
342 cleanup_inform (cause, make_core);
343
344 LOG (llevDebug, "cleanup begin.\n");
345
346 if (init_done && !in_cleanup)
347 {
348 in_cleanup = true;
349 emergency_save ();
350 }
351 else
352 in_cleanup = true;
353
354 LOG (llevDebug, "running cleanup handlers.\n");
355 INVOKE_GLOBAL (CLEANUP);
356
357 LOG (llevDebug, "cleanup done.\n");
358
359 log_cleanup ();
360
361 if (make_core)
362 cleanup_inform (cause, make_core);
363 else
364 {
365 new_draw_info_format (NDI_REPLY | NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "Maps and players successfully saved, exiting.");
366 new_draw_info_format (NDI_REPLY | NDI_UNIQUE | NDI_ALL | NDI_GREEN, 0, 0, "And again: " CLEANUP_MESSAGE);
367 client::flush_sockets ();
368 }
369
370 cfperl_cleanup (make_core);
371 _exit (make_core);
372 }
373
374 /*
375 * do_specials() is a collection of functions to call from time to time.
376 * Modified 2000-1-14 MSW to use the global server_tick count to determine how
377 * often to do things. This will allow us to spred them out more often.
378 * I use prime numbers for the factor count - in that way, it is less likely
379 * these actions will fall on the same tick (compared to say using 500/2500/15000
380 * which would mean on that 15,000 tick count a whole bunch of stuff gets
381 * done). Of course, there can still be times where multiple specials are
382 * done on the same tick, but that will happen very infrequently
383 *
384 * I also think this code makes it easier to see how often we really are
385 * doing the various things.
386 */
387 static void
388 do_specials ()
389 {
390 shstr::gc ();
391 archetype::gc ();
392
393 if (ecb_expect_false (!(server_tick % TICKS_PER_HOUR)))
394 maptile::adjust_daylight ();
395
396 if (ecb_expect_false (!(server_tick % 2503)))
397 fix_weight (); /* Hack to fix weightproblems caused by bugs */
398
399 if (ecb_expect_false (!(server_tick % 5003)))
400 write_book_archive ();
401
402 if (ecb_expect_false (!(server_tick % 5009)))
403 clean_friendly_list ();
404
405 if (ecb_expect_false (!(server_tick % 5011)))
406 obsolete_parties ();
407
408 if (ecb_expect_false (!(server_tick % 12503)))
409 fix_luck ();
410 }
411
412 void
413 one_tick ()
414 {
415 // first do the user visible stuff
416 INVOKE_GLOBAL (CLOCK);
417 process_events (); // "do" something with objects with speed
418 client::clock (); // draw client maps etc.
419
420 // then do some bookkeeping, should not really be here
421 do_specials (); /* Routines called from time to time. */
422 attachable::check_mortals ();
423
424 // now that we aggressively reuse id's, this is very unlikely to happen...
425 if (object::object_count >= RESTART_COUNT)
426 cleanup ("running out of protocol ID values - need full restart");
427 }
428
429 // normal main
430 int
431 main (int argc, char **argv)
432 {
433 settings.argc = argc;
434 settings.argv = argv;
435
436 rndm.seed (time (0));
437
438 // temporary(?) testing hack
439 if (argc >= 2 && !strcmp (argv [1], "--noise"))
440 {
441 void noise_test ();
442 noise_test ();
443 exit (0);
444 }
445
446 #if 0
447 // code sometiems used for timing benchmarks
448 random_number_generator<freeciv_random_generator> rng;
449 rng.seed(0);
450 for (int i = 0; i < 10; ++i)
451 printf ("%08x\n", rng.get_u32());
452 for (int i = 0; i < 1000000; ++i)
453 volatile int r = rng.get_u32 ();
454 cstamp s = stamp ();
455 for (int i = 0; i < 1000000; ++i)
456 volatile int r = rng.next ();
457 printf ("c %d\n", (int)measure (s));
458 exit (0);
459 #endif
460
461 init (argc, argv);
462
463 for (;;)
464 cfperl_main ();
465 }
466