package main import ( "fmt" "log" "sync" "time" ) // WorldServer represents a game world server type WorldServer struct { ID int32 Name string Description string IPAddress string Port int Status string Population int32 PopulationLevel uint8 Locked bool Hidden bool Online bool CreatedDate time.Time LastUpdate time.Time } // WorldServerStats holds runtime statistics type WorldServerStats struct { Population int32 ZonesActive int32 PlayersOnline int32 UptimeSeconds int64 } // WorldList manages all world servers type WorldList struct { servers map[int32]*WorldServer mutex sync.RWMutex database *Database updateTicker *time.Ticker stopChan chan struct{} } // NewWorldList creates a new world list manager func NewWorldList(database *Database) *WorldList { return &WorldList{ servers: make(map[int32]*WorldServer), database: database, stopChan: make(chan struct{}), } } // Start begins world server monitoring func (wl *WorldList) Start() { log.Println("Starting world list monitoring...") // Load world servers from database if err := wl.LoadFromDatabase(); err != nil { log.Printf("Failed to load world servers: %v", err) } // Start periodic updates wl.updateTicker = time.NewTicker(30 * time.Second) go func() { for { select { case <-wl.updateTicker.C: wl.UpdateStats() case <-wl.stopChan: return } } }() } // Stop shuts down world server monitoring func (wl *WorldList) Stop() { log.Println("Stopping world list monitoring...") if wl.updateTicker != nil { wl.updateTicker.Stop() } close(wl.stopChan) } // LoadFromDatabase loads world servers from the database func (wl *WorldList) LoadFromDatabase() error { servers, err := wl.database.GetWorldServers() if err != nil { return fmt.Errorf("failed to load world servers: %w", err) } wl.mutex.Lock() defer wl.mutex.Unlock() // Clear existing servers wl.servers = make(map[int32]*WorldServer) // Add loaded servers for _, server := range servers { wl.servers[server.ID] = server log.Printf("Loaded world server: %s (ID: %d)", server.Name, server.ID) } log.Printf("Loaded %d world servers", len(servers)) return nil } // GetActiveWorlds returns all online world servers func (wl *WorldList) GetActiveWorlds() []*WorldServer { wl.mutex.RLock() defer wl.mutex.RUnlock() var active []*WorldServer for _, server := range wl.servers { if server.Online && !server.Hidden { active = append(active, server) } } return active } // GetWorld returns a specific world server by ID func (wl *WorldList) GetWorld(id int32) *WorldServer { wl.mutex.RLock() defer wl.mutex.RUnlock() return wl.servers[id] } // GetActiveCount returns the number of online world servers func (wl *WorldList) GetActiveCount() int { wl.mutex.RLock() defer wl.mutex.RUnlock() count := 0 for _, server := range wl.servers { if server.Online { count++ } } return count } // UpdateServerStatus updates a world server's status func (wl *WorldList) UpdateServerStatus(id int32, online bool, population int32, locked bool) { wl.mutex.Lock() defer wl.mutex.Unlock() server, exists := wl.servers[id] if !exists { log.Printf("Attempted to update unknown server ID: %d", id) return } server.Online = online server.Population = population server.Locked = locked server.LastUpdate = time.Now() // Update population level server.PopulationLevel = wl.calculatePopulationLevel(population) if online { server.Status = "online" } else { server.Status = "offline" } log.Printf("Updated server %s: online=%t, population=%d, locked=%t", server.Name, online, population, locked) } // calculatePopulationLevel converts population to display level func (wl *WorldList) calculatePopulationLevel(population int32) uint8 { switch { case population >= 1000: return 3 // Full case population >= 500: return 2 // High case population >= 100: return 1 // Medium default: return 0 // Low } } // UpdateStats updates world server statistics func (wl *WorldList) UpdateStats() { wl.mutex.RLock() servers := make([]*WorldServer, 0, len(wl.servers)) for _, server := range wl.servers { servers = append(servers, server) } wl.mutex.RUnlock() // Update statistics for each server for _, server := range servers { if server.Online { stats := &WorldServerStats{ Population: server.Population, ZonesActive: 0, // Would be updated by world server PlayersOnline: server.Population, UptimeSeconds: int64(time.Since(server.LastUpdate).Seconds()), } if err := wl.database.UpdateWorldServerStats(server.ID, stats); err != nil { log.Printf("Failed to update stats for server %d: %v", server.ID, err) } } } } // SendPlayRequest sends a character play request to a world server func (wl *WorldList) SendPlayRequest(world *WorldServer, accountID, charID int32) error { // In a real implementation, this would establish communication with the world server // and send the play request. For now, we'll simulate the response. log.Printf("Sending play request to world server %s for account %d, character %d", world.Name, accountID, charID) // Simulate world server response after a short delay go func() { time.Sleep(100 * time.Millisecond) // For demonstration, we'll always succeed // In reality, the world server would validate the character and respond accessKey := generateAccessKey() // This would normally come from the world server's response wl.HandlePlayResponse(world.ID, accountID, charID, true, world.IPAddress, world.Port, accessKey) }() return nil } // HandlePlayResponse processes a play response from a world server func (wl *WorldList) HandlePlayResponse(worldID, accountID, charID int32, success bool, ipAddress string, port int, accessKey int32) { // Find the client that requested this // This is simplified - in reality you'd track pending requests log.Printf("Play response from world %d: success=%t, access_key=%d", worldID, success, accessKey) // Send response to appropriate client // This would need to be implemented with proper client tracking } // generateAccessKey generates a random access key for world server connections func generateAccessKey() int32 { return int32(time.Now().UnixNano() & 0x7FFFFFFF) } // AddServer adds a new world server (for dynamic registration) func (wl *WorldList) AddServer(server *WorldServer) { wl.mutex.Lock() defer wl.mutex.Unlock() wl.servers[server.ID] = server log.Printf("Added world server: %s (ID: %d)", server.Name, server.ID) } // RemoveServer removes a world server func (wl *WorldList) RemoveServer(id int32) { wl.mutex.Lock() defer wl.mutex.Unlock() if server, exists := wl.servers[id]; exists { delete(wl.servers, id) log.Printf("Removed world server: %s (ID: %d)", server.Name, id) } } // GetServerList returns a formatted server list for client packets func (wl *WorldList) GetServerList() []byte { wl.mutex.RLock() defer wl.mutex.RUnlock() // Build server list packet data data := make([]byte, 0, 1024) // Count active servers activeCount := 0 for _, server := range wl.servers { if !server.Hidden { activeCount++ } } // Add server count data = append(data, byte(activeCount)) // Add server data for _, server := range wl.servers { if server.Hidden { continue } // Server ID (4 bytes) serverIDBytes := make([]byte, 4) serverIDBytes[0] = byte(server.ID) serverIDBytes[1] = byte(server.ID >> 8) serverIDBytes[2] = byte(server.ID >> 16) serverIDBytes[3] = byte(server.ID >> 24) data = append(data, serverIDBytes...) // Server name (null-terminated) data = append(data, []byte(server.Name)...) data = append(data, 0) // Server flags var flags byte if server.Online { flags |= 0x01 } if server.Locked { flags |= 0x02 } data = append(data, flags) // Population level data = append(data, server.PopulationLevel) } return data } // GetStats returns world list statistics func (wl *WorldList) GetStats() map[string]any { wl.mutex.RLock() defer wl.mutex.RUnlock() totalServers := len(wl.servers) onlineServers := 0 totalPopulation := int32(0) for _, server := range wl.servers { if server.Online { onlineServers++ totalPopulation += server.Population } } return map[string]any{ "total_servers": totalServers, "online_servers": onlineServers, "offline_servers": totalServers - onlineServers, "total_population": totalPopulation, } }