Atrinik Server 2.5
server/hiscore.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00034 typedef struct scr
00035 {
00037     char name[BIG_NAME];
00038 
00040     char title[BIG_NAME];
00041 
00043     char killer[BIG_NAME];
00044 
00046     char maplevel[MAX_BUF];
00047 
00049     uint64 exp;
00050 
00052     int maxhp, maxsp, maxgrace;
00053 
00055     int position;
00056 } score;
00057 
00060 typedef struct
00061 {
00063     char fname[MAX_BUF];
00064 
00066     score entry[HIGHSCORE_LENGTH];
00067 } score_table;
00068 
00071 static score_table hiscore_table;
00072 
00078 static void put_score(const score *sc, char *buf, int size)
00079 {
00080     snprintf(buf, size, "%s:%s:%"FMT64U":%s:%s:%d:%d:%d", sc->name, sc->title, sc->exp, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00081 }
00082 
00086 static void hiscore_save(const score_table *table)
00087 {
00088     FILE *fp;
00089     size_t i;
00090     char buf[MAX_BUF];
00091 
00092     LOG(llevDebug, "Writing highscore file %s\n", table->fname);
00093     fp = fopen(table->fname, "w");
00094 
00095     if (!fp)
00096     {
00097         LOG(llevBug, "Cannot create highscore file %s: %s\n", table->fname, strerror_local(errno));
00098         return;
00099     }
00100 
00101     for (i = 0; i < HIGHSCORE_LENGTH; i++)
00102     {
00103         if (table->entry[i].name[0] == '\0')
00104         {
00105             break;
00106         }
00107 
00108         put_score(&table->entry[i], buf, sizeof(buf));
00109         fprintf(fp, "%s\n", buf);
00110     }
00111 
00112     if (ferror(fp))
00113     {
00114         LOG(llevBug, "Cannot write to highscore file %s: %s\n", table->fname, strerror_local(errno));
00115         fclose(fp);
00116     }
00117     else if (fclose(fp) != 0)
00118     {
00119         LOG(llevBug, "Cannot write to highscore file %s: %s\n", table->fname, strerror_local(errno));
00120     }
00121 }
00122 
00129 static int get_score(char *bp, score *sc)
00130 {
00131     char *cp, *tmp[8];
00132 
00133     cp = strchr(bp, '\n');
00134 
00135     if (cp)
00136     {
00137         *cp = '\0';
00138     }
00139 
00140     if (split_string(bp, tmp, 8, ':') != 8)
00141     {
00142         return 0;
00143     }
00144 
00145     strncpy(sc->name, tmp[0], sizeof(sc->name));
00146     sc->name[sizeof(sc->name) - 1] = '\0';
00147 
00148     strncpy(sc->title, tmp[1], sizeof(sc->title));
00149     sc->title[sizeof(sc->title) - 1] = '\0';
00150 
00151     sscanf(tmp[2], "%"FMT64U, &sc->exp);
00152 
00153     strncpy(sc->killer, tmp[3], sizeof(sc->killer));
00154     sc->killer[sizeof(sc->killer) - 1] = '\0';
00155 
00156     strncpy(sc->maplevel, tmp[4], sizeof(sc->maplevel));
00157     sc->maplevel[sizeof(sc->maplevel) - 1] = '\0';
00158 
00159     sscanf(tmp[5], "%d", &sc->maxhp);
00160     sscanf(tmp[6], "%d", &sc->maxsp);
00161     sscanf(tmp[7], "%d", &sc->maxgrace);
00162     return 1;
00163 }
00164 
00171 static char *draw_one_high_score(const score *sc, char *buf, size_t size)
00172 {
00173     if (sc->killer[0] == '\0')
00174     {
00175         snprintf(buf, size, "<green>%3d</green> %s <green>%s</green> the %s (%s) <%d><%d><%d>.", sc->position, format_number_comma(sc->exp), sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00176     }
00177     else
00178     {
00179         const char *s1, *s2;
00180 
00181         if (!strcmp(sc->killer, "left"))
00182         {
00183             s1 = sc->killer;
00184             s2 = "the game";
00185         }
00186         else
00187         {
00188             s1 = "was killed by";
00189             s2 = sc->killer;
00190         }
00191 
00192         snprintf(buf, size, "<green>%3d</green> %s <green>%s</green> the %s %s %s on map %s <%d><%d><%d>.", sc->position, format_number_comma(sc->exp), sc->name, sc->title, s1, s2, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
00193     }
00194 
00195     return buf;
00196 }
00197 
00204 static void add_score(score_table *table, score *new_score, score *old_score)
00205 {
00206     size_t i;
00207 
00208     new_score->position = HIGHSCORE_LENGTH + 1;
00209     memset(old_score, 0, sizeof(*old_score));
00210     old_score->position = -1;
00211 
00212     /* Find existing entry by name */
00213     for (i = 0; i < HIGHSCORE_LENGTH; i++)
00214     {
00215         if (table->entry[i].name[0] == '\0')
00216         {
00217             table->entry[i] = *new_score;
00218             table->entry[i].position = i + 1;
00219             break;
00220         }
00221 
00222         if (strcmp(new_score->name, table->entry[i].name) == 0)
00223         {
00224             *old_score = table->entry[i];
00225 
00226             if (table->entry[i].exp <= new_score->exp)
00227             {
00228                 table->entry[i] = *new_score;
00229                 table->entry[i].position = i + 1;
00230             }
00231 
00232             break;
00233         }
00234     }
00235 
00236     /* Entry for unknown name */
00237     if (i >= HIGHSCORE_LENGTH)
00238     {
00239         /* New exp is less than lowest hiscore entry => drop */
00240         if (new_score->exp < table->entry[i - 1].exp)
00241         {
00242             return;
00243         }
00244 
00245         /* New exp is not less than lowest hiscore entry => add */
00246         i--;
00247         table->entry[i] = *new_score;
00248         table->entry[i].position = i + 1;
00249     }
00250 
00251     /* Move entry to correct position */
00252     while (i > 0 && new_score->exp >= table->entry[i - 1].exp)
00253     {
00254         score tmp;
00255 
00256         tmp = table->entry[i - 1];
00257         table->entry[i - 1] = table->entry[i];
00258         table->entry[i] = tmp;
00259 
00260         table->entry[i - 1].position = i;
00261         table->entry[i].position = i + 1;
00262 
00263         i--;
00264     }
00265 
00266     new_score->position = table->entry[i].position;
00267     hiscore_save(table);
00268 }
00269 
00273 static void hiscore_load(score_table *table)
00274 {
00275     FILE *fp;
00276     size_t i = 0;
00277 
00278     fp = fopen(table->fname, "r");
00279 
00280     if (!fp)
00281     {
00282         if (errno == ENOENT)
00283         {
00284             LOG(llevDebug, "Highscore file %s does not exist\n", table->fname);
00285         }
00286         else
00287         {
00288             LOG(llevBug, "Cannot open highscore file %s: %s\n", table->fname, strerror_local(errno));
00289         }
00290     }
00291     else
00292     {
00293         LOG(llevDebug, "Reading highscore file %s\n", table->fname);
00294 
00295         while (i < HIGHSCORE_LENGTH)
00296         {
00297             char buf[MAX_BUF];
00298 
00299             if (!fgets(buf, sizeof(buf), fp))
00300             {
00301                 break;
00302             }
00303 
00304             if (!get_score(buf, &table->entry[i]))
00305             {
00306                 break;
00307             }
00308 
00309             table->entry[i].position = i + 1;
00310             i++;
00311         }
00312 
00313         fclose(fp);
00314     }
00315 
00316     while (i < HIGHSCORE_LENGTH)
00317     {
00318         memset(&table->entry[i], 0, sizeof(table->entry[i]));
00319         table->entry[i].position = i + 1;
00320         i++;
00321     }
00322 }
00323 
00326 void hiscore_init()
00327 {
00328     snprintf(hiscore_table.fname, sizeof(hiscore_table.fname), "%s/%s", settings.localdir, HIGHSCORE);
00329     hiscore_load(&hiscore_table);
00330 }
00331 
00338 void hiscore_check(object *op, int quiet)
00339 {
00340     score new_score, old_score;
00341     char bufscore[MAX_BUF], race[MAX_BUF];
00342     const char *message;
00343 
00344     if (!op->stats.exp)
00345     {
00346         return;
00347     }
00348 
00349     if (QUERY_FLAG(op, FLAG_WAS_WIZ))
00350     {
00351         if (!quiet)
00352         {
00353             new_draw_info(0, COLOR_WHITE, op, "Since you have been in wizard mode, you can't enter the high-score list.");
00354         }
00355 
00356         return;
00357     }
00358 
00359     strncpy(new_score.name, op->name, sizeof(new_score.name));
00360     new_score.name[sizeof(new_score.name) - 1] = '\0';
00361 
00362     strncpy(new_score.title, player_get_race_class(op, race, sizeof(race)), sizeof(new_score.title));
00363     new_score.title[sizeof(new_score.title) - 1] = '\0';
00364 
00365     strncpy(new_score.killer, CONTR(op)->killer, sizeof(new_score.killer));
00366     new_score.killer[sizeof(new_score.killer) - 1] = '\0';
00367 
00368     new_score.exp = calculate_total_exp(op);
00369 
00370     if (op->map == NULL)
00371     {
00372         *new_score.maplevel = '\0';
00373     }
00374     else
00375     {
00376         size_t i;
00377 
00378         strncpy(new_score.maplevel, op->map->name ? op->map->name : op->map->path, sizeof(new_score.maplevel) - 1);
00379         new_score.maplevel[sizeof(new_score.maplevel) - 1] = '\0';
00380 
00381         /* Replace ':' in the map name with ' ' so it doesn't break the loading. */
00382         for (i = 0; new_score.maplevel[i]; i++)
00383         {
00384             if (new_score.maplevel[i] == ':')
00385             {
00386                 new_score.maplevel[i] = ' ';
00387             }
00388         }
00389     }
00390 
00391     new_score.maxhp = (int) op->stats.maxhp;
00392     new_score.maxsp = (int) op->stats.maxsp;
00393     new_score.maxgrace = (int) op->stats.maxgrace;
00394     add_score(&hiscore_table, &new_score, &old_score);
00395 
00396     /* Everything below here is just related to print messages
00397      * to the player.  If quiet is set, we can just return
00398      * now. */
00399     if (quiet)
00400     {
00401         return;
00402     }
00403 
00404     if (old_score.position == -1)
00405     {
00406         if (new_score.position > HIGHSCORE_LENGTH)
00407         {
00408             message = "You didn't enter the highscore list:";
00409         }
00410         else
00411         {
00412             message = "You entered the highscore list:";
00413         }
00414     }
00415     else
00416     {
00417         if (new_score.position > HIGHSCORE_LENGTH)
00418         {
00419             message = "You left the highscore list:";
00420         }
00421         else if (new_score.exp  > old_score.exp)
00422         {
00423             message = "You beat your last score:";
00424         }
00425         else
00426         {
00427             message = "You didn't beat your last score:";
00428         }
00429     }
00430 
00431     new_draw_info(0, COLOR_WHITE, op, message);
00432 
00433     if (old_score.position != -1)
00434     {
00435         new_draw_info(0, COLOR_WHITE, op, draw_one_high_score(&old_score, bufscore, sizeof(bufscore)));
00436     }
00437 
00438     new_draw_info(0, COLOR_WHITE, op, draw_one_high_score(&new_score, bufscore, sizeof(bufscore)));
00439 }
00440 
00447 void hiscore_display(object *op, int max, const char *match)
00448 {
00449     int printed_entries = 0;
00450     size_t j;
00451 
00452     new_draw_info(0, COLOR_WHITE, op, "Nr    Score   Who <max hp><max sp><max grace>");
00453 
00454     for (j = 0; j < HIGHSCORE_LENGTH && hiscore_table.entry[j].name[0] != '\0' && printed_entries < max; j++)
00455     {
00456         char scorebuf[MAX_BUF];
00457 
00458         if (match && !strcasestr_local(hiscore_table.entry[j].name, match) && !strcasestr_local(hiscore_table.entry[j].title, match))
00459         {
00460             continue;
00461         }
00462 
00463         draw_one_high_score(&hiscore_table.entry[j], scorebuf, sizeof(scorebuf));
00464         printed_entries++;
00465 
00466         if (op == NULL)
00467         {
00468             LOG(llevDebug, "%s\n", scorebuf);
00469         }
00470         else
00471         {
00472             new_draw_info(0, COLOR_WHITE, op, scorebuf);
00473         }
00474     }
00475 }