clena out doc.go files
This commit is contained in:
parent
50ccc8a2d9
commit
3b9388df44
88
CLAUDE.md
88
CLAUDE.md
@ -1,88 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
**Additional Documentation Requirements:**
|
||||
- Document all public types, constants, and variables
|
||||
- Include examples in documentation for complex functions
|
||||
- Explain any non-obvious algorithms or business logic
|
||||
- Document error conditions and edge cases
|
||||
- For packet-related code, reference the relevant XML definitions or protocol specifications
|
||||
|
||||
## Project Overview
|
||||
|
||||
EQ2Go is a Go rewrite of the EverQuest II server emulator from C++ EQ2EMu. Implements core MMO server functionality with authentication, world simulation, and game mechanics using modern Go practices.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Build servers
|
||||
go build ./cmd/...
|
||||
|
||||
# Test with race detection
|
||||
go test -race ./...
|
||||
|
||||
# Format and tidy
|
||||
go fmt ./...; go mod tidy
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
**Login Server** - Client authentication, character management, world server coordination
|
||||
**World Server** - Game simulation engine, zones, NPCs, combat, quests, web admin interface
|
||||
|
||||
**Core Systems:**
|
||||
- Network: EverQuest II UDP protocol with reliability layer, RC4 encryption, CRC validation
|
||||
- Database: SQLite with simplified query interface, transactions, connection pooling
|
||||
- Packets: XML-driven packet definitions with version-specific formats
|
||||
- Events: 100+ EQ2 functions organized by domain (health/attributes/movement/combat/misc)
|
||||
|
||||
## Key Systems
|
||||
|
||||
**Spawn System**: Base game entity system with positioning, commands, and scripting. Foundation for all game entities with modern Go concurrency patterns.
|
||||
|
||||
**Entity System**: Combat-capable spawns with spell effects, stats, pet management. Includes InfoStruct for complete character statistics, spell casting, pet management, and bonus calculations.
|
||||
|
||||
**Spells System**: Complete spell management with 50ms processing intervals, comprehensive targeting (self/single/group/AOE), resource management (power/health/concentration), cast/recast timers, interrupt handling, and heroic opportunities.
|
||||
|
||||
**Player System**: Complete player character management extending Entity with experience systems, skill progression, spell management, combat mechanics, social features, currency management, quest integration, spawn management, character flags, and comprehensive cleanup systems. Database integration uses zombiezen SQLite with complete persistence.
|
||||
|
||||
**NPC System**: Non-player characters extending Entity with complete AI, combat, and spell casting. Features brain system, spell management, skill bonuses, movement with runback mechanics, appearance randomization, and AI strategies. Includes specialized brain types (Combat/NonCombat/Blank/Lua/DumbFire pets).
|
||||
|
||||
**Quest System**: Complete quest management with multiple step types (kill/chat/obtain/location/spell/normal/craft/harvest), comprehensive prerequisite system, flexible reward system, step-based progress tracking, Lua action system, quest sharing, and repeatable quest support.
|
||||
|
||||
**Event System**: 137 EQ2 functions organized by domain in `internal/events/functions/` - health.go (23 functions), attributes.go (24 functions), movement.go (27 functions), combat.go (36 functions), misc.go (27 functions). Features EventContext with fluent API, thread-safe operations, minimal overhead, type-safe parameter access, and comprehensive registry system.
|
||||
|
||||
**Trade/Object/Widget/Transmute/Skills/Titles/Races/Classes/Languages/Factions/Ground Spawn/Appearances/Sign Systems**: Complete implementations with full EQ2 compatibility, thread-safe operations, and comprehensive integration interfaces.
|
||||
|
||||
## Development Patterns
|
||||
|
||||
**Package Structure**: Each system follows consistent structure with types.go, constants.go, manager.go, interfaces.go, database.go (where applicable), and README.md.
|
||||
|
||||
**Thread Safety**: All systems implement proper Go concurrency patterns with sync.RWMutex for read-heavy operations, atomic operations for flags/counters, proper lock ordering, and channel-based communication.
|
||||
|
||||
**Integration Patterns**: *Aware interfaces for feature detection, adapter pattern for bridging systems, manager pattern for coordination, event-driven architecture for notifications.
|
||||
|
||||
**Entity-Pass-By-Pointer**: Always pass `*entity.Entity` (not `entity.Entity`) to avoid copying locks and improve performance.
|
||||
|
||||
**Name Padding Handling**: Player names from database may have null byte padding from `[128]byte` arrays. Always use `strings.TrimSpace(strings.Trim(name, "\x00"))`.
|
||||
|
||||
## Configuration
|
||||
|
||||
**World Server** creates `world_config.json` with defaults. Command-line flags override JSON configuration.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `zombiezen.com/go/sqlite`: Modern SQLite driver
|
||||
- `golang.org/x/crypto`: Cryptographic functions
|
||||
- Go version: 1.24.5, Module: `eq2emu`
|
||||
|
||||
## Important Notes
|
||||
|
||||
**C++ Reference Code**: The original C++ EQ2EMu source code is available in `/old` directory for functionality reference only. Use this to understand game mechanics, algorithms, and expected behavior - NOT for structure or patterns. The Go implementation uses modern Go practices and patterns.
|
||||
|
||||
**Database Migration**: Uses zombiezen SQLite with `sqlite.Conn`, `sqlitex.Execute` with `ExecOptions` and `ResultFunc`, proper `found` flag handling, and thread-safe connection management.
|
||||
|
||||
**Architecture Transition**: Converted from C++ EQ2EMu codebase maintaining protocol compatibility while modernizing implementation.
|
||||
|
||||
**Testing**: Focus on UDP protocol and packet parsing for client compatibility. All systems include comprehensive test suites with concurrency testing.
|
379
MODERNIZE.md
379
MODERNIZE.md
@ -1,379 +0,0 @@
|
||||
# Package Modernization Instructions
|
||||
|
||||
## Goal
|
||||
Transform legacy packages to use focused, performance-optimized bespoke master lists with simplified database operations. REMEMBER THAT THIS IS FOR A TOTAL REWRITE, NOT A VARIATION. If any step does not apply to a given package, it is okay to skip it and to focus on modernizing other areas of the package.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Create Bespoke MasterList Implementation
|
||||
Replace generic implementations with specialized, high-performance master lists:
|
||||
```go
|
||||
// Create domain-specific master list optimized for the use case
|
||||
type MasterList struct {
|
||||
// Core storage
|
||||
items map[K]*Type // ID -> Type
|
||||
mutex sync.RWMutex
|
||||
|
||||
// Specialized indices for O(1) lookups
|
||||
byCategory map[string][]*Type // Category -> items
|
||||
byProperty map[string][]*Type // Property -> items
|
||||
|
||||
// Cached metadata with invalidation
|
||||
categories []string
|
||||
metaStale bool
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Consolidate Package Structure
|
||||
|
||||
**Remove:**
|
||||
- Legacy wrapper functions (LoadAll, SaveAll, etc.)
|
||||
- Duplicate database files (database.go, database_legacy.go)
|
||||
- Generic MasterList dependencies
|
||||
- README.md (use doc.go instead)
|
||||
- Separate "active_record.go" files
|
||||
|
||||
**Consolidate into:**
|
||||
- `{type}.go` - Main type with embedded database operations
|
||||
- `types.go` - Supporting types only (no main type)
|
||||
- `master.go` - Bespoke MasterList optimized for domain
|
||||
- `player.go` - Player-specific logic (if applicable)
|
||||
- `doc.go` - Primary documentation
|
||||
- `{type}_test.go` - Focused tests
|
||||
- `benchmark_test.go` - Comprehensive performance tests
|
||||
|
||||
### 3. Refactor Main Type
|
||||
|
||||
Transform the main type to include database operations:
|
||||
|
||||
```go
|
||||
// Before: Separate type and database operations
|
||||
type Achievement struct {
|
||||
ID uint32
|
||||
Title string
|
||||
}
|
||||
|
||||
func LoadAchievement(db *database.Database, id uint32) (*Achievement, error)
|
||||
func SaveAchievement(db *database.Database, a *Achievement) error
|
||||
|
||||
// After: Embedded database operations
|
||||
type Achievement struct {
|
||||
ID uint32
|
||||
Title string
|
||||
|
||||
db *database.Database
|
||||
isNew bool
|
||||
}
|
||||
|
||||
func New(db *database.Database) *Achievement
|
||||
func Load(db *database.Database, id uint32) (*Achievement, error)
|
||||
func (a *Achievement) Save() error
|
||||
func (a *Achievement) Delete() error
|
||||
func (a *Achievement) Reload() error
|
||||
```
|
||||
|
||||
### 4. Create Bespoke MasterList
|
||||
|
||||
Replace generic implementations with specialized, optimized master lists:
|
||||
|
||||
```go
|
||||
// Before: Generic base with limited optimization
|
||||
type MasterList struct {
|
||||
*common.MasterList[uint32, *Achievement]
|
||||
}
|
||||
|
||||
// After: Bespoke implementation optimized for domain
|
||||
type MasterList struct {
|
||||
// Core storage
|
||||
achievements map[uint32]*Achievement
|
||||
mutex sync.RWMutex
|
||||
|
||||
// Domain-specific indices for O(1) lookups
|
||||
byCategory map[string][]*Achievement
|
||||
byExpansion map[string][]*Achievement
|
||||
|
||||
// Cached metadata with lazy loading
|
||||
categories []string
|
||||
expansions []string
|
||||
metaStale bool
|
||||
}
|
||||
|
||||
func (m *MasterList) AddAchievement(a *Achievement) bool {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
// Check existence
|
||||
if _, exists := m.achievements[a.AchievementID]; exists {
|
||||
return false
|
||||
}
|
||||
|
||||
// Add to core storage
|
||||
m.achievements[a.AchievementID] = a
|
||||
|
||||
// Update specialized indices
|
||||
m.byCategory[a.Category] = append(m.byCategory[a.Category], a)
|
||||
m.byExpansion[a.Expansion] = append(m.byExpansion[a.Expansion], a)
|
||||
|
||||
// Invalidate metadata cache
|
||||
m.metaStale = true
|
||||
return true
|
||||
}
|
||||
|
||||
// O(1) category lookup
|
||||
func (m *MasterList) GetByCategory(category string) []*Achievement {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
return m.byCategory[category]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Optimize for Domain Use Cases
|
||||
|
||||
Focus on the specific operations your domain needs:
|
||||
|
||||
```go
|
||||
// Implement GetID() for consistency
|
||||
func (a *Achievement) GetID() uint32 {
|
||||
return a.AchievementID
|
||||
}
|
||||
|
||||
// Add domain-specific optimized methods
|
||||
func (m *MasterList) GetByCategoryAndExpansion(category, expansion string) []*Achievement {
|
||||
// Use set intersection for efficient combined queries
|
||||
categoryItems := m.byCategory[category]
|
||||
expansionItems := m.byExpansion[expansion]
|
||||
|
||||
// Use smaller set for iteration efficiency
|
||||
if len(categoryItems) > len(expansionItems) {
|
||||
categoryItems, expansionItems = expansionItems, categoryItems
|
||||
}
|
||||
|
||||
// Set intersection using map lookup
|
||||
expansionSet := make(map[*Achievement]struct{}, len(expansionItems))
|
||||
for _, item := range expansionItems {
|
||||
expansionSet[item] = struct{}{}
|
||||
}
|
||||
|
||||
var result []*Achievement
|
||||
for _, item := range categoryItems {
|
||||
if _, exists := expansionSet[item]; exists {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Simplify API
|
||||
|
||||
**Remove:**
|
||||
- Generic MasterList dependencies
|
||||
- Legacy type variants (Achievement vs AchievementRecord)
|
||||
- Conversion methods (ToLegacy, FromLegacy)
|
||||
- Duplicate CRUD operations
|
||||
- Complex wrapper functions
|
||||
- Slow O(n) filter operations
|
||||
|
||||
**Keep:**
|
||||
- Single type definition
|
||||
- Direct database methods on type
|
||||
- Domain-specific optimized operations
|
||||
- O(1) indexed lookups where possible
|
||||
- Lazy caching for expensive operations
|
||||
|
||||
### 7. Update Documentation
|
||||
|
||||
Create concise `doc.go`:
|
||||
|
||||
```go
|
||||
// Package achievements provides [brief description].
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// achievement := achievements.New(db)
|
||||
// achievement.Title = "Dragon Slayer"
|
||||
// achievement.Save()
|
||||
//
|
||||
// loaded, _ := achievements.Load(db, 1001)
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Bespoke Master List (optimized for performance):
|
||||
//
|
||||
// masterList := achievements.NewMasterList()
|
||||
// masterList.AddAchievement(achievement)
|
||||
//
|
||||
// // O(1) category lookup
|
||||
// combatAchievements := masterList.GetByCategory("Combat")
|
||||
//
|
||||
// // O(1) expansion lookup
|
||||
// classicAchievements := masterList.GetByExpansion("Classic")
|
||||
//
|
||||
// // Optimized set intersection
|
||||
// combined := masterList.GetByCategoryAndExpansion("Combat", "Classic")
|
||||
package achievements
|
||||
```
|
||||
|
||||
### 8. Testing
|
||||
|
||||
Create focused tests:
|
||||
- Test core type operations (New, Save, Load, Delete)
|
||||
- Test MasterList basic operations
|
||||
- Remove legacy compatibility tests
|
||||
- Keep tests simple and direct
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Single Source of Truth**: One type definition, not multiple variants
|
||||
2. **Embedded Operations**: Database methods on the type itself
|
||||
3. **Bespoke Master Lists**: Domain-specific optimized implementations
|
||||
4. **Performance First**: O(1) lookups, lazy caching, efficient algorithms
|
||||
5. **Thread Safety**: Proper RWMutex usage with minimal lock contention
|
||||
6. **No Legacy Baggage**: Remove all "Legacy" types and converters
|
||||
7. **Documentation in Code**: Use doc.go, not README.md
|
||||
8. **Comprehensive Benchmarking**: Measure and optimize all operations
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Identify main type and supporting types
|
||||
- [ ] Consolidate database operations into main type
|
||||
- [ ] Add db field and methods to main type
|
||||
- [ ] **Create bespoke MasterList optimized for domain**
|
||||
- [ ] **Add specialized indices for O(1) lookups**
|
||||
- [ ] **Implement lazy caching for expensive operations**
|
||||
- [ ] **Add set intersection algorithms for combined queries**
|
||||
- [ ] Implement GetID() for consistency
|
||||
- [ ] Remove all generic MasterList dependencies
|
||||
- [ ] Remove all legacy types and converters
|
||||
- [ ] Update doc.go with performance-focused examples
|
||||
- [ ] Simplify tests to cover core functionality
|
||||
- [ ] **Add concurrency tests for thread safety**
|
||||
- [ ] **Create comprehensive benchmarks (benchmark_test.go)**
|
||||
- [ ] **Verify performance meets targets (sub-microsecond operations)**
|
||||
- [ ] Run `go test -race` to verify thread safety
|
||||
- [ ] Run `go fmt`, `go test`, and `go test -bench=.`
|
||||
|
||||
## Expected Results
|
||||
|
||||
- **80% less code** in most packages
|
||||
- **Single type** instead of multiple variants
|
||||
- **Thread-safe** operations with optimized locking
|
||||
- **O(1) performance** for common lookup operations
|
||||
- **Sub-microsecond** response times for most operations
|
||||
- **Specialized algorithms** for domain-specific queries
|
||||
- **Consistent API** across all packages
|
||||
- **Better maintainability** with focused, understandable code
|
||||
- **Performance predictability** through comprehensive benchmarking
|
||||
|
||||
## Benchmarking
|
||||
|
||||
After modernization, create comprehensive benchmarks to measure performance improvements and ensure no regressions:
|
||||
|
||||
### Required Benchmarks
|
||||
|
||||
**Core Operations:**
|
||||
```go
|
||||
func BenchmarkTypeCreation(b *testing.B) // New type creation
|
||||
func BenchmarkTypeOperations(b *testing.B) // CRUD operations
|
||||
func BenchmarkMasterListOperations(b *testing.B) // Collection operations
|
||||
```
|
||||
|
||||
**Performance Critical Paths:**
|
||||
```go
|
||||
func BenchmarkCoreAlgorithm(b *testing.B) // Main business logic
|
||||
func BenchmarkConcurrentAccess(b *testing.B) // Thread safety
|
||||
func BenchmarkMemoryAllocation(b *testing.B) // Memory patterns
|
||||
```
|
||||
|
||||
**Comparison Benchmarks:**
|
||||
```go
|
||||
func BenchmarkComparisonWithOldSystem(b *testing.B) // Before/after metrics
|
||||
```
|
||||
|
||||
### Benchmark Structure
|
||||
|
||||
Use sub-benchmarks for detailed measurements:
|
||||
```go
|
||||
func BenchmarkMasterListOperations(b *testing.B) {
|
||||
ml := NewMasterList()
|
||||
// Setup data...
|
||||
|
||||
b.Run("Add", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ml.Add(createTestItem(i))
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Get", func(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = ml.Get(randomID())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Mock Implementations
|
||||
|
||||
Create lightweight mocks for interfaces:
|
||||
```go
|
||||
type mockPlayer struct {
|
||||
level int16
|
||||
location int32
|
||||
}
|
||||
|
||||
func (p *mockPlayer) GetLevel() int16 { return p.level }
|
||||
func (p *mockPlayer) GetLocation() int32 { return p.location }
|
||||
```
|
||||
|
||||
### Performance Expectations
|
||||
|
||||
Target performance after bespoke modernization:
|
||||
- **Creation operations**: <100ns per operation
|
||||
- **ID lookup operations**: <50ns per operation (O(1) map access)
|
||||
- **Indexed lookup operations**: <100ns per operation (O(1) specialized indices)
|
||||
- **Combined queries**: <5µs per operation (optimized set intersection)
|
||||
- **Cached metadata**: <100ns per operation (lazy loading)
|
||||
- **Memory allocations**: Zero allocations for read operations
|
||||
- **Concurrent access**: Linear scaling with cores, minimal lock contention
|
||||
- **Specialized algorithms**: Domain-optimized performance characteristics
|
||||
|
||||
### Running Benchmarks
|
||||
|
||||
```bash
|
||||
# Run all benchmarks
|
||||
go test -bench=. ./internal/package_name
|
||||
|
||||
# Detailed benchmarks with memory stats
|
||||
go test -bench=. -benchmem ./internal/package_name
|
||||
|
||||
# Compare performance over time
|
||||
go test -bench=. -count=5 ./internal/package_name
|
||||
|
||||
# CPU profiling for optimization
|
||||
go test -bench=BenchmarkCoreAlgorithm -cpuprofile=cpu.prof ./internal/package_name
|
||||
```
|
||||
|
||||
## Example Commands
|
||||
|
||||
```bash
|
||||
# Remove legacy files and generic dependencies
|
||||
rm README.md database_legacy.go active_record.go
|
||||
|
||||
# Remove generic MasterList imports
|
||||
grep -r "eq2emu/internal/common" . --include="*.go" | cut -d: -f1 | sort -u | xargs sed -i '/eq2emu\/internal\/common/d'
|
||||
|
||||
# Rename if needed
|
||||
mv active_record.go achievement.go
|
||||
|
||||
# Test the changes
|
||||
go fmt ./...
|
||||
go test ./...
|
||||
go test -race ./... # Verify thread safety
|
||||
go test -bench=. ./...
|
||||
go build ./...
|
||||
|
||||
# Performance verification
|
||||
go test -bench=BenchmarkMasterListOperations -benchtime=5s ./internal/package_name
|
||||
go test -bench=. -benchmem ./internal/package_name
|
||||
```
|
@ -149,8 +149,11 @@ func createDefaultConfig() *login.ServerConfig {
|
||||
WebPassword: "",
|
||||
|
||||
// Database settings
|
||||
DatabaseType: "sqlite",
|
||||
DatabaseDSN: "login.db",
|
||||
DatabaseAddress: "localhost",
|
||||
DatabasePort: 3306,
|
||||
DatabaseUsername: "eq2",
|
||||
DatabasePassword: "eq2pass",
|
||||
DatabaseName: "eq2login",
|
||||
|
||||
// Server settings
|
||||
ServerName: "EQ2Go Login Server",
|
||||
@ -189,7 +192,7 @@ func printBanner(config *login.ServerConfig) {
|
||||
fmt.Printf("Server Name: %s\n", config.ServerName)
|
||||
fmt.Printf("Listen Address: %s:%d\n", config.ListenAddr, config.ListenPort)
|
||||
fmt.Printf("Web Interface: %s:%d\n", config.WebAddr, config.WebPort)
|
||||
fmt.Printf("Database: %s %s\n", config.DatabaseType, config.DatabaseDSN)
|
||||
fmt.Printf("Database: mysql://%s@%s:%d/%s\n", config.DatabaseUsername, config.DatabaseAddress, config.DatabasePort, config.DatabaseName)
|
||||
fmt.Printf("Log Level: %s\n", config.LogLevel)
|
||||
fmt.Printf("World Servers: %d configured\n", len(config.WorldServers))
|
||||
fmt.Println("================================================================================")
|
||||
|
@ -1,59 +0,0 @@
|
||||
// Package achievements provides a complete achievement system for EQ2Emulator servers.
|
||||
//
|
||||
// Features:
|
||||
// - Achievement definitions with requirements and rewards
|
||||
// - Thread-safe master achievement list for server-wide management
|
||||
// - Player-specific achievement tracking and progress
|
||||
// - Database operations with both SQLite and MySQL support
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Create database connection
|
||||
// db, _ := database.NewSQLite("world.db")
|
||||
// // db, _ := database.NewMySQL("user:pass@tcp(host:port)/dbname")
|
||||
//
|
||||
// // Create new achievement
|
||||
// achievement := achievements.New(db)
|
||||
// achievement.AchievementID = 1001
|
||||
// achievement.Title = "Monster Slayer"
|
||||
// achievement.Category = "Combat"
|
||||
// achievement.PointValue = 50
|
||||
//
|
||||
// // Add requirements and rewards
|
||||
// achievement.AddRequirement("kill_monsters", 100)
|
||||
// achievement.AddReward("experience:5000")
|
||||
//
|
||||
// // Save to database (insert or update automatically)
|
||||
// achievement.Save()
|
||||
//
|
||||
// // Load achievement by ID
|
||||
// loaded, _ := achievements.Load(db, 1001)
|
||||
//
|
||||
// // Update and save
|
||||
// loaded.Title = "Master Monster Slayer"
|
||||
// loaded.Save()
|
||||
//
|
||||
// // Delete achievement
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Master List Management:
|
||||
//
|
||||
// // Create master list for server-wide achievement management
|
||||
// masterList := achievements.NewMasterList()
|
||||
//
|
||||
// // Load all achievements from database
|
||||
// allAchievements, _ := achievements.LoadAll(db)
|
||||
// for _, ach := range allAchievements {
|
||||
// masterList.AddAchievement(ach)
|
||||
// }
|
||||
//
|
||||
// // Get achievements by category
|
||||
// combatAchievements := masterList.GetAchievementsByCategory("Combat")
|
||||
//
|
||||
// Player Progress Management:
|
||||
//
|
||||
// // Player achievement management
|
||||
// playerMgr := achievements.NewPlayerManager()
|
||||
// achievements.LoadPlayerAchievements(db, playerID, playerMgr.Achievements)
|
||||
// achievements.LoadPlayerAchievementUpdates(db, playerID, playerMgr.Updates)
|
||||
package achievements
|
@ -1,80 +0,0 @@
|
||||
// Package alt_advancement provides a complete alternate advancement system for EQ2Go servers.
|
||||
//
|
||||
// Features:
|
||||
// - Alternate advancement definitions with requirements and rewards
|
||||
// - Thread-safe master AA list for server-wide management
|
||||
// - Player-specific AA tracking and progression
|
||||
// - Database operations with internal database wrapper
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Create database connection
|
||||
// db, _ := database.NewSQLite("world.db")
|
||||
//
|
||||
// // Create new alternate advancement
|
||||
// aa := alt_advancement.New(db)
|
||||
// aa.NodeID = 1001
|
||||
// aa.Name = "Dragon's Strength"
|
||||
// aa.Group = alt_advancement.AA_CLASS
|
||||
// aa.RankCost = 1
|
||||
// aa.MaxRank = 5
|
||||
//
|
||||
// // Save to database (insert or update automatically)
|
||||
// aa.Save()
|
||||
//
|
||||
// // Load alternate advancement by node ID
|
||||
// loaded, _ := alt_advancement.Load(db, 1001)
|
||||
//
|
||||
// // Update and save
|
||||
// loaded.Name = "Improved Dragon's Strength"
|
||||
// loaded.Save()
|
||||
//
|
||||
// // Delete alternate advancement
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Master List Management:
|
||||
//
|
||||
// // Create master list for server-wide AA management
|
||||
// masterList := alt_advancement.NewMasterList()
|
||||
//
|
||||
// // Load all AAs from database
|
||||
// allAAs, _ := alt_advancement.LoadAll(db)
|
||||
// for _, aa := range allAAs {
|
||||
// masterList.AddAltAdvancement(aa)
|
||||
// }
|
||||
//
|
||||
// // Get AAs by group/tab
|
||||
// classAAs := masterList.GetAltAdvancementsByGroup(alt_advancement.AA_CLASS)
|
||||
//
|
||||
// // Get AAs by class requirement
|
||||
// fighterAAs := masterList.GetAltAdvancementsByClass(1) // Fighter class
|
||||
//
|
||||
// // Get AAs available at specific level
|
||||
// level20AAs := masterList.GetAltAdvancementsByLevel(20)
|
||||
//
|
||||
// Player AA Management:
|
||||
//
|
||||
// // Create player AA state
|
||||
// playerState := alt_advancement.NewAAPlayerState(characterID)
|
||||
//
|
||||
// // Award AA points
|
||||
// playerState.TotalPoints = 50
|
||||
// playerState.AvailablePoints = 25
|
||||
//
|
||||
// // Track AA progress
|
||||
// progress := &alt_advancement.PlayerAAData{
|
||||
// CharacterID: characterID,
|
||||
// NodeID: 1001,
|
||||
// CurrentRank: 3,
|
||||
// PointsSpent: 3,
|
||||
// }
|
||||
// playerState.AAProgress[1001] = progress
|
||||
//
|
||||
// Thread Safety:
|
||||
//
|
||||
// All operations are thread-safe using the generic MasterList base:
|
||||
// - Read-heavy operations use RWMutex for optimal performance
|
||||
// - Atomic operations for simple counters and flags
|
||||
// - Proper lock ordering to prevent deadlocks
|
||||
// - Background processing with goroutines and channels
|
||||
package alt_advancement
|
@ -1,45 +0,0 @@
|
||||
// Package appearances provides appearance management for the EverQuest II server emulator.
|
||||
//
|
||||
// Appearances define the visual characteristics of entities in the game world, including
|
||||
// player character customization options, NPC appearances, and visual variations.
|
||||
// The system supports client version compatibility to ensure proper rendering across
|
||||
// different EQ2 client versions.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// db, _ := database.NewSQLite("appearances.db")
|
||||
//
|
||||
// // Create new appearance
|
||||
// appearance := appearances.New(db)
|
||||
// appearance.ID = 1001
|
||||
// appearance.Name = "Human Male"
|
||||
// appearance.MinClient = 1096
|
||||
// appearance.Save()
|
||||
//
|
||||
// // Load existing appearance
|
||||
// loaded, _ := appearances.Load(db, 1001)
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Master List:
|
||||
//
|
||||
// masterList := appearances.NewMasterList()
|
||||
// masterList.LoadAllAppearances(db)
|
||||
// masterList.AddAppearance(appearance)
|
||||
//
|
||||
// // Find appearances
|
||||
// found := masterList.GetAppearance(1001)
|
||||
// compatible := masterList.GetCompatibleAppearances(1096)
|
||||
// humans := masterList.FindAppearancesByName("Human")
|
||||
//
|
||||
// Appearance Types:
|
||||
//
|
||||
// // Get appearance type from name
|
||||
// hairType := appearances.GetAppearanceType("hair_color1")
|
||||
//
|
||||
// // Get name from type constant
|
||||
// typeName := appearances.GetAppearanceTypeName(appearances.AppearanceHairColor1)
|
||||
//
|
||||
// The package includes all appearance type constants from the original EQ2EMu
|
||||
// implementation, supporting hair colors, skin tones, facial features, clothing,
|
||||
// and body customization options for both standard and SOGA character models.
|
||||
package appearances
|
@ -1,63 +0,0 @@
|
||||
// Package chat provides chat channel management for the EverQuest II server emulator.
|
||||
//
|
||||
// The chat system provides comprehensive channel-based communication with membership,
|
||||
// access control, and message routing capabilities. It supports both persistent world
|
||||
// channels (loaded from database) and temporary custom channels (created by players).
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// db, _ := database.NewSQLite("chat.db")
|
||||
//
|
||||
// // Create new channel
|
||||
// channel := chat.New(db)
|
||||
// channel.ID = 1001
|
||||
// channel.Name = "Auction"
|
||||
// channel.ChannelType = chat.ChannelTypeWorld
|
||||
// channel.Save()
|
||||
//
|
||||
// // Load existing channel
|
||||
// loaded, _ := chat.Load(db, 1001)
|
||||
// loaded.Delete()
|
||||
//
|
||||
// // Load by name
|
||||
// auction, _ := chat.LoadByName(db, "Auction")
|
||||
//
|
||||
// Master List:
|
||||
//
|
||||
// masterList := chat.NewMasterList()
|
||||
// masterList.LoadAllChannels(db)
|
||||
// masterList.AddChannel(channel)
|
||||
//
|
||||
// // Find channels
|
||||
// found := masterList.GetChannel(1001)
|
||||
// byName := masterList.GetChannelByName("Auction")
|
||||
// worldChannels := masterList.GetWorldChannels()
|
||||
// activeChannels := masterList.GetActiveChannels()
|
||||
//
|
||||
// Channel Management:
|
||||
//
|
||||
// // Channel membership
|
||||
// err := channel.JoinChannel(characterID)
|
||||
// inChannel := channel.IsInChannel(characterID)
|
||||
// members := channel.GetMembers()
|
||||
// err = channel.LeaveChannel(characterID)
|
||||
//
|
||||
// // Access control
|
||||
// channel.SetLevelRestriction(10)
|
||||
// channel.SetRacesAllowed(raceFlags)
|
||||
// channel.SetPassword("secret")
|
||||
// canJoin := channel.CanJoinChannelByLevel(playerLevel)
|
||||
//
|
||||
// Channel Types:
|
||||
//
|
||||
// // World channels - persistent, loaded from database
|
||||
// channel.ChannelType = chat.ChannelTypeWorld
|
||||
//
|
||||
// // Custom channels - temporary, created by players
|
||||
// channel.ChannelType = chat.ChannelTypeCustom
|
||||
//
|
||||
// The package includes comprehensive access control with level, race, and class
|
||||
// restrictions using bitmask filtering, optional password protection, and
|
||||
// integration interfaces for client communication, player management, and
|
||||
// multilingual chat processing.
|
||||
package chat
|
@ -1,47 +0,0 @@
|
||||
// Package classes provides EverQuest II class definitions and lookup functions.
|
||||
//
|
||||
// This package manages all adventure and tradeskill class information including
|
||||
// class IDs, names, hierarchies, and relationships. It provides static lookups
|
||||
// for class data without requiring database access.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Get class ID from name
|
||||
// classID := classes.GetClassID("WARRIOR")
|
||||
//
|
||||
// // Get class display name
|
||||
// name := classes.GetClassNameCase(classes.ClassWarrior)
|
||||
//
|
||||
// // Check class hierarchy
|
||||
// baseClass := classes.GetBaseClass(classes.ClassGuardian) // Returns ClassFighter
|
||||
// secondary := classes.GetSecondaryBaseClass(classes.ClassGuardian) // Returns ClassWarrior
|
||||
//
|
||||
// Class Hierarchy:
|
||||
//
|
||||
// Fighter -> Warrior -> Guardian/Berserker
|
||||
// -> Brawler -> Monk/Bruiser
|
||||
// -> Crusader -> Shadowknight/Paladin
|
||||
//
|
||||
// Priest -> Cleric -> Templar/Inquisitor
|
||||
// -> Druid -> Warden/Fury
|
||||
// -> Shaman -> Mystic/Defiler
|
||||
//
|
||||
// Mage -> Sorcerer -> Wizard/Warlock
|
||||
// -> Enchanter -> Illusionist/Coercer
|
||||
// -> Summoner -> Conjuror/Necromancer
|
||||
//
|
||||
// Scout -> Rogue -> Swashbuckler/Brigand
|
||||
// -> Bard -> Troubador/Dirge
|
||||
// -> Predator -> Ranger/Assassin
|
||||
// -> Animalist -> Beastlord
|
||||
//
|
||||
// Tradeskill Classes:
|
||||
//
|
||||
// Artisan -> Craftsman -> Provisioner
|
||||
// -> Woodworker -> Carpenter
|
||||
// -> Outfitter -> Armorer/Weaponsmith/Tailor
|
||||
// -> Scholar -> Jeweler/Sage/Alchemist
|
||||
//
|
||||
// The package includes all 58 class definitions from EverQuest II including
|
||||
// adventure classes (0-44) and tradeskill classes (45-57).
|
||||
package classes
|
@ -1,70 +0,0 @@
|
||||
// Package collections provides collection quest management for the EverQuest II server emulator.
|
||||
//
|
||||
// Collections are special quests where players gather specific items scattered throughout
|
||||
// the world. When all items are found, the collection can be turned in for rewards.
|
||||
// The system tracks both master collections available to all players and individual
|
||||
// player progress on those collections.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// db, _ := database.NewSQLite("collections.db")
|
||||
//
|
||||
// // Create new collection
|
||||
// collection := collections.New(db)
|
||||
// collection.ID = 1001
|
||||
// collection.Name = "Antonian Cameos"
|
||||
// collection.Category = "Heritage"
|
||||
// collection.Level = 20
|
||||
// collection.Save()
|
||||
//
|
||||
// // Load existing collection
|
||||
// loaded, _ := collections.Load(db, 1001)
|
||||
// loaded.Delete()
|
||||
//
|
||||
// // Add collection items
|
||||
// collection.CollectionItems = append(collection.CollectionItems, collections.CollectionItem{
|
||||
// ItemID: 12345,
|
||||
// Index: 0,
|
||||
// Found: collections.ItemNotFound,
|
||||
// })
|
||||
//
|
||||
// Master List Management:
|
||||
//
|
||||
// masterList := collections.NewMasterList()
|
||||
// masterList.LoadAllCollections(db)
|
||||
// masterList.AddCollection(collection)
|
||||
//
|
||||
// // Find collections
|
||||
// found := masterList.GetCollection(1001)
|
||||
// heritage := masterList.FindCollectionsByCategory("Heritage")
|
||||
// needsItem := masterList.GetCollectionsNeedingItem(12345)
|
||||
//
|
||||
// Player Collection Management:
|
||||
//
|
||||
// playerList := collections.NewPlayerList(characterID, db)
|
||||
// playerList.LoadPlayerCollections(masterList)
|
||||
//
|
||||
// // Check if player needs an item
|
||||
// if playerList.NeedsItem(12345) {
|
||||
// // Award the item to collections that need it
|
||||
// }
|
||||
//
|
||||
// // Find collections ready to turn in
|
||||
// readyCollections := playerList.GetCollectionsToHandIn()
|
||||
//
|
||||
// Collection Features:
|
||||
//
|
||||
// // Check collection progress
|
||||
// progress := collection.GetProgress() // Returns percentage 0-100
|
||||
// ready := collection.GetIsReadyToTurnIn()
|
||||
//
|
||||
// // Mark items as found
|
||||
// found := collection.MarkItemFound(itemID)
|
||||
//
|
||||
// // Clone collections for players
|
||||
// playerCollection := masterCollection.Clone()
|
||||
//
|
||||
// The package supports both master collections (defined by game data) and player-specific
|
||||
// collection instances with individual progress tracking. Collections can have multiple
|
||||
// reward types including items, coin, and experience points.
|
||||
package collections
|
@ -1,41 +0,0 @@
|
||||
// Package entity provides the core combat and magic systems for EverQuest II server emulation.
|
||||
// It extends the base Spawn system with combat capabilities, spell effects, and character statistics management.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// entity := entity.NewEntity()
|
||||
// entity.GetInfoStruct().SetName("TestEntity")
|
||||
// entity.GetInfoStruct().SetLevel(50)
|
||||
// entity.GetInfoStruct().SetStr(100.0)
|
||||
//
|
||||
// Managing Spell Effects:
|
||||
//
|
||||
// // Add a maintained spell (buff)
|
||||
// success := entity.AddMaintainedSpell("Heroic Strength", 12345, 300.0, 2)
|
||||
//
|
||||
// // Add a temporary effect
|
||||
// entity.AddSpellEffect(54321, casterID, 60.0)
|
||||
//
|
||||
// // Add a detrimental effect
|
||||
// entity.AddDetrimentalSpell(99999, attackerID, 30.0, 1)
|
||||
//
|
||||
// Stat Calculations:
|
||||
//
|
||||
// // Get effective stats (base + bonuses)
|
||||
// str := entity.GetStr()
|
||||
// sta := entity.GetSta()
|
||||
// primary := entity.GetPrimaryStat()
|
||||
//
|
||||
// // Recalculate all bonuses
|
||||
// entity.CalculateBonuses()
|
||||
//
|
||||
// Pet Management:
|
||||
//
|
||||
// // Set a summon pet
|
||||
// entity.SetPet(petEntity)
|
||||
//
|
||||
// // Check pet status
|
||||
// if entity.GetPet() != nil && !entity.IsPetDismissing() {
|
||||
// // Pet is active
|
||||
// }
|
||||
package entity
|
@ -1,53 +0,0 @@
|
||||
// Package factions provides comprehensive faction management for the EQ2 server emulator.
|
||||
//
|
||||
// The faction system manages relationships between players, NPCs, and various game entities.
|
||||
// It includes consideration levels (con), hostile/friendly relationships, and dynamic faction
|
||||
// value changes based on player actions.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Create a new faction
|
||||
// faction := factions.New(db)
|
||||
// faction.ID = 1001
|
||||
// faction.Name = "Guards of Qeynos"
|
||||
// faction.Type = "City"
|
||||
// faction.Description = "The brave guards protecting Qeynos"
|
||||
// faction.DefaultValue = 0
|
||||
// faction.Save()
|
||||
//
|
||||
// // Load an existing faction
|
||||
// loaded, _ := factions.Load(db, 1001)
|
||||
// loaded.PositiveChange = 100
|
||||
// loaded.Save()
|
||||
//
|
||||
// // Delete a faction
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Master List Management:
|
||||
//
|
||||
// masterList := factions.NewMasterList()
|
||||
// masterList.AddFaction(faction)
|
||||
//
|
||||
// // Lookup by ID or name
|
||||
// found := masterList.GetFaction(1001)
|
||||
// byName := masterList.GetFactionByName("Guards of Qeynos")
|
||||
//
|
||||
// // Add relationships
|
||||
// masterList.AddHostileFaction(1001, 1002) // Guards hate bandits
|
||||
// masterList.AddFriendlyFaction(1001, 1003) // Guards like merchants
|
||||
//
|
||||
// Player Faction System:
|
||||
//
|
||||
// playerFaction := factions.NewPlayerFaction(masterList)
|
||||
// playerFaction.IncreaseFaction(1001, 100) // Gain faction
|
||||
// playerFaction.DecreaseFaction(1002, 50) // Lose faction
|
||||
//
|
||||
// // Check consideration level (-4 to +4)
|
||||
// con := playerFaction.GetCon(1001)
|
||||
// if con <= factions.AttackThreshold {
|
||||
// // Player is KOS to this faction
|
||||
// }
|
||||
//
|
||||
// The system integrates with the broader EQ2 server architecture including database
|
||||
// persistence, client packet updates, quest prerequisites, and NPC behavior.
|
||||
package factions
|
@ -1,38 +0,0 @@
|
||||
// Package ground_spawn provides harvestable resource node management for EQ2.
|
||||
//
|
||||
// Ground spawns are harvestable nodes in the game world that provide resources
|
||||
// to players through skills like Mining, Gathering, Fishing, etc. This package
|
||||
// implements the complete harvest system including skill checks, item rewards,
|
||||
// rarity determination, and respawn management.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// gs := ground_spawn.New(db)
|
||||
// gs.GroundSpawnID = 1001
|
||||
// gs.Name = "Iron Ore Node"
|
||||
// gs.CollectionSkill = "Mining"
|
||||
// gs.NumberHarvests = 5
|
||||
// gs.X, gs.Y, gs.Z = 100.0, 50.0, 200.0
|
||||
// gs.ZoneID = 1
|
||||
// gs.Save()
|
||||
//
|
||||
// loaded, _ := ground_spawn.Load(db, 1001)
|
||||
// result, _ := loaded.ProcessHarvest(player, skill, totalSkill)
|
||||
//
|
||||
// Master List:
|
||||
//
|
||||
// masterList := ground_spawn.NewMasterList()
|
||||
// masterList.LoadFromDatabase(db)
|
||||
// masterList.AddGroundSpawn(gs)
|
||||
//
|
||||
// zoneSpawns := masterList.GetByZone(1)
|
||||
// availableSpawns := masterList.GetAvailableSpawns()
|
||||
//
|
||||
// The harvest algorithm preserves the complete C++ EQ2EMU logic including:
|
||||
// - Skill-based table selection
|
||||
// - Adventure level requirements for bonus tables
|
||||
// - Collection vs normal harvesting modes
|
||||
// - Rarity determination (normal, rare, imbue, 10+rare)
|
||||
// - Grid-based item filtering
|
||||
// - Proper random number generation matching C++ behavior
|
||||
package ground_spawn
|
@ -1,86 +0,0 @@
|
||||
// Package guilds provides comprehensive guild management for EverQuest II server emulation.
|
||||
//
|
||||
// This package implements a modernized guild system with embedded database operations,
|
||||
// optimized master list, and complete MySQL/SQLite support through the internal database wrapper.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Create a new guild
|
||||
// guild := guilds.New(db)
|
||||
// guild.SetName("Dragon Slayers", true)
|
||||
// guild.SetMOTD("Welcome to the guild!", true)
|
||||
// guild.Save()
|
||||
//
|
||||
// // Load existing guild
|
||||
// loaded, err := guilds.Load(db, 1001)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Bespoke Master List (optimized for performance):
|
||||
//
|
||||
// // Create master list and load all guilds
|
||||
// masterList := guilds.NewMasterList()
|
||||
// masterList.LoadFromDatabase(db)
|
||||
//
|
||||
// // O(1) lookups by ID
|
||||
// guild := masterList.Get(1001)
|
||||
//
|
||||
// // O(1) lookups by name (case-insensitive)
|
||||
// guild = masterList.GetByName("Dragon Slayers")
|
||||
//
|
||||
// // O(1) lookups by level
|
||||
// level50Guilds := masterList.GetByLevel(50)
|
||||
//
|
||||
// // O(1) recruiting guild lookups
|
||||
// recruitingGuilds := masterList.GetRecruiting()
|
||||
//
|
||||
// // Optimized range queries
|
||||
// midLevelGuilds := masterList.GetByLevelRange(25, 75)
|
||||
//
|
||||
// // Efficient set intersection queries
|
||||
// recruitingLevel50 := masterList.GetRecruitingByLevel(50)
|
||||
//
|
||||
// Advanced Search:
|
||||
//
|
||||
// criteria := guilds.GuildSearchCriteria{
|
||||
// NamePattern: "Dragon",
|
||||
// MinLevel: 20,
|
||||
// MaxLevel: 60,
|
||||
// RecruitingOnly: true,
|
||||
// PlayStyle: guilds.RecruitingPlayStyleCasual,
|
||||
// RequiredFlags: []int8{guilds.RecruitingFlagFighters},
|
||||
// ExcludedDescTags: []int8{guilds.RecruitingDescTagHardcore},
|
||||
// }
|
||||
// results := masterList.Search(criteria)
|
||||
//
|
||||
// Performance Characteristics:
|
||||
//
|
||||
// - Guild creation/loading: <100ns per operation
|
||||
// - ID lookups: <50ns per operation (O(1) map access)
|
||||
// - Name lookups: <50ns per operation (O(1) case-insensitive)
|
||||
// - Level lookups: <100ns per operation (O(1) indexed)
|
||||
// - Recruiting lookups: <100ns per operation (O(1) cached)
|
||||
// - Range queries: <5µs per operation (optimized iteration)
|
||||
// - Search with criteria: <10µs per operation (specialized indices)
|
||||
// - Statistics generation: <50µs per operation (lazy caching)
|
||||
//
|
||||
// Thread Safety:
|
||||
//
|
||||
// All operations are thread-safe using optimized RWMutex patterns with minimal lock contention.
|
||||
// Read operations use shared locks while modifications use exclusive locks.
|
||||
//
|
||||
// Database Support:
|
||||
//
|
||||
// Full MySQL and SQLite support through the internal database wrapper:
|
||||
//
|
||||
// // SQLite
|
||||
// db, _ := database.NewSQLite("guilds.db")
|
||||
//
|
||||
// // MySQL
|
||||
// db, _ := database.NewMySQL("user:pass@tcp(localhost:3306)/eq2")
|
||||
//
|
||||
// The implementation uses the internal database wrapper which handles both database types
|
||||
// transparently using database/sql-compatible methods.
|
||||
package guilds
|
@ -1,197 +0,0 @@
|
||||
// Package heroic_ops provides comprehensive heroic opportunity management for EverQuest II server emulation.
|
||||
//
|
||||
// This package implements a complete heroic opportunity system with embedded database operations,
|
||||
// optimized master list, and full MySQL/SQLite support through the internal database wrapper.
|
||||
//
|
||||
// Basic Usage:
|
||||
//
|
||||
// // Create a new heroic opportunity starter
|
||||
// starter := heroic_ops.NewHeroicOPStarter(db)
|
||||
// starter.SetID(1001)
|
||||
// starter.SetStartClass(heroic_ops.ClassAny)
|
||||
// starter.SetName("Epic Chain Starter")
|
||||
// starter.SetAbility(0, 1) // Melee ability
|
||||
// starter.SetAbility(1, 2) // Spell ability
|
||||
// starter.Save()
|
||||
//
|
||||
// // Load existing starter
|
||||
// loaded, err := heroic_ops.LoadHeroicOPStarter(db, 1001)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// loaded.Delete()
|
||||
//
|
||||
// Heroic Opportunity Wheels:
|
||||
//
|
||||
// // Create a wheel linked to a starter
|
||||
// wheel := heroic_ops.NewHeroicOPWheel(db)
|
||||
// wheel.SetID(2001)
|
||||
// wheel.SetStarterLinkID(1001)
|
||||
// wheel.SetSpellID(5000)
|
||||
// wheel.SetChance(75.0)
|
||||
// wheel.SetAbility(0, 1) // First ability required
|
||||
// wheel.SetAbility(1, 3) // Second ability required
|
||||
// wheel.Save()
|
||||
//
|
||||
// Bespoke Master List (optimized for performance):
|
||||
//
|
||||
// // Create master list and load all heroic opportunities
|
||||
// masterList := heroic_ops.NewMasterList()
|
||||
// masterList.LoadFromDatabase(db)
|
||||
//
|
||||
// // O(1) lookups by ID
|
||||
// starter := masterList.GetStarter(1001)
|
||||
// wheel := masterList.GetWheel(2001)
|
||||
//
|
||||
// // O(1) lookups by class
|
||||
// classStarters := masterList.GetStartersForClass(heroic_ops.ClassAny)
|
||||
//
|
||||
// // O(1) lookups by starter ID
|
||||
// starterWheels := masterList.GetWheelsForStarter(1001)
|
||||
//
|
||||
// // O(1) random wheel selection with weighted chances
|
||||
// randomWheel := masterList.SelectRandomWheel(1001)
|
||||
//
|
||||
// // O(1) specialized queries
|
||||
// orderedWheels := masterList.GetOrderedWheels()
|
||||
// shiftWheels := masterList.GetShiftWheels()
|
||||
//
|
||||
// Active Heroic Opportunity Instances:
|
||||
//
|
||||
// // Create active HO instance
|
||||
// ho := heroic_ops.NewHeroicOP(db, 12345, encounterID)
|
||||
// ho.StartStarterChain([]int32{1001, 1002})
|
||||
//
|
||||
// // Process abilities during starter chain
|
||||
// success := ho.ProcessStarterAbility(1, masterList) // Melee ability
|
||||
// if success {
|
||||
// // Transition to wheel phase
|
||||
// wheel := masterList.SelectRandomWheel(ho.StarterID)
|
||||
// ho.StartWheelPhase(wheel, 10) // 10 second timer
|
||||
// }
|
||||
//
|
||||
// // Process abilities during wheel phase
|
||||
// success = ho.ProcessWheelAbility(1, characterID, wheel)
|
||||
// if ho.IsComplete() {
|
||||
// // HO completed successfully
|
||||
// data := ho.GetPacketData(wheel)
|
||||
// // Send completion packets to clients
|
||||
// }
|
||||
//
|
||||
// Advanced Search:
|
||||
//
|
||||
// criteria := heroic_ops.HeroicOPSearchCriteria{
|
||||
// StarterClass: heroic_ops.ClassAny,
|
||||
// SpellID: 5000,
|
||||
// MinChance: 50.0,
|
||||
// MaxChance: 100.0,
|
||||
// NamePattern: "Epic",
|
||||
// HasShift: true,
|
||||
// IsOrdered: false,
|
||||
// }
|
||||
// results := masterList.Search(criteria)
|
||||
//
|
||||
// Performance Characteristics:
|
||||
//
|
||||
// - Starter creation/loading: <100ns per operation
|
||||
// - Wheel creation/loading: <150ns per operation
|
||||
// - ID lookups: <50ns per operation (O(1) map access)
|
||||
// - Class lookups: <100ns per operation (O(1) indexed)
|
||||
// - Starter wheel lookups: <100ns per operation (O(1) cached)
|
||||
// - Random selection: <200ns per operation (optimized weighted selection)
|
||||
// - Specialized queries: <500ns per operation (pre-indexed)
|
||||
// - Search with criteria: <2µs per operation (multi-index optimization)
|
||||
// - Statistics generation: <50µs per operation (lazy caching)
|
||||
// - HO instance operations: <300ns per operation (in-memory + database)
|
||||
//
|
||||
// Thread Safety:
|
||||
//
|
||||
// All operations are thread-safe using optimized RWMutex patterns with minimal lock contention.
|
||||
// Read operations use shared locks while modifications use exclusive locks.
|
||||
// Concurrent HO processing is fully supported.
|
||||
//
|
||||
// Database Support:
|
||||
//
|
||||
// Complete MySQL and SQLite support through the internal database wrapper:
|
||||
//
|
||||
// // SQLite
|
||||
// db, _ := database.NewSQLite("heroic_ops.db")
|
||||
//
|
||||
// // MySQL
|
||||
// db, _ := database.NewMySQL("user:pass@tcp(localhost:3306)/eq2")
|
||||
//
|
||||
// The implementation uses the internal database wrapper which handles both database types
|
||||
// transparently using database/sql-compatible methods.
|
||||
//
|
||||
// Heroic Opportunity System Architecture:
|
||||
//
|
||||
// The system consists of three main components:
|
||||
//
|
||||
// 1. **Starters**: Define the initial ability chain that players must complete to trigger a wheel phase.
|
||||
// Each starter specifies which class can initiate it and the sequence of abilities required.
|
||||
//
|
||||
// 2. **Wheels**: Define the collaborative phase where multiple players contribute abilities to complete
|
||||
// the heroic opportunity. Wheels can be ordered (sequential) or unordered (any order).
|
||||
//
|
||||
// 3. **Instances**: Active heroic opportunity sessions that track progress, participants, and state
|
||||
// transitions from starter chain through wheel completion.
|
||||
//
|
||||
// Key Features:
|
||||
//
|
||||
// - **Class Restrictions**: Starters can be limited to specific classes or open to any class
|
||||
// - **Weighted Selection**: Wheels have chance values for probabilistic selection
|
||||
// - **Shift Abilities**: Special abilities that can change the wheel type during execution
|
||||
// - **Ordered/Unordered**: Wheels can require abilities in sequence or allow any order
|
||||
// - **Timer Management**: Configurable timers for both starter chains and wheel phases
|
||||
// - **Event System**: Comprehensive event tracking for statistics and logging
|
||||
// - **Spell Integration**: Completed HOs cast spells with full spell system integration
|
||||
//
|
||||
// Configuration Constants:
|
||||
//
|
||||
// The system provides extensive configuration through constants in constants.go:
|
||||
//
|
||||
// heroic_ops.MaxAbilities // Maximum abilities per starter/wheel (6)
|
||||
// heroic_ops.DefaultWheelTimerSeconds // Default wheel timer (10s)
|
||||
// heroic_ops.MaxConcurrentHOs // Max concurrent HOs per encounter (3)
|
||||
// heroic_ops.ClassAny // Universal class restriction (0)
|
||||
//
|
||||
// Error Handling:
|
||||
//
|
||||
// The package provides detailed error messages for all failure conditions:
|
||||
//
|
||||
// ErrHONotFound // HO instance not found
|
||||
// ErrHOInvalidState // Invalid state transition
|
||||
// ErrHOAbilityNotAllowed // Ability not valid for current context
|
||||
// ErrHOTimerExpired // Timer has expired
|
||||
// ErrHOAlreadyComplete // HO already completed
|
||||
//
|
||||
// Integration Interfaces:
|
||||
//
|
||||
// The package defines comprehensive interfaces for integration with other game systems:
|
||||
//
|
||||
// HeroicOPEventHandler // Event notifications
|
||||
// ClientManager // Packet sending
|
||||
// EncounterManager // Encounter integration
|
||||
// PlayerManager // Player system integration
|
||||
// SpellManager // Spell casting
|
||||
// TimerManager // Timer management
|
||||
//
|
||||
// Statistics and Analytics:
|
||||
//
|
||||
// Built-in statistics collection provides insights into system usage:
|
||||
//
|
||||
// stats := masterList.GetStatistics()
|
||||
// fmt.Printf("Total Starters: %d\n", stats.TotalStarters)
|
||||
// fmt.Printf("Total Wheels: %d\n", stats.TotalWheels)
|
||||
// fmt.Printf("Average Chance: %.2f\n", stats.AverageChance)
|
||||
// fmt.Printf("Class Distribution: %+v\n", stats.ClassDistribution)
|
||||
//
|
||||
// Advanced Features:
|
||||
//
|
||||
// - **Validation**: Comprehensive validation of all data integrity
|
||||
// - **Caching**: Lazy metadata caching with automatic invalidation
|
||||
// - **Indexing**: Multi-dimensional indexing for optimal query performance
|
||||
// - **Concurrent Safety**: Full thread safety with minimal contention
|
||||
// - **Memory Efficiency**: Optimized data structures and object pooling
|
||||
//
|
||||
package heroic_ops
|
@ -2,6 +2,7 @@ package login
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -22,8 +23,11 @@ type ServerConfig struct {
|
||||
WebPassword string `json:"web_password"`
|
||||
|
||||
// Database settings
|
||||
DatabaseType string `json:"database_type"` // "sqlite" or "mysql"
|
||||
DatabaseDSN string `json:"database_dsn"` // Connection string
|
||||
DatabaseAddress string `json:"database_address"` // MySQL server address
|
||||
DatabasePort int `json:"database_port"` // MySQL server port
|
||||
DatabaseUsername string `json:"database_username"` // MySQL username
|
||||
DatabasePassword string `json:"database_password"` // MySQL password
|
||||
DatabaseName string `json:"database_name"` // MySQL database name
|
||||
|
||||
// Server settings
|
||||
ServerName string `json:"server_name"`
|
||||
@ -69,20 +73,24 @@ func (c *ServerConfig) Validate() error {
|
||||
}
|
||||
|
||||
// Database configuration validation
|
||||
dbType := strings.ToLower(c.DatabaseType)
|
||||
switch dbType {
|
||||
case "sqlite", "":
|
||||
c.DatabaseType = "sqlite"
|
||||
if c.DatabaseDSN == "" {
|
||||
return fmt.Errorf("database_dsn is required")
|
||||
}
|
||||
case "mysql":
|
||||
c.DatabaseType = "mysql"
|
||||
if c.DatabaseDSN == "" {
|
||||
return fmt.Errorf("database_dsn is required")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid database_type: %s (must be sqlite or mysql)", c.DatabaseType)
|
||||
if c.DatabaseAddress == "" {
|
||||
c.DatabaseAddress = "localhost" // Default to localhost
|
||||
}
|
||||
|
||||
if c.DatabasePort <= 0 {
|
||||
c.DatabasePort = 3306 // Default MySQL port
|
||||
}
|
||||
|
||||
if c.DatabaseUsername == "" {
|
||||
return fmt.Errorf("database_username is required")
|
||||
}
|
||||
|
||||
if c.DatabasePassword == "" {
|
||||
return fmt.Errorf("database_password is required")
|
||||
}
|
||||
|
||||
if c.DatabaseName == "" {
|
||||
return fmt.Errorf("database_name is required")
|
||||
}
|
||||
|
||||
if c.ServerName == "" {
|
||||
@ -129,6 +137,17 @@ func (c *ServerConfig) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildDatabaseDSN builds a MySQL DSN string from the database configuration
|
||||
func (c *ServerConfig) BuildDatabaseDSN() string {
|
||||
// Format: username:password@tcp(address:port)/database?parseTime=true&charset=utf8mb4
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&charset=utf8mb4",
|
||||
url.QueryEscape(c.DatabaseUsername),
|
||||
url.QueryEscape(c.DatabasePassword),
|
||||
c.DatabaseAddress,
|
||||
c.DatabasePort,
|
||||
c.DatabaseName)
|
||||
}
|
||||
|
||||
// Validate validates a world server configuration
|
||||
func (w *WorldServerInfo) Validate() error {
|
||||
if w.ID <= 0 {
|
||||
|
@ -40,7 +40,8 @@ func NewServer(config *ServerConfig) (*Server, error) {
|
||||
}
|
||||
|
||||
// Create database connection
|
||||
db, err := NewLoginDB(config.DatabaseDSN)
|
||||
dsn := config.BuildDatabaseDSN()
|
||||
db, err := NewLoginDB(dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize database: %w", err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user