eq2go/internal/alt_advancement
..
2025-07-31 11:22:03 -05:00
2025-07-31 11:22:03 -05:00
2025-07-31 11:22:03 -05:00
2025-07-31 11:22:03 -05:00
2025-07-31 11:22:03 -05:00
2025-07-30 19:42:37 -05:00
2025-07-31 11:22:03 -05:00

Alternate Advancement System

The alternate advancement system (internal/alt_advancement) provides comprehensive character progression beyond normal leveling for the EQ2Go server emulator. This system is converted from the original C++ EQ2EMu AltAdvancement implementation with modern Go concurrency patterns and clean architecture principles.

Overview

The alternate advancement (AA) system manages character progression through specialized skill trees including:

  • Class Trees: Class-specific advancement paths
  • Subclass Trees: Subclass-specific specializations
  • Heroic Trees: Heroic advancement from Destiny of Velious
  • Shadows Trees: Shadow-based abilities from Shadows of Luclin
  • Tradeskill Trees: Tradeskill-focused advancement
  • Prestige Trees: Prestigious advancement paths
  • Dragon Trees: Dragon-themed advancement
  • Far Seas Trees: Far Seas trading company advancement

Architecture

Core Components

MasterAAList - Global repository of all AA definitions with fast lookup capabilities
MasterAANodeList - Tree node configurations mapping classes to AA trees
AAManager - Central management system for all AA operations
AAPlayerState - Individual player AA progression and template management
DatabaseImpl - Database operations for persistent AA data

Key Files

  • types.go - Core data structures and type definitions
  • constants.go - All AA system constants, tab definitions, and limits
  • master_list.go - MasterAAList and MasterAANodeList implementations
  • manager.go - Central AAManager with player state management
  • database.go - Database operations and persistence
  • interfaces.go - System integration interfaces and adapters
  • README.md - This documentation

System Initialization

// Create AA manager with configuration
config := alt_advancement.DefaultAAManagerConfig()
config.EnableCaching = true
config.DatabaseEnabled = true

aaManager := alt_advancement.NewAAManager(config)

// Set up database integration
database := alt_advancement.NewDatabaseImpl(db, masterAAList, masterNodeList, logger)
aaManager.SetDatabase(database)

// Start the system
err := aaManager.Start()
if err != nil {
    log.Fatalf("Failed to start AA system: %v", err)
}

AA Data Management

// Load all AA data from database
err := aaManager.LoadAAData()
if err != nil {
    log.Printf("Failed to load AA data: %v", err)
}

// Get specific AA by node ID
aaData, err := aaManager.GetAA(nodeID)
if err != nil {
    log.Printf("AA not found: %v", err)
}

// Get AAs for a specific class
classAAs, err := aaManager.GetAAsByClass(classID)
fmt.Printf("Found %d AAs for class %d\n", len(classAAs), classID)

// Get AAs for a specific tab/group
tabAAs, err := aaManager.GetAAsByGroup(alt_advancement.AA_CLASS)
fmt.Printf("Class tab has %d AAs\n", len(tabAAs))

Player AA Management

// Load player's AA data
characterID := int32(12345)
playerState, err := aaManager.LoadPlayerAA(characterID)
if err != nil {
    log.Printf("Failed to load player AA: %v", err)
}

// Get player's AA point totals
totalPoints, spentPoints, availablePoints, err := aaManager.GetAAPoints(characterID)
fmt.Printf("Player has %d total, %d spent, %d available AA points\n", 
    totalPoints, spentPoints, availablePoints)

// Award AA points to player
err = aaManager.AwardAAPoints(characterID, 10, "Level up bonus")
if err != nil {
    log.Printf("Failed to award AA points: %v", err)
}

AA Purchasing System

// Purchase an AA for a player
nodeID := int32(1001)
targetRank := int8(1)

err := aaManager.PurchaseAA(characterID, nodeID, targetRank)
if err != nil {
    log.Printf("AA purchase failed: %v", err)
} else {
    fmt.Println("AA purchased successfully!")
}

// Refund an AA
err = aaManager.RefundAA(characterID, nodeID)
if err != nil {
    log.Printf("AA refund failed: %v", err)
}

// Check available AAs for a tab
availableAAs, err := aaManager.GetAvailableAAs(characterID, alt_advancement.AA_CLASS)
fmt.Printf("Player can purchase %d AAs in class tab\n", len(availableAAs))

AA Templates System

// Change active AA template
templateID := int8(alt_advancement.AA_TEMPLATE_PERSONAL_1)
err := aaManager.ChangeAATemplate(characterID, templateID)
if err != nil {
    log.Printf("Template change failed: %v", err)
}

// Save custom AA template
err = aaManager.SaveAATemplate(characterID, templateID, "My Build")
if err != nil {
    log.Printf("Template save failed: %v", err)
}

// Get all templates for player
templates, err := aaManager.GetAATemplates(characterID)
if err != nil {
    log.Printf("Failed to get templates: %v", err)
} else {
    for id, template := range templates {
        fmt.Printf("Template %d: %s (%d entries)\n", 
            id, template.Name, len(template.Entries))
    }
}

AA Data Structures

