|
|
|
package main
|
|
|
|
|
|
|
|
// TODO make poll time faster
|
|
|
|
// TODO password redo field
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/unixvoid/glogger"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
|
|
"gopkg.in/gcfg.v1"
|
|
|
|
"gopkg.in/redis.v5"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
NethackLauncher struct {
|
|
|
|
Loglevel string
|
|
|
|
ServerDisplay string
|
|
|
|
NethackVersion string
|
|
|
|
HackDir string
|
|
|
|
NhdatLocation string
|
|
|
|
BootstrapDelay time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
Redis struct {
|
|
|
|
Host string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
config = Config{}
|
|
|
|
wg sync.WaitGroup
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
// read conf file
|
|
|
|
readConf()
|
|
|
|
// init config file and logger
|
|
|
|
initLogger()
|
|
|
|
// init redis connection
|
|
|
|
redisClient, redisErr := initRedisConnection()
|
|
|
|
if redisErr != nil {
|
|
|
|
//glogger.Debug.Printf("redis connection cannot be made, trying again in %s second(s)\n", config.NethackLauncher.BootstrapDelay*time.Second)
|
|
|
|
time.Sleep(config.NethackLauncher.BootstrapDelay * time.Second)
|
|
|
|
redisClient, redisErr = initRedisConnection()
|
|
|
|
if redisErr != nil {
|
|
|
|
//glogger.Error.Println("redis connection cannot be made, exiting.")
|
|
|
|
panic(redisErr)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//glogger.Debug.Println("connection to redis succeeded.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// create initial files needed by nethack
|
|
|
|
createInitialFiles()
|
|
|
|
|
|
|
|
// start janitor
|
|
|
|
go janitor(redisClient)
|
|
|
|
|
|
|
|
// start homescreen
|
|
|
|
screenFunction := printWelcomeScreen(redisClient)
|
|
|
|
fmt.Printf("screen %s recieved\n", screenFunction)
|
|
|
|
}
|
|
|
|
|
|
|
|
func readConf() {
|
|
|
|
// init config file
|
|
|
|
err := gcfg.ReadFileInto(&config, "config.gcfg")
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Could not load config.gcfg, error: %s\n", err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func initLogger() {
|
|
|
|
// init logger
|
|
|
|
if config.NethackLauncher.Loglevel == "debug" {
|
|
|
|
glogger.LogInit(os.Stdout, os.Stdout, os.Stdout, os.Stderr)
|
|
|
|
} else if config.NethackLauncher.Loglevel == "cluster" {
|
|
|
|
glogger.LogInit(os.Stdout, os.Stdout, ioutil.Discard, os.Stderr)
|
|
|
|
} else if config.NethackLauncher.Loglevel == "info" {
|
|
|
|
glogger.LogInit(os.Stdout, ioutil.Discard, ioutil.Discard, os.Stderr)
|
|
|
|
} else {
|
|
|
|
glogger.LogInit(ioutil.Discard, ioutil.Discard, ioutil.Discard, os.Stderr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func initRedisConnection() (*redis.Client, error) {
|
|
|
|
// init redis connection
|
|
|
|
redisClient := redis.NewClient(&redis.Options{
|
|
|
|
Addr: config.Redis.Host,
|
|
|
|
Password: config.Redis.Password,
|
|
|
|
DB: 0,
|
|
|
|
})
|
|
|
|
|
|
|
|
_, redisErr := redisClient.Ping().Result()
|
|
|
|
return redisClient, redisErr
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkFiles() {
|
|
|
|
// make sure record file exists
|
|
|
|
if _, err := os.Stat(fmt.Sprintf("%s/record", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
|
|
|
|
glogger.Info.Printf("record file not found in %s/record\n", config.NethackLauncher.HackDir)
|
|
|
|
fmt.Printf("%s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
// make sure initial rcfile exists
|
|
|
|
hackRCLoc := fmt.Sprintf("%s/.nethackrc", config.NethackLauncher.HackDir)
|
|
|
|
if _, err := os.Stat(hackRCLoc); os.IsNotExist(err) {
|
|
|
|
glogger.Info.Printf("initial config file not found at: %s\n", hackRCLoc)
|
|
|
|
fmt.Printf("%s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func clearScreen() {
|
|
|
|
cmd := exec.Command("clear")
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
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(">> ")
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
switch string(b) {
|
|
|
|
case "l":
|
|
|
|
// restart display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
printLoginScreen(redisClient)
|
|
|
|
case "r":
|
|
|
|
// restart display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
printRegisterScreen(redisClient)
|
|
|
|
case "w":
|
|
|
|
clearScreen()
|
|
|
|
printProgressScreen(redisClient, "")
|
|
|
|
case "h":
|
|
|
|
clearScreen()
|
|
|
|
printHighScores(redisClient, "")
|
|
|
|
case "q":
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
os.Exit(0)
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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(" l) Logout")
|
|
|
|
println(" w) Watch games in progress")
|
|
|
|
println(" h) View highscores")
|
|
|
|
println(" e) Edit config")
|
|
|
|
fmt.Printf(" p) Play NetHack %s\n", config.NethackLauncher.NethackVersion)
|
|
|
|
println(" q) Quit")
|
|
|
|
println("")
|
|
|
|
fmt.Printf(">> ")
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
switch string(b) {
|
|
|
|
case "l":
|
|
|
|
// restart display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
case "e":
|
|
|
|
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", "-Z", hackRCLoc)
|
|
|
|
nh.Stdout = os.Stdout
|
|
|
|
nh.Stdin = os.Stdin
|
|
|
|
nh.Stderr = os.Stderr
|
|
|
|
nh.Run()
|
|
|
|
clearScreen()
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
case "w":
|
|
|
|
clearScreen()
|
|
|
|
printProgressScreen(redisClient, username)
|
|
|
|
case "h":
|
|
|
|
clearScreen()
|
|
|
|
printHighScores(redisClient, username)
|
|
|
|
case "p":
|
|
|
|
wg.Add(1)
|
|
|
|
currentTime := time.Now().UTC()
|
|
|
|
fulltime := currentTime.Format("2006-01-02.03:04:05")
|
|
|
|
go runGame(username, fulltime)
|
|
|
|
watcher := startWatcher(username, fulltime, redisClient)
|
|
|
|
wg.Wait()
|
|
|
|
close(watcher)
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
case "q":
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
os.Exit(0)
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printLoginScreen(redisClient *redis.Client) {
|
|
|
|
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
|
|
|
|
println("")
|
|
|
|
println(" Please enter your username. (blank entry aborts)")
|
|
|
|
println("")
|
|
|
|
fmt.Printf(">> ")
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
for scanner.Scan() {
|
|
|
|
if scanner.Text() == "" {
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check redis for user
|
|
|
|
username := scanner.Text()
|
|
|
|
storedHash, err := redisClient.Get(fmt.Sprintf("user:%s", username)).Result()
|
|
|
|
if err != nil {
|
|
|
|
// user does not exist
|
|
|
|
fmt.Printf(" There was a problem with your last entry.\n>> ")
|
|
|
|
} else {
|
|
|
|
// get password from user and compare
|
|
|
|
// turn off echo display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
|
|
|
|
|
|
|
|
noPass := true
|
|
|
|
for noPass {
|
|
|
|
fmt.Printf("\n Please enter your password (blank entry aborts).\n>> ")
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
|
|
|
|
typedAuth, _ := reader.ReadString('\n')
|
|
|
|
typedAuth = strings.Replace(typedAuth, "\n", "", -1)
|
|
|
|
|
|
|
|
if typedAuth == "" {
|
|
|
|
// exit to main menu
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
// get hash of typedAuth
|
|
|
|
typedHash := sha3.Sum512([]byte(typedAuth))
|
|
|
|
if fmt.Sprintf("%x", typedHash) == storedHash {
|
|
|
|
// user authed
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
noPass = false
|
|
|
|
} else {
|
|
|
|
fmt.Printf("\n There was a problem with your last entry.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
fmt.Printf("%s\n", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printRegisterScreen(redisClient *redis.Client) {
|
|
|
|
// TODO : configure password restriction checking
|
|
|
|
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
|
|
|
|
println("")
|
|
|
|
println(" Welcome new user. Please enter a username")
|
|
|
|
println(" Only characters and numbers are allowed, with no spaces.")
|
|
|
|
println(" 20 characters max. (blank entry aborts)")
|
|
|
|
println("")
|
|
|
|
fmt.Printf(">> ")
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
for scanner.Scan() {
|
|
|
|
if scanner.Text() == "" {
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check redis for user
|
|
|
|
username := scanner.Text()
|
|
|
|
_, err := redisClient.Get(fmt.Sprintf("user:%s", username)).Result()
|
|
|
|
if err != redis.Nil {
|
|
|
|
// user already exists
|
|
|
|
fmt.Printf("There was a problem with your last entry.\n>> ")
|
|
|
|
} else {
|
|
|
|
// set up user
|
|
|
|
|
|
|
|
// turn off echo display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
|
|
|
|
noPass := true
|
|
|
|
sec := ""
|
|
|
|
for noPass {
|
|
|
|
// pull pass the first time
|
|
|
|
fmt.Printf(" Please enter your password (blank entry aborts).\n>> ")
|
|
|
|
sec0, _ := reader.ReadString('\n')
|
|
|
|
sec0 = strings.Replace(sec0, "\n", "", -1)
|
|
|
|
if sec0 == "" {
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
// pull pass the second time
|
|
|
|
fmt.Printf("\n Please enter your password again.\n>> ")
|
|
|
|
sec1, _ := reader.ReadString('\n')
|
|
|
|
sec1 = strings.Replace(sec1, "\n", "", -1)
|
|
|
|
|
|
|
|
// make sure passwords match
|
|
|
|
if sec0 == sec1 {
|
|
|
|
sec = sec0
|
|
|
|
noPass = false
|
|
|
|
} else {
|
|
|
|
fmt.Println("Lets try that again.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
|
|
|
|
// set user in redis
|
|
|
|
secHash := sha3.Sum512([]byte(sec))
|
|
|
|
redisClient.Set(fmt.Sprintf("user:%s", username), fmt.Sprintf("%x", secHash), 0).Err()
|
|
|
|
|
|
|
|
// create user directories
|
|
|
|
userPath := fmt.Sprintf("%s/user/%s/ttyrec/", config.NethackLauncher.HackDir, username)
|
|
|
|
exec.Command("mkdir", "-p", userPath).Run()
|
|
|
|
|
|
|
|
// copy in rc file
|
|
|
|
hackRCLoc := fmt.Sprintf("%s/.nethackrc", config.NethackLauncher.HackDir)
|
|
|
|
hackRCDest := fmt.Sprintf("%s/user/%s/.nethackrc", config.NethackLauncher.HackDir, username)
|
|
|
|
exec.Command("cp", hackRCLoc, hackRCDest).Run()
|
|
|
|
|
|
|
|
// back to main screen
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
fmt.Printf("%s\n", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printProgressScreen(redisClient *redis.Client, username string) {
|
|
|
|
// print header
|
|
|
|
fmt.Printf(" %s\n", config.NethackLauncher.ServerDisplay)
|
|
|
|
println("")
|
|
|
|
|
|
|
|
// check directory for live players
|
|
|
|
isNotEmpty, err := redisClient.Exists("inprogress").Result()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if isNotEmpty {
|
|
|
|
println(" Choose a player to spectate ('enter' without selection returns)")
|
|
|
|
} else {
|
|
|
|
println(" No live players currently (blank entry returns)")
|
|
|
|
}
|
|
|
|
println("")
|
|
|
|
|
|
|
|
inProg := make(map[int]string)
|
|
|
|
inProgTimer := 1
|
|
|
|
inprogress, err := redisClient.SMembers("inprogress").Result()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for _, i := range inprogress {
|
|
|
|
// loop through users
|
|
|
|
fmt.Printf(" %d) %s\n", inProgTimer, i)
|
|
|
|
|
|
|
|
// add user to line map
|
|
|
|
inProg[inProgTimer] = i
|
|
|
|
inProgTimer++
|
|
|
|
}
|
|
|
|
|
|
|
|
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 == "" {
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
} else {
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if selection is in out map
|
|
|
|
if inProg[s] != "" {
|
|
|
|
user := strings.Split(inProg[s], ":")
|
|
|
|
fmt.Printf("going to spectate '%s'\n", user[0])
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
// restart display
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
nh := exec.Command("ttyplay", "-p", ttyrecPath)
|
|
|
|
//nh := exec.Command("termplay", "-f", "live", ttyrecPath)
|
|
|
|
nh.Stdout = os.Stdout
|
|
|
|
nh.Stdin = os.Stdin
|
|
|
|
nh.Stderr = os.Stderr
|
|
|
|
nh.Run()
|
|
|
|
if username == "" {
|
|
|
|
printWelcomeScreen(redisClient)
|
|
|
|
} else {
|
|
|
|
printUserScreen(redisClient, username)
|
|
|
|
}
|
|
|
|
// TODO fix bug where user has to <ctrl-c> when game exists
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func runGame(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, "--", "nethack")
|
|
|
|
nh.Env = os.Environ()
|
|
|
|
nh.Env = append(nh.Env, fmt.Sprintf("HOME=%s", homeDir))
|
|
|
|
nh.Env = append(nh.Env, fmt.Sprintf("USER=%s", username))
|
|
|
|
nh.Env = append(nh.Env, fmt.Sprintf("NETHACKDIR=%s", config.NethackLauncher.HackDir))
|
|
|
|
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 startWatcher(username, timestamp string, redisClient *redis.Client) chan struct{} {
|
|
|
|
// create initial keys
|
|
|
|
redisClient.SAdd("inprogress", username)
|
|
|
|
redisClient.Set(fmt.Sprintf("inprogress:%s", username), timestamp, 0)
|
|
|
|
|
|
|
|
ch := make(chan struct{})
|
|
|
|
// enter inital inprogress yet
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ch:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
redisClient.Expire(fmt.Sprintf("inprogress:%s", username), 10*time.Second)
|
|
|
|
time.Sleep(8 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func createInitialFiles() {
|
|
|
|
// create necessary directories if they dont exist
|
|
|
|
if _, err := os.Stat(config.NethackLauncher.HackDir); os.IsNotExist(err) {
|
|
|
|
os.Mkdir(config.NethackLauncher.HackDir, os.ModeDir)
|
|
|
|
}
|
|
|
|
if _, err := os.Stat(fmt.Sprintf("%s/dumps/", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
|
|
|
|
os.Mkdir(fmt.Sprintf("%s/dumps/", config.NethackLauncher.HackDir), os.ModeDir)
|
|
|
|
}
|
|
|
|
if _, err := os.Stat(fmt.Sprintf("%s/save/", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
|
|
|
|
os.Mkdir(fmt.Sprintf("%s/save/", config.NethackLauncher.HackDir), os.ModeDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create necessary files if they dont exist
|
|
|
|
os.OpenFile(fmt.Sprintf("%s/logfile", config.NethackLauncher.HackDir), os.O_RDONLY|os.O_CREATE, 0666)
|
|
|
|
os.OpenFile(fmt.Sprintf("%s/perm", config.NethackLauncher.HackDir), os.O_RDONLY|os.O_CREATE, 0666)
|
|
|
|
os.OpenFile(fmt.Sprintf("%s/record", config.NethackLauncher.HackDir), os.O_RDONLY|os.O_CREATE, 0666)
|
|
|
|
os.OpenFile(fmt.Sprintf("%s/xlogfile", config.NethackLauncher.HackDir), os.O_RDONLY|os.O_CREATE, 0666)
|
|
|
|
|
|
|
|
// move in nhdat file if it does not exist
|
|
|
|
if _, err := os.Stat(fmt.Sprintf("%s/nhdat", config.NethackLauncher.HackDir)); os.IsNotExist(err) {
|
|
|
|
exec.Command("cp", config.NethackLauncher.NhdatLocation, config.NethackLauncher.HackDir).Run()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func janitor(redisClient *redis.Client) {
|
|
|
|
// loop through the set
|
|
|
|
for {
|
|
|
|
inprogress, err := redisClient.SMembers("inprogress").Result()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for _, i := range inprogress {
|
|
|
|
// for each user, make sure the expire key exists
|
|
|
|
exists, err := redisClient.Exists(fmt.Sprintf("inprogress:%s", i)).Result()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
redisClient.SRem("inprogress", i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printHighScores(redisClient *redis.Client, username string) {
|
|
|
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run()
|
|
|
|
clearScreen()
|
|
|
|
|
|
|
|
nh := exec.Command("nethack", "-d", config.NethackLauncher.HackDir, "-s")
|
|
|
|
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 checkDir(dirName string) (bool, error) {
|
|
|
|
f, err := os.Open(dirName)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
_, err = f.Readdirnames(1)
|
|
|
|
if err == io.EOF {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|