Crossfire Server, Branch 1.12  R12190
hiscore.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_hiscore_c =
00003  *   "$Id: hiscore.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #ifndef __CEXTRACT__
00036 #include <sproto.h>
00037 #endif
00038 
00043 typedef struct scr {
00044     char name[BIG_NAME];      
00045     char title[BIG_NAME];     
00046     char killer[BIG_NAME];    
00047     sint64 exp;               
00048     char maplevel[BIG_NAME];  
00049     int maxhp, maxsp, maxgrace; 
00050     int position;             
00051 } score;
00052 
00065 static char *spool(char *bp, const char *error) {
00066     static char *prev_pos = NULL;
00067     char *next_pos;
00068 
00069     if (bp == NULL) {
00070         if (prev_pos == NULL) {
00071             LOG(llevError, "Called spool (%s) with NULL without previous call.\n", error);
00072             return NULL;
00073         }
00074         bp = prev_pos;
00075     }
00076     if (*bp == '\0') {
00077         LOG(llevError, "spool: End of line at %s\n", error);
00078         return NULL;
00079     }
00080     if ((next_pos = strchr(bp, ':')) != NULL) {
00081         *next_pos = '\0';
00082         prev_pos = next_pos+1;
00083     } else
00084         prev_pos = NULL;
00085     return bp;
00086 }
00087 
00097 static void copy_score(const score *sc1, score *sc2) {
00098     strncpy(sc2->name, sc1->name, BIG_NAME);
00099     sc2->name[BIG_NAME-1] = '\0';
00100     strncpy(sc2->title, sc1->title, BIG_NAME);
00101     sc2->title[BIG_NAME-1] = '\0';
00102     strncpy(sc2->killer, sc1->killer, BIG_NAME);
00103     sc2->killer[BIG_NAME-1] = '\0';
00104     sc2->exp = sc1->exp;
00105     strcpy(sc2->maplevel, sc1->maplevel);
00106     sc2->maxhp = sc1->maxhp;
00107     sc2->maxsp = sc1->maxsp;
00108     sc2->maxgrace = sc1->maxgrace;
00109 }
00110 
00121 static void put_score(const score *sc, char *buf, int size) {
00122     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);
00123 }
00124 
00136 static score *get_score(char *bp) {
00137     static score sc;
00138     char *cp;
00139 
00140     if ((cp = strchr(bp, '\n')) != NULL)
00141         *cp = '\0';
00142 
00143     if ((cp = spool(bp, "name")) == NULL)
00144         return NULL;
00145     strncpy(sc.name, cp, BIG_NAME);
00146     sc.name[BIG_NAME-1] = '\0';
00147 
00148     if ((cp = spool(NULL, "title")) == NULL)
00149         return NULL;
00150     strncpy(sc.title, cp, BIG_NAME);
00151     sc.title[BIG_NAME-1] = '\0';
00152 
00153     if ((cp = spool(NULL, "score")) == NULL)
00154         return NULL;
00155 
00156     sscanf(cp, "%"FMT64, &sc.exp);
00157 
00158     if ((cp = spool(NULL, "killer")) == NULL)
00159         return NULL;
00160     strncpy(sc.killer, cp, BIG_NAME);
00161     sc.killer[BIG_NAME-1] = '\0';
00162 
00163     if ((cp = spool(NULL, "map")) == NULL)
00164         return NULL;
00165     strncpy(sc.maplevel, cp, BIG_NAME);
00166     sc.maplevel[BIG_NAME-1] = '\0';
00167 
00168     if ((cp = spool(NULL, "maxhp")) == NULL)
00169         return NULL;
00170     sscanf(cp, "%d", &sc.maxhp);
00171 
00172     if ((cp = spool(NULL, "maxsp")) == NULL)
00173         return NULL;
00174     sscanf(cp, "%d", &sc.maxsp);
00175 
00176     if ((cp = spool(NULL, "maxgrace")) == NULL)
00177         return NULL;
00178     sscanf(cp, "%d", &sc.maxgrace);
00179     return &sc;
00180 }
00181 
00194 static char *draw_one_high_score(const score *sc, char *buf, int size) {
00195     if (!strncmp(sc->killer, "quit", MAX_NAME))
00196         snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s quit the game on map %s <%d><%d><%d>.",
00197                  sc->position, sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00198     else if (!strncmp(sc->killer, "left", MAX_NAME))
00199         snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s left the game on map %s <%d><%d><%d>.",
00200                  sc->position, sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00201     else
00202         snprintf(buf, size, "[Fixed]%3d %10"FMT64"[Print] %s the %s was killed by %s on map %s <%d><%d><%d>.",
00203                  sc->position, sc->exp, sc->name, sc->title, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00204     return buf;
00205 }
00206 
00217 static score *add_score(score *new_score) {
00218     FILE *fp;
00219     static score old_score;
00220     score *tmp_score, pscore[HIGHSCORE_LENGTH];
00221     char buf[MAX_BUF], filename[MAX_BUF], bp[MAX_BUF];
00222     int nrofscores = 0, flag = 0, i, comp;
00223 
00224     new_score->position = HIGHSCORE_LENGTH+1;
00225     old_score.position = -1;
00226     snprintf(filename, sizeof(filename), "%s/%s", settings.localdir, HIGHSCORE);
00227     if ((fp = open_and_uncompress(filename, 1, &comp)) != NULL) {
00228         while (fgets(buf, MAX_BUF, fp) != NULL && nrofscores < HIGHSCORE_LENGTH) {
00229             if ((tmp_score = get_score(buf)) == NULL)
00230                 break;
00231             if (!flag && new_score->exp >= tmp_score->exp) {
00232                 copy_score(new_score, &pscore[nrofscores]);
00233                 new_score->position = nrofscores;
00234                 flag = 1;
00235                 if (++nrofscores >= HIGHSCORE_LENGTH)
00236                     break;
00237             }
00238             if (!strcmp(new_score->name, tmp_score->name)) { /* Another entry */
00239                 copy_score(tmp_score, &old_score);
00240                 old_score.position = nrofscores;
00241                 if (flag)
00242                     continue;
00243             }
00244             copy_score(tmp_score, &pscore[nrofscores++]);
00245         }
00246         close_and_delete(fp, comp);
00247     }
00248     if (old_score.position != -1 && old_score.exp >= new_score->exp)
00249         return &old_score; /* Did not beat old score */
00250     if (!flag && nrofscores < HIGHSCORE_LENGTH)
00251         copy_score(new_score, &pscore[nrofscores++]);
00252     if ((fp = fopen(filename, "w")) == NULL) {
00253         LOG(llevError, "Cannot write to highscore file %s: %s\n", filename, strerror_local(errno, buf, sizeof(buf)));
00254         return NULL;
00255     }
00256     for (i = 0; i < nrofscores; i++) {
00257         put_score(&pscore[i], bp, sizeof(bp));
00258         fprintf(fp, "%s\n", bp);
00259     }
00260     fclose(fp);
00261     if (flag) {
00262         /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
00263            hiscore-list */
00264         if (old_score.position == -1)
00265             return new_score;
00266         return &old_score;
00267     }
00268     new_score->position = -1;
00269     if (old_score.position != -1)
00270         return &old_score;
00271     if (nrofscores) {
00272         copy_score(&pscore[nrofscores-1], &old_score);
00273         return &old_score;
00274     }
00275     LOG(llevError, "Highscore error.\n");
00276     return NULL;
00277 }
00278 
00289 void check_score(object *op, int quiet) {
00290     score new_score;
00291     score *old_score;
00292     char bufscore[MAX_BUF];
00293 
00294     if (op->stats.exp == 0)
00295         return;
00296 
00297     if (!op->contr->name_changed) {
00298         if (op->stats.exp > 0) {
00299             if (!quiet)
00300                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00301                               "As you haven't changed your name, you won't "
00302                               "get into the high-score list.", NULL);
00303         }
00304         return;
00305     }
00306     if (QUERY_FLAG(op, FLAG_WAS_WIZ)) {
00307         if (!quiet)
00308             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00309                           "Since you have been in wizard mode, "
00310                           "you can't enter the high-score list.", NULL);
00311         return;
00312     }
00313     if (op->contr->explore) {
00314         if (!quiet)
00315             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00316                           "Since you were in explore mode, "
00317                           "you can't enter the high-score list.", NULL);
00318         return;
00319     }
00320     if (!op->stats.exp) {
00321         if (!quiet)
00322             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
00323                           "You don't deserve to save your character yet.", NULL);
00324         return;
00325     }
00326 
00327     strncpy(new_score.name, op->name, BIG_NAME);
00328     new_score.name[BIG_NAME-1] = '\0';
00329     strncpy(new_score.title, op->contr->own_title, BIG_NAME);
00330     if (new_score.title[0] == '\0')
00331         strncpy(new_score.title, op->contr->title, BIG_NAME);
00332     new_score.title[BIG_NAME-1] = '\0';
00333     strncpy(new_score.killer, op->contr->killer, BIG_NAME);
00334     if (new_score.killer[0] == '\0')
00335         strcpy(new_score.killer, "a dungeon collapse");
00336     new_score.killer[BIG_NAME-1] = '\0';
00337     new_score.exp = op->stats.exp;
00338     if (op->map == NULL)
00339         *new_score.maplevel = '\0';
00340     else {
00341         strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME-1);
00342         new_score.maplevel[BIG_NAME-1] = '\0';
00343     }
00344     new_score.maxhp = (int)op->stats.maxhp;
00345     new_score.maxsp = (int)op->stats.maxsp;
00346     new_score.maxgrace = (int)op->stats.maxgrace;
00347     if ((old_score = add_score(&new_score)) == NULL) {
00348         if (!quiet)
00349             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00350                           "Error in the highscore list.", NULL);
00351         return;
00352     }
00353     /* Everything below here is just related to print messages
00354      * to the player.  If quiet is set, we can just return
00355      * now.
00356      */
00357     if (quiet)
00358         return;
00359 
00360     if (new_score.position == -1) {
00361         new_score.position = HIGHSCORE_LENGTH+1; /* Not strictly correct... */
00362 
00363         if (!strcmp(old_score->name, new_score.name))
00364             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00365                           "You didn't beat your last highscore:", NULL);
00366         else
00367             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00368                           "You didn't enter the highscore list:", NULL);
00369 
00370         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00371                       draw_one_high_score(old_score, bufscore, sizeof(bufscore)), NULL);
00372 
00373         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00374                       draw_one_high_score(&new_score, bufscore, sizeof(bufscore)), NULL);
00375         return;
00376     }
00377     if (old_score->exp >= new_score.exp)
00378         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00379                       "You didn't beat your last score:", NULL);
00380     else
00381         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00382                       "You beat your last score:", NULL);
00383 
00384     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00385                   draw_one_high_score(old_score, bufscore, sizeof(bufscore)), NULL);
00386     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00387                          draw_one_high_score(&new_score, bufscore, sizeof(bufscore)), NULL);
00388 }
00389 
00390 
00391 
00402 void display_high_score(object *op, int max, const char *match) {
00403     FILE *fp;
00404     char buf[MAX_BUF], scorebuf[MAX_BUF];
00405     int i = 0, j = 0, comp;
00406     score *sc;
00407 
00408     snprintf(buf, sizeof(buf), "%s/%s", settings.localdir, HIGHSCORE);
00409     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00410         char err[MAX_BUF];
00411 
00412         LOG(llevError, "Cannot open highscore file %s: %s\n", buf, strerror_local(errno, err, sizeof(err)));
00413         if (op != NULL)
00414             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
00415                           "There is no highscore file.", NULL);
00416         return;
00417     }
00418 
00419     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE,
00420                   "[Fixed]Nr    Score    Who <max hp><max sp><max grace>",
00421                   "Nr    Score    Who <max hp><max sp><max grace>");
00422 
00423     while (fgets(buf, MAX_BUF, fp) != NULL) {
00424         if (j >= HIGHSCORE_LENGTH || i >= (max-1))
00425             break;
00426         if ((sc = get_score(buf)) == NULL)
00427             break;
00428         sc->position = ++j;
00429         if (match == NULL
00430         || strcasestr_local(sc->name, match)
00431         || strcasestr_local(sc->title, match)) {
00432             draw_one_high_score(sc, scorebuf, sizeof(scorebuf));
00433             i++;
00434         } else
00435             continue;
00436 
00437         if (op == NULL)
00438             LOG(llevDebug, "%s\n", scorebuf);
00439         else
00440             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_HISCORE, scorebuf, NULL);
00441 
00442     }
00443     close_and_delete(fp, comp);
00444 }