diff options
Diffstat (limited to 'internal/net')
| -rw-r--r-- | internal/net/server.go | 166 |
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...)) +} |
