diff --git a/go.mod b/go.mod index 619d066..07205c0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module krumel.moe/serverwrapper go 1.16 require ( - github.com/awesome-gocui/gocui v1.0.0 // indirect + github.com/awesome-gocui/gocui v1.0.0 github.com/jroimartin/gocui v0.4.0 // indirect github.com/nsf/termbox-go v1.1.1 // indirect github.com/pelletier/go-toml v1.9.1 diff --git a/serverwrapper.go b/serverwrapper.go index f21e4d2..f56427d 100644 --- a/serverwrapper.go +++ b/serverwrapper.go @@ -4,9 +4,13 @@ import ( "bufio" "fmt" "io" + "io/ioutil" "log" - "time" "os/exec" + "path" + "syscall" + "time" + "github.com/awesome-gocui/gocui" "github.com/pelletier/go-toml" ) @@ -14,6 +18,7 @@ import ( var ( settings *toml.Tree srv_stdin io.WriteCloser + srv_cmd *exec.Cmd ) @@ -22,26 +27,70 @@ func reload() { settings, _ = toml.LoadFile("serverwrapper.toml") } +func settings_list(option string) []string { + list := settings.Get(option).([]interface{}) + str_array := make([]string, len(list)) + for i := range list { + str_array[i] = list[i].(string) + } + return str_array +} + //builds the cmd which is called to run the server func build_cmd() (string, []string) { - return "ping", []string{"-t", "8.8.8.8"} + //for debugging + //return "ping", []string{"8.8.8.8"} + + java_args := []string{"-jar"} + + jvm_args := settings_list("server.jvm-args") + java_args = append(java_args, jvm_args...) + + forge_path := path.Join(settings.Get("server.directory").(string), settings.Get("server.forge").(string)) + java_args = append(java_args, forge_path) + + jar_args := settings_list("server.jar-args") + java_args = append(java_args, jar_args...) + + return "java", java_args } //run the server-thread func server_run(g *gocui.Gui) { //create the command cmd_str, cmd_args := build_cmd() - cmd := exec.Command(cmd_str, cmd_args...) + srv_cmd = exec.Command(cmd_str, cmd_args...) + + //execute in the server directory + srv_cmd.Dir = settings.Get("server.directory").(string) //connect pipes - stdout, err := cmd.StdoutPipe() - cmd.Stderr = cmd.Stdout - srv_stdin, err = cmd.StdinPipe() //this one is global, because we write to it elsewhere + stdout, err := srv_cmd.StdoutPipe() + srv_cmd.Stderr = srv_cmd.Stdout + srv_stdin, err = srv_cmd.StdinPipe() //this one is global, because we write to it elsewhere + + //debugging stuff + cmd_bytes := []byte(srv_cmd.String()) + ioutil.WriteFile("cmd.txt", cmd_bytes, 0644) + + g.Update(func(g *gocui.Gui) error { + v, err := g.View("srv_log") + g.SetCurrentView("srv_log") + if err != nil { + return err + } + fmt.Fprintln(v, cmd_str) + for i := range cmd_args { + fmt.Fprintln(v, cmd_args[i]) + } + g.SetCurrentView("input") + return nil + }) // run the process - err = cmd.Start() + err = srv_cmd.Start() if err != nil { - log.Panicln("Failed to call server command \"" + cmd.String() + "\"") + log.Panicln("Failed to call server command \"" + srv_cmd.String() + "\"") } //read pipes and write them to the ring buffer @@ -50,14 +99,16 @@ func server_run(g *gocui.Gui) { if buf.Scan() { g.Update(func(g *gocui.Gui) error { v, err := g.View("srv_log") + g.SetCurrentView("srv_log") if err != nil { return err } fmt.Fprintln(v, buf.Text()) + g.SetCurrentView("input") return nil }) } else { - time.Sleep(300 * time.Millisecond) + time.Sleep(10 * time.Millisecond) } } } @@ -67,7 +118,7 @@ func main() { reload() //init the CUI - g, err := gocui.NewGui(gocui.OutputNormal, false) + g, err := gocui.NewGui(gocui.OutputNormal, true) if err != nil { log.Panicln(err) } @@ -75,10 +126,20 @@ func main() { g.SetManagerFunc(layout) + g.Cursor = false + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyCtrlI, gocui.ModNone, procstatus); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, killserver); err != nil { + log.Panicln(err) + } + go server_run(g) //run the CUI main loop @@ -87,22 +148,117 @@ func main() { } } +func inputHandler(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { + switch { + case ch != 0 && mod == 0: + v.EditWrite(ch) + case key == gocui.KeyBackspace || key == gocui.KeyBackspace2: + v.EditDelete(true) + case key == gocui.KeyEnter: + s := v.Buffer() + srv_stdin.Write([]byte(s)) + v.EditDeleteToStartOfLine() + } +} + //create or update the CUI layout func layout(g *gocui.Gui) error { maxX, maxY := g.Size() - v, err := g.SetView("srv_log", 0, 0, maxX-1, int(float32(maxY) * 0.8), 0) + v_log, err := g.SetView("srv_log", 0, 0, maxX-1, maxY-5, 0) if err != nil { if err != gocui.ErrUnknownView { return err } //*only* put initialization code for the view here - v.Autoscroll = true + v_log.Autoscroll = true + v_log.Title = "Server Output" + v_log.Overlaps = gocui.BOTTOM } + v_status, err := g.SetView("status", 0, maxY-5, maxX-1, maxY-3, 0) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + //*only* put initialization code for the view here + v_status.Title = "Status" + v_status.Overlaps = gocui.TOP | gocui.BOTTOM + } + + v_in, err := g.SetView("input", 0, maxY-3, maxX-1, maxY-1, 0) + if err != nil { + if err != gocui.ErrUnknownView { + return err + } + //*only* put initialization code for the view here + v_in.Title = "Input" + v_in.Editable = true + v_in.Editor = gocui.EditorFunc(inputHandler) + v_in.Overlaps = gocui.TOP + } + + g.SetCurrentView("input") + + return nil +} + +func killserver(g *gocui.Gui, v *gocui.View) error { + + v_status, err := g.View("status") + if err != nil { + return err + } + v_status.Clear() + err = srv_cmd.Process.Signal(syscall.SIGTERM) + if err != nil { + fmt.Fprintf(v_status, fmt.Sprint(err)) + } + err = srv_cmd.Wait() + if err != nil { + fmt.Fprintf(v_status, fmt.Sprint(err)) + } + return nil +} + +func procstatus(g *gocui.Gui, v *gocui.View) error { + + v_status, err := g.View("status") + if err != nil { + return err + } + v_status.Clear() + fmt.Fprintf(v_status, fmt.Sprint(srv_cmd.ProcessState)) return nil } func quit(g *gocui.Gui, v *gocui.View) error { + v_status, err := g.View("status") + if err != nil { + return err + } + + if srv_cmd != nil { + if srv_cmd.Process != nil { + if srv_cmd.ProcessState != nil { + if srv_cmd.ProcessState.Exited() { + return gocui.ErrQuit + } else { + v_status.Clear() + fmt.Fprintf(v_status, fmt.Sprint(srv_cmd.ProcessState)) + } + } else { + v_status.Clear() + fmt.Fprintf(v_status, "Server process still running, sending SIGTERM... ") + srv_cmd.Process.Signal(syscall.SIGTERM) + err = srv_cmd.Wait() + if err != nil { + fmt.Fprintf(v_status, fmt.Sprint(err)) + } + return nil + } + } + + } return gocui.ErrQuit } \ No newline at end of file diff --git a/serverwrapper.toml b/serverwrapper.toml index f430b93..70d0a6d 100644 --- a/serverwrapper.toml +++ b/serverwrapper.toml @@ -2,8 +2,8 @@ java_cmd = "java" [server] -directory = "/home/minecraft/Valhelsia 3.1.6" -forge = "forge-1.16.5-36.0.1.jar" +directory = "/home/minecraft/Slightly Cinnamon Flavoured 2.0.0" +forge = "forge-1.16.5-36.1.0.jar" jvm-args = [ "-Xms20G", "-Xmx20G",