372 lines
12 KiB
Markdown
372 lines
12 KiB
Markdown
# 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
```sql
|
|
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
|
|
```sql
|
|
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
|
|
```sql
|
|
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:
|
|
```bash
|
|
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 |