diff --git a/README.md b/README.md
index d14d0f6..88e60b9 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
A fully native RCON client implementation, zero third parties*
-*except for other golang maintained packages, until i fully master tty :(
+*except for other golang maintained packages about terminal emulators, until i fully master tty :(

+
diff --git a/cmd/terminal.go b/cmd/terminal.go
index d972e2a..2dbf4b8 100644
--- a/cmd/terminal.go
+++ b/cmd/terminal.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "io"
"os"
"strconv"
"strings"
@@ -17,10 +18,14 @@ import (
func runRconTerminal(client *client.RCONClient, ctx context.Context, logLevel uint8) {
app := fullterm.CreateApp(fmt.Sprintf("rcon@%v", client.Address))
+ // dont worry we are resetting the logger before returning
logger.SetupCustomDestination(logLevel, app)
- errChan := make(chan error, 1)
+
+ appErrors := make(chan error, 1)
+ connectionErrors := make(chan error, 1)
+
appRun := func() {
- errChan <- app.Run(ctx)
+ appErrors <- app.Run(ctx)
}
packetChannel := packet.CreateResponseChannel(client, ctx)
packetReader := func() {
@@ -34,15 +39,19 @@ func runRconTerminal(client *client.RCONClient, ctx context.Context, logLevel ui
logger.Debug.Println("read deadline reached; connection is idle or server is silent.")
continue
}
+ if errors.Is(streamedPacket.Error, io.EOF) {
+ connectionErrors <- io.EOF
+ return
+ }
logger.Err.Println(errors.Join(errors.New("error while reading from RCON client"), streamedPacket.Error))
continue
}
fmt.Fprintf(
app,
- "(%v): RESPONSE TYPE %v\n%v\n",
+ "(%v): RCV PKT %v\n%v\n",
ansi.Format(strconv.Itoa(int(streamedPacket.Id)), ansi.Green, ansi.Bold),
ansi.Format(strconv.Itoa(int(streamedPacket.Type)), ansi.Green, ansi.Bold),
- ansi.Format(strings.TrimRight(streamedPacket.BodyStr(), "\n\r")+"\n", ansi.Green),
+ ansi.Format(strings.TrimRight(streamedPacket.BodyStr(), "\n\r"), ansi.Green),
)
}
}
@@ -55,6 +64,12 @@ func runRconTerminal(client *client.RCONClient, ctx context.Context, logLevel ui
return
case cmd := <-submissionChan:
execPacket := packet.New(client.Id(), packet.SERVERDATA_EXECCOMMAND, []byte(cmd))
+ fmt.Fprintf(
+ app,
+ "(%v): SND CMD %v\n",
+ ansi.Format(strconv.Itoa(int(client.Id())), ansi.Green, ansi.Bold),
+ ansi.Format(cmd, ansi.Blue),
+ )
client.Write(execPacket.Serialize())
}
}
@@ -62,17 +77,29 @@ func runRconTerminal(client *client.RCONClient, ctx context.Context, logLevel ui
go submissionReader()
go packetReader()
go appRun()
- for {
- select {
- case <-ctx.Done():
- return
- case err := <-errChan:
- logger.Setup(logLevel)
- logger.Debug.Println("exiting app")
- if err != nil {
- logger.Critical.Println(err)
- }
- return
+
+ select {
+ case <-ctx.Done():
+ logger.Debug.Println("context done")
+ break
+ case err := <-connectionErrors:
+ defer func() {
+ logger.Critical.Println(errors.Join(errors.New("connection error"), err))
+ }()
+ break
+ case err := <-appErrors:
+ // lets do this because the app might be unrealiable at this point
+ if err != nil {
+ defer func() {
+ logger.Critical.Println(errors.Join(errors.New("app error"), err))
+ }()
+ } else {
+ defer func() {
+ logger.Debug.Println("graceful app exit")
+ }()
}
+ break
}
+ app.Close()
+ logger.Setup(logLevel)
}
diff --git a/internal/fullterm/app.go b/internal/fullterm/app.go
index 262a8be..28f0bb3 100644
--- a/internal/fullterm/app.go
+++ b/internal/fullterm/app.go
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"os/signal"
+ "sync"
"syscall"
"github.com/UltimateForm/tcprcon/internal/ansi"
@@ -22,6 +23,7 @@ type app struct {
cmdLine []byte
content []string
commandSignature string
+ once sync.Once
}
func (src *app) Write(bytes []byte) (int, error) {
@@ -132,11 +134,13 @@ func (src *app) Run(context context.Context) error {
}
func (src *app) Close() {
- // note: consider closing channels
- fmt.Print(ansi.ExitAltScreen)
- src.DrawContent(true)
- term.Restore(src.fd, src.prevState)
- fmt.Println()
+ src.once.Do(func() {
+ // note: consider closing channels
+ fmt.Print(ansi.ExitAltScreen)
+ src.DrawContent(true)
+ term.Restore(src.fd, src.prevState)
+ fmt.Println()
+ })
}
func CreateApp(commandSignature string) *app {