Compare commits

..

No commits in common. 'develop' and 'master' have entirely different histories.

  1. 1
      .gitignore
  2. 35
      Makefile
  3. 15
      config.gcfg
  4. 93
      deps/Dockerfile
  5. 5
      deps/chowner.sh
  6. 30
      deps/clean.sh
  7. 28
      deps/link_sotw.sh
  8. 4
      deps/nethackrc
  9. 929
      deps/reclist.c
  10. BIN
      deps/scores.dat
  11. 12
      deps/server_config.gcfg
  12. 16
      go.mod
  13. 95
      go.sum
  14. 19
      nethack-launcher/cleanup.go
  15. 24
      nethack-launcher/create_initial_files.go
  16. 30
      nethack-launcher/create_user_files.go
  17. 144
      nethack-launcher/library.go
  18. 19
      nethack-launcher/nethack-launcher.go
  19. 42
      nethack-launcher/print_high_scores.go
  20. 1
      nethack-launcher/print_login_screen.go
  21. 3
      nethack-launcher/print_progress_screen.go
  22. 61
      nethack-launcher/print_user_screen.go
  23. 5
      nethack-launcher/print_welcome_screen.go
  24. 23
      nethack-launcher/run_game.go

1
.gitignore vendored

@ -2,4 +2,3 @@
hack/
stage.tmp/
bin/
deps/shadow-of-the-wyrm-release-*.tar.gz

35
Makefile

