package ground_spawn import ( "fmt" "math/rand" "testing" "eq2emu/internal/database" ) // Mock implementations for benchmarking // mockPlayer implements Player interface for benchmarks type mockPlayer struct { level int16 location int32 name string } func (p *mockPlayer) GetLevel() int16 { return p.level } func (p *mockPlayer) GetLocation() int32 { return p.location } func (p *mockPlayer) GetName() string { return p.name } // mockSkill implements Skill interface for benchmarks type mockSkill struct { current int16 max int16 } func (s *mockSkill) GetCurrentValue() int16 { return s.current } func (s *mockSkill) GetMaxValue() int16 { return s.max } // createTestGroundSpawn creates a ground spawn for benchmarking func createTestGroundSpawn(b *testing.B, id int32) *GroundSpawn { b.Helper() db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } gs := New(db) gs.GroundSpawnID = id gs.Name = fmt.Sprintf("Benchmark Node %d", id) gs.CollectionSkill = "Mining" gs.NumberHarvests = 5 gs.AttemptsPerHarvest = 1 gs.CurrentHarvests = 5 gs.X, gs.Y, gs.Z = float32(rand.Intn(1000)), float32(rand.Intn(1000)), float32(rand.Intn(1000)) gs.ZoneID = int32(rand.Intn(10) + 1) gs.GridID = int32(rand.Intn(100)) // Add mock harvest entries for realistic benchmarking gs.HarvestEntries = []*HarvestEntry{ { GroundSpawnID: id, MinSkillLevel: 10, MinAdventureLevel: 1, BonusTable: false, Harvest1: 80.0, Harvest3: 20.0, Harvest5: 10.0, HarvestImbue: 5.0, HarvestRare: 2.0, Harvest10: 1.0, }, { GroundSpawnID: id, MinSkillLevel: 50, MinAdventureLevel: 10, BonusTable: true, Harvest1: 90.0, Harvest3: 30.0, Harvest5: 15.0, HarvestImbue: 8.0, HarvestRare: 5.0, Harvest10: 2.0, }, } // Add mock harvest items gs.HarvestItems = []*HarvestEntryItem{ {GroundSpawnID: id, ItemID: 1001, IsRare: ItemRarityNormal, GridID: 0, Quantity: 1}, {GroundSpawnID: id, ItemID: 1002, IsRare: ItemRarityNormal, GridID: 0, Quantity: 1}, {GroundSpawnID: id, ItemID: 1003, IsRare: ItemRarityRare, GridID: 0, Quantity: 1}, {GroundSpawnID: id, ItemID: 1004, IsRare: ItemRarityImbue, GridID: 0, Quantity: 1}, } return gs } // BenchmarkGroundSpawnCreation measures ground spawn creation performance func BenchmarkGroundSpawnCreation(b *testing.B) { db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } defer db.Close() b.ResetTimer() b.Run("Sequential", func(b *testing.B) { for i := 0; i < b.N; i++ { gs := New(db) gs.GroundSpawnID = int32(i) gs.Name = fmt.Sprintf("Node %d", i) _ = gs } }) b.Run("Parallel", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { id := int32(0) for pb.Next() { gs := New(db) gs.GroundSpawnID = id gs.Name = fmt.Sprintf("Node %d", id) id++ _ = gs } }) }) } // BenchmarkGroundSpawnState measures state operations func BenchmarkGroundSpawnState(b *testing.B) { gs := createTestGroundSpawn(b, 1001) b.Run("IsAvailable", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = gs.IsAvailable() } }) }) b.Run("IsDepleted", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = gs.IsDepleted() } }) }) b.Run("GetHarvestMessageName", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = gs.GetHarvestMessageName(true, false) } }) }) b.Run("GetHarvestSpellType", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = gs.GetHarvestSpellType() } }) }) } // BenchmarkHarvestAlgorithm measures the core harvest processing performance func BenchmarkHarvestAlgorithm(b *testing.B) { gs := createTestGroundSpawn(b, 1001) player := &mockPlayer{level: 50, location: 1, name: "BenchmarkPlayer"} skill := &mockSkill{current: 75, max: 100} b.Run("ProcessHarvest", func(b *testing.B) { for i := 0; i < b.N; i++ { // Reset harvest count for consistent benchmarking gs.CurrentHarvests = gs.NumberHarvests _, err := gs.ProcessHarvest(player, skill, 75) if err != nil { b.Fatalf("Harvest failed: %v", err) } } }) b.Run("FilterHarvestTables", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = gs.filterHarvestTables(player, 75) } }) b.Run("SelectHarvestTable", func(b *testing.B) { tables := gs.filterHarvestTables(player, 75) for i := 0; i < b.N; i++ { _ = gs.selectHarvestTable(tables, 75) } }) b.Run("DetermineHarvestType", func(b *testing.B) { tables := gs.filterHarvestTables(player, 75) table := gs.selectHarvestTable(tables, 75) for i := 0; i < b.N; i++ { _ = gs.determineHarvestType(table, false) } }) b.Run("AwardHarvestItems", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = gs.awardHarvestItems(HarvestType3Items, player) } }) } // BenchmarkMasterListOperations measures master list performance func BenchmarkMasterListOperations(b *testing.B) { ml := NewMasterList() // Pre-populate with ground spawns for realistic testing const numSpawns = 1000 spawns := make([]*GroundSpawn, numSpawns) b.StopTimer() for i := 0; i < numSpawns; i++ { spawns[i] = createTestGroundSpawn(b, int32(i+1)) spawns[i].ZoneID = int32(i%10 + 1) // Distribute across 10 zones spawns[i].CollectionSkill = []string{"Mining", "Gathering", "Fishing", "Trapping"}[i%4] ml.AddGroundSpawn(spawns[i]) } b.StartTimer() b.Run("GetGroundSpawn", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { id := int32((rand.Intn(numSpawns) + 1)) _ = ml.GetGroundSpawn(id) } }) }) b.Run("AddGroundSpawn", func(b *testing.B) { startID := int32(numSpawns + 1) b.ResetTimer() for i := 0; i < b.N; i++ { gs := createTestGroundSpawn(b, startID+int32(i)) ml.AddGroundSpawn(gs) } }) b.Run("GetByZone", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { zoneID := int32(rand.Intn(10) + 1) _ = ml.GetByZone(zoneID) } }) }) b.Run("GetBySkill", func(b *testing.B) { skills := []string{"Mining", "Gathering", "Fishing", "Trapping"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { skill := skills[rand.Intn(len(skills))] _ = ml.GetBySkill(skill) } }) }) b.Run("GetAvailableSpawns", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = ml.GetAvailableSpawns() } }) b.Run("GetDepletedSpawns", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = ml.GetDepletedSpawns() } }) b.Run("GetStatistics", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = ml.GetStatistics() } }) } // BenchmarkConcurrentHarvesting measures concurrent harvest performance func BenchmarkConcurrentHarvesting(b *testing.B) { const numSpawns = 100 spawns := make([]*GroundSpawn, numSpawns) for i := 0; i < numSpawns; i++ { spawns[i] = createTestGroundSpawn(b, int32(i+1)) } player := &mockPlayer{level: 50, location: 1, name: "BenchmarkPlayer"} skill := &mockSkill{current: 75, max: 100} b.Run("ConcurrentHarvesting", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { spawnIdx := rand.Intn(numSpawns) gs := spawns[spawnIdx] // Reset if depleted if gs.IsDepleted() { gs.Respawn() } _, _ = gs.ProcessHarvest(player, skill, 75) } }) }) } // BenchmarkMemoryAllocation measures memory allocation patterns func BenchmarkMemoryAllocation(b *testing.B) { db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } defer db.Close() b.Run("GroundSpawnAllocation", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { gs := New(db) gs.GroundSpawnID = int32(i) gs.HarvestEntries = make([]*HarvestEntry, 2) gs.HarvestItems = make([]*HarvestEntryItem, 4) _ = gs } }) b.Run("MasterListAllocation", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { ml := NewMasterList() _ = ml } }) b.Run("HarvestResultAllocation", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := &HarvestResult{ Success: true, HarvestType: HarvestType3Items, ItemsAwarded: make([]*HarvestedItem, 3), MessageText: "You harvested 3 items", SkillGained: false, } _ = result } }) } // BenchmarkRespawnOperations measures respawn performance func BenchmarkRespawnOperations(b *testing.B) { gs := createTestGroundSpawn(b, 1001) b.Run("Respawn", func(b *testing.B) { for i := 0; i < b.N; i++ { gs.CurrentHarvests = 0 // Deplete gs.Respawn() } }) b.Run("RespawnWithRandomHeading", func(b *testing.B) { gs.RandomizeHeading = true for i := 0; i < b.N; i++ { gs.CurrentHarvests = 0 // Deplete gs.Respawn() } }) } // BenchmarkStringOperations measures string processing performance func BenchmarkStringOperations(b *testing.B) { skills := []string{"Mining", "Gathering", "Collecting", "Fishing", "Trapping", "Foresting", "Unknown"} b.Run("HarvestMessageGeneration", func(b *testing.B) { gs := createTestGroundSpawn(b, 1001) b.RunParallel(func(pb *testing.PB) { for pb.Next() { skill := skills[rand.Intn(len(skills))] gs.CollectionSkill = skill _ = gs.GetHarvestMessageName(rand.Intn(2) == 1, rand.Intn(2) == 1) } }) }) b.Run("SpellTypeGeneration", func(b *testing.B) { gs := createTestGroundSpawn(b, 1001) b.RunParallel(func(pb *testing.PB) { for pb.Next() { skill := skills[rand.Intn(len(skills))] gs.CollectionSkill = skill _ = gs.GetHarvestSpellType() } }) }) } // BenchmarkComparisonWithOldSystem provides comparison benchmarks // These would help measure the performance improvement from modernization func BenchmarkComparisonWithOldSystem(b *testing.B) { ml := NewMasterList() const numSpawns = 1000 // Setup b.StopTimer() for i := 0; i < numSpawns; i++ { gs := createTestGroundSpawn(b, int32(i+1)) ml.AddGroundSpawn(gs) } b.StartTimer() b.Run("ModernizedLookup", func(b *testing.B) { // Modern generic-based lookup b.RunParallel(func(pb *testing.PB) { for pb.Next() { id := int32(rand.Intn(numSpawns) + 1) _ = ml.GetGroundSpawn(id) } }) }) b.Run("ModernizedFiltering", func(b *testing.B) { // Modern filter-based operations for i := 0; i < b.N; i++ { _ = ml.GetAvailableSpawns() } }) }