Crossfire Server, Trunk  R20513
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 
19 #include "global.h"
20 
21 #include <errno.h>
22 #include <string.h>
23 
24 #include "sproto.h"
25 #include "output_file.h"
26 
30 typedef struct scr {
31  char name[BIG_NAME];
32  char title[BIG_NAME];
33  char killer[BIG_NAME];
36  int maxhp,
37  maxsp,
38  maxgrace;
39  int position;
40 } score;
41 
45 typedef struct {
46  char fname[MAX_BUF];
48 } score_table;
49 
54 
65 static void put_score(const score *sc, char *buf, size_t size) {
66  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);
67 }
68 
75 static void hiscore_save(const score_table *table) {
76  FILE *fp;
77  OutputFile of;
78  size_t i;
79  char buf[MAX_BUF];
80 
81  LOG(llevDebug, "Writing highscore files %s\n", table->fname);
82 
83  fp = of_open(&of, table->fname);
84  if (fp == NULL)
85  return;
86 
87  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
88  if (table->entry[i].name[0] == '\0')
89  break;
90 
91  put_score(&table->entry[i], buf, sizeof(buf));
92  fprintf(fp, "%s\n", buf);
93  }
94  of_close(&of);
95 }
96 
109 static int get_score(char *bp, score *sc) {
110  char *cp;
111  char *tmp[8];
112 
113  cp = strchr(bp, '\n');
114  if (cp != NULL)
115  *cp = '\0';
116 
117  if (split_string(bp, tmp, 8, ':') != 8)
118  return 0;
119 
120  strncpy(sc->name, tmp[0], BIG_NAME);
121  sc->name[BIG_NAME-1] = '\0';
122 
123  strncpy(sc->title, tmp[1], BIG_NAME);
124  sc->title[BIG_NAME-1] = '\0';
125 
126  sscanf(tmp[2], "%"FMT64, &sc->exp);
127 
128  strncpy(sc->killer, tmp[3], BIG_NAME);
129  sc->killer[BIG_NAME-1] = '\0';
130 
131  strncpy(sc->maplevel, tmp[4], BIG_NAME);
132  sc->maplevel[BIG_NAME-1] = '\0';
133 
134  sscanf(tmp[5], "%d", &sc->maxhp);
135 
136  sscanf(tmp[6], "%d", &sc->maxsp);
137 
138  sscanf(tmp[7], "%d", &sc->maxgrace);
139  return 1;
140 }
141 
154 static char *draw_one_high_score(const score *sc, char *buf, size_t size) {
155  const char *s1;
156  const char *s2;
157 
158  if (strcmp(sc->killer, "quit") == 0 || strcmp(sc->killer, "left") == 0) {
159  s1 = sc->killer;
160  s2 = "the game";
161  } else {
162  s1 = "was killed by";
163  s2 = sc->killer;
164  }
165  snprintf(buf, size, "[fixed]%3d %10"FMT64"[print] %s %s %s %s on map %s <%d><%d><%d>.",
166  sc->position, sc->exp, sc->name, sc->title, s1, s2, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
167  return buf;
168 }
169 
181 static void add_score(score_table *table, score *new_score, score *old_score) {
182  size_t i;
183 
184  new_score->position = HIGHSCORE_LENGTH+1;
185  memset(old_score, 0, sizeof(*old_score));
186  old_score->position = -1;
187 
188  /* find existing entry by name */
189  for (i = 0; i < HIGHSCORE_LENGTH; i++) {
190  if (table->entry[i].name[0] == '\0') {
191  table->entry[i] = *new_score;
192  table->entry[i].position = i+1;
193  break;
194  }
195  if (strcmp(new_score->name, table->entry[i].name) == 0) {
196  *old_score = table->entry[i];
197  if (table->entry[i].exp <= new_score->exp) {
198  table->entry[i] = *new_score;
199  table->entry[i].position = i+1;
200  }
201  break;
202  }
203  }
204 
205  if (i >= HIGHSCORE_LENGTH) {
206  /* entry for unknown name */
207 
208  if (new_score->exp < table->entry[i-1].exp) {
209  /* new exp is less than lowest hiscore entry => drop */
210  return;
211  }
212 
213  /* new exp is not less than lowest hiscore entry => add */
214  i--;
215  table->entry[i] = *new_score;
216  table->entry[i].position = i+1;
217  }
218 
219  /* move entry to correct position */
220  while (i > 0 && new_score->exp >= table->entry[i-1].exp) {
221  score tmp;
222 
223  tmp = table->entry[i-1];
224  table->entry[i-1] = table->entry[i];
225  table->entry[i] = tmp;
226 
227  table->entry[i-1].position = i;
228  table->entry[i].position = i+1;
229 
230  i--;
231  }
232 
233  new_score->position = table->entry[i].position;
234  hiscore_save(table);
235 }
236 
243 static void hiscore_load(score_table *table) {
244  FILE *fp;
245  size_t i;
246 
247  i = 0;
248 
249  fp = fopen(table->fname, "r");
250  if (fp == NULL) {
251  if (errno == ENOENT) {
252  LOG(llevInfo, "Highscore file %s does not exist\n", table->fname);
253  } else {
254  LOG(llevError, "Cannot open highscore file %s: %s\n", table->fname, strerror(errno));
255  }
256  } else {
257  LOG(llevDebug, "Reading highscore file %s\n", table->fname);
258  while (i < HIGHSCORE_LENGTH) {
259  char buf[MAX_BUF];
260 
261  if (fgets(buf, MAX_BUF, fp) == NULL) {
262  break;
263  }
264 
265  if (!get_score(buf, &table->entry[i]))
266  break;
267  table->entry[i].position = i+1;
268  i++;
269  }
270 
271  fclose(fp);
272  }
273 
274  while (i < HIGHSCORE_LENGTH) {
275  memset(&table->entry[i], 0, sizeof(table->entry[i]));
276  table->entry[i].position = i+1;
277  i++;
278  }
279 }
280 
284 void hiscore_init(void) {
285  snprintf(hiscore_table.fname, sizeof(hiscore_table.fname), "%s/%s", settings.localdir, HIGHSCORE);
286  hiscore_load(&hiscore_table);
287 }
288 
302 void hiscore_check(object *op, int quiet) {
303  score new_score;
304  score old_score;
305  char bufscore[MAX_BUF];
306  const char *message;
307 
308  if (op->stats.exp == 0 || !op->contr->name_changed)
309  return;
310 
311  if (QUERY_FLAG(op, FLAG_WAS_WIZ)) {
312  if (!quiet)
314  "Since you have been in wizard mode, "
315  "you can't enter the high-score list.");
316  return;
317  }
318  if (!op->stats.exp) {
319  if (!quiet)
321  "You don't deserve to save your character yet.");
322  return;
323  }
324 
325  strncpy(new_score.name, op->name, BIG_NAME);
326  new_score.name[BIG_NAME-1] = '\0';
327  player_get_title(op->contr, new_score.title, sizeof(new_score.title));
328  strncpy(new_score.killer, op->contr->killer, BIG_NAME);
329  if (new_score.killer[0] == '\0')
330  strcpy(new_score.killer, "a dungeon collapse");
331  new_score.killer[BIG_NAME-1] = '\0';
332  new_score.exp = op->stats.exp;
333  if (op->map == NULL)
334  *new_score.maplevel = '\0';
335  else {
336  strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME-1);
337  new_score.maplevel[BIG_NAME-1] = '\0';
338  }
339  new_score.maxhp = (int)op->stats.maxhp;
340  new_score.maxsp = (int)op->stats.maxsp;
341  new_score.maxgrace = (int)op->stats.maxgrace;
342  add_score(&hiscore_table, &new_score, &old_score);
343 
344  /* Everything below here is just related to print messages
345  * to the player. If quiet is set, we can just return
346  * now.
347  */
348  if (quiet)
349  return;
350 
351  if (old_score.position == -1) {
352  if (new_score.position > HIGHSCORE_LENGTH)
353  message = "You didn't enter the highscore list:";
354  else
355  message = "You entered the highscore list:";
356  } else {
357  if (new_score.position > HIGHSCORE_LENGTH)
358  message = "You left the highscore list:";
359  else if (new_score.exp > old_score.exp)
360  message = "You beat your last score:";
361  else
362  message = "You didn't beat your last score:";
363  }
364 
366  if (old_score.position != -1)
368  draw_one_high_score(&old_score, bufscore, sizeof(bufscore)));
370  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)));
371 }
372 
386 void hiscore_display(object *op, int max, const char *match) {
387  int printed_entries;
388  size_t j;
389 
391  "[fixed]Nr Score [print] Who <max hp><max sp><max grace>");
392 
393  printed_entries = 0;
394  for (j = 0; j < HIGHSCORE_LENGTH && hiscore_table.entry[j].name[0] != '\0' && printed_entries < max; j++) {
395  char scorebuf[MAX_BUF];
396 
397  if (*match != '\0'
398  && !strcasestr_local(hiscore_table.entry[j].name, match)
399  && !strcasestr_local(hiscore_table.entry[j].title, match))
400  continue;
401 
402  draw_one_high_score(&hiscore_table.entry[j], scorebuf, sizeof(scorebuf));
403  printed_entries++;
404 
405  if (op == NULL)
406  LOG(llevDebug, "%s\n", scorebuf);
407  else
409  }
410 }
static score_table hiscore_table
The highscore table.
Definition: hiscore.c:53
Error, serious thing.
Definition: logger.h:11
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:365
char killer[BIG_NAME]
Name (+ title) or "left".
Definition: hiscore.c:33
Information.
Definition: logger.h:12
void hiscore_display(object *op, int max, const char *match)
Displays the high score file.
Definition: hiscore.c:386
static void hiscore_save(const score_table *table)
Saves the highscore_table into the highscore file.
Definition: hiscore.c:75
void player_get_title(const struct pl *pl, char *buf, size_t bufsize)
Returns the player&#39;s title.
Definition: player.c:224
uint32_t name_changed
If true, the player has set a name.
Definition: player.h:130
Information on one title.
Definition: readable.c:106
#define BIG_NAME
Definition: define.h:42
char title[BIG_NAME]
Title.
Definition: hiscore.c:32
struct scr score
The score structure is used when treating new high-scores.
static char * draw_one_high_score(const score *sc, char *buf, size_t size)
Formats one score to display to a player.
Definition: hiscore.c:154
int16_t maxgrace
Maximum grace.
Definition: living.h:44
The score structure is used when treating new high-scores.
Definition: hiscore.c:30
char fname[MAX_BUF]
Filename of the backing file.
Definition: hiscore.c:46
int64_t exp
Experience.
Definition: living.h:46
#define MSG_TYPE_ADMIN_HISCORE
Hiscore list.
Definition: newclient.h:476
static int get_score(char *bp, score *sc)
The opposite of put_score(), get_score reads from the given buffer into a static score structure...
Definition: hiscore.c:109
Global type definitions and header inclusions.
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.c:310
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:384
int16_t maxsp
Max spell points.
Definition: living.h:42
A highscore table.
Definition: hiscore.c:45
static void hiscore_load(score_table *table)
Loads the hiscore_table from the highscore file.
Definition: hiscore.c:243
#define strcasestr_local
Definition: compat.h:24
char * name
Name of map as given by its creator.
Definition: map.h:328
int16_t maxhp
Max hit points.
Definition: living.h:40
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:379
#define MSG_TYPE_COMMAND_ERROR
Bad syntax/can&#39;t use command.
Definition: newclient.h:509
int position
Position in the highscore list.
Definition: hiscore.c:39
struct mapdef * map
Pointer to the map in which this object is present.
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)
Closes an output file.
Definition: output_file.c:61
const char * name
The name of the object, obviously...
Definition: object.h:311
int maxhp
Max hp when killed.
Definition: hiscore.c:36
int maxsp
Max sp when killed.
Definition: hiscore.c:36
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
score entry[HIGHSCORE_LENGTH]
The entries in decreasing exp order.
Definition: hiscore.c:47
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
static void put_score(const score *sc, char *buf, size_t size)
Writes the given score structure to specified buffer.
Definition: hiscore.c:65
#define MAX_BUF
Used for all kinds of things.
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)
Adds the given score-structure to the high-score list, but only if it was good enough to deserve a pl...
Definition: hiscore.c:181
char killer[BIG_NAME]
Who killed this player.
Definition: player.h:171
void hiscore_init(void)
Initializes the module.
Definition: hiscore.c:284
#define HIGHSCORE
Definition: config.h:568
const char * localdir
Read/write data files.
Definition: global.h:245
living stats
Str, Con, Dex, etc.
Definition: object.h:368
#define HIGHSCORE_LENGTH
How many entries there are room for.
Definition: config.h:576
char maplevel[BIG_NAME]
Killed on what level.
Definition: hiscore.c:35
Only for debugging purposes.
Definition: logger.h:13
struct Settings settings
Server settings.
Definition: init.c:40
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.c:30
Functions for creating text output files.
int maxgrace
Max grace when killed.
Definition: hiscore.c:36
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings...
Definition: utils.c:499
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
char name[BIG_NAME]
Name.
Definition: hiscore.c:31
#define FLAG_WAS_WIZ
Player was once a wiz.
Definition: define.h:234
void hiscore_check(object *op, int quiet)
Checks if player should enter the hiscore, and if so writes her into the list.
Definition: hiscore.c:302
int64_t exp
Experience.
Definition: hiscore.c:34