@ -11,11 +11,9 @@ all: stat
run:
go run \
nethack-launcher/check_dir.go \
nethack-launcher/cleanup.go \
nethack-launcher/create_initial_files.go \
nethack-launcher/create_user_files.go \
nethack-launcher/janitor.go \
nethack-launcher/library.go \
nethack-launcher/nethack-launcher.go \
nethack-launcher/print_change_password_screen.go \
nethack-launcher/print_high_scores.go \
@ -28,47 +26,36 @@ run:
nethack-launcher/run_game.go \
nethack-launcher/start_watcher.go
stat:
mkdir -p bin/
$(CGOR) $(GOC) $(GOFLAGS) -o bin/nethack-launcher nethack-launcher/*.go
dependencies:
go mod init git.bitnuke.io/mfaltys/nethack-launcher
go mod tidy
go get github.com/gorilla/mux
build_docker: fetch_sotw
build_docker:
mkdir -p stage.tmp/
cp deps/Dockerfile stage.tmp/
cp config.gcfg stage.tmp/config.gcfg
cp deps/server_config.gcfg stage.tmp/config.gcfg
cp bin/nethack-launcher stage.tmp/
cp deps/redis.conf stage.tmp/
cp deps/run.sh stage.tmp/
cp deps/nethackrc stage.tmp/
cp deps/chowner.sh stage.tmp/
cp deps/run_nethack.sh stage.tmp/
cp deps/reclist.c stage.tmp/
cp deps/link_sotw.sh stage.tmp/
cp deps/clean.sh stage.tmp/
cp deps/scores.dat stage.tmp/
cd stage.tmp/ && \
$(OS_PERMS) docker build -t $(IMAGE_NAME) .
fetch_sotw:
mkdir -p stage.tmp/
cp deps/shadow-of-the-wyrm-release-1.6.5.tar.gz stage.tmp/sotw.tar.gz
run_docker:
$(OS_PERMS) docker run \
-d \
-p 2223:22 \
--name=nethackdev \
-v $(NETHACK_DIR):/hack-dev:rw \
--restart always \
$(IMAGE_NAME)
stop_docker:
$(OS_PERMS) docker stop -t 0 nethackdev
$(OS_PERMS) docker rm nethackdev
-d \
-p 2222:22 \
--name=nethack \
-v $(NETHACK_DIR):/hack:rw \
--restart always \
$(IMAGE_NAME)
clean:
rm -rf bin/

15
config.gcfg

@ -1,15 +1,10 @@
[nethacklauncher]
loglevel = "debug"
serverdisplay = "bitnuke.io underground nethack server"
nethackversion = "3.6.7-0"
hackdir = "/hack"
nhdatlocation = "/NetHack/dat/nhdat"
recoverbinary = "/NetHack/util/recover"
sysconflocation = "/NetHack/sys/unix/sysconf"
ttyreccache = "10"
sotwversion = "1.6.5"
sotwroot = "/sotw"
sotwdumpcache = "5"
serverdisplay = "unixvoid.com underground nethack server"
nethackversion = "3.6.1"
hackdir = "savedata/hack"
nhdatlocation = "/usr/lib/games/nethack/nhdat"
recoverbinary = "/usr/lib/games/nethack/recover"
bootstrapdelay = 1
[redis]

93
deps/Dockerfile vendored

@ -1,70 +1,21 @@
FROM debian:stable-slim
FROM debian
# install needed packages
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
openssh-server \
autoconf \
bison \
bsdmainutils \
bzip2 \
flex \
gcc \
gdb \
git \
groff \
libncursesw5-dev \
libsqlite3-dev \
libboost-all-dev \
libncurses5-dev \
libncursesw5-dev \
lua5.1 \
liblua5.1-0-dev \
libsdl2-dev \
libsdl2-image-2.0-0 \
libsdl2-mixer-2.0-0 \
libsdl2-image-dev \
libxerces-c-dev \
make \
ncurses-dev \
premake4 \
sqlite3 \
redis-server \
tar \
telnetd \
xinetd \
locales \
wget \
vim
RUN apt-get clean
# TODO
# nightly instructions
#
RUN git clone https://github.com/NetHack/NetHack.git
RUN cd NetHack && \
git checkout NetHack-3.6 && \
sed -i 's/#ifndef SYSCF/#ifdef SYSCF/g' include/config.h && \
sys/unix/setup.sh sys/unix/hints/linux && \
sed -i '/POSTINSTALL=/d' Makefile && \
sed -i 's/.*-DSYSCF -DSYSCF_FILE=.*/CFLAGS+=-DSECURE/' Makefile && \
sed -i 's/#CFLAGS+=-DSCORE_ON_BOTL/CFLAGS+=-DSCORE_ON_BOTL/' Makefile && \
cat Makefile && \
make all && \
make install
RUN find /* -name nethack
RUN chmod 777 /NetHack/sys/unix/sysconf
# update sysconf
RUN sed -i 's/.*WIZARDS=.*/#WIZARDS=/' /NetHack/sys/unix/sysconf
RUN sed -i 's/.*GENERICUSERS=.*/#GENERICUSERS=/' /NetHack/sys/unix/sysconf
RUN sed -i 's/.*MAXPLAYERS=.*/MAXPLAYERS=0/' /NetHack/sys/unix/sysconf
DEBIAN_FRONTEND=noninteractive apt-get install -y \
nethack-console \
redis-server \
openssh-server \
g++ \
make \
git \
vim
# make required dirs
RUN mkdir -p /redisbackup/
## update nethack bin location
RUN cp /NetHack/src/nethack /bin/nethack
# update nethack bin location
RUN mv /usr/games/nethack /bin/nethack
RUN chmod 777 /bin/nethack
RUN mkdir /hack/
@ -76,8 +27,6 @@ RUN echo "PermitEmptyPasswords yes" >> /etc/ssh/sshd_config
RUN useradd nethack -d / -s /nethack-launcher
RUN echo 'nethack:U6aMy0wojraho' | chpasswd -e
RUN chown -R nethack /hack/
RUN chown -R nethack /NetHack/
RUN touch /.hushlogin
# build ttyrec
RUN git clone https://github.com/ovh/ovh-ttyrec && \
@ -88,17 +37,7 @@ RUN git clone https://github.com/ovh/ovh-ttyrec && \
RUN rm -rf ovh-ttyrec/
RUN DEBIAN_FRONTEND=noninteractive apt-get remove -y\
make \
git \
autoconf \
bison \
bsdmainutils \
flex \
groff
# build reclist
COPY reclist.c /
RUN gcc reclist.c -o /bin/reclist
RUN rm reclist.c
git
# copy in files
COPY config.gcfg /
@ -108,13 +47,5 @@ COPY run.sh /
COPY chowner.sh /bin/
COPY nethackrc /.nethackrc
COPY run_nethack.sh /
COPY link_sotw.sh /bin/
COPY clean.sh /bin/
ADD sotw.tar.gz /
RUN mv /shadow-of-the-wyrm* /sotw/
COPY scores.dat /sotw/
# update ini to match current season by default
RUN cd /sotw && \
sed -i 's/current_month_is_start_month=0/current_month_is_start_month=1/g' swyrm.ini
CMD ["/run.sh"]

