Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_login_c = 00003 * "$Id: login.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2006 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00034 #include <global.h> 00035 #ifndef __CEXTRACT__ 00036 #include <sproto.h> 00037 #endif 00038 #include <spells.h> 00039 #include <loader.h> 00040 #include <define.h> 00041 00042 static void copy_file(const char *filename, FILE *fpout); 00043 00051 void emergency_save(int flag) { 00052 #ifndef NO_EMERGENCY_SAVE 00053 player *pl; 00054 00055 trying_emergency_save = 1; 00056 LOG(llevError, "Emergency save: "); 00057 for (pl = first_player; pl != NULL; pl = pl->next) { 00058 if (!pl->ob) { 00059 LOG(llevError, "No name, ignoring this.\n"); 00060 continue; 00061 } 00062 LOG(llevError, "%s ", pl->ob->name); 00063 draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00064 "Emergency save...", NULL); 00065 00066 /* If we are not exiting the game (ie, this is sort of a backup save), then 00067 * don't change the location back to the village. Note that there are other 00068 * options to have backup saves be done at the starting village 00069 */ 00070 if (!flag) { 00071 strcpy(pl->maplevel, first_map_path); 00072 if (pl->ob->map != NULL) 00073 pl->ob->map = NULL; 00074 pl->ob->x = -1; 00075 pl->ob->y = -1; 00076 } 00077 if (!save_player(pl->ob, flag)) { 00078 LOG(llevError, "(failed) "); 00079 draw_ext_info(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00080 "Emergency save failed, checking score...", NULL); 00081 } 00082 check_score(pl->ob, 1); 00083 } 00084 LOG(llevError, "\n"); 00085 #else 00086 LOG(llevInfo, "Emergency saves disabled, no save attempted\n"); 00087 #endif 00088 } 00089 00097 void delete_character(const char *name) { 00098 char buf[MAX_BUF]; 00099 00100 snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, name); 00101 /* this effectively does an rm -rf on the directory */ 00102 remove_directory(buf); 00103 } 00104 00120 int verify_player(const char *name, char *password) { 00121 char buf[MAX_BUF]; 00122 int comp; 00123 FILE *fp; 00124 00125 if (strpbrk(name, "/.\\") != NULL) { 00126 LOG(llevError, "Username contains illegal characters: %s\n", name); 00127 return 1; 00128 } 00129 00130 snprintf(buf, sizeof(buf), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, name, name); 00131 if (strlen(buf) >= sizeof(buf)-1) { 00132 LOG(llevError, "Username too long: %s\n", name); 00133 return 1; 00134 } 00135 00136 if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) 00137 return 1; 00138 00139 /* Read in the file until we find the password line. Our logic could 00140 * be a bit better on cleaning up the password from the file, but since 00141 * it is written by the program, I think it is fair to assume that the 00142 * syntax should be pretty standard. 00143 */ 00144 while (fgets(buf, MAX_BUF-1, fp) != NULL) { 00145 if (!strncmp(buf, "password ", 9)) { 00146 buf[strlen(buf)-1] = 0; /* remove newline */ 00147 if (check_password(password, buf+9)) { 00148 close_and_delete(fp, comp); 00149 return 0; 00150 } else { 00151 close_and_delete(fp, comp); 00152 return 2; 00153 } 00154 } 00155 } 00156 LOG(llevDebug, "Could not find a password line in player %s\n", name); 00157 close_and_delete(fp, comp); 00158 return 1; 00159 } 00160 00173 int check_name(player *me, const char *name) { 00174 if (*name == '\0') { 00175 draw_ext_info(NDI_UNIQUE, 0, me->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00176 "Your username cannot be blank.", NULL); 00177 return 0; 00178 } 00179 00180 if (!playername_ok(name)) { 00181 draw_ext_info(NDI_UNIQUE, 0, me->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00182 "That name contains illegal characters. Use letters, hyphens and underscores only. Hyphens and underscores are not allowed as the first character.", NULL); 00183 return 0; 00184 } 00185 if (strlen(name) >= MAX_NAME) { 00186 draw_ext_info_format(NDI_UNIQUE, 0, me->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00187 "That name is too long. (Max length: %d characters)", NULL, MAX_NAME); 00188 return 0; 00189 } 00190 00191 return 1; 00192 } 00193 00201 void destroy_object(object *op) { 00202 object *tmp; 00203 while ((tmp = op->inv)) 00204 destroy_object(tmp); 00205 00206 if (!QUERY_FLAG(op, FLAG_REMOVED)) 00207 remove_ob(op); 00208 free_object(op); 00209 } 00210 00223 int save_player(object *op, int flag) { 00224 FILE *fp; 00225 char filename[MAX_BUF], *tmpfilename, backupfile[MAX_BUF]; 00226 object *tmp, *container = NULL; 00227 player *pl = op->contr; 00228 int i, wiz = QUERY_FLAG(op, FLAG_WIZ); 00229 long checksum; 00230 #ifdef BACKUP_SAVE_AT_HOME 00231 sint16 backup_x, backup_y; 00232 #endif 00233 00234 if (!op->stats.exp) 00235 return 0; /* no experience, no save */ 00236 00237 flag &= 1; 00238 00239 if (!pl->name_changed||(!flag&&!op->stats.exp)) { 00240 if (!flag) { 00241 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00242 "Your game is not valid, game not saved.", NULL); 00243 } 00244 return 0; 00245 } 00246 00247 /* Sanity check - some stuff changes this when player is exiting */ 00248 if (op->type != PLAYER) 00249 return 0; 00250 00251 /* Prevent accidental saves if connection is reset after player has 00252 * mostly exited. 00253 */ 00254 if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD) 00255 return 0; 00256 00257 if (flag == 0) 00258 terminate_all_pets(op); 00259 00260 snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name); 00261 make_path_to_file(filename); 00262 fp = tempnam_secure(settings.tmpdir, NULL, &tmpfilename); 00263 if (!fp) { 00264 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00265 "Can't get secure temporary file for save.", NULL); 00266 LOG(llevDebug, "Can't get secure temporary file for save.\n"); 00267 return 0; 00268 } 00269 00270 /* Eneq(@csd.uu.se): If we have an open container hide it. */ 00271 if (op->container) { 00272 container = op->container; 00273 op->container = NULL; 00274 } 00275 00276 fprintf(fp, "password %s\n", pl->password); 00277 if (settings.set_title == TRUE) 00278 if (pl->own_title[0] != '\0') 00279 fprintf(fp, "title %s\n", pl->own_title); 00280 00281 fprintf(fp, "explore %d\n", pl->explore); 00282 fprintf(fp, "gen_hp %d\n", pl->gen_hp); 00283 fprintf(fp, "gen_sp %d\n", pl->gen_sp); 00284 fprintf(fp, "gen_grace %d\n", pl->gen_grace); 00285 fprintf(fp, "listening %d\n", pl->listening); 00286 fprintf(fp, "shoottype %d\n", pl->shoottype); 00287 fprintf(fp, "bowtype %d\n", pl->bowtype); 00288 fprintf(fp, "petmode %d\n", pl->petmode); 00289 fprintf(fp, "peaceful %d\n", pl->peaceful); 00290 fprintf(fp, "no_shout %d\n", pl->no_shout); 00291 fprintf(fp, "digestion %d\n", pl->digestion); 00292 fprintf(fp, "pickup %u\n", pl->mode); 00293 fprintf(fp, "outputs_sync %d\n", pl->outputs_sync); 00294 fprintf(fp, "outputs_count %d\n", pl->outputs_count); 00295 /* Match the enumerations but in string form */ 00296 fprintf(fp, "usekeys %s\n", pl->usekeys == key_inventory ? "key_inventory" : (pl->usekeys == keyrings ? "keyrings" : "containers")); 00297 /* Match the enumerations but in string form */ 00298 fprintf(fp, "unapply %s\n", pl->unapply == unapply_nochoice ? "unapply_nochoice" : (pl->unapply == unapply_never ? "unapply_never" : "unapply_always")); 00299 00300 #ifdef BACKUP_SAVE_AT_HOME 00301 if (op->map != NULL && flag == 0) 00302 #else 00303 if (op->map != NULL) 00304 #endif 00305 fprintf(fp, "map %s\n", op->map->path); 00306 else 00307 fprintf(fp, "map %s\n", settings.emergency_mapname); 00308 00309 fprintf(fp, "savebed_map %s\n", pl->savebed_map); 00310 fprintf(fp, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y); 00311 fprintf(fp, "weapon_sp %f\n", pl->weapon_sp); 00312 fprintf(fp, "Str %d\n", pl->orig_stats.Str); 00313 fprintf(fp, "Dex %d\n", pl->orig_stats.Dex); 00314 fprintf(fp, "Con %d\n", pl->orig_stats.Con); 00315 fprintf(fp, "Int %d\n", pl->orig_stats.Int); 00316 fprintf(fp, "Pow %d\n", pl->orig_stats.Pow); 00317 fprintf(fp, "Wis %d\n", pl->orig_stats.Wis); 00318 fprintf(fp, "Cha %d\n", pl->orig_stats.Cha); 00319 00320 fprintf(fp, "lev_array %d\n", MIN(op->level, 10)); 00321 for (i = 1; i <= pl->last_level && i <= 10; i++) { 00322 fprintf(fp, "%d\n", pl->levhp[i]); 00323 fprintf(fp, "%d\n", pl->levsp[i]); 00324 fprintf(fp, "%d\n", pl->levgrace[i]); 00325 } 00326 fprintf(fp, "party_rejoin_mode %d\n", pl->rejoin_party); 00327 if (pl->party != NULL) { 00328 fprintf(fp, "party_rejoin_name %s\n", pl->party->partyname); 00329 fprintf(fp, "party_rejoin_password %s\n", pl->party->passwd); 00330 } 00331 fprintf(fp, "language %d\n", pl->language); 00332 fprintf(fp, "endplst\n"); 00333 00334 SET_FLAG(op, FLAG_NO_FIX_PLAYER); 00335 CLEAR_FLAG(op, FLAG_WIZ); 00336 #ifdef BACKUP_SAVE_AT_HOME 00337 if (flag) { 00338 backup_x = op->x; 00339 backup_y = op->y; 00340 op->x = -1; 00341 op->y = -1; 00342 } 00343 /* Save objects, but not unpaid objects. Don't remove objects from 00344 * inventory. 00345 */ 00346 i = save_object(fp, op, SAVE_FLAG_NO_REMOVE); 00347 if (flag) { 00348 op->x = backup_x; 00349 op->y = backup_y; 00350 } 00351 #else 00352 i = save_object(fp, op, SAVE_FLAG_SAVE_UNPAID|SAVE_FLAG_NO_REMOVE); /* don't check and don't remove */ 00353 #endif 00354 00355 if (wiz) 00356 SET_FLAG(op, FLAG_WIZ); 00357 00358 if (fclose(fp) != 0 || i != SAVE_ERROR_OK) { /* make sure the write succeeded */ 00359 draw_ext_info(NDI_UNIQUE|NDI_RED, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00360 "Can't save character!", NULL); 00361 draw_ext_info_format(NDI_ALL_DMS|NDI_RED, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "Save failure for player %s!", NULL, op->name); 00362 unlink(tmpfilename); 00363 free(tmpfilename); 00364 return 0; 00365 } 00366 00367 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 00368 00369 if (!flag) { 00370 while ((tmp = op->inv)) 00371 destroy_object(tmp); 00372 00373 /* destroying objects will most likely destroy the pointer 00374 * in op->contr->ranges[], so clear the range to a safe value. 00375 */ 00376 op->contr->shoottype = range_none; 00377 } 00378 00379 checksum = 0; 00380 snprintf(backupfile, sizeof(backupfile), "%s.tmp", filename); 00381 rename(filename, backupfile); 00382 fp = fopen(filename, "w"); 00383 if (!fp) { 00384 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00385 "Can't open file for save.", NULL); 00386 unlink(tmpfilename); 00387 free(tmpfilename); 00388 return 0; 00389 } 00390 fprintf(fp, "checksum %lx\n", checksum); 00391 copy_file(tmpfilename, fp); 00392 unlink(tmpfilename); 00393 free(tmpfilename); 00394 if (fclose(fp) == EOF) { /* got write error */ 00395 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, 00396 "Can't close file for save.", NULL); 00397 rename(backupfile, filename); /* Restore the original */ 00398 return 0; 00399 } else 00400 unlink(backupfile); 00401 00402 /* Eneq(@csd.uu.se): Reveal the container if we have one. */ 00403 if (flag && container != NULL) 00404 op->container = container; 00405 00406 if (!flag) 00407 esrv_send_inventory(op, op); 00408 00409 chmod(filename, SAVE_MODE); 00410 return 1; 00411 } 00412 00421 static void copy_file(const char *filename, FILE *fpout) { 00422 FILE *fp; 00423 char buf[MAX_BUF]; 00424 00425 if ((fp = fopen(filename, "r")) == NULL) { 00426 LOG(llevError, "copy_file failed to open \"%s\", player file(s) may be corrupt.\n", filename); 00427 return; 00428 } 00429 while (fgets(buf, MAX_BUF, fp) != NULL) 00430 fputs(buf, fpout); 00431 fclose(fp); 00432 } 00433 00441 static void wrong_password(object *op) { 00442 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00443 "\nA character with this name already exists. " 00444 "Please choose another name, or make sure you entered your " 00445 "password correctly.\n", 00446 NULL); 00447 00448 FREE_AND_COPY(op->name, "noname"); 00449 FREE_AND_COPY(op->name_pl, "noname"); 00450 00451 op->contr->socket.password_fails++; 00452 if (op->contr->socket.password_fails >= MAX_PASSWORD_FAILURES) { 00453 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00454 "You gave an incorrect password too many times, " 00455 "you will now be dropped from the server.", 00456 NULL); 00457 00458 LOG(llevInfo, "A player connecting from %s has been dropped for password failure\n", 00459 op->contr->socket.host); 00460 00461 op->contr->socket.status = Ns_Dead; /* the socket loop should handle the rest for us */ 00462 } else 00463 get_name(op); 00464 } 00465 00473 void check_login(object *op) { 00474 FILE *fp; 00475 char filename[MAX_BUF]; 00476 char buf[MAX_BUF], bufall[MAX_BUF]; 00477 int i, value, comp; 00478 long checksum = 0; 00479 player *pl = op->contr, *pltmp; 00480 int correct = 0; 00481 time_t elapsed_save_time = 0; 00482 struct stat statbuf; 00483 char *party_name = NULL, party_password[9]; 00484 00485 strcpy(pl->maplevel, first_map_path); 00486 party_password[0] = 0; 00487 00488 /* Check if this matches a connected player, and if yes disconnect old / connect new. */ 00489 for (pltmp = first_player; pltmp != NULL; pltmp = pltmp->next) { 00490 if (pltmp != pl && pltmp->ob->name != NULL && !strcmp(pltmp->ob->name, op->name)) { 00491 if (check_password(pl->write_buf+1, pltmp->password)) { 00492 00493 /* We could try and be more clever and re-assign the existing 00494 * object to the new player, etc. However, I'm concerned that 00495 * there may be a lot of other state that still needs to be sent 00496 * in that case (we can't make any assumptions on what the 00497 * client knows, as maybe the client crashed), so treating it 00498 * as just a normal login is the safest and easiest thing to do. 00499 */ 00500 00501 pltmp->socket.status = Ns_Dead; 00502 00503 save_player(pltmp->ob, 0); 00504 if (!QUERY_FLAG(pltmp->ob, FLAG_REMOVED)) { 00505 /* Need to terminate the pets, since the new object 00506 * will be different 00507 */ 00508 terminate_all_pets(pltmp->ob); 00509 remove_ob(pltmp->ob); 00510 } 00511 leave(pltmp, 1); 00512 final_free_player(pltmp); 00513 break; 00514 } else { 00515 wrong_password(op); 00516 return; 00517 } 00518 } 00519 } 00520 00521 snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name); 00522 00523 /* If no file, must be a new player, so lets get confirmation of 00524 * the password. Return control to the higher level dispatch, 00525 * since the rest of this just deals with loading of the file. 00526 */ 00527 if ((fp = open_and_uncompress(filename, 1, &comp)) == NULL) { 00528 confirm_password(op); 00529 return; 00530 } 00531 if (fstat(fileno(fp), &statbuf)) { 00532 LOG(llevError, "Unable to stat %s?\n", filename); 00533 elapsed_save_time = 0; 00534 } else { 00535 elapsed_save_time = time(NULL)-statbuf.st_mtime; 00536 if (elapsed_save_time < 0) { 00537 LOG(llevError, "Player file %s was saved in the future? (%d time)\n", filename, elapsed_save_time); 00538 elapsed_save_time = 0; 00539 } 00540 } 00541 00542 if (fgets(bufall, MAX_BUF, fp) != NULL) { 00543 if (!strncmp(bufall, "checksum ", 9)) { 00544 checksum = strtol(bufall+9, (char **)NULL, 16); 00545 (void)fgets(bufall, MAX_BUF, fp); 00546 } 00547 if (sscanf(bufall, "password %s\n", buf)) { 00548 /* New password scheme: */ 00549 correct = check_password(pl->write_buf+1, buf); 00550 } 00551 /* Old password mode removed - I have no idea what it 00552 * was, and the current password mechanism has been used 00553 * for at least several years. 00554 */ 00555 } 00556 if (!correct) { 00557 wrong_password(op); 00558 fclose(fp); 00559 return; 00560 } 00561 00562 #ifdef SAVE_INTERVAL 00563 pl->last_save_time = time(NULL); 00564 #endif /* SAVE_INTERVAL */ 00565 pl->party = NULL; 00566 if (settings.search_items == TRUE) 00567 pl->search_str[0] = '\0'; 00568 pl->name_changed = 1; 00569 pl->orig_stats.Str = 0; 00570 pl->orig_stats.Dex = 0; 00571 pl->orig_stats.Con = 0; 00572 pl->orig_stats.Int = 0; 00573 pl->orig_stats.Pow = 0; 00574 pl->orig_stats.Wis = 0; 00575 pl->orig_stats.Cha = 0; 00576 strcpy(pl->savebed_map, first_map_path); 00577 pl->bed_x = 0, 00578 pl->bed_y = 0; 00579 pl->spellparam[0] = '\0'; 00580 00581 /* Loop through the file, loading the rest of the values */ 00582 while (fgets(bufall, MAX_BUF, fp) != NULL) { 00583 sscanf(bufall, "%s %d\n", buf, &value); 00584 if (!strcmp(buf, "endplst")) 00585 break; 00586 else if (!strcmp(buf, "title") && settings.set_title == TRUE) 00587 sscanf(bufall, "title %[^\n]", pl->own_title); 00588 else if (!strcmp(buf, "explore")) 00589 pl->explore = value; 00590 else if (!strcmp(buf, "gen_hp")) 00591 pl->gen_hp = value; 00592 else if (!strcmp(buf, "shoottype")) 00593 pl->shoottype = (rangetype)value; 00594 else if (!strcmp(buf, "bowtype")) 00595 pl->bowtype = (bowtype_t)value; 00596 else if (!strcmp(buf, "petmode")) 00597 pl->petmode = (petmode_t)value; 00598 else if (!strcmp(buf, "gen_sp")) 00599 pl->gen_sp = value; 00600 else if (!strcmp(buf, "gen_grace")) 00601 pl->gen_grace = value; 00602 else if (!strcmp(buf, "listening")) 00603 pl->listening = value; 00604 else if (!strcmp(buf, "peaceful")) 00605 pl->peaceful = value; 00606 else if (!strcmp(buf, "no_shout")) 00607 pl->no_shout = value; 00608 else if (!strcmp(buf, "digestion")) 00609 pl->digestion = value; 00610 else if (!strcmp(buf, "pickup")) 00611 pl->mode = value; 00612 else if (!strcmp(buf, "outputs_sync")) 00613 pl->outputs_sync = value; 00614 else if (!strcmp(buf, "outputs_count")) 00615 pl->outputs_count = value; 00616 else if (!strcmp(buf, "map")) 00617 sscanf(bufall, "map %s", pl->maplevel); 00618 else if (!strcmp(buf, "savebed_map")) 00619 sscanf(bufall, "savebed_map %s", pl->savebed_map); 00620 else if (!strcmp(buf, "bed_x")) 00621 pl->bed_x = value; 00622 else if (!strcmp(buf, "bed_y")) 00623 pl->bed_y = value; 00624 else if (!strcmp(buf,"weapon_sp")) 00625 sscanf(buf, "weapon_sp %f", &pl->weapon_sp); 00626 else if (!strcmp(buf, "Str")) 00627 pl->orig_stats.Str = value; 00628 else if (!strcmp(buf, "Dex")) 00629 pl->orig_stats.Dex = value; 00630 else if (!strcmp(buf, "Con")) 00631 pl->orig_stats.Con = value; 00632 else if (!strcmp(buf, "Int")) 00633 pl->orig_stats.Int = value; 00634 else if (!strcmp(buf, "Pow")) 00635 pl->orig_stats.Pow = value; 00636 else if (!strcmp(buf, "Wis")) 00637 pl->orig_stats.Wis = value; 00638 else if (!strcmp(buf, "Cha")) 00639 pl->orig_stats.Cha = value; 00640 else if (!strcmp(buf, "usekeys")) { 00641 if (!strcmp(bufall+8, "key_inventory\n")) 00642 pl->usekeys = key_inventory; 00643 else if (!strcmp(bufall+8, "keyrings\n")) 00644 pl->usekeys = keyrings; 00645 else if (!strcmp(bufall+8, "containers\n")) 00646 pl->usekeys = containers; 00647 else 00648 LOG(llevDebug, "load_player: got unknown usekeys type: %s\n", bufall+8); 00649 } else if (!strcmp(buf, "unapply")) { 00650 if (!strcmp(bufall+8, "unapply_nochoice\n")) 00651 pl->unapply = unapply_nochoice; 00652 else if (!strcmp(bufall+8, "unapply_never\n")) 00653 pl->unapply = unapply_never; 00654 else if (!strcmp(bufall+8, "unapply_always\n")) 00655 pl->unapply = unapply_always; 00656 else 00657 LOG(llevDebug, "load_player: got unknown unapply type: %s\n", bufall+8); 00658 } else if (!strcmp(buf, "lev_array")) { 00659 for (i = 1; i <= value; i++) { 00660 int j; 00661 00662 fscanf(fp, "%d\n", &j); 00663 pl->levhp[i] = j; 00664 fscanf(fp, "%d\n", &j); 00665 pl->levsp[i] = j; 00666 fscanf(fp, "%d\n", &j); 00667 pl->levgrace[i] = j; 00668 } 00669 } else if (!strcmp(buf, "party_rejoin_mode")) { 00670 pl->rejoin_party = value; 00671 } else if (!strcmp(buf, "party_rejoin_name")) { 00672 party_name = strdup_local(bufall+strlen("party_rejoin_name")+1); 00673 if (party_name && strlen(party_name) > 0) 00674 party_name[strlen(party_name)-1] = '\0'; 00675 } else if (!strcmp(buf, "party_rejoin_password")) { 00676 size_t len; 00677 00678 snprintf(party_password, sizeof(party_password), "%s", bufall+strlen("party_rejoin_password")+1); 00679 len = strlen(party_password); 00680 /* Remove trailing \n if needed. If password is 8 chars long, 00681 * snprintf would already have dropped the \n. 00682 */ 00683 if (len > 0 && len < 8) 00684 party_password[len-1] = '\0'; 00685 } else if (!strcmp(buf, "language")) { 00686 if (value < 0 || value >= NUM_LANGUAGES) 00687 value = 0; 00688 pl->language = value; 00689 } 00690 } /* End of loop loading the character file */ 00691 remove_ob(op); 00692 op->speed = 0; 00693 update_ob_speed(op); 00694 /*FIXME dangerous call, reset_object should be used to init freshly allocated obj struct!*/ 00695 reset_object(op); 00696 op->contr = pl; 00697 pl->ob = op; 00698 /* this loads the standard objects values. */ 00699 load_object(fp, op, LO_NEWFILE, 0); 00700 close_and_delete(fp, comp); 00701 00702 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 00703 00704 strncpy(pl->title, op->arch->clone.name, sizeof(pl->title)-1); 00705 pl->title[sizeof(pl->title)-1] = '\0'; 00706 00707 /* If the map where the person was last saved does not exist, 00708 * restart them on their home-savebed. This is good for when 00709 * maps change between versions 00710 * First, we check for partial path, then check to see if the full 00711 * path (for unique player maps) 00712 */ 00713 if (check_path(pl->maplevel, 1) == -1) { 00714 if (check_path(pl->maplevel, 0) == -1) { 00715 strcpy(pl->maplevel, pl->savebed_map); 00716 op->x = pl->bed_x, 00717 op->y = pl->bed_y; 00718 /* if the map was a shop, the player can have unpaid items, remove them. */ 00719 remove_unpaid_objects(op, NULL, 1); 00720 } 00721 } 00722 00723 /* If player saved beyond some time ago, and the feature is 00724 * enabled, put the player back on his savebed map. 00725 */ 00726 if ((settings.reset_loc_time > 0) && (elapsed_save_time > settings.reset_loc_time)) { 00727 strcpy(pl->maplevel, pl->savebed_map); 00728 op->x = pl->bed_x, op->y = pl->bed_y; 00729 /* if the map was a shop, the player can have unpaid items, remove them. */ 00730 remove_unpaid_objects(op, NULL, 1); 00731 } 00732 00733 /* make sure he's a player--needed because of class change. */ 00734 op->type = PLAYER; 00735 00736 enter_exit(op, NULL); 00737 00738 pl->name_changed = 1; 00739 pl->state = ST_PLAYING; 00740 #ifdef AUTOSAVE 00741 pl->last_save_tick = pticks; 00742 #endif 00743 op->carrying = sum_weight(op); 00744 00745 link_player_skills(op); 00746 00747 if (!legal_range(op, op->contr->shoottype)) 00748 op->contr->shoottype = range_none; 00749 00750 /* if it's a dragon player, set the correct title here */ 00751 if (is_dragon_pl(op) && op->inv != NULL) { 00752 object *tmp, *abil = NULL, *skin = NULL; 00753 00754 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) { 00755 if (tmp->type == FORCE) { 00756 if (strcmp(tmp->arch->name, "dragon_ability_force") == 0) 00757 abil = tmp; 00758 else if (strcmp(tmp->arch->name, "dragon_skin_force") == 0) 00759 skin = tmp; 00760 } 00761 } 00762 set_dragon_name(op, abil, skin); 00763 } 00764 00765 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00766 "Welcome Back!", NULL); 00767 draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL, 00768 MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER, 00769 "%s has entered the game.", 00770 "%s has entered the game.", 00771 pl->ob->name); 00772 00773 /* Lauwenmark : Here we handle the LOGIN global event */ 00774 execute_global_event(EVENT_LOGIN, pl, pl->socket.host); 00775 op->contr->socket.update_look = 1; 00776 /* If the player should be dead, call kill_player for them 00777 * Only check for hp - if player lacks food, let the normal 00778 * logic for that to take place. If player is permanently 00779 * dead, and not using permadeath mode, the kill_player will 00780 * set the play_again flag, so return. 00781 */ 00782 if (op->stats.hp < 0) { 00783 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN, 00784 "Your character was dead last your played.", 00785 NULL); 00786 kill_player(op); 00787 if (pl->state != ST_PLAYING) 00788 return; 00789 } 00790 LOG(llevInfo, "LOGIN: Player named %s from ip %s\n", op->name, 00791 op->contr->socket.host); 00792 00793 /* Do this after checking for death - no reason sucking up bandwidth if 00794 * the data isn't needed. 00795 */ 00796 esrv_new_player(op->contr, op->weight+op->carrying); 00797 /* Need to do these after esvr_new_player, as once the client 00798 * sees that, it wipes any info it has about the player. 00799 */ 00800 esrv_add_spells(op->contr, NULL); 00801 00802 /* Need to call fix_object now - program modified so that it is not 00803 * called during the load process (FLAG_NO_FIX_PLAYER set when 00804 * saved) 00805 * Moved ahead of the esrv functions, so proper weights will be 00806 * sent to the client. Needs to be after esvr_add_spells, otherwise 00807 * we'll try to update spells from fix_object. 00808 */ 00809 fix_object(op); 00810 00811 esrv_send_inventory(op, op); 00812 esrv_send_pickup(pl); 00813 00814 CLEAR_FLAG(op, FLAG_FRIENDLY); 00815 00816 /* can_use_shield is a new flag. However, the can_use.. seems to largely come 00817 * from the class, and not race. I don't see any way to get the class information 00818 * to then update this. I don't think this will actually break anything - anyone 00819 * that can use armour should be able to use a shield. What this may 'break' 00820 * are features new characters get, eg, if someone starts up with a Q, they 00821 * should be able to use a shield. However, old Q's won't get that advantage. 00822 */ 00823 if (QUERY_FLAG(op, FLAG_USE_ARMOUR)) 00824 SET_FLAG(op, FLAG_USE_SHIELD); 00825 00826 /* Rejoin party if needed. */ 00827 if (pl->rejoin_party != party_rejoin_no && party_name != NULL) { 00828 partylist *party; 00829 for (party = get_firstparty(); party; party = party->next) { 00830 if (strcmp(party_name, party->partyname) == 0) 00831 break; 00832 } 00833 if (!party && pl->rejoin_party == party_rejoin_always) { 00834 party = form_party(op, party_name); 00835 snprintf(party->passwd, sizeof(party->passwd), "%s", party_password); 00836 } 00837 if (party && strcmp(party->passwd, party_password) == 0) { 00838 pl->party = party; 00839 snprintf(buf, MAX_BUF, "%s joins party %s", op->name, party->partyname); 00840 send_party_message(op, buf); 00841 } 00842 00843 if (pl->party) 00844 snprintf(buf, MAX_BUF, "Rejoined party %s.", party->partyname); 00845 else 00846 snprintf(buf, MAX_BUF, "Couldn't rejoined party %s: %s.", party_name, party ? "invalid password." : "no such party."); 00847 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00848 buf, NULL); 00849 } 00850 if (party_name) 00851 free(party_name); 00852 00853 return; 00854 }