Crossfire Server, Branch 1.12  R12190
login.c
Go to the documentation of this file.
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 }