5
deps/chowner.sh vendored

@ -2,12 +2,7 @@
while :
do
# chown nethack directory
chown -R nethack:nethack /hack 2> /dev/null
chmod -R 744 /hack 2> /dev/null
# chown sotw directory
chown -R nethack:nethack /sotw 2> /dev/null
chmod -R 744 /sotw 2> /dev/null
sleep 2
done

30
deps/clean.sh vendored

@ -1,30 +0,0 @@
#!/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

28
deps/link_sotw.sh vendored

@ -1,28 +0,0 @@
#!/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

4
deps/nethackrc vendored

@ -25,8 +25,8 @@ OPTIONS=showexp,time
OPTIONS=sortloot:full
# curses interface
OPTIONS=windowtype:curses
OPTIONS=perm_invent
#OPTIONS=windowtype:curses
#OPTIONS=perm_invent
###

929
deps/reclist.c vendored

@ -1,929 +0,0 @@
/* 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);
}

BIN
deps/scores.dat vendored

Binary file not shown.

12
deps/server_config.gcfg vendored

@ -0,0 +1,12 @@
[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 = ""

16
go.mod

@ -1,16 +0,0 @@
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
)

95
go.sum

@ -1,95 +0,0 @@
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=

19
nethack-launcher/cleanup.go

@ -1,19 +0,0 @@
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)
}

24
nethack-launcher/create_initial_files.go

@ -14,23 +14,7 @@ func createInitialFiles() {
// move in nhdat file if it does not exist
if _, err := os.Stat(fmt.Sprintf("%s/nhdat", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
fmt.Printf("%s\n", err)
println("moving in initial nhdat file")
out, err := exec.Command("cp", config.NethackLauncher.NhdatLocation, fmt.Sprintf("%s/nhdat", config.NethackLauncher.HackDir)).Output()
if err != nil {
fmt.Printf("%s\n", err)
}
fmt.Printf("%s\n", out)
}
// move in sysconf file if it does not exist
if _, err := os.Stat(fmt.Sprintf("%s/sysconf", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
exec.Command("cp", config.NethackLauncher.SysconfLocation, config.NethackLauncher.HackDir).Run()
}
// make sure record file exists
if _, err := os.Stat(fmt.Sprintf("%s/record", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
os.OpenFile(fmt.Sprintf("%s/record", config.NethackLauncher.HackDir), os.O_RDONLY|os.O_CREATE, 0666)
exec.Command("cp", config.NethackLauncher.NhdatLocation, config.NethackLauncher.HackDir).Run()
}
// make sure initial rcfile exists
@ -46,14 +30,8 @@ func createInitialFiles() {
os.Exit(1)
} else {
// move nethackrc file to proper location
println("moving in initial config file")
exec.Command("cp", "/.nethackrc", config.NethackLauncher.HackDir).Run()
}
}
// make sure initial sotw scores file exists
if _, err := os.Stat(fmt.Sprintf("%s/scores.dat", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
exec.Command("cp", fmt.Sprintf("%s/scores.dat", config.NethackLauncher.SotwRoot), config.NethackLauncher.HackDir).Run()
}
}

30
nethack-launcher/create_user_files.go

@ -1,7 +1,6 @@
package main
import (
"errors"
"fmt"
"os"
"os/exec"
@ -12,15 +11,10 @@ func createUserFiles(username string) {
userPath := fmt.Sprintf("%s/user/%s/ttyrec/", config.NethackLauncher.HackDir, username)
exec.Command("mkdir", "-p", userPath).Run()
// copy in rc file if it does not exist
// copy in rc file
hackRCLoc := fmt.Sprintf("%s/.nethackrc", config.NethackLauncher.HackDir)
if _, err := os.Stat(fmt.Sprintf("%s/user/%s/.nethackrc", config.NethackLauncher.HackDir, username)); errors.Is(err, os.ErrNotExist) {
println(fmt.Sprintf("%s/user/%s/.nethackrc does not exist, copying it.", config.NethackLauncher.HackDir, username))
hackRCDest := fmt.Sprintf("%s/user/%s/.nethackrc", config.NethackLauncher.HackDir, username)
exec.Command("cp", hackRCLoc, hackRCDest).Run()
} else {
println(fmt.Sprintf("%s/.nethackrc exists, not overwriting.", config.NethackLauncher.HackDir))
}
hackRCDest := fmt.Sprintf("%s/user/%s/.nethackrc", config.NethackLauncher.HackDir, username)
exec.Command("cp", hackRCLoc, hackRCDest).Run()
// create necessary directories if they dont exist
userpath := fmt.Sprintf("%s/user/%s", config.NethackLauncher.HackDir, username)
@ -35,25 +29,9 @@ func createUserFiles(username string) {
// create necessary files if they dont exist
os.OpenFile(fmt.Sprintf("%s/logfile", userpath), os.O_RDONLY|os.O_CREATE, 0666)
os.OpenFile(fmt.Sprintf("%s/perm", userpath), os.O_RDONLY|os.O_CREATE, 0666)
os.OpenFile(fmt.Sprintf("%s/record", userpath), os.O_RDONLY|os.O_CREATE, 0666)
os.OpenFile(fmt.Sprintf("%s/xlogfile", userpath), os.O_RDONLY|os.O_CREATE, 0666)
// create record symlink
exec.Command("ln", "-s", fmt.Sprintf("%s/record", config.NethackLauncher.HackDir), fmt.Sprintf("%s/record", userpath)).Run()
// create sysconf symlink
exec.Command("ln", "-s", fmt.Sprintf("%s/sysconf", config.NethackLauncher.HackDir), fmt.Sprintf("%s/sysconf", userpath)).Run()
// move in nhdat file if it does not exist
exec.Command("cp", fmt.Sprintf("%s/nhdat", config.NethackLauncher.HackDir), userpath).Run()
// run sotw prep script
if _, err := os.Stat(fmt.Sprintf("%s/sotw/", userpath)); os.IsNotExist(err) {
println("running /bin/link_sotw.sh")
out, err := exec.Command("/bin/link_sotw.sh", userpath, config.NethackLauncher.HackDir).Output()
if err != nil {
println("ope: ")
fmt.Sprintf("%s", err)
}
println(string(out))
}
}

144
nethack-launcher/library.go

@ -1,144 +0,0 @@
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)
}
}

19
nethack-launcher/nethack-launcher.go

@ -13,18 +13,13 @@ import (
type Config struct {
NethackLauncher struct {
Loglevel string
ServerDisplay string
NethackVersion string
HackDir string
NhdatLocation string
RecoverBinary string
SysconfLocation string
TTYRecCache string
SotwVersion string
SotwRoot string
SotwDumpCache string
BootstrapDelay time.Duration
Loglevel string
ServerDisplay string
NethackVersion string
HackDir string
NhdatLocation string
RecoverBinary string
BootstrapDelay time.Duration
}
Redis struct {

42
nethack-launcher/print_high_scores.go

@ -12,47 +12,7 @@ func printHighScores(redisClient *redis.Client, username string) {
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
nh := exec.Command("reclist", "-f", fmt.Sprintf("%s/record", config.NethackLauncher.HackDir), "-n", "10")
//cmd := fmt.Sprintf("nethack -d %s -s -v |head -31", config.NethackLauncher.HackDir)
//nh := exec.Command("bash", "-c", cmd)
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr
nh.Run()
println("")
println(" Press enter to return to menu")
println("")
fmt.Printf(">> ")
// allow user back to home screen
// 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)
// check if user is trying to navigate
if string(b) == "\n" {
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
if username == "" {
printWelcomeScreen(redisClient)
} else {
printUserScreen(redisClient, username)
}
}
}
}
func printAllScores(redisClient *redis.Client, username string) {
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
nh := exec.Command("nethack", "-d", config.NethackLauncher.HackDir, "-s", "-v")
nh := exec.Command("nethack", "-d", config.NethackLauncher.HackDir, "-s")
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr

1
nethack-launcher/print_login_screen.go

@ -52,7 +52,6 @@ func printLoginScreen(redisClient *redis.Client) {
typedHash := sha3.Sum512([]byte(typedAuth))
if fmt.Sprintf("%x", typedHash) == storedHash {
// user authed
createUserFiles(username)
printUserScreen(redisClient, username)
noPass = false
} else {

3
nethack-launcher/print_progress_screen.go

@ -75,12 +75,11 @@ func printProgressScreen(redisClient *redis.Client, username string) {
// set ttyrec path
ttyName, _ := redisClient.Get(fmt.Sprintf("inprogress:%s", user[0])).Result()
ttyrecPath := fmt.Sprintf("%s/user/%s/ttyrec/%s.ttyrec", config.NethackLauncher.HackDir, user[0], ttyName)
fullCommand := fmt.Sprintf("ttyplay -n %s && ttyplay -p %s", ttyrecPath, ttyrecPath)
// restart display
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
nh := exec.Command("bash", "-c", fullCommand)
nh := exec.Command("ttyplay", "-p", ttyrecPath)
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr

61
nethack-launcher/print_user_screen.go

@ -12,25 +12,16 @@ import (
func printUserScreen(redisClient *redis.Client, username string) string {
clearScreen()
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
println("")
fmt.Printf(" Logged in as: %s\n", username)
println("")
println("Global")
println(" l) Logout")
println(" c) Change password")
println(" w) Watch games in progress")
println(" x) Browse library of ttyrecs")
println("")
println("Shadow of the Wyrm")
println(" f) Edit SOTW config")
fmt.Printf(" o) Play Shadow of the Wyrm %s\n", config.NethackLauncher.SotwVersion)
println("")
println("Nethack")
println(" h) View NetHack highscores")
println(" a) View all NetHack scores")
println(" e) Edit NetHack config")
println(" r) Recover NetHack from crash")
println(" h) View highscores")
println(" e) Edit config")
println(" r) Recover from crash")
fmt.Printf(" p) Play NetHack %s\n", config.NethackLauncher.NethackVersion)
println("")
println(" q) Quit")
println("")
fmt.Printf(">> ")
@ -53,32 +44,7 @@ func printUserScreen(redisClient *redis.Client, username string) string {
hackRCLoc := fmt.Sprintf("%s/user/%s/.nethackrc", config.NethackLauncher.HackDir, username)
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
nh := exec.Command("vim", "-i", "NONE", "-Z", hackRCLoc)
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr
nh.Run()
clearScreen()
printUserScreen(redisClient, username)
case "f":
// check if the file has been uplifted
SOTWLoc := fmt.Sprintf("%s/user/%s/sotw/swyrm.ini", config.NethackLauncher.HackDir, username)
//il, _ := os.Lstat(SOTWLoc)
//if il.Mode()&os.ModeSymlink == os.ModeSymlink {
// // the file is still a legacy symlink, remove and recopy
// err := os.Remove(SOTWLoc)
// if err != nil {
// fmt.Println(err)
// }
// SOTWOrigin := fmt.Sprintf("%s/swyrm.ini", config.NethackLauncher.SotwRoot)
// exec.Command("cp", SOTWOrigin, SOTWLoc).Run()
//}
//SOTWOrigin := fmt.Sprintf("%s/swyrm.ini", config.NethackLauncher.SotwRoot)
//exec.Command("cp", SOTWOrigin, SOTWLoc).Run()
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
nh := exec.Command("vim", "-i", "NONE", "-Z", SOTWLoc)
nh := exec.Command("vim", "-Z", hackRCLoc)
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr
@ -91,31 +57,14 @@ func printUserScreen(redisClient *redis.Client, username string) string {
case "w":
clearScreen()
printProgressScreen(redisClient, username)
case "x":
clearScreen()
printLibraryScreen(redisClient, username)
case "h":
clearScreen()
printHighScores(redisClient, username)
case "a":
clearScreen()
printAllScores(redisClient, username)
case "p":
wg.Add(1)
currentTime := time.Now().UTC()
fulltime := currentTime.Format("2006-01-02.03:04:05")
go runGame(username, fulltime)
go runCleanup(username)
watcher := startWatcher(username, fulltime, redisClient)
wg.Wait()
close(watcher)
printUserScreen(redisClient, username)
case "o":
wg.Add(1)
currentTime := time.Now().UTC()
fulltime := currentTime.Format("2006-01-02.03:04:05")
go runSotwGame(username, fulltime)
go runCleanup(username)
watcher := startWatcher(username, fulltime, redisClient)
wg.Wait()
close(watcher)

5
nethack-launcher/print_welcome_screen.go

@ -11,11 +11,13 @@ import (
func printWelcomeScreen(redisClient *redis.Client) string {
clearScreen()
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
println("")
println(" Not logged in.")
println("")
println(" l) Login")
println(" r) Register new user")
println(" w) Watch games in progress")
println(" h) View highscores")
println(" q) Quit")
println("")
fmt.Printf(">> ")
@ -42,6 +44,9 @@ func printWelcomeScreen(redisClient *redis.Client) string {
case "w":
clearScreen()
printProgressScreen(redisClient, "")
case "h":
clearScreen()
printHighScores(redisClient, "")
case "q":
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()

23
nethack-launcher/run_game.go

@ -40,29 +40,6 @@ func runGame(username, timestamp string) {
wg.Done()
}
func runSotwGame(username, timestamp string) {
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
clearScreen()
// put together users home dir
//homeDir := fmt.Sprintf("%s/user/%s/", config.NethackLauncher.HackDir, username)
ttyrecPath := fmt.Sprintf("%s/user/%s/ttyrec/%s.ttyrec", config.NethackLauncher.HackDir, username, timestamp)
nh := exec.Command("ttyrec", "-f", ttyrecPath, "--", "./ShadowOfTheWyrm")
//nh := exec.Command("./ShadowOfTheWyrm")
nh.Dir = (fmt.Sprintf("%s/user/%s/sotw/", config.NethackLauncher.HackDir, username))
//nh.Dir = (config.NethackLauncher.SotwRoot)
nh.Stdout = os.Stdout
nh.Stdin = os.Stdin
nh.Stderr = os.Stderr
err := nh.Run()
if err != nil {
fmt.Print(err)
}
exec.Command("exit").Run()
wg.Done()
}
func runtimeRecover(username string) {
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
println("")

Loading…
Cancel
Save