Crossfire Server, Trunk  R21024
account_char.c
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 <stdlib.h>
50 #include <string.h>
51 
52 #include "account_char.h"
53 #include "object.h"
54 #include "output_file.h"
55 #include "sproto.h"
56 #include "player.h"
57 
59 #define NUM_ACCOUNT_CHAR_FIELDS 8
60 
68 #define ACCOUNT_DIR "account"
69 
80 Account_Char *account_char_load(const char *account_name) {
81  char fname[MAX_BUF], buf[VERY_BIG_BUF];
82  FILE *fp;
83  Account_Char *first = NULL, *ac, *last = NULL;
84 
85  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, account_name);
86  fp = fopen(fname, "r");
87  if (!fp) {
88  /* This may not in fact be a critical error - for a new account, there
89  * may not be any data associated with it.
90  */
91  LOG(llevInfo, "Warning: Unable to open %s\n", fname);
92  return NULL;
93  }
94  while (fgets(buf, VERY_BIG_BUF, fp)) {
95  char *tmp[NUM_ACCOUNT_CHAR_FIELDS], *cp;
96 
97  /* Ignore any comment lines */
98  if (buf[0] == '#') continue;
99 
100  /* remove newline */
101  cp = strchr(buf, '\n');
102  if (cp) *cp = '\0';
103 
105  if (!tmp[7]) {
106  LOG(llevInfo, "Outdated entry in %s: %s\n", fname, buf);
107  tmp[7] = (char *) add_string("0");
108  } else {
109  LOG(llevError, "Corrupt entry in %s: %s\n", fname, buf);
110  continue;
111  }
112  }
113  ac = malloc(sizeof (Account_Char));
114  ac->name = add_string(tmp[0]);
115  ac->character_class = add_string(tmp[1]);
116  ac->race = add_string(tmp[2]);
117  ac->level = strtoul(tmp[3], (char**) NULL, 10);
118  ac->face = add_string(tmp[4]);
119  ac->party = add_string(tmp[5]);
120  ac->map = add_string(tmp[6]);
121  ac->isDead = strtoul(tmp[7], (char**) NULL, 10);
122 
123  ac->next = NULL;
124 
125  /* We tack on to the end of the list - in this way,
126  * the order of the file remains the same.
127  */
128  if (last)
129  last->next = ac;
130  else
131  first = ac;
132  last = ac;
133 
134  }
135  fclose(fp);
136  return (first);
137 }
138 
146 void account_char_save(const char *account, Account_Char *chars) {
147  char fname[MAX_BUF];
148  FILE *fp;
149  OutputFile of;
150  Account_Char *ac;
151 
152  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, account);
153 
154  /* It is certanly possibly that all characters for an account have
155  * been removed/deleted - in that case, we just want to remove this
156  * file.
157  */
158  if (chars == NULL) {
159  unlink(fname);
160  return;
161  }
162 
163  fp = of_open(&of, fname);
164  if (fp == NULL)
165  return;
166 
167  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
168  "# only read when the server starts, and any changes will be overwritten when\n"
169  "# the server exits.\n");
170  for (ac = chars; ac; ac = ac->next) {
171  fprintf(fp, "%s:%s:%s:%d:%s:%s:%s:%d\n",
172  ac->name, ac->character_class, ac->race, ac->level,
173  ac->face, ac->party, ac->map, ac->isDead);
174  }
175  of_close(&of);
176 }
177 
195 
196  Account_Char *ap, *last = NULL;
197 
198  for (ap = chars; ap; ap = ap->next) {
199  if (!strcmp(ap->name, pl->ob->name)) break;
200  last = ap;
201  }
202  /* If ap is not NULL, it means we found a match.
203  * Rather than checking to see if values have changed, just
204  * update them.
205  */
206  if (ap) {
207  /* We know the name can not be changing, as otherwise
208  * we wouldn't have gotten a match. So no need to
209  * update that.
210  */
211 #if 0
212  /* As of right now, the class of the character is not stored
213  * anyplace, so we don't know what it is. Keep this code here
214  * until it can be determined.
215  */
217  ap->character_class = add_string();
218 #else
219  ap->character_class = add_string("");
220 #endif
221 
222  free_string(ap->race);
223  /* This looks pretty nasty. Basically, the player object is
224  * the race archetype, but its name has been changed to the player
225  * name. So we have to go back to the actual original archetype,
226  * the clone object in there, to get the name.
227  */
228  ap->race = add_string(pl->ob->arch->clone.name);
229 
230  ap->level = pl->ob->level;
231 
232  /* We should try and get the best face (front view) of
233  * the character - right now, we just get whatever view the character
234  * happens to be facing. Unfortunately, the animation code is such
235  * that it isn't a simple matter as most of that logic is not
236  * conveniently exposed.
237  */
238  free_string(ap->face);
239  ap->face = add_string(pl->ob->face->name);
240 
241  free_string(ap->party);
242  if (pl->party)
243  ap->party = add_string(pl->party->partyname);
244  else
245  ap->party = add_string("");
246 
247  free_string(ap->map);
248 
249  /* If there is a real name set for the map, use that instead
250  * of the pathname, which is what maplevel holds. This is
251  * more friendly (eg, Scorn Inn vs /scorn/inns/....)
252  */
253  if (pl->ob->map && pl->ob->map->name) {
254  ap->map = add_string(pl->ob->map->name);
255  } else {
256  /* Use the stored value - this may not be as up to date, but is
257  * probably more reliable, as depending when this charapter is added,
258  * it may not really be on any map.
259  */
260  ap->map = add_string(pl->maplevel);
261  }
262  } else {
263  /* In this case, we are adding a new entry */
264  ap = malloc(sizeof (Account_Char));
265  ap->name = add_string(pl->ob->name);
266  ap->character_class = add_string("");
267  ap->race = add_string(pl->ob->arch->clone.name);
268  ap->level = pl->ob->level;
269  ap->face = add_string(pl->ob->face->name);
270  if (pl->party)
271  ap->party = add_string(pl->party->partyname);
272  else
273  ap->party = add_string("");
274  ap->map = add_string(pl->maplevel);
275  /* The character cannot be dead already */
276  ap->isDead = 0;
277 
278  ap->next = NULL;
279  if (last)
280  last->next = ap;
281  else
282  chars = ap;
283  }
284  return chars;
285 }
286 
298 Account_Char *account_char_remove(Account_Char *chars, const char *pl_name) {
299  Account_Char *ap, *last = NULL;
300 
301  for (ap = chars; ap; ap = ap->next) {
302  if (!strcmp(ap->name, pl_name)) break;
303  last = ap;
304  }
305  /* If we didn't find this character, nothing to do */
306  if (!ap) return (chars);
307 
308  /* As per previous notes, these should never be NULL */
309  free_string(ap->name);
311  free_string(ap->race);
312  free_string(ap->face);
313  free_string(ap->party);
314  free_string(ap->map);
315 
316  /* remove this link, or update head of list as appropriate */
317  if (last) {
318  last->next = ap->next;
319  } else {
320  chars = ap->next;
321  }
322  free(ap);
323  return (chars);
324 
325 }
326 
335  Account_Char *ap, *next;
336 
337  for (ap = chars; ap; ap = next) {
338  next = ap->next;
339 
340  free_string(ap->name);
342  free_string(ap->race);
343  free_string(ap->face);
344  free_string(ap->party);
345  free_string(ap->map);
346  free(ap);
347  }
348 }
349 
359 int make_perma_dead(object *op) {
360  player *pl = op->contr;
361  Account_Char *chars, *ac;
362 
363  if (!pl) {
364  return 1;
365  }
366  /* Is this necessary? I'm not sure. It was in the code I found to use as an example */
367  pl = get_player(pl);
368  /* Make sure there is an account name to do things to */
369  if (!pl->socket.account_name) {
370  return 1;
371  }
372 
373  /* Load the appropriate account for the action. */
375 
376  /* Find the right character. */
377  for (ac = chars; ac; ac = ac->next) {
378  if (strcmp(ac->name, op->name) == 0)
379  break;
380  }
381 
382  /* This character is dead */
383  ac->isDead = 1;
385  return 0;
386 }
387 
399 int unmake_perma_dead(char *account, char *player) {
400  Account_Char *chars, *ac;
401 
402  /*
403  * If no account name, then there is nothing to do here.
404  * The character was dead before the account was kept track of.
405  */
406  if (!account) {
407  return 1;
408  }
409 
410  struct pl* pl = account_get_logged_in_player(account);
411  if (pl == NULL) {
412  /* Load the appropriate account for the action. */
413  chars = account_char_load(account);
414  } else {
415  chars = pl->socket.account_chars;
416  }
417 
418  /* Find the right character. */
419  for (ac = chars; ac; ac = ac->next) {
420  if (strcmp(ac->name, player) == 0)
421  break;
422  }
423 
424  /* This character is alive */
425  ac->isDead = 0;
426  account_char_save(account, chars);
427  return 0;
428 }
const char * party
Definition: account_char.h:32
Definition: player.h:92
const char * name
Definition: account_char.h:27
char * account_name
Definition: newserver.h:126
void account_char_free(Account_Char *chars)
Definition: account_char.c:334
Account_Char * account_char_load(const char *account_name)
Definition: account_char.c:80
void free_string(sstring str)
Definition: shstr.c:280
object clone
Definition: object.h:470
struct account_struct * next
Definition: account.c:85
socket_struct socket
Definition: player.h:94
#define ACCOUNT_DIR
Definition: account_char.c:68
const char * map
Definition: account_char.h:33
char * partyname
Definition: party.h:14
partylist * party
Definition: player.h:186
struct account_char_struct * next
Definition: account_char.h:35
char * name
Definition: map.h:328
const char * name
Definition: face.h:20
Account_Char * account_chars
Definition: newserver.h:127
void account_char_save(const char *account, Account_Char *chars)
Definition: account_char.c:146
#define NUM_ACCOUNT_CHAR_FIELDS
Definition: account_char.c:59
struct mapdef * map
Definition: object.h:297
#define snprintf
Definition: win32.h:46
int unmake_perma_dead(char *account, char *player)
Definition: account_char.c:399
int of_close(OutputFile *of)
Definition: output_file.c:61
const char * name
Definition: object.h:311
Account_Char * account_char_remove(Account_Char *chars, const char *pl_name)
Definition: account_char.c:298
player * account_get_logged_in_player(const char *name)
Definition: account.c:587
player * get_player(player *p)
Definition: player.c:280
struct pl * contr
Definition: object.h:276
#define MAX_BUF
Definition: define.h:35
const char * character_class
Definition: account_char.h:28
object * ob
Definition: player.h:158
Account_Char * account_char_add(Account_Char *chars, player *pl)
Definition: account_char.c:194
int make_perma_dead(object *op)
Definition: account_char.c:359
#define VERY_BIG_BUF
Definition: define.h:36
const char * localdir
Definition: global.h:243
struct archt * arch
Definition: object.h:412
#define unlink(__a)
Definition: win32.h:56
struct Settings settings
Definition: init.c:40
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.c:30
sstring add_string(const char *str)
Definition: shstr.c:124
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.c:500
char maplevel[MAX_BUF]
Definition: player.h:96
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
const New_Face * face
Definition: object.h:332
int16_t level
Definition: object.h:351
const char * face
Definition: account_char.h:31
const char * race
Definition: account_char.h:29