|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 #include <check_proto.h> 00032 00033 #ifdef HAVE_CRYPT_H 00034 # include <crypt.h> 00035 #endif 00036 00037 #ifdef MEMPOOL_OBJECT_TRACKING 00038 extern void check_use_object_list(); 00039 #endif 00040 00042 static object marker; 00044 static const char *const branch_paths[] = 00045 { 00046 ".", ".." 00047 }; 00049 static uint32 branch_revision = 0; 00050 00051 static char *unclean_path(const char *src); 00052 static void process_players1(); 00053 static void process_players2(); 00054 static void dequeue_path_requests(); 00055 static void do_specials(); 00056 00061 void fatal(int err) 00062 { 00063 LOG(llevSystem, "Fatal: Shutdown server. Reason: %s\n", err == llevError ? "Fatal Error" : "BUG flood"); 00064 00065 if (init_done) 00066 { 00067 emergency_save(0); 00068 cleanup(); 00069 } 00070 00071 abort(); 00072 LOG(llevSystem, "Exiting...\n"); 00073 exit(-1); 00074 } 00075 00080 void version(object *op) 00081 { 00082 if (op) 00083 { 00084 if (branch_revision) 00085 { 00086 new_draw_info_format(0, COLOR_WHITE, op, "This is Atrinik v%s (r%d)", VERSION, branch_revision); 00087 } 00088 else 00089 { 00090 new_draw_info_format(0, COLOR_WHITE, op, "This is Atrinik v%s", VERSION); 00091 } 00092 } 00093 else 00094 { 00095 LOG(llevInfo, "This is Atrinik v%s.\n", VERSION); 00096 } 00097 } 00098 00104 char *crypt_string(char *str, char *salt) 00105 { 00106 #ifdef HAVE_CRYPT 00107 static const char *const c = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; 00108 char s[2]; 00109 00110 if (salt == NULL) 00111 { 00112 size_t stringlen = strlen(c); 00113 00114 s[0] = c[rndm(1, stringlen) - 1]; 00115 s[1] = c[rndm(1, stringlen) - 1]; 00116 } 00117 else 00118 { 00119 s[0] = salt[0]; 00120 s[1] = salt[1]; 00121 } 00122 00123 return crypt(str, s); 00124 #else 00125 return str; 00126 #endif 00127 } 00128 00135 int check_password(char *typed, char *crypted) 00136 { 00137 return !strcmp(crypt_string(typed, crypted), crypted); 00138 } 00139 00146 void enter_player_savebed(object *op) 00147 { 00148 mapstruct *oldmap = op->map; 00149 object *tmp = get_object(); 00150 00151 FREE_AND_COPY_HASH(EXIT_PATH(tmp), CONTR(op)->savebed_map); 00152 EXIT_X(tmp) = CONTR(op)->bed_x; 00153 EXIT_Y(tmp) = CONTR(op)->bed_y; 00154 enter_exit(op, tmp); 00155 00156 /* If the player has not changed maps and the name does not match 00157 * that of the savebed, his savebed map is gone. Lets go back 00158 * to the emergency path. Update what the players savebed is 00159 * while we're at it. */ 00160 if (oldmap == op->map && strcmp(CONTR(op)->savebed_map, oldmap->path)) 00161 { 00162 LOG(llevDebug, "Player %s savebed location %s is invalid - going to EMERGENCY_MAPPATH (%s)\n", query_name(op, NULL), CONTR(op)->savebed_map, EMERGENCY_MAPPATH); 00163 strcpy(CONTR(op)->savebed_map, EMERGENCY_MAPPATH); 00164 CONTR(op)->bed_x = EMERGENCY_X; 00165 CONTR(op)->bed_y = EMERGENCY_Y; 00166 FREE_AND_COPY_HASH(EXIT_PATH(tmp), CONTR(op)->savebed_map); 00167 EXIT_X(tmp) = CONTR(op)->bed_x; 00168 EXIT_Y(tmp) = CONTR(op)->bed_y; 00169 enter_exit(op, tmp); 00170 } 00171 } 00172 00177 void leave_map(object *op) 00178 { 00179 mapstruct *oldmap = op->map; 00180 00181 remove_ob(op); 00182 check_walk_off(op, NULL, MOVE_APPLY_VANISHED); 00183 00184 if (oldmap && !oldmap->player_first) 00185 { 00186 set_map_timeout(oldmap); 00187 } 00188 } 00189 00203 static void enter_map(object *op, mapstruct *newmap, int x, int y, int pos_flag) 00204 { 00205 int i = 0; 00206 object *tmp; 00207 mapstruct *oldmap = op->map; 00208 00209 if (op->head) 00210 { 00211 op = op->head; 00212 LOG(llevBug, "enter_map(): called from tail of object! (obj:%s map: %s (%d,%d))\n", op->name, newmap->path, x, y); 00213 } 00214 00215 /* this is a last secure check. In fact, newmap MUST legal and we only 00216 * check x and y. No get_map_from_coord() - we want check that x,y is part of this newmap. 00217 * if not, we have somewhere missed some checks - give a note to the log. */ 00218 if (OUT_OF_REAL_MAP(newmap, x, y)) 00219 { 00220 LOG(llevBug, "enter_map(): supplied coordinates are not within the map! (obj:%s map: %s (%d,%d))\n", op->name, newmap->path, x, y); 00221 x = MAP_ENTER_X(newmap); 00222 y = MAP_ENTER_Y(newmap); 00223 } 00224 00225 /* try to find a spot for our object - (single arch or multi head) 00226 * but only when we don't put it on fix position */ 00227 if (!pos_flag && arch_blocked(op->arch, op, newmap, x, y)) 00228 { 00229 /* First choice blocked */ 00230 /* We try to find a spot for the player, starting closest in. 00231 * We could use find_first_free_spot, but that doesn't randomize it at all, 00232 * So for example, if the north space is free, you would always end up there even 00233 * if other spaces around are available. 00234 * Note that for the second and third calls, we could start at a position other 00235 * than one, but then we could end up on the other side of walls and so forth. */ 00236 i = find_free_spot(op->arch, NULL, newmap, x, y, 1, SIZEOFFREE1 + 1); 00237 00238 if (i == -1) 00239 { 00240 i = find_free_spot(op->arch, NULL, newmap, x, y, 1, SIZEOFFREE2 + 1); 00241 00242 if (i == -1) 00243 { 00244 i = find_free_spot(op->arch, NULL, newmap, x, y, 1, SIZEOFFREE + 1); 00245 } 00246 } 00247 00248 if (i == -1) 00249 { 00250 i = 0; 00251 } 00252 } 00253 00254 /* If it is a player login, he has yet to be inserted anyplace. 00255 * Otherwise, we need to deal with removing the player here. */ 00256 if (!QUERY_FLAG(op, FLAG_REMOVED)) 00257 { 00258 remove_ob(op); 00259 00260 if (check_walk_off(op, NULL, MOVE_APPLY_DEFAULT) != CHECK_WALK_OK) 00261 { 00262 return; 00263 } 00264 } 00265 00266 if (op->map && op->type == PLAYER && !op->head && op->map->events) 00267 { 00268 trigger_map_event(MEVENT_LEAVE, op->map, op, NULL, NULL, NULL, 0); 00269 } 00270 00271 /* Set single or all part of a multi arch */ 00272 for (tmp = op; tmp != NULL; tmp = tmp->more) 00273 { 00274 tmp->x = tmp->arch->clone.x + x + freearr_x[i]; 00275 tmp->y = tmp->arch->clone.y + y + freearr_y[i]; 00276 } 00277 00278 if (!insert_ob_in_map(op, newmap, NULL, 0)) 00279 { 00280 return; 00281 } 00282 00283 if (newmap->events) 00284 { 00285 trigger_map_event(MEVENT_ENTER, newmap, op, NULL, NULL, NULL, 0); 00286 } 00287 00288 newmap->timeout = 0; 00289 00290 /* Do some action special for players after we have inserted them */ 00291 if (op->type == PLAYER) 00292 { 00293 if (CONTR(op)) 00294 { 00295 strcpy(CONTR(op)->maplevel, newmap->path); 00296 CONTR(op)->count = 0; 00297 } 00298 00299 /* If the player is changing maps, we need to do some special things 00300 * Do this after the player is on the new map - otherwise the force swap of the 00301 * old map does not work. */ 00302 if (oldmap != newmap && oldmap && !oldmap->player_first) 00303 { 00304 set_map_timeout(oldmap); 00305 } 00306 00307 /* Attempt to open a closed door. */ 00308 if (GET_MAP_FLAGS(op->map, op->x, op->y) & P_DOOR_CLOSED) 00309 { 00310 open_door(op, op->map, op->x, op->y, 1); 00311 } 00312 } 00313 } 00314 00318 void set_map_timeout(mapstruct *map) 00319 { 00320 #if MAP_DEFAULTTIMEOUT 00321 uint32 swap_time = MAP_SWAP_TIME(map); 00322 00323 if (swap_time == 0) 00324 { 00325 swap_time = MAP_DEFAULTTIMEOUT; 00326 } 00327 00328 if (swap_time >= MAP_MAXTIMEOUT) 00329 { 00330 swap_time = MAP_MAXTIMEOUT; 00331 } 00332 00333 map->timeout = swap_time; 00334 #else 00335 /* Save out the map. */ 00336 swap_map(map, 0); 00337 #endif 00338 } 00339 00346 char *clean_path(const char *file) 00347 { 00348 static char newpath[MAX_BUF], *cp; 00349 00350 strncpy(newpath, file, MAX_BUF - 1); 00351 newpath[MAX_BUF - 1] = '\0'; 00352 00353 for (cp = newpath; *cp != '\0'; cp++) 00354 { 00355 if (*cp == '/') 00356 { 00357 *cp = '$'; 00358 } 00359 } 00360 00361 return newpath; 00362 } 00363 00375 static char *unclean_path(const char *src) 00376 { 00377 static char newpath[MAX_BUF], *cp2; 00378 const char *cp; 00379 00380 cp = strrchr(src, '/'); 00381 00382 if (cp) 00383 { 00384 strncpy(newpath, cp + 1, MAX_BUF - 1); 00385 } 00386 else 00387 { 00388 strncpy(newpath, src, MAX_BUF - 1); 00389 } 00390 00391 newpath[MAX_BUF - 1] = '\0'; 00392 00393 for (cp2 = newpath; *cp2 != '\0'; cp2++) 00394 { 00395 if (*cp2 == '$') 00396 { 00397 *cp2 = '/'; 00398 } 00399 } 00400 00401 return newpath; 00402 } 00403 00409 static void enter_random_map(object *pl, object *exit_ob) 00410 { 00411 mapstruct *new_map; 00412 char newmap_name[HUGE_BUF]; 00413 static uint64 reference_number = 0; 00414 RMParms rp; 00415 00416 memset(&rp, 0, sizeof(RMParms)); 00417 rp.Xsize = -1; 00418 rp.Ysize = -1; 00419 00420 if (exit_ob->msg) 00421 { 00422 set_random_map_variable(&rp, exit_ob->msg); 00423 } 00424 00425 rp.origin_x = exit_ob->x; 00426 rp.origin_y = exit_ob->y; 00427 strcpy(rp.origin_map, pl->map->path); 00428 00429 /* Pick a new pathname for the new map. Currently, we just use a 00430 * static variable and increment the counter one each time. */ 00431 snprintf(newmap_name, sizeof(newmap_name), "/random/%" FMT64U, reference_number++); 00432 00433 /* Now to generate the actual map. */ 00434 new_map = generate_random_map(newmap_name, &rp); 00435 00436 /* Update the exit_ob so it now points directly at the newly created 00437 * random map. */ 00438 if (new_map) 00439 { 00440 int x, y; 00441 00442 x = EXIT_X(exit_ob) = MAP_ENTER_X(new_map); 00443 y = EXIT_Y(exit_ob) = MAP_ENTER_Y(new_map); 00444 FREE_AND_COPY_HASH(EXIT_PATH(exit_ob), newmap_name); 00445 FREE_AND_COPY_HASH(new_map->path, newmap_name); 00446 enter_map(pl, new_map, x, y, QUERY_FLAG(exit_ob, FLAG_USE_FIX_POS)); 00447 } 00448 } 00449 00454 static void enter_unique_map(object *op, object *exit_ob) 00455 { 00456 char apartment[HUGE_BUF]; 00457 mapstruct *newmap; 00458 00459 /* Absolute path */ 00460 if (EXIT_PATH(exit_ob)[0] == '/') 00461 { 00462 snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(EXIT_PATH(exit_ob))); 00463 newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE); 00464 00465 if (!newmap) 00466 { 00467 newmap = load_original_map(create_pathname(EXIT_PATH(exit_ob)), MAP_PLAYER_UNIQUE); 00468 } 00469 } 00470 /* Relative path */ 00471 else 00472 { 00473 char reldir[HUGE_BUF], tmpc[HUGE_BUF], tmp_path[HUGE_BUF], *cp; 00474 00475 if (MAP_UNIQUE(exit_ob->map)) 00476 { 00477 strncpy(reldir, unclean_path(exit_ob->map->path), sizeof(reldir) - 1); 00478 00479 /* Need to copy this over, as clean_path only has one static return buffer */ 00480 strncpy(tmpc, clean_path(reldir), sizeof(tmpc) - 1); 00481 00482 /* Remove final component, if any */ 00483 if ((cp = strrchr(tmpc, '$')) != NULL) 00484 { 00485 *cp = 0; 00486 } 00487 00488 snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s_%s", settings.localdir, settings.playerdir, op->name, tmpc, clean_path(EXIT_PATH(exit_ob))); 00489 newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE); 00490 00491 if (!newmap) 00492 { 00493 newmap = load_original_map(create_pathname(normalize_path(reldir, EXIT_PATH(exit_ob), tmp_path)), MAP_PLAYER_UNIQUE); 00494 } 00495 } 00496 else 00497 { 00498 /* The exit is unique, but the map we are coming from is not unique. So 00499 * use the basic logic - don't need to demangle the path name */ 00500 snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(normalize_path(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path))); 00501 newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE); 00502 00503 if (!newmap) 00504 { 00505 newmap = ready_map_name(normalize_path(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path), 0); 00506 } 00507 } 00508 } 00509 00510 if (newmap) 00511 { 00512 FREE_AND_COPY_HASH(newmap->path, apartment); 00513 newmap->map_flags |= MAP_FLAG_UNIQUE; 00514 00515 enter_map(op, newmap, EXIT_X(exit_ob), EXIT_Y(exit_ob), QUERY_FLAG(exit_ob, FLAG_USE_FIX_POS)); 00516 } 00517 else 00518 { 00519 new_draw_info_format(0, COLOR_WHITE, op, "The %s is closed.", query_name(exit_ob, NULL)); 00520 LOG(llevDebug, "enter_unique_map: Exit %s (%d,%d) on map %s leads no where.\n", query_name(exit_ob, NULL), exit_ob->x, exit_ob->y, exit_ob->map ? exit_ob->map->path ? exit_ob->map->path : "NO_PATH (script?)" : "NO_MAP (script?)"); 00521 } 00522 } 00523 00530 void enter_exit(object *op, object *exit_ob) 00531 { 00532 object *tmp; 00533 00534 if (op->head) 00535 { 00536 op = op->head; 00537 } 00538 00539 /* First, lets figure out what map we go */ 00540 if (exit_ob) 00541 { 00542 /* check to see if we make a randomly generated map */ 00543 if (EXIT_PATH(exit_ob) && EXIT_PATH(exit_ob)[1] == '!') 00544 { 00545 if (op->type != PLAYER) 00546 { 00547 return; 00548 } 00549 00550 if (exit_ob->sub_type == ST1_EXIT_SOUND && exit_ob->map) 00551 { 00552 play_sound_map(exit_ob->map, CMD_SOUND_EFFECT, "teleport.ogg", exit_ob->x, exit_ob->y, 0, 0); 00553 } 00554 00555 enter_random_map(op, exit_ob); 00556 } 00557 else if (exit_ob->last_eat == MAP_PLAYER_MAP) 00558 { 00559 if (op->type != PLAYER) 00560 { 00561 return; 00562 } 00563 00564 if (exit_ob->sub_type == ST1_EXIT_SOUND && exit_ob->map) 00565 { 00566 play_sound_map(exit_ob->map, CMD_SOUND_EFFECT, "teleport.ogg", exit_ob->x, exit_ob->y, 0, 0); 00567 } 00568 00569 enter_unique_map(op, exit_ob); 00570 } 00571 else 00572 { 00573 int x = EXIT_X(exit_ob), y = EXIT_Y(exit_ob); 00574 char tmp_path[HUGE_BUF]; 00575 /* 'Normal' exits that do not do anything special 00576 * Simple enough we don't need another routine for it. */ 00577 mapstruct *newmap = NULL; 00578 00579 if (exit_ob->map) 00580 { 00581 if (strcmp(EXIT_PATH(exit_ob), "/random/")) 00582 { 00583 if (strncmp(exit_ob->map->path, settings.localdir, strlen(settings.localdir))) 00584 { 00585 newmap = ready_map_name(normalize_path(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path), 0); 00586 } 00587 else 00588 { 00589 newmap = ready_map_name(normalize_path("", EXIT_PATH(exit_ob), tmp_path), 0); 00590 } 00591 } 00592 00593 /* This is either a new random map to load, or we failed 00594 * to load an old random map. */ 00595 if (!newmap && !strncmp(EXIT_PATH(exit_ob), "/random/", 8)) 00596 { 00597 if (op->type != PLAYER) 00598 { 00599 return; 00600 } 00601 00602 /* Maps that go down have a message set. However, maps 00603 * that go up, don't. If the going home has reset, there 00604 * isn't much point generating a random map, because it 00605 * won't match the maps, so just teleport the player 00606 * to their savebed map. */ 00607 if (exit_ob->msg) 00608 { 00609 if (exit_ob->sub_type == ST1_EXIT_SOUND && op->map) 00610 { 00611 play_sound_map(exit_ob->map, CMD_SOUND_EFFECT, "teleport.ogg", exit_ob->x, exit_ob->y, 0, 0); 00612 } 00613 00614 enter_random_map(op, exit_ob); 00615 } 00616 else 00617 { 00618 enter_player_savebed(op); 00619 } 00620 00621 /* For exits that cause damages (like pits). Don't know if any 00622 * random maps use this or not. */ 00623 if (exit_ob->stats.dam && op->type == PLAYER) 00624 { 00625 hit_player(op, exit_ob->stats.dam, exit_ob, AT_INTERNAL); 00626 } 00627 00628 return; 00629 } 00630 } 00631 else 00632 { 00633 /* For word of recall and other force objects 00634 * They contain the full pathname of the map to go back to, 00635 * so we don't need to normalize it. 00636 * But we do need to see if it is unique or not */ 00637 if (!strncmp(EXIT_PATH(exit_ob), settings.localdir, strlen(settings.localdir))) 00638 { 00639 newmap = ready_map_name(EXIT_PATH(exit_ob), MAP_NAME_SHARED|MAP_PLAYER_UNIQUE); 00640 } 00641 else 00642 { 00643 newmap = ready_map_name(EXIT_PATH(exit_ob), MAP_NAME_SHARED); 00644 } 00645 } 00646 00647 if (!newmap) 00648 { 00649 if (op->type == PLAYER) 00650 { 00651 new_draw_info_format(0, COLOR_WHITE, op, "The %s is closed.", query_name(exit_ob, NULL)); 00652 } 00653 00654 return; 00655 } 00656 00657 /* -1, -1 marks to use the default ENTER_xx position of the map */ 00658 if (x == -1 && y == -1) 00659 { 00660 x = MAP_ENTER_X(newmap); 00661 y = MAP_ENTER_Y(newmap); 00662 } 00663 00664 /* If exit is damned, update player's death and WoR home-position 00665 * and delete town portal. */ 00666 if (QUERY_FLAG(exit_ob, FLAG_DAMNED)) 00667 { 00668 if (op->type != PLAYER) 00669 { 00670 return; 00671 } 00672 00673 /* Remove an old force with a slaying field == PORTAL_DESTINATION_NAME */ 00674 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) 00675 { 00676 if (tmp->type == FORCE && tmp->slaying && tmp->slaying == shstr_cons.portal_destination_name) 00677 { 00678 break; 00679 } 00680 } 00681 00682 if (tmp) 00683 { 00684 remove_ob(tmp); 00685 } 00686 00687 if (exit_ob->map) 00688 { 00689 strcpy(CONTR(op)->savebed_map, normalize_path(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path)); 00690 } 00691 else 00692 { 00693 strcpy(CONTR(op)->savebed_map, EXIT_PATH(exit_ob)); 00694 } 00695 00696 CONTR(op)->bed_x = EXIT_X(exit_ob), CONTR(op)->bed_y = EXIT_Y(exit_ob); 00697 save_player(op, 1); 00698 } 00699 00700 if (exit_ob->sub_type == ST1_EXIT_SOUND && exit_ob->map) 00701 { 00702 play_sound_map(exit_ob->map, CMD_SOUND_EFFECT, "teleport.ogg", exit_ob->x, exit_ob->y, 0, 0); 00703 } 00704 00705 enter_map(op, newmap, x, y, QUERY_FLAG(exit_ob, FLAG_USE_FIX_POS)); 00706 } 00707 00708 /* For exits that cause damage (like pits) */ 00709 if (exit_ob->stats.dam && op->type == PLAYER) 00710 { 00711 hit_player(op, exit_ob->stats.dam, exit_ob, AT_INTERNAL); 00712 } 00713 } 00714 else if (op->type == PLAYER) 00715 { 00716 int flags = 0; 00717 mapstruct *newmap; 00718 00719 /* Hypothetically, I guess its possible that a standard map matches 00720 * the localdir, but that seems pretty unlikely - unlikely enough that 00721 * I'm not going to attempt to try to deal with that possibility. 00722 * We use the fact that when a player saves on a unique map, it prepends 00723 * the localdir to that name. So its an easy way to see of the map is 00724 * unique or not. */ 00725 if (!strncmp(CONTR(op)->maplevel, settings.localdir, strlen(settings.localdir))) 00726 { 00727 flags = MAP_PLAYER_UNIQUE; 00728 } 00729 00730 newmap = ready_map_name(CONTR(op)->maplevel, flags); 00731 00732 if (!newmap) 00733 { 00734 if (strncmp(CONTR(op)->maplevel, "/random/", 8)) 00735 { 00736 LOG(llevBug, "enter_exit(): Pathname to map does not exist! player: %s (%s)\n", op->name, CONTR(op)->maplevel); 00737 newmap = ready_map_name(EMERGENCY_MAPPATH, 0); 00738 op->x = EMERGENCY_X; 00739 op->y = EMERGENCY_Y; 00740 00741 /* If we can't load the emergency map, something is probably 00742 * really screwed up, so bail out now. */ 00743 if (!newmap) 00744 { 00745 LOG(llevError, "enter_exit(): could not load emergency map? Fatal error! (player: %s)\n", op->name); 00746 } 00747 } 00748 else 00749 { 00750 enter_player_savebed(op); 00751 return; 00752 } 00753 } 00754 00755 /* -1,-1 marks to use the default ENTER_xx position of the map */ 00756 if ((op->x == -1 && op->y == -1) || MAP_FIXEDLOGIN(newmap)) 00757 { 00758 op->x = MAP_ENTER_X(newmap); 00759 op->y = MAP_ENTER_Y(newmap); 00760 } 00761 00762 enter_map(op, newmap, op->x, op->y, 1); 00763 } 00764 } 00765 00769 static void process_players1() 00770 { 00771 player *pl, *plnext; 00772 int retval; 00773 00774 for (pl = first_player; pl; pl = plnext) 00775 { 00776 plnext = pl->next; 00777 00778 while ((retval = handle_newcs_player(pl)) == 1) 00779 { 00780 } 00781 00782 if (retval == -1) 00783 { 00784 continue; 00785 } 00786 00787 /* Update the next pointer, in case the above handle_newcs_player() 00788 * call(s) caused the previous next pointer to be freed (double 00789 * login kicked the playing character, for example). */ 00790 plnext = pl->next; 00791 00792 if (pl->followed_player[0]) 00793 { 00794 player *followed = find_player(pl->followed_player); 00795 00796 if (followed && followed->ob && followed->ob->map) 00797 { 00798 rv_vector rv; 00799 00800 if (!on_same_map(pl->ob, followed->ob) || (get_rangevector(pl->ob, followed->ob, &rv, 0) && rv.distance > 4)) 00801 { 00802 int space = find_free_spot(pl->ob->arch, pl->ob, followed->ob->map, followed->ob->x, followed->ob->y, 1, SIZEOFFREE2 + 1); 00803 00804 if (space != -1 && followed->ob->x + freearr_x[space] >= 0 && followed->ob->y + freearr_y[space] >= 0 && followed->ob->x + freearr_x[space] < MAP_WIDTH(followed->ob->map) && followed->ob->y + freearr_y[space] < MAP_HEIGHT(followed->ob->map)) 00805 { 00806 remove_ob(pl->ob); 00807 pl->ob->x = followed->ob->x + freearr_x[space]; 00808 pl->ob->y = followed->ob->y + freearr_y[space]; 00809 insert_ob_in_map(pl->ob, followed->ob->map, NULL, 0); 00810 } 00811 } 00812 } 00813 else 00814 { 00815 new_draw_info_format(0, COLOR_RED, pl->ob, "Player %s left.", pl->followed_player); 00816 pl->followed_player[0] = '\0'; 00817 } 00818 } 00819 00820 pl->ob->weapon_speed_left -= pl->ob->weapon_speed_add; 00821 00822 /* Use the target system to hit our target - don't hit friendly 00823 * objects, ourselves or when we are not in combat mode. */ 00824 if (pl->target_object && pl->combat_mode && OBJECT_ACTIVE(pl->target_object) && pl->target_object_count != pl->ob->count && !is_friend_of(pl->ob, pl->target_object)) 00825 { 00826 if (pl->ob->weapon_speed_left <= 0) 00827 { 00828 /* Now we force target as enemy */ 00829 pl->ob->enemy = pl->target_object; 00830 pl->ob->enemy_count = pl->target_object_count; 00831 00832 if (!OBJECT_VALID(pl->ob->enemy, pl->ob->enemy_count) || pl->ob->enemy->owner == pl->ob) 00833 { 00834 pl->ob->enemy = NULL; 00835 } 00836 else if (is_melee_range(pl->ob, pl->ob->enemy)) 00837 { 00838 if (!OBJECT_VALID(pl->ob->enemy->enemy, pl->ob->enemy->enemy_count)) 00839 { 00840 set_npc_enemy(pl->ob->enemy, pl->ob, NULL); 00841 } 00842 /* Our target already has an enemy - then note we had attacked */ 00843 else 00844 { 00845 pl->ob->enemy->attacked_by = pl->ob; 00846 pl->ob->enemy->attacked_by_distance = 1; 00847 } 00848 00849 pl->praying = 0; 00850 skill_attack(pl->ob->enemy, pl->ob, 0, NULL); 00851 /* We want only *one* swing - not several swings per tick */ 00852 pl->ob->weapon_speed_left += FABS((int) pl->ob->weapon_speed_left) + 1; 00853 } 00854 } 00855 } 00856 else 00857 { 00858 if (pl->ob->weapon_speed_left <= 0) 00859 { 00860 pl->ob->weapon_speed_left = 0; 00861 } 00862 } 00863 00864 if (pl->move_path) 00865 { 00866 player_path_handle(pl); 00867 } 00868 00869 do_some_living(pl->ob); 00870 00871 #ifdef AUTOSAVE 00872 /* Check for ST_PLAYING state so that we don't try to save off when 00873 * the player is logging in. */ 00874 if ((pl->last_save_tick + AUTOSAVE) < pticks && pl->state == ST_PLAYING) 00875 { 00876 save_player(pl->ob, 1); 00877 pl->last_save_tick = pticks; 00878 hiscore_check(pl->ob, 1); 00879 } 00880 #endif 00881 00882 /* Update total playing time. */ 00883 if (pl->state == ST_PLAYING && time(NULL) > pl->last_stat_time_played) 00884 { 00885 pl->last_stat_time_played = time(NULL); 00886 00887 if (pl->afk) 00888 { 00889 pl->stat_time_afk++; 00890 } 00891 else 00892 { 00893 pl->stat_time_played++; 00894 } 00895 } 00896 } 00897 } 00898 00902 static void process_players2() 00903 { 00904 player *pl; 00905 00906 for (pl = first_player; pl; pl = pl->next) 00907 { 00908 /* Check if our target is still valid - if not, update client. */ 00909 if (pl->ob->map && (!pl->target_object || (pl->target_object != pl->ob && pl->target_object_count != pl->target_object->count) || QUERY_FLAG(pl->target_object, FLAG_SYS_OBJECT) || (QUERY_FLAG(pl->target_object, FLAG_IS_INVISIBLE) && !QUERY_FLAG(pl->ob, FLAG_SEE_INVISIBLE)))) 00910 { 00911 send_target_command(pl); 00912 } 00913 00914 if (pl->ob->speed_left > pl->ob->speed) 00915 { 00916 pl->ob->speed_left = pl->ob->speed; 00917 } 00918 } 00919 } 00920 00924 void process_events(mapstruct *map) 00925 { 00926 object *op; 00927 tag_t tag; 00928 00929 process_players1(); 00930 00931 /* Put marker object at beginning of active list */ 00932 marker.active_next = active_objects; 00933 00934 if (marker.active_next) 00935 { 00936 marker.active_next->active_prev = ▮ 00937 } 00938 00939 marker.active_prev = NULL; 00940 active_objects = ▮ 00941 00942 while (marker.active_next) 00943 { 00944 op = marker.active_next; 00945 tag = op->count; 00946 00947 /* Move marker forward - swap op and marker */ 00948 op->active_prev = marker.active_prev; 00949 00950 if (op->active_prev) 00951 { 00952 op->active_prev->active_next = op; 00953 } 00954 else 00955 { 00956 active_objects = op; 00957 } 00958 00959 marker.active_next = op->active_next; 00960 00961 if (marker.active_next) 00962 { 00963 marker.active_next->active_prev = ▮ 00964 } 00965 00966 marker.active_prev = op; 00967 op->active_next = ▮ 00968 00969 /* Now process op */ 00970 if (OBJECT_FREE(op)) 00971 { 00972 LOG(llevBug, "process_events(): Free object on active list\n"); 00973 op->speed = 0; 00974 update_ob_speed(op); 00975 continue; 00976 } 00977 00978 if (QUERY_FLAG(op, FLAG_REMOVED)) 00979 { 00980 op->speed = 0; 00981 update_ob_speed(op); 00982 continue; 00983 } 00984 00985 if (!op->speed) 00986 { 00987 LOG(llevBug, "process_events(): Object %s (%s, type:%d count:%d) has no speed, but is on active list\n", op->arch->name, query_name(op, NULL), op->type, op->count); 00988 update_ob_speed(op); 00989 continue; 00990 } 00991 00992 if (op->map == NULL && op->env == NULL && op->name && op->type != MAP && map == NULL) 00993 { 00994 if (op->type == PLAYER && CONTR(op)->state != ST_PLAYING) 00995 { 00996 continue; 00997 } 00998 00999 LOG(llevBug, "process_events(): Object without map or inventory is on active list: %s (%d)\n", query_name(op, NULL), op->count); 01000 op->speed = 0; 01001 update_ob_speed(op); 01002 continue; 01003 } 01004 01005 if (map && op->map != map) 01006 { 01007 continue; 01008 } 01009 01010 /* As long we are > 0, we are not ready to swing. */ 01011 if (op->weapon_speed_left > 0) 01012 { 01013 op->weapon_speed_left -= op->weapon_speed_add; 01014 } 01015 01016 if (op->speed_left > 0) 01017 { 01018 --op->speed_left; 01019 process_object(op); 01020 01021 if (was_destroyed(op, tag)) 01022 { 01023 continue; 01024 } 01025 } 01026 01027 /* Handle archetype-field anim_speed differently when it comes to 01028 * the animation. If we have a value on this we don't animate it 01029 * at speed-events. */ 01030 if (QUERY_FLAG(op, FLAG_ANIMATE)) 01031 { 01032 if (op->last_anim >= op->anim_speed) 01033 { 01034 animate_object(op, 1); 01035 01036 /* Check for direction changing */ 01037 if (op->type == PLAYER && NUM_FACINGS(op) >= 25) 01038 { 01039 if (op->anim_moving_dir != -1) 01040 { 01041 op->anim_last_facing = op->anim_moving_dir; 01042 op->anim_moving_dir = -1; 01043 } 01044 01045 if (op->anim_enemy_dir != -1) 01046 { 01047 op->anim_last_facing = op->anim_enemy_dir; 01048 op->anim_enemy_dir = -1; 01049 } 01050 } 01051 01052 op->last_anim = 1; 01053 } 01054 else 01055 { 01056 /* Check for direction changing */ 01057 if (NUM_FACINGS(op) >= 25) 01058 { 01059 animate_object(op, 0); 01060 } 01061 01062 op->last_anim++; 01063 } 01064 } 01065 01066 if (op->speed_left <= 0) 01067 { 01068 op->speed_left += FABS(op->speed); 01069 } 01070 } 01071 01072 /* Remove marker object from active list */ 01073 if (marker.active_prev) 01074 { 01075 marker.active_prev->active_next = NULL; 01076 } 01077 else 01078 { 01079 active_objects = NULL; 01080 } 01081 01082 process_players2(); 01083 } 01084 01087 void clean_tmp_files() 01088 { 01089 mapstruct *m, *next; 01090 01091 LOG(llevInfo, "Cleaning up...\n"); 01092 01093 /* We save the maps - it may not be intuitive why, but if there are 01094 * unique items, we need to save the map so they get saved off. */ 01095 for (m = first_map; m != NULL; m = next) 01096 { 01097 next = m->next; 01098 01099 if (m->in_memory == MAP_IN_MEMORY) 01100 { 01101 #if RECYCLE_TMP_MAPS 01102 swap_map(m, 0); 01103 #else 01104 new_save_map(m, 0); 01105 clean_tmp_map(m); 01106 #endif 01107 } 01108 } 01109 01110 /* Write the clock */ 01111 write_todclock(); 01112 } 01113 01116 void cleanup() 01117 { 01118 LOG(llevDebug, "Cleanup called. Freeing data.\n"); 01119 clean_tmp_files(); 01120 #if MEMORY_DEBUG 01121 free_all_maps(); 01122 free_style_maps(); 01123 free_all_archs(); 01124 free_all_treasures(); 01125 free_all_images(); 01126 free_all_newserver(); 01127 free_all_recipes(); 01128 free_all_readable(); 01129 free_all_god(); 01130 free_all_anim(); 01131 free_strings(); 01132 race_free(); 01133 free_exp_objects(); 01134 free_srv_files(); 01135 free_regions(); 01136 free_mempools(); 01137 #endif 01138 cache_remove_all(); 01139 remove_plugins(); 01140 } 01141 01146 static void dequeue_path_requests() 01147 { 01148 #ifdef LEFTOVER_CPU_FOR_PATHFINDING 01149 static struct timeval new_time; 01150 long leftover_sec, leftover_usec; 01151 object *wp; 01152 01153 while ((wp = get_next_requested_path())) 01154 { 01155 waypoint_compute_path(wp); 01156 01157 (void) GETTIMEOFDAY(&new_time); 01158 01159 leftover_sec = last_time.tv_sec - new_time.tv_sec; 01160 leftover_usec = max_time - (new_time.tv_usec - last_time.tv_usec); 01161 01162 /* This is very ugly, but probably the fastest for our use: */ 01163 while (leftover_usec < 0) 01164 { 01165 leftover_usec += 1000000; 01166 leftover_sec -= 1; 01167 } 01168 while (leftover_usec > 1000000) 01169 { 01170 leftover_usec -= 1000000; 01171 leftover_sec += 1; 01172 } 01173 01174 /* Try to save about 10 ms */ 01175 if (leftover_sec < 1 && leftover_usec < 10000) 01176 { 01177 break; 01178 } 01179 } 01180 #else 01181 object *wp = get_next_requested_path(); 01182 01183 if (wp) 01184 { 01185 waypoint_compute_path(wp); 01186 } 01187 #endif 01188 } 01189 01198 int swap_apartments(const char *mapold, const char *mapnew, int x, int y, object *op) 01199 { 01200 char oldmappath[HUGE_BUF], newmappath[HUGE_BUF]; 01201 int i, j; 01202 object *ob, *tmp, *tmp2, *dummy; 01203 mapstruct *oldmap, *newmap; 01204 01205 snprintf(oldmappath, sizeof(oldmappath), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(mapold)); 01206 snprintf(newmappath, sizeof(newmappath), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(mapnew)); 01207 01208 /* So we can transfer our items from the old apartment. */ 01209 oldmap = ready_map_name(oldmappath, 2); 01210 01211 if (!oldmap) 01212 { 01213 LOG(llevBug, "swap_apartments(): Could not get oldmap using ready_map_name().\n"); 01214 return 0; 01215 } 01216 01217 /* Our new map. */ 01218 newmap = ready_map_name(create_pathname(mapnew), 6); 01219 01220 if (!newmap) 01221 { 01222 LOG(llevBug, "swap_apartments(): Could not get newmap using ready_map_name().\n"); 01223 return 0; 01224 } 01225 01226 /* Goes to player directory. */ 01227 FREE_AND_COPY_HASH(newmap->path, newmappath); 01228 newmap->map_flags |= MAP_FLAG_UNIQUE; 01229 01230 /* Go through every square on old apartment map, looking for things 01231 * to transfer. */ 01232 for (i = 0; i < MAP_WIDTH(oldmap); i++) 01233 { 01234 for (j = 0; j < MAP_HEIGHT(oldmap); j++) 01235 { 01236 for (ob = get_map_ob(oldmap, i, j); ob; ob = tmp2) 01237 { 01238 tmp2 = ob->above; 01239 01240 /* We teleport any possible players here to emergency map. */ 01241 if (ob->type == PLAYER) 01242 { 01243 dummy = get_object(); 01244 dummy->map = ob->map; 01245 FREE_AND_COPY_HASH(EXIT_PATH(dummy), EMERGENCY_MAPPATH); 01246 FREE_AND_COPY_HASH(dummy->name, EMERGENCY_MAPPATH); 01247 enter_exit(ob, dummy); 01248 continue; 01249 } 01250 01251 /* If it's sys_object 1, there's no need to transfer it. */ 01252 if (QUERY_FLAG(ob, FLAG_SYS_OBJECT)) 01253 { 01254 continue; 01255 } 01256 01257 /* A pickable item... Tranfer it */ 01258 if (!QUERY_FLAG(ob, FLAG_NO_PICK)) 01259 { 01260 remove_ob(ob); 01261 ob->x = x; 01262 ob->y = y; 01263 insert_ob_in_map(ob, newmap, NULL, INS_NO_MERGE | INS_NO_WALK_ON); 01264 } 01265 /* Fixed part of map */ 01266 else 01267 { 01268 /* Now we test for containers, because player 01269 * can have items stored in it. So, go through 01270 * the container and look for things to transfer. */ 01271 for (tmp = ob->inv; tmp; tmp = tmp2) 01272 { 01273 tmp2 = tmp->below; 01274 01275 if (QUERY_FLAG(tmp, FLAG_SYS_OBJECT) || QUERY_FLAG(tmp, FLAG_NO_PICK)) 01276 { 01277 continue; 01278 } 01279 01280 remove_ob(tmp); 01281 tmp->x = x; 01282 tmp->y = y; 01283 insert_ob_in_map(tmp, newmap, NULL, INS_NO_MERGE | INS_NO_WALK_ON); 01284 } 01285 } 01286 } 01287 } 01288 } 01289 01290 /* Save the map */ 01291 new_save_map(newmap, 0); 01292 01293 /* Check for old save bed */ 01294 if (strcmp(oldmap->path, CONTR(op)->savebed_map) == 0) 01295 { 01296 strcpy(CONTR(op)->savebed_map, EMERGENCY_MAPPATH); 01297 CONTR(op)->bed_x = EMERGENCY_X; 01298 CONTR(op)->bed_y = EMERGENCY_Y; 01299 } 01300 01301 unlink(oldmap->path); 01302 01303 /* Free the maps */ 01304 free_map(newmap, 1); 01305 free_map(oldmap, 1); 01306 01307 return 1; 01308 } 01309 01312 static void do_specials() 01313 { 01314 if (!(pticks % 2)) 01315 { 01316 dequeue_path_requests(); 01317 } 01318 01319 /* If watchdog is enabled */ 01320 if (settings.watchdog) 01321 { 01322 if (!(pticks % 503)) 01323 { 01324 watchdog(); 01325 } 01326 } 01327 01328 if (!(pticks % PTICKS_PER_CLOCK)) 01329 { 01330 tick_the_clock(); 01331 } 01332 01333 /* Clears the tmp-files of maps which have reset */ 01334 if (!(pticks % 509)) 01335 { 01336 flush_old_maps(); 01337 } 01338 01339 if (settings.meta_on && !(pticks % 2521)) 01340 { 01341 metaserver_info_update(); 01342 } 01343 } 01344 01347 static void iterate_main_loop() 01348 { 01349 nroferrors = 0; 01350 01351 /* Check and run a shutdown count (with messages and shutdown) */ 01352 shutdown_agent(-1, NULL); 01353 01354 doeric_server(); 01355 01356 #ifdef MEMPOOL_OBJECT_TRACKING 01357 check_use_object_list(); 01358 #endif 01359 01360 /* Global round ticker. */ 01361 global_round_tag++; 01362 01363 /* "do" something with objects with speed */ 01364 process_events(NULL); 01365 01366 /* Process the timers */ 01367 cftimer_process_timers(); 01368 01369 /* Removes unused maps after a certain timeout */ 01370 check_active_maps(); 01371 01372 /* Routines called from time to time. */ 01373 do_specials(); 01374 01375 doeric_server_write(); 01376 01377 /* Clean up the object pool */ 01378 object_gc(); 01379 01380 /* Sleep proper amount of time before next tick */ 01381 sleep_delta(); 01382 } 01383 01384 #ifdef HAVE_WORLD_MAKER 01385 void world_maker(); 01386 #endif 01387 01393 int main(int argc, char **argv) 01394 { 01395 char buf[HUGE_BUF]; 01396 size_t i; 01397 FILE *fp; 01398 01399 #ifdef WIN32 01400 /* Open all files in binary mode by default. */ 01401 _set_fmode(_O_BINARY); 01402 #endif 01403 01404 init(argc, argv); 01405 init_plugins(); 01406 01407 #ifdef HAVE_CHECK 01408 /* Now that we have everything loaded, we can run unit tests. */ 01409 if (settings.unit_tests) 01410 { 01411 LOG(llevInfo, "Running unit tests...\n"); 01412 check_main(); 01413 exit(0); 01414 } 01415 #endif 01416 01417 #ifdef HAVE_WORLD_MAKER 01418 if (settings.world_maker) 01419 { 01420 LOG(llevInfo, "Running the world maker...\n"); 01421 world_maker(); 01422 exit(0); 01423 } 01424 #endif 01425 01426 memset(&marker, 0, sizeof(struct obj)); 01427 01428 /* Try to find branch revision. */ 01429 for (i = 0; i < arraysize(branch_paths); i++) 01430 { 01431 snprintf(buf, sizeof(buf), "%s/.bzr/branch/last-revision", branch_paths[i]); 01432 fp = fopen(buf, "r"); 01433 01434 if (fp && fgets(buf, sizeof(buf) - 1, fp)) 01435 { 01436 char *end = strchr(buf, ' '); 01437 01438 if (end) 01439 { 01440 *end = '\0'; 01441 } 01442 01443 branch_revision = atoi(buf); 01444 fclose(fp); 01445 break; 01446 } 01447 } 01448 01449 LOG(llevInfo, "Server ready. Waiting for connections...\n"); 01450 01451 for (; ;) 01452 { 01453 iterate_main_loop(); 01454 } 01455 }
1.7.4