diff --git a/.gitignore b/.gitignore index 9491a2f..88afcfb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +<<<<<<< HEAD ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## @@ -360,4 +361,10 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +# Project Specific +vendor/ +shipsgs +go.sum +runlog.log diff --git a/config.json b/config.json new file mode 100644 index 0000000..1369098 --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "Address": ":8080", + "AdminSecret": "netadmin" +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8a0cdf0 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module daydev.org/shipsgs + +go 1.17 + +require github.com/xlab/closer v1.1.0 + +require github.com/cristalhq/aconfig v0.18.3 // indirect diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..1ef16d4 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,61 @@ +package config + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" +) + +const ConfigPath string = "config.json" + +type S_Config struct { + Address string `json:"Address"` + AdminSecret string `json:"AdminSecret"` +} + +var Config S_Config + +func initialize() error { + configExists, err := os.Open(ConfigPath) + + if err != nil { + log.Println("management.initialize: new deployment: building config") + + configNew, err := os.OpenFile("config.json", os.O_CREATE|os.O_WRONLY, 0644) + if err == nil { + Config.Address = ":8080" + Config.AdminSecret = "netadmin" + + configByte, err := json.MarshalIndent(Config, "", " ") + if err != nil { + log.Fatal("config.initialize: could not Marshall Settings: ", err) + return err + } + configNew.Write(configByte) + } else { + log.Fatal("config.initialize: could not create new config.json file: ", err) + return err + } + + defer configNew.Close() + } else { + configByte, err := ioutil.ReadAll(configExists) + if err != nil { + log.Fatal("config.initialize: could not read config: ", err) + return err + } + + json.Unmarshal(configByte, &Config) + + defer configExists.Close() + } + + return nil +} + +func ReadConfig() { + // If doesnt exist, write default config + + initialize() +} diff --git a/internal/player/player.go b/internal/player/player.go new file mode 100644 index 0000000..452c176 --- /dev/null +++ b/internal/player/player.go @@ -0,0 +1,17 @@ +package player + +type Player struct { + Name string `json:"Name"` + Password string `json:"Password"` + + AuthString string `json:"AuthString"` + + Level string `json:"Level"` // hidden from user, for balancing purposes + + Kills int `json:"Kills"` + Killed int `json:"Killed"` + + Won int `json:"Won"` + Lost int `json:"Lost"` + WinRate int `json:"WinRate"` +} diff --git a/internal/server/server.go b/internal/server/server.go new file mode 100644 index 0000000..ab3dd53 --- /dev/null +++ b/internal/server/server.go @@ -0,0 +1,38 @@ +package server + +import ( + "context" + "fmt" + "log" + "net/http" +) + +func index(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "index") + +} + +func wsEndpoint(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "hello") +} + +func setupRoutes() { + http.HandleFunc("/", index) + http.HandleFunc("/ws", wsEndpoint) + +} + +func SetupAndRun(port string, sighup *chan bool) { + setupRoutes() + + srv := &http.Server{Addr: port} + + go func() { + <-*sighup + log.Println("Shutting down the webserver") + srv.Shutdown(context.TODO()) + }() + + log.Fatal(srv.ListenAndServe()) + +} diff --git a/internal/utils/jsonlog.go b/internal/utils/jsonlog.go new file mode 100644 index 0000000..4fd9caf --- /dev/null +++ b/internal/utils/jsonlog.go @@ -0,0 +1,107 @@ +package utils + +import ( + "encoding/json" + "io" + "os" + "runtime/debug" + "sync" + "time" +) + +// Define a Level type to represent the severity level for a log entry. +type Level int8 + +// Initialize constants which represent a specific severity level. We use the iota +// keyword as a shortcut to +const ( + LevelInfo Level = iota + LevelError + LevelFatal + LevelOff +) + +// assign successive integer values to the constants. +// Has the value 0. // Has the value 1. // Has the value 2. // Has the value 3. +// Return a human-friendly string for the severity level. +func (l Level) String() string { + switch l { + case LevelInfo: + return "INFO" + case LevelError: + return "ERROR" + case LevelFatal: + return "FATAL" + default: + return "" + } +} + +// Define a custom Logger type. This holds the output destination that the log entries // will be written to, the minimum severity level that log entries will be written for, // plus a mutex for coordinating the writes. +type Logger struct { + out io.Writer + minLevel Level + mu sync.Mutex +} + +// Return a new Logger instance which writes log entries at or above a minimum severity // level to a specific output destination. +func New(out io.Writer, minLevel Level) *Logger { + return &Logger{out: out, + minLevel: minLevel} +} + +// Declare some helper methods for writing log entries at the different levels. Notice // that these all accept a map as the second parameter which can contain any arbitrary // 'properties' that you want to appear in the log entry. +func (l *Logger) PrintInfo(message string, properties map[string]string) { + l.print(LevelInfo, message, properties) +} +func (l *Logger) PrintError(err error, properties map[string]string) { + l.print(LevelError, err.Error(), properties) +} +func (l *Logger) PrintFatal(err error, properties map[string]string) { + l.print(LevelFatal, err.Error(), properties) + os.Exit(1) // For entries at the FATAL level, we also terminate the application. +} + +// Print is an internal method for writing the log entry. +func (l *Logger) print(level Level, message string, properties map[string]string) (int, error) { // If the severity level of the log entry is below the minimum severity for the + // logger, then return with no further action. + if level < l.minLevel { + return 0, nil + } + // Declare an anonymous struct holding the data for the log entry. + + aux := struct { + Level string `json:"level"` + Time string `json:"time"` + Message string `json:"message"` + Properties map[string]string `json:"properties,omitempty"` + Trace string `json:"trace,omitempty"` + }{ + Level: level.String(), + Time: time.Now().UTC().Format(time.RFC3339), Message: message, + Properties: properties, + } + // Include a stack trace for entries at the ERROR and FATAL levels. + if level >= LevelError { + aux.Trace = string(debug.Stack()) + } + // Declare a line variable for holding the actual log entry text. + var line []byte + // Marshal the anonymous struct to JSON and store it in the line variable. If there // was a problem creating the JSON, set the contents of the log entry to be that + // plain-text error message instead. + line, err := json.Marshal(aux) + if err != nil { + line = []byte(LevelError.String() + ": unable to marshal log message: " + err.Error()) + } + // Lock the mutex so that no two writes to the output destination cannot happen // concurrently. If we don't do this, it's possible that the text for two or more // log entries will be intermingled in the output. + l.mu.Lock() + defer l.mu.Unlock() + // Write the log entry followed by a newline. + return l.out.Write(append(line, '\n')) +} + +// We also implement a Write() method on our Logger type so that it satisfies the +// io.Writer interface. This writes a log entry at the ERROR level with no additional // properties. +func (l *Logger) Write(message []byte) (n int, err error) { + return l.print(LevelError, string(message), nil) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..67ea9d8 --- /dev/null +++ b/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "log" + "os" + + "daydev.org/shipsgs/internal/config" + "daydev.org/shipsgs/internal/server" + "daydev.org/shipsgs/internal/utils" + "github.com/xlab/closer" +) + +var Logger *utils.Logger +var ch_sighup *chan bool + +func main() { + logFile, err := os.OpenFile("runlog.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Println(err) + } else { + log.SetOutput(logFile) + } + defer logFile.Close() + + closer.Bind(cleanup) + + sighup := make(chan bool) + ch_sighup = &sighup + + //If debug - fill Stdout too - LOG to use? + //Logger = utils.New(os.Stdout, utils.LevelInfo) + + config.ReadConfig() + + log.Println("Starting Healthy") + + //closer.Hold() + server.SetupAndRun(":8080", ch_sighup) + +} + +func cleanup() { + fmt.Println("Shutting down ") + + *ch_sighup <- true + + Logger.PrintInfo("main", map[string]string{ + "Info": "Closing Application Normally", + }) + +}