package login import ( "fmt" "strings" ) // ServerConfig represents the login server configuration type ServerConfig struct { // Network settings ListenAddr string `json:"listen_addr"` ListenPort int `json:"listen_port"` MaxClients int `json:"max_clients"` // Web interface settings WebAddr string `json:"web_addr"` WebPort int `json:"web_port"` WebCertFile string `json:"web_cert_file"` WebKeyFile string `json:"web_key_file"` WebKeyPassword string `json:"web_key_password"` WebUser string `json:"web_user"` WebPassword string `json:"web_password"` // Database settings DatabaseType string `json:"database_type"` // "sqlite" or "mysql" DatabaseDSN string `json:"database_dsn"` // Connection string // Server settings ServerName string `json:"server_name"` LogLevel string `json:"log_level"` // World servers configuration WorldServers []WorldServerInfo `json:"world_servers"` } // WorldServerInfo represents information about a world server type WorldServerInfo struct { ID int `json:"id"` Name string `json:"name"` Address string `json:"address"` Port int `json:"port"` AdminPort int `json:"admin_port"` Key string `json:"key"` Status string `json:"status"` // "up", "down", "locked" Population int `json:"population"` // Current player count MaxPlayers int `json:"max_players"` // Maximum allowed players Description string `json:"description"` // Server flags Locked bool `json:"locked"` Hidden bool `json:"hidden"` // Connection tracking LastHeartbeat int64 `json:"last_heartbeat"` } // Validate validates the server configuration func (c *ServerConfig) Validate() error { if c.ListenAddr == "" { return fmt.Errorf("listen_addr is required") } if c.ListenPort <= 0 || c.ListenPort > 65535 { return fmt.Errorf("listen_port must be between 1 and 65535") } if c.MaxClients <= 0 { c.MaxClients = 1000 // Default value } // Database configuration validation dbType := strings.ToLower(c.DatabaseType) switch dbType { case "sqlite", "": c.DatabaseType = "sqlite" if c.DatabaseDSN == "" { return fmt.Errorf("database_dsn is required") } case "mysql": c.DatabaseType = "mysql" if c.DatabaseDSN == "" { return fmt.Errorf("database_dsn is required") } default: return fmt.Errorf("invalid database_type: %s (must be sqlite or mysql)", c.DatabaseType) } if c.ServerName == "" { c.ServerName = "EQ2Go Login Server" } // Validate log level logLevel := strings.ToLower(c.LogLevel) switch logLevel { case "debug", "info", "warn", "error": c.LogLevel = logLevel case "": c.LogLevel = "info" // Default default: return fmt.Errorf("invalid log_level: %s (must be debug, info, warn, or error)", c.LogLevel) } // Validate web configuration if c.WebPort > 0 { if c.WebPort <= 0 || c.WebPort > 65535 { return fmt.Errorf("web_port must be between 1 and 65535") } if c.WebAddr == "" { c.WebAddr = "0.0.0.0" } // If TLS files are specified, both cert and key must be provided if c.WebCertFile != "" && c.WebKeyFile == "" { return fmt.Errorf("web_key_file is required when web_cert_file is specified") } if c.WebKeyFile != "" && c.WebCertFile == "" { return fmt.Errorf("web_cert_file is required when web_key_file is specified") } } // Validate world servers for i, ws := range c.WorldServers { if err := ws.Validate(); err != nil { return fmt.Errorf("world_server[%d]: %w", i, err) } } return nil } // Validate validates a world server configuration func (w *WorldServerInfo) Validate() error { if w.ID <= 0 { return fmt.Errorf("id must be positive") } if w.Name == "" { return fmt.Errorf("name is required") } if w.Address == "" { return fmt.Errorf("address is required") } if w.Port <= 0 || w.Port > 65535 { return fmt.Errorf("port must be between 1 and 65535") } if w.AdminPort <= 0 || w.AdminPort > 65535 { return fmt.Errorf("admin_port must be between 1 and 65535") } if w.AdminPort == w.Port { return fmt.Errorf("admin_port cannot be the same as port") } if w.MaxPlayers <= 0 { w.MaxPlayers = 1000 // Default value } // Normalize status status := strings.ToLower(w.Status) switch status { case "up", "down", "locked": w.Status = status case "": w.Status = "down" // Default default: return fmt.Errorf("invalid status: %s (must be up, down, or locked)", w.Status) } return nil } // IsOnline returns whether the world server is currently online func (w *WorldServerInfo) IsOnline() bool { return w.Status == "up" } // IsLocked returns whether the world server is locked func (w *WorldServerInfo) IsLocked() bool { return w.Locked || w.Status == "locked" } // IsHidden returns whether the world server should be hidden from the server list func (w *WorldServerInfo) IsHidden() bool { return w.Hidden } // GetPopulationPercentage returns the current population as a percentage of max capacity func (w *WorldServerInfo) GetPopulationPercentage() float64 { if w.MaxPlayers <= 0 { return 0 } return float64(w.Population) / float64(w.MaxPlayers) * 100 } // GetPopulationLevel returns a string representation of the population level func (w *WorldServerInfo) GetPopulationLevel() string { pct := w.GetPopulationPercentage() switch { case pct >= 95: return "FULL" case pct >= 80: return "HIGH" case pct >= 50: return "MEDIUM" case pct >= 25: return "LOW" default: return "LIGHT" } } // Clone creates a deep copy of the WorldServerInfo func (w *WorldServerInfo) Clone() *WorldServerInfo { clone := *w return &clone }