modernize, improve entities. First pass
This commit is contained in:
parent
6df4b00201
commit
7ce87100e6
@ -1,167 +0,0 @@
|
|||||||
# Entity Package
|
|
||||||
|
|
||||||
The Entity package provides the core combat and magic systems for EverQuest II server emulation. It extends the base Spawn system with combat capabilities, spell effects, and character statistics management.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The Entity system is built on three main components:
|
|
||||||
|
|
||||||
1. **InfoStruct** - Comprehensive character statistics and information
|
|
||||||
2. **SpellEffectManager** - Manages all spell effects, buffs, debuffs, and bonuses
|
|
||||||
3. **Entity** - Combat-capable spawn with spell casting and pet management
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
Spawn (base class)
|
|
||||||
└── Entity (combat-capable)
|
|
||||||
├── Player (player characters)
|
|
||||||
└── NPC (non-player characters)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Components
|
|
||||||
|
|
||||||
### InfoStruct
|
|
||||||
|
|
||||||
Contains all character statistics including:
|
|
||||||
- Primary attributes (STR, STA, AGI, WIS, INT)
|
|
||||||
- Combat stats (attack, mitigation, avoidance)
|
|
||||||
- Resistances (heat, cold, magic, mental, divine, disease, poison)
|
|
||||||
- Experience points and currency
|
|
||||||
- Equipment and weapon information
|
|
||||||
- Group and encounter settings
|
|
||||||
|
|
||||||
**Thread Safety**: All access methods use RWMutex for safe concurrent access.
|
|
||||||
|
|
||||||
### SpellEffectManager
|
|
||||||
|
|
||||||
Manages four types of spell effects:
|
|
||||||
|
|
||||||
1. **Maintained Effects** - Buffs that consume concentration
|
|
||||||
2. **Spell Effects** - Temporary buffs/debuffs with durations
|
|
||||||
3. **Detrimental Effects** - Debuffs and harmful effects
|
|
||||||
4. **Bonus Values** - Stat modifications from various sources
|
|
||||||
|
|
||||||
**Key Features**:
|
|
||||||
- Automatic expiration handling
|
|
||||||
- Control effect tracking (stun, root, mez, etc.)
|
|
||||||
- Bonus calculations with class/race/faction requirements
|
|
||||||
- Thread-safe effect management
|
|
||||||
|
|
||||||
### Entity
|
|
||||||
|
|
||||||
Combat-capable spawn that extends base Spawn functionality:
|
|
||||||
|
|
||||||
**Combat Systems**:
|
|
||||||
- Health/Power/Savagery management
|
|
||||||
- Combat state tracking (in combat, casting)
|
|
||||||
- Damage resistance calculations
|
|
||||||
- Speed and movement modifiers
|
|
||||||
|
|
||||||
**Magic Systems**:
|
|
||||||
- Spell effect application and removal
|
|
||||||
- Concentration-based maintained spells
|
|
||||||
- Bonus stat calculations
|
|
||||||
- Control effect immunity
|
|
||||||
|
|
||||||
**Pet Systems**:
|
|
||||||
- Multiple pet types (summon, charm, deity, cosmetic)
|
|
||||||
- Pet ownership and dismissal
|
|
||||||
- Pet spell tracking
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Creating an Entity
|
|
||||||
|
|
||||||
```go
|
|
||||||
entity := NewEntity()
|
|
||||||
entity.GetInfoStruct().SetName("TestEntity")
|
|
||||||
entity.GetInfoStruct().SetLevel(50)
|
|
||||||
entity.GetInfoStruct().SetStr(100.0)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Managing Spell Effects
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Add a maintained spell (buff)
|
|
||||||
success := entity.AddMaintainedSpell("Heroic Strength", 12345, 300.0, 2)
|
|
||||||
|
|
||||||
// Add a temporary effect
|
|
||||||
entity.AddSpellEffect(54321, casterID, 60.0)
|
|
||||||
|
|
||||||
// Add a detrimental effect
|
|
||||||
entity.AddDetrimentalSpell(99999, attackerID, 30.0, 1)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stat Calculations
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get effective stats (base + bonuses)
|
|
||||||
str := entity.GetStr()
|
|
||||||
sta := entity.GetSta()
|
|
||||||
primary := entity.GetPrimaryStat()
|
|
||||||
|
|
||||||
// Recalculate all bonuses
|
|
||||||
entity.CalculateBonuses()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pet Management
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Set a summon pet
|
|
||||||
entity.SetPet(petEntity)
|
|
||||||
|
|
||||||
// Check pet status
|
|
||||||
if entity.GetPet() != nil && !entity.IsPetDismissing() {
|
|
||||||
// Pet is active
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Constants and Enums
|
|
||||||
|
|
||||||
### Pet Types
|
|
||||||
- `PetTypeSummon` - Summoned pets
|
|
||||||
- `PetTypeCharm` - Charmed creatures
|
|
||||||
- `PetTypeDeity` - Deity pets
|
|
||||||
- `PetTypeCosmetic` - Cosmetic pets
|
|
||||||
|
|
||||||
### Control Effects
|
|
||||||
- `ControlEffectStun` - Cannot move or act
|
|
||||||
- `ControlEffectRoot` - Cannot move
|
|
||||||
- `ControlEffectMez` - Mesmerized
|
|
||||||
- `ControlEffectDaze` - Dazed
|
|
||||||
- `ControlEffectFear` - Feared
|
|
||||||
- `ControlEffectSlow` - Movement slowed
|
|
||||||
- `ControlEffectSnare` - Movement impaired
|
|
||||||
- `ControlEffectCharm` - Mind controlled
|
|
||||||
|
|
||||||
## Thread Safety
|
|
||||||
|
|
||||||
All Entity operations are thread-safe using:
|
|
||||||
- `sync.RWMutex` for read/write operations
|
|
||||||
- `sync.atomic` for simple state flags
|
|
||||||
- Separate mutexes for different subsystems to minimize lock contention
|
|
||||||
|
|
||||||
## Integration with Spawn System
|
|
||||||
|
|
||||||
The Entity extends the base Spawn class and requires:
|
|
||||||
- `spawn.NewSpawn()` for initialization
|
|
||||||
- Access to Spawn position and basic methods
|
|
||||||
- Integration with zone update systems
|
|
||||||
|
|
||||||
## Future Extensions
|
|
||||||
|
|
||||||
Areas marked with TODO comments for future implementation:
|
|
||||||
- Complete item and equipment systems
|
|
||||||
- Combat calculation methods
|
|
||||||
- Threat and hate management
|
|
||||||
- Group combat mechanics
|
|
||||||
- Spell casting systems
|
|
||||||
- LUA script integration
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
- `entity.go` - Main Entity class implementation
|
|
||||||
- `info_struct.go` - Character statistics and information
|
|
||||||
- `spell_effects.go` - Spell effect management system
|
|
||||||
- `README.md` - This documentation file
|
|
@ -20,7 +20,7 @@ func BenchmarkEntityCreation(b *testing.B) {
|
|||||||
// BenchmarkEntityCombatState measures combat state operations
|
// BenchmarkEntityCombatState measures combat state operations
|
||||||
func BenchmarkEntityCombatState(b *testing.B) {
|
func BenchmarkEntityCombatState(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
b.Run("Sequential", func(b *testing.B) {
|
b.Run("Sequential", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
entity.SetInCombat(true)
|
entity.SetInCombat(true)
|
||||||
@ -28,7 +28,7 @@ func BenchmarkEntityCombatState(b *testing.B) {
|
|||||||
entity.SetInCombat(false)
|
entity.SetInCombat(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("Parallel", func(b *testing.B) {
|
b.Run("Parallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -43,7 +43,7 @@ func BenchmarkEntityCombatState(b *testing.B) {
|
|||||||
// BenchmarkEntityCastingState measures casting state operations
|
// BenchmarkEntityCastingState measures casting state operations
|
||||||
func BenchmarkEntityCastingState(b *testing.B) {
|
func BenchmarkEntityCastingState(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
b.Run("Sequential", func(b *testing.B) {
|
b.Run("Sequential", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
entity.SetCasting(true)
|
entity.SetCasting(true)
|
||||||
@ -51,7 +51,7 @@ func BenchmarkEntityCastingState(b *testing.B) {
|
|||||||
entity.SetCasting(false)
|
entity.SetCasting(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("Parallel", func(b *testing.B) {
|
b.Run("Parallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -67,14 +67,14 @@ func BenchmarkEntityCastingState(b *testing.B) {
|
|||||||
func BenchmarkEntityStatCalculations(b *testing.B) {
|
func BenchmarkEntityStatCalculations(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
info := entity.GetInfoStruct()
|
info := entity.GetInfoStruct()
|
||||||
|
|
||||||
// Set up some base stats
|
// Set up some base stats
|
||||||
info.SetStr(100.0)
|
info.SetStr(100.0)
|
||||||
info.SetSta(100.0)
|
info.SetSta(100.0)
|
||||||
info.SetAgi(100.0)
|
info.SetAgi(100.0)
|
||||||
info.SetWis(100.0)
|
info.SetWis(100.0)
|
||||||
info.SetIntel(100.0)
|
info.SetIntel(100.0)
|
||||||
|
|
||||||
b.Run("GetStats", func(b *testing.B) {
|
b.Run("GetStats", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = entity.GetStr()
|
_ = entity.GetStr()
|
||||||
@ -84,7 +84,7 @@ func BenchmarkEntityStatCalculations(b *testing.B) {
|
|||||||
_ = entity.GetIntel()
|
_ = entity.GetIntel()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("GetStatsParallel", func(b *testing.B) {
|
b.Run("GetStatsParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -96,13 +96,13 @@ func BenchmarkEntityStatCalculations(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("GetPrimaryStat", func(b *testing.B) {
|
b.Run("GetPrimaryStat", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = entity.GetPrimaryStat()
|
_ = entity.GetPrimaryStat()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("CalculateBonuses", func(b *testing.B) {
|
b.Run("CalculateBonuses", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
entity.CalculateBonuses()
|
entity.CalculateBonuses()
|
||||||
@ -113,7 +113,7 @@ func BenchmarkEntityStatCalculations(b *testing.B) {
|
|||||||
// BenchmarkEntitySpellEffects measures spell effect operations
|
// BenchmarkEntitySpellEffects measures spell effect operations
|
||||||
func BenchmarkEntitySpellEffects(b *testing.B) {
|
func BenchmarkEntitySpellEffects(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
b.Run("AddRemoveSpellEffect", func(b *testing.B) {
|
b.Run("AddRemoveSpellEffect", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
spellID := int32(i + 1000)
|
spellID := int32(i + 1000)
|
||||||
@ -121,7 +121,7 @@ func BenchmarkEntitySpellEffects(b *testing.B) {
|
|||||||
entity.RemoveSpellEffect(spellID)
|
entity.RemoveSpellEffect(spellID)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("AddRemoveSpellEffectParallel", func(b *testing.B) {
|
b.Run("AddRemoveSpellEffectParallel", func(b *testing.B) {
|
||||||
var counter int64
|
var counter int64
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
@ -140,7 +140,7 @@ func BenchmarkEntityMaintainedSpells(b *testing.B) {
|
|||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
info := entity.GetInfoStruct()
|
info := entity.GetInfoStruct()
|
||||||
info.SetMaxConcentration(1000) // Large pool for benchmarking
|
info.SetMaxConcentration(1000) // Large pool for benchmarking
|
||||||
|
|
||||||
b.Run("AddRemoveMaintainedSpell", func(b *testing.B) {
|
b.Run("AddRemoveMaintainedSpell", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
spellID := int32(i + 2000)
|
spellID := int32(i + 2000)
|
||||||
@ -149,7 +149,7 @@ func BenchmarkEntityMaintainedSpells(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("AddRemoveMaintainedSpellParallel", func(b *testing.B) {
|
b.Run("AddRemoveMaintainedSpellParallel", func(b *testing.B) {
|
||||||
var counter int64
|
var counter int64
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
@ -167,14 +167,14 @@ func BenchmarkEntityMaintainedSpells(b *testing.B) {
|
|||||||
// BenchmarkInfoStructBasicOps measures basic InfoStruct operations
|
// BenchmarkInfoStructBasicOps measures basic InfoStruct operations
|
||||||
func BenchmarkInfoStructBasicOps(b *testing.B) {
|
func BenchmarkInfoStructBasicOps(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
|
|
||||||
b.Run("SetGetName", func(b *testing.B) {
|
b.Run("SetGetName", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
info.SetName("BenchmarkCharacter")
|
info.SetName("BenchmarkCharacter")
|
||||||
_ = info.GetName()
|
_ = info.GetName()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SetGetNameParallel", func(b *testing.B) {
|
b.Run("SetGetNameParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -183,14 +183,14 @@ func BenchmarkInfoStructBasicOps(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SetGetLevel", func(b *testing.B) {
|
b.Run("SetGetLevel", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
info.SetLevel(int16(i % 100))
|
info.SetLevel(int16(i % 100))
|
||||||
_ = info.GetLevel()
|
_ = info.GetLevel()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SetGetStats", func(b *testing.B) {
|
b.Run("SetGetStats", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
val := float32(i % 1000)
|
val := float32(i % 1000)
|
||||||
@ -199,7 +199,7 @@ func BenchmarkInfoStructBasicOps(b *testing.B) {
|
|||||||
info.SetAgi(val)
|
info.SetAgi(val)
|
||||||
info.SetWis(val)
|
info.SetWis(val)
|
||||||
info.SetIntel(val)
|
info.SetIntel(val)
|
||||||
|
|
||||||
_ = info.GetStr()
|
_ = info.GetStr()
|
||||||
_ = info.GetSta()
|
_ = info.GetSta()
|
||||||
_ = info.GetAgi()
|
_ = info.GetAgi()
|
||||||
@ -213,7 +213,7 @@ func BenchmarkInfoStructBasicOps(b *testing.B) {
|
|||||||
func BenchmarkInfoStructConcentration(b *testing.B) {
|
func BenchmarkInfoStructConcentration(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
info.SetMaxConcentration(1000)
|
info.SetMaxConcentration(1000)
|
||||||
|
|
||||||
b.Run("AddRemoveConcentration", func(b *testing.B) {
|
b.Run("AddRemoveConcentration", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
amount := int16(i%10 + 1)
|
amount := int16(i%10 + 1)
|
||||||
@ -222,7 +222,7 @@ func BenchmarkInfoStructConcentration(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("AddRemoveConcentrationParallel", func(b *testing.B) {
|
b.Run("AddRemoveConcentrationParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -238,7 +238,7 @@ func BenchmarkInfoStructConcentration(b *testing.B) {
|
|||||||
// BenchmarkInfoStructCoins measures coin operations
|
// BenchmarkInfoStructCoins measures coin operations
|
||||||
func BenchmarkInfoStructCoins(b *testing.B) {
|
func BenchmarkInfoStructCoins(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
|
|
||||||
b.Run("AddRemoveCoins", func(b *testing.B) {
|
b.Run("AddRemoveCoins", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
amount := int32(i%10000 + 1)
|
amount := int32(i%10000 + 1)
|
||||||
@ -246,7 +246,7 @@ func BenchmarkInfoStructCoins(b *testing.B) {
|
|||||||
info.RemoveCoins(amount / 2)
|
info.RemoveCoins(amount / 2)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("AddRemoveCoinsParallel", func(b *testing.B) {
|
b.Run("AddRemoveCoinsParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -256,7 +256,7 @@ func BenchmarkInfoStructCoins(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("GetCoins", func(b *testing.B) {
|
b.Run("GetCoins", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = info.GetCoins()
|
_ = info.GetCoins()
|
||||||
@ -268,7 +268,7 @@ func BenchmarkInfoStructCoins(b *testing.B) {
|
|||||||
func BenchmarkInfoStructResistances(b *testing.B) {
|
func BenchmarkInfoStructResistances(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
|
resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
|
||||||
|
|
||||||
b.Run("SetGetResistances", func(b *testing.B) {
|
b.Run("SetGetResistances", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
resistType := resistTypes[i%len(resistTypes)]
|
resistType := resistTypes[i%len(resistTypes)]
|
||||||
@ -277,7 +277,7 @@ func BenchmarkInfoStructResistances(b *testing.B) {
|
|||||||
_ = info.GetResistance(resistType)
|
_ = info.GetResistance(resistType)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SetGetResistancesParallel", func(b *testing.B) {
|
b.Run("SetGetResistancesParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -293,7 +293,7 @@ func BenchmarkInfoStructResistances(b *testing.B) {
|
|||||||
// BenchmarkInfoStructClone measures clone operations
|
// BenchmarkInfoStructClone measures clone operations
|
||||||
func BenchmarkInfoStructClone(b *testing.B) {
|
func BenchmarkInfoStructClone(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
|
|
||||||
// Set up some state to clone
|
// Set up some state to clone
|
||||||
info.SetName("Original Character")
|
info.SetName("Original Character")
|
||||||
info.SetLevel(50)
|
info.SetLevel(50)
|
||||||
@ -304,9 +304,9 @@ func BenchmarkInfoStructClone(b *testing.B) {
|
|||||||
info.SetIntel(105.0)
|
info.SetIntel(105.0)
|
||||||
info.AddConcentration(5)
|
info.AddConcentration(5)
|
||||||
info.AddCoins(50000)
|
info.AddCoins(50000)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
clone := info.Clone()
|
clone := info.Clone()
|
||||||
_ = clone
|
_ = clone
|
||||||
@ -316,7 +316,7 @@ func BenchmarkInfoStructClone(b *testing.B) {
|
|||||||
// BenchmarkEntityPetManagement measures pet management operations
|
// BenchmarkEntityPetManagement measures pet management operations
|
||||||
func BenchmarkEntityPetManagement(b *testing.B) {
|
func BenchmarkEntityPetManagement(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
b.Run("SetGetPet", func(b *testing.B) {
|
b.Run("SetGetPet", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pet := NewEntity()
|
pet := NewEntity()
|
||||||
@ -325,7 +325,7 @@ func BenchmarkEntityPetManagement(b *testing.B) {
|
|||||||
entity.SetPet(nil)
|
entity.SetPet(nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("SetGetPetParallel", func(b *testing.B) {
|
b.Run("SetGetPetParallel", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -336,23 +336,23 @@ func BenchmarkEntityPetManagement(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("AllPetTypes", func(b *testing.B) {
|
b.Run("AllPetTypes", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
pet := NewEntity()
|
pet := NewEntity()
|
||||||
|
|
||||||
entity.SetPet(pet)
|
entity.SetPet(pet)
|
||||||
_ = entity.GetPet()
|
_ = entity.GetPet()
|
||||||
|
|
||||||
entity.SetCharmedPet(pet)
|
entity.SetCharmedPet(pet)
|
||||||
_ = entity.GetCharmedPet()
|
_ = entity.GetCharmedPet()
|
||||||
|
|
||||||
entity.SetDeityPet(pet)
|
entity.SetDeityPet(pet)
|
||||||
_ = entity.GetDeityPet()
|
_ = entity.GetDeityPet()
|
||||||
|
|
||||||
entity.SetCosmeticPet(pet)
|
entity.SetCosmeticPet(pet)
|
||||||
_ = entity.GetCosmeticPet()
|
_ = entity.GetCosmeticPet()
|
||||||
|
|
||||||
// Clear all pets
|
// Clear all pets
|
||||||
entity.SetPet(nil)
|
entity.SetPet(nil)
|
||||||
entity.SetCharmedPet(nil)
|
entity.SetCharmedPet(nil)
|
||||||
@ -366,7 +366,7 @@ func BenchmarkEntityPetManagement(b *testing.B) {
|
|||||||
func BenchmarkConcurrentWorkload(b *testing.B) {
|
func BenchmarkConcurrentWorkload(b *testing.B) {
|
||||||
numEntities := 100
|
numEntities := 100
|
||||||
entities := make([]*Entity, numEntities)
|
entities := make([]*Entity, numEntities)
|
||||||
|
|
||||||
// Create entities
|
// Create entities
|
||||||
for i := 0; i < numEntities; i++ {
|
for i := 0; i < numEntities; i++ {
|
||||||
entities[i] = NewEntity()
|
entities[i] = NewEntity()
|
||||||
@ -375,15 +375,15 @@ func BenchmarkConcurrentWorkload(b *testing.B) {
|
|||||||
info.SetName("Entity" + string(rune('A'+i%26)))
|
info.SetName("Entity" + string(rune('A'+i%26)))
|
||||||
info.SetLevel(int16(i%100 + 1))
|
info.SetLevel(int16(i%100 + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
b.Run("MixedOperations", func(b *testing.B) {
|
b.Run("MixedOperations", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
entityIdx := rand.Intn(numEntities)
|
entityIdx := rand.Intn(numEntities)
|
||||||
entity := entities[entityIdx]
|
entity := entities[entityIdx]
|
||||||
|
|
||||||
switch rand.Intn(10) {
|
switch rand.Intn(10) {
|
||||||
case 0, 1: // Combat state changes (20%)
|
case 0, 1: // Combat state changes (20%)
|
||||||
entity.SetInCombat(rand.Intn(2) == 1)
|
entity.SetInCombat(rand.Intn(2) == 1)
|
||||||
@ -430,7 +430,7 @@ func BenchmarkMemoryAllocation(b *testing.B) {
|
|||||||
_ = entity
|
_ = entity
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("InfoStructAllocation", func(b *testing.B) {
|
b.Run("InfoStructAllocation", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -438,15 +438,15 @@ func BenchmarkMemoryAllocation(b *testing.B) {
|
|||||||
_ = info
|
_ = info
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("CloneAllocation", func(b *testing.B) {
|
b.Run("CloneAllocation", func(b *testing.B) {
|
||||||
info := NewInfoStruct()
|
info := NewInfoStruct()
|
||||||
info.SetName("Test")
|
info.SetName("Test")
|
||||||
info.SetLevel(50)
|
info.SetLevel(50)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
clone := info.Clone()
|
clone := info.Clone()
|
||||||
_ = clone
|
_ = clone
|
||||||
@ -459,7 +459,7 @@ func BenchmarkContention(b *testing.B) {
|
|||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
info := entity.GetInfoStruct()
|
info := entity.GetInfoStruct()
|
||||||
info.SetMaxConcentration(10) // Low limit to create contention
|
info.SetMaxConcentration(10) // Low limit to create contention
|
||||||
|
|
||||||
b.Run("HighContentionConcentration", func(b *testing.B) {
|
b.Run("HighContentionConcentration", func(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
@ -471,7 +471,7 @@ func BenchmarkContention(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run("HighContentionSpellEffects", func(b *testing.B) {
|
b.Run("HighContentionSpellEffects", func(b *testing.B) {
|
||||||
var spellCounter int64
|
var spellCounter int64
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
@ -488,11 +488,11 @@ func BenchmarkContention(b *testing.B) {
|
|||||||
// BenchmarkScalability tests performance as load increases
|
// BenchmarkScalability tests performance as load increases
|
||||||
func BenchmarkScalability(b *testing.B) {
|
func BenchmarkScalability(b *testing.B) {
|
||||||
goroutineCounts := []int{1, 2, 4, 8, 16, 32, 64}
|
goroutineCounts := []int{1, 2, 4, 8, 16, 32, 64}
|
||||||
|
|
||||||
for _, numGoroutines := range goroutineCounts {
|
for _, numGoroutines := range goroutineCounts {
|
||||||
b.Run(fmt.Sprintf("Goroutines_%d", numGoroutines), func(b *testing.B) {
|
b.Run(fmt.Sprintf("Goroutines_%d", numGoroutines), func(b *testing.B) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
entity.SetInCombat(true)
|
entity.SetInCombat(true)
|
||||||
@ -503,4 +503,3 @@ func BenchmarkScalability(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
// Test 1: Concurrent combat and casting state changes
|
// Test 1: Concurrent combat and casting state changes
|
||||||
t.Run("CombatCastingStates", func(t *testing.T) {
|
t.Run("CombatCastingStates", func(t *testing.T) {
|
||||||
var combatOps, castingOps int64
|
var combatOps, castingOps int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
// Combat state operations
|
// Combat state operations
|
||||||
entity.SetInCombat(true)
|
entity.SetInCombat(true)
|
||||||
@ -33,7 +33,7 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
atomic.AddInt64(&combatOps, 1)
|
atomic.AddInt64(&combatOps, 1)
|
||||||
}
|
}
|
||||||
entity.SetInCombat(false)
|
entity.SetInCombat(false)
|
||||||
|
|
||||||
// Casting state operations
|
// Casting state operations
|
||||||
entity.SetCasting(true)
|
entity.SetCasting(true)
|
||||||
if entity.IsCasting() {
|
if entity.IsCasting() {
|
||||||
@ -44,26 +44,26 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Combat operations: %d, Casting operations: %d", combatOps, castingOps)
|
t.Logf("Combat operations: %d, Casting operations: %d", combatOps, castingOps)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 2: Concurrent spell effect operations
|
// Test 2: Concurrent spell effect operations
|
||||||
t.Run("SpellEffects", func(t *testing.T) {
|
t.Run("SpellEffects", func(t *testing.T) {
|
||||||
var addOps, removeOps int64
|
var addOps, removeOps int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
spellID := int32(goroutineID*1000 + j)
|
spellID := int32(goroutineID*1000 + j)
|
||||||
|
|
||||||
if entity.AddSpellEffect(spellID, int32(goroutineID), 30.0) {
|
if entity.AddSpellEffect(spellID, int32(goroutineID), 30.0) {
|
||||||
atomic.AddInt64(&addOps, 1)
|
atomic.AddInt64(&addOps, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if entity.RemoveSpellEffect(spellID) {
|
if entity.RemoveSpellEffect(spellID) {
|
||||||
atomic.AddInt64(&removeOps, 1)
|
atomic.AddInt64(&removeOps, 1)
|
||||||
}
|
}
|
||||||
@ -71,28 +71,28 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Spell effect adds: %d, removes: %d", addOps, removeOps)
|
t.Logf("Spell effect adds: %d, removes: %d", addOps, removeOps)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 3: Concurrent maintained spell operations with concentration management
|
// Test 3: Concurrent maintained spell operations with concentration management
|
||||||
t.Run("MaintainedSpells", func(t *testing.T) {
|
t.Run("MaintainedSpells", func(t *testing.T) {
|
||||||
var addOps, removeOps, concentrationFailures int64
|
var addOps, removeOps, concentrationFailures int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer ops due to concentration limits
|
for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer ops due to concentration limits
|
||||||
spellID := int32(goroutineID*100 + j + 10000)
|
spellID := int32(goroutineID*100 + j + 10000)
|
||||||
|
|
||||||
if entity.AddMaintainedSpell("Stress Test Spell", spellID, 60.0, 1) {
|
if entity.AddMaintainedSpell("Stress Test Spell", spellID, 60.0, 1) {
|
||||||
atomic.AddInt64(&addOps, 1)
|
atomic.AddInt64(&addOps, 1)
|
||||||
|
|
||||||
// Small delay to increase contention
|
// Small delay to increase contention
|
||||||
time.Sleep(time.Microsecond)
|
time.Sleep(time.Microsecond)
|
||||||
|
|
||||||
if entity.RemoveMaintainedSpell(spellID) {
|
if entity.RemoveMaintainedSpell(spellID) {
|
||||||
atomic.AddInt64(&removeOps, 1)
|
atomic.AddInt64(&removeOps, 1)
|
||||||
}
|
}
|
||||||
@ -103,10 +103,10 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Maintained spell adds: %d, removes: %d, concentration failures: %d",
|
t.Logf("Maintained spell adds: %d, removes: %d, concentration failures: %d",
|
||||||
addOps, removeOps, concentrationFailures)
|
addOps, removeOps, concentrationFailures)
|
||||||
|
|
||||||
// Verify concentration was properly managed
|
// Verify concentration was properly managed
|
||||||
currentConc := info.GetCurConcentration()
|
currentConc := info.GetCurConcentration()
|
||||||
if currentConc != 0 {
|
if currentConc != 0 {
|
||||||
@ -117,23 +117,23 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
// Test 4: Concurrent stat calculations with bonuses
|
// Test 4: Concurrent stat calculations with bonuses
|
||||||
t.Run("StatCalculations", func(t *testing.T) {
|
t.Run("StatCalculations", func(t *testing.T) {
|
||||||
var statReads int64
|
var statReads int64
|
||||||
|
|
||||||
// Set some base stats
|
// Set some base stats
|
||||||
info.SetStr(100.0)
|
info.SetStr(100.0)
|
||||||
info.SetSta(100.0)
|
info.SetSta(100.0)
|
||||||
info.SetAgi(100.0)
|
info.SetAgi(100.0)
|
||||||
info.SetWis(100.0)
|
info.SetWis(100.0)
|
||||||
info.SetIntel(100.0)
|
info.SetIntel(100.0)
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
// Add some stat bonuses
|
// Add some stat bonuses
|
||||||
entity.AddStatBonus(int32(goroutineID*1000+j), 1, float32(j%10))
|
entity.AddStatBonus(int32(goroutineID*1000+j), 1, float32(j%10))
|
||||||
|
|
||||||
// Read stats (these involve bonus calculations)
|
// Read stats (these involve bonus calculations)
|
||||||
_ = entity.GetStr()
|
_ = entity.GetStr()
|
||||||
_ = entity.GetSta()
|
_ = entity.GetSta()
|
||||||
@ -141,31 +141,31 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
_ = entity.GetWis()
|
_ = entity.GetWis()
|
||||||
_ = entity.GetIntel()
|
_ = entity.GetIntel()
|
||||||
_ = entity.GetPrimaryStat()
|
_ = entity.GetPrimaryStat()
|
||||||
|
|
||||||
atomic.AddInt64(&statReads, 6)
|
atomic.AddInt64(&statReads, 6)
|
||||||
|
|
||||||
// Trigger bonus recalculation
|
// Trigger bonus recalculation
|
||||||
entity.CalculateBonuses()
|
entity.CalculateBonuses()
|
||||||
}
|
}
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Stat reads: %d", statReads)
|
t.Logf("Stat reads: %d", statReads)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 5: Concurrent pet management
|
// Test 5: Concurrent pet management
|
||||||
t.Run("PetManagement", func(t *testing.T) {
|
t.Run("PetManagement", func(t *testing.T) {
|
||||||
var petOps int64
|
var petOps int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine/10; j++ {
|
for j := 0; j < operationsPerGoroutine/10; j++ {
|
||||||
pet := NewEntity()
|
pet := NewEntity()
|
||||||
|
|
||||||
// Test different pet types
|
// Test different pet types
|
||||||
switch j % 4 {
|
switch j % 4 {
|
||||||
case 0:
|
case 0:
|
||||||
@ -185,13 +185,13 @@ func TestEntityConcurrencyStress(t *testing.T) {
|
|||||||
_ = entity.GetCosmeticPet()
|
_ = entity.GetCosmeticPet()
|
||||||
entity.SetCosmeticPet(nil)
|
entity.SetCosmeticPet(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(&petOps, 1)
|
atomic.AddInt64(&petOps, 1)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Pet operations: %d", petOps)
|
t.Logf("Pet operations: %d", petOps)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -208,23 +208,23 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
// Test 1: Concurrent basic property access
|
// Test 1: Concurrent basic property access
|
||||||
t.Run("BasicProperties", func(t *testing.T) {
|
t.Run("BasicProperties", func(t *testing.T) {
|
||||||
var nameOps, levelOps, statOps int64
|
var nameOps, levelOps, statOps int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
// Name operations
|
// Name operations
|
||||||
info.SetName("TestChar" + string(rune('A'+goroutineID%26)))
|
info.SetName("TestChar" + string(rune('A'+goroutineID%26)))
|
||||||
_ = info.GetName()
|
_ = info.GetName()
|
||||||
atomic.AddInt64(&nameOps, 1)
|
atomic.AddInt64(&nameOps, 1)
|
||||||
|
|
||||||
// Level operations
|
// Level operations
|
||||||
info.SetLevel(int16(j % 100))
|
info.SetLevel(int16(j % 100))
|
||||||
_ = info.GetLevel()
|
_ = info.GetLevel()
|
||||||
atomic.AddInt64(&levelOps, 1)
|
atomic.AddInt64(&levelOps, 1)
|
||||||
|
|
||||||
// Stat operations
|
// Stat operations
|
||||||
info.SetStr(float32(j))
|
info.SetStr(float32(j))
|
||||||
_ = info.GetStr()
|
_ = info.GetStr()
|
||||||
@ -233,28 +233,28 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Name ops: %d, Level ops: %d, Stat ops: %d", nameOps, levelOps, statOps)
|
t.Logf("Name ops: %d, Level ops: %d, Stat ops: %d", nameOps, levelOps, statOps)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 2: Concurrent concentration management
|
// Test 2: Concurrent concentration management
|
||||||
t.Run("ConcentrationManagement", func(t *testing.T) {
|
t.Run("ConcentrationManagement", func(t *testing.T) {
|
||||||
var addSuccesses, addFailures, removes int64
|
var addSuccesses, addFailures, removes int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
amount := int16(j%5 + 1) // 1-5 concentration points
|
amount := int16(j%5 + 1) // 1-5 concentration points
|
||||||
|
|
||||||
if info.AddConcentration(amount) {
|
if info.AddConcentration(amount) {
|
||||||
atomic.AddInt64(&addSuccesses, 1)
|
atomic.AddInt64(&addSuccesses, 1)
|
||||||
|
|
||||||
// Small delay to increase contention
|
// Small delay to increase contention
|
||||||
time.Sleep(time.Microsecond)
|
time.Sleep(time.Microsecond)
|
||||||
|
|
||||||
info.RemoveConcentration(amount)
|
info.RemoveConcentration(amount)
|
||||||
atomic.AddInt64(&removes, 1)
|
atomic.AddInt64(&removes, 1)
|
||||||
} else {
|
} else {
|
||||||
@ -264,10 +264,10 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Concentration adds: %d, failures: %d, removes: %d",
|
t.Logf("Concentration adds: %d, failures: %d, removes: %d",
|
||||||
addSuccesses, addFailures, removes)
|
addSuccesses, addFailures, removes)
|
||||||
|
|
||||||
// Verify final state
|
// Verify final state
|
||||||
finalConc := info.GetCurConcentration()
|
finalConc := info.GetCurConcentration()
|
||||||
if finalConc < 0 || finalConc > info.GetMaxConcentration() {
|
if finalConc < 0 || finalConc > info.GetMaxConcentration() {
|
||||||
@ -278,19 +278,19 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
// Test 3: Concurrent coin operations
|
// Test 3: Concurrent coin operations
|
||||||
t.Run("CoinOperations", func(t *testing.T) {
|
t.Run("CoinOperations", func(t *testing.T) {
|
||||||
var addOps, removeSuccesses, removeFailures int64
|
var addOps, removeSuccesses, removeFailures int64
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
amount := int32((goroutineID*1000 + j) % 10000)
|
amount := int32((goroutineID*1000 + j) % 10000)
|
||||||
|
|
||||||
// Add coins
|
// Add coins
|
||||||
info.AddCoins(amount)
|
info.AddCoins(amount)
|
||||||
atomic.AddInt64(&addOps, 1)
|
atomic.AddInt64(&addOps, 1)
|
||||||
|
|
||||||
// Try to remove some coins
|
// Try to remove some coins
|
||||||
removeAmount := amount / 2
|
removeAmount := amount / 2
|
||||||
if info.RemoveCoins(removeAmount) {
|
if info.RemoveCoins(removeAmount) {
|
||||||
@ -298,17 +298,17 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
atomic.AddInt64(&removeFailures, 1)
|
atomic.AddInt64(&removeFailures, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read total coins
|
// Read total coins
|
||||||
_ = info.GetCoins()
|
_ = info.GetCoins()
|
||||||
}
|
}
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Coin adds: %d, remove successes: %d, failures: %d",
|
t.Logf("Coin adds: %d, remove successes: %d, failures: %d",
|
||||||
addOps, removeSuccesses, removeFailures)
|
addOps, removeSuccesses, removeFailures)
|
||||||
|
|
||||||
// Verify coins are non-negative
|
// Verify coins are non-negative
|
||||||
finalCoins := info.GetCoins()
|
finalCoins := info.GetCoins()
|
||||||
if finalCoins < 0 {
|
if finalCoins < 0 {
|
||||||
@ -320,16 +320,16 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
t.Run("ResistanceOperations", func(t *testing.T) {
|
t.Run("ResistanceOperations", func(t *testing.T) {
|
||||||
var resistOps int64
|
var resistOps int64
|
||||||
resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
|
resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(goroutineID int) {
|
go func(goroutineID int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine; j++ {
|
for j := 0; j < operationsPerGoroutine; j++ {
|
||||||
resistType := resistTypes[j%len(resistTypes)]
|
resistType := resistTypes[j%len(resistTypes)]
|
||||||
value := int16(j % 100)
|
value := int16(j % 100)
|
||||||
|
|
||||||
info.SetResistance(resistType, value)
|
info.SetResistance(resistType, value)
|
||||||
_ = info.GetResistance(resistType)
|
_ = info.GetResistance(resistType)
|
||||||
atomic.AddInt64(&resistOps, 1)
|
atomic.AddInt64(&resistOps, 1)
|
||||||
@ -337,25 +337,25 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Resistance operations: %d", resistOps)
|
t.Logf("Resistance operations: %d", resistOps)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 5: Concurrent clone operations
|
// Test 5: Concurrent clone operations
|
||||||
t.Run("CloneOperations", func(t *testing.T) {
|
t.Run("CloneOperations", func(t *testing.T) {
|
||||||
var cloneOps int64
|
var cloneOps int64
|
||||||
|
|
||||||
// Set some initial state
|
// Set some initial state
|
||||||
info.SetName("Original")
|
info.SetName("Original")
|
||||||
info.SetLevel(50)
|
info.SetLevel(50)
|
||||||
info.SetStr(100.0)
|
info.SetStr(100.0)
|
||||||
info.AddConcentration(5)
|
info.AddConcentration(5)
|
||||||
|
|
||||||
wg.Add(numGoroutines)
|
wg.Add(numGoroutines)
|
||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer clones as they're expensive
|
for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer clones as they're expensive
|
||||||
clone := info.Clone()
|
clone := info.Clone()
|
||||||
if clone != nil {
|
if clone != nil {
|
||||||
@ -370,7 +370,7 @@ func TestInfoStructConcurrencyStress(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
t.Logf("Clone operations: %d", cloneOps)
|
t.Logf("Clone operations: %d", cloneOps)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -393,24 +393,24 @@ func TestRaceConditionDetection(t *testing.T) {
|
|||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(id int) {
|
go func(id int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for j := 0; j < 50; j++ {
|
for j := 0; j < 50; j++ {
|
||||||
// Mix of read and write operations that could race
|
// Mix of read and write operations that could race
|
||||||
entity.SetInCombat(id%2 == 0)
|
entity.SetInCombat(id%2 == 0)
|
||||||
isInCombat := entity.IsInCombat()
|
isInCombat := entity.IsInCombat()
|
||||||
|
|
||||||
entity.SetCasting(j%2 == 0)
|
entity.SetCasting(j%2 == 0)
|
||||||
isCasting := entity.IsCasting()
|
isCasting := entity.IsCasting()
|
||||||
|
|
||||||
// Stats with bonus calculations
|
// Stats with bonus calculations
|
||||||
info.SetStr(float32(id + j))
|
info.SetStr(float32(id + j))
|
||||||
str := entity.GetStr()
|
str := entity.GetStr()
|
||||||
|
|
||||||
// Concentration with potential for contention
|
// Concentration with potential for contention
|
||||||
if info.AddConcentration(1) {
|
if info.AddConcentration(1) {
|
||||||
info.RemoveConcentration(1)
|
info.RemoveConcentration(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the values to prevent optimization
|
// Use the values to prevent optimization
|
||||||
_ = isInCombat
|
_ = isInCombat
|
||||||
_ = isCasting
|
_ = isCasting
|
||||||
@ -425,21 +425,21 @@ func TestRaceConditionDetection(t *testing.T) {
|
|||||||
func TestConcurrencyCleanup(t *testing.T) {
|
func TestConcurrencyCleanup(t *testing.T) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
info := entity.GetInfoStruct()
|
info := entity.GetInfoStruct()
|
||||||
|
|
||||||
// Verify entity is in clean state after stress tests
|
// Verify entity is in clean state after stress tests
|
||||||
if entity.IsInCombat() {
|
if entity.IsInCombat() {
|
||||||
t.Error("Entity should not be in combat after tests")
|
t.Error("Entity should not be in combat after tests")
|
||||||
}
|
}
|
||||||
|
|
||||||
if entity.IsCasting() {
|
if entity.IsCasting() {
|
||||||
t.Error("Entity should not be casting after tests")
|
t.Error("Entity should not be casting after tests")
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.GetCurConcentration() < 0 {
|
if info.GetCurConcentration() < 0 {
|
||||||
t.Error("Concentration should not be negative")
|
t.Error("Concentration should not be negative")
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.GetCoins() < 0 {
|
if info.GetCoins() < 0 {
|
||||||
t.Error("Coins should not be negative")
|
t.Error("Coins should not be negative")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
internal/entity/doc.go
Normal file
41
internal/entity/doc.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Package entity provides the core combat and magic systems for EverQuest II server emulation.
|
||||||
|
// It extends the base Spawn system with combat capabilities, spell effects, and character statistics management.
|
||||||
|
//
|
||||||
|
// Basic Usage:
|
||||||
|
//
|
||||||
|
// entity := entity.NewEntity()
|
||||||
|
// entity.GetInfoStruct().SetName("TestEntity")
|
||||||
|
// entity.GetInfoStruct().SetLevel(50)
|
||||||
|
// entity.GetInfoStruct().SetStr(100.0)
|
||||||
|
//
|
||||||
|
// Managing Spell Effects:
|
||||||
|
//
|
||||||
|
// // Add a maintained spell (buff)
|
||||||
|
// success := entity.AddMaintainedSpell("Heroic Strength", 12345, 300.0, 2)
|
||||||
|
//
|
||||||
|
// // Add a temporary effect
|
||||||
|
// entity.AddSpellEffect(54321, casterID, 60.0)
|
||||||
|
//
|
||||||
|
// // Add a detrimental effect
|
||||||
|
// entity.AddDetrimentalSpell(99999, attackerID, 30.0, 1)
|
||||||
|
//
|
||||||
|
// Stat Calculations:
|
||||||
|
//
|
||||||
|
// // Get effective stats (base + bonuses)
|
||||||
|
// str := entity.GetStr()
|
||||||
|
// sta := entity.GetSta()
|
||||||
|
// primary := entity.GetPrimaryStat()
|
||||||
|
//
|
||||||
|
// // Recalculate all bonuses
|
||||||
|
// entity.CalculateBonuses()
|
||||||
|
//
|
||||||
|
// Pet Management:
|
||||||
|
//
|
||||||
|
// // Set a summon pet
|
||||||
|
// entity.SetPet(petEntity)
|
||||||
|
//
|
||||||
|
// // Check pet status
|
||||||
|
// if entity.GetPet() != nil && !entity.IsPetDismissing() {
|
||||||
|
// // Pet is active
|
||||||
|
// }
|
||||||
|
package entity
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"eq2emu/internal/common"
|
"eq2emu/internal/common"
|
||||||
"eq2emu/internal/spawn"
|
"eq2emu/internal/spawn"
|
||||||
|
"eq2emu/internal/spells"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combat and pet types
|
// Combat and pet types
|
||||||
@ -43,7 +44,7 @@ type Entity struct {
|
|||||||
regenPowerRate int16 // Power regeneration rate
|
regenPowerRate int16 // Power regeneration rate
|
||||||
|
|
||||||
// Spell and effect management
|
// Spell and effect management
|
||||||
spellEffectManager *SpellEffectManager
|
spellEffectManager *spells.SpellEffectManager
|
||||||
|
|
||||||
// Pet system
|
// Pet system
|
||||||
pet *Entity // Summon pet
|
pet *Entity // Summon pet
|
||||||
@ -106,7 +107,7 @@ func NewEntity() *Entity {
|
|||||||
lastHeading: -1,
|
lastHeading: -1,
|
||||||
regenHpRate: 0,
|
regenHpRate: 0,
|
||||||
regenPowerRate: 0,
|
regenPowerRate: 0,
|
||||||
spellEffectManager: NewSpellEffectManager(),
|
spellEffectManager: spells.NewSpellEffectManager(),
|
||||||
pet: nil,
|
pet: nil,
|
||||||
charmedPet: nil,
|
charmedPet: nil,
|
||||||
deityPet: nil,
|
deityPet: nil,
|
||||||
@ -357,7 +358,7 @@ func (e *Entity) AddMaintainedSpell(name string, spellID int32, duration float32
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
effect := NewMaintainedEffects(name, spellID, duration)
|
effect := spells.NewMaintainedEffects(name, spellID, duration)
|
||||||
effect.ConcUsed = concentration
|
effect.ConcUsed = concentration
|
||||||
|
|
||||||
e.maintainedMutex.Lock()
|
e.maintainedMutex.Lock()
|
||||||
@ -390,7 +391,7 @@ func (e *Entity) RemoveMaintainedSpell(spellID int32) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaintainedSpell retrieves a maintained spell effect
|
// GetMaintainedSpell retrieves a maintained spell effect
|
||||||
func (e *Entity) GetMaintainedSpell(spellID int32) *MaintainedEffects {
|
func (e *Entity) GetMaintainedSpell(spellID int32) *spells.MaintainedEffects {
|
||||||
e.maintainedMutex.RLock()
|
e.maintainedMutex.RLock()
|
||||||
defer e.maintainedMutex.RUnlock()
|
defer e.maintainedMutex.RUnlock()
|
||||||
|
|
||||||
@ -399,7 +400,7 @@ func (e *Entity) GetMaintainedSpell(spellID int32) *MaintainedEffects {
|
|||||||
|
|
||||||
// AddSpellEffect adds a temporary spell effect
|
// AddSpellEffect adds a temporary spell effect
|
||||||
func (e *Entity) AddSpellEffect(spellID int32, casterID int32, duration float32) bool {
|
func (e *Entity) AddSpellEffect(spellID int32, casterID int32, duration float32) bool {
|
||||||
effect := NewSpellEffects(spellID, casterID, duration)
|
effect := spells.NewSpellEffects(spellID, casterID, duration)
|
||||||
|
|
||||||
e.spellEffectMutex.Lock()
|
e.spellEffectMutex.Lock()
|
||||||
defer e.spellEffectMutex.Unlock()
|
defer e.spellEffectMutex.Unlock()
|
||||||
@ -417,7 +418,7 @@ func (e *Entity) RemoveSpellEffect(spellID int32) bool {
|
|||||||
|
|
||||||
// AddDetrimentalSpell adds a detrimental effect
|
// AddDetrimentalSpell adds a detrimental effect
|
||||||
func (e *Entity) AddDetrimentalSpell(spellID int32, casterID int32, duration float32, detType int8) {
|
func (e *Entity) AddDetrimentalSpell(spellID int32, casterID int32, duration float32, detType int8) {
|
||||||
effect := NewDetrimentalEffects(spellID, casterID, duration)
|
effect := spells.NewDetrimentalEffects(spellID, casterID, duration)
|
||||||
effect.DetType = detType
|
effect.DetType = detType
|
||||||
|
|
||||||
e.detrimentalMutex.Lock()
|
e.detrimentalMutex.Lock()
|
||||||
@ -435,7 +436,7 @@ func (e *Entity) RemoveDetrimentalSpell(spellID int32, casterID int32) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDetrimentalEffect retrieves a detrimental effect
|
// GetDetrimentalEffect retrieves a detrimental effect
|
||||||
func (e *Entity) GetDetrimentalEffect(spellID int32, casterID int32) *DetrimentalEffects {
|
func (e *Entity) GetDetrimentalEffect(spellID int32, casterID int32) *spells.DetrimentalEffects {
|
||||||
e.detrimentalMutex.RLock()
|
e.detrimentalMutex.RLock()
|
||||||
defer e.detrimentalMutex.RUnlock()
|
defer e.detrimentalMutex.RUnlock()
|
||||||
|
|
||||||
@ -451,13 +452,13 @@ func (e *Entity) HasControlEffect(controlType int8) bool {
|
|||||||
|
|
||||||
// AddSkillBonus adds a skill-related bonus
|
// AddSkillBonus adds a skill-related bonus
|
||||||
func (e *Entity) AddSkillBonus(spellID int32, skillID int32, value float32) {
|
func (e *Entity) AddSkillBonus(spellID int32, skillID int32, value float32) {
|
||||||
bonus := NewBonusValues(spellID, int16(skillID+100), value) // Skill bonuses use type 100+
|
bonus := spells.NewBonusValues(spellID, int16(skillID+100), value) // Skill bonuses use type 100+
|
||||||
e.spellEffectManager.AddBonus(bonus)
|
e.spellEffectManager.AddBonus(bonus)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddStatBonus adds a stat bonus
|
// AddStatBonus adds a stat bonus
|
||||||
func (e *Entity) AddStatBonus(spellID int32, statType int16, value float32) {
|
func (e *Entity) AddStatBonus(spellID int32, statType int16, value float32) {
|
||||||
bonus := NewBonusValues(spellID, statType, value)
|
bonus := spells.NewBonusValues(spellID, statType, value)
|
||||||
e.spellEffectManager.AddBonus(bonus)
|
e.spellEffectManager.AddBonus(bonus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"eq2emu/internal/spells"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewEntity(t *testing.T) {
|
func TestNewEntity(t *testing.T) {
|
||||||
@ -68,7 +70,7 @@ func TestEntityIsEntity(t *testing.T) {
|
|||||||
|
|
||||||
func TestEntityInfoStruct(t *testing.T) {
|
func TestEntityInfoStruct(t *testing.T) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
stats := entity.GetInfoStruct()
|
stats := entity.GetInfoStruct()
|
||||||
if stats == nil {
|
if stats == nil {
|
||||||
t.Error("Expected InfoStruct to be initialized")
|
t.Error("Expected InfoStruct to be initialized")
|
||||||
@ -92,7 +94,7 @@ func TestEntityInfoStruct(t *testing.T) {
|
|||||||
|
|
||||||
func TestEntityClient(t *testing.T) {
|
func TestEntityClient(t *testing.T) {
|
||||||
entity := NewEntity()
|
entity := NewEntity()
|
||||||
|
|
||||||
// Base Entity should return nil for GetClient
|
// Base Entity should return nil for GetClient
|
||||||
client := entity.GetClient()
|
client := entity.GetClient()
|
||||||
if client != nil {
|
if client != nil {
|
||||||
@ -628,7 +630,7 @@ func TestEntityControlEffects(t *testing.T) {
|
|||||||
|
|
||||||
// Test has control effect - should work without panicking
|
// Test has control effect - should work without panicking
|
||||||
// The actual implementation depends on the spell effect manager
|
// The actual implementation depends on the spell effect manager
|
||||||
hasStun := entity.HasControlEffect(ControlEffectStun)
|
hasStun := entity.HasControlEffect(spells.ControlEffectStun)
|
||||||
_ = hasStun // We can't easily test the actual value without setting up effects
|
_ = hasStun // We can't easily test the actual value without setting up effects
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +653,7 @@ func TestEntityConstants(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test control effect constants (re-exported from spells package)
|
// Test control effect constants (re-exported from spells package)
|
||||||
if ControlEffectStun == 0 && ControlEffectRoot == 0 {
|
if spells.ControlEffectStun == 0 && spells.ControlEffectRoot == 0 {
|
||||||
t.Error("Control effect constants should be non-zero")
|
t.Error("Control effect constants should be non-zero")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,14 +374,14 @@ func TestInfoStructConcurrency(t *testing.T) {
|
|||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(index int) {
|
go func(index int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// Each goroutine sets unique values
|
// Each goroutine sets unique values
|
||||||
info.SetName("Character" + string(rune('A'+index)))
|
info.SetName("Character" + string(rune('A'+index)))
|
||||||
_ = info.GetName()
|
_ = info.GetName()
|
||||||
|
|
||||||
info.SetLevel(int16(10 + index))
|
info.SetLevel(int16(10 + index))
|
||||||
_ = info.GetLevel()
|
_ = info.GetLevel()
|
||||||
|
|
||||||
info.SetStr(float32(10.0 + float32(index)))
|
info.SetStr(float32(10.0 + float32(index)))
|
||||||
_ = info.GetStr()
|
_ = info.GetStr()
|
||||||
}(i)
|
}(i)
|
||||||
@ -393,7 +393,7 @@ func TestInfoStructConcurrency(t *testing.T) {
|
|||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// Try to add concentration
|
// Try to add concentration
|
||||||
if info.AddConcentration(1) {
|
if info.AddConcentration(1) {
|
||||||
// If successful, remove it after a short delay
|
// If successful, remove it after a short delay
|
||||||
@ -413,10 +413,10 @@ func TestInfoStructConcurrency(t *testing.T) {
|
|||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(amount int32) {
|
go func(amount int32) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
info.AddCoins(amount)
|
info.AddCoins(amount)
|
||||||
_ = info.GetCoins()
|
_ = info.GetCoins()
|
||||||
|
|
||||||
// Try to remove some coins
|
// Try to remove some coins
|
||||||
info.RemoveCoins(amount / 2)
|
info.RemoveCoins(amount / 2)
|
||||||
}(int32(100 + i))
|
}(int32(100 + i))
|
||||||
@ -428,10 +428,10 @@ func TestInfoStructConcurrency(t *testing.T) {
|
|||||||
for i := 0; i < numGoroutines; i++ {
|
for i := 0; i < numGoroutines; i++ {
|
||||||
go func(value int16) {
|
go func(value int16) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
info.SetResistance("heat", value)
|
info.SetResistance("heat", value)
|
||||||
_ = info.GetResistance("heat")
|
_ = info.GetResistance("heat")
|
||||||
|
|
||||||
info.SetResistance("cold", value+1)
|
info.SetResistance("cold", value+1)
|
||||||
_ = info.GetResistance("cold")
|
_ = info.GetResistance("cold")
|
||||||
}(int16(i))
|
}(int16(i))
|
||||||
@ -445,7 +445,7 @@ func TestInfoStructLargeValues(t *testing.T) {
|
|||||||
// Test with large coin amounts
|
// Test with large coin amounts
|
||||||
largeCoinAmount := int32(2000000000) // 2 billion copper
|
largeCoinAmount := int32(2000000000) // 2 billion copper
|
||||||
info.AddCoins(largeCoinAmount)
|
info.AddCoins(largeCoinAmount)
|
||||||
|
|
||||||
totalCoins := info.GetCoins()
|
totalCoins := info.GetCoins()
|
||||||
if totalCoins != largeCoinAmount {
|
if totalCoins != largeCoinAmount {
|
||||||
t.Errorf("Expected large coin amount %d, got %d", largeCoinAmount, totalCoins)
|
t.Errorf("Expected large coin amount %d, got %d", largeCoinAmount, totalCoins)
|
||||||
@ -464,11 +464,11 @@ func TestInfoStructLargeValues(t *testing.T) {
|
|||||||
// Test with maximum values
|
// Test with maximum values
|
||||||
info.SetMaxConcentration(32767) // Max int16
|
info.SetMaxConcentration(32767) // Max int16
|
||||||
info.SetLevel(32767)
|
info.SetLevel(32767)
|
||||||
|
|
||||||
if info.GetMaxConcentration() != 32767 {
|
if info.GetMaxConcentration() != 32767 {
|
||||||
t.Errorf("Expected max concentration 32767, got %d", info.GetMaxConcentration())
|
t.Errorf("Expected max concentration 32767, got %d", info.GetMaxConcentration())
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.GetLevel() != 32767 {
|
if info.GetLevel() != 32767 {
|
||||||
t.Errorf("Expected level 32767, got %d", info.GetLevel())
|
t.Errorf("Expected level 32767, got %d", info.GetLevel())
|
||||||
}
|
}
|
||||||
@ -511,4 +511,4 @@ func TestInfoStructEdgeCases(t *testing.T) {
|
|||||||
if info.GetName() != longName {
|
if info.GetName() != longName {
|
||||||
t.Error("Expected to handle very long names")
|
t.Error("Expected to handle very long names")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package entity
|
|
||||||
|
|
||||||
// DEPRECATED: This file now imports spell effect structures from the spells package.
|
|
||||||
// The spell effect management has been moved to internal/spells for better organization.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"eq2emu/internal/spells"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Re-export spell effect types for backward compatibility
|
|
||||||
// These will eventually be removed in favor of direct imports from spells package
|
|
||||||
|
|
||||||
type BonusValues = spells.BonusValues
|
|
||||||
type MaintainedEffects = spells.MaintainedEffects
|
|
||||||
type SpellEffects = spells.SpellEffects
|
|
||||||
type DetrimentalEffects = spells.DetrimentalEffects
|
|
||||||
type SpellEffectManager = spells.SpellEffectManager
|
|
||||||
|
|
||||||
// Re-export constructor functions
|
|
||||||
var NewBonusValues = spells.NewBonusValues
|
|
||||||
var NewMaintainedEffects = spells.NewMaintainedEffects
|
|
||||||
var NewSpellEffects = spells.NewSpellEffects
|
|
||||||
var NewDetrimentalEffects = spells.NewDetrimentalEffects
|
|
||||||
var NewSpellEffectManager = spells.NewSpellEffectManager
|
|
||||||
|
|
||||||
// Re-export constants
|
|
||||||
const (
|
|
||||||
ControlEffectStun = spells.ControlEffectStun
|
|
||||||
ControlEffectRoot = spells.ControlEffectRoot
|
|
||||||
ControlEffectMez = spells.ControlEffectMez
|
|
||||||
ControlEffectDaze = spells.ControlEffectDaze
|
|
||||||
ControlEffectFear = spells.ControlEffectFear
|
|
||||||
ControlEffectSlow = spells.ControlEffectSlow
|
|
||||||
ControlEffectSnare = spells.ControlEffectSnare
|
|
||||||
ControlEffectCharm = spells.ControlEffectCharm
|
|
||||||
ControlMaxEffects = spells.ControlMaxEffects
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user