461 lines
11 KiB
Go

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"`
}