ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.22
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_2, rel-2_3
Changes since 1.21: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.20 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.18 *
4 root 1.20 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5     * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7 pippijn 1.18 *
8 root 1.22 * Crossfire TRT is free software: you can redistribute it and/or modify
9     * 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     * The authors can be reached via e-mail to <crossfire@schmorp.de>
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     assign (new_score.killer, op->contr->killer[0] ? op->contr->killer : "a dungeon collapse");
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