Crossfire Server, Trunk
account_char.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and 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 
46 #include "global.h"
47 
48 #include <assert.h>
49 #include <errno.h>
50 #include <stdlib.h>
51 #include <string.h>
52 
53 #include "account_char.h"
54 #include "object.h"
55 #include "output_file.h"
56 #include "sproto.h"
57 #include "player.h"
58 
60 #define NUM_ACCOUNT_CHAR_FIELDS 8
61 
69 #define ACCOUNT_DIR "account"
70 
71 static Account_Chars **chars_loaded = NULL;
72 static size_t chars_loaded_count = 0;
73 static size_t chars_loaded_allocated = 0;
80  char fname[MAX_BUF], buf[VERY_BIG_BUF];
81  FILE *fp;
82  Account_Char *ac, *last = NULL;
83 
84  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, chars->account_name);
85  fp = fopen(fname, "r");
86  if (!fp) {
87  /* This may not in fact be a critical error - for a new account, there
88  * may not be any data associated with it.
89  */
90  LOG(llevInfo, "Warning: Unable to open %s: %s\n", fname, strerror(errno));
91  return;
92  }
93  while (fgets(buf, VERY_BIG_BUF, fp)) {
94  char *tmp[NUM_ACCOUNT_CHAR_FIELDS], *cp;
95 
96  /* Ignore any comment lines */
97  if (buf[0] == '#') continue;
98 
99  /* remove newline */
100  cp = strchr(buf, '\n');
101  if (cp) *cp = '\0';
102 
104  if (!tmp[7]) {
105  LOG(llevInfo, "Outdated entry in %s: %s\n", fname, buf);
106  tmp[7] = (char *) add_string("0");
107  } else {
108  LOG(llevError, "Corrupt entry in %s: %s\n", fname, buf);
109  continue;
110  }
111  }
112  ac = static_cast<Account_Char *>(malloc(sizeof (Account_Char)));
113  ac->name = add_string(tmp[0]);
114  ac->character_class = add_string(tmp[1]);
115  ac->race = add_string(tmp[2]);
116  ac->level = strtoul(tmp[3], (char**) NULL, 10);
117  ac->face = add_string(tmp[4]);
118  ac->party = add_string(tmp[5]);
119  ac->map = add_string(tmp[6]);
120  ac->isDead = strtoul(tmp[7], (char**) NULL, 10);
121 
122  ac->next = NULL;
123 
124  /* We tack on to the end of the list - in this way,
125  * the order of the file remains the same.
126  */
127  if (last)
128  last->next = ac;
129  else
130  chars->chars = ac;
131  last = ac;
132 
133  }
134  fclose(fp);
135 }
136 
147 Account_Chars *account_char_load(const char *account_name) {
148  for (size_t loaded = 0; loaded < chars_loaded_count; loaded++) {
149  if (strcmp(chars_loaded[loaded]->account_name, account_name) == 0) {
150  chars_loaded[loaded]->ref_count++;
151  return chars_loaded[loaded];
152  }
153  }
154 
155  Account_Chars *ac = static_cast<Account_Chars *>(calloc(1, sizeof(*ac)));
156  ac->ref_count++;
157  ac->account_name = add_string(account_name);
159 
162  chars_loaded = static_cast<Account_Chars **>(realloc(chars_loaded, chars_loaded_allocated * sizeof(chars_loaded[0])));
163  if (!chars_loaded) {
164  LOG(llevError, "Out of memory allocating %zu account chars\n", chars_loaded_allocated);
166  }
167  }
170 
171  return ac;
172 }
173 
180  char fname[MAX_BUF];
181  FILE *fp;
182  OutputFile of;
183  Account_Char *ac;
184 
185  if (!chars) {
186  return;
187  }
188 
189  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, chars->account_name);
190 
191  /* It is certanly possibly that all characters for an account have
192  * been removed/deleted - in that case, we just want to remove this
193  * file.
194  */
195  if (chars->chars == NULL) {
196  unlink(fname);
197  return;
198  }
199 
200  fp = of_open(&of, fname);
201  if (fp == NULL)
202  return;
203 
204  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
205  "# only read when the server starts, and any changes will be overwritten when\n"
206  "# the server exits.\n");
207  for (ac = chars->chars; ac; ac = ac->next) {
208  fprintf(fp, "%s:%s:%s:%d:%s:%s:%s:%d\n",
209  ac->name, ac->character_class, ac->race, ac->level,
210  ac->face, ac->party, ac->map, ac->isDead);
211  }
212  of_close(&of);
213 }
214 
230 
231  if (!chars) {
232  return;
233  }
234 
235  Account_Char *ap, *last = NULL;
236 
237  for (ap = chars->chars; ap; ap = ap->next) {
238  if (!strcmp(ap->name, pl->ob->name)) break;
239  last = ap;
240  }
241  /* If ap is not NULL, it means we found a match.
242  * Rather than checking to see if values have changed, just
243  * update them.
244  */
245  if (ap) {
246  /* We know the name can not be changing, as otherwise
247  * we wouldn't have gotten a match. So no need to
248  * update that.
249  */
250 #if 0
251  /* As of right now, the class of the character is not stored
252  * anyplace, so we don't know what it is. Keep this code here
253  * until it can be determined.
254  */
256  ap->character_class = add_string();
257 #else
258  ap->character_class = add_string("");
259 #endif
260 
261  free_string(ap->race);
262  /* This looks pretty nasty. Basically, the player object is
263  * the race archetype, but its name has been changed to the player
264  * name. So we have to go back to the actual original archetype,
265  * the clone object in there, to get the name.
266  */
267  ap->race = add_string(pl->ob->arch->clone.name);
268 
269  ap->level = pl->ob->level;
270 
271  /* We should try and get the best face (front view) of
272  * the character - right now, we just get whatever view the character
273  * happens to be facing. Unfortunately, the animation code is such
274  * that it isn't a simple matter as most of that logic is not
275  * conveniently exposed.
276  */
277  free_string(ap->face);
278  ap->face = add_string(pl->ob->face->name);
279 
280  free_string(ap->party);
281  if (pl->party)
282  ap->party = add_string(pl->party->partyname);
283  else
284  ap->party = add_string("");
285 
286  free_string(ap->map);
287 
288  /* If there is a real name set for the map, use that instead
289  * of the pathname, which is what maplevel holds. This is
290  * more friendly (eg, Scorn Inn vs /scorn/inns/....)
291  */
292  if (pl->ob->map && pl->ob->map->name) {
293  ap->map = add_string(pl->ob->map->name);
294  } else {
295  /* Use the stored value - this may not be as up to date, but is
296  * probably more reliable, as depending when this charapter is added,
297  * it may not really be on any map.
298  */
299  ap->map = add_string(pl->maplevel);
300  }
301  } else {
302  /* In this case, we are adding a new entry */
303  ap = static_cast<Account_Char *>(malloc(sizeof (Account_Char)));
304  ap->name = add_string(pl->ob->name);
305  ap->character_class = add_string("");
306  ap->race = add_string(pl->ob->arch->clone.name);
307  ap->level = pl->ob->level;
308  ap->face = add_string(pl->ob->face->name);
309  if (pl->party)
310  ap->party = add_string(pl->party->partyname);
311  else
312  ap->party = add_string("");
313  ap->map = add_string(pl->maplevel);
314  /* The character cannot be dead already */
315  ap->isDead = 0;
316 
317  ap->next = NULL;
318  if (last)
319  last->next = ap;
320  else
321  chars->chars = ap;
322  }
323 }
324 
336 void account_char_remove(Account_Chars *chars, const char *pl_name) {
337  Account_Char *ap, *last = NULL;
338 
339  if (!chars) {
340  return;
341  }
342 
343  for (ap = chars->chars; ap; ap = ap->next) {
344  if (!strcmp(ap->name, pl_name)) break;
345  last = ap;
346  }
347  /* If we didn't find this character, nothing to do */
348  if (!ap) return;
349 
350  /* As per previous notes, these should never be NULL */
351  free_string(ap->name);
353  free_string(ap->race);
354  free_string(ap->face);
355  free_string(ap->party);
356  free_string(ap->map);
357 
358  /* remove this link, or update head of list as appropriate */
359  if (last) {
360  last->next = ap->next;
361  } else {
362  chars->chars = ap->next;
363  }
364  free(ap);
365 }
366 
375  if (!chars) {
376  return;
377  }
378 
379  if (chars->ref_count > 1) {
380  chars->ref_count--;
381  return;
382  }
383 
384  for (size_t p = 0; p < chars_loaded_count; p++) {
385  if (chars_loaded[p] == chars) {
386  if (p < chars_loaded_count - 1) {
388  }
390  break;
391  }
392  }
393 
394  Account_Char *ap, *next;
395 
396  for (ap = chars->chars; ap; ap = next) {
397  next = ap->next;
398 
399  free_string(ap->name);
401  free_string(ap->race);
402  free_string(ap->face);
403  free_string(ap->party);
404  free_string(ap->map);
405  free(ap);
406  }
407  free_string(chars->account_name);
408  free(chars);
409 }
410 
420 int make_perma_dead(object *op) {
421  player *pl = op->contr;
422  Account_Chars *chars;
423  Account_Char *ac;
424 
425  if (!pl) {
426  return 1;
427  }
428  /* Is this necessary? I'm not sure. It was in the code I found to use as an example */
429  pl = get_player(pl);
430  /* Make sure there is an account name to do things to */
431  if (!pl->socket->account_name) {
432  return 1;
433  }
434 
435  /* Load the appropriate account for the action. */
437 
438  /* Find the right character. */
439  for (ac = chars->chars; ac; ac = ac->next) {
440  if (strcmp(ac->name, op->name) == 0)
441  break;
442  }
443 
444  /* This character is dead */
445  ac->isDead = 1;
446  account_char_save(chars);
447  account_char_free(chars);
448  return 0;
449 }
450 
462 int unmake_perma_dead(char *account, char *player) {
463  Account_Chars *chars;
464  Account_Char *ac;
465 
466  /*
467  * If no account name, then there is nothing to do here.
468  * The character was dead before the account was kept track of.
469  */
470  if (!account) {
471  return 1;
472  }
473 
474  chars = account_char_load(account);
475 
476  /* Find the right character. */
477  for (ac = chars->chars; ac; ac = ac->next) {
478  if (strcmp(ac->name, player) == 0)
479  break;
480  }
481 
482  /* This character is alive */
483  ac->isDead = 0;
484  account_char_save(chars);
485  account_char_free(chars);
486  return 0;
487 }
Face::name
sstring name
Definition: face.h:19
give.next
def next
Definition: give.py:44
output_file.h
global.h
settings
struct Settings settings
Definition: init.cpp:139
account_char_struct::isDead
uint8_t isDead
Definition: account_char.h:20
obj::face
const Face * face
Definition: object.h:339
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
unmake_perma_dead
int unmake_perma_dead(char *account, char *player)
Definition: account_char.cpp:462
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
obj::map
struct mapdef * map
Definition: object.h:303
account_char_struct::level
uint8_t level
Definition: account_char.h:16
account_char_struct::map
sstring map
Definition: account_char.h:19
pl
Definition: player.h:105
pl::ob
object * ob
Definition: player.h:176
account_char_free
void account_char_free(Account_Chars *chars)
Definition: account_char.cpp:374
get_player
player * get_player(player *p)
Definition: player.cpp:283
account_char_struct::name
sstring name
Definition: account_char.h:13
Ice.tmp
int tmp
Definition: Ice.py:207
curse_on_apply.ac
ac
Definition: curse_on_apply.py:4
account_chars_struct
Definition: account_char.h:28
pl::maplevel
char maplevel[MAX_BUF]
Definition: player.h:109
account_char_struct
Definition: account_char.h:12
buf
StringBuffer * buf
Definition: readable.cpp:1611
party_struct::partyname
char * partyname
Definition: party.h:14
make_perma_dead
int make_perma_dead(object *op)
Definition: account_char.cpp:420
obj::name
sstring name
Definition: object.h:317
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:483
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
NUM_ACCOUNT_CHAR_FIELDS
#define NUM_ACCOUNT_CHAR_FIELDS
Definition: account_char.cpp:60
socket_struct::account_name
char * account_name
Definition: newserver.h:126
account_char_add
void account_char_add(Account_Chars *chars, player *pl)
Definition: account_char.cpp:229
account_char_struct::race
sstring race
Definition: account_char.h:15
account_char_struct::next
struct account_char_struct * next
Definition: account_char.h:21
sproto.h
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:580
mapdef::name
char * name
Definition: map.h:320
MAX_BUF
#define MAX_BUF
Definition: define.h:35
chars_loaded_count
static size_t chars_loaded_count
Definition: account_char.cpp:72
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
obj::arch
struct archt * arch
Definition: object.h:420
llevInfo
@ llevInfo
Definition: logger.h:12
archt::clone
object clone
Definition: object.h:476
player.h
ACCOUNT_DIR
#define ACCOUNT_DIR
Definition: account_char.cpp:69
chars_loaded_allocated
static size_t chars_loaded_allocated
Definition: account_char.cpp:73
give.op
op
Definition: give.py:33
account_char_struct::party
sstring party
Definition: account_char.h:18
account_char_struct::character_class
sstring character_class
Definition: account_char.h:14
account_chars_struct::chars
Account_Char * chars
Definition: account_char.h:31
account_char_save
void account_char_save(Account_Chars *chars)
Definition: account_char.cpp:179
account_char_remove
void account_char_remove(Account_Chars *chars, const char *pl_name)
Definition: account_char.cpp:336
account_char_struct::face
sstring face
Definition: account_char.h:17
account_char.h
account_chars_struct::account_name
sstring account_name
Definition: account_char.h:29
VERY_BIG_BUF
#define VERY_BIG_BUF
Definition: define.h:36
pl::party
partylist * party
Definition: player.h:202
account_chars_struct::ref_count
uint8_t ref_count
Definition: account_char.h:30
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
pl::socket
socket_struct * socket
Definition: player.h:107
account_char_load_from_file
static void account_char_load_from_file(Account_Chars *chars)
Definition: account_char.cpp:79
object.h
obj::level
int16_t level
Definition: object.h:359
account_char_load
Account_Chars * account_char_load(const char *account_name)
Definition: account_char.cpp:147
chars_loaded
static Account_Chars ** chars_loaded
Definition: account_char.cpp:71
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Definition: global.h:249