#include "c_includ.h" /* * cbzone_scores.c * -- Todd W Mummert, December 1990, CMU * * RCS Info * $Header: c_scores.c,v 1.1 91/01/12 02:03:36 mummert Locked $ * * Derived from work I did on our local version of golddig. Thanks * to Bennet Yee (CMU) for the flock() code. */ /* * Okay, let's try to clean this up. The following things may * happen: * score < 0: then the user is just asking for scores. the score * file should not be changed. * score >= 0: score is either valid, for practice, or played by an * old version. In all cases, the file should change. * * file status: * can be not there * available for reading * available for both reading and writing * not there but can be written * * we know the following: * whether the game was for practice only. * any scores in the score file WILL be in the correct order. * * the format of the score file is as follows: * Version identification * number of games played, followed by number of valid games * scores, one per line w/uid and player's name * * how should we go about comparing users? * use getuid and cuserid...check both of them since we may be * saving scores over a distributed file system * * if the game was scoresonly, print to tty if opt->output allows; * else print to X screen. */ extern int errno; int x, y, ydelta; void xprintf(s, output) /* print to the X screen */ char* s; Bool output; { printstring (x, y, s, strlen(s)); y += ydelta; } void nprintf(s, output) /* print to the parent tty */ char* s; Bool output; { if (output) printf("%s\n", s); } void printandwait(s, c) /* print a string and then */ char* s; /* wait for a character c */ char c; /* to be pressed. If c==0 */ { /* then any keypress will */ y += ydelta; /* do. */ xprintf(s, True); waitforkey(c); } LONG scores(score) LONG score; { struct score_struct { LONG score; int uid; char name[50]; struct score_struct *next; } player, *current, *top_score, *prev_score; FILE *sfile; char buf[100]; char version[100]; char *login, *getlogin(); int i; Font fontid; int numgame = 0; int numscore = 0; int numscoreable = 0; int player_scores = 0; int tries = MAX_RETRIES; Bool score_printed = False; Bool scoresonly = False; Bool wrong_version = False; Bool file_readable = False; Bool file_writeable = False; Bool practice = opt->practice; struct passwd* pw; void (*p)(); version[0] ='\0'; if (score < 0) { practice = True; scoresonly = True; p = nprintf; } else { p = xprintf; gprsetclippingactive(False); fontid = gprloadfontfile(GAMEOVERFONT); gprsettextfont(fontid); printstring (165, 300, "GAME OVER", 9); fontid = gprloadfontfile(GENERALFONT); gprsettextfont(fontid); printstring (165, 320, "1986 JSR", 8); flushwindow(); sleep(1); clearentirescreen(); } x = 350; y = 100; ydelta = 15; sprintf(buf,"%s%s",TANKDIR,SCOREFILE); sfile = fopen(buf,"r"); /* just check if it is there */ if (sfile == NULL) { p("Score file not readable.", opt->output); if (scoresonly) return 1; else { p("Will try and create new scorefile.", opt->output); } } else file_readable = True; if (!scoresonly) { (void) signal(SIGINT, SIG_IGN); /* no leaving this routine */ (void) signal(SIGHUP, SIG_IGN); /* no how, no way */ file_writeable = True; if (sfile != NULL) fclose(sfile); retry: if (file_readable) sfile = fopen(buf,"r+"); /* okay, now open for update */ else sfile = fopen(buf,"w"); if (sfile != NULL) { if (flock(fileno(sfile),LOCK_EX) < 0) { if (errno == EWOULDBLOCK && AFS) { fclose(sfile); sleep(AFS_SLEEP); if (tries--) goto retry; } p("File not lockable, scores will not be saved.", opt->output); file_writeable = False; } } else { p("File not writeable, scores will not be saved.", opt->output); file_writeable = False; } /* okay, it's possible we could have closed the file and never * reopened it. Also, we just may not have been able to lock * it. In either case, lets close it again and then open only * for reading. */ if (file_readable && !file_writeable) { if (sfile != NULL) fclose(sfile); if ((sfile = fopen(buf, "r")) == NULL) file_readable = False; } } if (!file_readable && !file_writeable) { p("Scorefile not readable or writeable...Goodbye!", opt->output); (void) signal(SIGINT, SIG_DFL); /* this would probably happen */ (void) signal(SIGHUP, SIG_DFL); /* on exit anyway */ if (!scoresonly) printandwait("Press any key to continue...", 0); return 1; } if (file_readable) { if(fgets(version,200,sfile) && file_writeable) { version[strlen(version)-1] = '\0'; /* strip the newline */ if (strcmp(version,VERSION)) { wrong_version = True; p("Incorrect version played for this scorefile.", opt->output); p("Score not valid for inclusion.", opt->output); sprintf(buf,"Your version is \"%s\", while",VERSION); p(buf, opt->output); sprintf(buf, " the scorefile version is \"%s\"", version); p(buf, opt->output); } } if (*version == '\0') strcpy(version,VERSION); if(fgets(buf,200,sfile)) sscanf(buf,"%d %d",&numgame,&numscoreable); top_score = (struct score_struct*) malloc(sizeof(struct score_struct)); current = top_score; while(fgets(buf,200,sfile) && numscore < NUMHIGH) if (sscanf(buf,"%d %d %[^\n]",¤t->score, ¤t->uid,current->name) != 3) { p("Invalid line in score file...Skipping.", opt->output); } else { current->next = (struct score_struct*) malloc(sizeof(struct score_struct)); prev_score = current; current = current->next; numscore ++; } } if (numscore) prev_score->next = NULL; else top_score = NULL; if (numgame < numscoreable) numgame = numscoreable; if (!scoresonly) { /* try to get it from the passwd file first, just in case this * person su'd from another acct. */ player.uid = getuid(); player.score = score; pw = getpwuid(player.uid); if (pw == NULL) if ((login = getlogin()) != NULL) strcpy(player.name, login); else { p("Can't find out who you are....bye.", opt->output); fclose(sfile); (void) signal(SIGINT, SIG_DFL); (void) signal(SIGHUP, SIG_DFL); if (!scoresonly) printandwait("Press any key to continue...", 0); return 1; } else strcpy(player.name, pw->pw_name); if (numscore < NUMHIGH || player.score > prev_score->score) { score_printed = True; numscore++; for (current = top_score; current != NULL && player.score <= current->score; prev_score = current, current = current->next); player.next = current; if (current == top_score) top_score = &player; else prev_score->next = &player; } numgame++; if (!practice && !wrong_version) numscoreable++; } sprintf(buf, "High scores after %d games, %d scoreable:", numgame, numscoreable); p(buf, opt->output); current = top_score; while (current != NULL) { if (current == &player) *buf = '>'; else *buf = ' '; sprintf(buf+1, "%-20s %8d", current->name, current->score); p(buf, opt->output); if (((wrong_version || practice) && current==&player) || (current->uid==player.uid && !strcmp(player.name, current->name) && ++player_scores>INDIVIDUAL_SCORES)) { numscore--; if (current == top_score) top_score = current->next; else prev_score->next = current->next; } else prev_score = current; current = current->next; } if (!scoresonly && !score_printed) { p("...", opt->output); sprintf(buf, ">%-20s %8d", player.name,player.score); p(buf, opt->output); } if (file_writeable) { rewind(sfile); fprintf(sfile,"%s\n",version); fprintf(sfile,"%d %d\n",numgame,numscoreable); for(current = top_score, i = 0; current != NULL && i < NUMHIGH; current = current->next, i++) fprintf(sfile,"%d %d %s\n",current->score,current->uid,current->name); if (practice) { y += ydelta; p("Your game was for practice only...", opt->output); p("For a valid score use: cbzone [-q] [-d 0-5]", opt->output); } } fclose(sfile); (void) signal(SIGINT, SIG_DFL); (void) signal(SIGHUP, SIG_DFL); if (!scoresonly) printandwait("Press any key when ready...", 0); return 0; }