eq2go/cmd/login_server/login_server.go

209 lines
5.1 KiB
Go

package main
import (
"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 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
}