Crossfire Server, Trunk  R213250
hiscore.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 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 
18 #define _GNU_SOURCE // strcasestr() is a GNU extension in string.h
19 
20 #include "global.h"
21 
22 #include <errno.h>
23 #include <string.h>
24 
25 #include "sproto.h"
26 #include "output_file.h"
27 
31 typedef struct scr {
32  char name[BIG_NAME];
33  char title[BIG_NAME];
34  char killer[BIG_NAME];
37  int maxhp,
38  maxsp,
39  maxgrace;
40  int position;
41 } score;
42 
46 typedef struct {
47  char fname[MAX_BUF];
49 } score_table;
50 
55 
66 static void put_score(const score *sc, char *buf, size_t size) {
67  snprintf(buf, size, "%s:%s:%"FMT64":%s:%s:%d:%d:%d", sc->name, sc->title, sc->exp, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
68 }
69 
76 static void hiscore_save(const score_table *table) {
77  FILE *fp;
78  OutputFile of;
79  size_t i;
80  char buf[MAX_BUF];
81 
82  LOG(llevDebug, "Writing highscore files %s\n", table->fname);
83 
84  fp = of_open(&of, table->fname);
85  if (fp == NULL)
86  return;
87 
88  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
89  if (table->entry[i].name[0] == '\0')
90  break;
91 
92  put_score(&table->entry[i], buf, sizeof(buf));
93  fprintf(fp, "%s\n", buf);
94  }
95  of_close(&of);
96 }
97 
110 static int get_score(char *bp, score *sc) {
111  char *cp;
112  char *tmp[8];
113 
114  cp = strchr(bp, '\n');
115  if (cp != NULL)
116  *cp = '\0';
117 
118  if (split_string(bp, tmp, 8, ':') != 8)
119  return 0;
120 
121  strncpy(sc->name, tmp[0], BIG_NAME);
122  sc->name[BIG_NAME-1] = '\0';
123 
124  strncpy(sc->title, tmp[1], BIG_NAME);
125  sc->title[BIG_NAME-1] = '\0';
126 
127  sscanf(tmp[2], "%"FMT64, &sc->exp);
128 
129  strncpy(sc->killer, tmp[3], BIG_NAME);
130  sc->killer[BIG_NAME-1] = '\0';
131 
132  strncpy(sc->maplevel, tmp[4], BIG_NAME);
133  sc->maplevel[BIG_NAME-1] = '\0';
134 
135  sscanf(tmp[5], "%d", &sc->maxhp);
136 
137  sscanf(tmp[6], "%d", &sc->maxsp);
138 
139  sscanf(tmp[7], "%d", &sc->maxgrace);
140  return 1;
141 }
142 
155 static char *draw_one_high_score(const score *sc, char *buf, size_t size) {
156  const char *s1;
157  const char *s2;
158 
159  if (strcmp(sc->killer, "quit") == 0 || strcmp(sc->killer, "left") == 0) {
160  s1 = sc->killer;
161  s2 = "the game";
162  } else {
163  s1 = "was killed by";
164  s2 = sc->killer;
165  }
166  snprintf(buf, size, "[fixed]%3d %10"FMT64"[print] %s%s%s %s %s on map %s <%d><%d><%d>.",
167  sc->position, sc->exp, sc->name, sc->title[0]==',' ? "" : " ", sc->title, s1, s2, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
168  return buf;
169 }
170 
182 static void add_score(score_table *table, score *new_score, score *old_score) {
183  size_t i;
184 
185  new_score->position = HIGHSCORE_LENGTH+1;
186  memset(old_score, 0, sizeof(*old_score));
187  old_score->position = -1;
188 
189  /* find existing entry by name */
190  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
191  if (table->entry[i].name[0] == '\0') {
192  table->entry[i] = *new_score;
193  table->entry[i].position = i+1;
194  break;
195  }
196  if (strcmp(new_score->name, table->entry[i].name) == 0) {
197  *old_score = table->entry[i];
198  if (table->entry[i].exp <= new_score->exp) {
199  table->entry[i] = *new_score;
200  table->entry[i].position = i+1;
201  }
202  break;
203  }
204  }
205 
206  if (i >= HIGHSCORE_LENGTH) {
207  /* entry for unknown name */
208 
209  if (new_score->exp < table->entry[i-1].exp) {
210  /* new exp is less than lowest hiscore entry => drop */
211  return;
212  }
213 
214  /* new exp is not less than lowest hiscore entry => add */
215  i--;
216  table->entry[i] = *new_score;
217  table->entry[i].position = i+1;
218  }
219 
220  /* move entry to correct position */
221  while (i > 0 && new_score->exp >= table->entry[i-1].exp) {
222  score tmp;
223 
224  tmp = table->entry[i-1];
225  table->entry[i-1] = table->entry[i];
226  table->entry[i] = tmp;
227 
228  table->entry[i-1].position = i;
229  table->entry[i].position = i+1;
230 
231  i--;
232  }
233 
234  new_score->position = table->entry[i].position;
235  hiscore_save(table);
236 }
237 
244 static void hiscore_load(score_table *table) {
245  FILE *fp;
246  size_t i;
247 
248  i = 0;
249 
250  fp = fopen(table->fname, "r");
251  if (fp == NULL) {
252  if (errno == ENOENT) {
253  LOG(llevInfo, "Highscore file %s does not exist\n", table->fname);
254  } else {
255  LOG(llevError, "Cannot open highscore file %s: %s\n", table->fname, strerror(errno));
256  }
257  } else {
258  LOG(llevDebug, "Reading highscore file %s\n", table->fname);
259  while (i < HIGHSCORE_LENGTH) {
260  char buf[MAX_BUF];
261 
262  if (fgets(buf, MAX_BUF, fp) == NULL) {
263  break;
264  }
265 
266  if (!get_score(buf, &table->entry[i]))
267  break;
268  table->entry[i].position = i+1;
269  i++;
270  }
271 
272  fclose(fp);
273  }
274 
275  while (i < HIGHSCORE_LENGTH) {
276  memset(&table->entry[i], 0, sizeof(table->entry[i]));
277  // This cannot be ++i due the right-to-left association of assignment.
278  table->entry[i].position = i + 1;
279  i++;
280  }
281 }
282 
286 void hiscore_init(void) {
287  snprintf(hiscore_table.fname, sizeof(hiscore_table.fname), "%s/%s", settings.localdir, HIGHSCORE);
288  hiscore_load(&hiscore_table);
289 }
290 
304 void hiscore_check(object *op, int quiet) {
305  score new_score;
306  score old_score;
307  char bufscore[MAX_BUF];
308  const char *message;
309 
310  if (op->stats.exp == 0 || !op->contr->name_changed)
311  return;
312 
313  if (QUERY_FLAG(op, FLAG_WAS_WIZ)) {
314  if (!quiet)
316  "Since you have been in wizard mode, "
317  "you can't enter the high-score list.");
318  return;
319  }
320  if (!op->stats.exp) {
321  if (!quiet)
323  "You don't deserve to save your character yet.");
324  return;
325  }
326 
327  strncpy(new_score.name, op->name, BIG_NAME);
328  new_score.name[BIG_NAME-1] = '\0';
329  player_get_title(op->contr, new_score.title, sizeof(new_score.title));
330  strncpy(new_score.killer, op->contr->killer, BIG_NAME);
331  if (new_score.killer[0] == '\0')
332  strcpy(new_score.killer, "a dungeon collapse");
333  new_score.killer[BIG_NAME-1] = '\0';
334  new_score.exp = op->stats.exp;
335  if (op->map == NULL)
336  *new_score.maplevel = '\0';
337  else {
338  strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME-1);
339  new_score.maplevel[BIG_NAME-1] = '\0';
340  }
341  new_score.maxhp = (int)op->stats.maxhp;
342  new_score.maxsp = (int)op->stats.maxsp;
343  new_score.maxgrace = (int)op->stats.maxgrace;
344  add_score(&hiscore_table, &new_score, &old_score);
345 
346  /* Everything below here is just related to print messages
347  * to the player. If quiet is set, we can just return
348  * now.
349  */
350  if (quiet)
351  return;
352 
353  if (old_score.position == -1) {
354  if (new_score.position > HIGHSCORE_LENGTH)
355  message = "You didn't enter the highscore list:";
356  else
357  message = "You entered the highscore list:";
358  } else {
359  if (new_score.position > HIGHSCORE_LENGTH)
360  message = "You left the highscore list:";
361  else if (new_score.exp > old_score.exp)
362  message = "You beat your last score:";
363  else
364  message = "You didn't beat your last score:";
365  }
366 
368  if (old_score.position != -1)
370  draw_one_high_score(&old_score, bufscore, sizeof(bufscore)));
372  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)));
373 }
374 
388 void hiscore_display(object *op, int max, const char *match) {
389  int printed_entries;
390  size_t j;
391 
393  "[fixed]Nr Score [print] Who <max hp><max sp><max grace>");
394 
395  printed_entries = 0;
396  for (j = 0; j < HIGHSCORE_LENGTH && hiscore_table.entry[j].name[0] != '\0' && printed_entries < max; j++) {
397  char scorebuf[MAX_BUF];
398 
399  if (*match != '\0'
400  && !strcasestr_local(hiscore_table.entry[j].name, match)
401  && !strcasestr_local(hiscore_table.entry[j].title, match))
402  continue;
403 
404  draw_one_high_score(&hiscore_table.entry[j], scorebuf, sizeof(scorebuf));
405  printed_entries++;
406 
407  if (op == NULL)
408  LOG(llevDebug, "%s\n", scorebuf);
409  else
411  }
412 }
static score_table hiscore_table
Definition: hiscore.c:54
char path[HUGE_BUF]
Definition: map.h:365
char killer[BIG_NAME]
Definition: hiscore.c:34
void hiscore_display(object *op, int max, const char *match)
Definition: hiscore.c:388
static void hiscore_save(const score_table *table)
Definition: hiscore.c:76
void player_get_title(const struct pl *pl, char *buf, size_t bufsize)
Definition: player.c:224
uint32_t name_changed
Definition: player.h:130
#define BIG_NAME
Definition: define.h:42
char title[BIG_NAME]
Definition: hiscore.c:33
struct scr score
static char * draw_one_high_score(const score *sc, char *buf, size_t size)
Definition: hiscore.c:155
int16_t maxgrace
Definition: living.h:45
Definition: hiscore.c:31
char fname[MAX_BUF]
Definition: hiscore.c:47
int64_t exp
Definition: living.h:47
#define MSG_TYPE_ADMIN_HISCORE
Definition: newclient.h:476
static int get_score(char *bp, score *sc)
Definition: hiscore.c:110
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:311
#define MSG_TYPE_APPLY
Definition: newclient.h:384
int16_t maxsp
Definition: living.h:43
static void hiscore_load(score_table *table)
Definition: hiscore.c:244
#define strcasestr_local
Definition: compat.h:24
char * name
Definition: map.h:328
int16_t maxhp
Definition: living.h:41
#define MSG_TYPE_COMMAND
Definition: newclient.h:379
#define MSG_TYPE_COMMAND_ERROR
Definition: newclient.h:509
int position
Definition: hiscore.c:40
struct mapdef * map
Definition: object.h:297
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:596
#define FMT64
Definition: compat.h:12
int of_close(OutputFile *of)
Definition: output_file.c:61
const char * name
Definition: object.h:311
int maxhp
Definition: hiscore.c:37
int maxsp
Definition: hiscore.c:37
struct pl * contr
Definition: object.h:276
score entry[HIGHSCORE_LENGTH]
Definition: hiscore.c:48
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
static void put_score(const score *sc, char *buf, size_t size)
Definition: hiscore.c:66
#define MAX_BUF
Definition: define.h:35
#define MSG_TYPE_ADMIN
Definition: newclient.h:377
signed __int64 int64_t
Definition: win32.h:168
static void add_score(score_table *table, score *new_score, score *old_score)
Definition: hiscore.c:182
char killer[BIG_NAME]
Definition: player.h:171
void hiscore_init(void)
Definition: hiscore.c:286
#define HIGHSCORE
Definition: config.h:518
const char * localdir
Definition: global.h:249
living stats
Definition: object.h:369
#define HIGHSCORE_LENGTH
Definition: config.h:526
char maplevel[BIG_NAME]
Definition: hiscore.c:36
struct Settings settings
Definition: init.c:39
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.c:30
int maxgrace
Definition: hiscore.c:37
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.c:500
#define NDI_UNIQUE
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
char name[BIG_NAME]
Definition: hiscore.c:32
#define FLAG_WAS_WIZ
Definition: define.h:234
void hiscore_check(object *op, int quiet)
Definition: hiscore.c:304
int64_t exp
Definition: hiscore.c:35