EverQuest II Zone System
This package implements a comprehensive zone management system for the EverQuest II server emulator, converted from the original C++ implementation while leveraging Go's concurrency and type safety features.
Overview
The zone system handles:
- Zone Management: Loading, initialization, and lifecycle management of game zones
- Instance Management: Creating and managing instanced zones for groups, raids, and solo play
- Spawn Management: NPCs, objects, widgets, signs, and ground spawns with spatial optimization
- Movement System: NPC movement with pathfinding, stuck detection, and multiple movement modes
- Position System: 3D position calculations, distance functions, and EQ2-specific heading math
- Weather System: Dynamic weather with patterns, severity controls, and client synchronization
- Database Integration: Persistent storage of zone configuration, spawns, and player data
- Client Management: Player connections, spawn visibility, and packet communication
- Grid System: Spatial partitioning for efficient range queries and spawn management
Architecture
Core Components
ZoneServer (zone_server.go
)
- Central coordinator for all zone functionality
- Manages clients, spawns, timers, and processing loops
- Handles zone initialization, configuration, and shutdown
- Thread-safe operations using sync.RWMutex and atomic values
- Supports both regular zones and instances
ZoneManager (zone_manager.go
)
- High-level management of multiple zones and instances
- Automatic loading/unloading based on demand
- Instance creation with type-specific player limits
- Statistics collection and monitoring
- Cleanup of inactive instances
MobMovementManager (movement_manager.go
)
- Advanced NPC movement system with command queuing
- Pathfinding integration with multiple backends
- Stuck detection and recovery mechanisms
- Multiple movement modes (walk, run, swim, fly)
- Thread-safe processing with delta time calculations
Position System (position.go
)
- EverQuest II specific 3D math utilities
- Distance calculations (2D, 3D, 4D with heading)
- Heading conversion and normalization (512-unit circle)
- Bounding box and cylinder collision detection
- Interpolation and random position generation
Database Layer (database.go
)
- Complete zone data persistence with prepared statements
- Transaction support for atomic updates
- Efficient loading of zone configuration and spawn data
- Support for spawn locations, groups, and associations
- Thread-safe operations with connection pooling
Key Features
Spatial Optimization
- Grid System: Spatial partitioning for efficient spawn queries
- Range-based Updates: Only process spawns within client visibility
- Distance Culling: Automatic spawn loading/unloading based on distance
- Grid-based Indexing: Fast lookup of nearby spawns and objects
Instance System
- Multiple Instance Types: Group, raid, solo, tradeskill, housing, quest instances
- Automatic Limits: Type-specific player count restrictions
- Lifecycle Management: Automatic creation and cleanup
- Persistence Options: Lockout vs persistent instances
Movement System
- Command Queuing: Sequential movement command execution
- Pathfinding Integration: Multiple pathfinding backends (navmesh, waypoint, null)
- Stuck Detection: Position-based stuck detection with recovery strategies
- Smooth Movement: Delta-time based position interpolation
- Multi-mode Support: Walking, running, swimming, flying
Weather System
- Dynamic Patterns: Normal, dynamic, random, and chaotic weather types
- Severity Control: Min/max bounds with configurable change rates
- Pattern Support: Increasing, decreasing, and random severity patterns
- Client Synchronization: Automatic weather updates to all clients
Usage Examples
Basic Zone Creation and Management
// Create zone server
zoneServer := NewZoneServer("qeynos")
// Configure zone
config := &ZoneServerConfig{
ZoneName: "qeynos",
ZoneFile: "qeynos.zone",
ZoneDescription: "Qeynos: Capitol of Antonica",
ZoneID: 100,
InstanceID: 0,
InstanceType: InstanceTypeNone,
MaxPlayers: 200,
MinLevel: 1,
MaxLevel: 100,
SafeX: 830.0,
SafeY: -25.0,
SafeZ: -394.0,
SafeHeading: 0.0,
LoadMaps: true,
EnableWeather: true,
EnablePathfinding: true,
}
// Initialize zone
err := zoneServer.Initialize(config)
if err != nil {
log.Fatal(err)
}
// Add client to zone
err = zoneServer.AddClient(client)
if err != nil {
log.Printf("Failed to add client: %v", err)
}
Zone Manager Usage
// Create zone manager
config := &ZoneManagerConfig{
MaxZones: 50,
MaxInstanceZones: 500,
ProcessInterval: time.Millisecond * 100,
CleanupInterval: time.Minute * 5,
EnableWeather: true,
EnablePathfinding: true,
}
zoneManager := NewZoneManager(config, database)
// Start zone manager
err := zoneManager.Start()
if err != nil {
log.Fatal(err)
}
// Load a zone
zone, err := zoneManager.LoadZone(100) // Qeynos
if err != nil {
log.Printf("Failed to load zone: %v", err)
}
// Create an instance
instance, err := zoneManager.CreateInstance(100, InstanceTypeGroupLockout, playerID)
if err != nil {
log.Printf("Failed to create instance: %v", err)
}
Movement System Usage
// Get movement manager from zone
movementMgr := zoneServer.movementMgr
// Add NPC to movement tracking
spawnID := int32(1001)
movementMgr.AddMovementSpawn(spawnID)
// Command NPC to move
err := movementMgr.MoveTo(spawnID, 100.0, 200.0, 0.0, DefaultRunSpeed)
if err != nil {
log.Printf("Movement command failed: %v", err)
}
// Queue multiple commands
movementMgr.MoveTo(spawnID, 150.0, 250.0, 0.0, DefaultWalkSpeed)
movementMgr.RotateTo(spawnID, 256.0, 90.0) // Turn around
movementMgr.MoveTo(spawnID, 100.0, 200.0, 0.0, DefaultRunSpeed) // Return
// Check if moving
if movementMgr.IsMoving(spawnID) {
state := movementMgr.GetMovementState(spawnID)
log.Printf("NPC %d is moving at speed %.2f", spawnID, state.Speed)
}
Position Calculations
// Calculate distance between two points
distance := Distance3D(0, 0, 0, 100, 100, 100)
log.Printf("Distance: %.2f", distance)
// Calculate heading from one point to another
heading := CalculateHeading(0, 0, 100, 100)
log.Printf("Heading: %.2f", heading)
// Work with positions
pos1 := NewPosition(10.0, 20.0, 30.0, 128.0)
pos2 := NewPosition(50.0, 60.0, 30.0, 256.0)
distance = pos1.DistanceTo3D(pos2)
log.Printf("Position distance: %.2f", distance)
// Check if positions are within range
if IsWithinRange(pos1, pos2, 100.0) {
log.Println("Positions are within range")
}
// Create bounding box and test containment
bbox := NewBoundingBox(0, 0, 0, 100, 100, 100)
if bbox.ContainsPosition(pos1) {
log.Println("Position is inside bounding box")
}
Weather System
// Set rain level
zoneServer.SetRain(0.8) // Heavy rain
// Weather is processed automatically, but can be triggered manually
zoneServer.ProcessWeather()
// Configure weather (typically done during initialization)
zoneServer.weatherEnabled = true
zoneServer.weatherType = WeatherTypeDynamic
zoneServer.weatherFrequency = 600 // 10 minutes
zoneServer.weatherMinSeverity = 0.0
zoneServer.weatherMaxSeverity = 1.0
zoneServer.weatherChangeAmount = 0.1
zoneServer.weatherChangeChance = 75 // 75% chance of change
Database Schema
The zone system uses several database tables:
Core Zone Configuration
CREATE TABLE 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,
max_players INTEGER DEFAULT 100,
instance_type INTEGER DEFAULT 0,
expansion_flag INTEGER DEFAULT 0,
weather_allowed INTEGER DEFAULT 1,
-- ... additional fields
);
Spawn Locations
CREATE TABLE spawn_location_placement (
id INTEGER PRIMARY KEY,
zone_id INTEGER,
x REAL,
y REAL,
z REAL,
heading REAL,
spawn_type INTEGER,
respawn_time INTEGER DEFAULT 600,
conditions INTEGER DEFAULT 0,
spawn_percentage REAL DEFAULT 100.0
);
Spawn Groups
CREATE TABLE spawn_location_group (
group_id INTEGER,
location_id INTEGER,
zone_id INTEGER
);
Configuration
Key Constants
- Distance Constants:
SendSpawnDistance
(250),RemoveSpawnDistance
(300) - Movement Speeds:
DefaultWalkSpeed
(2.5),DefaultRunSpeed
(7.0) - Timer Intervals: Configurable processing intervals for different systems
- Capacity Limits:
MaxSpawnsPerGrid
(100),MaxClientsPerZone
(200)
Zone Rules
Zones can be configured with various rules:
- Player level restrictions (min/max)
- Client version requirements
- PvP enablement
- Weather settings
- Instance type and capacity
- Expansion and holiday flags
Thread Safety
All zone operations are designed to be thread-safe:
- RWMutex Usage: Separate read/write locks for different data structures
- Atomic Operations: For simple flags and counters
- Channel Communication: For cross-goroutine messaging
- Immutable Data: Where possible, data structures are immutable
- Copy-on-Read: Returns copies of data to prevent race conditions
Performance Considerations
- Spatial Indexing: Grid-based partitioning reduces O(n) to O(1) for range queries
- Prepared Statements: All database queries use prepared statements
- Object Pooling: Reuse of frequently allocated objects
- Lazy Loading: Zone data loaded on demand
- Concurrent Processing: Multiple goroutines for different subsystems
- Memory Management: Regular cleanup of expired objects and timers
Error Handling
The zone system provides comprehensive error handling:
- Graceful Degradation: Systems continue operating when non-critical components fail
- Detailed Logging: All errors logged with appropriate prefixes and context
- Recovery Mechanisms: Automatic recovery from common error conditions
- Validation: Input validation at all API boundaries
- Timeouts: All operations have appropriate timeouts
Testing
Comprehensive test suite includes:
- Unit tests for all major components
- Integration tests for database operations
- Performance benchmarks for critical paths
- Mock implementations for testing isolation
- Property-based testing for mathematical functions
Run tests with:
go test ./internal/zone/...
go test -race ./internal/zone/... # Race condition detection
go test -bench=. ./internal/zone/ # Performance benchmarks
Migration from C++
This Go implementation maintains compatibility with the original C++ EQ2EMu zone system:
- Database Schema: Identical table structure and relationships
- Protocol Compatibility: Same client communication protocols
- Algorithmic Equivalence: Math functions produce identical results
- Configuration Format: Compatible configuration files and settings
- Performance: Comparable or improved performance through Go's concurrency
Dependencies
- Standard Library: sync, time, database/sql, math
- Internal Packages: database, spawn, common
- External: SQLite driver (zombiezen.com/go/sqlite)
Future Enhancements
Planned improvements include:
- Advanced Pathfinding: Integration with Detour navigation mesh
- Lua Scripting: Full Lua integration for spawn behaviors
- Physics Engine: Advanced collision detection and physics
- Clustering: Multi-server zone distribution
- Hot Reloading: Dynamic configuration updates without restart