|
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 <loader.h> 00032 00038 void emergency_save(int flag) 00039 { 00040 #ifndef NO_EMERGENCY_SAVE 00041 LOG(llevSystem, "Emergency save: "); 00042 00043 for (pl = first_player; pl; pl = pl->next) 00044 { 00045 if (!pl->ob) 00046 { 00047 LOG(llevSystem, "No name, ignoring this.\n"); 00048 continue; 00049 } 00050 00051 LOG(llevSystem, "%s ", pl->ob->name); 00052 new_draw_info(0, COLOR_WHITE, pl->ob, "Emergency save..."); 00053 00054 /* If we are not exiting the game (ie, this is sort of a backup 00055 * save), then don't change the location back to the village. 00056 * Note that there are other options to have backup saves be done 00057 * at the starting village */ 00058 if (!flag) 00059 { 00060 strcpy(pl->maplevel, first_map_path); 00061 00062 if (pl->ob->map != NULL) 00063 { 00064 pl->ob->map = NULL; 00065 } 00066 00067 pl->ob->x = -1; 00068 pl->ob->y = -1; 00069 } 00070 00071 container_unlink(pl, NULL); 00072 00073 if (!save_player(pl->ob, flag)) 00074 { 00075 LOG(llevSystem, "(failed) "); 00076 new_draw_info(0, COLOR_WHITE, pl->ob, "Emergency save failed, checking score..."); 00077 } 00078 00079 hiscore_check(pl->ob, 1); 00080 } 00081 00082 LOG(llevSystem, "\n"); 00083 #else 00084 (void) flag; 00085 LOG(llevSystem, "Emergency saves disabled, no save attempted\n"); 00086 #endif 00087 } 00088 00096 int check_name(player *pl, char *name) 00097 { 00098 size_t name_len; 00099 00100 if (name[0] == '\0') 00101 { 00102 send_socket_message(COLOR_RED, &pl->socket, "You must provide a name to log in."); 00103 return 0; 00104 } 00105 00106 name_len = strlen(name); 00107 00108 if (name_len < PLAYER_NAME_MIN || name_len > PLAYER_NAME_MAX) 00109 { 00110 send_socket_message(COLOR_RED, &pl->socket, "That name has an invalid length."); 00111 return 0; 00112 } 00113 00114 if (!playername_ok(name)) 00115 { 00116 send_socket_message(COLOR_RED, &pl->socket, "That name contains illegal characters."); 00117 return 0; 00118 } 00119 00120 return 1; 00121 } 00122 00129 int save_player(object *op, int flag) 00130 { 00131 FILE *fp; 00132 char filename[MAX_BUF], backupfile[MAX_BUF]; 00133 player *pl = CONTR(op); 00134 int i, wiz = QUERY_FLAG(op, FLAG_WIZ); 00135 00136 flag &= 1; 00137 00138 /* Sanity check - some stuff changes this when player is exiting */ 00139 if (op->type != PLAYER) 00140 { 00141 return 0; 00142 } 00143 00144 /* Prevent accidental saves if connection is reset after player has 00145 * mostly exited. */ 00146 if (pl->state != ST_PLAYING) 00147 { 00148 return 0; 00149 } 00150 00151 /* Is this a map players can't save on? */ 00152 if (op->map && MAP_PLAYER_NO_SAVE(op->map)) 00153 { 00154 return 0; 00155 } 00156 00157 snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name); 00158 make_path_to_file(filename); 00159 fp = fopen(filename, "w"); 00160 snprintf(backupfile, sizeof(backupfile), "%s.tmp", filename); 00161 rename(filename, backupfile); 00162 00163 if (!fp) 00164 { 00165 new_draw_info(0, COLOR_WHITE, op, "Can't open file for saving."); 00166 LOG(llevDebug, "Can't open file for saving (%s).\n", filename); 00167 rename(backupfile, filename); 00168 return 0; 00169 } 00170 00171 fprintf(fp, "password %s\n", pl->password); 00172 fprintf(fp, "dm_stealth %d\n", pl->dm_stealth); 00173 fprintf(fp, "ms_privacy %d\n", pl->ms_privacy); 00174 fprintf(fp, "no_shout %d\n", pl->no_shout); 00175 fprintf(fp, "gen_hp %d\n", pl->gen_hp); 00176 fprintf(fp, "gen_sp %d\n", pl->gen_sp); 00177 fprintf(fp, "gen_grace %d\n", pl->gen_grace); 00178 fprintf(fp, "spell %d\n", pl->chosen_spell); 00179 fprintf(fp, "shoottype %d\n", pl->shoottype); 00180 fprintf(fp, "digestion %d\n", pl->digestion); 00181 00182 if (op->map) 00183 { 00184 fprintf(fp, "map %s\n", op->map->path); 00185 } 00186 else 00187 { 00188 fprintf(fp, "map %s\n", EMERGENCY_MAPPATH); 00189 } 00190 00191 fprintf(fp, "savebed_map %s\n", pl->savebed_map); 00192 fprintf(fp, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y); 00193 fprintf(fp, "Str %d\n", pl->orig_stats.Str); 00194 fprintf(fp, "Dex %d\n", pl->orig_stats.Dex); 00195 fprintf(fp, "Con %d\n", pl->orig_stats.Con); 00196 fprintf(fp, "Int %d\n", pl->orig_stats.Int); 00197 fprintf(fp, "Pow %d\n", pl->orig_stats.Pow); 00198 fprintf(fp, "Wis %d\n", pl->orig_stats.Wis); 00199 fprintf(fp, "Cha %d\n", pl->orig_stats.Cha); 00200 00201 /* Save hp table */ 00202 fprintf(fp, "lev_hp %d\n", op->level); 00203 00204 for (i = 1; i <= op->level; i++) 00205 { 00206 fprintf(fp, "%d\n", pl->levhp[i]); 00207 } 00208 00209 /* Save sp table */ 00210 fprintf(fp, "lev_sp %d\n", pl->exp_ptr[EXP_MAGICAL]->level); 00211 00212 for (i = 1; i <= pl->exp_ptr[EXP_MAGICAL]->level; i++) 00213 { 00214 fprintf(fp, "%d\n", pl->levsp[i]); 00215 } 00216 00217 /* Save grace table */ 00218 fprintf(fp, "lev_grace %d\n", pl->exp_ptr[EXP_WISDOM]->level); 00219 00220 for (i = 1; i <= pl->exp_ptr[EXP_WISDOM]->level; i++) 00221 { 00222 fprintf(fp, "%d\n", pl->levgrace[i]); 00223 } 00224 00225 for (i = 0; i < pl->nrofknownspells; i++) 00226 { 00227 fprintf(fp, "known_spell %s\n", spells[pl->known_spells[i]].name); 00228 } 00229 00230 for (i = 0; i < pl->num_cmd_permissions; i++) 00231 { 00232 if (pl->cmd_permissions[i]) 00233 { 00234 fprintf(fp, "cmd_permission %s\n", pl->cmd_permissions[i]); 00235 } 00236 } 00237 00238 for (i = 0; i < MAX_QUICKSLOT; i++) 00239 { 00240 if (pl->spell_quickslots[i] != SP_NO_SPELL) 00241 { 00242 fprintf(fp, "spell_quickslot %d %d\n", i, pl->spell_quickslots[i]); 00243 } 00244 } 00245 00246 for (i = 0; i < pl->num_faction_ids; i++) 00247 { 00248 if (pl->faction_ids[i]) 00249 { 00250 fprintf(fp, "faction %s %"FMT64"\n", pl->faction_ids[i], pl->faction_reputation[i]); 00251 } 00252 } 00253 00254 fprintf(fp, "fame %"FMT64"\n", pl->fame); 00255 fprintf(fp, "endplst\n"); 00256 00257 SET_FLAG(op, FLAG_NO_FIX_PLAYER); 00258 CLEAR_FLAG(op, FLAG_WIZ); 00259 00260 /* Don't check and don't remove */ 00261 save_object(fp, op, 3); 00262 00263 /* Make sure the write succeeded */ 00264 if (fclose(fp) == EOF) 00265 { 00266 new_draw_info(0, COLOR_WHITE, op, "Can't save character."); 00267 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 00268 rename(backupfile, filename); 00269 return 0; 00270 } 00271 00272 if (wiz) 00273 { 00274 SET_FLAG(op, FLAG_WIZ); 00275 } 00276 00277 rename(backupfile, filename); 00278 chmod(filename, SAVE_MODE); 00279 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 00280 return 1; 00281 } 00282 00288 static int spell_sort(const void *a1, const void *a2) 00289 { 00290 return strcmp(spells[(int) *(sint16 *) a1].name, spells[(int) *(sint16 *) a2].name); 00291 } 00292 00300 static void reorder_inventory(object *op) 00301 { 00302 object *tmp, *tmp2; 00303 00304 tmp2 = op->inv->below; 00305 op->inv->above = NULL; 00306 op->inv->below = NULL; 00307 00308 for (; tmp2; ) 00309 { 00310 tmp = tmp2; 00311 /* Save the following element */ 00312 tmp2 = tmp->below; 00313 tmp->above = NULL; 00314 /* Resort it like in insert_ob_in_ob() */ 00315 tmp->below = op->inv; 00316 tmp->below->above = tmp; 00317 op->inv = tmp; 00318 00319 if (tmp->inv) 00320 { 00321 reorder_inventory(tmp); 00322 } 00323 } 00324 } 00325 00331 static void wrong_password(player *pl) 00332 { 00333 pl->socket.password_fails++; 00334 00335 LOG(llevSystem, "%s@%s: Failed to provide correct password.\n", query_name(pl->ob, NULL), pl->socket.host); 00336 00337 if (pl->socket.password_fails >= MAX_PASSWORD_FAILURES) 00338 { 00339 LOG(llevSystem, "%s@%s: Failed to provide a correct password too many times!\n", query_name(pl->ob, NULL), pl->socket.host); 00340 send_socket_message(COLOR_RED, &pl->socket, "You have failed to provide a correct password too many times."); 00341 pl->socket.status = Ns_Zombie; 00342 } 00343 else 00344 { 00345 FREE_AND_COPY_HASH(pl->ob->name, "noname"); 00346 get_name(pl->ob); 00347 } 00348 } 00349 00353 void check_login(object *op) 00354 { 00355 FILE *fp; 00356 void *mybuffer; 00357 char filename[MAX_BUF], buf[MAX_BUF], bufall[MAX_BUF]; 00358 int i, value, comp, correct = 0, type; 00359 player *pl = CONTR(op), *pltmp; 00360 time_t elapsed_save_time = 0; 00361 struct stat statbuf; 00362 object *tmp; 00363 00364 strcpy(pl->maplevel, first_map_path); 00365 00366 /* Check if this matches a connected player, and if so disconnect old 00367 * and connect new. */ 00368 for (pltmp = first_player; pltmp != NULL; pltmp = pltmp->next) 00369 { 00370 if (pltmp != pl && pltmp->ob->name != NULL && !strcmp(pltmp->ob->name, op->name)) 00371 { 00372 if (check_password(pl->write_buf + 1, pltmp->password)) 00373 { 00374 pltmp->socket.status = Ns_Dead; 00375 /* Need to call this, otherwise the player won't get saved correctly. */ 00376 remove_ns_dead_player(pltmp); 00377 break; 00378 } 00379 else 00380 { 00381 wrong_password(pl); 00382 return; 00383 } 00384 } 00385 } 00386 00387 if (pl->state == ST_PLAYING) 00388 { 00389 LOG(llevSystem, ">%s< from IP %s - double login!\n", op->name, pl->socket.host); 00390 send_socket_message(COLOR_RED, &pl->socket, "Connection refused.\nYou manipulated the login procedure."); 00391 pl->socket.status = Ns_Zombie; 00392 return; 00393 } 00394 00395 if (checkbanned(op->name, pl->socket.host)) 00396 { 00397 LOG(llevSystem, "Ban: Banned player tried to login. [%s@%s]\n", op->name, pl->socket.host); 00398 send_socket_message(COLOR_RED, &pl->socket, "Connection refused.\nYou are banned!"); 00399 pl->socket.status = Ns_Zombie; 00400 return; 00401 } 00402 00403 LOG(llevInfo, "Login %s from IP %s\n", op->name, pl->socket.host); 00404 00405 snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name); 00406 00407 /* If no file, must be a new player, so lets get confirmation of 00408 * the password. Return control to the higher level dispatch, 00409 * since the rest of this just deals with loading of the file. */ 00410 if ((fp = open_and_uncompress(filename, 1, &comp)) == NULL) 00411 { 00412 confirm_password(op); 00413 return; 00414 } 00415 00416 if (fstat(fileno(fp), &statbuf)) 00417 { 00418 LOG(llevBug, "Unable to stat %s?\n", filename); 00419 elapsed_save_time = 0; 00420 } 00421 else 00422 { 00423 elapsed_save_time = time(NULL) - statbuf.st_mtime; 00424 00425 if (elapsed_save_time < 0) 00426 { 00427 LOG(llevBug, "Player file %s was saved in the future? (%"FMT64U" time)\n", filename, (uint64) elapsed_save_time); 00428 elapsed_save_time = 0; 00429 } 00430 } 00431 00432 if (fgets(bufall, sizeof(bufall), fp)) 00433 { 00434 if (sscanf(bufall, "password %s\n", buf)) 00435 { 00436 correct = check_password(pl->write_buf + 1, buf); 00437 } 00438 } 00439 00440 if (!correct) 00441 { 00442 wrong_password(pl); 00443 return; 00444 } 00445 00446 #ifdef SAVE_INTERVAL 00447 pl->last_save_time = time(NULL); 00448 #endif 00449 00450 pl->party = NULL; 00451 00452 pl->orig_stats.Str = 0; 00453 pl->orig_stats.Dex = 0; 00454 pl->orig_stats.Con = 0; 00455 pl->orig_stats.Int = 0; 00456 pl->orig_stats.Pow = 0; 00457 pl->orig_stats.Wis = 0; 00458 pl->orig_stats.Cha = 0; 00459 strcpy(pl->savebed_map, first_map_path); 00460 pl->bed_x = 0; 00461 pl->bed_y = 0; 00462 00463 /* Loop through the file, loading the rest of the values. */ 00464 while (fgets(bufall, sizeof(bufall), fp)) 00465 { 00466 sscanf(bufall, "%s %d\n", buf, &value); 00467 00468 if (!strcmp(buf, "endplst")) 00469 { 00470 break; 00471 } 00472 else if (!strcmp(buf, "dm_stealth")) 00473 { 00474 pl->dm_stealth = value; 00475 } 00476 else if (!strcmp(buf, "ms_privacy")) 00477 { 00478 pl->ms_privacy = value; 00479 } 00480 else if (!strcmp(buf, "no_shout")) 00481 { 00482 pl->no_shout = value; 00483 } 00484 else if (!strcmp(buf, "gen_hp")) 00485 { 00486 pl->gen_hp = value; 00487 } 00488 else if (!strcmp(buf, "shoottype")) 00489 { 00490 pl->shoottype = (rangetype) value; 00491 } 00492 else if (!strcmp(buf, "gen_sp")) 00493 { 00494 pl->gen_sp = value; 00495 } 00496 else if (!strcmp(buf, "gen_grace")) 00497 { 00498 pl->gen_grace = value; 00499 } 00500 else if (!strcmp(buf, "spell")) 00501 { 00502 pl->chosen_spell = value; 00503 } 00504 else if (!strcmp(buf, "digestion")) 00505 { 00506 pl->digestion = value; 00507 } 00508 else if (!strcmp(buf, "map")) 00509 { 00510 sscanf(bufall, "map %s", pl->maplevel); 00511 } 00512 else if (!strcmp(buf, "savebed_map")) 00513 { 00514 sscanf(bufall, "savebed_map %s", pl->savebed_map); 00515 } 00516 else if (!strcmp(buf, "bed_x")) 00517 { 00518 pl->bed_x = value; 00519 } 00520 else if (!strcmp(buf, "bed_y")) 00521 { 00522 pl->bed_y = value; 00523 } 00524 else if (!strcmp(buf, "Str")) 00525 { 00526 pl->orig_stats.Str = value; 00527 } 00528 else if (!strcmp(buf, "Dex")) 00529 { 00530 pl->orig_stats.Dex = value; 00531 } 00532 else if (!strcmp(buf, "Con")) 00533 { 00534 pl->orig_stats.Con = value; 00535 } 00536 else if (!strcmp(buf, "Int")) 00537 { 00538 pl->orig_stats.Int = value; 00539 } 00540 else if (!strcmp(buf, "Pow")) 00541 { 00542 pl->orig_stats.Pow = value; 00543 } 00544 else if (!strcmp(buf, "Wis")) 00545 { 00546 pl->orig_stats.Wis = value; 00547 } 00548 else if (!strcmp(buf, "Cha")) 00549 { 00550 pl->orig_stats.Cha = value; 00551 } 00552 else if (!strcmp(buf, "lev_hp")) 00553 { 00554 int j; 00555 00556 for (i = 1; i <= value; i++) 00557 { 00558 if (fscanf(fp, "%d\n", &j)) 00559 { 00560 pl->levhp[i] = j; 00561 } 00562 } 00563 } 00564 else if (!strcmp(buf, "lev_sp")) 00565 { 00566 int j; 00567 00568 for (i = 1; i <= value; i++) 00569 { 00570 if (fscanf(fp, "%d\n", &j)) 00571 { 00572 pl->levsp[i] = j; 00573 } 00574 } 00575 } 00576 else if (!strcmp(buf, "lev_grace")) 00577 { 00578 int j; 00579 00580 for (i = 1; i <= value; i++) 00581 { 00582 if (fscanf(fp, "%d\n", &j)) 00583 { 00584 pl->levgrace[i] = j; 00585 } 00586 } 00587 } 00588 else if (!strcmp(buf, "known_spell")) 00589 { 00590 char *cp = strchr(bufall, '\n'); 00591 00592 *cp = '\0'; 00593 cp = strchr(bufall, ' '); 00594 cp++; 00595 00596 for (i = 0; i < NROFREALSPELLS; i++) 00597 { 00598 if (!strcmp(spells[i].name, cp)) 00599 { 00600 pl->known_spells[pl->nrofknownspells++] = i; 00601 break; 00602 } 00603 } 00604 00605 if (i == NROFREALSPELLS) 00606 { 00607 LOG(llevDebug, "check_login(): Bogus spell (%s) in %s\n", cp, filename); 00608 } 00609 } 00610 else if (!strcmp(buf, "cmd_permission")) 00611 { 00612 char *cp = strchr(bufall, '\n'); 00613 00614 *cp = '\0'; 00615 cp = strchr(bufall, ' '); 00616 cp++; 00617 00618 pl->num_cmd_permissions++; 00619 pl->cmd_permissions = realloc(pl->cmd_permissions, sizeof(char *) * pl->num_cmd_permissions); 00620 pl->cmd_permissions[pl->num_cmd_permissions - 1] = strdup_local(cp); 00621 } 00622 else if (!strcmp(buf, "spell_quickslot")) 00623 { 00624 char *cp = strrchr(bufall, ' '); 00625 sint16 spell_id = atoi(cp + 1); 00626 00627 if (spell_id < 0 || spell_id >= NROFREALSPELLS) 00628 { 00629 LOG(llevDebug, "check_login(): Bogus spell ID (#%d) in %s\n", spell_id, filename); 00630 } 00631 00632 pl->spell_quickslots[value] = spell_id; 00633 } 00634 else if (!strcmp(buf, "faction")) 00635 { 00636 char faction_id[MAX_BUF]; 00637 sint64 rep; 00638 00639 if (sscanf(bufall, "faction %s %"FMT64, faction_id, &rep) == 2) 00640 { 00641 pl->faction_ids = realloc(pl->faction_ids, sizeof(*pl->faction_ids) * (pl->num_faction_ids + 1)); 00642 pl->faction_reputation = realloc(pl->faction_reputation, sizeof(*pl->faction_reputation) * (pl->num_faction_ids + 1)); 00643 pl->faction_ids[pl->num_faction_ids] = add_string(faction_id); 00644 pl->faction_reputation[pl->num_faction_ids] = rep; 00645 pl->num_faction_ids++; 00646 } 00647 } 00648 else if (!strcmp(buf, "fame")) 00649 { 00650 pl->fame = value; 00651 } 00652 } 00653 00654 /* Take the player object out from the void */ 00655 if (!QUERY_FLAG(op, FLAG_REMOVED)) 00656 { 00657 remove_ob(op); 00658 } 00659 00660 /* We transfer it to a new object */ 00661 op->custom_attrset = NULL; 00662 00663 LOG(llevDebug, "load obj for player: %s\n", op->name); 00664 00665 /* Create a new object for the real player data */ 00666 op = get_object(); 00667 00668 /* This loads the standard objects values. */ 00669 mybuffer = create_loader_buffer(fp); 00670 load_object(fp, op, mybuffer, LO_REPEAT, 0); 00671 delete_loader_buffer(mybuffer); 00672 close_and_delete(fp, comp); 00673 00674 /* The inventory of players is reverse loaded, so let's exchange the 00675 * order here. */ 00676 if (op->inv) 00677 { 00678 reorder_inventory(op); 00679 } 00680 00681 op->custom_attrset = pl; 00682 pl->ob = op; 00683 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 00684 00685 op->type = PLAYER; 00686 00687 /* This is a funny thing: what happens when the autosave function saves a player 00688 * with negative hp? Well, the sever tries to create a gravestone and heals the 00689 * player... and then server tries to insert gravestone and anim on a map - but 00690 * player is still in login! So, we are nice and set hp to 1 if here negative. */ 00691 if (op->stats.hp < 0) 00692 { 00693 op->stats.hp = 1; 00694 } 00695 00696 pl->state = ST_PLAYING; 00697 #ifdef AUTOSAVE 00698 pl->last_save_tick = pticks; 00699 #endif 00700 op->carrying = sum_weight(op); 00701 00702 init_player_exp(op); 00703 link_player_skills(op); 00704 00705 if (!legal_range(op, pl->shoottype)) 00706 { 00707 pl->shoottype = range_none; 00708 } 00709 00710 fix_player(op); 00711 00712 /* Display Message of the Day */ 00713 display_motd(op); 00714 00715 if (!pl->dm_stealth) 00716 { 00717 new_draw_info_format(NDI_ALL, COLOR_DK_ORANGE, NULL, "%s has entered the game.", query_name(pl->ob, NULL)); 00718 } 00719 00720 /* Trigger the global LOGIN event */ 00721 trigger_global_event(GEVENT_LOGIN, pl, pl->socket.host); 00722 00723 esrv_new_player(pl, op->weight + op->carrying); 00724 esrv_send_inventory(op, op); 00725 00726 /* This seems to compile without warnings now. Don't know if it works 00727 * on SGI's or not, however. */ 00728 qsort((void *) pl->known_spells, pl->nrofknownspells, sizeof(pl->known_spells[0]), (void *) (int (*)()) spell_sort); 00729 00730 if (!QUERY_FLAG(op, FLAG_FRIENDLY)) 00731 { 00732 LOG(llevBug, "Player %s was loaded without friendly flag!", query_name(op, NULL)); 00733 SET_FLAG(op, FLAG_FRIENDLY); 00734 } 00735 00736 enter_exit(op, NULL); 00737 00738 pl->socket.update_tile = 0; 00739 pl->socket.look_position = 0; 00740 pl->socket.ext_title_flag = 1; 00741 00742 /* No direction; default to southeast. */ 00743 if (!op->direction) 00744 { 00745 op->direction = SOUTHEAST; 00746 } 00747 00748 op->anim_last_facing = op->anim_last_facing_last = op->facing = op->direction; 00749 /* We assume that players always have a valid animation. */ 00750 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction); 00751 esrv_new_player(pl, op->weight + op->carrying); 00752 send_spelllist_cmd(op, NULL, SPLIST_MODE_ADD); 00753 send_skilllist_cmd(op, NULL, SPLIST_MODE_ADD); 00754 send_quickslots(pl); 00755 00756 /* Go through the player's inventory and inform the client about 00757 * readied objects. */ 00758 for (tmp = op->inv; tmp; tmp = tmp->below) 00759 { 00760 if (QUERY_FLAG(tmp, FLAG_IS_READY)) 00761 { 00762 type = cmd_ready_determine(tmp); 00763 00764 if (type != -1) 00765 { 00766 pl->ready_object[type] = tmp; 00767 pl->ready_object_tag[type] = tmp->count; 00768 cmd_ready_send(pl, tmp->count, type); 00769 } 00770 } 00771 } 00772 00773 if (op->map && op->map->events) 00774 { 00775 trigger_map_event(MEVENT_LOGIN, op->map, op, NULL, NULL, NULL, 0); 00776 } 00777 }
1.7.4