aboutsummaryrefslogtreecommitdiff
path: root/internal/net/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/net/server.go')
-rw-r--r--internal/net/server.go166
1 files changed, 166 insertions, 0 deletions
diff --git a/internal/net/server.go b/internal/net/server.go
new file mode 100644
index 0000000..2a10a9a
--- /dev/null
+++ b/internal/net/server.go
@@ -0,0 +1,166 @@
+package net
+
+import (
+ "bufio"
+ "fmt"
+ "log"
+ "net"
+ "strings"
+)
+
+type SessionState int
+
+const (
+ StateAccountName SessionState = iota
+ StatePassword
+ StateNewAccountPass
+ StateNewAccountConfirm
+ StateMenu
+ StateNewCharName
+ StateNewCharConfirm
+ StateRenameAccount
+ StateRenameChar
+ StateRenameCharName
+ StateDeleteChar
+ StatePurgeAccount
+ StateGame
+)
+
+type Session struct {
+ Conn net.Conn
+ Reader *bufio.Reader
+ State SessionState
+ Account *AccountEntry
+ Player interface{} // *player.Player once character is selected
+ PendingChar string // char being renamed/deleted
+ PendingPass string // first password during signup
+}
+
+type AccountEntry struct {
+ Name string
+ PasswordHash string
+ Characters []string
+}
+
+type Server struct {
+ listener net.Listener
+ hub *Hub
+}
+
+type Hub struct {
+ sessions map[*Session]bool
+ // roomID -> sessions
+ rooms map[int]map[*Session]bool
+}
+
+func NewHub() *Hub {
+ return &Hub{
+ sessions: make(map[*Session]bool),
+ rooms: make(map[int]map[*Session]bool),
+ }
+}
+
+func (h *Hub) Add(s *Session) {
+ h.sessions[s] = true
+}
+
+func (h *Hub) Remove(s *Session) {
+ delete(h.sessions, s)
+ for _, room := range h.rooms {
+ delete(room, s)
+ }
+}
+
+func (h *Hub) EnterRoom(s *Session, roomID int) {
+ h.LeaveRoom(s)
+ if h.rooms[roomID] == nil {
+ h.rooms[roomID] = make(map[*Session]bool)
+ }
+ h.rooms[roomID][s] = true
+}
+
+func (h *Hub) LeaveRoom(s *Session) {
+ for _, room := range h.rooms {
+ delete(room, s)
+ }
+}
+
+func (h *Hub) PlayersInRoom(roomID int) []*Session {
+ var out []*Session
+ if room, ok := h.rooms[roomID]; ok {
+ for s := range room {
+ out = append(out, s)
+ }
+ }
+ return out
+}
+
+func NewServer(addr string) (*Server, error) {
+ l, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+ return &Server{listener: l, hub: NewHub()}, nil
+}
+
+func (s *Server) Hub() *Hub {
+ return s.hub
+}
+
+func (s *Server) ListenAndServe(handler func(*Session, string)) error {
+ for {
+ conn, err := s.listener.Accept()
+ if err != nil {
+ return err
+ }
+ session := &Session{
+ Conn: conn,
+ Reader: bufio.NewReader(conn),
+ State: StateAccountName,
+ }
+ s.hub.Add(session)
+ go s.handleSession(session, handler)
+ }
+}
+
+func (s *Server) handleSession(session *Session, handler func(*Session, string)) {
+ defer func() {
+ if r := recover(); r != nil {
+ log.Printf("session panic: %v", r)
+ }
+ s.hub.Remove(session)
+ session.Conn.Close()
+ }()
+
+ _, _ = session.Conn.Write([]byte("\033[2J\033[H")) // clear screen
+ session.Write("Welcome to Third Collapse!\r\n")
+ session.Write("Account name: ")
+
+ for {
+ line, err := session.Reader.ReadString('\n')
+ if err != nil {
+ log.Printf("session read error: %v", err)
+ return
+ }
+ line = strings.TrimSpace(line)
+ handler(session, line)
+ }
+}
+
+func (sess *Session) Write(msg string) {
+ _, _ = sess.Conn.Write([]byte(msg))
+}
+
+func (sess *Session) WriteLine(msg string) {
+ _, _ = sess.Conn.Write([]byte(msg + "\r\n"))
+}
+
+func (sess *Session) WriteLines(lines ...string) {
+ for _, l := range lines {
+ sess.WriteLine(l)
+ }
+}
+
+func (sess *Session) Writef(format string, args ...interface{}) {
+ sess.Write(fmt.Sprintf(format, args...))
+}