335 lines
8.9 KiB
Go
335 lines
8.9 KiB
Go
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)
|
|
}
|
|
} |