2025-08-06 14:39:39 -05:00
..
2025-08-06 14:39:39 -05:00
2025-07-31 15:34:04 -05:00
2025-08-06 14:39:39 -05:00
2025-07-31 15:34:04 -05:00
2025-08-06 14:39:39 -05:00
2025-08-06 14:39:39 -05:00
2025-08-06 14:39:39 -05:00
2025-07-31 15:34:04 -05:00
2025-08-06 14:39:39 -05:00
2025-08-06 14:39:39 -05:00
2025-08-06 14:39:39 -05:00
2025-08-06 14:39:39 -05:00

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