package main import ( "crypto/rand" "encoding/hex" "encoding/json" "flag" "fmt" "log" "net" "server/internal/db" "time" "golang.org/x/crypto/bcrypt" ) type LoginServer struct { listener net.Listener database *db.Database worldURL string serverID string } type LoginRequest struct { Type string `json:"type"` Username string `json:"username"` Password string `json:"password"` } type LoginResponse struct { Type string `json:"type"` Success bool `json:"success"` Message string `json:"message,omitempty"` Token string `json:"token,omitempty"` WorldURL string `json:"worldUrl,omitempty"` PlayerID int64 `json:"playerId,omitempty"` } type RegisterRequest struct { Type string `json:"type"` Username string `json:"username"` Password string `json:"password"` } type RegisterResponse struct { Type string `json:"type"` Success bool `json:"success"` Message string `json:"message,omitempty"` } func main() { var ( port = flag.String("port", "8081", "Login server port") dbDSN = flag.String("db", "user:password@tcp(localhost:3306)/game", "Database DSN") worldURL = flag.String("world", "localhost:8082", "World server URL") ) flag.Parse() database, err := db.New(*dbDSN) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer database.Close() server := &LoginServer{ database: database, worldURL: *worldURL, serverID: generateServerID(), } listener, err := net.Listen("tcp", ":"+*port) if err != nil { log.Fatalf("Failed to start login server: %v", err) } server.listener = listener log.Printf("Login server started on port %s (ID: %s)", *port, server.serverID) go server.cleanupSessions() for { conn, err := listener.Accept() if err != nil { log.Printf("Accept error: %v", err) continue } go server.handleConnection(conn) } } func (s *LoginServer) handleConnection(conn net.Conn) { defer conn.Close() decoder := json.NewDecoder(conn) encoder := json.NewEncoder(conn) for { var msg json.RawMessage if err := decoder.Decode(&msg); err != nil { return } var baseMsg struct { Type string `json:"type"` } if err := json.Unmarshal(msg, &baseMsg); err != nil { continue } switch baseMsg.Type { case "login": var req LoginRequest if err := json.Unmarshal(msg, &req); err != nil { continue } s.handleLogin(req, encoder) case "register": var req RegisterRequest if err := json.Unmarshal(msg, &req); err != nil { continue } s.handleRegister(req, encoder) } } } func (s *LoginServer) handleLogin(req LoginRequest, encoder *json.Encoder) { player, err := s.database.GetPlayerByUsername(req.Username) if err != nil { encoder.Encode(LoginResponse{ Type: "loginResponse", Success: false, Message: "Invalid username or password", }) return } if err := bcrypt.CompareHashAndPassword([]byte(player.PasswordHash), []byte(req.Password)); err != nil { encoder.Encode(LoginResponse{ Type: "loginResponse", Success: false, Message: "Invalid username or password", }) return } token := generateToken() if err := s.database.CreateSession(player.ID, token, "world", s.serverID, 24*time.Hour); err != nil { encoder.Encode(LoginResponse{ Type: "loginResponse", Success: false, Message: "Failed to create session", }) return } s.database.UpdateLastLogin(player.ID) encoder.Encode(LoginResponse{ Type: "loginResponse", Success: true, Token: token, WorldURL: s.worldURL, PlayerID: player.ID, }) } func (s *LoginServer) handleRegister(req RegisterRequest, encoder *json.Encoder) { if len(req.Username) < 3 || len(req.Username) > 20 { encoder.Encode(RegisterResponse{ Type: "registerResponse", Success: false, Message: "Username must be between 3 and 20 characters", }) return } if len(req.Password) < 6 { encoder.Encode(RegisterResponse{ Type: "registerResponse", Success: false, Message: "Password must be at least 6 characters", }) return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { encoder.Encode(RegisterResponse{ Type: "registerResponse", Success: false, Message: "Failed to process password", }) return } _, err = s.database.CreatePlayer(req.Username, string(hashedPassword)) if err != nil { encoder.Encode(RegisterResponse{ Type: "registerResponse", Success: false, Message: "Username already exists", }) return } encoder.Encode(RegisterResponse{ Type: "registerResponse", Success: true, Message: "Registration successful", }) } func (s *LoginServer) cleanupSessions() { ticker := time.NewTicker(1 * time.Hour) defer ticker.Stop() for range ticker.C { if err := s.database.CleanExpiredSessions(); err != nil { log.Printf("Failed to clean expired sessions: %v", err) } } } func generateToken() string { b := make([]byte, 32) rand.Read(b) return hex.EncodeToString(b) } func generateServerID() string { return fmt.Sprintf("login-%d", time.Now().Unix()) }