ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.23
Committed: Thu Nov 8 19:43:27 2007 UTC (16 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_4, rel-2_5, rel-2_52, rel-2_32, rel-2_43, rel-2_42, rel-2_41
Changes since 1.22: +4 -4 lines
Log Message:
update copyrights and other minor stuff to deliantra

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra 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 *
13 * 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 *
18 * 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 *
21 * The authors can be reached via e-mail to <support@deliantra.net>
22 */
23
24 #include <global.h>
25 #include <sproto.h>
26
27 /*
28 * The score structure is used when treating new high-scores
29 */
30 typedef struct scr
31 {
32 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 } 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 char *
46 spool (char *bp, const char *error)
47 {
48 static char *prev_pos = NULL;
49 char *next_pos;
50
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 return NULL;
64 }
65 if ((next_pos = strchr (bp, ':')) != NULL)
66 {
67 *next_pos = '\0';
68 prev_pos = next_pos + 1;
69 }
70 else
71 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 static void
80 copy_score (score *sc1, score *sc2)
81 {
82 assign (sc2->name , sc1->name);
83 assign (sc2->title , sc1->title);
84 assign (sc2->killer , sc1->killer);
85 assign (sc2->maplevel, sc1->maplevel);
86 sc2->exp = sc1->exp;
87 sc2->maxhp = sc1->maxhp;
88 sc2->maxsp = sc1->maxsp;
89 sc2->maxgrace = sc1->maxgrace;
90 }
91
92 /*
93 * Writes the given score structure to a static buffer, and returns
94 * a pointer to it.
95 */
96 static char *
97 put_score (score *sc)
98 {
99 static char buf[MAX_BUF];
100
101 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 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 static score *
113 get_score (char *bp)
114 {
115 static score sc;
116 char *cp;
117
118 if ((cp = strchr (bp, '\n')) != NULL)
119 *cp = '\0';
120
121 if ((cp = spool (bp, "name")) == NULL)
122 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
134 sscanf (cp, "%" SCNd64, &sc.exp);
135
136 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
149 sscanf (cp, "%d", &sc.maxhp);
150
151 if ((cp = spool (0, "maxsp")) == NULL)
152 return 0;
153
154 sscanf (cp, "%d", &sc.maxsp);
155
156 if ((cp = spool (0, "maxgrace")) == NULL)
157 return 0;
158
159 sscanf (cp, "%d", &sc.maxgrace);
160 return &sc;
161 }
162
163 static char *
164 draw_one_high_score (score *sc)
165 {
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
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 static score *
186 add_score (score *new_score)
187 {
188 FILE *fp;
189 static score old_score;
190 score *tmp_score, pscore[HIGHSCORE_LENGTH];
191 char buf[MAX_BUF], filename[MAX_BUF], *bp;
192 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 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
241 hiscore-list */
242 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 return &old_score;
249 if (nrofscores)
250 {
251 copy_score (&pscore[nrofscores - 1], &old_score);
252 return &old_score;
253 }
254 LOG (llevError, "Highscore error.\n");
255 return NULL;
256 }
257
258 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 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 assign (new_score.maplevel, op->map ? op->map->name ? &op->map->name : &op->map->path : "");
271
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 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
283 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
295 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
300 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 }
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 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
328 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
335 if ((sc = get_score (buf)) == NULL)
336 break;
337
338 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 }
351 else
352 continue;
353 }
354
355 /* 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 assign (buf, scorebuf);
360
361 cp = buf;
362 while (strlen (cp) > maxchar)
363 {
364 bp = cp + maxchar - 1;
365 while (*bp != ' ' && bp > cp)
366 bp--;
367 *bp = '\0';
368
369 if (op == NULL)
370 LOG (llevDebug, "%s\n", cp);
371 else
372 new_draw_info (NDI_UNIQUE, 0, op, cp);
373
374 sprintf (buf, " %s", bp + 1);
375 cp = buf;
376 i++;
377 }
378
379 if (op == NULL)
380 LOG (llevDebug, "%s\n", buf);
381 else
382 new_draw_info (NDI_UNIQUE, 0, op, buf);
383 }
384
385 close_and_delete (fp, comp);
386 }
387