Crossfire Server, Trunk
login.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2022 the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 
25 #include "define.h"
26 #include "loader.h"
27 #include "output_file.h"
28 #include "spells.h"
29 #include "sproto.h"
30 #include "server.h"
31 
32 static void copy_file(const char *filename, FILE *fpout);
33 
41 void emergency_save(int flag) {
42 #ifndef NO_EMERGENCY_SAVE
43  player *pl;
44 
46  LOG(llevError, "Emergency save: ");
47  for (pl = first_player; pl != NULL; pl = pl->next) {
48  if (!pl->ob) {
49  LOG(llevError, "No name, ignoring this.\n");
50  continue;
51  }
52  LOG(llevError, "%s ", pl->ob->name);
54  "Emergency save...");
55 
56  /* If we are not exiting the game (ie, this is sort of a backup save), then
57  * don't change the location back to the village. Note that there are other
58  * options to have backup saves be done at the starting village
59  */
60  if (!flag) {
61  strcpy(pl->maplevel, first_map_path);
62  if (pl->ob->map != NULL)
63  pl->ob->map = NULL;
64  pl->ob->x = -1;
65  pl->ob->y = -1;
66  }
67  if (!save_player(pl->ob, flag)) {
68  LOG(llevError, "(failed) ");
70  "Emergency save failed, checking score...");
71  }
72  hiscore_check(pl->ob, 1);
73  }
74  LOG(llevError, "\n");
75 #else
76  (void)flag;
77  LOG(llevInfo, "Emergency saves disabled, no save attempted\n");
78 #endif
79 }
80 
88 void delete_character(const char *name) {
89  char buf[MAX_BUF];
90 
91  snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, name);
92  /* this effectively does an rm -rf on the directory */
94 }
95 
111 int verify_player(const char *name, char *password) {
112  char buf[MAX_BUF];
113  FILE *fp;
114  player *pltmp;
115 
116  if (strpbrk(name, "/.\\") != NULL) {
117  LOG(llevError, "Username contains illegal characters: %s\n", name);
118  return 1;
119  }
120 
121  /* Make sure we don't have a character that has just been loaded into the game with this name.
122  * It is possible for that character to not have been saved yet, and yet a character exists
123  * with that name.
124  * Creating a second character of the same name makes the game *really* confused, so don't
125  * let that happen.
126  *
127  * Daniel Hawkins 2021-07-26
128  */
129  for (pltmp = first_player; pltmp != NULL; pltmp = pltmp->next) {
130  if (pltmp->ob->name != NULL && !strcmp(pltmp->ob->name, name)) {
131  // If we find a player already playing with that name, then disallow it.
132  return 2;
133  }
134  }
135 
136  // There is no unsaved player with that name, check the files
137  snprintf(buf, sizeof(buf), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, name, name);
138  if (strlen(buf) >= sizeof(buf)-1) {
139  LOG(llevError, "Username too long: %s\n", name);
140  return 1;
141  }
142 
143  fp = fopen(buf, "r");
144  if (fp == NULL)
145  return 1;
146 
147  /* Read in the file until we find the password line. Our logic could
148  * be a bit better on cleaning up the password from the file, but since
149  * it is written by the program, I think it is fair to assume that the
150  * syntax should be pretty standard.
151  */
152  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
153  if (!strncmp(buf, "password ", 9)) {
154  buf[strlen(buf)-1] = 0; /* remove newline */
155  if (check_password(password, buf+9)) {
156  fclose(fp);
157  return 0;
158  }
159 
160  fclose(fp);
161  return 2;
162  }
163  }
164  LOG(llevDebug, "Could not find a password line in player %s\n", name);
165  fclose(fp);
166  return 1;
167 }
168 
181 int check_name(player *me, const char *name) {
182  if (*name == '\0') {
184  "Your username cannot be blank.");
185  return 0;
186  }
187 
188  if (!playername_ok(name)) {
190  "That name contains illegal characters. Use letters, hyphens and underscores only. Hyphens and underscores are not allowed as the first character.");
191  return 0;
192  }
193  if (strlen(name) >= MAX_NAME) {
195  "That name is too long. (Max length: %d characters)", MAX_NAME);
196  return 0;
197  }
198 
199  return 1;
200 }
201 
209 void destroy_object(object *op) {
210  while (op->inv != NULL)
211  destroy_object(op->inv);
212 
213  if (!QUERY_FLAG(op, FLAG_REMOVED))
214  object_remove(op);
216 }
217 
230 int save_player(object *op, int flag) {
231  FILE *fp;
232  OutputFile of;
233  char filename[MAX_BUF], *tmpfilename;
234  player *pl = op->contr;
235  int i, wiz = QUERY_FLAG(op, FLAG_WIZ);
236  long checksum;
237 #ifdef BACKUP_SAVE_AT_HOME
238  int16_t backup_x, backup_y;
239 #endif
240 
241  PROFILE_BEGIN();
242  if (!op->stats.exp)
243  return 0; /* no experience, no save */
244 
245  flag &= 1;
246 
247  if (!pl->name_changed) {
248  if (!flag) {
250  "Your game is not valid, game not saved.");
251  }
252  return 0;
253  }
254 
255  /* Sanity check - some stuff changes this when player is exiting */
256  if (op->type != PLAYER)
257  return 0;
258 
259  /* Prevent accidental saves if connection is reset after player has
260  * mostly exited.
261  */
262  if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD)
263  return 0;
264 
265  if (flag == 0)
267 
268  /* Update information on this character. Only do it if it is eligible for
269  * for saving.
270  */
271  if (pl->socket->account_name) {
272  account_char_add(pl->socket->account_chars, pl);
273  account_char_save(pl->socket->account_chars);
274  /* Add this character to the account. This really only comes up
275  * for new characters, at which time we want to wait until save -
276  * otherwise there is a good chance that character will be
277  * terminated.
278  */
279  if (!account_get_account_for_char(pl->ob->name))
280  account_link(pl->socket->account_name, pl->ob->name);
281  }
282 
283 
284  snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name);
286  fp = tempnam_secure(settings.tmpdir, NULL, &tmpfilename);
287  if (!fp) {
289  "Can't get secure temporary file for save.");
290  LOG(llevDebug, "Can't get secure temporary file for save.\n");
291  return 0;
292  }
293 
294  fprintf(fp, "password %s\n", pl->password);
295  if (settings.set_title == TRUE)
297  fprintf(fp, "title %s\n", player_get_own_title(pl));
298 
299  fprintf(fp, "gen_hp %d\n", pl->gen_hp);
300  fprintf(fp, "gen_sp %d\n", pl->gen_sp);
301  fprintf(fp, "gen_grace %d\n", pl->gen_grace);
302  fprintf(fp, "listening %d\n", pl->listening);
303  fprintf(fp, "shoottype %d\n", pl->shoottype);
304  fprintf(fp, "bowtype %d\n", pl->bowtype);
305  fprintf(fp, "petmode %d\n", pl->petmode);
306  fprintf(fp, "peaceful %d\n", pl->peaceful);
307  fprintf(fp, "no_shout %d\n", pl->no_shout);
308  fprintf(fp, "digestion %d\n", pl->digestion);
309  fprintf(fp, "pickup %u\n", pl->mode);
310  fprintf(fp, "partial_commands %u\n", pl->partial_commands);
311  /*
312  * outputs_sync and outputs_count are now unused in favor of the facility
313  * being supported on the client instead of in the server, but for now,
314  * set sane values in case an older server is run on a new player file.
315  * Once the server is officially 2.x, this should likely be removed.
316  */
317  fprintf(fp, "outputs_sync %d\n", 16);
318  fprintf(fp, "outputs_count %d\n", 1);
319  /* Match the enumerations but in string form */
320  fprintf(fp, "usekeys %s\n", pl->usekeys == key_inventory ? "key_inventory" : (pl->usekeys == keyrings ? "keyrings" : "containers"));
321  /* Match the enumerations but in string form */
322  fprintf(fp, "unapply %s\n", pl->unapply == unapply_nochoice ? "unapply_nochoice" : (pl->unapply == unapply_never ? "unapply_never" : "unapply_always"));
323  if (pl->unarmed_skill) fprintf(fp, "unarmed_skill %s\n", pl->unarmed_skill);
324 
325 #ifdef BACKUP_SAVE_AT_HOME
326  if (op->map != NULL && flag == 0)
327 #else
328  if (op->map != NULL)
329 #endif
330  fprintf(fp, "map %s\n", op->map->path);
331  else
332  fprintf(fp, "map %s\n", settings.emergency_mapname);
333 
334  fprintf(fp, "savebed_map %s\n", pl->savebed_map);
335  fprintf(fp, "bed_x %d\nbed_y %d\n", pl->bed_x, pl->bed_y);
336  fprintf(fp, "Str %d\n", pl->orig_stats.Str);
337  fprintf(fp, "Dex %d\n", pl->orig_stats.Dex);
338  fprintf(fp, "Con %d\n", pl->orig_stats.Con);
339  fprintf(fp, "Int %d\n", pl->orig_stats.Int);
340  fprintf(fp, "Pow %d\n", pl->orig_stats.Pow);
341  fprintf(fp, "Wis %d\n", pl->orig_stats.Wis);
342  fprintf(fp, "Cha %d\n", pl->orig_stats.Cha);
343 
344  fprintf(fp, "lev_array %d\n", MIN(op->level, 10));
345  for (i = 1; i <= MIN(op->level, 10) && i <= 10; i++) {
346  fprintf(fp, "%d\n", pl->levhp[i]);
347  fprintf(fp, "%d\n", pl->levsp[i]);
348  fprintf(fp, "%d\n", pl->levgrace[i]);
349  }
350  fprintf(fp, "party_rejoin_mode %d\n", pl->rejoin_party);
351  if (pl->party != NULL) {
352  fprintf(fp, "party_rejoin_name %s\n", pl->party->partyname);
353  fprintf(fp, "party_rejoin_password %s\n", party_get_password(pl->party));
354  }
355  fprintf(fp, "language %s\n", i18n_get_language_code(pl->language));
356  fprintf(fp, "ticks_played %u\n", pl->ticks_played);
357  fprintf(fp, "endplst\n");
358 
361 #ifdef BACKUP_SAVE_AT_HOME
362  if (flag) {
363  backup_x = op->x;
364  backup_y = op->y;
365  op->x = -1;
366  op->y = -1;
367  }
368  /* Save objects, but not unpaid objects. Don't remove objects from
369  * inventory.
370  */
372  if (flag) {
373  op->x = backup_x;
374  op->y = backup_y;
375  }
376 #else
377  i = save_object(fp, op, SAVE_FLAG_SAVE_UNPAID|SAVE_FLAG_NO_REMOVE); /* don't check and don't remove */
378 #endif
379 
380  if (wiz)
381  SET_FLAG(op, FLAG_WIZ);
382 
383  if (fclose(fp) != 0 || i != SAVE_ERROR_OK) { /* make sure the write succeeded */
385  "Can't save character!");
386  draw_ext_info_format(NDI_ALL_DMS|NDI_RED, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE, "Save failure for player %s!", op->name);
387  unlink(tmpfilename);
388  free(tmpfilename);
389  return 0;
390  }
391 
393 
394  if (!flag) {
395  // Clear last_skill_ob before removing inventory. This prevents us
396  // from accessing removed skill objects during cleanup.
397  for (int i = 0; i < MAX_SKILLS; i++) {
398  op->contr->last_skill_ob[i] = NULL;
399  }
400 
401  while (op->inv != NULL)
402  destroy_object(op->inv);
403 
404  /* destroying objects will most likely destroy the pointer
405  * in op->contr->ranges[], so clear the range to a safe value.
406  */
407  op->contr->shoottype = range_none;
408  }
409 
410  checksum = 0;
411  fp = of_open(&of, filename);
412  if (fp == NULL) {
414  "Can't open file for save.");
415  unlink(tmpfilename);
416  free(tmpfilename);
417  return 0;
418  }
419  fprintf(fp, "checksum %lx\n", checksum);
420  copy_file(tmpfilename, fp);
421  unlink(tmpfilename);
422  free(tmpfilename);
423  if (!of_close(&of)) {
425  "Can't close file for save.");
426  return 0;
427  }
428 
429  if (!flag)
431 
432  if (chmod(filename, SAVE_MODE) != 0) {
433  LOG(llevError, "Could not set permissions on '%s'\n", filename);
434  }
435 
436  /* if this is the first player save, quest or knowledge states can be unsaved */
437  if (!op->contr->has_directory) {
438  op->contr->has_directory = 1;
440  quest_first_player_save(op->contr);
441  }
442 
443  PROFILE_END(diff, LOG(llevDebug, "Saved player %s (%ld ms)\n", op->name, diff/1000));
444  return 1;
445 }
446 
455 static void copy_file(const char *filename, FILE *fpout) {
456  FILE *fp;
457  char buf[MAX_BUF];
458 
459  fp = fopen(filename, "r");
460  if (fp == NULL) {
461  LOG(llevError, "copy_file failed to open \"%s\", player file(s) may be corrupt.\n", filename);
462  return;
463  }
464  while (fgets(buf, MAX_BUF, fp) != NULL)
465  fputs(buf, fpout);
466  fclose(fp);
467 }
468 
476 static void wrong_password(object *op) {
478  "\nA character with this name already exists. "
479  "Please choose another name, or make sure you entered your "
480  "password correctly.\n");
481 
482  FREE_AND_COPY(op->name, "noname");
483  FREE_AND_COPY(op->name_pl, "noname");
484 
485  op->contr->socket->password_fails++;
486  if (op->contr->socket->password_fails >= MAX_PASSWORD_FAILURES) {
488  "You gave an incorrect password too many times, "
489  "you will now be dropped from the server.");
490 
491  LOG(llevInfo, "A player connecting from %s has been dropped for password failure\n",
492  op->contr->socket->host);
493 
494  op->contr->socket->status = Ns_Dead; /* the socket loop should handle the rest for us */
495  } else
496  get_name(op);
497 }
498 
511 void check_login(object *op, const char *password) {
512  FILE *fp;
513  char filename[MAX_BUF];
514  char buf[MAX_BUF], bufall[MAX_BUF];
515  int i, value;
516  uint32_t uvalue;
517  player *pl = op->contr, *pltmp;
518  int correct = 0;
519  time_t elapsed_save_time = 0;
520  struct stat statbuf;
521  char *party_name = NULL, party_password[9];
522 
523  strcpy(pl->maplevel, first_map_path);
524  party_password[0] = 0;
525 
526  /* Check if this matches a connected player, and if yes disconnect old / connect new. */
527  for (pltmp = first_player; pltmp != NULL; pltmp = pltmp->next) {
528  if (pltmp != pl && pltmp->ob->name != NULL && !strcmp(pltmp->ob->name, op->name)) {
529  if (!password || check_password(password, pltmp->password)) {
530  /* We could try and be more clever and re-assign the existing
531  * object to the new player, etc. However, I'm concerned that
532  * there may be a lot of other state that still needs to be sent
533  * in that case (we can't make any assumptions on what the
534  * client knows, as maybe the client crashed), so treating it
535  * as just a normal login is the safest and easiest thing to do.
536  */
537 
538  pltmp->socket->status = Ns_Dead;
539 
540  save_player(pltmp->ob, 0);
541  leave(pltmp, 1);
542  final_free_player(pltmp);
543  break;
544  }
545  if (password) {
547  return;
548  }
549  }
550  }
551 
552  snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, op->name, op->name);
553 
554  /* If no file, must be a new player, so lets get confirmation of
555  * the password. Return control to the higher level dispatch,
556  * since the rest of this just deals with loading of the file.
557  */
558  fp = fopen(filename, "r");
559  if (fp == NULL) {
561  return;
562  }
563  if (fstat(fileno(fp), &statbuf)) {
564  LOG(llevError, "Unable to stat %s?\n", filename);
565  elapsed_save_time = 0;
566  } else {
567  elapsed_save_time = time(NULL)-statbuf.st_mtime;
568  if (elapsed_save_time < 0) {
569  LOG(llevError, "Player file %s was saved in the future? (%ld time)\n", filename, (long)elapsed_save_time);
570  elapsed_save_time = 0;
571  }
572  }
573 
574  if (fgets(bufall, MAX_BUF, fp) != NULL) {
575  if (!strncmp(bufall, "checksum ", 9)) {
576  if ( fgets(bufall, MAX_BUF, fp) == NULL ) {
577  bufall[0]=0; /* should never happen */
578  }
579  }
580  if (sscanf(bufall, "password %s\n", buf)) {
581  /* New password scheme: */
582  correct = password && check_password(password, buf);
583  if (!password) {
584  /* We want to preserve the password. Normally,
585  * pl->password is filled in when user enters
586  * data in the password prompt, but with new login,
587  * there is no password prompt.
588  */
589  strncpy(pl->password, buf, 15);
590  pl->password[15] = 0;
591  }
592  }
593  /* Old password mode removed - I have no idea what it
594  * was, and the current password mechanism has been used
595  * for at least several years.
596  */
597  }
598  if (!correct && password) {
600  fclose(fp);
601  return;
602  }
603 
604 #ifdef SAVE_INTERVAL
605  pl->last_save_time = time(NULL);
606 #endif /* SAVE_INTERVAL */
607  pl->party = NULL;
608  if (settings.search_items == TRUE)
609  pl->search_str[0] = '\0';
610  pl->name_changed = 1;
611  pl->orig_stats.Str = 0;
612  pl->orig_stats.Dex = 0;
613  pl->orig_stats.Con = 0;
614  pl->orig_stats.Int = 0;
615  pl->orig_stats.Pow = 0;
616  pl->orig_stats.Wis = 0;
617  pl->orig_stats.Cha = 0;
618  strcpy(pl->savebed_map, first_map_path);
619  pl->bed_x = 0,
620  pl->bed_y = 0;
621  pl->spellparam[0] = '\0';
622 
623  /* Loop through the file, loading the rest of the values */
624  while (fgets(bufall, MAX_BUF, fp) != NULL) {
625  char *val_string, *p;
626 
627  sscanf(bufall, "%s %d\n", buf, &value);
628 
629  val_string = bufall + strlen(buf) +1;
630  p = strchr(val_string, '\n');
631  if (p != NULL)
632  *p = '\0';
633 
634  /* uvalue is an unsigned value. Since at least a
635  * couple different things want an usigned value, cleaner
636  * to just do it once here vs everyplace it may be needed.
637  */
638 
639  uvalue = strtoul(val_string, (char **)NULL, 10);
640 
641  if (!strcmp(buf, "endplst"))
642  break;
643  if (!strcmp(buf, "title") && settings.set_title == TRUE)
644  player_set_own_title(pl, val_string);
645  else if (!strcmp(buf, "unarmed_skill"))
646  pl->unarmed_skill = add_string(val_string);
647  else if (!strcmp(buf, "explore"))
648  ; /* ignore: explore mode has been removed */
649  else if (!strcmp(buf, "gen_hp"))
650  pl->gen_hp = value;
651  else if (!strcmp(buf, "shoottype"))
652  pl->shoottype = (rangetype)value;
653  else if (!strcmp(buf, "bowtype"))
654  pl->bowtype = (bowtype_t)value;
655  else if (!strcmp(buf, "petmode"))
656  pl->petmode = (petmode_t)value;
657  else if (!strcmp(buf, "gen_sp"))
658  pl->gen_sp = value;
659  else if (!strcmp(buf, "gen_grace"))
660  pl->gen_grace = value;
661  else if (!strcmp(buf, "listening"))
662  pl->listening = value;
663  else if (!strcmp(buf, "peaceful"))
664  pl->peaceful = value;
665  else if (!strcmp(buf, "no_shout"))
666  pl->no_shout = value;
667  else if (!strcmp(buf, "digestion"))
668  pl->digestion = value;
669  else if (!strcmp(buf, "pickup")) {
670  pl->mode = uvalue;
671  } else if (!strcmp(buf, "partial_commands")) {
672  pl->partial_commands = uvalue;
673  }
674  else if (!strcmp(buf, "map"))
675  strlcpy(pl->maplevel, val_string, sizeof(pl->maplevel));
676  else if (!strcmp(buf, "savebed_map"))
677  strlcpy(pl->savebed_map, val_string, sizeof(pl->savebed_map));
678  else if (!strcmp(buf, "bed_x"))
679  pl->bed_x = value;
680  else if (!strcmp(buf, "bed_y"))
681  pl->bed_y = value;
682  else if (!strcmp(buf, "Str"))
683  pl->orig_stats.Str = value;
684  else if (!strcmp(buf, "Dex"))
685  pl->orig_stats.Dex = value;
686  else if (!strcmp(buf, "Con"))
687  pl->orig_stats.Con = value;
688  else if (!strcmp(buf, "Int"))
689  pl->orig_stats.Int = value;
690  else if (!strcmp(buf, "Pow"))
691  pl->orig_stats.Pow = value;
692  else if (!strcmp(buf, "Wis"))
693  pl->orig_stats.Wis = value;
694  else if (!strcmp(buf, "Cha"))
695  pl->orig_stats.Cha = value;
696  else if (!strcmp(buf, "usekeys")) {
697  if (!strcmp(val_string, "key_inventory"))
698  pl->usekeys = key_inventory;
699  else if (!strcmp(val_string, "keyrings"))
700  pl->usekeys = keyrings;
701  else if (!strcmp(val_string, "containers"))
702  pl->usekeys = containers;
703  else
704  LOG(llevDebug, "load_player: got unknown usekeys type: %s\n", val_string);
705  } else if (!strcmp(buf, "unapply")) {
706  if (!strcmp(val_string, "unapply_nochoice"))
707  pl->unapply = unapply_nochoice;
708  else if (!strcmp(val_string, "unapply_never"))
709  pl->unapply = unapply_never;
710  else if (!strcmp(val_string, "unapply_always"))
711  pl->unapply = unapply_always;
712  else
713  LOG(llevDebug, "load_player: got unknown unapply type: %s\n", val_string);
714  } else if (!strcmp(buf, "lev_array")) {
715  for (i = 1; i <= value; i++) {
716  int j;
717  int count=0;
718 
719  count = fscanf(fp, "%d\n", &j);
720  if ( !count ) j=0; // sanity; should never happen
721  if (j < 3)
722  j = 3;
723  else if (j > 9)
724  j = 9;
725  pl->levhp[i] = j;
726  count = fscanf(fp, "%d\n", &j);
727  if ( !count ) j=0; // sanity; should never happen
728  if (j < 2)
729  j = 2;
730  else if (j > 6)
731  j = 6;
732  pl->levsp[i] = j;
733  count = fscanf(fp, "%d\n", &j);
734  if ( !count ) j=0; // sanity; should never happen
735  if (j < 1)
736  j = 1;
737  else if (j > 3)
738  j = 3;
739  pl->levgrace[i] = j;
740  }
741  } else if (!strcmp(buf, "party_rejoin_mode"))
742  pl->rejoin_party = (enum party_rejoin_mode)value;
743  else if (!strcmp(buf, "party_rejoin_name"))
744  party_name = strdup_local(val_string);
745  else if (!strcmp(buf, "party_rejoin_password")) {
746  strncpy(party_password, val_string, sizeof(party_password));
747  party_password[sizeof(party_password) - 1] = 0;
748  } else if (!strcmp(buf, "language")) {
749  pl->language = i18n_get_language_by_code(val_string);
750  }
751  else if (!strcmp(buf, "ticks_played")) {
752  pl->ticks_played = uvalue;
753  }
754  } /* End of loop loading the character file */
755 
756  /* on first login via account, this player does not exist anyplace -
757  * so don't remove them.
758  */
759  if (!QUERY_FLAG(op, FLAG_REMOVED))
760  object_remove(op);
761  op->speed = 0;
763  /*FIXME dangerous call, object_reset() should be used to init freshly allocated obj struct!*/
764  object_reset(op);
765  op->contr = pl;
766  pl->ob = op;
767  /* this loads the standard objects values. */
768  PROFILE_BEGIN();
769  load_object(fp, op, LO_NEWFILE, 0, false);
770  PROFILE_END(diff, LOG(llevDebug, "Loaded player file for %s (%ld ms)\n", op->name, diff/1000));
771  fclose(fp);
772 
774 
775  LOG(llevInfo, "login: %s from %s\n", op->name, op->contr->socket->host);
776  strncpy(pl->title, op->arch->clone.name, sizeof(pl->title)-1);
777  pl->title[sizeof(pl->title)-1] = '\0';
778 
779  /* If the map where the person was last saved does not exist,
780  * restart them on their home-savebed. This is good for when
781  * maps change between versions
782  * First, we check for partial path, then check to see if the full
783  * path (for unique player maps)
784  */
785  if (has_been_loaded(pl->maplevel) == NULL
786  && check_path(pl->maplevel, 1) == -1
787  && check_path(pl->maplevel, 0) == -1) {
788  strcpy(pl->maplevel, pl->savebed_map);
789  op->x = pl->bed_x,
790  op->y = pl->bed_y;
791  /* if the map was a shop, the player can have unpaid items, remove them. */
792  remove_unpaid_objects(op, NULL, 1);
793  }
794 
795  /* If player saved beyond some time ago, and the feature is
796  * enabled, put the player back on his savebed map.
797  */
798  if ((settings.reset_loc_time > 0) && (elapsed_save_time > settings.reset_loc_time)) {
799  strcpy(pl->maplevel, pl->savebed_map);
800  op->x = pl->bed_x, op->y = pl->bed_y;
801  /* if the map was a shop, the player can have unpaid items, remove them. */
802  remove_unpaid_objects(op, NULL, 1);
803  }
804 
805  /* make sure he's a player--needed because of class change. */
806  op->type = PLAYER;
808 
809  pl->name_changed = 1;
811 #ifdef AUTOSAVE
812  pl->last_save_tick = pticks;
813 #endif
814  op->carrying = object_sum_weight(op);
815 
817 
818  if (!legal_range(op, op->contr->shoottype))
819  op->contr->shoottype = range_none;
820 
821  /* if it's a dragon player, set the correct title here */
822  if (is_dragon_pl(op) && op->inv != NULL) {
823  object *abil, *skin;
824 
825  abil = object_find_by_type_and_arch_name(op, FORCE, "dragon_ability_force");
826  skin = object_find_by_type_and_arch_name(op, FORCE, "dragon_skin_force");
827  set_dragon_name(op, abil, skin);
828  }
829 
831  "Welcome Back!");
834  "%s has entered the game.",
835  pl->ob->name);
837 
838  events_execute_global_event(EVENT_LOGIN, pl, pl->socket->host);
839  op->contr->socket->update_look = 1;
840  /* If the player should be dead, call kill_player for them
841  * Only check for hp - if player lacks food, let the normal
842  * logic for that to take place. If player is permanently
843  * dead, and not using permadeath mode, the kill_player will
844  * set the play_again flag, so return.
845  */
846  if (op->stats.hp < 0) {
848  "Your character was dead last time you played.");
849  kill_player(op, NULL);
850  if (pl->state != ST_PLAYING)
851  {
852  // Prevent memory leak from strdup-ed party_name.
853  if (party_name)
854  free(party_name);
855  return;
856  }
857  }
858 
859  /* Do this after checking for death - no reason sucking up bandwidth if
860  * the data isn't needed.
861  */
862  esrv_new_player(op->contr, op->weight+op->carrying);
863  /* Need to do these after esvr_new_player, as once the client
864  * sees that, it wipes any info it has about the player.
865  */
866  esrv_add_spells(op->contr, NULL);
867 
868  /* Need to call fix_object now - program modified so that it is not
869  * called during the load process (FLAG_NO_FIX_PLAYER set when
870  * saved)
871  * Moved ahead of the esrv functions, so proper weights will be
872  * sent to the client. Needs to be after esvr_add_spells, otherwise
873  * we'll try to update spells from fix_object.
874  */
875  fix_object(op);
876 
877  pl->has_directory = 1;
878 
883 
885 
886  /* can_use_shield is a new flag. However, the can_use.. seems to largely come
887  * from the class, and not race. I don't see any way to get the class information
888  * to then update this. I don't think this will actually break anything - anyone
889  * that can use armour should be able to use a shield. What this may 'break'
890  * are features new characters get, eg, if someone starts up with a Q, they
891  * should be able to use a shield. However, old Q's won't get that advantage.
892  */
895 
896  /* Rejoin party if needed. */
897  if (pl->rejoin_party != party_rejoin_no && party_name != NULL) {
898  partylist *party;
899 
900  party = party_find(party_name);
901  if (!party && pl->rejoin_party == party_rejoin_always) {
902  party = party_form(op, party_name);
903  if (party)
904  party_set_password(party, party_password);
905  }
906  if (party && !pl->party && party_confirm_password(party, party_password)) {
907  party_join(op, party);
908  }
909 
910  if (pl->party)
911  snprintf(buf, MAX_BUF, "Rejoined party %s.", party->partyname);
912  else
913  snprintf(buf, MAX_BUF, "Couldn't rejoin party %s: %s.", party_name, party ? "invalid password." : "no such party.");
915  buf);
916  }
917  free(party_name);
918 }
party_rejoin_no
@ party_rejoin_no
Definition: player.h:99
PLAYER
@ PLAYER
Definition: object.h:112
player::next
player * next
Definition: player.h:106
output_file.h
global.h
first_player
player * first_player
Definition: init.cpp:106
settings
struct Settings settings
Definition: init.cpp:139
SAVE_MODE
#define SAVE_MODE
Definition: config.h:563
pets_terminate_all
void pets_terminate_all(object *owner)
Definition: pets.cpp:225
MSG_TYPE_COMMAND_SUCCESS
#define MSG_TYPE_COMMAND_SUCCESS
Definition: newclient.h:519
llevError
@ llevError
Definition: logger.h:11
MSG_TYPE_ADMIN_PLAYER
#define MSG_TYPE_ADMIN_PLAYER
Definition: newclient.h:485
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
player
Definition: player.h:105
load_object
int load_object(FILE *fp, object *op, int bufstate, int map_flags)
Definition: loader.c:5205
strdup_local
#define strdup_local
Definition: compat.h:29
SAVE_FLAG_NO_REMOVE
#define SAVE_FLAG_NO_REMOVE
Definition: map.h:108
first_map_path
char first_map_path[MAX_BUF]
Definition: init.cpp:120
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
login_check_shutdown
void login_check_shutdown(object *const op)
Definition: server.cpp:1490
has_been_loaded
mapstruct * has_been_loaded(const char *name)
Definition: map.cpp:78
Settings::search_items
uint8_t search_items
Definition: global.h:267
esrv_new_player
void esrv_new_player(player *pl, uint32_t weight)
Definition: request.cpp:991
bowtype_t
bowtype_t
Definition: player.h:41
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Definition: item.cpp:316
party_find
partylist * party_find(const char *partyname)
Definition: party.cpp:148
player::ob
object * ob
Definition: player.h:177
copy_file
static void copy_file(const char *filename, FILE *fpout)
Definition: login.cpp:455
esrv_send_pickup
void esrv_send_pickup(player *pl)
Definition: request.cpp:1795
Settings::localdir
const char * localdir
Definition: global.h:249
player_set_own_title
void player_set_own_title(struct player *pl, const char *title)
Definition: player.cpp:272
range_none
@ range_none
Definition: player.h:30
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
checksum
static unsigned checksum(const mtar_raw_header_t *rh)
Definition: microtar.cpp:49
MIN
#define MIN(x, y)
Definition: compat.h:21
object_reset
void object_reset(object *op)
Definition: object.cpp:934
NDI_ALL_DMS
#define NDI_ALL_DMS
Definition: newclient.h:253
Settings::tmpdir
const char * tmpdir
Definition: global.h:255
player_has_own_title
int player_has_own_title(const struct player *pl)
Definition: player.cpp:247
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
PROFILE_BEGIN
#define PROFILE_BEGIN(expr)
Definition: global.h:372
NDI_RED
#define NDI_RED
Definition: newclient.h:234
save_player
int save_player(object *op, int flag)
Definition: login.cpp:230
account_link
int account_link(const char *account_name, const char *player_name)
Definition: account.cpp:445
unapply_always
@ unapply_always
Definition: player.h:78
LO_NEWFILE
#define LO_NEWFILE
Definition: loader.h:17
npc_dialog.filename
filename
Definition: npc_dialog.py:99
esrv_add_spells
void esrv_add_spells(player *pl, object *spell)
Definition: request.cpp:1909
partylist
Definition: party.h:10
pticks
uint32_t pticks
Definition: time.cpp:47
buf
StringBuffer * buf
Definition: readable.cpp:1552
hiscore_check
void hiscore_check(object *op, int quiet)
Definition: hiscore.cpp:348
party_rejoin_always
@ party_rejoin_always
Definition: player.h:101
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
party_join
void party_join(object *op, partylist *party)
Definition: party.cpp:85
Ns_Dead
@ Ns_Dead
Definition: newserver.h:67
remove_directory
void remove_directory(const char *path)
Definition: porting.cpp:117
PROFILE_END
#define PROFILE_END(var, expr)
Definition: global.h:377
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
EVENT_LOGIN
#define EVENT_LOGIN
Definition: events.h:44
final_free_player
void final_free_player(player *pl)
Definition: init.cpp:455
Settings::set_title
uint8_t set_title
Definition: global.h:265
party_confirm_password
int party_confirm_password(const partylist *party, const char *password)
Definition: party.cpp:259
playername_ok
int playername_ok(const char *cp)
Definition: player.cpp:257
i18n_get_language_code
sstring i18n_get_language_code(language_t language)
Definition: languages.cpp:85
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
Definition: account.cpp:580
remove_unpaid_objects
void remove_unpaid_objects(object *op, object *env, int free_items)
Definition: player.cpp:3207
of
a copper bar weighs and has a value of
Definition: ore.txt:3
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
wrong_password
static void wrong_password(object *op)
Definition: login.cpp:476
legal_range
int legal_range(object *op, int r)
Definition: c_range.cpp:247
petmode_t
petmode_t
Definition: player.h:57
enter_player_maplevel
void enter_player_maplevel(object *op)
Definition: server.cpp:689
trying_emergency_save
long trying_emergency_save
Definition: init.cpp:111
verify_player
int verify_player(const char *name, char *password)
Definition: login.cpp:111
delete_character
void delete_character(const char *name)
Definition: login.cpp:88
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1344
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
party_set_password
void party_set_password(partylist *party, const char *password)
Definition: party.cpp:244
Settings::reset_loc_time
int reset_loc_time
Definition: global.h:264
FLAG_USE_SHIELD
#define FLAG_USE_SHIELD
Definition: define.h:237
leave
void leave(player *pl, int draw_exit)
Definition: server.cpp:1305
MAX_NAME
#define MAX_NAME
Definition: define.h:41
disinfect.count
int count
Definition: disinfect.py:7
rangetype
rangetype
Definition: player.h:28
Settings::playerdir
const char * playerdir
Definition: global.h:250
sproto.h
MAX_SKILLS
#define MAX_SKILLS
Definition: skills.h:70
partylist::partyname
char * partyname
Definition: party.h:14
party_form
partylist * party_form(object *op, const char *partyname)
Definition: party.cpp:40
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
key_inventory
@ key_inventory
Definition: player.h:66
MAX_BUF
#define MAX_BUF
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
Definition: newclient.h:488
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.cpp:222
quest_first_player_save
void quest_first_player_save(player *pl)
Definition: quest.cpp:967
ST_PLAYING
#define ST_PLAYING
Definition: define.h:541
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
llevInfo
@ llevInfo
Definition: logger.h:12
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
destroy_object
void destroy_object(object *op)
Definition: login.cpp:209
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
spells.h
object::name
sstring name
Definition: object.h:319
party_get_password
const char * party_get_password(const partylist *party)
Definition: party.cpp:232
i18n_get_language_by_code
language_t i18n_get_language_by_code(const char *code)
Definition: languages.cpp:73
is_dragon_pl
int is_dragon_pl(const object *op)
Definition: player.cpp:122
me
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to me
Definition: protocol.txt:56
unapply_nochoice
@ unapply_nochoice
Definition: player.h:76
FLAG_USE_ARMOUR
#define FLAG_USE_ARMOUR
Definition: define.h:295
check_name
int check_name(player *me, const char *name)
Definition: login.cpp:181
give.op
op
Definition: give.py:33
NDI_ALL
#define NDI_ALL
Definition: newclient.h:252
autojail.value
value
Definition: autojail.py:6
MAX_PASSWORD_FAILURES
#define MAX_PASSWORD_FAILURES
Definition: newserver.h:84
define.h
FLAG_NO_FIX_PLAYER
#define FLAG_NO_FIX_PLAYER
Definition: define.h:277
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
tempnam_secure
FILE * tempnam_secure(const char *dir, const char *pfx, char **filename)
Definition: porting.cpp:71
OutputFile
Definition: output_file.h:41
check_login
void check_login(object *op, const char *password)
Definition: login.cpp:511
NDI_DK_ORANGE
#define NDI_DK_ORANGE
Definition: newclient.h:237
object_find_by_type_and_arch_name
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Definition: object.cpp:4268
containers
@ containers
Definition: player.h:68
keyrings
@ keyrings
Definition: player.h:67
account_char_add
void account_char_add(Account_Chars *chars, player *pl)
Definition: account_char.cpp:207
MSG_TYPE_ADMIN_LOGIN
#define MSG_TYPE_ADMIN_LOGIN
Definition: newclient.h:489
SAVE_ERROR_OK
#define SAVE_ERROR_OK
Definition: map.h:140
loader.h
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Definition: define.h:549
object_remove
void object_remove(object *op)
Definition: object.cpp:1828
player_set_state
void player_set_state(player *pl, uint8_t state)
Definition: player.cpp:4475
object_sum_weight
signed long object_sum_weight(object *op)
Definition: object.cpp:568
check_password
bool check_password(const char *typed, const char *crypted)
Definition: server.cpp:114
make_path_to_file
void make_path_to_file(const char *filename)
Definition: porting.cpp:164
emergency_save
void emergency_save(int flag)
Definition: login.cpp:41
knowledge_send_known
void knowledge_send_known(player *pl)
Definition: knowledge.cpp:1400
unapply_never
@ unapply_never
Definition: player.h:77
server.h
player_get_own_title
const char * player_get_own_title(const struct player *pl)
Definition: player.cpp:260
confirm_password
void confirm_password(object *op)
Definition: player.cpp:1006
TRUE
#define TRUE
Definition: compat.h:11
link_player_skills
void link_player_skills(object *op)
Definition: player.cpp:287
set_dragon_name
void set_dragon_name(object *pl, const object *abil, const object *skin)
Definition: living.cpp:1677
party_rejoin_mode
party_rejoin_mode
Definition: player.h:98
account_char_save
void account_char_save(Account_Chars *chars)
Definition: account_char.cpp:158
SAVE_FLAG_SAVE_UNPAID
#define SAVE_FLAG_SAVE_UNPAID
Definition: map.h:107
save_object
int save_object(FILE *fp, object *op, int flag)
Definition: object.cpp:5381
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
kill_player
void kill_player(object *op, const object *killer)
Definition: player.cpp:3488
if
if(!(yy_init))
Definition: loader.c:2626
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
FORCE
@ FORCE
Definition: object.h:229
check_path
int check_path(const char *name, int prepend_dir)
Definition: map.cpp:200
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Definition: events.cpp:27
llevDebug
@ llevDebug
Definition: logger.h:13
quest_send_initial_states
void quest_send_initial_states(player *pl)
Definition: quest.cpp:909
Settings::emergency_mapname
char * emergency_mapname
Definition: global.h:299
get_name
void get_name(object *op)
Definition: player.cpp:881
knowledge_first_player_save
void knowledge_first_player_save(player *pl)
Definition: knowledge.cpp:1420