263 lines
6.7 KiB
Go
263 lines
6.7 KiB
Go
// 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)
|
|
} |