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 std::vector<Account_Chars *> chars_loaded;
78  char fname[MAX_BUF], buf[VERY_BIG_BUF];
79  FILE *fp;
81 
82  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, chars->account_name);
83  fp = fopen(fname, "r");
84  if (!fp) {
85  /* This may not in fact be a critical error - for a new account, there
86  * may not be any data associated with it.
87  */
88  LOG(llevInfo, "Warning: Unable to open %s: %s\n", fname, strerror(errno));
89  return;
90  }
91  while (fgets(buf, VERY_BIG_BUF, fp)) {
92  char *tmp[NUM_ACCOUNT_CHAR_FIELDS], *cp;
93 
94  /* Ignore any comment lines */
95  if (buf[0] == '#') continue;
96 
97  /* remove newline */
98  cp = strchr(buf, '\n');
99  if (cp) *cp = '\0';
100 
102  if (!tmp[7]) {
103  LOG(llevInfo, "Outdated entry in %s: %s\n", fname, buf);
104  tmp[7] = (char *) add_string("0");
105  } else {
106  LOG(llevError, "Corrupt entry in %s: %s\n", fname, buf);
107  continue;
108  }
109  }
110  ac = static_cast<Account_Char *>(malloc(sizeof (Account_Char)));
111  ac->name = add_string(tmp[0]);
112  ac->character_class = add_string(tmp[1]);
113  ac->race = add_string(tmp[2]);
114  ac->level = strtoul(tmp[3], (char**) NULL, 10);
115  ac->face = add_string(tmp[4]);
116  ac->party = add_string(tmp[5]);
117  ac->map = add_string(tmp[6]);
118  ac->isDead = strtoul(tmp[7], (char**) NULL, 10);
119 
120  chars->chars.push_back(ac);
121  }
122  fclose(fp);
123 }
124 
135 Account_Chars *account_char_load(const char *account_name) {
136  for (auto account : chars_loaded) {
137  if (strcmp(account->account_name, account_name) == 0) {
138  account->ref_count++;
139  return account;
140  }
141  }
142 
143  Account_Chars *ac = new Account_Chars();
144  ac->ref_count++;
145  ac->account_name = add_string(account_name);
147 
148  chars_loaded.push_back(ac);
149 
150  return ac;
151 }
152 
159  char fname[MAX_BUF];
160  FILE *fp;
161  OutputFile of;
162 
163  if (!chars) {
164  return;
165  }
166 
167  snprintf(fname, MAX_BUF, "%s/%s/%s", settings.localdir, ACCOUNT_DIR, chars->account_name);
168 
169  /* It is certanly possibly that all characters for an account have
170  * been removed/deleted - in that case, we just want to remove this
171  * file.
172  */
173  if (chars->chars.empty()) {
174  unlink(fname);
175  return;
176  }
177 
178  fp = of_open(&of, fname);
179  if (fp == NULL)
180  return;
181 
182  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
183  "# only read when the server starts, and any changes will be overwritten when\n"
184  "# the server exits.\n");
185  for (auto ac : chars->chars) {
186  fprintf(fp, "%s:%s:%s:%d:%s:%s:%s:%d\n",
187  ac->name, ac->character_class, ac->race, ac->level,
188  ac->face, ac->party, ac->map, ac->isDead);
189  }
190  of_close(&of);
191 }
192 
208 
209  if (!chars) {
210  return;
211  }
212 
213  Account_Char *ap = nullptr;
214 
215  for (auto ch : chars->chars) {
216  if (!strcmp(ch->name, pl->ob->name)) {
217  ap = ch;
218  break;
219  }
220  }
221 
222  /* If ap is not NULL, it means we found a match.
223  * Rather than checking to see if values have changed, just
224  * update them.
225  */
226  if (ap) {
227  /* We know the name can not be changing, as otherwise
228  * we wouldn't have gotten a match. So no need to
229  * update that.
230  */
231 #if 0
232  /* As of right now, the class of the character is not stored
233  * anyplace, so we don't know what it is. Keep this code here
234  * until it can be determined.
235  */
237  ap->character_class = add_string();
238 #else
239  ap->character_class = add_string("");
240 #endif
241 
242  free_string(ap->race);
243  /* This looks pretty nasty. Basically, the player object is
244  * the race archetype, but its name has been changed to the player
245  * name. So we have to go back to the actual original archetype,
246  * the clone object in there, to get the name.
247  */
248  ap->race = add_string(pl->ob->arch->clone.name);
249 
250  ap->level = pl->ob->level;
251 
252  /* We should try and get the best face (front view) of
253  * the character - right now, we just get whatever view the character
254  * happens to be facing. Unfortunately, the animation code is such
255  * that it isn't a simple matter as most of that logic is not
256  * conveniently exposed.
257  */
258  free_string(ap->face);
259  ap->face = add_string(pl->ob->face->name);
260 
261  free_string(ap->party);
262  if (pl->party)
263  ap->party = add_string(pl->party->partyname);
264  else
265  ap->party = add_string("");
266 
267  free_string(ap->map);
268 
269  /* If there is a real name set for the map, use that instead
270  * of the pathname, which is what maplevel holds. This is
271  * more friendly (eg, Scorn Inn vs /scorn/inns/....)
272  */
273  if (pl->ob->map && pl->ob->map->name) {
274  ap->map = add_string(pl->ob->map->name);
275  } else {
276  /* Use the stored value - this may not be as up to date, but is
277  * probably more reliable, as depending when this charapter is added,
278  * it may not really be on any map.
279  */
280  ap->map = add_string(pl->maplevel);
281  }
282  } else {
283  /* In this case, we are adding a new entry */
284  ap = static_cast<Account_Char *>(malloc(sizeof (Account_Char)));
285  ap->name = add_string(pl->ob->name);
286  ap->character_class = add_string("");
287  ap->race = add_string(pl->ob->arch->clone.name);
288  ap->level = pl->ob->level;
289  ap->face = add_string(pl->ob->face->name);
290  if (pl->party)
291  ap->party = add_string(pl->party->partyname);
292  else
293  ap->party = add_string("");
294  ap->map = add_string(pl->maplevel);
295  /* The character cannot be dead already */
296  ap->isDead = 0;
297 
298  chars->chars.push_back(ap);
299  }
300 }
301 
313 void account_char_remove(Account_Chars *chars, const char *pl_name) {
314  Account_Char *ap;
315 
316  if (!chars) {
317  return;
318  }
319 
320  auto ch = std::find_if(chars->chars.begin(), chars->chars.end(), [&] (const auto ch) { return !strcmp(ch->name, pl_name); });
321  /* If we didn't find this character, nothing to do */
322  if (ch == chars->chars.end())
323  return;
324 
325  ap = *ch;
326  chars->chars.erase(ch);
327 
328  /* As per previous notes, these should never be NULL */
329  free_string(ap->name);
331  free_string(ap->race);
332  free_string(ap->face);
333  free_string(ap->party);
334  free_string(ap->map);
335  free(ap);
336 }
337 
346  if (!chars) {
347  return;
348  }
349 
350  if (chars->ref_count > 1) {
351  chars->ref_count--;
352  return;
353  }
354 
355  chars_loaded.erase(std::remove(chars_loaded.begin(), chars_loaded.end(), chars), chars_loaded.end());
356 
357  for (auto ap : chars->chars) {
358  free_string(ap->name);
359  free_string(ap->character_class);
360  free_string(ap->race);
361  free_string(ap->face);
362  free_string(ap->party);
363  free_string(ap->map);
364  free(ap);
365  }
366  free_string(chars->account_name);
367  delete chars;
368 }
369 
379 int make_perma_dead(object *op) {
380  player *pl = op->contr;
381  Account_Chars *chars;
382 
383  if (!pl) {
384  return 1;
385  }
386  /* Is this necessary? I'm not sure. It was in the code I found to use as an example */
387  pl = get_player(pl);
388  /* Make sure there is an account name to do things to */
389  if (!pl->socket->account_name) {
390  return 1;
391  }
392 
393  /* Load the appropriate account for the action. */
394  chars = account_char_load(pl->socket->account_name);
395 
396  /* Find the right character. */
397  for (auto ac : chars->chars) {
398  if (strcmp(ac->name, op->name) == 0) {
399  /* This character is dead */
400  ac->isDead = 1;
401  account_char_save(chars);
402  account_char_free(chars);
403  break;
404  }
405  }
406 
407  return 0;
408 }
409 
421 int unmake_perma_dead(char *account, char *player) {
422  Account_Chars *chars;
423 
424  /*
425  * If no account name, then there is nothing to do here.
426  * The character was dead before the account was kept track of.
427  */
428  if (!account) {
429  return 1;
430  }
431 
432  chars = account_char_load(account);
433 
434  /* Find the right character. */
435  for (auto ac : chars->chars) {
436  if (strcmp(ac->name, player) == 0) {
437  /* This character is alive */
438  ac->isDead = 0;
439  account_char_save(chars);
440  account_char_free(chars);
441  break;
442  }
443  }
444 
445  return 0;
446 }
output_file.h
global.h
settings
struct Settings settings
Definition: init.cpp:139
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
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:421
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
player
Definition: player.h:105
Account_Char::race
sstring race
Definition: account_char.h:15
chars_loaded
static std::vector< Account_Chars * > chars_loaded
Definition: account_char.cpp:71
Account_Chars::account_name
sstring account_name
Definition: account_char.h:28
account_char_free
void account_char_free(Account_Chars *chars)
Definition: account_char.cpp:345
get_player
player * get_player(player *p)
Definition: player.cpp:285
Ice.tmp
int tmp
Definition: Ice.py:207
curse_on_apply.ac
ac
Definition: curse_on_apply.py:4
Account_Char
Definition: account_char.h:12
buf
StringBuffer * buf
Definition: readable.cpp:1565
Account_Chars::chars
std::vector< Account_Char * > chars
Definition: account_char.h:30
Account_Char::character_class
sstring character_class
Definition: account_char.h:14
make_perma_dead
int make_perma_dead(object *op)
Definition: account_char.cpp:379
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:473
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
account_char_add
void account_char_add(Account_Chars *chars, player *pl)
Definition: account_char.cpp:207
sproto.h
MAX_BUF
#define MAX_BUF
Definition: define.h:35
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
Account_Char::party
sstring party
Definition: account_char.h:18
llevInfo
@ llevInfo
Definition: logger.h:12
Account_Char::name
sstring name
Definition: account_char.h:13
Account_Char::isDead
uint8_t isDead
Definition: account_char.h:20
player.h
ACCOUNT_DIR
#define ACCOUNT_DIR
Definition: account_char.cpp:69
give.op
op
Definition: give.py:33
Account_Char::face
sstring face
Definition: account_char.h:17
account_char_save
void account_char_save(Account_Chars *chars)
Definition: account_char.cpp:158
account_char_remove
void account_char_remove(Account_Chars *chars, const char *pl_name)
Definition: account_char.cpp:313
Account_Char::map
sstring map
Definition: account_char.h:19
Account_Char::level
uint8_t level
Definition: account_char.h:16
account_char.h
Account_Chars
Definition: account_char.h:27
VERY_BIG_BUF
#define VERY_BIG_BUF
Definition: define.h:36
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
account_char_load_from_file
static void account_char_load_from_file(Account_Chars *chars)
Definition: account_char.cpp:77
Account_Chars::ref_count
uint8_t ref_count
Definition: account_char.h:29
object.h
account_char_load
Account_Chars * account_char_load(const char *account_name)
Definition: account_char.cpp:135
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Definition: global.h:249