package alt_advancement import ( "database/sql" "sync" "time" ) // AADatabase interface for database operations type AADatabase interface { // Core data loading LoadAltAdvancements() error LoadTreeNodes() error // Player data operations LoadPlayerAA(characterID int32) (*AAPlayerState, error) SavePlayerAA(playerState *AAPlayerState) error DeletePlayerAA(characterID int32) error // Template operations LoadPlayerAADefaults(classID int8) (map[int8][]*AAEntry, error) // Statistics GetAAStatistics() (map[string]any, error) } // AAPacketHandler interface for handling AA-related packets type AAPacketHandler interface { // List packets GetAAListPacket(client any) ([]byte, error) SendAAUpdate(client any, playerState *AAPlayerState) error // Purchase packets HandleAAPurchase(client any, nodeID int32, rank int8) error SendAAPurchaseResponse(client any, success bool, nodeID int32, newRank int8) error // Template packets SendAATemplateList(client any, templates map[int8]*AATemplate) error HandleAATemplateChange(client any, templateID int8) error // Display packets DisplayAA(client any, templateID int8, changeMode int8) error SendAATabUpdate(client any, tabID int8, tab *AATab) error } // AAEventHandler interface for handling AA events type AAEventHandler interface { // Purchase events OnAAPurchased(characterID int32, nodeID int32, newRank int8, pointsSpent int32) error OnAARefunded(characterID int32, nodeID int32, oldRank int8, pointsRefunded int32) error // Template events OnAATemplateChanged(characterID int32, oldTemplate, newTemplate int8) error OnAATemplateCreated(characterID int32, templateID int8, name string) error // System events OnAASystemLoaded(totalAAs int32, totalNodes int32) error OnAADataReloaded() error // Player events OnPlayerAALoaded(characterID int32, playerState *AAPlayerState) error OnPlayerAAPointsChanged(characterID int32, oldPoints, newPoints int32) error } // AAValidator interface for validating AA operations type AAValidator interface { // Purchase validation ValidateAAPurchase(playerState *AAPlayerState, nodeID int32, targetRank int8) error ValidateAAPrerequisites(playerState *AAPlayerState, aaData *AltAdvancement) error ValidateAAPoints(playerState *AAPlayerState, pointsRequired int32) error // Player validation ValidatePlayerLevel(playerState *AAPlayerState, aaData *AltAdvancement) error ValidatePlayerClass(playerState *AAPlayerState, aaData *AltAdvancement) error ValidateExpansionRequirements(playerState *AAPlayerState, aaData *AltAdvancement) error // Template validation ValidateTemplateChange(playerState *AAPlayerState, templateID int8) error ValidateTemplateEntries(entries []*AAEntry) error // System validation ValidateAAData(aaData *AltAdvancement) error ValidateTreeNodeData(nodeData *TreeNodeData) error } // AANotifier interface for sending notifications type AANotifier interface { // Purchase notifications NotifyAAPurchaseSuccess(characterID int32, aaName string, newRank int8) error NotifyAAPurchaseFailure(characterID int32, reason string) error NotifyAARefund(characterID int32, aaName string, pointsRefunded int32) error // Progress notifications NotifyAAProgressUpdate(characterID int32, tabID int8, pointsSpent int32) error NotifyAAPointsAwarded(characterID int32, pointsAwarded int32, reason string) error // System notifications NotifyAASystemUpdate(message string) error NotifyAASystemMaintenance(maintenanceStart time.Time, duration time.Duration) error // Achievement notifications NotifyAAMilestone(characterID int32, milestone string, totalPoints int32) error NotifyAATreeCompleted(characterID int32, tabID int8, tabName string) error } // AAStatistics interface for tracking AA statistics type AAStatistics interface { // Purchase statistics RecordAAPurchase(characterID int32, nodeID int32, pointsSpent int32) RecordAARefund(characterID int32, nodeID int32, pointsRefunded int32) // Usage statistics RecordAAUsage(characterID int32, nodeID int32, usageType string) RecordPlayerLogin(characterID int32, totalAAPoints int32) RecordPlayerLogout(characterID int32, sessionDuration time.Duration) // Performance statistics RecordDatabaseQuery(queryType string, duration time.Duration) RecordPacketSent(packetType string, size int32) RecordCacheHit(cacheType string) RecordCacheMiss(cacheType string) // Aggregated statistics GetAAPurchaseStats() map[int32]int64 GetPopularAAs() map[int32]int64 GetPlayerProgressStats() map[string]any GetSystemPerformanceStats() map[string]any } // AACache interface for caching AA data type AACache interface { // AA data caching GetAA(nodeID int32) (*AltAdvancement, bool) SetAA(nodeID int32, aaData *AltAdvancement) InvalidateAA(nodeID int32) // Player state caching GetPlayerState(characterID int32) (*AAPlayerState, bool) SetPlayerState(characterID int32, playerState *AAPlayerState) InvalidatePlayerState(characterID int32) // Tree node caching GetTreeNode(treeID int32) (*TreeNodeData, bool) SetTreeNode(treeID int32, nodeData *TreeNodeData) InvalidateTreeNode(treeID int32) // Cache management Clear() GetStats() map[string]any SetMaxSize(maxSize int32) } // Client interface for client operations (to avoid circular dependencies) type Client interface { GetCharacterID() int32 GetPlayer() Player SendPacket(data []byte) error GetClientVersion() int16 } // Player interface for player operations (to avoid circular dependencies) type Player interface { GetCharacterID() int32 GetLevel() int8 GetClass() int8 GetRace() int8 GetName() string GetAdventureClass() int8 HasExpansion(expansionFlag int8) bool } // Transaction interface for database transactions type Transaction interface { Exec(query string, args ...any) (sql.Result, error) Query(query string, args ...any) (*sql.Rows, error) QueryRow(query string, args ...any) *sql.Row Commit() error Rollback() error } // Note: Database operations are now embedded in the main AltAdvancement type // AAManagerInterface defines the main interface for AA management type AAManagerInterface interface { // System lifecycle Start() error Stop() error IsRunning() bool // Data loading LoadAAData() error ReloadAAData() error // Player operations LoadPlayerAA(characterID int32) (*AAPlayerState, error) SavePlayerAA(characterID int32) error GetPlayerAAState(characterID int32) (*AAPlayerState, error) // AA operations PurchaseAA(characterID int32, nodeID int32, targetRank int8) error RefundAA(characterID int32, nodeID int32) error GetAvailableAAs(characterID int32, tabID int8) ([]*AltAdvancement, error) // Template operations ChangeAATemplate(characterID int32, templateID int8) error SaveAATemplate(characterID int32, templateID int8, name string) error GetAATemplates(characterID int32) (map[int8]*AATemplate, error) // Point operations AwardAAPoints(characterID int32, points int32, reason string) error GetAAPoints(characterID int32) (int32, int32, int32, error) // total, spent, available // Query operations GetAA(nodeID int32) (*AltAdvancement, error) GetAABySpellID(spellID int32) (*AltAdvancement, error) GetAAsByGroup(group int8) ([]*AltAdvancement, error) GetAAsByClass(classID int8) ([]*AltAdvancement, error) // Statistics GetSystemStats() *AAManagerStats GetPlayerStats(characterID int32) map[string]any // Configuration SetConfig(config AAManagerConfig) error GetConfig() AAManagerConfig // Integration SetDatabase(db AADatabase) SetPacketHandler(handler AAPacketHandler) SetEventHandler(handler AAEventHandler) SetValidator(validator AAValidator) SetNotifier(notifier AANotifier) SetStatistics(stats AAStatistics) SetCache(cache AACache) } // AAAware interface for entities that can interact with the AA system type AAAware interface { // AA point management GetAAPoints() (total, spent, available int32) SetAAPoints(total, spent, available int32) AwardAAPoints(points int32, reason string) error SpendAAPoints(points int32) error // AA progression GetAAState() *AAPlayerState SetAAState(state *AAPlayerState) GetAARank(nodeID int32) int8 SetAARank(nodeID int32, rank int8) error // Template management GetActiveAATemplate() int8 SetActiveAATemplate(templateID int8) error GetAATemplate(templateID int8) *AATemplate SaveAATemplate(templateID int8, name string) error } // AAAdapter adapts AA functionality for other systems type AAAdapter struct { manager AAManagerInterface characterID int32 } // NewAAAdapter creates a new AA adapter func NewAAAdapter(manager AAManagerInterface, characterID int32) *AAAdapter { return &AAAdapter{ manager: manager, characterID: characterID, } } // GetManager returns the wrapped AA manager func (aa *AAAdapter) GetManager() AAManagerInterface { return aa.manager } // GetCharacterID returns the character ID func (aa *AAAdapter) GetCharacterID() int32 { return aa.characterID } // GetAAPoints returns the character's AA points func (aa *AAAdapter) GetAAPoints() (total, spent, available int32, err error) { return aa.manager.GetAAPoints(aa.characterID) } // PurchaseAA purchases an AA for the character func (aa *AAAdapter) PurchaseAA(nodeID int32, targetRank int8) error { return aa.manager.PurchaseAA(aa.characterID, nodeID, targetRank) } // RefundAA refunds an AA for the character func (aa *AAAdapter) RefundAA(nodeID int32) error { return aa.manager.RefundAA(aa.characterID, nodeID) } // GetAvailableAAs returns available AAs for a tab func (aa *AAAdapter) GetAvailableAAs(tabID int8) ([]*AltAdvancement, error) { return aa.manager.GetAvailableAAs(aa.characterID, tabID) } // ChangeTemplate changes the active AA template func (aa *AAAdapter) ChangeTemplate(templateID int8) error { return aa.manager.ChangeAATemplate(aa.characterID, templateID) } // GetTemplates returns all AA temples for the character func (aa *AAAdapter) GetTemplates() (map[int8]*AATemplate, error) { return aa.manager.GetAATemplates(aa.characterID) } // GetPlayerStats returns AA statistics for the character func (aa *AAAdapter) GetPlayerStats() map[string]any { return aa.manager.GetPlayerStats(aa.characterID) } // AwardPoints awards AA points to the character func (aa *AAAdapter) AwardPoints(points int32, reason string) error { return aa.manager.AwardAAPoints(aa.characterID, points, reason) } // GetAAState returns the character's complete AA state func (aa *AAAdapter) GetAAState() (*AAPlayerState, error) { return aa.manager.GetPlayerAAState(aa.characterID) } // SaveAAState saves the character's AA state func (aa *AAAdapter) SaveAAState() error { return aa.manager.SavePlayerAA(aa.characterID) } // PlayerAAAdapter adapts player functionality for AA systems type PlayerAAAdapter struct { player Player } // NewPlayerAAAdapter creates a new player AA adapter func NewPlayerAAAdapter(player Player) *PlayerAAAdapter { return &PlayerAAAdapter{player: player} } // GetPlayer returns the wrapped player func (paa *PlayerAAAdapter) GetPlayer() Player { return paa.player } // GetCharacterID returns the player's character ID func (paa *PlayerAAAdapter) GetCharacterID() int32 { return paa.player.GetCharacterID() } // GetLevel returns the player's level func (paa *PlayerAAAdapter) GetLevel() int8 { return paa.player.GetLevel() } // GetClass returns the player's class func (paa *PlayerAAAdapter) GetClass() int8 { return paa.player.GetClass() } // GetAdventureClass returns the player's adventure class func (paa *PlayerAAAdapter) GetAdventureClass() int8 { return paa.player.GetAdventureClass() } // GetRace returns the player's race func (paa *PlayerAAAdapter) GetRace() int8 { return paa.player.GetRace() } // GetName returns the player's name func (paa *PlayerAAAdapter) GetName() string { return paa.player.GetName() } // HasExpansion checks if the player has a specific expansion func (paa *PlayerAAAdapter) HasExpansion(expansionFlag int8) bool { return paa.player.HasExpansion(expansionFlag) } // ClientAAAdapter adapts client functionality for AA systems type ClientAAAdapter struct { client Client } // NewClientAAAdapter creates a new client AA adapter func NewClientAAAdapter(client Client) *ClientAAAdapter { return &ClientAAAdapter{client: client} } // GetClient returns the wrapped client func (caa *ClientAAAdapter) GetClient() Client { return caa.client } // GetCharacterID returns the client's character ID func (caa *ClientAAAdapter) GetCharacterID() int32 { return caa.client.GetCharacterID() } // GetPlayer returns the client's player func (caa *ClientAAAdapter) GetPlayer() Player { return caa.client.GetPlayer() } // SendPacket sends a packet to the client func (caa *ClientAAAdapter) SendPacket(data []byte) error { return caa.client.SendPacket(data) } // GetClientVersion returns the client version func (caa *ClientAAAdapter) GetClientVersion() int16 { return caa.client.GetClientVersion() } // Simple cache implementation for testing type SimpleAACache struct { aaData map[int32]*AltAdvancement playerStates map[int32]*AAPlayerState treeNodes map[int32]*TreeNodeData mutex sync.RWMutex maxSize int32 hits int64 misses int64 } // NewSimpleAACache creates a new simple cache func NewSimpleAACache(maxSize int32) *SimpleAACache { return &SimpleAACache{ aaData: make(map[int32]*AltAdvancement), playerStates: make(map[int32]*AAPlayerState), treeNodes: make(map[int32]*TreeNodeData), maxSize: maxSize, } } // GetAA retrieves AA data from cache func (c *SimpleAACache) GetAA(nodeID int32) (*AltAdvancement, bool) { c.mutex.RLock() defer c.mutex.RUnlock() if data, exists := c.aaData[nodeID]; exists { c.hits++ return data.Clone(), true } c.misses++ return nil, false } // SetAA stores AA data in cache func (c *SimpleAACache) SetAA(nodeID int32, aaData *AltAdvancement) { c.mutex.Lock() defer c.mutex.Unlock() if int32(len(c.aaData)) >= c.maxSize { // Simple eviction: remove a random entry for k := range c.aaData { delete(c.aaData, k) break } } c.aaData[nodeID] = aaData.Clone() } // InvalidateAA removes AA data from cache func (c *SimpleAACache) InvalidateAA(nodeID int32) { c.mutex.Lock() defer c.mutex.Unlock() delete(c.aaData, nodeID) } // GetPlayerState retrieves player state from cache func (c *SimpleAACache) GetPlayerState(characterID int32) (*AAPlayerState, bool) { c.mutex.RLock() defer c.mutex.RUnlock() if state, exists := c.playerStates[characterID]; exists { c.hits++ return state, true } c.misses++ return nil, false } // SetPlayerState stores player state in cache func (c *SimpleAACache) SetPlayerState(characterID int32, playerState *AAPlayerState) { c.mutex.Lock() defer c.mutex.Unlock() if int32(len(c.playerStates)) >= c.maxSize { // Simple eviction: remove a random entry for k := range c.playerStates { delete(c.playerStates, k) break } } c.playerStates[characterID] = playerState } // InvalidatePlayerState removes player state from cache func (c *SimpleAACache) InvalidatePlayerState(characterID int32) { c.mutex.Lock() defer c.mutex.Unlock() delete(c.playerStates, characterID) } // GetTreeNode retrieves tree node from cache func (c *SimpleAACache) GetTreeNode(treeID int32) (*TreeNodeData, bool) { c.mutex.RLock() defer c.mutex.RUnlock() if node, exists := c.treeNodes[treeID]; exists { c.hits++ nodeCopy := *node return &nodeCopy, true } c.misses++ return nil, false } // SetTreeNode stores tree node in cache func (c *SimpleAACache) SetTreeNode(treeID int32, nodeData *TreeNodeData) { c.mutex.Lock() defer c.mutex.Unlock() if int32(len(c.treeNodes)) >= c.maxSize { // Simple eviction: remove a random entry for k := range c.treeNodes { delete(c.treeNodes, k) break } } nodeCopy := *nodeData c.treeNodes[treeID] = &nodeCopy } // InvalidateTreeNode removes tree node from cache func (c *SimpleAACache) InvalidateTreeNode(treeID int32) { c.mutex.Lock() defer c.mutex.Unlock() delete(c.treeNodes, treeID) } // Clear removes all cached data func (c *SimpleAACache) Clear() { c.mutex.Lock() defer c.mutex.Unlock() c.aaData = make(map[int32]*AltAdvancement) c.playerStates = make(map[int32]*AAPlayerState) c.treeNodes = make(map[int32]*TreeNodeData) } // GetStats returns cache statistics func (c *SimpleAACache) GetStats() map[string]any { c.mutex.RLock() defer c.mutex.RUnlock() return map[string]any{ "hits": c.hits, "misses": c.misses, "aa_data_count": len(c.aaData), "player_count": len(c.playerStates), "tree_node_count": len(c.treeNodes), "max_size": c.maxSize, } } // SetMaxSize sets the maximum cache size func (c *SimpleAACache) SetMaxSize(maxSize int32) { c.mutex.Lock() defer c.mutex.Unlock() c.maxSize = maxSize }