460 lines
11 KiB
Go
460 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"`
|
|
} |