package rules import ( "fmt" "strconv" "sync" ) // Rule represents a single rule with category, type, and value // Converted from C++ Rule class type Rule struct { category RuleCategory // Rule category (Client, Player, etc.) ruleType RuleType // Rule type within category value string // Rule value as string combined string // Combined category:type string mutex sync.RWMutex // Thread safety } // NewRule creates a new rule with default values func NewRule() *Rule { return &Rule{ category: 0, ruleType: 0, value: "", combined: "NONE", } } // NewRuleWithValues creates a new rule with specified values func NewRuleWithValues(category RuleCategory, ruleType RuleType, value string, combined string) *Rule { return &Rule{ category: category, ruleType: ruleType, value: value, combined: combined, } } // NewRuleFromRule creates a copy of another rule func NewRuleFromRule(source *Rule) *Rule { if source == nil { return NewRule() } source.mutex.RLock() defer source.mutex.RUnlock() return &Rule{ category: source.category, ruleType: source.ruleType, value: source.value, combined: source.combined, } } // SetValue sets the rule value func (r *Rule) SetValue(value string) { r.mutex.Lock() defer r.mutex.Unlock() r.value = value } // GetCategory returns the rule category func (r *Rule) GetCategory() RuleCategory { r.mutex.RLock() defer r.mutex.RUnlock() return r.category } // GetType returns the rule type func (r *Rule) GetType() RuleType { r.mutex.RLock() defer r.mutex.RUnlock() return r.ruleType } // GetValue returns the rule value as string func (r *Rule) GetValue() string { r.mutex.RLock() defer r.mutex.RUnlock() return r.value } // GetCombined returns the combined category:type string func (r *Rule) GetCombined() string { r.mutex.RLock() defer r.mutex.RUnlock() return r.combined } // Type conversion methods matching C++ implementation // GetInt8 returns the rule value as int8 func (r *Rule) GetInt8() int8 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseInt(r.value, 10, 8); err == nil { return int8(val) } return 0 } // GetInt16 returns the rule value as int16 func (r *Rule) GetInt16() int16 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseInt(r.value, 10, 16); err == nil { return int16(val) } return 0 } // GetInt32 returns the rule value as int32 func (r *Rule) GetInt32() int32 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseInt(r.value, 10, 32); err == nil { return int32(val) } return 0 } // GetInt64 returns the rule value as int64 func (r *Rule) GetInt64() int64 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseInt(r.value, 10, 64); err == nil { return val } return 0 } // GetUInt8 returns the rule value as uint8 func (r *Rule) GetUInt8() uint8 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseUint(r.value, 10, 8); err == nil { return uint8(val) } return 0 } // GetUInt16 returns the rule value as uint16 func (r *Rule) GetUInt16() uint16 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseUint(r.value, 10, 16); err == nil { return uint16(val) } return 0 } // GetUInt32 returns the rule value as uint32 func (r *Rule) GetUInt32() uint32 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseUint(r.value, 10, 32); err == nil { return uint32(val) } return 0 } // GetUInt64 returns the rule value as uint64 func (r *Rule) GetUInt64() uint64 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseUint(r.value, 10, 64); err == nil { return val } return 0 } // GetBool returns the rule value as bool (> 0 = true) func (r *Rule) GetBool() bool { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseUint(r.value, 10, 32); err == nil { return val > 0 } return false } // GetFloat32 returns the rule value as float32 func (r *Rule) GetFloat32() float32 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseFloat(r.value, 32); err == nil { return float32(val) } return 0.0 } // GetFloat64 returns the rule value as float64 func (r *Rule) GetFloat64() float64 { r.mutex.RLock() defer r.mutex.RUnlock() if val, err := strconv.ParseFloat(r.value, 64); err == nil { return val } return 0.0 } // GetChar returns the first character of the rule value func (r *Rule) GetChar() byte { r.mutex.RLock() defer r.mutex.RUnlock() if len(r.value) > 0 { return r.value[0] } return 0 } // GetString returns the rule value as string (same as GetValue) func (r *Rule) GetString() string { return r.GetValue() } // IsValid checks if the rule has valid data func (r *Rule) IsValid() bool { r.mutex.RLock() defer r.mutex.RUnlock() return r.combined != "" && r.combined != "NONE" } // String returns a string representation of the rule func (r *Rule) String() string { r.mutex.RLock() defer r.mutex.RUnlock() return fmt.Sprintf("Rule{%s: %s}", r.combined, r.value) } // RuleSet represents a collection of rules with a name and ID // Converted from C++ RuleSet class type RuleSet struct { id int32 // Rule set ID name string // Rule set name rules map[RuleCategory]map[RuleType]*Rule // Rules organized by category and type mutex sync.RWMutex // Thread safety } // NewRuleSet creates a new empty rule set func NewRuleSet() *RuleSet { return &RuleSet{ id: 0, name: "", rules: make(map[RuleCategory]map[RuleType]*Rule), } } // NewRuleSetFromRuleSet creates a copy of another rule set func NewRuleSetFromRuleSet(source *RuleSet) *RuleSet { if source == nil { return NewRuleSet() } ruleSet := &RuleSet{ id: source.GetID(), name: source.GetName(), rules: make(map[RuleCategory]map[RuleType]*Rule), } // Deep copy all rules source.mutex.RLock() for category, typeMap := range source.rules { ruleSet.rules[category] = make(map[RuleType]*Rule) for ruleType, rule := range typeMap { ruleSet.rules[category][ruleType] = NewRuleFromRule(rule) } } source.mutex.RUnlock() return ruleSet } // SetID sets the rule set ID func (rs *RuleSet) SetID(id int32) { rs.mutex.Lock() defer rs.mutex.Unlock() rs.id = id } // SetName sets the rule set name func (rs *RuleSet) SetName(name string) { rs.mutex.Lock() defer rs.mutex.Unlock() rs.name = name } // GetID returns the rule set ID func (rs *RuleSet) GetID() int32 { rs.mutex.RLock() defer rs.mutex.RUnlock() return rs.id } // GetName returns the rule set name func (rs *RuleSet) GetName() string { rs.mutex.RLock() defer rs.mutex.RUnlock() return rs.name } // AddRule adds a rule to the rule set func (rs *RuleSet) AddRule(rule *Rule) { if rule == nil { return } rs.mutex.Lock() defer rs.mutex.Unlock() category := rule.GetCategory() ruleType := rule.GetType() // Initialize category map if it doesn't exist if rs.rules[category] == nil { rs.rules[category] = make(map[RuleType]*Rule) } // If rule already exists, just update the value if existingRule, exists := rs.rules[category][ruleType]; exists { existingRule.SetValue(rule.GetValue()) } else { // Add new rule rs.rules[category][ruleType] = rule } } // GetRule retrieves a rule by category and type func (rs *RuleSet) GetRule(category RuleCategory, ruleType RuleType) *Rule { rs.mutex.RLock() defer rs.mutex.RUnlock() if categoryMap, exists := rs.rules[category]; exists { if rule, exists := categoryMap[ruleType]; exists { return rule } } return nil } // GetRuleByName retrieves a rule by category and type names func (rs *RuleSet) GetRuleByName(categoryName string, typeName string) *Rule { combined := fmt.Sprintf("%s:%s", categoryName, typeName) rs.mutex.RLock() defer rs.mutex.RUnlock() // Search through all rules for matching combined string for _, categoryMap := range rs.rules { for _, rule := range categoryMap { if rule.GetCombined() == combined { return rule } } } return nil } // CopyRulesInto copies rules from another rule set into this one func (rs *RuleSet) CopyRulesInto(source *RuleSet) { if source == nil { return } rs.ClearRules() rs.mutex.Lock() defer rs.mutex.Unlock() source.mutex.RLock() defer source.mutex.RUnlock() // Deep copy all rules from source for category, typeMap := range source.rules { rs.rules[category] = make(map[RuleType]*Rule) for ruleType, rule := range typeMap { rs.rules[category][ruleType] = NewRuleFromRule(rule) } } } // ClearRules removes all rules from the rule set func (rs *RuleSet) ClearRules() { rs.mutex.Lock() defer rs.mutex.Unlock() rs.rules = make(map[RuleCategory]map[RuleType]*Rule) } // GetRules returns the rules map (for iteration - use carefully) func (rs *RuleSet) GetRules() map[RuleCategory]map[RuleType]*Rule { rs.mutex.RLock() defer rs.mutex.RUnlock() // Return a deep copy to prevent external modification rulesCopy := make(map[RuleCategory]map[RuleType]*Rule) for category, typeMap := range rs.rules { rulesCopy[category] = make(map[RuleType]*Rule) for ruleType, rule := range typeMap { rulesCopy[category][ruleType] = NewRuleFromRule(rule) } } return rulesCopy } // Size returns the total number of rules in the rule set func (rs *RuleSet) Size() int { rs.mutex.RLock() defer rs.mutex.RUnlock() count := 0 for _, typeMap := range rs.rules { count += len(typeMap) } return count } // GetRulesByCategory returns all rules in a specific category func (rs *RuleSet) GetRulesByCategory(category RuleCategory) map[RuleType]*Rule { rs.mutex.RLock() defer rs.mutex.RUnlock() if categoryMap, exists := rs.rules[category]; exists { // Return a copy to prevent external modification rulesCopy := make(map[RuleType]*Rule) for ruleType, rule := range categoryMap { rulesCopy[ruleType] = NewRuleFromRule(rule) } return rulesCopy } return make(map[RuleType]*Rule) } // HasRule checks if a rule exists in the rule set func (rs *RuleSet) HasRule(category RuleCategory, ruleType RuleType) bool { rs.mutex.RLock() defer rs.mutex.RUnlock() if categoryMap, exists := rs.rules[category]; exists { _, exists := categoryMap[ruleType] return exists } return false } // String returns a string representation of the rule set func (rs *RuleSet) String() string { rs.mutex.RLock() defer rs.mutex.RUnlock() return fmt.Sprintf("RuleSet{ID: %d, Name: %s, Rules: %d}", rs.id, rs.name, rs.Size()) } // RuleManagerStats tracks rule system usage and performance metrics type RuleManagerStats struct { TotalRuleSets int32 // Total rule sets loaded TotalRules int32 // Total rules across all sets GlobalRuleSetID int32 // Current global rule set ID ZoneRuleSets int32 // Number of zone-specific rule sets RuleGetOperations int64 // Number of rule get operations RuleSetOperations int64 // Number of rule set operations DatabaseOperations int64 // Number of database operations mutex sync.RWMutex } // IncrementRuleGetOperations increments the rule get counter func (rms *RuleManagerStats) IncrementRuleGetOperations() { rms.mutex.Lock() defer rms.mutex.Unlock() rms.RuleGetOperations++ } // IncrementRuleSetOperations increments the rule set counter func (rms *RuleManagerStats) IncrementRuleSetOperations() { rms.mutex.Lock() defer rms.mutex.Unlock() rms.RuleSetOperations++ } // IncrementDatabaseOperations increments the database operations counter func (rms *RuleManagerStats) IncrementDatabaseOperations() { rms.mutex.Lock() defer rms.mutex.Unlock() rms.DatabaseOperations++ } // GetSnapshot returns a snapshot of the current statistics func (rms *RuleManagerStats) GetSnapshot() RuleManagerStats { rms.mutex.RLock() defer rms.mutex.RUnlock() return RuleManagerStats{ TotalRuleSets: rms.TotalRuleSets, TotalRules: rms.TotalRules, GlobalRuleSetID: rms.GlobalRuleSetID, ZoneRuleSets: rms.ZoneRuleSets, RuleGetOperations: rms.RuleGetOperations, RuleSetOperations: rms.RuleSetOperations, DatabaseOperations: rms.DatabaseOperations, } } // Reset resets all statistics counters func (rms *RuleManagerStats) Reset() { rms.mutex.Lock() defer rms.mutex.Unlock() rms.TotalRuleSets = 0 rms.TotalRules = 0 rms.GlobalRuleSetID = 0 rms.ZoneRuleSets = 0 rms.RuleGetOperations = 0 rms.RuleSetOperations = 0 rms.DatabaseOperations = 0 }