Atrinik Server  4.0
main.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program 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 2 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, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/gitversion.h>
32 #include <toolkit/string.h>
33 #include <plugin.h>
34 #include <arch.h>
35 #include <player.h>
36 #include <object.h>
37 #include <player.h>
38 #include <object_methods.h>
39 #include <waypoint.h>
40 #include <server.h>
41 
42 #include <toolkit/process.h>
43 #include <toolkit/console.h>
44 
45 #ifdef HAVE_CHECK
46 # include <check.h>
47 # include <check_proto.h>
48 #endif
49 
51 static object marker;
52 
68 
69 uint32_t global_round_tag;
70 int process_delay;
71 
72 static long shutdown_time;
73 static uint8_t shutdown_active = 0;
74 
75 static void dequeue_path_requests(void);
76 static void do_specials(void);
77 
84 void version(object *op)
85 {
86  char buf[HUGE_BUF];
87 
88  snprintf(VS(buf), "This is Atrinik v%s", PACKAGE_VERSION);
89 #ifdef GITVERSION
90  snprintfcat(VS(buf), "%s", " (" STRINGIFY(GITBRANCH) "/"
91  STRINGIFY(GITVERSION) " by " STRINGIFY(GITAUTHOR) ")");
92 #endif
93 
94  if (op != NULL) {
95  draw_info(COLOR_WHITE, op, buf);
96  } else {
97  LOG(INFO, "%s", buf);
98  }
99 }
100 
107 void leave_map(object *op)
108 {
109  object_remove(op, 0);
110 
111  if (!op->map->player_first) {
112  set_map_timeout(op->map);
113  }
114 
115  op->map = NULL;
116  CONTR(op)->last_update = NULL;
117 }
118 
125 {
126 #if MAP_DEFAULTTIMEOUT
127  uint32_t swap_time = MAP_SWAP_TIME(map);
128 
129  if (swap_time == 0) {
130  swap_time = MAP_DEFAULTTIMEOUT;
131  }
132 
133  if (swap_time >= MAP_MAXTIMEOUT) {
134  swap_time = MAP_MAXTIMEOUT;
135  }
136 
137  map->timeout = swap_time;
138 #else
139  /* Save out the map. */
140  swap_map(map, 0);
141 #endif
142 }
143 
147 void
149 {
150  object *op;
151  tag_t tag;
152 
153  /* Put marker object at beginning of active list */
154  marker.active_next = active_objects;
155 
156  if (marker.active_next) {
157  marker.active_next->active_prev = &marker;
158  }
159 
160  marker.active_prev = NULL;
162 
163  while (marker.active_next) {
164  op = marker.active_next;
165  tag = op->count;
166 
167  /* Move marker forward - swap op and marker */
168  op->active_prev = marker.active_prev;
169 
170  if (op->active_prev) {
171  op->active_prev->active_next = op;
172  } else {
173  active_objects = op;
174  }
175 
176  marker.active_next = op->active_next;
177 
178  if (marker.active_next) {
179  marker.active_next->active_prev = &marker;
180  }
181 
182  marker.active_prev = op;
183  op->active_next = &marker;
184 
185  /* Now process op */
186  if (unlikely(OBJECT_FREE(op))) {
187  LOG(ERROR, "Free object on active list");
188  op->speed = 0;
190  continue;
191  }
192 
193  if (unlikely(QUERY_FLAG(op, FLAG_REMOVED))) {
194  /*
195  * This is not actually an error; object_remove() doesn't remove
196  * active objects from the active list, since the two most common
197  * next steps are to either: re-insert the object elsewhere (for
198  * which we would have to re-add it to the active list), or destroy
199  * the object altogether (which does remove it from the active
200  * list).
201  *
202  * For now, just drop a DEVEL message about this case, so we can
203  * get a better idea of the objects that rely on this behavior.
204  */
205  LOG(DEVEL, "Removed object on active list: %s", object_get_str(op));
206  op->speed = 0;
208  continue;
209  }
210 
211  if (unlikely(DBL_EQUAL(op->speed, 0.0))) {
212  LOG(ERROR, "Object has no speed, but is on active list: %s",
213  object_get_str(op));
215  continue;
216  }
217 
218  if (unlikely(op->map == NULL && op->env == NULL)) {
219  LOG(ERROR, "Object without map or inventory is on active list: %s",
220  object_get_str(op));
221  op->speed = 0;
223  continue;
224  }
225 
226  /* As long we are > 0, we are not ready to swing. */
227  if (op->weapon_speed_left > 0) {
228  op->weapon_speed_left -= op->weapon_speed;
229  }
230 
231  if (op->speed_left <= 0) {
232  op->speed_left += FABS(op->speed);
233  }
234 
235  if (op->type == PLAYER && op->speed_left > op->speed) {
236  op->speed_left = op->speed;
237  }
238 
239  if (op->speed_left >= 0 || op->type == PLAYER) {
240  if (op->type != PLAYER) {
241  --op->speed_left;
242  }
243 
244  object_process(op);
245 
246  if (OBJECT_DESTROYED(op, tag)) {
247  continue;
248  }
249  }
250 
251  if (op->anim_flags & ANIM_FLAG_STOP_MOVING) {
252  op->anim_flags &= ~(ANIM_FLAG_MOVING | ANIM_FLAG_STOP_MOVING);
253  }
254 
255  if (op->anim_flags & ANIM_FLAG_STOP_ATTACKING) {
256  if (op->enemy == NULL || !attack_is_melee_range(op, op->enemy)) {
257  op->anim_flags &= ~ANIM_FLAG_ATTACKING;
258  }
259 
260  op->anim_flags &= ~ANIM_FLAG_STOP_ATTACKING;
261  }
262 
263  /* Handle archetype-field anim_speed differently when it comes to
264  * the animation. If we have a value on this we don't animate it
265  * at speed-events. */
266  if (QUERY_FLAG(op, FLAG_ANIMATE)) {
267  if (op->last_anim >= op->anim_speed) {
268  animate_object(op);
269  op->last_anim = 1;
270 
271  if (op->anim_flags & ANIM_FLAG_ATTACKING) {
272  op->anim_flags |= ANIM_FLAG_STOP_ATTACKING;
273  }
274 
275  if (op->anim_flags & ANIM_FLAG_MOVING) {
276  if ((op->anim_flags & ANIM_FLAG_ATTACKING &&
277  !(op->anim_flags & ANIM_FLAG_STOP_ATTACKING)) ||
278  op->type == PLAYER ||
279  !OBJECT_VALID(op->enemy, op->enemy_count)) {
280  op->anim_flags |= ANIM_FLAG_STOP_MOVING;
281  }
282  }
283  } else {
284  op->last_anim++;
285  }
286  }
287  }
288 
289  /* Remove marker object from active list */
290  if (marker.active_prev) {
291  marker.active_prev->active_next = NULL;
292  } else {
293  active_objects = NULL;
294  }
295 }
296 
300 void clean_tmp_files(void)
301 {
302  mapstruct *m, *tmp;
303 
304  /* We save the maps - it may not be intuitive why, but if there are
305  * unique items, we need to save the map so they get saved off. */
306  DL_FOREACH_SAFE(first_map, m, tmp)
307  {
308  if (m->in_memory == MAP_IN_MEMORY) {
310  swap_map(m, 0);
311  } else {
312  new_save_map(m, 0);
313  clean_tmp_map(m);
314  }
315  }
316  }
317 
318  /* Write the clock */
319  write_todclock();
320 
322  write_map_log();
323  }
324 }
325 
329 void server_shutdown(void)
330 {
332  clean_tmp_files();
333  exit(0);
334 }
335 
341 static void dequeue_path_requests(void)
342 {
343 #ifdef LEFTOVER_CPU_FOR_PATHFINDING
344  static struct timeval new_time;
345  long leftover_sec, leftover_usec;
346  object *wp;
347 
348  while ((wp = path_get_next_request())) {
350 
351  (void) GETTIMEOFDAY(&new_time);
352 
353  leftover_sec = last_time.tv_sec - new_time.tv_sec;
354  leftover_usec = max_time - (new_time.tv_usec - last_time.tv_usec);
355 
356  /* This is very ugly, but probably the fastest for our use: */
357  while (leftover_usec < 0) {
358  leftover_usec += 1000000;
359  leftover_sec -= 1;
360  }
361  while (leftover_usec > 1000000) {
362  leftover_usec -= 1000000;
363  leftover_sec += 1;
364  }
365 
366  /* Try to save about 10 ms */
367  if (leftover_sec < 1 && leftover_usec < 10000) {
368  break;
369  }
370  }
371 #else
372  object *wp = path_get_next_request();
373 
374  if (wp) {
376  }
377 #endif
378 }
379 
395 int swap_apartments(const char *mapold, const char *mapnew, int x, int y, object *op)
396 {
397  char *cleanpath, *path;
398  int i, j;
399  object *ob, *tmp, *tmp2;
400  mapstruct *oldmap, *newmap;
401 
402  /* So we can transfer our items from the old apartment. */
403  cleanpath = estrdup(mapold);
404  string_replace_char(cleanpath, "/", '$');
405  path = player_make_path(op->name, cleanpath);
406  oldmap = ready_map_name(path, NULL, MAP_PLAYER_UNIQUE);
407  efree(path);
408  efree(cleanpath);
409 
410  if (!oldmap) {
411  LOG(BUG, "Could not get oldmap using ready_map_name().");
412  return 0;
413  }
414 
415  /* Our new map. */
416  cleanpath = estrdup(mapnew);
417  string_replace_char(cleanpath, "/", '$');
418  path = player_make_path(op->name, cleanpath);
419  newmap = ready_map_name(path, NULL, MAP_PLAYER_UNIQUE);
420  efree(path);
421  efree(cleanpath);
422 
423  if (!newmap) {
424  LOG(BUG, "Could not get newmap using ready_map_name().");
425  return 0;
426  }
427 
428  /* Go through every square on old apartment map, looking for things
429  * to transfer. */
430  for (i = 0; i < MAP_WIDTH(oldmap); i++) {
431  for (j = 0; j < MAP_HEIGHT(oldmap); j++) {
432  for (ob = GET_MAP_OB(oldmap, i, j); ob; ob = tmp2) {
433  tmp2 = ob->above;
434 
435  /* We teleport any possible players here to emergency map. */
436  if (ob->type == PLAYER) {
437  object_enter_map(ob, NULL, NULL, 0, 0, false);
438  continue;
439  }
440 
441  /* If it's sys_object 1, there's no need to transfer it. */
442  if (QUERY_FLAG(ob, FLAG_SYS_OBJECT)) {
443  continue;
444  }
445 
446  /* A pickable item... Tranfer it */
447  if (!QUERY_FLAG(ob, FLAG_NO_PICK)) {
448  object_remove(ob, 0);
449  ob->x = x;
450  ob->y = y;
451  object_insert_map(ob, newmap, NULL, INS_NO_MERGE | INS_NO_WALK_ON);
452  } else {
453  /* Fixed part of map */
454 
455  /* Now we test for containers, because player
456  * can have items stored in it. So, go through
457  * the container and look for things to transfer. */
458  for (tmp = ob->inv; tmp; tmp = tmp2) {
459  tmp2 = tmp->below;
460 
461  if (QUERY_FLAG(tmp, FLAG_SYS_OBJECT) || QUERY_FLAG(tmp, FLAG_NO_PICK)) {
462  continue;
463  }
464 
465  object_remove(tmp, 0);
466  tmp->x = x;
467  tmp->y = y;
468  object_insert_map(tmp, newmap, NULL, INS_NO_MERGE | INS_NO_WALK_ON);
469  }
470  }
471  }
472  }
473  }
474 
475  /* Save the map */
476  new_save_map(newmap, 0);
477 
478  /* Check for old save bed */
479  if (strcmp(oldmap->path, CONTR(op)->savebed_map) == 0) {
480  strcpy(CONTR(op)->savebed_map, EMERGENCY_MAPPATH);
481  CONTR(op)->bed_x = EMERGENCY_X;
482  CONTR(op)->bed_y = EMERGENCY_Y;
483  }
484 
485  unlink(oldmap->path);
486 
487  /* Free the maps */
488  free_map(newmap, 1);
489  free_map(oldmap, 1);
490 
491  return 1;
492 }
493 
497 static void do_specials(void)
498 {
499  if (!(pticks % 2)) {
501  }
502 
503  if (!(pticks % PTICKS_PER_CLOCK)) {
504  tick_the_clock();
505  }
506 
507  /* Clears the tmp-files of maps which have reset */
508  if (!(pticks % 509)) {
509  flush_old_maps();
510  }
511 
512  if (*settings.server_host != '\0' && !(pticks % 2521)) {
514  }
515 
516  if (!(pticks % 40)) {
517  memory_check_all();
518  }
519 
520  if (!(pticks % 80)) {
521  process_check_all();
522  }
523 }
524 
525 void shutdown_timer_start(long secs)
526 {
527  shutdown_time = pticks + secs * MAX_TICKS;
528  shutdown_active = 1;
529 }
530 
531 void shutdown_timer_stop(void)
532 {
533  shutdown_active = 0;
534 }
535 
536 static int shutdown_timer_check(void)
537 {
538  if (!shutdown_active) {
539  return 0;
540  }
541 
542  if (pticks >= shutdown_time) {
543  return 1;
544  }
545 
546  if (((shutdown_time - pticks) % (long) (60 * MAX_TICKS)) == 0 ||
547  pticks == shutdown_time - (long) (5 * MAX_TICKS)) {
548  draw_info_type_format(CHAT_TYPE_CHAT, NULL, COLOR_GREEN, NULL,
549  "[Server]: Server will shut down in %02"PRIu64
550  ":%02"PRIu64 " minutes.", (uint64_t) ((shutdown_time - pticks) /
551  MAX_TICKS / 60), (uint64_t) ((shutdown_time - pticks) / (long)
552  MAX_TICKS % 60));
553  }
554 
555  return 0;
556 }
557 
561 void main_process(void)
562 {
563  /* Global round ticker. */
564  global_round_tag++;
565  pticks++;
566 
567  /* "do" something with objects with speed */
568  process_events();
569 
570  /* Removes unused maps after a certain timeout */
572 
573  /* Routines called from time to time. */
574  do_specials();
575 
576  trigger_global_event(GEVENT_TICK, NULL, NULL);
577 }
578 
588 int main(int argc, char **argv)
589 {
590 #ifdef WIN32
591  /* Open all files in binary mode by default. */
592  _set_fmode(_O_BINARY);
593 #endif
594 
595  init(argc, argv);
596  memset(&marker, 0, sizeof(struct obj));
597 
599  LOG(INFO, "Running plugin unit tests...");
600  object *activator = player_get_dummy(PLAYER_TESTING_NAME1, NULL);
601  object *me = player_get_dummy(PLAYER_TESTING_NAME2, NULL);
602  trigger_unit_event(activator, me);
603 
604  if (!settings.unit_tests) {
605  cleanup();
606  exit(0);
607  }
608  }
609 
610  if (settings.unit_tests) {
611 #ifdef HAVE_CHECK
612  LOG(INFO, "Running unit tests...");
613  cleanup();
614  check_main(argc, argv);
615  exit(0);
616 #else
617  LOG(ERROR, "Unit tests have not been compiled, aborting.");
618  exit(1);
619 #endif
620  }
621 
622  atexit(cleanup);
623 
624  if (settings.world_maker) {
625 #ifdef HAVE_WORLD_MAKER
626  LOG(INFO, "Running the world maker...");
627  world_maker();
628  exit(0);
629 #else
630  LOG(ERROR, "Cannot run world maker; server was not compiled with libgd, exiting...");
631  exit(1);
632 #endif
633  }
634 
635  if (!settings.no_console) {
636  console_start_thread();
637  }
638 
639  process_delay = 0;
640 
641  LOG(INFO, "Server ready. Waiting for connections...");
642 
643  for (; ; ) {
644  if (unlikely(shutdown_timer_check())) {
645  break;
646  }
647 
648  console_command_handle();
650 
651  if (++process_delay >= max_time_multiplier) {
652  process_delay = 0;
653  main_process();
654  }
655 
657 
658  /* Sleep proper amount of time before next tick */
659  sleep_delta();
660  }
661 
662  server_shutdown();
663 
664  return 0;
665 }
treasure_list_t * first_treasurelist
Definition: main.c:61
Definition: object.h:94
void animate_object(object *op)
Definition: anim.c:197
#define MAP_IN_MEMORY
Definition: map.h:170
#define FLAG_SYS_OBJECT
Definition: define.h:1243
double weapon_speed
Definition: object.h:475
tag_t enemy_count
Definition: object.h:216
void process_events(void)
Definition: main.c:148
#define MAP_WIDTH(m)
Definition: map.h:120
uint8_t last_anim
Definition: object.h:396
bool unit_tests
Definition: global.h:388
#define FLAG_ANIMATE
Definition: define.h:912
shstr * path
Definition: map.h:568
double speed_left
Definition: object.h:472
uint32_t in_memory
Definition: map.h:627
#define EMERGENCY_MAPPATH
Definition: config.h:134
bool attack_is_melee_range(object *hitter, object *enemy)
Definition: attack.c:1476
void waypoint_compute_path(object *op)
Definition: waypoint.c:180
#define GEVENT_TICK
Definition: plugin.h:161
void object_process(object *op)
struct obj * above
Definition: object.h:120
static void do_specials(void)
Definition: main.c:497
void check_active_maps(void)
Definition: swap.c:176
void leave_map(object *op)
Definition: main.c:107
#define PLAYER
Definition: define.h:122
int swap_apartments(const char *mapold, const char *mapnew, int x, int y, object *op)
Definition: main.c:395
bool object_enter_map(object *op, object *exit, mapstruct *m, int x, int y, bool fixed_pos)
Definition: object.c:2956
#define OBJECT_FREE(_ob_)
Definition: object.h:554
bool plugin_unit_tests
Definition: global.h:393
int new_save_map(mapstruct *m, int flag)
Definition: map.c:1363
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
#define MAP_PLAYER_UNIQUE
Definition: map.h:149
struct obj * enemy
Definition: object.h:196
void world_maker(void)
Definition: world_maker.c:490
void object_remove(object *op, int flags)
Definition: object.c:1623
struct timeval last_time
Definition: time.c:50
void free_map(mapstruct *m, int flag)
Definition: map.c:1493
void main_process(void)
Definition: main.c:561
struct artifact_list * first_artifactlist
Definition: main.c:63
void clean_tmp_files(void)
Definition: main.c:300
void server_shutdown(void)
Definition: main.c:329
int16_t y
Definition: object.h:276
#define MAP_DEFAULTTIMEOUT
Definition: config.h:108
void init(int argc, char **argv)
Definition: init.c:1215
object * player_get_dummy(const char *name, const char *host)
Definition: player.c:2583
const char * object_get_str(const object *op)
Definition: object.c:3151
void metaserver_info_update(void)
Definition: metaserver.c:588
bool recycle_tmp_maps
Definition: global.h:449
double weapon_speed_left
Definition: object.h:478
#define FLAG_NO_PICK
Definition: define.h:900
struct mapdef * map
Definition: object.h:139
struct obj * active_prev
Definition: object.h:111
char server_host[MAX_BUF]
Definition: global.h:368
object * active_objects
Definition: object.c:42
uint8_t world_maker
Definition: global.h:383
const char * name
Definition: object.h:168
struct obj * env
Definition: object.h:130
void write_todclock(void)
Definition: init.c:1158
struct obj * below
Definition: object.h:114
#define INS_NO_WALK_ON
Definition: object.h:570
#define MAP_MAXTIMEOUT
Definition: config.h:112
uint8_t anim_speed
Definition: object.h:393
void cleanup(void)
Definition: init.c:194
void player_disconnect_all(void)
Definition: player.c:79
#define MAP_HEIGHT(m)
Definition: map.h:122
void swap_map(mapstruct *map, int force_flag)
Definition: swap.c:128
double speed
Definition: object.h:469
void trigger_unit_event(object *const activator, object *const me)
Definition: plugins.c:477
#define OBJECT_DESTROYED(obj, tag)
Definition: object.h:716
static void dequeue_path_requests(void)
Definition: main.c:341
struct obj * active_next
Definition: object.h:103
int16_t x
Definition: object.h:273
#define FLAG_REMOVED
Definition: define.h:930
void tick_the_clock(void)
Definition: weather.c:67
object * object_insert_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1741
struct settings_struct settings
Definition: init.c:55
int32_t timeout
Definition: map.h:617
void sleep_delta(void)
Definition: time.c:174
void socket_server_post_process(void)
Definition: server.c:902
#define INS_NO_MERGE
Definition: object.h:564
void flush_old_maps(void)
Definition: swap.c:208
tag_t count
Definition: object.h:142
void version(object *op)
Definition: main.c:84
void write_map_log(void)
Definition: swap.c:37
uint8_t type
Definition: object.h:360
void trigger_global_event(int event_type, void *parm1, void *parm2)
Definition: plugins.c:455
object * player_first
Definition: map.h:594
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
struct obj * inv
Definition: object.h:123
uint8_t anim_flags
Definition: object.h:390
void socket_server_process(void)
Definition: server.c:722
object * path_get_next_request(void)
Definition: pathfinder.c:303
mapstruct * ready_map_name(const char *name, mapstruct *originator, int flags)
Definition: map.c:1584
player * first_player
Definition: main.c:57
void object_update_speed(object *op)
Definition: object.c:1043
static object marker
Definition: main.c:51
Definition: map.h:536
bool no_console
Definition: global.h:398
void clean_tmp_map(mapstruct *m)
Definition: map.c:1662
int main(int argc, char **argv)
Definition: main.c:588
mapstruct * first_map
Definition: main.c:59
#define PTICKS_PER_CLOCK
Definition: tod.h:34
player * last_player
Definition: main.c:67
void set_map_timeout(mapstruct *map)
Definition: main.c:124
#define MAP_SWAP_TIME(m)
Definition: map.h:92