ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.24
Committed: Wed Apr 30 06:40:28 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_53
Changes since 1.23: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.23 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 pippijn 1.18 *
4 root 1.23 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.20 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7 pippijn 1.18 *
8 root 1.23 * Deliantra is free software: you can redistribute it and/or modify
9 root 1.22 * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation, either version 3 of the License, or
11     * (at your option) any later version.
12 pippijn 1.18 *
13 root 1.22 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17 pippijn 1.18 *
18 root 1.22 * You should have received a copy of the GNU General Public License
19     * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 root 1.20 *
21 root 1.23 * The authors can be reached via e-mail to <support@deliantra.net>
22 pippijn 1.18 */
23 elmex 1.1
24     #include <global.h>
25 root 1.14 #include <sproto.h>
26 elmex 1.1
27     /*
28     * The score structure is used when treating new high-scores
29     */
30 root 1.5 typedef struct scr
31     {
32 root 1.9 char name[64]; // name */
33     char title[64]; // Title */
34     char killer[64]; // name (+ title) or "quit" */
35     sint64 exp; // Experience */
36     char maplevel[128]; // Killed on what level */
37     int maxhp, maxsp, maxgrace; // Max hp, sp, grace when killed */
38     int position; // Position in the highscore list */
39 elmex 1.1 } score;
40    
41     /*
42     * spool works mostly like strtok(char *, ":"), but it can also
43     * log a specified error message if something goes wrong.
44     */
45 root 1.5 char *
46 root 1.19 spool (char *bp, const char *error)
47 root 1.5 {
48 elmex 1.1 static char *prev_pos = NULL;
49     char *next_pos;
50 root 1.5
51     if (bp == NULL)
52     {
53     if (prev_pos == NULL)
54     {
55     LOG (llevError, "Called spool (%s) with NULL without previous call.\n", error);
56     return NULL;
57     }
58     bp = prev_pos;
59     }
60     if (*bp == '\0')
61     {
62     LOG (llevError, "spool: End of line at %s\n", error);
63 elmex 1.1 return NULL;
64     }
65 root 1.5 if ((next_pos = strchr (bp, ':')) != NULL)
66     {
67     *next_pos = '\0';
68     prev_pos = next_pos + 1;
69     }
70     else
71 elmex 1.1 prev_pos = NULL;
72     return bp;
73     }
74    
75     /*
76     * Does what it says, copies the contents of the first score structure
77     * to the second one.
78     */
79 root 1.5 static void
80 root 1.6 copy_score (score *sc1, score *sc2)
81 root 1.5 {
82 root 1.7 assign (sc2->name , sc1->name);
83     assign (sc2->title , sc1->title);
84     assign (sc2->killer , sc1->killer);
85     assign (sc2->maplevel, sc1->maplevel);
86 root 1.5 sc2->exp = sc1->exp;
87     sc2->maxhp = sc1->maxhp;
88     sc2->maxsp = sc1->maxsp;
89     sc2->maxgrace = sc1->maxgrace;
90 elmex 1.1 }
91    
92     /*
93     * Writes the given score structure to a static buffer, and returns
94     * a pointer to it.
95     */
96 root 1.5 static char *
97 root 1.6 put_score (score *sc)
98 root 1.5 {
99 elmex 1.1 static char buf[MAX_BUF];
100 root 1.5
101 root 1.12 sprintf (buf, "%s:%s:%" PRId64 ":%s:%s:%d:%d:%d",
102     sc->name, sc->title, (sint64)sc->exp, sc->killer,
103     sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
104 elmex 1.1 return buf;
105     }
106    
107     /*
108     * The oposite of put_score, get_score reads from the given buffer into
109     * a static score structure, and returns a pointer to it.
110     */
111    
112 root 1.5 static score *
113     get_score (char *bp)
114     {
115 elmex 1.1 static score sc;
116     char *cp;
117    
118 root 1.5 if ((cp = strchr (bp, '\n')) != NULL)
119     *cp = '\0';
120 elmex 1.1
121 root 1.5 if ((cp = spool (bp, "name")) == NULL)
122 root 1.7 return 0;
123    
124     assign (sc.name, cp);
125    
126     if ((cp = spool (0, "title")) == NULL)
127     return 0;
128    
129     assign (sc.title, cp);
130    
131     if ((cp = spool (0, "score")) == NULL)
132     return 0;
133 elmex 1.1
134 root 1.12 sscanf (cp, "%" SCNd64, &sc.exp);
135 root 1.5
136 root 1.7 if ((cp = spool (0, "killer")) == NULL)
137     return 0;
138    
139     assign (sc.killer, cp);
140    
141     if ((cp = spool (0, "map")) == NULL)
142     return 0;
143    
144     assign (sc.maplevel, cp);
145    
146     if ((cp = spool (0, "maxhp")) == NULL)
147     return 0;
148 elmex 1.1
149 root 1.5 sscanf (cp, "%d", &sc.maxhp);
150 elmex 1.1
151 root 1.7 if ((cp = spool (0, "maxsp")) == NULL)
152     return 0;
153    
154 root 1.5 sscanf (cp, "%d", &sc.maxsp);
155 elmex 1.1
156 root 1.7 if ((cp = spool (0, "maxgrace")) == NULL)
157     return 0;
158    
159 root 1.5 sscanf (cp, "%d", &sc.maxgrace);
160 elmex 1.1 return &sc;
161     }
162    
163 root 1.5 static char *
164 root 1.6 draw_one_high_score (score *sc)
165 root 1.5 {
166     static char retbuf[MAX_BUF];
167    
168     if (!strncmp (sc->killer, "quit", MAX_NAME))
169     sprintf (retbuf, "%3d %10lld %s the %s quit the game on map %s [%d][%d][%d].",
170     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
171     else if (!strncmp (sc->killer, "left", MAX_NAME))
172     sprintf (retbuf, "%3d %10lld %s the %s left the game on map %s [%d][%d][%d].",
173     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
174     else
175     sprintf (retbuf, "%3d %10lld %s the %s was killed by %s on map %s [%d][%d][%d].",
176     sc->position, (long long) sc->exp, sc->name, sc->title, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
177     return retbuf;
178     }
179 elmex 1.1
180     /*
181     * add_score() adds the given score-structure to the high-score list, but
182     * only if it was good enough to deserve a place.
183     */
184    
185 root 1.5 static score *
186 root 1.6 add_score (score *new_score)
187 root 1.5 {
188 elmex 1.1 FILE *fp;
189     static score old_score;
190 root 1.5 score *tmp_score, pscore[HIGHSCORE_LENGTH];
191 elmex 1.1 char buf[MAX_BUF], filename[MAX_BUF], *bp;
192 root 1.5 int nrofscores = 0, flag = 0, i, comp;
193    
194     new_score->position = HIGHSCORE_LENGTH + 1;
195     old_score.position = -1;
196     sprintf (filename, "%s/%s", settings.localdir, HIGHSCORE);
197     if ((fp = open_and_uncompress (filename, 1, &comp)) != NULL)
198     {
199     while (fgets (buf, MAX_BUF, fp) != NULL && nrofscores < HIGHSCORE_LENGTH)
200     {
201     if ((tmp_score = get_score (buf)) == NULL)
202     break;
203     if (!flag && new_score->exp >= tmp_score->exp)
204     {
205     copy_score (new_score, &pscore[nrofscores]);
206     new_score->position = nrofscores;
207     flag = 1;
208     if (++nrofscores >= HIGHSCORE_LENGTH)
209     break;
210     }
211     if (!strcmp (new_score->name, tmp_score->name))
212     { /* Another entry */
213     copy_score (tmp_score, &old_score);
214     old_score.position = nrofscores;
215     if (flag)
216     continue;
217     }
218     copy_score (tmp_score, &pscore[nrofscores++]);
219     }
220     close_and_delete (fp, comp);
221     }
222     if (old_score.position != -1 && old_score.exp >= new_score->exp)
223     return &old_score; /* Did not beat old score */
224     if (!flag && nrofscores < HIGHSCORE_LENGTH)
225     copy_score (new_score, &pscore[nrofscores++]);
226     if ((fp = fopen (filename, "w")) == NULL)
227     {
228     LOG (llevError, "Cannot write to highscore file %s: %s\n", filename, strerror (errno));
229     return NULL;
230     }
231     for (i = 0; i < nrofscores; i++)
232     {
233     bp = put_score (&pscore[i]);
234     fprintf (fp, "%s\n", bp);
235     }
236     fclose (fp);
237     if (flag)
238     {
239    
240 elmex 1.1 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
241     hiscore-list */
242 root 1.5 if (old_score.position == -1)
243     return new_score;
244     return &old_score;
245     }
246     new_score->position = -1;
247     if (old_score.position != -1)
248 elmex 1.1 return &old_score;
249 root 1.5 if (nrofscores)
250     {
251     copy_score (&pscore[nrofscores - 1], &old_score);
252     return &old_score;
253     }
254     LOG (llevError, "Highscore error.\n");
255 elmex 1.1 return NULL;
256     }
257    
258 root 1.5 void
259     check_score (object *op)
260     {
261     score new_score;
262     score *old_score;
263    
264     if (op->stats.exp == 0)
265     return;
266    
267 root 1.7 assign (new_score.name, op->name);
268     assign (new_score.title, op->title.length () ? &op->title : op->contr->title);
269 root 1.24 assign (new_score.killer, op->contr->killer_name ());
270 root 1.15 assign (new_score.maplevel, op->map ? op->map->name ? &op->map->name : &op->map->path : "");
271 root 1.7
272     new_score.exp = op->stats.exp;
273     new_score.maxhp = op->stats.maxhp;
274     new_score.maxsp = op->stats.maxsp;
275     new_score.maxgrace = op->stats.maxgrace;
276    
277 root 1.5 if ((old_score = add_score (&new_score)) == NULL)
278     {
279     new_draw_info (NDI_UNIQUE, 0, op, "Error in the highscore list.");
280     return;
281     }
282 root 1.7
283 root 1.5 if (new_score.position == -1)
284     {
285     new_score.position = HIGHSCORE_LENGTH + 1; /* Not strictly correct... */
286     if (!strcmp (old_score->name, new_score.name))
287     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last highscore:");
288     else
289     new_draw_info (NDI_UNIQUE, 0, op, "You didn't enter the highscore list:");
290     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
291     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
292     return;
293     }
294 root 1.7
295 root 1.5 if (old_score->exp >= new_score.exp)
296     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last score:");
297     else
298     new_draw_info (NDI_UNIQUE, 0, op, "You beat your last score:");
299 elmex 1.1
300 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
301     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
302 elmex 1.1 }
303    
304     /* displays the high score file. object is the calling object
305     * (null if being called via command line.) max is the maximum
306     * number of scores to display. match, if set, is the name or class
307     * to match to.
308     */
309    
310 root 1.5 void
311     display_high_score (object *op, int max, const char *match)
312     {
313     const size_t maxchar = 80;
314     FILE *fp;
315     char buf[MAX_BUF], *scorebuf, *bp, *cp;
316     int i = 0, j = 0, comp;
317     score *sc;
318    
319     sprintf (buf, "%s/%s", settings.localdir, HIGHSCORE);
320     if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
321     {
322     LOG (llevError, "Cannot open highscore file %s: %s\n", buf, strerror (errno));
323     if (op != NULL)
324     new_draw_info (NDI_UNIQUE, 0, op, "There is no highscore file.");
325     return;
326     }
327 root 1.21
328 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, "Nr Score Who [max hp][max sp][max grace]");
329    
330     while (fgets (buf, MAX_BUF, fp) != NULL)
331     {
332     if (j >= HIGHSCORE_LENGTH || i >= (max - 1))
333     break;
334 root 1.21
335 root 1.5 if ((sc = get_score (buf)) == NULL)
336     break;
337 root 1.21
338 root 1.5 sc->position = ++j;
339     if (match == NULL)
340     {
341     scorebuf = draw_one_high_score (sc);
342     i++;
343     }
344     else
345     {
346     if (!strcasecmp (sc->name, match) || !strcasecmp (sc->title, match))
347     {
348     scorebuf = draw_one_high_score (sc);
349     i++;
350 root 1.2 }
351 root 1.5 else
352     continue;
353 root 1.2 }
354 root 1.21
355 root 1.5 /* Replaced what seemed to an overly complicated word wrap method
356     * still word wraps, but assumes at most 2 lines of data.
357     * mw - 2-12-97
358     */
359 root 1.7 assign (buf, scorebuf);
360    
361 root 1.5 cp = buf;
362     while (strlen (cp) > maxchar)
363     {
364     bp = cp + maxchar - 1;
365     while (*bp != ' ' && bp > cp)
366     bp--;
367     *bp = '\0';
368 root 1.7
369 root 1.5 if (op == NULL)
370 root 1.7 LOG (llevDebug, "%s\n", cp);
371 root 1.5 else
372 root 1.7 new_draw_info (NDI_UNIQUE, 0, op, cp);
373    
374 root 1.5 sprintf (buf, " %s", bp + 1);
375     cp = buf;
376     i++;
377 root 1.2 }
378 root 1.7
379 root 1.5 if (op == NULL)
380     LOG (llevDebug, "%s\n", buf);
381     else
382     new_draw_info (NDI_UNIQUE, 0, op, buf);
383 elmex 1.1 }
384 root 1.7
385 root 1.5 close_and_delete (fp, comp);
386 elmex 1.1 }
387 root 1.21