AltAdvanceData - Individual AA Definition

type AltAdvanceData struct {
    SpellID        int32  // Associated spell ID
    NodeID         int32  // Unique node identifier
    Name           string // Display name
    Description    string // AA description
    Group          int8   // Tab/group (AA_CLASS, AA_SUBCLASS, etc.)
    Col            int8   // Column position in tree
    Row            int8   // Row position in tree
    Icon           int16  // Display icon ID
    RankCost       int8   // Cost per rank
    MaxRank        int8   // Maximum achievable rank
    MinLevel       int8   // Minimum character level
    RankPrereqID   int32  // Prerequisite AA node ID
    RankPrereq     int8   // Required rank in prerequisite
    ClassReq       int8   // Required class (0 = all classes)
    // ... additional fields
}

AAPlayerState - Player AA Progression

type AAPlayerState struct {
    CharacterID     int32  // Character identifier
    TotalPoints     int32  // Total AA points earned
    SpentPoints     int32  // Total AA points spent
    AvailablePoints int32  // Available AA points for spending
    BankedPoints    int32  // Banked AA points
    ActiveTemplate  int8   // Currently active template
    Templates       map[int8]*AATemplate    // All templates
    Tabs            map[int8]*AATab         // Tab states
    AAProgress      map[int32]*PlayerAAData // AA progression by node ID
    // ... synchronization and metadata
}

AA Tab System

The system supports 10 different AA tabs:

// AA tab constants
const (
    AA_CLASS               = 0  // Class-specific abilities
    AA_SUBCLASS            = 1  // Subclass specializations
    AA_SHADOW              = 2  // Shadow abilities
    AA_HEROIC              = 3  // Heroic advancement
    AA_TRADESKILL          = 4  // Tradeskill abilities
    AA_PRESTIGE            = 5  // Prestige advancement
    AA_TRADESKILL_PRESTIGE = 6  // Tradeskill prestige
    AA_DRAGON              = 7  // Dragon abilities
    AA_DRAGONCLASS         = 8  // Dragon class abilities
    AA_FARSEAS             = 9  // Far Seas abilities
)

// Get maximum AA points for each tab
maxClassAA := alt_advancement.GetMaxAAForTab(alt_advancement.AA_CLASS)  // 100
maxHeroicAA := alt_advancement.GetMaxAAForTab(alt_advancement.AA_HEROIC) // 50

Database Integration

// Custom database implementation
type MyAADatabase struct {
    db *sql.DB
}

func (db *MyAADatabase) LoadAltAdvancements() error {
    // Load AA definitions from database
    return nil
}

func (db *MyAADatabase) LoadPlayerAA(characterID int32) (*AAPlayerState, error) {
    // Load player AA data from database
    return nil, nil
}

func (db *MyAADatabase) SavePlayerAA(playerState *AAPlayerState) error {
    // Save player AA data to database
    return nil
}

// Set database implementation
aaManager.SetDatabase(&MyAADatabase{db: myDB})

Event Handling

// Custom event handler
type MyAAEventHandler struct{}

func (h *MyAAEventHandler) OnAAPurchased(characterID int32, nodeID int32, newRank int8, pointsSpent int32) error {
    fmt.Printf("Player %d purchased AA %d rank %d for %d points\n", 
        characterID, nodeID, newRank, pointsSpent)
    return nil
}

func (h *MyAAEventHandler) OnAATemplateChanged(characterID int32, oldTemplate, newTemplate int8) error {
    fmt.Printf("Player %d changed template from %d to %d\n", 
        characterID, oldTemplate, newTemplate)
    return nil
}

func (h *MyAAEventHandler) OnPlayerAAPointsChanged(characterID int32, oldPoints, newPoints int32) error {
    fmt.Printf("Player %d AA points changed from %d to %d\n", 
        characterID, oldPoints, newPoints)
    return nil
}

// Register event handler
aaManager.SetEventHandler(&MyAAEventHandler{})

Validation System

// Custom validator
type MyAAValidator struct{}

func (v *MyAAValidator) ValidateAAPurchase(playerState *AAPlayerState, nodeID int32, targetRank int8) error {
    // Check if player has enough points
    if playerState.AvailablePoints < int32(targetRank) {
        return fmt.Errorf("insufficient AA points")
    }
    
    // Check prerequisites
    // ... validation logic
    
    return nil
}

func (v *MyAAValidator) ValidatePlayerLevel(playerState *AAPlayerState, aaData *AltAdvanceData) error {
    // Check minimum level requirement
    // ... validation logic
    return nil
}

// Set validator
aaManager.SetValidator(&MyAAValidator{})

Statistics and Monitoring

// Get system statistics
stats := aaManager.GetSystemStats()
fmt.Printf("Total AAs loaded: %d\n", stats.TotalAAsLoaded)
fmt.Printf("Active players: %d\n", stats.ActivePlayers)
fmt.Printf("Total purchases: %d\n", stats.TotalAAPurchases)
fmt.Printf("Average points spent: %.1f\n", stats.AveragePointsSpent)

// Get player-specific statistics
playerStats := aaManager.GetPlayerStats(characterID)
fmt.Printf("Player stats: %+v\n", playerStats)

