From 6b20fd455a337d28f9847f87fc91177474356986 Mon Sep 17 00:00:00 2001 From: raven Date: Sat, 21 Mar 2026 03:37:02 -0500 Subject: console commands --- cmd/metronode/main.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 ++ server/commands.go | 2 +- server/player.go | 1 + server/server.go | 4 +- 5 files changed, 119 insertions(+), 4 deletions(-) diff --git a/cmd/metronode/main.go b/cmd/metronode/main.go index b7edd63..31cd8e5 100644 --- a/cmd/metronode/main.go +++ b/cmd/metronode/main.go @@ -1,12 +1,18 @@ package main import ( + "os" "net" "log" + "bufio" + "strings" + "sync/atomic" + "golang.org/x/term" "git.citrons.xyz/metronode/server" ) func main() { + defer os.Exit(0) s := server.NewServer(server.ServerInfo { Name: "Metronode", Motd: "hello, world", @@ -15,5 +21,111 @@ func main() { if err != nil { log.Fatal(err) } + + cons := initConsole() + defer cons.Close() + log.SetOutput(&cons) + go readConsole(s, &cons) + s.Serve(ln) } + +func readConsole(s *server.Server, cons *console) { + rd := bufio.NewReader(cons) + for { + line, err := rd.ReadString('\r') + if line == "" { + continue + } + if len(line) > 0 { + line = line[:len(line) - 1] + } + cons.Write([]byte("> " + line + "\n")) + if err != nil { + return + } + s.ExecuteCommand(cons, server.ConsoleAuth, line) + } +} + +type console struct { + input atomic.Pointer[string] + termState *term.State +} + +func initConsole() console { + var (c console; input string) + c.input.Store(&input) + c.termState, _ = term.MakeRaw(int(os.Stdin.Fd())) + c.showInput() + return c +} + +func (c *console) Close() error { + term.Restore(int(os.Stdin.Fd()), c.termState) + os.Stdout.Write([]byte("\n")) + return nil +} + +func (c *console) Read(p []byte) (n int, err error) { + n, err = os.Stdin.Read(p) + if err != nil { + return + } + var sb strings.Builder + for _, c := range p[:n] { + if c == '\r' { + c = '\n' + } + if c >= 32 || c == '\n' { + sb.WriteByte(c) + } + } + s := sb.String() + newline := strings.LastIndex(s, "\n") + if newline != -1 { + s = s[newline + 1:] + c.input.Store(&s) + } else { + s = *c.input.Load() + s + c.input.Store(&s) + } + c.Write([]byte{}) + return +} + +func (c *console) showInput() { + s := "\0337" // save cursor position + w, _, _ := term.GetSize(int(os.Stdout.Fd())) + s = s + strings.Repeat(" ", w) + s = s + "\0338" // restore + s = s + "\0337" // save + s = s + "> " + *c.input.Load() + os.Stdout.Write([]byte(s)) +} + +func (c *console) Write(p []byte) (n int, err error) { + s := "\0338" // restore cursor position + s = s + "\033[0J" // erase until end of screen + s = s + strings.Replace(string(p), "\n", "\r\n", -1) + n, err = os.Stdout.Write([]byte(s)) + n = max(0, n - (len(s) - len(p))) + c.showInput() + return +} + +func (c *console) OnCommandOutput(from *server.Server, output string) { + s := "\033[33m" // yellow + s = s + output + s = s + "\033[0m" // reset + s = s + "\r\n" + c.Write([]byte(s)) +} + +func (c *console) OnCommandError(from *server.Server, err string) { + s := "\033[31m" // red + s = s + err + s = s + "\033[0m" // reset + s = s + "\r\n" + c.Write([]byte(s)) +} diff --git a/go.mod b/go.mod index 948af49..97d3bc1 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module git.citrons.xyz/metronode go 1.25.7 + +require golang.org/x/term v0.41.0 + +require golang.org/x/sys v0.42.0 // indirect diff --git a/server/commands.go b/server/commands.go index 0b56aaf..8154b7b 100644 --- a/server/commands.go +++ b/server/commands.go @@ -110,7 +110,7 @@ var commands = map[string]commandHandler { sort.Strings(usages) ctx.sender.OnCommandOutput(ctx.server, "Available commands:") for _, usage := range usages { - ctx.sender.OnCommandOutput(ctx.server, "&7* &e" + usage) + ctx.sender.OnCommandOutput(ctx.server, "* " + usage) } return "" } diff --git a/server/player.go b/server/player.go index 4a562a7..ba4767c 100644 --- a/server/player.go +++ b/server/player.go @@ -30,6 +30,7 @@ type playerState struct { const ( defaultAuth = iota opAuth + ConsoleAuth ) var playerNameRegex = regexp.MustCompile("^[.-_a-zA-Z0-9]*$") diff --git a/server/server.go b/server/server.go index 2740e09..834d5fe 100644 --- a/server/server.go +++ b/server/server.go @@ -95,9 +95,7 @@ func (s *Server) Serve(ln net.Listener) { case <-ping: s.SendPings() case <-tick: s.Tick() case <-save: s.Save(nil) - case <-sigterm: - fmt.Println("") - s.Stop(nil) + case <-sigterm: s.Stop(nil) case <-s.stopped: return } } -- cgit v1.2.3