refactor telnet server and sessions into standalone types

main
Nick Dumas 2 weeks ago
parent 9781fb6736
commit b4e7c1c0a8

@ -0,0 +1,56 @@
package main
import (
"context"
"flag"
"io"
"log/slog"
"os"
"os/signal"
"syscall"
"time"
"code.ndumas.com/ndumas/muddy/core"
)
func main() {
var (
level string
logFile string
)
flag.StringVar(&level, "level", "INFO", "Log level: INFO/info, DEBUG/debug, or ERROR/error.")
flag.StringVar(&logFile, "log", "out.log", "Path to log file.")
flag.Parse()
f, err := os.Create(logFile)
if err != nil {
slog.With(
slog.Any("error", err),
).Error("could not open log file")
os.Exit(1)
}
appConfig := core.AppConfig{
Port: ":3001",
LogDest: io.MultiWriter(f, os.Stderr),
LogLevel: level,
TickInterval: time.Millisecond * 30,
}
app := core.New(appConfig)
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
cancel()
}()
app.Run(ctx)
}

@ -1,24 +1,35 @@
package core
import (
"bufio"
"context"
"io"
"log/slog"
"os"
"time"
"github.com/EngoEngine/ecs"
)
type Printer struct {
}
type AppConfig struct {
LogDest io.Writer
LogLevel string
Port string
TickInterval time.Duration
}
type App struct {
W *ecs.World
L *slog.Logger
Config AppConfig
W *ecs.World
L *slog.Logger
}
func New(level string, logFile io.Writer) *App {
func New(ac AppConfig) *App {
var logLevel slog.Level
switch level {
switch ac.LogLevel {
case "INFO", "info":
logLevel = slog.LevelInfo
case "DEBUG", "debug":
@ -28,7 +39,7 @@ func New(level string, logFile io.Writer) *App {
}
l := slog.New(slog.NewTextHandler(
logFile, &slog.HandlerOptions{
ac.LogDest, &slog.HandlerOptions{
Level: logLevel,
},
),
@ -37,34 +48,25 @@ func New(level string, logFile io.Writer) *App {
world := &ecs.World{}
return &App{
L: l,
W: world,
L: l,
W: world,
Config: ac,
}
}
func (a *App) Run(ctx context.Context) {
go func() {
t := time.NewTicker(time.Millisecond * 30)
t := time.NewTicker(a.Config.TickInterval)
for range t.C {
a.W.Update(1)
}
}()
scanner := bufio.NewScanner(os.Stdin)
var line string
for {
select {
case <-ctx.Done():
a.L.Error("cancelled")
return
default:
if scanner.Scan() {
line = scanner.Text()
}
a.L.Info("scanning input")
a.L.With(slog.String("input", line)).Info("received command")
}
}
s := NewServer(ctx, a.L, a.Config.Port)
go s.Start()
for session := range s.Sessions {
go session.Start()
}
}

@ -0,0 +1,94 @@
package core
import (
"bufio"
"context"
"fmt"
"log/slog"
"net"
)
type Server struct {
l *slog.Logger
port string
ctx context.Context
Sessions chan *Session
ln net.Listener
}
func NewServer(ctx context.Context, l *slog.Logger, port string) *Server {
var s Server
// TODO: Check for context cancellation and cleanup
c := make(chan *Session)
s.Sessions = c
ln, err := net.Listen("tcp", port)
if err != nil {
s.l.With(
slog.Any("error", err),
).Error("could not start tcp listener")
return &s
}
s.ctx = ctx
s.ln = ln
s.l = l
s.port = port
return &s
}
func (s *Server) Start() {
s.l.Debug("starting telnet server")
// TODO: Check for context cancellation and cleanup
for {
conn, err := s.ln.Accept()
if err != nil {
s.l.With(
slog.Any("error", err),
).Error("could not accept tcp connection")
}
s.Sessions <- NewSession(s.ctx, conn, Printer{}, s.l)
}
}
type Session struct {
c net.Conn
ctx context.Context
p Printer
l *slog.Logger
userID uint64
}
func NewSession(ctx context.Context, c net.Conn, p Printer, l *slog.Logger) *Session {
return &Session{
ctx: ctx,
l: l,
p: p,
c: c,
}
}
func (s *Session) Start() {
s.l.Debug("starting telnet session")
fmt.Fprint(s.c, "Welcome to Bing Bong.\n")
scanner := bufio.NewScanner(s.c)
var line string
for {
select {
case <-s.ctx.Done():
s.l.Error("cancelled")
return
default:
if scanner.Scan() {
line = scanner.Text()
fmt.Fprintf(s.c, "> %q\n", line)
}
// a.L.Info("scanning input")
s.l.With(slog.String("input", line)).Info("received command")
}
}
}
Loading…
Cancel
Save