// Get database statistics (if database supports it)
if db, ok := aaManager.database.(*DatabaseImpl); ok {
    dbStats, err := db.GetAAStatistics()
    if err == nil {
        fmt.Printf("Database stats: %+v\n", dbStats)
    }
}

Configuration Options

// Configure the AA system
config := alt_advancement.AAManagerConfig{
    EnableAASystem:     true,
    EnableCaching:      true,
    EnableValidation:   true,
    EnableLogging:      false,
    AAPointsPerLevel:   2,
    MaxBankedPoints:    30,
    EnableAABanking:    true,
    CacheSize:          10000,
    UpdateInterval:     1 * time.Second,
    BatchSize:          100,
    DatabaseEnabled:    true,
    AutoSave:           true,
    SaveInterval:       5 * time.Minute,
}

aaManager.SetConfig(config)

Caching System

// Enable caching for better performance
cache := alt_advancement.NewSimpleAACache(1000)
aaManager.SetCache(cache)

// Get cache statistics
cacheStats := cache.GetStats()
fmt.Printf("Cache hits: %d, misses: %d\n", 
    cacheStats["hits"], cacheStats["misses"])

Packet Handling Integration

// Custom packet handler
type MyAAPacketHandler struct{}

func (ph *MyAAPacketHandler) GetAAListPacket(client interface{}) ([]byte, error) {
    // Build AA list packet for client
    return []byte{}, nil
}

func (ph *MyAAPacketHandler) SendAAUpdate(client interface{}, playerState *AAPlayerState) error {
    // Send AA update to client
    return nil
}

func (ph *MyAAPacketHandler) HandleAAPurchase(client interface{}, nodeID int32, rank int8) error {
    // Handle AA purchase from client
    return nil
}

// Set packet handler
aaManager.SetPacketHandler(&MyAAPacketHandler{})

Advanced Usage

Custom AA Trees

// Create custom AA data
customAA := &alt_advancement.AltAdvanceData{
    SpellID:      2001,
    NodeID:       2001,
    Name:         "Custom Ability",
    Description:  "A custom AA ability",
    Group:        alt_advancement.AA_CLASS,
    Col:          1,
    Row:          1,
    Icon:         100,
    RankCost:     1,
    MaxRank:      5,
    MinLevel:     20,
    ClassReq:     1, // Fighter only
}

// Add to master list
err := masterAAList.AddAltAdvancement(customAA)
if err != nil {
    log.Printf("Failed to add custom AA: %v", err)
}

Bulk Operations

// Award AA points to multiple players
playerIDs := []int32{1001, 1002, 1003}
for _, playerID := range playerIDs {
    err := aaManager.AwardAAPoints(playerID, 5, "Server event bonus")
    if err != nil {
        log.Printf("Failed to award points to player %d: %v", playerID, err)
    }
}

// Batch save player states
for _, playerID := range playerIDs {
    err := aaManager.SavePlayerAA(playerID)
    if err != nil {
        log.Printf("Failed to save player %d AA data: %v", playerID, err)
    }
}

Thread Safety

All AA operations are thread-safe using appropriate synchronization:

  • RWMutex for read-heavy operations (AA lookups, player state access)
  • Atomic operations for simple counters and flags
  • Proper lock ordering to prevent deadlocks
  • Background goroutines for periodic processing and auto-save
  • Channel-based communication for event handling

Performance Considerations

  • Efficient data structures with hash maps for O(1) lookups
  • Caching system to reduce database queries
  • Batch processing for bulk operations
  • Background processing to avoid blocking gameplay
  • Statistics collection with minimal overhead
  • Memory-efficient storage with proper cleanup

Integration with Other Systems

The AA system integrates with:

  • Player System - Player-specific AA progression and point management
  • Spell System - AA abilities are linked to spells
  • Class System - Class-specific AA trees and requirements
  • Level System - Level-based AA point awards and prerequisites
  • Database System - Persistent storage of AA data and player progress
  • Client System - AA UI updates and purchase handling
  • Achievement System - AA milestones and progression tracking

Migration from C++

This Go implementation maintains compatibility with the original C++ EQ2EMu AA system while providing:

  • Modern concurrency with goroutines and channels
  • Better error handling with Go's error interface
  • Cleaner architecture with interface-based design
  • Improved maintainability with package organization
  • Enhanced testing capabilities
  • Type safety with Go's type system
  • Memory management with Go's garbage collector

TODO Items

The conversion includes areas for future implementation:

  • Complete packet handling for all client communication
  • Advanced validation for complex AA prerequisites
  • Lua scripting integration for custom AA behaviors
  • Web administration interface for AA management
  • Performance optimizations for large-scale deployments
  • Advanced caching strategies with TTL and eviction policies
  • Metrics and monitoring integration with external systems
  • AA import/export functionality for configuration management

Usage Examples

See the code examples throughout this documentation for detailed usage patterns. The system is designed to be used alongside the existing EQ2Go server infrastructure with proper initialization and configuration.

The AA system provides a solid foundation for character progression mechanics while maintaining the flexibility to extend and customize behavior through the comprehensive interface system.