221 lines
5.5 KiB
Go
221 lines
5.5 KiB
Go
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
|
|
} |