// Package common provides shared utilities and patterns used across multiple game systems. // // The MasterList type provides a generic, thread-safe collection management pattern // that is used extensively throughout the EQ2Go server implementation for managing // game entities like items, spells, spawns, achievements, etc. package common import ( "fmt" "sync" ) // Identifiable represents any type that can be identified by a key type Identifiable[K comparable] interface { GetID() K } // MasterList provides a generic, thread-safe collection for managing game entities. // It implements the common pattern used across all EQ2Go master lists with consistent // CRUD operations, bulk operations, and thread safety. // // K is the key type (typically int32, uint32, or string) // V is the value type (must implement Identifiable[K]) type MasterList[K comparable, V Identifiable[K]] struct { items map[K]V mutex sync.RWMutex } // NewMasterList creates a new master list instance func NewMasterList[K comparable, V Identifiable[K]]() *MasterList[K, V] { return &MasterList[K, V]{ items: make(map[K]V), } } // Add adds an item to the master list. Returns true if added, false if it already exists. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Add(item V) bool { ml.mutex.Lock() defer ml.mutex.Unlock() id := item.GetID() if _, exists := ml.items[id]; exists { return false } ml.items[id] = item return true } // AddOrUpdate adds an item to the master list or updates it if it already exists. // Always returns true. Thread-safe for concurrent access. func (ml *MasterList[K, V]) AddOrUpdate(item V) bool { ml.mutex.Lock() defer ml.mutex.Unlock() id := item.GetID() ml.items[id] = item return true } // Get retrieves an item by its ID. Returns the zero value of V if not found. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Get(id K) V { ml.mutex.RLock() defer ml.mutex.RUnlock() return ml.items[id] } // GetSafe retrieves an item by its ID with existence check. // Returns the item and true if found, zero value and false if not found. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) GetSafe(id K) (V, bool) { ml.mutex.RLock() defer ml.mutex.RUnlock() item, exists := ml.items[id] return item, exists } // Exists checks if an item with the given ID exists in the list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Exists(id K) bool { ml.mutex.RLock() defer ml.mutex.RUnlock() _, exists := ml.items[id] return exists } // Remove removes an item by its ID. Returns true if removed, false if not found. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Remove(id K) bool { ml.mutex.Lock() defer ml.mutex.Unlock() if _, exists := ml.items[id]; !exists { return false } delete(ml.items, id) return true } // Update updates an existing item. Returns error if the item doesn't exist. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Update(item V) error { ml.mutex.Lock() defer ml.mutex.Unlock() id := item.GetID() if _, exists := ml.items[id]; !exists { return fmt.Errorf("item with ID %v not found", id) } ml.items[id] = item return nil } // Size returns the number of items in the list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Size() int { ml.mutex.RLock() defer ml.mutex.RUnlock() return len(ml.items) } // IsEmpty returns true if the list contains no items. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) IsEmpty() bool { ml.mutex.RLock() defer ml.mutex.RUnlock() return len(ml.items) == 0 } // Clear removes all items from the list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Clear() { ml.mutex.Lock() defer ml.mutex.Unlock() // Create new map to ensure memory is freed ml.items = make(map[K]V) } // GetAll returns a copy of all items in the list. // The returned map is safe to modify without affecting the master list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) GetAll() map[K]V { ml.mutex.RLock() defer ml.mutex.RUnlock() result := make(map[K]V, len(ml.items)) for k, v := range ml.items { result[k] = v } return result } // GetAllSlice returns a slice containing all items in the list. // The returned slice is safe to modify without affecting the master list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) GetAllSlice() []V { ml.mutex.RLock() defer ml.mutex.RUnlock() result := make([]V, 0, len(ml.items)) for _, v := range ml.items { result = append(result, v) } return result } // GetAllIDs returns a slice containing all IDs in the list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) GetAllIDs() []K { ml.mutex.RLock() defer ml.mutex.RUnlock() result := make([]K, 0, len(ml.items)) for k := range ml.items { result = append(result, k) } return result } // ForEach executes a function for each item in the list. // The function receives a copy of each item, so modifications won't affect the list. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) ForEach(fn func(K, V)) { ml.mutex.RLock() defer ml.mutex.RUnlock() for k, v := range ml.items { fn(k, v) } } // Filter returns a new slice containing items that match the predicate function. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Filter(predicate func(V) bool) []V { ml.mutex.RLock() defer ml.mutex.RUnlock() var result []V for _, v := range ml.items { if predicate(v) { result = append(result, v) } } return result } // Find returns the first item that matches the predicate function. // Returns zero value and false if no match is found. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Find(predicate func(V) bool) (V, bool) { ml.mutex.RLock() defer ml.mutex.RUnlock() for _, v := range ml.items { if predicate(v) { return v, true } } var zero V return zero, false } // Count returns the number of items that match the predicate function. // Thread-safe for concurrent access. func (ml *MasterList[K, V]) Count(predicate func(V) bool) int { ml.mutex.RLock() defer ml.mutex.RUnlock() count := 0 for _, v := range ml.items { if predicate(v) { count++ } } return count } // WithReadLock executes a function while holding a read lock on the list. // Use this for complex operations that need consistent read access to multiple items. func (ml *MasterList[K, V]) WithReadLock(fn func(map[K]V)) { ml.mutex.RLock() defer ml.mutex.RUnlock() fn(ml.items) } // WithWriteLock executes a function while holding a write lock on the list. // Use this for complex operations that need to modify multiple items atomically. func (ml *MasterList[K, V]) WithWriteLock(fn func(map[K]V)) { ml.mutex.Lock() defer ml.mutex.Unlock() fn(ml.items) }