Crossfire Server, Branches 1.12  R18729
hiscore.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_hiscore_c =
3  * "$Id: hiscore.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2006 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
34 #include <global.h>
35 #ifndef __CEXTRACT__
36 #include <sproto.h>
37 #endif
38 
43 typedef struct scr {
44  char name[BIG_NAME];
45  char title[BIG_NAME];
46  char killer[BIG_NAME];
47  sint64 exp;
50  int position;
51 } score;
52 
65 static char *spool(char *bp, const char *error) {
66  static char *prev_pos = NULL;
67  char *next_pos;
68 
69  if (bp == NULL) {
70  if (prev_pos == NULL) {
71  LOG(llevError, "Called spool (%s) with NULL without previous call.\n", error);
72  return NULL;
73  }
74  bp = prev_pos;
75  }
76  if (*bp == '\0') {
77  LOG(llevError, "spool: End of line at %s\n", error);
78  return NULL;
79  }
80  if ((next_pos = strchr(bp, ':')) != NULL) {
81  *next_pos = '\0';
82  prev_pos = next_pos+1;
83  } else
84  prev_pos = NULL;
85  return bp;
86 }
87 
97 static void copy_score(const score *sc1, score *sc2) {
98  strncpy(sc2->name, sc1->name, BIG_NAME);
99  sc2->name[BIG_NAME-1] = '\0';
100  strncpy(sc2->title, sc1->title, BIG_NAME);
101  sc2->title[BIG_NAME-1] = '\0';
102  strncpy(sc2->killer, sc1->killer, BIG_NAME);
103  sc2->killer[BIG_NAME-1] = '\0';
104  sc2->exp = sc1->exp;
105  strcpy(sc2->maplevel, sc1->maplevel);
106  sc2->maxhp = sc1->maxhp;
107  sc2->maxsp = sc1->maxsp;
108  sc2->maxgrace = sc1->maxgrace;
109 }
110 
121 static void put_score(const score *sc, char *buf, int size) {
122  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);
123 }
124 
136 static score *get_score(char *bp) {
137  static score sc;
138  char *cp;
139 
140  if ((cp = strchr(bp, '\n')) != NULL)
141  *cp = '\0';
142 
143  if ((cp = spool(bp, "name")) == NULL)
144  return NULL;
145  strncpy(sc.name, cp, BIG_NAME);
146  sc.name[BIG_NAME-1] = '\0';
147 
148  if ((cp = spool(NULL, "title")) == NULL)
149  return NULL;
150  strncpy(sc.title, cp, BIG_NAME);
151  sc.title[BIG_NAME-1] = '\0';
152 
153  if ((cp = spool(NULL, "score")) == NULL)
154  return NULL;
155 
156  sscanf(cp, "%"FMT64, &sc.exp);
157 
158  if ((cp = spool(NULL, "killer")) == NULL)
159  return NULL;
160  strncpy(sc.killer, cp, BIG_NAME);
161  sc.killer[BIG_NAME-1] = '\0';
162 
163  if ((cp = spool(NULL, "map")) == NULL)
164  return NULL;
165  strncpy(sc.maplevel, cp, BIG_NAME);
166  sc.maplevel[BIG_NAME-1] = '\0';
167 
168  if ((cp = spool(NULL, "maxhp")) == NULL)
169  return NULL;
170  sscanf(cp, "%d", &sc.maxhp);
171 
172  if ((cp = spool(NULL, "maxsp")) == NULL)
173  return NULL;
174  sscanf(cp, "%d", &sc.maxsp);
175 
176  if ((cp = spool(NULL, "maxgrace")) == NULL)
177  return NULL;
178  sscanf(cp, "%d", &sc.maxgrace);
179  return &sc;
180 }
181 
194 static char *draw_one_high_score(const score *sc, char *buf, int size) {
195  if (!strncmp(sc->killer, "quit", MAX_NAME))
196  snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s quit the game on map %s <%d><%d><%d>.",
197  sc->position, sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
198  else if (!strncmp(sc->killer, "left", MAX_NAME))
199  snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s left the game on map %s <%d><%d><%d>.",
200  sc->position, sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
201  else
202  snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s was killed by %s on map %s <%d><%d><%d>.",
203  sc->position, sc->exp, sc->name, sc->title, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
204  return buf;
205 }
206 
217 static score *add_score(score *new_score) {
218  FILE *fp;
219  static score old_score;
220  score *tmp_score, pscore[HIGHSCORE_LENGTH];
221  char buf[MAX_BUF], filename[MAX_BUF], bp[MAX_BUF];
222  int nrofscores = 0, flag = 0, i, comp;
223 
224  new_score->position = HIGHSCORE_LENGTH+1;
225  old_score.position = -1;
226  snprintf(filename, sizeof(filename), "%s/%s", settings.localdir, HIGHSCORE);
227  if ((fp = open_and_uncompress(filename, 1, &comp)) != NULL) {
228  while (fgets(buf, MAX_BUF, fp) != NULL && nrofscores < HIGHSCORE_LENGTH) {
229  if ((tmp_score = get_score(buf)) == NULL)
230  break;
231  if (!flag && new_score->exp >= tmp_score->exp) {
232  copy_score(new_score, &pscore[nrofscores]);
233  new_score->position = nrofscores;
234  flag = 1;
235  if (++nrofscores >= HIGHSCORE_LENGTH)
236  break;
237  }
238  if (!strcmp(new_score->name, tmp_score->name)) { /* Another entry */
239  copy_score(tmp_score, &old_score);
240  old_score.position = nrofscores;
241  if (flag)
242  continue;
243  }
244  copy_score(tmp_score, &pscore[nrofscores++]);
245  }
246  close_and_delete(fp, comp);
247  }
248  if (old_score.position != -1 && old_score.exp >= new_score->exp)
249  return &old_score; /* Did not beat old score */
250  if (!flag && nrofscores < HIGHSCORE_LENGTH)
251  copy_score(new_score, &pscore[nrofscores++]);
252  if ((fp = fopen(filename, "w")) == NULL) {
253  LOG(llevError, "Cannot write to highscore file %s: %s\n", filename, strerror_local(errno, buf, sizeof(buf)));
254  return NULL;
255  }
256  for (i = 0; i < nrofscores; i++) {
257  put_score(&pscore[i], bp, sizeof(bp));
258  fprintf(fp, "%s\n", bp);
259  }
260  fclose(fp);
261  if (flag) {
262  /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
263  hiscore-list */
264  if (old_score.position == -1)
265  return new_score;
266  return &old_score;
267  }
268  new_score->position = -1;
269  if (old_score.position != -1)
270  return &old_score;
271  if (nrofscores) {
272  copy_score(&pscore[nrofscores-1], &old_score);
273  return &old_score;
274  }
275  LOG(llevError, "Highscore error.\n");
276  return NULL;
277 }
278 
289 void check_score(object *op, int quiet) {
290  score new_score;
291  score *old_score;
292  char bufscore[MAX_BUF];
293 
294  if (op->stats.exp == 0)
295  return;
296 
297  if (!op->contr->name_changed) {
298  if (op->stats.exp > 0) {
299  if (!quiet)
301  "As you haven't changed your name, you won't "
302  "get into the high-score list.", NULL);
303  }
304  return;
305  }
306  if (QUERY_FLAG(op, FLAG_WAS_WIZ)) {
307  if (!quiet)
309  "Since you have been in wizard mode, "
310  "you can't enter the high-score list.", NULL);
311  return;
312  }
313  if (op->contr->explore) {
314  if (!quiet)
316  "Since you were in explore mode, "
317  "you can't enter the high-score list.", NULL);
318  return;
319  }
320  if (!op->stats.exp) {
321  if (!quiet)
323  "You don't deserve to save your character yet.", NULL);
324  return;
325  }
326 
327  strncpy(new_score.name, op->name, BIG_NAME);
328  new_score.name[BIG_NAME-1] = '\0';
329  strncpy(new_score.title, op->contr->own_title, BIG_NAME);
330  if (new_score.title[0] == '\0')
331  strncpy(new_score.title, op->contr->title, BIG_NAME);
332  new_score.title[BIG_NAME-1] = '\0';
333  strncpy(new_score.killer, op->contr->killer, BIG_NAME);
334  if (new_score.killer[0] == '\0')
335  strcpy(new_score.killer, "a dungeon collapse");
336  new_score.killer[BIG_NAME-1] = '\0';
337  new_score.exp = op->stats.exp;
338  if (op->map == NULL)
339  *new_score.maplevel = '\0';
340  else {
341  strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME-1);
342  new_score.maplevel[BIG_NAME-1] = '\0';
343  }
344  new_score.maxhp = (int)op->stats.maxhp;
345  new_score.maxsp = (int)op->stats.maxsp;
346  new_score.maxgrace = (int)op->stats.maxgrace;
347  if ((old_score = add_score(&new_score)) == NULL) {
348  if (!quiet)
350  "Error in the highscore list.", NULL);
351  return;
352  }
353  /* Everything below here is just related to print messages
354  * to the player. If quiet is set, we can just return
355  * now.
356  */
357  if (quiet)
358  return;
359 
360  if (new_score.position == -1) {
361  new_score.position = HIGHSCORE_LENGTH+1; /* Not strictly correct... */
362 
363  if (!strcmp(old_score->name, new_score.name))
365  "You didn't beat your last highscore:", NULL);
366  else
368  "You didn't enter the highscore list:", NULL);
369 
371  draw_one_high_score(old_score, bufscore, sizeof(bufscore)), NULL);
372 
374  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)), NULL);
375  return;
376  }
377  if (old_score->exp >= new_score.exp)
379  "You didn't beat your last score:", NULL);
380  else
382  "You beat your last score:", NULL);
383 
385  draw_one_high_score(old_score, bufscore, sizeof(bufscore)), NULL);
387  draw_one_high_score(&new_score, bufscore, sizeof(bufscore)), NULL);
388 }
389 
390 
391 
402 void display_high_score(object *op, int max, const char *match) {
403  FILE *fp;
404  char buf[MAX_BUF], scorebuf[MAX_BUF];
405  int i = 0, j = 0, comp;
406  score *sc;
407 
408  snprintf(buf, sizeof(buf), "%s/%s", settings.localdir, HIGHSCORE);
409  if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
410  char err[MAX_BUF];
411 
412  LOG(llevError, "Cannot open highscore file %s: %s\n", buf, strerror_local(errno, err, sizeof(err)));
413  if (op != NULL)
415  "There is no highscore file.", NULL);
416  return;
417  }
418 
420  "[Fixed]Nr Score Who <max hp><max sp><max grace>",
421  "Nr Score Who <max hp><max sp><max grace>");
422 
423  while (fgets(buf, MAX_BUF, fp) != NULL) {
424  if (j >= HIGHSCORE_LENGTH || i >= (max-1))
425  break;
426  if ((sc = get_score(buf)) == NULL)
427  break;
428  sc->position = ++j;
429  if (match == NULL
430  || strcasestr_local(sc->name, match)
431  || strcasestr_local(sc->title, match)) {
432  draw_one_high_score(sc, scorebuf, sizeof(scorebuf));
433  i++;
434  } else
435  continue;
436 
437  if (op == NULL)
438  LOG(llevDebug, "%s\n", scorebuf);
439  else
441 
442  }
443  close_and_delete(fp, comp);
444 }
char path[HUGE_BUF]
Definition: map.h:384
char killer[BIG_NAME]
Definition: hiscore.c:46
char title[BIG_NAME]
Definition: player.h:216
uint32 name_changed
Definition: player.h:184
#define BIG_NAME
Definition: define.h:88
char title[BIG_NAME]
Definition: hiscore.c:45
struct scr score
sint16 maxgrace
Definition: living.h:86
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
static score * add_score(score *new_score)
Definition: hiscore.c:217
Definition: hiscore.c:43
sint64 exp
Definition: living.h:88
static void put_score(const score *sc, char *buf, int size)
Definition: hiscore.c:121
void close_and_delete(FILE *fp, int compressed)
Definition: porting.c:748
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
void display_high_score(object *op, int max, const char *match)
Definition: hiscore.c:402
sint16 maxsp
Definition: living.h:84
char * name
Definition: map.h:349
static char * spool(char *bp, const char *error)
Definition: hiscore.c:65
sint16 maxhp
Definition: living.h:82
#define MSG_TYPE_ADMIN_HISCORE
Definition: newclient.h:416
int position
Definition: hiscore.c:50
struct mapdef * map
Definition: object.h:155
const char * name
Definition: object.h:167
int maxhp
Definition: hiscore.c:49
static char * draw_one_high_score(const score *sc, char *buf, int size)
Definition: hiscore.c:194
int maxsp
Definition: hiscore.c:49
struct pl * contr
Definition: object.h:134
static void copy_score(const score *sc1, score *sc2)
Definition: hiscore.c:97
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
sint64 exp
Definition: hiscore.c:47
#define MSG_TYPE_APPLY
Definition: newclient.h:330
#define MAX_BUF
Definition: define.h:81
#define MSG_TYPE_ADMIN
Definition: newclient.h:324
char own_title[MAX_NAME]
Definition: player.h:214
uint32 explore
Definition: player.h:187
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
char killer[BIG_NAME]
Definition: player.h:222
#define HIGHSCORE
Definition: config.h:586
const char * localdir
Definition: global.h:335
living stats
Definition: object.h:219
#define HIGHSCORE_LENGTH
Definition: config.h:594
char maplevel[BIG_NAME]
Definition: hiscore.c:48
struct Settings settings
Definition: init.c:48
#define MSG_TYPE_COMMAND
Definition: newclient.h:326
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:516
int maxgrace
Definition: hiscore.c:49
#define NDI_UNIQUE
Definition: newclient.h:219
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
const char * strcasestr_local(const char *s, const char *find)
Definition: porting.c:461
char name[BIG_NAME]
Definition: hiscore.c:44
#define FLAG_WAS_WIZ
Definition: define.h:530
#define MAX_NAME
Definition: define.h:87
char * strerror_local(int errnum, char *buf, size_t size)
Definition: porting.c:525
void check_score(object *op, int quiet)
Definition: hiscore.c:289
#define MSG_TYPE_COMMAND_ERROR
Definition: newclient.h:447
FILE * open_and_uncompress(const char *name, int flag, int *compressed)
Definition: porting.c:724
static score * get_score(char *bp)
Definition: hiscore.c:136