package main import ( "eq2emu/internal/packets" "eq2emu/internal/udp" "fmt" "log" "sync" "time" ) // LoginServer manages the main login server functionality type LoginServer struct { config *Config server *udp.Server database *Database worldList *WorldList webServer *WebServer clients map[string]*LoginClient clientMutex sync.RWMutex // Statistics stats struct { ConnectionCount int32 LoginAttempts int32 SuccessfulLogins int32 StartTime time.Time } } // NewLoginServer creates a new login server instance func NewLoginServer(config *Config) (*LoginServer, error) { ls := &LoginServer{ config: config, clients: make(map[string]*LoginClient), } ls.stats.StartTime = time.Now() // Initialize packet definitions log.Printf("Loaded %d packet definitions", packets.GetPacketCount()) // Initialize database db, err := NewDatabase(config.Database) if err != nil { return nil, fmt.Errorf("database initialization failed: %w", err) } ls.database = db // Initialize world list ls.worldList = NewWorldList(db) // Create UDP server with login packet handler udpConfig := udp.DefaultConfig() udpConfig.MaxConnections = config.MaxConnections udpConfig.Timeout = time.Duration(config.TimeoutSeconds) * time.Second udpConfig.EnableCompression = config.EnableCompression udpConfig.EnableEncryption = config.EnableEncryption server, err := udp.NewServer(fmt.Sprintf(":%d", config.Port), ls.handlePacket, udpConfig) if err != nil { return nil, fmt.Errorf("UDP server creation failed: %w", err) } ls.server = server // Initialize web server if configured if config.WebServer.Enabled { webServer, err := NewWebServer(config.WebServer, ls) if err != nil { log.Printf("Web server initialization failed: %v", err) } else { ls.webServer = webServer } } return ls, nil } // Start begins accepting connections and processing packets func (ls *LoginServer) Start() error { log.Println("Starting login server components...") // Start world list monitoring go ls.worldList.Start() // Start web server if configured if ls.webServer != nil { go ls.webServer.Start() } // Start UDP server return ls.server.Start() } // Stop gracefully shuts down the server func (ls *LoginServer) Stop() { log.Println("Stopping login server...") if ls.webServer != nil { ls.webServer.Stop() } ls.worldList.Stop() ls.server.Stop() ls.database.Close() } // handlePacket processes incoming packets from clients func (ls *LoginServer) handlePacket(conn *udp.Connection, packet *udp.ApplicationPacket) { clientKey := conn.GetSessionID() // Get or create client ls.clientMutex.Lock() client, exists := ls.clients[fmt.Sprintf("%d", clientKey)] if !exists { client = NewLoginClient(conn, ls) ls.clients[fmt.Sprintf("%d", clientKey)] = client } ls.clientMutex.Unlock() // Process packet client.ProcessPacket(packet) } // RemoveClient removes a client from the active clients list func (ls *LoginServer) RemoveClient(sessionID string) { ls.clientMutex.Lock() delete(ls.clients, sessionID) ls.clientMutex.Unlock() } // UpdateStats updates server statistics func (ls *LoginServer) UpdateStats() { ls.clientMutex.RLock() ls.stats.ConnectionCount = int32(len(ls.clients)) ls.clientMutex.RUnlock() // Update world server statistics ls.worldList.UpdateStats() // Clean up old database entries ls.database.CleanupOldEntries() log.Printf("Stats: %d connections, %d login attempts, %d successful logins", ls.stats.ConnectionCount, ls.stats.LoginAttempts, ls.stats.SuccessfulLogins) } // CleanupStaleConnections removes inactive connections func (ls *LoginServer) CleanupStaleConnections() { var staleClients []string ls.clientMutex.RLock() for sessionID, client := range ls.clients { if client.IsStale() { staleClients = append(staleClients, sessionID) } } ls.clientMutex.RUnlock() ls.clientMutex.Lock() for _, sessionID := range staleClients { if client, exists := ls.clients[sessionID]; exists { client.Disconnect() delete(ls.clients, sessionID) } } ls.clientMutex.Unlock() if len(staleClients) > 0 { log.Printf("Cleaned up %d stale connections", len(staleClients)) } } // GetStats returns current server statistics func (ls *LoginServer) GetStats() map[string]any { ls.clientMutex.RLock() connectionCount := len(ls.clients) ls.clientMutex.RUnlock() return map[string]any{ "connection_count": connectionCount, "login_attempts": ls.stats.LoginAttempts, "successful_logins": ls.stats.SuccessfulLogins, "uptime_seconds": int(time.Since(ls.stats.StartTime).Seconds()), "world_server_count": ls.worldList.GetActiveCount(), } } // IncrementLoginAttempts atomically increments login attempt counter func (ls *LoginServer) IncrementLoginAttempts() { ls.stats.LoginAttempts++ } // IncrementSuccessfulLogins atomically increments successful login counter func (ls *LoginServer) IncrementSuccessfulLogins() { ls.stats.SuccessfulLogins++ } // GetDatabase returns the database instance func (ls *LoginServer) GetDatabase() *Database { return ls.database } // GetWorldList returns the world list instance func (ls *LoginServer) GetWorldList() *WorldList { return ls.worldList } // GetConfig returns the server configuration func (ls *LoginServer) GetConfig() *Config { return ls.config }