EQ2Go Command System
The EQ2Go command system provides a comprehensive framework for handling player commands, admin commands, and console commands in the EverQuest II server emulator. This system is converted from the original C++ EQ2EMu codebase while leveraging modern Go patterns and practices.
Architecture Overview
The command system consists of several key components:
- CommandManager: Central registry for all commands with thread-safe operations
- CommandContext: Execution context providing access to players, zones, arguments, and messaging
- Command Types: Different command categories (Player, Admin, Console, etc.)
- Subcommand Support: Hierarchical command structure for complex operations
Core Components
CommandManager
The CommandManager
is the central hub that:
- Registers commands by name with case-insensitive lookup
- Manages subcommands under parent commands
- Executes commands with proper admin level checking
- Parses raw command strings into structured contexts
CommandContext
The CommandContext
provides:
- Type-safe argument parsing (string, int, float, bool)
- Message queuing with channel and color support
- Validation helpers for argument counts and requirements
- Access to game objects (player, zone, target)
- Result storage for complex operations
Command Types
Commands are categorized by type:
- Player Commands (
CommandTypePlayer
): Basic player actions like/say
,/tell
,/who
- Admin Commands (
CommandTypeAdmin
): GM/Admin tools like/kick
,/ban
,/summon
- Console Commands (
CommandTypeConsole
): Server management from console - Specialized Types: Spawn, Zone, Guild, Item, and Quest commands
Admin Levels
The system supports four admin levels:
- Player (0): Basic player commands
- Guide (100): Helper/guide commands
- GM (200): Game master commands
- Admin (300): Full administrative access
Usage Examples
Basic Command Registration
// Initialize the command system
cm, err := InitializeCommands()
if err != nil {
log.Fatalf("Failed to initialize commands: %v", err)
}
// Register a custom command
customCmd := &Command{
Name: "custom",
Type: CommandTypePlayer,
Description: "A custom command",
Usage: "/custom <arg>",
RequiredLevel: AdminLevelPlayer,
Handler: func(ctx *CommandContext) error {
if err := ctx.ValidateArgumentCount(1, 1); err != nil {
ctx.AddErrorMessage("Usage: /custom <arg>")
return nil
}
arg := ctx.Arguments[0]
ctx.AddStatusMessage(fmt.Sprintf("You used custom command with: %s", arg))
return nil
},
}
err = cm.Register(customCmd)
if err != nil {
log.Printf("Failed to register custom command: %v", err)
}
Executing Commands
// Parse and execute a command from a client
err := ExecuteCommand(cm, "/say Hello everyone!", client)
if err != nil {
log.Printf("Command execution failed: %v", err)
}
Subcommand Registration
// Register a parent command
parentCmd := &Command{
Name: "manage",
Type: CommandTypeAdmin,
RequiredLevel: AdminLevelGM,
Handler: func(ctx *CommandContext) error {
ctx.AddErrorMessage("Usage: /manage <player|item|zone> [options]")
return nil
},
}
cm.Register(parentCmd)
// Register subcommands
playerSubCmd := &Command{
Name: "player",
Type: CommandTypeAdmin,
RequiredLevel: AdminLevelGM,
Handler: func(ctx *CommandContext) error {
// Handle player management
return nil
},
}
cm.RegisterSubCommand("manage", playerSubCmd)
Command Implementation
Player Commands
Located in player.go
, these include:
- Communication:
/say
,/tell
,/yell
,/shout
,/ooc
,/emote
- Group Management:
/group
,/groupsay
,/gsay
- Guild Management:
/guild
,/guildsay
,/officersay
- Information:
/who
,/time
,/location
,/consider
- Character:
/afk
,/anon
,/lfg
,/inventory
- Utilities:
/help
,/quit
,/trade
,/quest
Admin Commands
Located in admin.go
, these include:
- Player Management:
/kick
,/ban
,/unban
,/summon
,/goto
- Communication:
/broadcast
,/announce
- Spawn Management:
/spawn
,/npc
- Item Management:
/item
,/giveitem
- Character Modification:
/modify
,/setlevel
,/setclass
- Server Management:
/reload
,/shutdown
,/cancelshutdown
- GM Utilities:
/invisible
,/invulnerable
,/speed
,/flymode
- Information:
/info
,/version
Console Commands
Located in console.go
, these include prefixed versions of admin commands for console use:
- Player Management:
console_ban
,console_kick
,console_unban
- Communication:
console_broadcast
,console_announce
,console_tell
- Information:
console_guild
,player
,console_zone
,console_who
- Server Management:
console_reload
,console_shutdown
,exit
- MOTD Management:
getmotd
,setmotd
- Rules Management:
rules
Message Channels and Colors
Commands can send messages through various channels:
Channels
ChannelSay
(8): Local say messagesChannelTell
(28): Private messagesChannelBroadcast
(92): Server-wide broadcastsChannelError
(3): Error messagesChannelStatus
(4): Status updates
Colors
ColorWhite
(254): Standard textColorRed
(3): Errors and warningsColorYellow
(5): Status messagesColorChatRelation
(4): Relationship text
Error Handling
The command system provides comprehensive error handling:
func HandleExampleCommand(ctx *CommandContext) error {
// Validate player is present
if err := ctx.RequirePlayer(); err != nil {
return err
}
// Validate argument count
if err := ctx.ValidateArgumentCount(1, 3); err != nil {
ctx.AddErrorMessage("Usage: /example <required> [optional1] [optional2]")
return nil
}
// Validate admin level
if err := ctx.RequireAdminLevel(AdminLevelGM); err != nil {
return err
}
// Command logic here...
return nil
}
Testing
The command system includes comprehensive tests in commands_test.go
:
# Run all command tests
go test ./internal/commands -v
# Run specific test
go test ./internal/commands -run TestCommandManager_Execute
Tests cover:
- Command registration and retrieval
- Argument parsing and validation
- Message handling
- Admin level checking
- Subcommand functionality
- Error conditions
Integration with Server
To integrate the command system with the server:
// Initialize commands during server startup
commandManager, err := commands.InitializeCommands()
if err != nil {
log.Fatalf("Failed to initialize commands: %v", err)
}
// Handle incoming command from client
func handleClientCommand(client ClientInterface, rawCommand string) {
err := commands.ExecuteCommand(commandManager, rawCommand, client)
if err != nil {
log.Printf("Command execution error: %v", err)
}
}
Thread Safety
The command system is designed to be thread-safe:
- CommandManager uses read/write mutexes for concurrent access
- CommandContext uses mutexes for message and result storage
- All operations are safe for concurrent use
Extension Points
The system is designed for easy extension:
Custom Command Types
const CommandTypeCustom CommandType = 100
func (ct CommandType) String() string {
switch ct {
case CommandTypeCustom:
return "custom"
default:
return CommandType(ct).String()
}
}
Custom Handlers
func MyCustomHandler(ctx *CommandContext) error {
// Custom command logic
ctx.AddStatusMessage("Custom command executed!")
return nil
}
Performance Considerations
- Commands are indexed by name in a hash map for O(1) lookup
- Case-insensitive matching uses normalized lowercase keys
- Message batching reduces client communication overhead
- Argument parsing is lazy and type-safe
Migration from C++
Key differences from the original C++ implementation:
- Type Safety: Go's type system prevents many runtime errors
- Memory Management: Automatic garbage collection eliminates memory leaks
- Concurrency: Native goroutine support for concurrent operations
- Error Handling: Explicit error returns instead of exceptions
- Testing: Built-in testing framework with comprehensive coverage
Future Enhancements
Planned improvements:
- Lua script integration for dynamic commands
- Command aliasing system
- Advanced permission system
- Command cooldowns and rate limiting
- Audit logging for admin commands
- Dynamic command loading/unloading
Troubleshooting
Common Issues
Command Not Found: Ensure command is registered and name matches exactly Insufficient Privileges: Check admin level requirements Argument Validation: Use proper argument count validation Interface Errors: Ensure client implements ClientInterface correctly
Debug Output
Enable debug output for command execution:
ctx.AddDefaultMessage(fmt.Sprintf("Debug: Command %s executed with %d args",
ctx.CommandName, ctx.ArgumentCount()))
Conclusion
The EQ2Go command system provides a robust, extensible framework for handling all types of game commands while maintaining compatibility with the original EverQuest II protocol. The system's modular design and comprehensive testing ensure reliability and ease of maintenance.