520 lines
12 KiB
Go

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
}