refact round 2
This commit is contained in:
123
engine/server.go
Normal file
123
engine/server.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config holds the BBS server configuration
|
||||
type Config struct {
|
||||
Host string
|
||||
Port string
|
||||
Banner string
|
||||
Lang T
|
||||
}
|
||||
|
||||
// BBS is the main server struct
|
||||
type BBS struct {
|
||||
config Config
|
||||
state *State
|
||||
menu *Menu
|
||||
}
|
||||
|
||||
// New creates a new BBS server with the given configuration
|
||||
func New(cfg Config) *BBS {
|
||||
return &BBS{
|
||||
config: cfg,
|
||||
state: newState(),
|
||||
menu: &Menu{},
|
||||
}
|
||||
}
|
||||
|
||||
// State returns the shared server state (for use by content handlers)
|
||||
func (b *BBS) State() *State {
|
||||
return b.state
|
||||
}
|
||||
|
||||
// Menu configures the main menu using the DSL
|
||||
func (b *BBS) Menu(fn func(*Menu)) {
|
||||
fn(b.menu)
|
||||
}
|
||||
|
||||
// Start launches the TCP server and blocks waiting for connections
|
||||
func (b *BBS) Start() {
|
||||
ln, err := net.Listen("tcp", b.config.Host+":"+b.config.Port)
|
||||
if err != nil {
|
||||
fmt.Printf("Server start error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
fmt.Printf("BBS running → telnet localhost %s\n", b.config.Port)
|
||||
fmt.Println("Stop: Ctrl+C")
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("Accept error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("[+] Connected: %s\n", conn.RemoteAddr().String())
|
||||
go b.handleClient(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BBS) handleClient(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
addr := conn.RemoteAddr().String()
|
||||
lang := b.config.Lang
|
||||
printer := NewPrinter(conn)
|
||||
|
||||
// Telnet negotiation (IAC WILL ECHO, IAC WILL SGA, IAC WONT LINEMODE)
|
||||
printer.Send("\xff\xfb\x01\xff\xfb\x03\xff\xfe\x22")
|
||||
|
||||
printer.Send(CY + b.config.Banner + R)
|
||||
printer.Send(fmt.Sprintf("\n%s%s%s ", WH, lang["AskName"], R))
|
||||
|
||||
username, err := printer.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
username = strings.TrimSpace(username)
|
||||
if username == "" {
|
||||
username = "Anonymous"
|
||||
}
|
||||
if len(username) > 20 {
|
||||
username = username[:20]
|
||||
}
|
||||
|
||||
b.state.Mu.Lock()
|
||||
b.state.OnlineUsers[addr] = username
|
||||
b.state.Mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
b.state.Mu.Lock()
|
||||
delete(b.state.OnlineUsers, addr)
|
||||
b.state.Mu.Unlock()
|
||||
}()
|
||||
|
||||
printer.Send(fmt.Sprintf("\r\n%s%s%s\r\n", GR, fmt.Sprintf(lang["Greeting"], WH, username, GR), R))
|
||||
|
||||
session := &Session{
|
||||
State: b.state,
|
||||
Printer: printer,
|
||||
Username: username,
|
||||
Addr: addr,
|
||||
Lang: lang,
|
||||
}
|
||||
|
||||
for !session.Quit {
|
||||
printer.Send(b.menu.Render(printer, lang, username))
|
||||
choice, err := printer.ReadLine()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
b.menu.Dispatch(session, choice)
|
||||
}
|
||||
|
||||
if session.Quit {
|
||||
printer.Send(fmt.Sprintf("\r\n%s%s%s\r\n\r\n", RD, fmt.Sprintf(lang["Goodbye"], username), R))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user