package region import ( "fmt" "log" "math" "sync/atomic" "time" ) // NewRegionManager creates a new region manager for a zone func NewRegionManager(zoneName string) *RegionManager { return &RegionManager{ zoneName: zoneName, regionMaps: NewRegionMapRange(zoneName), activeNodes: make(map[string]*RegionNode), playerRegions: make(map[int32]map[string]bool), } } // AddVersionRange adds a version-specific region map func (rm *RegionManager) AddVersionRange(minVersion, maxVersion int32, regionMap RegionMap) error { return rm.regionMaps.AddVersionRange(minVersion, maxVersion, regionMap) } // GetRegionMap returns the appropriate region map for a client version func (rm *RegionManager) GetRegionMap(version int32) RegionMap { return rm.regionMaps.FindRegionByVersion(version) } // ReturnRegionType returns the region type at the given location for a client version func (rm *RegionManager) ReturnRegionType(location [3]float32, gridID int32, version int32) WaterRegionType { startTime := time.Now() defer func() { atomic.AddInt64(&rm.regionChecks, 1) // Update average check time (simplified moving average) // This would be implemented in a more sophisticated stats system }() regionMap := rm.GetRegionMap(version) if regionMap == nil { return RegionTypeNormal } return regionMap.ReturnRegionType(location, gridID) } // InWater checks if the location is in water for a client version func (rm *RegionManager) InWater(location [3]float32, gridID int32, version int32) bool { regionMap := rm.GetRegionMap(version) if regionMap == nil { return false } return regionMap.InWater(location, gridID) } // InLava checks if the location is in lava for a client version func (rm *RegionManager) InLava(location [3]float32, gridID int32, version int32) bool { regionMap := rm.GetRegionMap(version) if regionMap == nil { return false } return regionMap.InLava(location, gridID) } // InLiquid checks if the location is in any liquid for a client version func (rm *RegionManager) InLiquid(location [3]float32, version int32) bool { regionMap := rm.GetRegionMap(version) if regionMap == nil { return false } return regionMap.InLiquid(location) } // InPvP checks if the location is in a PvP area for a client version func (rm *RegionManager) InPvP(location [3]float32, version int32) bool { regionMap := rm.GetRegionMap(version) if regionMap == nil { return false } return regionMap.InPvP(location) } // InZoneLine checks if the location is in a zone line for a client version func (rm *RegionManager) InZoneLine(location [3]float32, version int32) bool { regionMap := rm.GetRegionMap(version) if regionMap == nil { return false } return regionMap.InZoneLine(location) } // AddRegionNode adds a new region node func (rm *RegionManager) AddRegionNode(name, envName string, gridID, triggerWidgetID uint32, distance float32, position [3]float32) error { rm.mutex.Lock() defer rm.mutex.Unlock() if _, exists := rm.activeNodes[name]; exists { return fmt.Errorf("region node '%s' already exists", name) } node := &RegionNode{ Name: name, EnvironmentName: envName, GridID: gridID, TriggerWidgetID: triggerWidgetID, Distance: distance, Position: position, Active: true, PlayerList: make(map[int32]bool), } rm.activeNodes[name] = node atomic.AddInt32(&rm.activeRegions, 1) log.Printf("[Region] Added region node '%s' at position (%.2f, %.2f, %.2f) in zone '%s'", name, position[0], position[1], position[2], rm.zoneName) return nil } // RemoveRegionNode removes a region node by name func (rm *RegionManager) RemoveRegionNode(name string) error { rm.mutex.Lock() defer rm.mutex.Unlock() node, exists := rm.activeNodes[name] if !exists { return fmt.Errorf("region node '%s' not found", name) } // Remove players from this region node.mutex.Lock() for playerID := range node.PlayerList { rm.removePlayerFromRegion(playerID, name) } node.mutex.Unlock() delete(rm.activeNodes, name) atomic.AddInt32(&rm.activeRegions, -1) log.Printf("[Region] Removed region node '%s' from zone '%s'", name, rm.zoneName) return nil } // UpdatePlayerRegions updates the regions for a player based on their position func (rm *RegionManager) UpdatePlayerRegions(playerID int32, position [3]float32, version int32) { rm.mutex.RLock() currentRegions := make(map[string]bool) if playerRegions, exists := rm.playerRegions[playerID]; exists { for regionName := range playerRegions { currentRegions[regionName] = true } } rm.mutex.RUnlock() newRegions := make(map[string]bool) // Check all active region nodes rm.mutex.RLock() for regionName, node := range rm.activeNodes { if !node.Active { continue } // Calculate distance to region dx := position[0] - node.Position[0] dy := position[1] - node.Position[1] dz := position[2] - node.Position[2] distance := float32(math.Sqrt(float64(dx*dx + dy*dy + dz*dz))) // Check if player is within region if distance <= node.Distance { newRegions[regionName] = true // Add player to region if not already there if !currentRegions[regionName] { rm.addPlayerToRegion(playerID, regionName, position) } } } rm.mutex.RUnlock() // Remove player from regions they've left for regionName := range currentRegions { if !newRegions[regionName] { rm.removePlayerFromRegion(playerID, regionName) } } // Update player's region list rm.mutex.Lock() if len(newRegions) > 0 { rm.playerRegions[playerID] = newRegions } else { delete(rm.playerRegions, playerID) } rm.mutex.Unlock() } // RemovePlayer removes a player from all regions func (rm *RegionManager) RemovePlayer(playerID int32) { rm.mutex.Lock() defer rm.mutex.Unlock() if playerRegions, exists := rm.playerRegions[playerID]; exists { for regionName := range playerRegions { if node, nodeExists := rm.activeNodes[regionName]; nodeExists { node.mutex.Lock() delete(node.PlayerList, playerID) node.mutex.Unlock() } } delete(rm.playerRegions, playerID) } } // GetActiveRegions returns a list of active region names func (rm *RegionManager) GetActiveRegions() []string { rm.mutex.RLock() defer rm.mutex.RUnlock() regions := make([]string, 0, len(rm.activeNodes)) for name, node := range rm.activeNodes { if node.Active { regions = append(regions, name) } } return regions } // GetPlayersInRegion returns a list of player IDs in the specified region func (rm *RegionManager) GetPlayersInRegion(regionName string) []int32 { rm.mutex.RLock() node, exists := rm.activeNodes[regionName] rm.mutex.RUnlock() if !exists { return nil } node.mutex.RLock() defer node.mutex.RUnlock() players := make([]int32, 0, len(node.PlayerList)) for playerID := range node.PlayerList { players = append(players, playerID) } return players } // GetPlayerRegions returns the regions a player is currently in func (rm *RegionManager) GetPlayerRegions(playerID int32) []string { rm.mutex.RLock() defer rm.mutex.RUnlock() if playerRegions, exists := rm.playerRegions[playerID]; exists { regions := make([]string, 0, len(playerRegions)) for regionName := range playerRegions { regions = append(regions, regionName) } return regions } return nil } // GetStats returns current region statistics func (rm *RegionManager) GetStats() *RegionStats { rm.mutex.RLock() defer rm.mutex.RUnlock() return &RegionStats{ TotalRegionChecks: atomic.LoadInt64(&rm.regionChecks), TotalRegionTransitions: atomic.LoadInt64(&rm.regionTransitions), ActiveRegions: atomic.LoadInt32(&rm.activeRegions), ActivePlayers: int32(len(rm.playerRegions)), RegionMapsLoaded: int32(rm.regionMaps.GetLoadedMapCount()), AverageCheckTime: 0.0, // Would be calculated from timing data } } // ResetStats resets all region statistics func (rm *RegionManager) ResetStats() { atomic.StoreInt64(&rm.regionChecks, 0) atomic.StoreInt64(&rm.regionTransitions, 0) } // GetZoneName returns the zone name for this region manager func (rm *RegionManager) GetZoneName() string { return rm.zoneName } // Private methods func (rm *RegionManager) addPlayerToRegion(playerID int32, regionName string, position [3]float32) { rm.mutex.RLock() node, exists := rm.activeNodes[regionName] rm.mutex.RUnlock() if exists { node.mutex.Lock() node.PlayerList[playerID] = true node.mutex.Unlock() atomic.AddInt64(&rm.regionTransitions, 1) log.Printf("[Region] Player %d entered region '%s' at position (%.2f, %.2f, %.2f)", playerID, regionName, position[0], position[1], position[2]) } } func (rm *RegionManager) removePlayerFromRegion(playerID int32, regionName string) { rm.mutex.RLock() node, exists := rm.activeNodes[regionName] rm.mutex.RUnlock() if exists { node.mutex.Lock() delete(node.PlayerList, playerID) node.mutex.Unlock() atomic.AddInt64(&rm.regionTransitions, 1) log.Printf("[Region] Player %d left region '%s'", playerID, regionName) } }