You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
8.2 KiB
326 lines
8.2 KiB
package main |
|
|
|
import ( |
|
"bufio" |
|
"fmt" |
|
"io/ioutil" |
|
"net/http" |
|
"os" |
|
"os/exec" |
|
"strings" |
|
"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 |
|
RecordLocation string |
|
ReclistLocation string |
|
BootstrapDelay time.Duration |
|
} |
|
|
|
Redis struct { |
|
Host string |
|
Password string |
|
} |
|
} |
|
|
|
var ( |
|
config = Config{} |
|
) |
|
|
|
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.") |
|
} |
|
|
|
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(config.NethackLauncher.RecordLocation); os.IsNotExist(err) { |
|
glogger.Info.Printf("record file not found in %s\n", config.NethackLauncher.RecordLocation) |
|
fmt.Printf("%s\n", err) |
|
os.Exit(1) |
|
} |
|
// make sure reclist bin exists |
|
if _, err := os.Stat(config.NethackLauncher.ReclistLocation); os.IsNotExist(err) { |
|
glogger.Info.Printf("reclist binary not found in %s\n", config.NethackLauncher.ReclistLocation) |
|
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(" 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() |
|
return ("w") |
|
case "q": |
|
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(" p) Play NetHack 3.4.3") |
|
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 "p": |
|
// restart display |
|
exec.Command("stty", "-F", "/dev/tty", "echo", "-cbreak").Run() |
|
clearScreen() |
|
os.Exit(0) |
|
case "q": |
|
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.") |
|
} |
|
} |
|
// set user in redis |
|
secHash := sha3.Sum512([]byte(sec)) |
|
redisClient.Set(fmt.Sprintf("user:%s", username), fmt.Sprintf("%x", secHash), 0).Err() |
|
printUserScreen(redisClient, username) |
|
} |
|
} |
|
if err := scanner.Err(); err != nil { |
|
fmt.Printf("%s\n", err) |
|
} |
|
} |
|
|
|
func gethighscore(w http.ResponseWriter, r *http.Request) { |
|
// run script |
|
output, err := exec.Command(config.NethackLauncher.ReclistLocation, |
|
"-f", |
|
config.NethackLauncher.RecordLocation).CombinedOutput() |
|
if err != nil { |
|
fmt.Printf("%s\n", err) |
|
} |
|
|
|
fmt.Fprintf(w, "%s\n", output) |
|
}
|
|
|