304 lines
7.2 KiB
Go
304 lines
7.2 KiB
Go
package object
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// ObjectManager manages all objects in the game world
|
|
type ObjectManager struct {
|
|
objects map[int32]*Object // Objects by database ID
|
|
objectsByZone map[string][]*Object // Objects grouped by zone
|
|
mutex sync.RWMutex // Thread safety
|
|
}
|
|
|
|
// NewObjectManager creates a new object manager
|
|
func NewObjectManager() *ObjectManager {
|
|
return &ObjectManager{
|
|
objects: make(map[int32]*Object),
|
|
objectsByZone: make(map[string][]*Object),
|
|
}
|
|
}
|
|
|
|
// AddObject adds an object to the manager
|
|
func (om *ObjectManager) AddObject(object *Object) error {
|
|
if object == nil {
|
|
return fmt.Errorf("cannot add nil object")
|
|
}
|
|
|
|
if err := object.Validate(); err != nil {
|
|
return fmt.Errorf("invalid object: %w", err)
|
|
}
|
|
|
|
om.mutex.Lock()
|
|
defer om.mutex.Unlock()
|
|
|
|
databaseID := object.GetDatabaseID()
|
|
if databaseID == 0 {
|
|
return fmt.Errorf("object must have a valid database ID")
|
|
}
|
|
|
|
// Check if object already exists
|
|
if _, exists := om.objects[databaseID]; exists {
|
|
return fmt.Errorf("object with database ID %d already exists", databaseID)
|
|
}
|
|
|
|
// Add to main index
|
|
om.objects[databaseID] = object
|
|
|
|
// Add to zone index
|
|
zoneName := object.GetZone()
|
|
if zoneName != "" {
|
|
om.objectsByZone[zoneName] = append(om.objectsByZone[zoneName], object)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveObject removes an object from the manager
|
|
func (om *ObjectManager) RemoveObject(databaseID int32) error {
|
|
om.mutex.Lock()
|
|
defer om.mutex.Unlock()
|
|
|
|
object, exists := om.objects[databaseID]
|
|
if !exists {
|
|
return fmt.Errorf("object with database ID %d not found", databaseID)
|
|
}
|
|
|
|
// Remove from main index
|
|
delete(om.objects, databaseID)
|
|
|
|
// Remove from zone index
|
|
zoneName := object.GetZone()
|
|
if zoneName != "" {
|
|
objects := om.objectsByZone[zoneName]
|
|
for i, obj := range objects {
|
|
if obj.GetDatabaseID() == databaseID {
|
|
// Remove from slice
|
|
om.objectsByZone[zoneName] = append(objects[:i], objects[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
|
|
// Clean up empty zone entries
|
|
if len(om.objectsByZone[zoneName]) == 0 {
|
|
delete(om.objectsByZone, zoneName)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetObject retrieves an object by database ID
|
|
func (om *ObjectManager) GetObject(databaseID int32) *Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
return om.objects[databaseID]
|
|
}
|
|
|
|
// GetObjectsByZone retrieves all objects in a specific zone
|
|
func (om *ObjectManager) GetObjectsByZone(zoneName string) []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
objects := om.objectsByZone[zoneName]
|
|
if objects == nil {
|
|
return []*Object{}
|
|
}
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make([]*Object, len(objects))
|
|
copy(result, objects)
|
|
return result
|
|
}
|
|
|
|
// GetObjectsByType retrieves objects by their type
|
|
func (om *ObjectManager) GetObjectsByType(objectType string) []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
var result []*Object
|
|
for _, object := range om.objects {
|
|
if object.GetObjectType() == objectType {
|
|
result = append(result, object)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetInteractiveObjects returns all objects that can be interacted with
|
|
func (om *ObjectManager) GetInteractiveObjects() []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
var result []*Object
|
|
for _, object := range om.objects {
|
|
if object.CanInteract() {
|
|
result = append(result, object)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetMerchantObjects returns all merchant objects
|
|
func (om *ObjectManager) GetMerchantObjects() []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
var result []*Object
|
|
for _, object := range om.objects {
|
|
if object.IsMerchant() {
|
|
result = append(result, object)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetTransportObjects returns all transport objects
|
|
func (om *ObjectManager) GetTransportObjects() []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
var result []*Object
|
|
for _, object := range om.objects {
|
|
if object.IsTransporter() {
|
|
result = append(result, object)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GetCollectorObjects returns all collector objects
|
|
func (om *ObjectManager) GetCollectorObjects() []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
var result []*Object
|
|
for _, object := range om.objects {
|
|
if object.IsCollector() {
|
|
result = append(result, object)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// UpdateObjectZone updates an object's zone (called when object moves zones)
|
|
func (om *ObjectManager) UpdateObjectZone(databaseID int32, newZoneName string) error {
|
|
om.mutex.Lock()
|
|
defer om.mutex.Unlock()
|
|
|
|
object, exists := om.objects[databaseID]
|
|
if !exists {
|
|
return fmt.Errorf("object with database ID %d not found", databaseID)
|
|
}
|
|
|
|
oldZoneName := object.GetZone()
|
|
|
|
// Remove from old zone index
|
|
if oldZoneName != "" {
|
|
objects := om.objectsByZone[oldZoneName]
|
|
for i, obj := range objects {
|
|
if obj.GetDatabaseID() == databaseID {
|
|
om.objectsByZone[oldZoneName] = append(objects[:i], objects[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
|
|
// Clean up empty zone entries
|
|
if len(om.objectsByZone[oldZoneName]) == 0 {
|
|
delete(om.objectsByZone, oldZoneName)
|
|
}
|
|
}
|
|
|
|
// Update object zone
|
|
object.SetZone(newZoneName)
|
|
|
|
// Add to new zone index
|
|
if newZoneName != "" {
|
|
om.objectsByZone[newZoneName] = append(om.objectsByZone[newZoneName], object)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetObjectCount returns the total number of objects
|
|
func (om *ObjectManager) GetObjectCount() int {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
return len(om.objects)
|
|
}
|
|
|
|
// GetZoneCount returns the number of zones with objects
|
|
func (om *ObjectManager) GetZoneCount() int {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
return len(om.objectsByZone)
|
|
}
|
|
|
|
// GetAllObjects returns all objects (use with caution for large datasets)
|
|
func (om *ObjectManager) GetAllObjects() []*Object {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
result := make([]*Object, 0, len(om.objects))
|
|
for _, object := range om.objects {
|
|
result = append(result, object)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Clear removes all objects from the manager
|
|
func (om *ObjectManager) Clear() {
|
|
om.mutex.Lock()
|
|
defer om.mutex.Unlock()
|
|
|
|
om.objects = make(map[int32]*Object)
|
|
om.objectsByZone = make(map[string][]*Object)
|
|
}
|
|
|
|
// GetObjectStatistics returns statistics about objects in the manager
|
|
func (om *ObjectManager) GetObjectStatistics() ObjectStatistics {
|
|
om.mutex.RLock()
|
|
defer om.mutex.RUnlock()
|
|
|
|
stats := ObjectStatistics{
|
|
TotalObjects: len(om.objects),
|
|
ZoneCount: len(om.objectsByZone),
|
|
TypeCounts: make(map[string]int),
|
|
}
|
|
|
|
// Count by type
|
|
for _, object := range om.objects {
|
|
objectType := object.GetObjectType()
|
|
stats.TypeCounts[objectType]++
|
|
}
|
|
|
|
// Count interactive objects
|
|
for _, object := range om.objects {
|
|
if object.CanInteract() {
|
|
stats.InteractiveObjects++
|
|
}
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
// ObjectStatistics contains statistics about objects
|
|
type ObjectStatistics struct {
|
|
TotalObjects int `json:"total_objects"`
|
|
InteractiveObjects int `json:"interactive_objects"`
|
|
ZoneCount int `json:"zone_count"`
|
|
TypeCounts map[string]int `json:"type_counts"`
|
|
}
|
|
|
|
// Global object manager instance
|
|
var globalObjectManager *ObjectManager
|
|
var globalObjectManagerOnce sync.Once
|
|
|
|
// GetGlobalObjectManager returns the global object manager instance
|
|
func GetGlobalObjectManager() *ObjectManager {
|
|
globalObjectManagerOnce.Do(func() {
|
|
globalObjectManager = NewObjectManager()
|
|
})
|
|
return globalObjectManager
|
|
} |