package languages import ( "fmt" "sync" ) // Language represents a single language that can be learned by players type Language struct { id int32 // Unique language identifier name string // Language name saveNeeded bool // Whether this language needs to be saved to database mutex sync.RWMutex // Thread safety } // NewLanguage creates a new language with default values func NewLanguage() *Language { return &Language{ id: 0, name: "", saveNeeded: false, } } // NewLanguageFromExisting creates a copy of an existing language func NewLanguageFromExisting(source *Language) *Language { if source == nil { return NewLanguage() } source.mutex.RLock() defer source.mutex.RUnlock() return &Language{ id: source.id, name: source.name, saveNeeded: source.saveNeeded, } } // GetID returns the language's unique identifier func (l *Language) GetID() int32 { l.mutex.RLock() defer l.mutex.RUnlock() return l.id } // SetID updates the language's unique identifier func (l *Language) SetID(id int32) { l.mutex.Lock() defer l.mutex.Unlock() l.id = id } // GetName returns the language name func (l *Language) GetName() string { l.mutex.RLock() defer l.mutex.RUnlock() return l.name } // SetName updates the language name func (l *Language) SetName(name string) { l.mutex.Lock() defer l.mutex.Unlock() // Truncate if too long if len(name) > MaxLanguageNameLength { name = name[:MaxLanguageNameLength] } l.name = name } // GetSaveNeeded returns whether this language needs database saving func (l *Language) GetSaveNeeded() bool { l.mutex.RLock() defer l.mutex.RUnlock() return l.saveNeeded } // SetSaveNeeded updates the save status func (l *Language) SetSaveNeeded(needed bool) { l.mutex.Lock() defer l.mutex.Unlock() l.saveNeeded = needed } // IsValid validates the language data func (l *Language) IsValid() bool { l.mutex.RLock() defer l.mutex.RUnlock() if l.id < MinLanguageID || l.id > MaxLanguageID { return false } if len(l.name) == 0 || len(l.name) > MaxLanguageNameLength { return false } return true } // String returns a string representation of the language func (l *Language) String() string { l.mutex.RLock() defer l.mutex.RUnlock() return fmt.Sprintf("Language{ID: %d, Name: %s, SaveNeeded: %v}", l.id, l.name, l.saveNeeded) } // Copy creates a deep copy of the language func (l *Language) Copy() *Language { return NewLanguageFromExisting(l) } // MasterLanguagesList manages the global list of all available languages type MasterLanguagesList struct { languages map[int32]*Language // Languages indexed by ID for fast lookup nameIndex map[string]*Language // Languages indexed by name for name lookups mutex sync.RWMutex // Thread safety } // NewMasterLanguagesList creates a new master languages list func NewMasterLanguagesList() *MasterLanguagesList { return &MasterLanguagesList{ languages: make(map[int32]*Language), nameIndex: make(map[string]*Language), } } // Clear removes all languages from the list func (mll *MasterLanguagesList) Clear() { mll.mutex.Lock() defer mll.mutex.Unlock() mll.languages = make(map[int32]*Language) mll.nameIndex = make(map[string]*Language) } // Size returns the number of languages in the list func (mll *MasterLanguagesList) Size() int32 { mll.mutex.RLock() defer mll.mutex.RUnlock() return int32(len(mll.languages)) } // AddLanguage adds a new language to the master list func (mll *MasterLanguagesList) AddLanguage(language *Language) error { if language == nil { return fmt.Errorf("language cannot be nil") } if !language.IsValid() { return fmt.Errorf("language is not valid: %s", language.String()) } mll.mutex.Lock() defer mll.mutex.Unlock() // Check for duplicate ID if _, exists := mll.languages[language.GetID()]; exists { return fmt.Errorf("language with ID %d already exists", language.GetID()) } // Check for duplicate name name := language.GetName() if _, exists := mll.nameIndex[name]; exists { return fmt.Errorf("language with name '%s' already exists", name) } // Add to both indexes mll.languages[language.GetID()] = language mll.nameIndex[name] = language return nil } // GetLanguage retrieves a language by ID func (mll *MasterLanguagesList) GetLanguage(id int32) *Language { mll.mutex.RLock() defer mll.mutex.RUnlock() return mll.languages[id] } // GetLanguageByName retrieves a language by name func (mll *MasterLanguagesList) GetLanguageByName(name string) *Language { mll.mutex.RLock() defer mll.mutex.RUnlock() return mll.nameIndex[name] } // GetAllLanguages returns a copy of all languages func (mll *MasterLanguagesList) GetAllLanguages() []*Language { mll.mutex.RLock() defer mll.mutex.RUnlock() result := make([]*Language, 0, len(mll.languages)) for _, lang := range mll.languages { result = append(result, lang) } return result } // HasLanguage checks if a language exists by ID func (mll *MasterLanguagesList) HasLanguage(id int32) bool { mll.mutex.RLock() defer mll.mutex.RUnlock() _, exists := mll.languages[id] return exists } // HasLanguageByName checks if a language exists by name func (mll *MasterLanguagesList) HasLanguageByName(name string) bool { mll.mutex.RLock() defer mll.mutex.RUnlock() _, exists := mll.nameIndex[name] return exists } // RemoveLanguage removes a language by ID func (mll *MasterLanguagesList) RemoveLanguage(id int32) bool { mll.mutex.Lock() defer mll.mutex.Unlock() language, exists := mll.languages[id] if !exists { return false } // Remove from both indexes delete(mll.languages, id) delete(mll.nameIndex, language.GetName()) return true } // UpdateLanguage updates an existing language func (mll *MasterLanguagesList) UpdateLanguage(language *Language) error { if language == nil { return fmt.Errorf("language cannot be nil") } if !language.IsValid() { return fmt.Errorf("language is not valid: %s", language.String()) } mll.mutex.Lock() defer mll.mutex.Unlock() id := language.GetID() oldLanguage, exists := mll.languages[id] if !exists { return fmt.Errorf("language with ID %d does not exist", id) } // Remove old name index if name changed oldName := oldLanguage.GetName() newName := language.GetName() if oldName != newName { delete(mll.nameIndex, oldName) // Check for name conflicts if _, exists := mll.nameIndex[newName]; exists { return fmt.Errorf("language with name '%s' already exists", newName) } mll.nameIndex[newName] = language } // Update language mll.languages[id] = language return nil } // GetLanguageNames returns all language names func (mll *MasterLanguagesList) GetLanguageNames() []string { mll.mutex.RLock() defer mll.mutex.RUnlock() names := make([]string, 0, len(mll.nameIndex)) for name := range mll.nameIndex { names = append(names, name) } return names } // PlayerLanguagesList manages languages known by a specific player type PlayerLanguagesList struct { languages map[int32]*Language // Player's languages indexed by ID nameIndex map[string]*Language // Player's languages indexed by name mutex sync.RWMutex // Thread safety } // NewPlayerLanguagesList creates a new player languages list func NewPlayerLanguagesList() *PlayerLanguagesList { return &PlayerLanguagesList{ languages: make(map[int32]*Language), nameIndex: make(map[string]*Language), } } // Clear removes all languages from the player's list func (pll *PlayerLanguagesList) Clear() { pll.mutex.Lock() defer pll.mutex.Unlock() pll.languages = make(map[int32]*Language) pll.nameIndex = make(map[string]*Language) } // Add adds a language to the player's known languages func (pll *PlayerLanguagesList) Add(language *Language) error { if language == nil { return fmt.Errorf("language cannot be nil") } pll.mutex.Lock() defer pll.mutex.Unlock() id := language.GetID() name := language.GetName() // Check if already known if _, exists := pll.languages[id]; exists { return fmt.Errorf("player already knows language with ID %d", id) } // Check player language limit if len(pll.languages) >= MaxLanguagesPerPlayer { return fmt.Errorf("player has reached maximum language limit (%d)", MaxLanguagesPerPlayer) } // Add to both indexes pll.languages[id] = language pll.nameIndex[name] = language return nil } // GetLanguage retrieves a language the player knows by ID func (pll *PlayerLanguagesList) GetLanguage(id int32) *Language { pll.mutex.RLock() defer pll.mutex.RUnlock() return pll.languages[id] } // GetLanguageByName retrieves a language the player knows by name func (pll *PlayerLanguagesList) GetLanguageByName(name string) *Language { pll.mutex.RLock() defer pll.mutex.RUnlock() return pll.nameIndex[name] } // GetAllLanguages returns a copy of all languages the player knows func (pll *PlayerLanguagesList) GetAllLanguages() []*Language { pll.mutex.RLock() defer pll.mutex.RUnlock() result := make([]*Language, 0, len(pll.languages)) for _, lang := range pll.languages { result = append(result, lang) } return result } // HasLanguage checks if the player knows a language by ID func (pll *PlayerLanguagesList) HasLanguage(id int32) bool { pll.mutex.RLock() defer pll.mutex.RUnlock() _, exists := pll.languages[id] return exists } // HasLanguageByName checks if the player knows a language by name func (pll *PlayerLanguagesList) HasLanguageByName(name string) bool { pll.mutex.RLock() defer pll.mutex.RUnlock() _, exists := pll.nameIndex[name] return exists } // RemoveLanguage removes a language from the player's known languages func (pll *PlayerLanguagesList) RemoveLanguage(id int32) bool { pll.mutex.Lock() defer pll.mutex.Unlock() language, exists := pll.languages[id] if !exists { return false } // Remove from both indexes delete(pll.languages, id) delete(pll.nameIndex, language.GetName()) return true } // Size returns the number of languages the player knows func (pll *PlayerLanguagesList) Size() int32 { pll.mutex.RLock() defer pll.mutex.RUnlock() return int32(len(pll.languages)) } // GetLanguageIDs returns all language IDs the player knows func (pll *PlayerLanguagesList) GetLanguageIDs() []int32 { pll.mutex.RLock() defer pll.mutex.RUnlock() ids := make([]int32, 0, len(pll.languages)) for id := range pll.languages { ids = append(ids, id) } return ids } // GetLanguageNames returns all language names the player knows func (pll *PlayerLanguagesList) GetLanguageNames() []string { pll.mutex.RLock() defer pll.mutex.RUnlock() names := make([]string, 0, len(pll.nameIndex)) for name := range pll.nameIndex { names = append(names, name) } return names } // LanguageStatistics contains language system statistics type LanguageStatistics struct { TotalLanguages int `json:"total_languages"` PlayersWithLanguages int `json:"players_with_languages"` LanguageUsageCount map[int32]int64 `json:"language_usage_count"` LanguageLookups int64 `json:"language_lookups"` LanguagesByName map[string]int32 `json:"languages_by_name"` }