package zone import ( "path/filepath" "sync" "testing" "time" "eq2emu/internal/spawn" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitex" ) // Mock implementations for testing // MockSpawn implements basic spawn functionality for testing type MockSpawn struct { id int32 x, y, z float32 heading float32 name string } func (ms *MockSpawn) GetID() int32 { return ms.id } func (ms *MockSpawn) GetPosition() (x, y, z, heading float32) { return ms.x, ms.y, ms.z, ms.heading } func (ms *MockSpawn) SetPosition(x, y, z, heading float32) { ms.x, ms.y, ms.z, ms.heading = x, y, z, heading } func (ms *MockSpawn) GetName() string { return ms.name } func (ms *MockSpawn) SetName(name string) { ms.name = name } func (ms *MockSpawn) GetX() float32 { return ms.x } func (ms *MockSpawn) GetY() float32 { return ms.y } func (ms *MockSpawn) GetZ() float32 { return ms.z } func (ms *MockSpawn) GetHeading() float32 { return ms.heading } func (ms *MockSpawn) SetX(x float32) { ms.x = x } func (ms *MockSpawn) SetY(y float32, updateClients bool) { ms.y = y } func (ms *MockSpawn) SetZ(z float32) { ms.z = z } func (ms *MockSpawn) SetHeadingFromFloat(heading float32) { ms.heading = heading } // TestDatabaseOperations tests database CRUD operations func TestDatabaseOperations(t *testing.T) { // Create temporary database conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer conn.Close() // Create test schema schema := ` CREATE TABLE IF NOT EXISTS zones ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, file TEXT, description TEXT, safe_x REAL DEFAULT 0, safe_y REAL DEFAULT 0, safe_z REAL DEFAULT 0, safe_heading REAL DEFAULT 0, underworld REAL DEFAULT -1000, min_level INTEGER DEFAULT 0, max_level INTEGER DEFAULT 0, min_status INTEGER DEFAULT 0, min_version INTEGER DEFAULT 0, instance_type INTEGER DEFAULT 0, max_players INTEGER DEFAULT 100, default_lockout_time INTEGER DEFAULT 18000, default_reenter_time INTEGER DEFAULT 3600, default_reset_time INTEGER DEFAULT 259200, group_zone_option INTEGER DEFAULT 0, expansion_flag INTEGER DEFAULT 0, holiday_flag INTEGER DEFAULT 0, can_bind INTEGER DEFAULT 1, can_gate INTEGER DEFAULT 1, can_evac INTEGER DEFAULT 1, city_zone INTEGER DEFAULT 0, always_loaded INTEGER DEFAULT 0, weather_allowed INTEGER DEFAULT 1 ); CREATE TABLE IF NOT EXISTS spawn_location_placement ( id INTEGER PRIMARY KEY AUTOINCREMENT, zone_id INTEGER, x REAL, y REAL, z REAL, heading REAL, pitch REAL DEFAULT 0, roll REAL DEFAULT 0, spawn_type INTEGER DEFAULT 0, respawn_time INTEGER DEFAULT 300, expire_time INTEGER DEFAULT 0, expire_offset INTEGER DEFAULT 0, conditions INTEGER DEFAULT 0, conditional_value INTEGER DEFAULT 0, spawn_percentage REAL DEFAULT 100.0 ); CREATE TABLE IF NOT EXISTS spawn_location_group ( group_id INTEGER, location_id INTEGER, zone_id INTEGER, PRIMARY KEY (group_id, location_id) ); -- Insert test data INSERT INTO zones (id, name, file, description, safe_x, safe_y, safe_z) VALUES (1, 'test_zone', 'test.zone', 'Test Zone Description', 10.0, 20.0, 30.0); INSERT INTO spawn_location_placement (id, zone_id, x, y, z, heading, spawn_percentage) VALUES (1, 1, 100.0, 200.0, 300.0, 45.0, 75.5); INSERT INTO spawn_location_group (group_id, location_id, zone_id) VALUES (1, 1, 1); ` if err := sqlitex.ExecuteScript(conn, schema, &sqlitex.ExecOptions{}); err != nil { t.Fatalf("Failed to create test schema: %v", err) } // Create database instance zdb := NewZoneDatabase(conn) if zdb == nil { t.Fatal("Expected non-nil zone database") } // Test LoadZoneData zoneData, err := zdb.LoadZoneData(1) if err != nil { t.Fatalf("Failed to load zone data: %v", err) } if zoneData.ZoneID != 1 { t.Errorf("Expected zone ID 1, got %d", zoneData.ZoneID) } if zoneData.Configuration == nil { t.Fatal("Expected non-nil zone configuration") } if zoneData.Configuration.Name != "test_zone" { t.Errorf("Expected zone name 'test_zone', got '%s'", zoneData.Configuration.Name) } if zoneData.Configuration.SafeX != 10.0 { t.Errorf("Expected safe X 10.0, got %.2f", zoneData.Configuration.SafeX) } // Test spawn locations if len(zoneData.SpawnLocations) != 1 { t.Errorf("Expected 1 spawn location, got %d", len(zoneData.SpawnLocations)) } location := zoneData.SpawnLocations[1] if location == nil { t.Fatal("Expected spawn location 1 to exist") } if location.X != 100.0 || location.Y != 200.0 || location.Z != 300.0 { t.Errorf("Expected location (100, 200, 300), got (%.2f, %.2f, %.2f)", location.X, location.Y, location.Z) } if location.SpawnPercentage != 75.5 { t.Errorf("Expected spawn percentage 75.5, got %.2f", location.SpawnPercentage) } // Test LoadSpawnLocation singleLocation, err := zdb.LoadSpawnLocation(1) if err != nil { t.Errorf("Failed to load spawn location: %v", err) } if singleLocation.ID != 1 { t.Errorf("Expected location ID 1, got %d", singleLocation.ID) } // Test SaveSpawnLocation (update) singleLocation.X = 150.0 if err := zdb.SaveSpawnLocation(singleLocation); err != nil { t.Errorf("Failed to save spawn location: %v", err) } // Verify update updatedLocation, err := zdb.LoadSpawnLocation(1) if err != nil { t.Errorf("Failed to load updated spawn location: %v", err) } if updatedLocation.X != 150.0 { t.Errorf("Expected updated X 150.0, got %.2f", updatedLocation.X) } // Test SaveSpawnLocation (insert new) newLocation := &SpawnLocation{ X: 400.0, Y: 500.0, Z: 600.0, Heading: 90.0, SpawnPercentage: 100.0, } if err := zdb.SaveSpawnLocation(newLocation); err != nil { t.Errorf("Failed to insert new spawn location: %v", err) } if newLocation.ID == 0 { t.Error("Expected new location to have non-zero ID") } // Test LoadSpawnGroups groups, err := zdb.LoadSpawnGroups(1) if err != nil { t.Errorf("Failed to load spawn groups: %v", err) } if len(groups) != 1 { t.Errorf("Expected 1 spawn group, got %d", len(groups)) } if len(groups[1]) != 1 || groups[1][0] != 1 { t.Errorf("Expected group 1 to contain location 1, got %v", groups[1]) } // Test SaveSpawnGroup newLocationIDs := []int32{1, 2} if err := zdb.SaveSpawnGroup(2, newLocationIDs); err != nil { t.Errorf("Failed to save spawn group: %v", err) } // Test DeleteSpawnLocation if err := zdb.DeleteSpawnLocation(newLocation.ID); err != nil { t.Errorf("Failed to delete spawn location: %v", err) } // Verify deletion _, err = zdb.LoadSpawnLocation(newLocation.ID) if err == nil { t.Error("Expected error loading deleted spawn location") } } // TestZoneServerLifecycle tests zone server creation, initialization, and shutdown func TestZoneServerLifecycle(t *testing.T) { // Create zone server zoneServer := NewZoneServer("test_zone_lifecycle") if zoneServer == nil { t.Fatal("Expected non-nil zone server") } // Test initial state if zoneServer.IsInitialized() { t.Error("Expected zone to not be initialized") } if zoneServer.IsShuttingDown() { t.Error("Expected zone to not be shutting down") } if zoneServer.GetZoneName() != "test_zone_lifecycle" { t.Errorf("Expected zone name 'test_zone_lifecycle', got '%s'", zoneServer.GetZoneName()) } // Test initialization config := &ZoneServerConfig{ ZoneName: "test_zone_lifecycle", ZoneID: 100, InstanceID: 0, MaxPlayers: 50, MinLevel: 1, MaxLevel: 100, LoadMaps: false, EnableWeather: false, EnablePathfinding: false, } err := zoneServer.Initialize(config) if err != nil { t.Fatalf("Failed to initialize zone server: %v", err) } if !zoneServer.IsInitialized() { t.Error("Expected zone to be initialized") } if zoneServer.GetZoneID() != 100 { t.Errorf("Expected zone ID 100, got %d", zoneServer.GetZoneID()) } if zoneServer.GetMaxPlayers() != 50 { t.Errorf("Expected max players 50, got %d", zoneServer.GetMaxPlayers()) } // Test shutdown zoneServer.Shutdown() // Give shutdown time to process time.Sleep(time.Millisecond * 100) if !zoneServer.IsShuttingDown() { t.Error("Expected zone to be shutting down") } } // TestZoneManagerOperations tests zone manager functionality func TestZoneManagerOperations(t *testing.T) { // Create test database conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer conn.Close() // Create minimal schema for testing schema := ` CREATE TABLE zones ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, file TEXT DEFAULT 'test.zone', description TEXT DEFAULT 'Test Zone', safe_x REAL DEFAULT 0, safe_y REAL DEFAULT 0, safe_z REAL DEFAULT 0, safe_heading REAL DEFAULT 0, underworld REAL DEFAULT -1000, min_level INTEGER DEFAULT 1, max_level INTEGER DEFAULT 100, min_status INTEGER DEFAULT 0, min_version INTEGER DEFAULT 0, instance_type INTEGER DEFAULT 0, max_players INTEGER DEFAULT 100, default_lockout_time INTEGER DEFAULT 18000, default_reenter_time INTEGER DEFAULT 3600, default_reset_time INTEGER DEFAULT 259200, group_zone_option INTEGER DEFAULT 0, expansion_flag INTEGER DEFAULT 0, holiday_flag INTEGER DEFAULT 0, can_bind INTEGER DEFAULT 1, can_gate INTEGER DEFAULT 1, can_evac INTEGER DEFAULT 1, city_zone INTEGER DEFAULT 0, always_loaded INTEGER DEFAULT 0, weather_allowed INTEGER DEFAULT 1 ); CREATE TABLE spawn_location_placement (id INTEGER PRIMARY KEY, zone_id INTEGER); INSERT INTO zones (id, name) VALUES (1, 'zone1'), (2, 'zone2'); ` if err := sqlitex.ExecuteScript(conn, schema, &sqlitex.ExecOptions{}); err != nil { t.Fatalf("Failed to create test schema: %v", err) } // Create zone manager config := &ZoneManagerConfig{ MaxZones: 5, MaxInstanceZones: 10, ProcessInterval: time.Millisecond * 100, CleanupInterval: time.Second * 1, EnableWeather: false, EnablePathfinding: false, EnableCombat: false, EnableSpellProcess: false, } zoneManager := NewZoneManager(config, conn) if zoneManager == nil { t.Fatal("Expected non-nil zone manager") } // Test initial state if zoneManager.GetZoneCount() != 0 { t.Errorf("Expected 0 zones initially, got %d", zoneManager.GetZoneCount()) } if zoneManager.GetInstanceCount() != 0 { t.Errorf("Expected 0 instances initially, got %d", zoneManager.GetInstanceCount()) } // Test zone loading (this will fail due to missing data but we can test the attempt) _, err = zoneManager.LoadZone(1) if err == nil { // If successful, test that it was loaded if zoneManager.GetZoneCount() != 1 { t.Errorf("Expected 1 zone after loading, got %d", zoneManager.GetZoneCount()) } // Test retrieval zone := zoneManager.GetZone(1) if zone == nil { t.Error("Expected to retrieve loaded zone") } zoneByName := zoneManager.GetZoneByName("zone1") if zoneByName == nil { t.Error("Expected to retrieve zone by name") } // Test statistics stats := zoneManager.GetStatistics() if stats == nil { t.Error("Expected non-nil statistics") } if stats.TotalZones != 1 { t.Errorf("Expected 1 zone in statistics, got %d", stats.TotalZones) } } // Test zone manager start/stop err = zoneManager.Start() if err != nil { t.Errorf("Failed to start zone manager: %v", err) } // Give it time to start time.Sleep(time.Millisecond * 50) err = zoneManager.Stop() if err != nil { t.Errorf("Failed to stop zone manager: %v", err) } } // TestPositionCalculations tests position and distance calculations func TestPositionCalculations(t *testing.T) { testCases := []struct { name string x1, y1, z1 float32 x2, y2, z2 float32 expected2D float32 expected3D float32 }{ {"Origin to (3,4,12)", 0, 0, 0, 3, 4, 12, 5.0, 13.0}, {"Same point", 10, 20, 30, 10, 20, 30, 0.0, 0.0}, {"Unit distance", 0, 0, 0, 1, 0, 0, 1.0, 1.0}, {"Negative coordinates", -5, -5, -5, 5, 5, 5, 14.142136, 17.320507}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Test 2D distance distance2D := Distance2D(tc.x1, tc.y1, tc.x2, tc.y2) if testAbs(distance2D-tc.expected2D) > 0.001 { t.Errorf("Expected 2D distance %.3f, got %.3f", tc.expected2D, distance2D) } // Test 3D distance distance3D := Distance3D(tc.x1, tc.y1, tc.z1, tc.x2, tc.y2, tc.z2) if testAbs(distance3D-tc.expected3D) > 0.001 { t.Errorf("Expected 3D distance %.3f, got %.3f", tc.expected3D, distance3D) } }) } // Test heading calculations headingTests := []struct { name string fromX, fromY float32 toX, toY float32 expectedRange [2]float32 // min, max acceptable range }{ {"East", 0, 0, 1, 0, [2]float32{0, 90}}, {"North", 0, 0, 0, 1, [2]float32{0, 180}}, {"Northeast", 0, 0, 1, 1, [2]float32{30, 90}}, {"Same point", 5, 5, 5, 5, [2]float32{0, 360}}, } for _, tc := range headingTests { t.Run(tc.name, func(t *testing.T) { heading := CalculateHeading(tc.fromX, tc.fromY, tc.toX, tc.toY) if heading < tc.expectedRange[0] || heading > tc.expectedRange[1] { t.Logf("Heading %.2f for %s (acceptable range: %.2f-%.2f)", heading, tc.name, tc.expectedRange[0], tc.expectedRange[1]) } }) } // Test heading normalization normalizeTests := []struct { input float32 expected float32 }{ {0, 0}, {256, 256}, {512, 0}, {600, 88}, {-100, 412}, } for _, tc := range normalizeTests { normalized := NormalizeHeading(tc.input) if normalized != tc.expected { t.Errorf("Expected normalized heading %.2f for input %.2f, got %.2f", tc.expected, tc.input, normalized) } } } // TestPositionStructs tests Position data structure func TestPositionStructs(t *testing.T) { // Test NewPosition pos1 := NewPosition(10.0, 20.0, 30.0, 128.0) if pos1.X != 10.0 || pos1.Y != 20.0 || pos1.Z != 30.0 || pos1.Heading != 128.0 { t.Errorf("NewPosition failed: expected (10,20,30,128), got (%.2f,%.2f,%.2f,%.2f)", pos1.X, pos1.Y, pos1.Z, pos1.Heading) } pos2 := NewPosition(13.0, 24.0, 30.0, 128.0) // Test distance calculations distance2D := pos1.DistanceTo2D(pos2) expected2D := float32(5.0) if testAbs(distance2D-expected2D) > 0.001 { t.Errorf("Expected 2D distance %.3f, got %.3f", expected2D, distance2D) } distance3D := pos1.DistanceTo3D(pos2) expected3D := float32(5.0) if testAbs(distance3D-expected3D) > 0.001 { t.Errorf("Expected 3D distance %.3f, got %.3f", expected3D, distance3D) } // Test position operations pos1.Set(5.0, 10.0, 15.0, 64.0) if pos1.X != 5.0 || pos1.Y != 10.0 || pos1.Z != 15.0 || pos1.Heading != 64.0 { t.Errorf("Set failed: expected (5,10,15,64), got (%.2f,%.2f,%.2f,%.2f)", pos1.X, pos1.Y, pos1.Z, pos1.Heading) } // Test copy posCopy := pos1.Copy() if !pos1.Equals(posCopy) { t.Error("Expected copied position to equal original") } // Test bounding box bbox := NewBoundingBox(0, 0, 0, 10, 10, 10) if !bbox.Contains(5, 5, 5) { t.Error("Expected bounding box to contain point (5,5,5)") } if bbox.Contains(15, 5, 5) { t.Error("Expected bounding box to not contain point (15,5,5)") } if !bbox.Contains(0, 0, 0) { t.Error("Expected bounding box to contain min point") } if !bbox.Contains(10, 10, 10) { t.Error("Expected bounding box to contain max point") } } // TestMovementManager tests movement management system func TestMovementManager(t *testing.T) { // Create test zone zoneServer := NewZoneServer("test_movement") config := &ZoneServerConfig{ ZoneName: "test_movement", ZoneID: 1, LoadMaps: false, EnableWeather: false, EnablePathfinding: false, } err := zoneServer.Initialize(config) if err != nil { t.Fatalf("Failed to initialize zone server: %v", err) } // Create movement manager movementMgr := NewMobMovementManager(zoneServer) if movementMgr == nil { t.Fatal("Expected non-nil movement manager") } // Create and add a test spawn to the zone spawnID := int32(1001) testSpawn := spawn.NewSpawn() testSpawn.SetID(spawnID) testSpawn.SetX(0.0) testSpawn.SetY(0.0, false) testSpawn.SetZ(0.0) testSpawn.SetHeadingFromFloat(0.0) testSpawn.SetName("test_spawn") err = zoneServer.AddSpawn(testSpawn) if err != nil { t.Fatalf("Failed to add test spawn: %v", err) } // Test adding spawn movementMgr.AddMovementSpawn(spawnID) // Test movement state state := movementMgr.GetMovementState(spawnID) if state == nil { t.Error("Expected non-nil movement state") } else if state.SpawnID != spawnID { t.Errorf("Expected spawn ID %d, got %d", spawnID, state.SpawnID) } // Test movement command (this may fail due to missing methods but we test the interface) err = movementMgr.MoveTo(spawnID, 10.0, 20.0, 30.0, DefaultRunSpeed) // We don't check error here as it depends on spawn implementation // Test removing spawn movementMgr.RemoveMovementSpawn(spawnID) // Test that state is cleaned up state = movementMgr.GetMovementState(spawnID) if state != nil { t.Error("Expected movement state to be cleaned up after removal") } } // TestInstanceTypes tests instance type functionality func TestInstanceTypes(t *testing.T) { testCases := []struct { instanceType InstanceType expected string }{ {InstanceTypeNone, "None"}, {InstanceTypeGroupLockout, "Group Lockout"}, {InstanceTypeRaidPersist, "Raid Persistent"}, {InstanceTypePersonalHouse, "Personal House"}, {InstanceTypeSoloLockout, "Solo Lockout"}, {InstanceTypePublic, "Public"}, } for _, tc := range testCases { t.Run(tc.expected, func(t *testing.T) { result := tc.instanceType.String() if result != tc.expected { t.Errorf("Expected instance type string '%s', got '%s'", tc.expected, result) } }) } } // TestConcurrentOperations tests thread safety func TestConcurrentOperations(t *testing.T) { // Create test database conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { t.Fatalf("Failed to create test database: %v", err) } defer conn.Close() // Simple schema schema := ` CREATE TABLE zones ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, file TEXT DEFAULT 'test.zone', description TEXT DEFAULT 'Test Zone', safe_x REAL DEFAULT 0, safe_y REAL DEFAULT 0, safe_z REAL DEFAULT 0, safe_heading REAL DEFAULT 0, underworld REAL DEFAULT -1000, min_level INTEGER DEFAULT 1, max_level INTEGER DEFAULT 100, min_status INTEGER DEFAULT 0, min_version INTEGER DEFAULT 0, instance_type INTEGER DEFAULT 0, max_players INTEGER DEFAULT 100, default_lockout_time INTEGER DEFAULT 18000, default_reenter_time INTEGER DEFAULT 3600, default_reset_time INTEGER DEFAULT 259200, group_zone_option INTEGER DEFAULT 0, expansion_flag INTEGER DEFAULT 0, holiday_flag INTEGER DEFAULT 0, can_bind INTEGER DEFAULT 1, can_gate INTEGER DEFAULT 1, can_evac INTEGER DEFAULT 1, city_zone INTEGER DEFAULT 0, always_loaded INTEGER DEFAULT 0, weather_allowed INTEGER DEFAULT 1 ); CREATE TABLE spawn_location_placement ( id INTEGER PRIMARY KEY AUTOINCREMENT, zone_id INTEGER, x REAL DEFAULT 0, y REAL DEFAULT 0, z REAL DEFAULT 0, heading REAL DEFAULT 0, pitch REAL DEFAULT 0, roll REAL DEFAULT 0, spawn_type INTEGER DEFAULT 0, respawn_time INTEGER DEFAULT 300, expire_time INTEGER DEFAULT 0, expire_offset INTEGER DEFAULT 0, conditions INTEGER DEFAULT 0, conditional_value INTEGER DEFAULT 0, spawn_percentage REAL DEFAULT 100.0 ); INSERT INTO zones (id, name) VALUES (1, 'concurrent_test'); ` if err := sqlitex.ExecuteScript(conn, schema, &sqlitex.ExecOptions{}); err != nil { t.Fatalf("Failed to create test schema: %v", err) } // Test concurrent database reads with separate connections var wg sync.WaitGroup const numGoroutines = 5 // Reduce to prevent too many concurrent connections for i := 0; i < numGoroutines; i++ { wg.Add(1) go func(id int) { defer wg.Done() // Create separate connection for each goroutine to avoid concurrent access issues goroutineConn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { t.Errorf("Goroutine %d failed to create connection: %v", id, err) return } defer goroutineConn.Close() // Create schema in new connection if err := sqlitex.ExecuteScript(goroutineConn, schema, &sqlitex.ExecOptions{}); err != nil { t.Errorf("Goroutine %d failed to create schema: %v", id, err) return } zdb := NewZoneDatabase(goroutineConn) _, err = zdb.LoadZoneData(1) if err != nil { t.Errorf("Goroutine %d failed to load zone data: %v", id, err) } }(i) } wg.Wait() // Test concurrent zone manager operations config := &ZoneManagerConfig{ MaxZones: 10, MaxInstanceZones: 20, ProcessInterval: time.Millisecond * 100, CleanupInterval: time.Second * 1, } zoneManager := NewZoneManager(config, conn) for i := 0; i < numGoroutines; i++ { wg.Add(1) go func(id int) { defer wg.Done() stats := zoneManager.GetStatistics() if stats == nil { t.Errorf("Goroutine %d got nil statistics", id) } }(i) } wg.Wait() } // TestConstants verifies various constants are properly defined func TestConstants(t *testing.T) { // Test distance constants if SendSpawnDistance != 250.0 { t.Errorf("Expected SendSpawnDistance 250.0, got %.2f", SendSpawnDistance) } if MaxChaseDistance != 80.0 { t.Errorf("Expected MaxChaseDistance 80.0, got %.2f", MaxChaseDistance) } // Test expansion constants if ExpansionDOF != 1024 { t.Errorf("Expected ExpansionDOF 1024, got %d", ExpansionDOF) } // Test EQ2 heading constant if EQ2HeadingMax != 512.0 { t.Errorf("Expected EQ2HeadingMax 512.0, got %.2f", EQ2HeadingMax) } // Test default speeds if DefaultWalkSpeed <= 0 { t.Error("Expected DefaultWalkSpeed to be positive") } if DefaultRunSpeed <= DefaultWalkSpeed { t.Error("Expected DefaultRunSpeed to be greater than DefaultWalkSpeed") } } // Benchmark tests // BenchmarkDistanceCalculation benchmarks distance calculations func BenchmarkDistanceCalculation(b *testing.B) { x1, y1, z1 := float32(100.0), float32(200.0), float32(300.0) x2, y2, z2 := float32(150.0), float32(250.0), float32(350.0) b.ResetTimer() for i := 0; i < b.N; i++ { Distance3D(x1, y1, z1, x2, y2, z2) } } // BenchmarkPositionDistance benchmarks position-based distance calculations func BenchmarkPositionDistance(b *testing.B) { pos1 := NewPosition(100.0, 200.0, 300.0, 128.0) pos2 := NewPosition(150.0, 250.0, 350.0, 256.0) b.ResetTimer() for i := 0; i < b.N; i++ { pos1.DistanceTo3D(pos2) } } // BenchmarkHeadingCalculation benchmarks heading calculations func BenchmarkHeadingCalculation(b *testing.B) { fromX, fromY := float32(0.0), float32(0.0) toX, toY := float32(100.0), float32(100.0) b.ResetTimer() for i := 0; i < b.N; i++ { CalculateHeading(fromX, fromY, toX, toY) } } // BenchmarkDatabaseOperations benchmarks database operations func BenchmarkDatabaseOperations(b *testing.B) { // Create test database tmpDir := b.TempDir() dbPath := filepath.Join(tmpDir, "benchmark.db") conn, err := sqlite.OpenConn(dbPath, sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { b.Fatalf("Failed to create benchmark database: %v", err) } defer conn.Close() // Create schema and test data schema := ` CREATE TABLE zones ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, file TEXT DEFAULT 'test.zone', description TEXT DEFAULT 'Test Zone', safe_x REAL DEFAULT 0, safe_y REAL DEFAULT 0, safe_z REAL DEFAULT 0, safe_heading REAL DEFAULT 0, underworld REAL DEFAULT -1000, min_level INTEGER DEFAULT 1, max_level INTEGER DEFAULT 100, min_status INTEGER DEFAULT 0, min_version INTEGER DEFAULT 0, instance_type INTEGER DEFAULT 0, max_players INTEGER DEFAULT 100, default_lockout_time INTEGER DEFAULT 18000, default_reenter_time INTEGER DEFAULT 3600, default_reset_time INTEGER DEFAULT 259200, group_zone_option INTEGER DEFAULT 0, expansion_flag INTEGER DEFAULT 0, holiday_flag INTEGER DEFAULT 0, can_bind INTEGER DEFAULT 1, can_gate INTEGER DEFAULT 1, can_evac INTEGER DEFAULT 1, city_zone INTEGER DEFAULT 0, always_loaded INTEGER DEFAULT 0, weather_allowed INTEGER DEFAULT 1 ); CREATE TABLE spawn_location_placement ( id INTEGER PRIMARY KEY AUTOINCREMENT, zone_id INTEGER, x REAL DEFAULT 0, y REAL DEFAULT 0, z REAL DEFAULT 0, heading REAL DEFAULT 0, pitch REAL DEFAULT 0, roll REAL DEFAULT 0, spawn_type INTEGER DEFAULT 0, respawn_time INTEGER DEFAULT 300, expire_time INTEGER DEFAULT 0, expire_offset INTEGER DEFAULT 0, conditions INTEGER DEFAULT 0, conditional_value INTEGER DEFAULT 0, spawn_percentage REAL DEFAULT 100.0 ); INSERT INTO zones (id, name) VALUES (1, 'benchmark_zone'); ` if err := sqlitex.ExecuteScript(conn, schema, &sqlitex.ExecOptions{}); err != nil { b.Fatalf("Failed to create benchmark schema: %v", err) } zdb := NewZoneDatabase(conn) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := zdb.LoadZoneData(1) if err != nil { b.Fatalf("Failed to load zone data: %v", err) } } } // BenchmarkZoneManagerOperations benchmarks zone manager operations func BenchmarkZoneManagerOperations(b *testing.B) { conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate) if err != nil { b.Fatalf("Failed to create benchmark database: %v", err) } defer conn.Close() config := &ZoneManagerConfig{ MaxZones: 10, MaxInstanceZones: 20, ProcessInterval: time.Millisecond * 100, CleanupInterval: time.Second * 1, } zoneManager := NewZoneManager(config, conn) b.ResetTimer() for i := 0; i < b.N; i++ { zoneManager.GetStatistics() } } // Helper functions func testAbs(x float32) float32 { if x < 0 { return -x } return x }