Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
Matthew Faltys | ed54ed59d4 | 3 months ago |
Matthew Faltys | bd8fd497c1 | 2 years ago |
Matthew Faltys | 7a2a7a5629 | 2 years ago |
Matthew Faltys | acd35ba734 | 2 years ago |
Matthew Faltys | b6f0768971 | 4 years ago |
Matthew Faltys | 7e2bf9949e | 5 years ago |
Matthew Faltys | ee891cd615 | 5 years ago |
Matthew Faltys | f79108ca31 | 5 years ago |
Matthew Faltys | 2439ca2771 | 5 years ago |
Matthew Faltys | 66b958d952 | 5 years ago |
Matthew Faltys | 55bc624128 | 5 years ago |
Matthew Faltys | 4fb58d628a | 5 years ago |
Matthew Faltys | cdafa1b2f3 | 5 years ago |
Matthew Faltys | 47f4886b48 | 5 years ago |
Matthew Faltys | 7a3357f7c5 | 5 years ago |
Matthew Faltys | f6cd22a3b9 | 5 years ago |
Matthew Faltys | 2dac08c098 | 5 years ago |
Matthew Faltys | d5f4aa988d | 5 years ago |
Matthew Faltys | a6a0e72a00 | 5 years ago |
Matthew Faltys | 39714170a1 | 5 years ago |
Matthew Faltys | 80a45f4736 | 5 years ago |
Matthew Faltys | a28a37c2e7 | 5 years ago |
Matthew Faltys | 99bc72707d | 5 years ago |
Matthew Faltys | 310b7b197f | 5 years ago |
Matthew Faltys | a8ad12cc31 | 5 years ago |
Matthew Faltys | 980ccd4ed0 | 5 years ago |
Matthew Faltys | b5ff188111 | 5 years ago |
Matthew Faltys | f8461b6854 | 5 years ago |
Matthew Faltys | 928848a5de | 5 years ago |
Matthew Faltys | 97aef57c93 | 5 years ago |
Matthew Faltys | e974ca4498 | 5 years ago |
Matthew Faltys | aa94fa4c19 | 5 years ago |
Matthew Faltys | e39fbeceef | 5 years ago |
Matthew Faltys | 56858a4aae | 5 years ago |
24 changed files with 1568 additions and 66 deletions
@ -0,0 +1,30 @@
|
||||
#!/bin/bash |
||||
HACKDIR=$1 |
||||
USERDIR=$2 |
||||
TTYRECCACHE=$3 |
||||
SOTWDUMPCACHE=$4 |
||||
|
||||
### |
||||
# clean old ttyrecs |
||||
### |
||||
cd /$HACKDIR/user/$USERDIR/ttyrec/ |
||||
ls -1tr | head -n -$TTYRECCACHE | xargs -d '\n' rm -f -- |
||||
|
||||
|
||||
### |
||||
# clean sotw files |
||||
### |
||||
cd /$HACKDIR/user/$USERDIR/sotw/ |
||||
|
||||
# there are always 4 symlinked files |
||||
# leave 5 of the latest dumps |
||||
DUMPFILES=$(expr $SOTWDUMPCACHE + 4) |
||||
|
||||
FILES=$(ls -1tr *.txt | head -n -$DUMPFILES) |
||||
|
||||
for f in $FILES; do |
||||
#test -h $f || echo "removing old file $f" |
||||
if [ ! -h $f ]; then |
||||
rm -rf $f |
||||
fi |
||||
done |
@ -0,0 +1,28 @@
|
||||
#!/bin/bash |
||||
|
||||
FILES=/sotw/* |
||||
USERDIR=$1 |
||||
HACKDIR=$2 |
||||
|
||||
# create initial dir if it does not exist |
||||
mkdir -p $USERDIR/sotw/ |
||||
|
||||
# link all game files to user dir |
||||
for f in $FILES |
||||
do |
||||
echo "linking $f" |
||||
ln -s $f $USERDIR/sotw/ |
||||
done |
||||
|
||||
# remove inital score file |
||||
rm -rf $USERDIR/sotw/scores.dat |
||||
# link in shared score file from hackdir |
||||
ln -s $HACKDIR/scores.dat $USERDIR/sotw/ |
||||
|
||||
## remove initial config file |
||||
rm -rf $USERDIR/sotw/swyrm.ini |
||||
## copy in sotw config file |
||||
cp /sotw/swyrm.ini $USERDIR/sotw/swyrm.ini |
||||
|
||||
# sed find and replace swyrm.ini to set users dir |
||||
sed -i "s:userdata_dir=:userdata_dir=${USERDIR}/sotw/:g" $USERDIR/sotw/swyrm.ini |
@ -0,0 +1,929 @@
|
||||
/* List the Nethack record file and the average score.
|
||||
Nethack 3.4-3.6, 3.3, 3.2 and 3.1 RECORD file formats supported |
||||
Output: stdout |
||||
|
||||
RECLIST [-f filename] [-u playername] [-p profession] [-r race] |
||||
[-n number] [-ns score] [-np interval] [-d] |
||||
[-ds date since] [-du date until] |
||||
[-g version] |
||||
|
||||
Check out the comments in the #defines and #includes before compiling |
||||
*/ |
||||
|
||||
/* J. Lahtinen 8/1993 - 12/2015 internet: jslahtin@gmail.com
|
||||
Some parts of this program are copied from the Nethack 3.3.0 sources, |
||||
the rest I wrote myself. |
||||
Tell me if you find any bugs.. including the program telling the death |
||||
reason differently from "nethack -s". |
||||
When reporting bugs, please include the line in your record file that |
||||
reclist doesn't treat properly (copy & paste is just fine). |
||||
|
||||
2015-12-12 - confirmed compatibility to Nethack 3.6.0 |
||||
- removed the warning of "too" modern game version |
||||
2015-02-21 confirmed the compatibility to the leaked Nethack 3.5.0 version |
||||
2003-01-20 changed the parameters -ds and -du from long to char* to allow |
||||
comparisons of partial date (for example -ds 2003 to list games |
||||
finished after the beginning of year 2003) |
||||
2002-03-29 v3.4.0 |
||||
2001-04-07 increased the size of the line buffer to fit Lethe patch scores |
||||
2000-09-06 removed the level number from escaping scores |
||||
2000-08-21 removed the level number from ascensions |
||||
anything in curly braces shown even if the "death" string isn't |
||||
(for the move counter added with my moves patch) |
||||
2000-08-12 v3.3.1 |
||||
2000-07-30 added option -ns to show only scores above some limit |
||||
2000-01-23 added option -a to select by alignment |
||||
2000-01-02 v3.3 |
||||
updated to understand the file format of game version 3.3.0 |
||||
removed old compile options NHPLUS and ENDGAME |
||||
980731 v3.2.2 |
||||
removed false append of "with the Amulet" on escaped games |
||||
980108 options -ds and -du to delimit game ending dates |
||||
971130 v3.2.1 |
||||
changed the endgame level names to those used by the game |
||||
version 3.2.2 |
||||
970525 don't split lines between [] (the game version 3.2 itself doesn't). |
||||
give an error message from parameter -f without a filename.. |
||||
961124 v3.2, fixed the endgame level indexes changed from game ver.3.1 |
||||
960628 option -g to select scores of a certain game version |
||||
960505 Both 3.1 and 3.2 game versions handled |
||||
960421 v3.0: File format changed for game version 3.2 |
||||
950524 don't print "in the endgame" when the exact end level is shown |
||||
I noticed the NhPlus class U missing from the helps, added it there.. |
||||
950523 2.3 |
||||
fixed a difference between this and nethack -s in splitting the line |
||||
when the death string is 55 characters |
||||
made the more accurate endgame level names optional (#define ENDGAME) |
||||
950521 alternative definition of getch for unix users |
||||
950228 2.2c |
||||
"Escaped with the Amulet" added |
||||
950117 a negative number in -npX stops display only at the end |
||||
-npX always stops at the end |
||||
941117 option -npX pauses after about every X lines |
||||
940601 -l option reads the logfile. |
||||
Option -help works like -? |
||||
940517 added the missing "." in the ascensions |
||||
940509 "Crushed" and "escaped" ends specified |
||||
940406 Make the difference between demigod and demigoddess |
||||
940322 2.2a |
||||
Endgame- levels specified |
||||
940224 the parameter -d displays the date also with the scores on a single |
||||
display line |
||||
940110 2.2 |
||||
the parameter -fs may be used to read stdin |
||||
931203 2.1b |
||||
conio.h headers removed for better operating system compatibility |
||||
extra space on "quit" scores removed |
||||
uid longer than 1 character caused some errors in unix, fixed |
||||
|
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <ctype.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#if defined(__MSDOS__)||defined(__OS2__) |
||||
#include <conio.h> |
||||
#else |
||||
#define getch() getc(stdin) |
||||
/* getch() reads one character without waiting for CR in MSDOS and OS/2
|
||||
and it's currently the only thing in conio.h I use */ |
||||
#endif |
||||
|
||||
/* The functions strnicmp and stricmp are not ANSI, according to the BorlandC
|
||||
help files. If your compiler doesn't understand them, you can replace them |
||||
with the standard functions (and lose the case insensitivity..) with these |
||||
following defines (or use suitable case-insensitive functions that your |
||||
compiler understands): |
||||
|
||||
#define strnicmp strncmp |
||||
#define stricmp strcmp |
||||
*/ |
||||
/* NOTE: With unix you may prefer defining them as strncasecmp and strcasecmp
|
||||
as below.. |
||||
*/ |
||||
|
||||
#if defined(__unix__) |
||||
#define RECORD "/usr/games/lib/nethackdir/record" |
||||
#define LOGFILE "/usr/games/lib/nethackdir/logfile" |
||||
#ifndef stricmp |
||||
#define strnicmp strncasecmp |
||||
#define stricmp strcasecmp |
||||
#endif |
||||
#else |
||||
#define RECORD "record" |
||||
#define LOGFILE "logfile" |
||||
#endif |
||||
|
||||
/* Some defines taken from game sources (src/topten.c) */ |
||||
#define NAMSZ 10 /* max length of player name */ |
||||
#define DTHSZ 100 /* max length of "killer" string */ |
||||
#define ROLESZ 3 /* role length */ |
||||
#define BUFSZ 256 /* text buffer size */ |
||||
|
||||
char *eos(char *p); |
||||
|
||||
char *ver="3.6.0"; /* version number */ |
||||
|
||||
/* command line parameters */ |
||||
int pause=0, /* pause interval */ |
||||
plin=0, /* lines printed without pause */ |
||||
pnum=0, /* how many scores to show */ |
||||
day=0; /* !0 : show dying dates */ |
||||
long low=0; /* minimum score to show */ |
||||
char *begdate=NULL, /* date boundaries */ |
||||
*enddate=NULL, |
||||
*name=NULL, /* if only one username is to be listed */ |
||||
*prole=NULL, /* character class */ |
||||
*prace=NULL, |
||||
*pver=NULL, /* if only scores of a certain version are to be listed */ |
||||
*pgend=NULL, /* list only this gender */ |
||||
*palign=NULL; /* only this alignment */ |
||||
|
||||
/* other globals used everywhere */ |
||||
int num=0; /* number of scores listed */ |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
FILE *inp; |
||||
|
||||
int i; |
||||
long sum=0, |
||||
score=0; |
||||
|
||||
char line[BUFSZ], |
||||
fver[6]; |
||||
char *filename=RECORD; |
||||
|
||||
long chkline(char *line); |
||||
void help(char *prog,char *par,char *explain); |
||||
|
||||
/* Command line parameters */ |
||||
for (i=1; i<argc; i++) { |
||||
if (argv[i][0]=='/') |
||||
argv[i][0]='-'; |
||||
if ((argv[i][0]=='?') || (argv[i][1]=='?') || |
||||
(!stricmp(argv[i],"-help")) || (!stricmp(argv[i],"--help"))) |
||||
help(argv[0],NULL,NULL); |
||||
else if (!(strnicmp(argv[i],"-f",2))) { /* filename */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
if (i==argc) { |
||||
printf("The -f parameter requires a filename.\n"); |
||||
exit(1); |
||||
} |
||||
filename=argv[i]; } |
||||
else |
||||
filename=argv[i]+2; |
||||
if (!stricmp(filename,"s")) |
||||
filename=NULL; /* stdio */ |
||||
} |
||||
else if (!(stricmp(argv[i],"-l"))) /* logfile */ |
||||
filename=LOGFILE; |
||||
else if (!(strnicmp(argv[i],"-u",2))) /* player name */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
name=argv[i]; } |
||||
else |
||||
name=argv[i]+2; |
||||
else if (!(strnicmp(argv[i],"-g",2))) { /* game version */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
pver=argv[i]; } |
||||
else |
||||
pver=argv[i]+2; |
||||
if (!isdigit(pver[0]) || pver[0]>'3') { |
||||
sprintf(line,"-g %s",pver); |
||||
help(argv[0],line,"Versions later than 3.x.x not supported yet"); |
||||
} |
||||
} |
||||
else if (!(strnicmp(argv[i],"-np",3))) /* pause interval, MUST be */ |
||||
if (strlen(argv[i])==3) { /* checked before -n */ |
||||
i++; |
||||
pause=atoi(argv[i]); } |
||||
else |
||||
pause=atoi(argv[i]+3); |
||||
else if (!(strnicmp(argv[i],"-ns",3))) /* minimum score to list */ |
||||
if (strlen(argv[i])==3) { /* MUST be checked before -n */ |
||||
i++; |
||||
low=atol(argv[i]); } |
||||
else |
||||
low=atol(argv[i]+3); |
||||
else if (!(strnicmp(argv[i],"-n",2))) /* number */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
pnum=atoi(argv[i]); } |
||||
else |
||||
pnum=atoi(argv[i]+2); |
||||
else if (!(strnicmp(argv[i],"-ds",3))) /* dates since.. */ |
||||
if (strlen(argv[i])==3) { /* MUST be checked before -d */ |
||||
i++; |
||||
begdate=argv[i]; } |
||||
else |
||||
begdate=argv[i]+3; |
||||
else if (!(strnicmp(argv[i],"-du",3))) /* dates until.. */ |
||||
if (strlen(argv[i])==3) { /* MUST be checked before -d */ |
||||
i++; |
||||
enddate=argv[i]; } |
||||
else |
||||
enddate=argv[i]+3; |
||||
else if (argv[i][1]=='d') /* display the dates */ |
||||
day=1; |
||||
else if (!(strnicmp(argv[i],"-p",2))) /* role */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
prole=argv[i]; } |
||||
else |
||||
prole=argv[i]+2; |
||||
else if (!(strnicmp(argv[i],"-r",2))) { /* race */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
prace=argv[i]; } |
||||
else |
||||
prace=argv[i]+2; |
||||
} |
||||
else if (!(strnicmp(argv[i],"-a",2))) { /* alignment */ |
||||
if (strlen(argv[i])==2) { |
||||
i++; |
||||
palign=argv[i]; } |
||||
else |
||||
palign=argv[i]+2; |
||||
if (strlen(palign)>3) { |
||||
sprintf(line,"-a %s",palign); |
||||
help(argv[0],line,"Alignment should be one of law/neu/cha/l/n/c"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* Command line ok, let's process the file */ |
||||
|
||||
if (!filename) |
||||
inp=stdin; /* I don't know if this works on all compilers.. */ |
||||
else |
||||
if (!(inp=fopen(filename,"r"))) { |
||||
printf ("File %s not found\n",filename); |
||||
exit (2); |
||||
} |
||||
|
||||
/* Actually I now think there's no point in this.
|
||||
* You'll probably see if the output looks funny anyway, |
||||
* so you might consider the warning more annoying than helpful. 12.12.2015 |
||||
fgets(line,sizeof(line),inp); |
||||
sscanf(line,"%5s",fver); |
||||
if (fver[1]=='.' && fver[3]=='.' && strncmp(fver,"3.6",3) > 0) { |
||||
printf ("The file seems to be from a game version newer than me.\n"); |
||||
printf ("The output may be incorrect.\n\n"); |
||||
} else if (fver[1] != '.') { |
||||
printf ("Not sure about which game version the file is from.\n"); |
||||
printf ("Version 3.1 assumed for unknown lines.\n\n"); |
||||
} |
||||
*/ |
||||
|
||||
printf("%-71s%s"," No Points Name","Hp [max]\n"); |
||||
/* HERE is the main loop */ |
||||
while ((!(feof(inp))) && ((!pnum) || (num<pnum))) { |
||||
|
||||
score = chkline(line); |
||||
if (pause>0 && plin >= pause) { |
||||
printf ("--more--\r"); |
||||
if (tolower(getch()=='n')) |
||||
pnum = num; |
||||
printf (" \r"); |
||||
plin = 0; |
||||
} |
||||
if (score == -9) { |
||||
printf ("\nInvalid file format!\n"); |
||||
return 1; |
||||
} |
||||
sum += score; |
||||
fgets(line,sizeof(line),inp); |
||||
} |
||||
|
||||
if (num) |
||||
printf("\n%d scores. Average score %ld\n",num,sum/(long)num); |
||||
else |
||||
printf("\nNo games found.\n"); |
||||
if (inp!=stdin) |
||||
fclose (inp); |
||||
if (pause) getch(); |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
/* Interpret one line of the file. Check for the game version. */ |
||||
|
||||
long chkline |
||||
(char *line) |
||||
{ |
||||
long line33 (char *line,int rank); |
||||
long line32 (char *line); |
||||
long line31 (char *line); |
||||
|
||||
int ver_major, ver_minor, patchlevel, |
||||
n; |
||||
static int rank=-1; |
||||
|
||||
/* Which game version did this score come from? Are we interested in
|
||||
scores of that version? */ |
||||
n = sscanf (line, "%d.%d.%d", &ver_major, &ver_minor, &patchlevel); |
||||
if (!pver || !strncmp(pver,line,strlen(pver)) || !strncmp(pver,"3.1",3)) { |
||||
rank++; |
||||
if ((ver_major == 3 && ver_minor >= 3) || ver_major > 3) |
||||
return line33(line,rank); |
||||
else if (ver_major == 3 && ver_minor == 2) |
||||
return line32(line); |
||||
else |
||||
return line31(line); |
||||
} |
||||
else if (pver) /* We didn't care about that version */ |
||||
return 0; |
||||
else |
||||
return -9; /* I don't understand this version. Newer than 3.3 */ |
||||
|
||||
} |
||||
|
||||
/* Interpret a line of game version 3.3 format */ |
||||
long line33 |
||||
(char *line, int rank) |
||||
{ |
||||
#define ENDGAMBR 7 |
||||
#define EGMIN -5 /* last endgame level */ |
||||
#define EGMAX -1 /* first endgame level */ |
||||
#define DUNMIN 0 /* minimum dungeonbranch number */ |
||||
#define DUNMAX 7 /* maximum dungeonbranch number */ |
||||
long score; |
||||
int dtype, dlev, maxlev, hp, maxhp; |
||||
char bdate[BUFSZ], /* 9 should be enough in VALID files, but we don't */ |
||||
ddate[BUFSZ], /* want to crash on invalid entries either.. */ |
||||
role[ROLESZ+1], |
||||
race[ROLESZ+1], |
||||
gender[ROLESZ+1], |
||||
align[ROLESZ+1], |
||||
plname[NAMSZ+1], |
||||
death[DTHSZ+1]; |
||||
char linebuf[BUFSZ], |
||||
prtbuf[81], |
||||
hpbuf[6]; |
||||
char *start, *end; |
||||
int n, |
||||
secline=1, |
||||
dayshown=0, |
||||
par=0, |
||||
hppos; |
||||
static char *eglev[] ={"Plane of Earth", /* -1 */ |
||||
"Plane of Air", /* -2 */ |
||||
"Plane of Fire", /* -3 */ |
||||
"Plane of Water", /* -4 */ |
||||
"Astral Plane"}; /* -5 */ |
||||
static char *dungeon[] ={"The Dungeons of Doom", /* 0 */ |
||||
"Gehennom", /* 1 */ |
||||
"The Gnomish Mines", /* 2 */ |
||||
"The Quest", /* 3 */ |
||||
"Sokoban", /* 4 */ |
||||
"Fort Ludios", /* 5 */ |
||||
"Vlad's Tower", /* 6 */ |
||||
"the endgame"}; /* 7 */ |
||||
|
||||
/* Fields in the line: version(3.3.n, ignored here), score, dungeon branch,
|
||||
dlevel, max dlevel, hp, max hp, number of deaths (ignored), birthdate, |
||||
death date, uid (ignored), role, race, gender, alignment, name, reason */ |
||||
n = sscanf(line, |
||||
"%*d.%*d.%*d %ld %d %d %d %d %d %*d %s %s %*d %s %s %s %s %[^,],%[^\n\r]", |
||||
&score,&dtype,&dlev,&maxlev,&hp,&maxhp,&ddate,&bdate, |
||||
role, race, gender, align, plname, death); |
||||
if (n<14) /* Invalid line, not all fields found */ |
||||
return 0L; |
||||
|
||||
/* Are we interested in this playername, role, alignment and deathdate? */ |
||||
if (role[0]<'A') |
||||
return 0L; |
||||
if ((prole) && (strnicmp(prole,role,strlen(prole)))) |
||||
return 0L; |
||||
if ((name) && (strcmp(name,plname))) |
||||
return 0L; |
||||
if ((prace) && (strnicmp(prace,race,strlen(prace)))) |
||||
return 0L; |
||||
if ((palign) && (strnicmp(palign,align,strlen(palign)))) |
||||
return 0L; |
||||
if ((begdate != NULL && strcmp(ddate,begdate) < 0) || |
||||
(enddate != NULL && strcmp(ddate,enddate) > 0)) |
||||
return 0L; |
||||
if (score<low) |
||||
return 0L; |
||||
|
||||
sprintf(linebuf,"%3d %10ld %.10s-%s-%s-%s-%s ", |
||||
rank,score,plname,role,race,gender,align); |
||||
if (!strncmp(death,"escaped",7)) { |
||||
strcat(linebuf,"escaped the dungeon"); |
||||
if (strstr(death,"Amulet")) |
||||
strcat(linebuf," with the Amulet"); |
||||
if (dtype!=ENDGAMBR) /* not in the Endgame */ |
||||
sprintf(eos(linebuf)," [max level %d]",maxlev); |
||||
secline=0; |
||||
} |
||||
else if (!strncmp(death,"ascended",8)) { |
||||
sprintf(eos(linebuf),"ascended to demigod%s-hood", |
||||
gender[0] == 'F'? "dess": ""); |
||||
secline=0; |
||||
} |
||||
else if (!strncmp(death,"quit",4)) { |
||||
strcat(linebuf,"quit"); |
||||
secline=0; |
||||
} |
||||
else if (!strncmp(death,"starv",5)) { |
||||
strcat(linebuf,"starved to death"); |
||||
secline=0; |
||||
} |
||||
else if (!strncmp(death,"choked",6)) { |
||||
sprintf(eos(linebuf),"choked on h%s food", |
||||
gender[0] == 'F'? "er": "is"); |
||||
secline=0; |
||||
} |
||||
else if (!strncmp(death,"poisoned",8)) |
||||
strcat(linebuf,"was poisoned"); |
||||
else if (!strncmp(death,"crushed",7)) |
||||
strcat(linebuf,"was crushed to death"); |
||||
else if (!strncmp(death,"petrified",9)) |
||||
strcat(linebuf,"turned to stone"); |
||||
else |
||||
strcat(linebuf,"died"); |
||||
|
||||
if (dtype == ENDGAMBR) { /* Endgame levels */ |
||||
if (strncmp(death,"ascend",6)) { /* No level number for ascensions */ |
||||
if (dlev >= EGMIN && dlev <= EGMAX) { |
||||
sprintf(eos(linebuf)," on the %s",eglev[-dlev - 1]); |
||||
} else { |
||||
strcat(linebuf," on the Plane of Void"); |
||||
} |
||||
} |
||||
} else if (strncmp(death,"escape",6)) { |
||||
sprintf(eos(linebuf), " in %s on level %d", |
||||
(dtype >= DUNMIN && dtype < DUNMAX)? dungeon[dtype]: |
||||
"an unknown dungeon branch", |
||||
dlev); |
||||
if (dlev != maxlev) |
||||
sprintf(eos(linebuf), " [max %d]", maxlev); |
||||
} |
||||
|
||||
strcat(linebuf,"."); |
||||
|
||||
if (secline) |
||||
sprintf(eos(linebuf)," %c%s.",toupper(*death),death+1); |
||||
else if (start=strchr(death,'{')) |
||||
/* Anyway, print what may be in curly braces. I have the game duration
|
||||
* appended there with my topten patch |
||||
* (http://www.clinet.fi/~walker/nethack.html#moves) */
|
||||
sprintf(eos(linebuf)," %s",start); |
||||
|
||||
start=linebuf; |
||||
if (hp>0) |
||||
sprintf(hpbuf,"%d",hp); |
||||
else |
||||
sprintf(hpbuf,"-"); |
||||
hppos=70; |
||||
while (strlen(start)>=hppos) { /* split the line as necessary */ |
||||
|
||||
for (end=start; end<start+hppos-1; end++) { |
||||
if (*end == '[') |
||||
par=1; |
||||
else if (*end == ']') |
||||
par=0; |
||||
} |
||||
while (end > start && (*end != ' ' || par)) { |
||||
if (*end == '[') |
||||
par=0; |
||||
else if (*end == ']') |
||||
par=1; |
||||
end--; |
||||
} |
||||
if (end == start) /* There was no good split position, shouldn't happen */ |
||||
end=start+hppos; |
||||
|
||||
strncpy(prtbuf, start, end-start); |
||||
prtbuf[end-start]=0; |
||||
printf("%s\n",prtbuf); /* This display line */ |
||||
plin++; |
||||
|
||||
if (day && !dayshown) { |
||||
printf(" %-15s", ddate); |
||||
dayshown=1; |
||||
} else |
||||
printf("%16s"," "); |
||||
start=end+1; /* Start position of the NEXT line */ |
||||
hppos= 57 - strlen(hpbuf); |
||||
} |
||||
end=eos(start); |
||||
while (end < start+hppos && end < linebuf+sizeof(linebuf)) |
||||
*end++=' '; |
||||
sprintf(end,hpbuf); |
||||
printf("%s",start); |
||||
sprintf(hpbuf,"[%d]",maxhp); |
||||
printf("%6s\n",hpbuf); |
||||
plin++; |
||||
if (day && !dayshown) { |
||||
printf(" %-15s\n",ddate); |
||||
plin++; |
||||
} |
||||
num++; |
||||
return score; |
||||
} |
||||
|
||||
/* Interpret a line of game version 3.2 format */ |
||||
long line32 |
||||
(char *line) |
||||
{ |
||||
int n, |
||||
pos, |
||||
pos2, |
||||
dtype, |
||||
dlev, |
||||
maxlev, |
||||
hp, |
||||
maxhp, |
||||
par=0, |
||||
dayshown=0; |
||||
static int nro=0; |
||||
long score; |
||||
char pvm1[BUFSZ], |
||||
pvm2[BUFSZ], |
||||
chclass, |
||||
sex, |
||||
sver[10], |
||||
death[150]={""}, |
||||
ascend=0; |
||||
char *reason; |
||||
|
||||
long line31(char *line); |
||||
|
||||
static char *dungeon[]={"The Dungeons of Doom",/* 0 */ |
||||
"Gehennom", /* 1 */ |
||||
"The Gnomish Mines", /* 2 */ |
||||
"The Quest", /* 3 */ |
||||
"Fort Ludios", /* 4 */ |
||||
"Vlad's Tower", /* 5 */ |
||||
"the endgame"}; /* 6 */ |
||||
|
||||
static char *eglev[]={"Plane of Earth", /* -1 */ |
||||
"Plane of Air", /* -2 */ |
||||
"Plane of Fire", /* -3 */ |
||||
"Plane of Water", /* -4 */ |
||||
"Astral Plane"}; /* -5 */ |
||||
nro++; |
||||
/* First there is the game version (3.2.something), then comes the actual
|
||||
score. Then the dungeon branch of death and dlevel, maxdlevel, hp, maxhp, |
||||
number of deaths(ignored), dates when the game ended and started, |
||||
userid(ignored, character class&sex, name and reason for game ending |
||||
*/ |
||||
|
||||
n = sscanf (line,"%9s %ld %d %d %d %d %d %*d %s %s %*d %c%c", |
||||
&sver,&score,&dtype,&dlev,&maxlev,&hp,&maxhp, |
||||
&pvm1,&pvm2,&chclass,&sex); |
||||
|
||||
if (chclass<'A') |
||||
return (0L); /* The VMS version may have end line with 'class' < A */ |
||||
if ((prole) && (prole[0]!=chclass)) |
||||
return (0L); /* Some other class specified on the command line */ |
||||
if (score<low) |
||||
return 0L; |
||||
|
||||
/* Find the player name. I didn't know how to use sscanf for it without
|
||||
stopping in the comma after the name when I wrote this.. Anyway, |
||||
the name is after the character class and sex, which are the first |
||||
alphabets on the line and can never be before position 38. |
||||
*/ |
||||
for (pos=38;!isalpha(line[pos]);pos++); /* pos points to class now */ |
||||
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */ |
||||
return (0L); /* to crash just if the file is invalid, do we.. */ |
||||
pos += 3; |
||||
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++) |
||||
death[pos2]=line[pos]; |
||||
death[pos2]=0; |
||||
|
||||
if ((name) && (strcmp(name,death))) |
||||
return (0L); /* We are only interested in somebody else's scores */ |
||||
|
||||
if ((begdate != NULL && strcmp(pvm1,begdate) < 0) || |
||||
(enddate != NULL && strcmp(pvm1,enddate) > 0)) |
||||
return (0L); /* We're not intrested in dates this early /late */ |
||||
|
||||
printf("%3d%11ld ",nro,score); |
||||
reason= &line[pos+1]; |
||||
strcat(death,"-"); /* The name is already there in the beginnin of string */ |
||||
pos=strlen(death); |
||||
death[pos]=chclass; death[pos+1]=0; |
||||
/* Here is the part where I'm not yet totally sure everything still works
|
||||
as it should.. but this worked on the 3.1 files */ |
||||
if (!(strncmp(reason,"poisoned",8))) |
||||
strcat(death," was poisoned"); |
||||
else if (!(strncmp(reason,"petrified",9))) |
||||
strcat(death," turned to stone"); |
||||
else if (!(strncmp(reason,"starv",5))) |
||||
strcat(death," starved to death"); |
||||
else if (!(strncmp(reason,"quit",4))) |
||||
strcat(death," quit"); |
||||
else if (!(strncmp(reason,"crushed",7))) |
||||
strcat(death," was crushed to death"); |
||||
else if (!(strncmp(reason,"choked",6))) |
||||
if (sex=='M') |
||||
strcat(death," choked on his food"); |
||||
else |
||||
strcat(death," choked on her food"); |
||||
else if (!(strncmp(reason,"ascended",8))) { |
||||
if (sex=='F') |
||||
strcat(death," ascended to demigoddess-hood."); |
||||
else |
||||
strcat(death," ascended to demigod-hood."); |
||||
ascend=1; |
||||
} |
||||
else if (!(strncmp(reason,"escaped",7))) { |
||||
sprintf (death+strlen(death)," escaped the dungeon [max level %d].",maxlev); |
||||
ascend=1; /* Not an ascension, but also listed in a special way. */ |
||||
} |
||||
else |
||||
strcat(death," died"); |
||||
if (!ascend) { |
||||
pos=strlen(death); |
||||
if (dtype==6) /* The endgame */ |
||||
{ |
||||
if (dlev < -5 || dlev > -1) { |
||||
printf ("Invalid endgame level number %d in file!\n", dlev); |
||||
return (0L); |
||||
} |
||||
sprintf (death+pos," on the %s. ",eglev[-1-dlev]); |
||||
/* dlev is negative if you die in the endgame, from -1 to -5 */ |
||||
} |
||||
else { |
||||
sprintf (death+pos," in %s",dungeon[dtype]); |
||||
if (dtype!=6) |
||||
sprintf(death+strlen(death)," on level %d",dlev); |
||||
if (maxlev!=dlev) |
||||
sprintf(death+strlen(death)," [max %d]. ",maxlev); |
||||
else strcat(death,". "); |
||||
} |
||||
} |
||||
|
||||
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10)) |
||||
&& (!ascend)) |
||||
{ reason[0]=toupper(reason[0]); |
||||
for (pos2=0; reason[pos2]>=' '; pos2++); |
||||
reason[pos2++]='.'; |
||||
reason[pos2]=0; |
||||
pos=strlen(death); |
||||
strcat(death,reason); |
||||
} |
||||
|
||||
pos=strlen(death); |
||||
if (pos>54) { /* split line at the last space before position 54 */ |
||||
while (pos>54 || death[pos]!=' ' || (par>0 && pos>30)) { |
||||
if(death[pos]==']') /* but we don't split between [] */ |
||||
par++; |
||||
if(death[pos]=='[') |
||||
par--; |
||||
pos--; |
||||
} |
||||
death[pos]=0; |
||||
if (day && !dayshown) { |
||||
printf ("%s\n %s\t\t",death,pvm1); |
||||
dayshown=1; |
||||
} |
||||
else { |
||||
printf("%s\n\t\t",death); |
||||
} |
||||
plin++; |
||||
pos++; |
||||
pos2=0; |
||||
while (death[pos]) { |
||||
death[pos2]=death[pos]; |
||||
pos++; |
||||
pos2++; } |
||||
death[pos2]=0; |
||||
} |
||||
|
||||
printf("%-54s",death); |
||||
pos=0; |
||||
|
||||
if (hp>0) |
||||
printf("%3d",hp); |
||||
else |
||||
printf(" -"); |
||||
sprintf(death,"[%d]\n",maxhp); |
||||
printf("%7s",death); |
||||
plin++; |
||||
|
||||
if (day && !dayshown) { |
||||
printf (" %s\n",pvm1); |
||||
plin++; |
||||
} |
||||
num++; |
||||
|
||||
return (score); |
||||
} |
||||
|
||||
/* Show some help about the arguments */ |
||||
void help(char *prog, char *par, char *explain) |
||||
{ |
||||
printf ("RECLIST, version %s\n",ver); |
||||
printf ("List Nethack 3.1 - 3.5 record file and calculate the average score.\n"); |
||||
printf("\nUsage: %s [-f file / -fs / -l] [-u name] [-p role] [-r race]\n", |
||||
prog); |
||||
printf(" [-a alignment] [-g ver] [-nX] [-nsX] [-npX] [-d]"); |
||||
printf(" [-ds date] [-du date]\n\n"); |
||||
printf("The options are:\n"); |
||||
printf("\t-u name list only the scores of player 'name'\n"); |
||||
printf("\t-p profession list only the scores for one character class\n"); |
||||
printf("\t-r race race to be listed (since 3.3: hum/dwa/elf/gno/orc)\n"); |
||||
printf("\t-a alignment list only this alignment (since 3.3: l/n/c)\n"); |
||||
printf("\t-f filename read the scores from the file 'filename'\n"); |
||||
printf("\t-fs read the scorelist from stdin\n"); |
||||
printf("\t-l read the logfile from %s\n", LOGFILE); |
||||
printf("\t-g ver list only scores of game version ver\n"); |
||||
printf("\t-n X list only X scores\n"); |
||||
printf("\t-np X pause after every X lines\n"); |
||||
printf("\t-ns X don't list scores below X points\n"); |
||||
printf("\t-d show also the dates\n"); |
||||
printf("\t-ds date games ended at or after date\n"); |
||||
printf("\t-du date games ended at or before date\n"); |
||||
printf("\nYou may specify the options in any order.\n"); |
||||
printf("The space after -f, -g, -u, -r, -a, -n, -ns or -np is optional.\n"); |
||||
if (par) |
||||
printf ("\nUnknown parameter %s\n",par); |
||||
if (explain) |
||||
printf("%s\n", explain); |
||||
exit (1); |
||||
} |
||||
|
||||
|
||||
/* If it wasn't a 3.2 record file, we may still check if it makes sense
|
||||
in the 3.1 format */ |
||||
|
||||
long line31 (char *line) |
||||
{ |
||||
int pos, pos2, |
||||
dtype, dlev, maxlev, |
||||
hp, maxhp, |
||||
dayshown=0; |
||||
static int nro=0, |
||||
plin=1; |
||||
long score, |
||||
pvm; |
||||
char chclass, sex, |
||||
death[70]={""}, |
||||
ascend=0; |
||||
char *reason; |
||||
|
||||
static char *dungeon[]={"The Dungeons of Doom",/* 0 */ |
||||
"Gehennom", /* 1 */ |
||||
"The Gnomish Mines", /* 2 */ |
||||
"The Quest", /* 3 */ |
||||
"Fort Ludios", /* 4 */ |
||||
"Vlad's Tower", /* 5 */ |
||||
"the endgame"}; /* 6 */ |
||||
|
||||
static char *eglev[]={"Earth", /* 0 */ |
||||
"Air", /* -1*/ |
||||
"Fire", /* -2*/ |
||||
"Water", /* -3*/ |
||||
"Astral"}; /* -4*/ |
||||
nro++; |
||||
/* The date uses positions 0-5 (YYMMDD), uid (starts) in 7.
|
||||
After that, the dungeon branch of death and dlevel, maxdlevel, hp, maxhp, |
||||
score, character class&sex, name and reason for game ending |
||||
*/ |
||||
sscanf (line,"%ld %*d %d %d %d %d %d %ld %c%c", |
||||
&pvm,&dtype,&dlev,&maxlev,&hp,&maxhp,&score,&chclass,&sex); |
||||
|
||||
if (chclass<'A') |
||||
return (0); /* The VMS version may have end line with class < A */ |
||||
if ((prole) && (prole[0]!=chclass)) |
||||
return (0); |
||||
if (score<low) |
||||
return 0L; |
||||
/* Player name */ |
||||
for (pos=20;!isalpha(line[pos]);pos++); /* pos points to class now */ |
||||
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */ |
||||
return (0); /* to crash just if the file is invalid.. */ |
||||
pos += 3; |
||||
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++) |
||||
death[pos2]=line[pos]; |
||||
death[pos2]=0; |
||||
|
||||
if ((name) && (strcmp(name,death))) |
||||
return (0); |
||||
|
||||
printf("%3d%11ld ",nro,score); |
||||
reason= &line[pos+1]; |
||||
strcat(death,"-"); /* The name is already there in the beginnin of string */ |
||||
pos=strlen(death); |
||||
death[pos]=chclass; death[pos+1]=0; |
||||
if (!(strncmp(reason,"poisoned",8))) |
||||
strcat(death," was poisoned"); |
||||
else if (!(strncmp(reason,"petrified",9))) |
||||
strcat(death," turned to stone"); |
||||
else if (!(strncmp(reason,"starv",5))) |
||||
strcat(death," starved to death"); |
||||
else if (!(strncmp(reason,"quit",4))) |
||||
strcat(death," quit"); |
||||
else if (!(strncmp(reason,"crushed",7))) |
||||
strcat(death," was crushed to death"); |
||||
else if (!(strncmp(reason,"choked",6))) |
||||
if (sex=='M') |
||||
strcat(death," choked on his food"); |
||||
else |
||||
strcat(death," choked on her food"); |
||||
else if (!(strncmp(reason,"ascended",8))) { |
||||
if (sex=='F') |
||||
strcat(death," ascended to demigoddess-hood."); |
||||
else |
||||
strcat(death," ascended to demigod-hood."); |
||||
ascend=1; |
||||
} |
||||
else if (!(strncmp(reason,"escaped",7))) { |
||||
strcat (death+strlen(death)," escaped the dungeon "); |
||||
if (!strcmp(reason+7," (with the Amulet)")) |
||||
sprintf (death+strlen(death),"[max level %d].",maxlev); |
||||
else |
||||
strcat (death,"with the Amulet"); |
||||
ascend=1; /* Not an ascension, but also listed in a special way. */ |
||||
} |
||||
else |
||||
strcat(death," died"); |
||||
if (!ascend) { |
||||
pos=strlen(death); |
||||
|
||||
if (dtype==6) /* The endgame */ |
||||
sprintf (death+pos," in %s on the %s Plane.", |
||||
dungeon[dtype],eglev[-dlev]); |
||||
/* dlev is negative if you die in the endgame */ |
||||
else { |
||||
sprintf (death+pos," in %s",dungeon[dtype]); |
||||
if (dtype!=6) |
||||
sprintf(death+strlen(death)," on level %d",dlev); |
||||
if (maxlev!=dlev) |
||||
sprintf(death+strlen(death)," [max %d].",maxlev); |
||||
else strcat(death,"."); |
||||
} |
||||
if (strlen(death)>54) { /* split the line before the death reason */ |
||||
death[pos]=0; |
||||
if (day && !dayshown) { |
||||
printf ("%s\n %ld\t\t",death,pvm); |
||||
plin++; |
||||
dayshown=0; |
||||
} |
||||
else { |
||||
printf("%s\n\t\t",death); |
||||
plin++; |
||||
} |
||||
pos++; |
||||
pos2=0; |
||||
while (death[pos]) { |
||||
death[pos2]=death[pos]; |
||||
pos++; |
||||
pos2++; } |
||||
death[pos2]=0; |
||||
} |
||||
/* if (strlen(death)==55)
|
||||
death[54]=0; */ |
||||
} |
||||
printf("%-54s",death); |
||||
|
||||
if (hp>0) |
||||
printf("%3d",hp); |
||||
else |
||||
printf(" -"); |
||||
sprintf(death,"[%d]",maxhp); |
||||
printf("%6s\n",death); |
||||
plin++; |
||||
|
||||
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10)) && (!ascend)) |
||||
{ reason[0]=toupper(reason[0]); |
||||
reason[strlen(reason)-1]=0; /* remove the linefeed at the end */ |
||||
if (day && !dayshown) { |
||||
printf(" %ld",pvm); |
||||
dayshown=0; |
||||
} |
||||
printf("\t\t%s.\n",reason); |
||||
plin++; |
||||
} |
||||
if (day && !dayshown) { |
||||
printf (" %ld\n",pvm); |
||||
plin++; |
||||
} |
||||
num++; |
||||
|
||||
if (pause>0 && plin >= pause) { |
||||
printf ("--more--\r"); |
||||
if (tolower(getch()=='n')) |
||||
pnum = num; |
||||
printf (" \r"); |
||||
plin = 0; |
||||
} |
||||
|
||||
return (score); |
||||
} |
||||
|
||||
char *eos(char *p) |
||||
{ |
||||
while (*p) |
||||
p++; |
||||
return (p); |
||||
} |
Binary file not shown.
@ -1,12 +0,0 @@
|
||||
[nethacklauncher] |
||||
loglevel = "debug" |
||||
serverdisplay = "unixvoid.com underground nethack server" |
||||
nethackversion = "3.6.1" |
||||
hackdir = "/hack" |
||||
nhdatlocation = "/usr/lib/games/nethack/nhdat" |
||||
recoverbinary = "/usr/lib/games/nethack/recover" |
||||
bootstrapdelay = 1 |
||||
|
||||
[redis] |
||||
host = "localhost:6379" |
||||
password = "" |
@ -0,0 +1,16 @@
|
||||
module git.bitnuke.io/mfaltys/nethack-launcher |
||||
|
||||
go 1.22.1 |
||||
|
||||
require ( |
||||
golang.org/x/crypto v0.26.0 |
||||
gopkg.in/gcfg.v1 v1.2.3 |
||||
gopkg.in/redis.v5 v5.2.9 |
||||
) |
||||
|
||||
require ( |
||||
github.com/onsi/ginkgo v1.16.5 // indirect |
||||
github.com/onsi/gomega v1.34.1 // indirect |
||||
golang.org/x/sys v0.23.0 // indirect |
||||
gopkg.in/warnings.v0 v0.1.2 // indirect |
||||
) |
@ -0,0 +1,95 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= |
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= |
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= |
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= |
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= |
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= |
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= |
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= |
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= |
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= |
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= |
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= |
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= |
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= |
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= |
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= |
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= |
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= |
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= |
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= |
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= |
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= |
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= |
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= |
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= |
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= |
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= |
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= |
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= |
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= |
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= |
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= |
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= |
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= |
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= |
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= |
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= |
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= |
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= |
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= |
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= |
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= |
||||
gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= |
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= |
||||
gopkg.in/redis.v5 v5.2.9 h1:MNZYOLPomQzZMfpN3ZtD1uyJ2IDonTTlxYiV/pEApiw= |
||||
gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY= |
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= |
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= |
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= |
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= |
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@ -0,0 +1,19 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os/exec" |
||||
) |
||||
|
||||
func runCleanup(username string) { |
||||
// clean up ttyrecs in /<hackdir>/user/<username>/ttyrec/
|
||||
// clean up sotw dumps, aka:
|
||||
// non-symlinked .txt files in /<hackdir>/user/<username>/sotw/
|
||||
|
||||
// run cleanup file
|
||||
out, err := exec.Command("/bin/clean.sh", config.NethackLauncher.HackDir, username, config.NethackLauncher.TTYRecCache, config.NethackLauncher.SotwDumpCache).Output() |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
} |
||||
fmt.Printf("%s\n", out) |
||||
} |
@ -0,0 +1,144 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"bufio" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"os/exec" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"gopkg.in/redis.v5" |
||||
) |
||||
|
||||
func printLibraryScreen(redisClient *redis.Client, username string) { |
||||
// print header
|
||||
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay) |
||||
println("") |
||||
|
||||
println(" Choose a users library to view ('enter' without selection returns)") |
||||
println("") |
||||
|
||||
us, err := ioutil.ReadDir(fmt.Sprintf("/%s/user", config.NethackLauncher.HackDir)) |
||||
if err != nil { |
||||
println(" No users exist ('enter' without selection returns)") |
||||
panic(err) |
||||
} |
||||
|
||||
users := make(map[int]string) |
||||
usersTimer := 1 |
||||
for _, u := range us { |
||||
fmt.Printf(" %d) %s\n", usersTimer, u.Name()) |
||||
|
||||
// add user to line map
|
||||
users[usersTimer] = fmt.Sprintf("%s", u.Name()) |
||||
usersTimer++ |
||||
} |
||||
|
||||
fmt.Println("") |
||||
fmt.Printf(">> ") |
||||
// start user input
|
||||
|
||||
// disable input buffering
|
||||
exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run() |
||||
// do not display entered characters on the screen
|
||||
exec.Command("stty", "-F", "/dev/tty", "-echo").Run() |
||||
|
||||
var b []byte = make([]byte, 1) |
||||
for { |
||||
os.Stdin.Read(b) |
||||
s, _ := strconv.Atoi(string(b)) |
||||
|
||||
// check if user is trying to navigate
|
||||
if string(b) == "\n" { |
||||
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run() |
||||
clearScreen() |
||||
if username == "" { |
||||
clearScreen() |
||||
printWelcomeScreen(redisClient) |
||||
} else { |
||||
clearScreen() |
||||
printUserScreen(redisClient, username) |
||||
} |
||||
} |
||||
// check if selection is in out map
|
||||
if users[s] != "" { |
||||
user := strings.Split(users[s], ":") |
||||
fmt.Printf("going to spectate '%s'\n", user[0]) |
||||
clearScreen() |
||||
printUserLibraryScreen(redisClient, username, user[0]) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func printUserLibraryScreen(redisClient *redis.Client, username, currentUser string) { |
||||
// print header
|
||||
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay) |
||||
println("") |
||||
|
||||
println(" Choose a ttyrec to view ('enter' without selection returns)") |
||||
println(" The following keybinds are available during playback:") |
||||
println(" • + or f: double the speed of playback") |
||||
println(" • - or s: halve the speed of playback") |
||||
println(" • 0: set playback speed to 0, pausing playback") |
||||
println(" • 1: set playback speed to 1 again") |
||||
println("") |
||||
|
||||
t, err := ioutil.ReadDir(fmt.Sprintf("/%s/user/%s/ttyrec", config.NethackLauncher.HackDir, currentUser)) |
||||
if err != nil { |
||||
println(" No ttyrecs exist ('enter' without selection returns)") |
||||
panic(err) |
||||
} |
||||
|
||||
ttyrecs := make(map[int]string) |
||||
ttyrecsTimer := 1 |
||||
for _, ty := range t { |
||||
fmt.Printf(" %d) %s\n", ttyrecsTimer, ty.Name()) |
||||
|
||||
// add user to line map
|
||||
ttyrecs[ttyrecsTimer] = fmt.Sprintf("%s", ty.Name()) |
||||
ttyrecsTimer++ |
||||
} |
||||
|
||||
fmt.Println("") |
||||
fmt.Printf(">> ") |
||||
// start user input
|
||||
|
||||
// disable input buffering
|
||||
exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run() |
||||
// enable showing input on screen
|
||||
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run() |
||||
|
||||
scanner := bufio.NewScanner(os.Stdin) |
||||
for scanner.Scan() { |
||||
if scanner.Text() == "" { |
||||
clearScreen() |
||||
printLibraryScreen(redisClient, username) |
||||
} |
||||
// save input
|
||||
s := scanner.Text() |
||||
sel, err := strconv.Atoi(s) |
||||
if err != nil { |
||||
fmt.Printf(" There was a problem with your last entry.\n>> ") |
||||
} |
||||
// check if selection is in our map
|
||||
if ttyrecs[sel] != "" { |
||||
fmt.Printf("going to watch '%s'\n", ttyrecs[sel]) |
||||
clearScreen() |
||||
|
||||
// set ttyrec path
|
||||
ttyrecPath := fmt.Sprintf("%s/user/%s/ttyrec/%s", config.NethackLauncher.HackDir, username, ttyrecs[sel]) |
||||
tp := exec.Command("ttyplay", ttyrecPath) |
||||
tp.Stdout = os.Stdout |
||||
tp.Stdin = os.Stdin |
||||
tp.Stderr = os.Stderr |
||||
err := tp.Run() |
||||
if err != nil { |
||||
fmt.Print(err) |
||||
} |
||||
} |
||||
clearScreen() |
||||
printUserLibraryScreen(redisClient, username, currentUser) |
||||
} |
||||
} |
Loading…
Reference in new issue