520 lines
12 KiB
Go
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
|
|
}
|