601 lines
17 KiB
Go

package alt_advancement
import (
"database/sql"
"log"
"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]interface{}, error)
}
// AAPacketHandler interface for handling AA-related packets
type AAPacketHandler interface {
// List packets
GetAAListPacket(client interface{}) ([]byte, error)
SendAAUpdate(client interface{}, playerState *AAPlayerState) error
// Purchase packets
HandleAAPurchase(client interface{}, nodeID int32, rank int8) error
SendAAPurchaseResponse(client interface{}, success bool, nodeID int32, newRank int8) error
// Template packets
SendAATemplateList(client interface{}, templates map[int8]*AATemplate) error
HandleAATemplateChange(client interface{}, templateID int8) error
// Display packets
DisplayAA(client interface{}, templateID int8, changeMode int8) error
SendAATabUpdate(client interface{}, 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 *AltAdvanceData) error
ValidateAAPoints(playerState *AAPlayerState, pointsRequired int32) error
// Player validation
ValidatePlayerLevel(playerState *AAPlayerState, aaData *AltAdvanceData) error
ValidatePlayerClass(playerState *AAPlayerState, aaData *AltAdvanceData) error
ValidateExpansionRequirements(playerState *AAPlayerState, aaData *AltAdvanceData) error
// Template validation
ValidateTemplateChange(playerState *AAPlayerState, templateID int8) error
ValidateTemplateEntries(entries []*AAEntry) error
// System validation
ValidateAAData(aaData *AltAdvanceData) 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]interface{}
GetSystemPerformanceStats() map[string]interface{}
}
// AACache interface for caching AA data
type AACache interface {
// AA data caching
GetAA(nodeID int32) (*AltAdvanceData, bool)
SetAA(nodeID int32, aaData *AltAdvanceData)
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]interface{}
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 ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
Commit() error
Rollback() error
}
// DatabaseImpl provides a concrete implementation of AADatabase
type DatabaseImpl struct {
db *sql.DB
masterAAList *MasterAAList
masterNodeList *MasterAANodeList
logger *log.Logger
}
// NewDatabaseImpl creates a new database implementation
func NewDatabaseImpl(db *sql.DB, masterAAList *MasterAAList, masterNodeList *MasterAANodeList, logger *log.Logger) *DatabaseImpl {
return &DatabaseImpl{
db: db,
masterAAList: masterAAList,
masterNodeList: masterNodeList,
logger: logger,
}
}
// 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) ([]*AltAdvanceData, 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) (*AltAdvanceData, error)
GetAABySpellID(spellID int32) (*AltAdvanceData, error)
GetAAsByGroup(group int8) ([]*AltAdvanceData, error)
GetAAsByClass(classID int8) ([]*AltAdvanceData, error)
// Statistics
GetSystemStats() *AAManagerStats
GetPlayerStats(characterID int32) map[string]interface{}
// 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) ([]*AltAdvanceData, 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]interface{} {
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]*AltAdvanceData
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]*AltAdvanceData),
playerStates: make(map[int32]*AAPlayerState),
treeNodes: make(map[int32]*TreeNodeData),
maxSize: maxSize,
}
}
// GetAA retrieves AA data from cache
func (c *SimpleAACache) GetAA(nodeID int32) (*AltAdvanceData, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
if data, exists := c.aaData[nodeID]; exists {
c.hits++
return data.Copy(), true
}
c.misses++
return nil, false
}
// SetAA stores AA data in cache
func (c *SimpleAACache) SetAA(nodeID int32, aaData *AltAdvanceData) {
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.Copy()
}
// 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]*AltAdvanceData)
c.playerStates = make(map[int32]*AAPlayerState)
c.treeNodes = make(map[int32]*TreeNodeData)
}
// GetStats returns cache statistics
func (c *SimpleAACache) GetStats() map[string]interface{} {
c.mutex.RLock()
defer c.mutex.RUnlock()
return map[string]interface{}{
"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
}