Languages Package
The languages package provides comprehensive multilingual character communication system for EverQuest II server emulation. It manages language learning, chat processing, and player language knowledge with thread-safe operations and efficient lookups.
Features
- Master Language Registry: Global list of all available languages with ID-based and name-based lookups
- Player Language Management: Individual player language collections with learning/forgetting mechanics
- Chat Processing: Multilingual message processing with scrambling for unknown languages
- Database Integration: Language persistence with player-specific language knowledge
- Thread Safety: All operations use proper Go concurrency patterns with mutexes
- Performance: Optimized hash table lookups with benchmark results ~15ns per operation
- EQ2 Compatibility: Supports all standard EverQuest II racial languages
Core Components
Language Types
// Core language representation
type Language struct {
id int32 // Unique language identifier
name string // Language name
saveNeeded bool // Whether this language needs database saving
mutex sync.RWMutex // Thread safety
}
// Master language registry (system-wide)
type MasterLanguagesList struct {
languages map[int32]*Language // Languages indexed by ID
nameIndex map[string]*Language // Languages indexed by name
mutex sync.RWMutex // Thread safety
}
// Player-specific language collection
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
}
Management System
// High-level language system manager
type Manager struct {
masterLanguagesList *MasterLanguagesList
database Database
logger Logger
// Statistics tracking
languageLookups int64
playersWithLanguages int64
languageUsageCount map[int32]int64
}
Integration Interfaces
// Entity interface for basic entity operations
type Entity interface {
GetID() int32
GetName() string
IsPlayer() bool
IsNPC() bool
IsBot() bool
}
// Player interface extending Entity for player-specific operations
type Player interface {
Entity
GetCharacterID() int32
SendMessage(message string)
}
// LanguageAware interface for entities with language capabilities
type LanguageAware interface {
GetKnownLanguages() *PlayerLanguagesList
KnowsLanguage(languageID int32) bool
GetPrimaryLanguage() int32
SetPrimaryLanguage(languageID int32)
CanUnderstand(languageID int32) bool
LearnLanguage(languageID int32) error
ForgetLanguage(languageID int32) error
}
Standard EQ2 Languages
The package includes all standard EverQuest II racial languages:
const (
LanguageIDCommon = 0 // Common tongue (default)
LanguageIDElvish = 1 // Elvish
LanguageIDDwarven = 2 // Dwarven
LanguageIDHalfling = 3 // Halfling
LanguageIDGnomish = 4 // Gnomish
LanguageIDIksar = 5 // Iksar
LanguageIDTrollish = 6 // Trollish
LanguageIDOgrish = 7 // Ogrish
LanguageIDFae = 8 // Fae
LanguageIDArasai = 9 // Arasai
LanguageIDSarnak = 10 // Sarnak
LanguageIDFroglok = 11 // Froglok
)
Usage Examples
Basic Setup
// Create language system
database := NewMockDatabase() // or your database implementation
logger := NewMockLogger() // or your logger implementation
manager := NewManager(database, logger)
// Add standard EQ2 languages
languages := map[int32]string{
LanguageIDCommon: "Common",
LanguageIDElvish: "Elvish",
LanguageIDDwarven: "Dwarven",
// ... add other languages
}
for id, name := range languages {
lang := NewLanguage()
lang.SetID(id)
lang.SetName(name)
manager.AddLanguage(lang)
}
Player Integration
// Player struct implementing LanguageAware
type Player struct {
id int32
name string
languageAdapter *PlayerLanguageAdapter
}
func NewPlayer(id int32, name string, languageManager *Manager, logger Logger) *Player {
return &Player{
id: id,
name: name,
languageAdapter: NewPlayerLanguageAdapter(languageManager, logger),
}
}
// Implement LanguageAware interface by delegating to adapter
func (p *Player) GetKnownLanguages() *PlayerLanguagesList {
return p.languageAdapter.GetKnownLanguages()
}
func (p *Player) KnowsLanguage(languageID int32) bool {
return p.languageAdapter.KnowsLanguage(languageID)
}
func (p *Player) LearnLanguage(languageID int32) error {
return p.languageAdapter.LearnLanguage(languageID)
}
// ... implement other LanguageAware methods
Chat Processing
// Create chat processor
processor := NewChatLanguageProcessor(manager, logger)
// Process message from speaker
message := "Mae govannen!" // Elvish greeting
processed, err := processor.ProcessMessage(speaker, message, LanguageIDElvish)
if err != nil {
// Handle error (speaker doesn't know language, etc.)
}
// Filter message for listener based on their language knowledge
filtered := processor.FilterMessage(listener, processed, LanguageIDElvish)
// If listener doesn't know Elvish, message would be scrambled
// Language scrambling for unknown languages
scrambled := processor.GetLanguageSkramble(message, 0.0) // 0% comprehension
// Returns scrambled version: "Nhl nbchuulu!"
Database Operations
// Player learning languages with database persistence
player := NewPlayer(123, "TestPlayer", manager, logger)
// Learn a language
err := player.LearnLanguage(LanguageIDElvish)
if err != nil {
log.Printf("Failed to learn language: %v", err)
}
// Save to database
err = player.languageAdapter.SavePlayerLanguages(database, player.GetCharacterID())
if err != nil {
log.Printf("Failed to save languages: %v", err)
}
// Load from database (e.g., on player login)
newPlayer := NewPlayer(123, "TestPlayer", manager, logger)
err = newPlayer.languageAdapter.LoadPlayerLanguages(database, player.GetCharacterID())
if err != nil {
log.Printf("Failed to load languages: %v", err)
}
Statistics and Management
// Get system statistics
stats := manager.GetStatistics()
fmt.Printf("Total Languages: %d\\n", stats.TotalLanguages)
fmt.Printf("Players with Languages: %d\\n", stats.PlayersWithLanguages)
fmt.Printf("Language Lookups: %d\\n", stats.LanguageLookups)
// Process management commands
result, err := manager.ProcessCommand("stats", []string{})
if err != nil {
log.Printf("Command failed: %v", err)
} else {
fmt.Println(result)
}
// Validate all languages
issues := manager.ValidateAllLanguages()
if len(issues) > 0 {
log.Printf("Language validation issues: %v", issues)
}
Database Interface
Implement the Database
interface to provide persistence:
type Database interface {
LoadAllLanguages() ([]*Language, error)
SaveLanguage(language *Language) error
DeleteLanguage(languageID int32) error
LoadPlayerLanguages(playerID int32) ([]*Language, error)
SavePlayerLanguage(playerID int32, languageID int32) error
DeletePlayerLanguage(playerID int32, languageID int32) error
}
Performance
Benchmark results on AMD Ryzen 7 8845HS:
- Language Lookup: ~15ns per operation
- Player Language Adapter: ~8ns per operation
- Integrated Operations: ~16ns per operation
The system uses hash tables for O(1) lookups and is optimized for high-frequency operations.
Thread Safety
All operations are thread-safe using:
sync.RWMutex
for read-heavy operations (language lookups)sync.Mutex
for write operations and statistics- Atomic operations where appropriate
System Limits
const (
MaxLanguagesPerPlayer = 100 // Reasonable limit per player
MaxTotalLanguages = 1000 // System-wide language limit
MaxLanguageNameLength = 50 // Maximum language name length
)
Command Interface
The manager supports administrative commands:
stats
- Show language system statisticslist
- List all languagesinfo <id|name>
- Show language informationvalidate
- Validate all languagesreload
- Reload from databaseadd <id> <name>
- Add new languageremove <id>
- Remove languagesearch <term>
- Search languages by name
Testing
Comprehensive test suite includes:
- Unit tests for all core components
- Integration tests with mock implementations
- Performance benchmarks
- Entity integration examples
- Thread safety validation
Run tests:
go test ./internal/languages -v
go test ./internal/languages -bench=.
Integration Notes
- Entity Integration: Players should embed
PlayerLanguageAdapter
and implementLanguageAware
- Database: Implement the
Database
interface for persistence - Logging: Implement the
Logger
interface for system logging - Chat System: Use
ChatLanguageProcessor
for multilingual message handling - Race Integration: Initialize players with their racial languages at character creation
The languages package follows the same architectural patterns as other EQ2Go systems and integrates seamlessly with the entity, player, and chat systems.