357 lines
8.4 KiB
Go
357 lines
8.4 KiB
Go
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,
|
|
}
|
|
}
|