package world import ( "fmt" "sync" "time" ) // ZoneServer represents a single zone instance type ZoneServer struct { ID int32 Name string InstanceID int32 ZoneFile string Description string MOTD string // Zone properties MinLevel int16 MaxLevel int16 MinVersion int16 XPModifier float32 CityZone bool WeatherAllowed bool // Safe location SafeX float32 SafeY float32 SafeZ float32 SafeHeading float32 // Zone state IsRunning bool IsShuttingDown bool Population int32 CreatedTime time.Time // Clients in zone clients map[int32]*Client clientMutex sync.RWMutex // Zone processing lastProcess time.Time processInterval time.Duration mutex sync.RWMutex } // ZoneList manages all active zones type ZoneList struct { zones map[int32]*ZoneServer zonesByName map[string][]*ZoneServer // Multiple instances per zone name instances map[int32]*ZoneServer // Instance ID to zone mapping nextInstanceID int32 mutex sync.RWMutex } // NewZoneList creates a new zone list func NewZoneList() *ZoneList { return &ZoneList{ zones: make(map[int32]*ZoneServer), zonesByName: make(map[string][]*ZoneServer), instances: make(map[int32]*ZoneServer), nextInstanceID: 1, } } // Add adds a zone to the list func (zl *ZoneList) Add(zone *ZoneServer) error { zl.mutex.Lock() defer zl.mutex.Unlock() if _, exists := zl.zones[zone.ID]; exists { return fmt.Errorf("zone with ID %d already exists", zone.ID) } // Assign instance ID if not set if zone.InstanceID == 0 { zone.InstanceID = zl.nextInstanceID zl.nextInstanceID++ } // Add to maps zl.zones[zone.ID] = zone zl.zonesByName[zone.Name] = append(zl.zonesByName[zone.Name], zone) zl.instances[zone.InstanceID] = zone zone.CreatedTime = time.Now() zone.IsRunning = true return nil } // Remove removes a zone from the list func (zl *ZoneList) Remove(zoneID int32) { zl.mutex.Lock() defer zl.mutex.Unlock() zone, exists := zl.zones[zoneID] if !exists { return } // Remove from zones map delete(zl.zones, zoneID) // Remove from instances map delete(zl.instances, zone.InstanceID) // Remove from name map if zones, ok := zl.zonesByName[zone.Name]; ok { newZones := make([]*ZoneServer, 0, len(zones)-1) for _, z := range zones { if z.ID != zoneID { newZones = append(newZones, z) } } if len(newZones) > 0 { zl.zonesByName[zone.Name] = newZones } else { delete(zl.zonesByName, zone.Name) } } } // GetByID returns a zone by its ID func (zl *ZoneList) GetByID(zoneID int32) *ZoneServer { zl.mutex.RLock() defer zl.mutex.RUnlock() return zl.zones[zoneID] } // GetByName returns all zones with the given name func (zl *ZoneList) GetByName(name string) []*ZoneServer { zl.mutex.RLock() defer zl.mutex.RUnlock() zones := zl.zonesByName[name] result := make([]*ZoneServer, len(zones)) copy(result, zones) return result } // GetByInstanceID returns a zone by its instance ID func (zl *ZoneList) GetByInstanceID(instanceID int32) *ZoneServer { zl.mutex.RLock() defer zl.mutex.RUnlock() return zl.instances[instanceID] } // GetByLowestPopulation returns the zone instance with the lowest population func (zl *ZoneList) GetByLowestPopulation(zoneName string) *ZoneServer { zl.mutex.RLock() defer zl.mutex.RUnlock() zones := zl.zonesByName[zoneName] if len(zones) == 0 { return nil } lowestPop := zones[0] for _, zone := range zones[1:] { if zone.Population < lowestPop.Population && zone.IsRunning && !zone.IsShuttingDown { lowestPop = zone } } return lowestPop } // Count returns the total number of zones func (zl *ZoneList) Count() int32 { zl.mutex.RLock() defer zl.mutex.RUnlock() return int32(len(zl.zones)) } // CountInstances returns the number of instance zones func (zl *ZoneList) CountInstances() int32 { zl.mutex.RLock() defer zl.mutex.RUnlock() count := int32(0) for _, zone := range zl.zones { if zone.InstanceID > 0 { count++ } } return count } // GetTotalPopulation returns the total population across all zones func (zl *ZoneList) GetTotalPopulation() int32 { zl.mutex.RLock() defer zl.mutex.RUnlock() total := int32(0) for _, zone := range zl.zones { total += zone.Population } return total } // ProcessAll processes all zones func (zl *ZoneList) ProcessAll() { zl.mutex.RLock() zones := make([]*ZoneServer, 0, len(zl.zones)) for _, zone := range zl.zones { zones = append(zones, zone) } zl.mutex.RUnlock() for _, zone := range zones { if zone.IsRunning && !zone.IsShuttingDown { zone.Process() } } } // SendTimeUpdate sends time update to all zones func (zl *ZoneList) SendTimeUpdate(worldTime *WorldTime) { zl.mutex.RLock() defer zl.mutex.RUnlock() for _, zone := range zl.zones { if zone.IsRunning { // TODO: Send time update packet to all clients in zone } } } // CheckHealth checks the health of all zones func (zl *ZoneList) CheckHealth() { zl.mutex.RLock() zones := make([]*ZoneServer, 0, len(zl.zones)) for _, zone := range zl.zones { zones = append(zones, zone) } zl.mutex.RUnlock() now := time.Now() for _, zone := range zones { zone.mutex.Lock() // Check if zone has been processing if zone.IsRunning && now.Sub(zone.lastProcess) > 30*time.Second { fmt.Printf("Warning: Zone %s (%d) has not processed in %v\n", zone.Name, zone.ID, now.Sub(zone.lastProcess)) } zone.mutex.Unlock() } } // CleanupDead removes zones that are no longer running func (zl *ZoneList) CleanupDead() { zl.mutex.Lock() defer zl.mutex.Unlock() toRemove := make([]int32, 0) for id, zone := range zl.zones { if !zone.IsRunning && zone.Population == 0 { toRemove = append(toRemove, id) } } for _, id := range toRemove { zl.Remove(id) fmt.Printf("Cleaned up dead zone ID %d\n", id) } } // ShutdownAll shuts down all zones func (zl *ZoneList) ShutdownAll() { zl.mutex.RLock() zones := make([]*ZoneServer, 0, len(zl.zones)) for _, zone := range zl.zones { zones = append(zones, zone) } zl.mutex.RUnlock() for _, zone := range zones { zone.Shutdown() } } // Process handles zone processing func (z *ZoneServer) Process() { z.mutex.Lock() defer z.mutex.Unlock() if !z.IsRunning || z.IsShuttingDown { return } now := time.Now() if now.Sub(z.lastProcess) < z.processInterval { return } z.lastProcess = now // TODO: Implement zone processing // - Process spawns // - Process spell timers // - Process movement // - Process combat // - Process respawns // - Send updates to clients } // Shutdown gracefully shuts down the zone func (z *ZoneServer) Shutdown() { z.mutex.Lock() defer z.mutex.Unlock() if z.IsShuttingDown { return } z.IsShuttingDown = true // Notify all clients z.clientMutex.RLock() for _, client := range z.clients { client.SendSimpleMessage("Zone is shutting down...") } z.clientMutex.RUnlock() // TODO: Save zone state // TODO: Disconnect all clients // TODO: Clean up resources z.IsRunning = false } // AddClient adds a client to the zone func (z *ZoneServer) AddClient(client *Client) { z.clientMutex.Lock() defer z.clientMutex.Unlock() if z.clients == nil { z.clients = make(map[int32]*Client) } z.clients[client.CharacterID] = client z.Population++ } // RemoveClient removes a client from the zone func (z *ZoneServer) RemoveClient(characterID int32) { z.clientMutex.Lock() defer z.clientMutex.Unlock() if _, exists := z.clients[characterID]; exists { delete(z.clients, characterID) z.Population-- } } // GetClient returns a client by character ID func (z *ZoneServer) GetClient(characterID int32) *Client { z.clientMutex.RLock() defer z.clientMutex.RUnlock() return z.clients[characterID] }