248 lines
6.6 KiB
Go
248 lines
6.6 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// NewCommandManager creates a new command manager instance
|
|
func NewCommandManager() *CommandManager {
|
|
return &CommandManager{
|
|
commands: make(map[string]*Command),
|
|
}
|
|
}
|
|
|
|
// Register registers a command with the manager
|
|
func (cm *CommandManager) Register(command *Command) error {
|
|
if command == nil {
|
|
return fmt.Errorf("command cannot be nil")
|
|
}
|
|
|
|
if command.Name == "" {
|
|
return fmt.Errorf("command name cannot be empty")
|
|
}
|
|
|
|
if command.Handler == nil {
|
|
return fmt.Errorf("command handler cannot be nil")
|
|
}
|
|
|
|
cm.mutex.Lock()
|
|
defer cm.mutex.Unlock()
|
|
|
|
// Normalize command name to lowercase for case-insensitive matching
|
|
normalizedName := strings.ToLower(command.Name)
|
|
|
|
if _, exists := cm.commands[normalizedName]; exists {
|
|
return fmt.Errorf("command '%s' is already registered", command.Name)
|
|
}
|
|
|
|
cm.commands[normalizedName] = command
|
|
return nil
|
|
}
|
|
|
|
// RegisterSubCommand registers a subcommand under a parent command
|
|
func (cm *CommandManager) RegisterSubCommand(parentName string, subCommand *Command) error {
|
|
if subCommand == nil {
|
|
return fmt.Errorf("subcommand cannot be nil")
|
|
}
|
|
|
|
cm.mutex.Lock()
|
|
defer cm.mutex.Unlock()
|
|
|
|
normalizedParent := strings.ToLower(parentName)
|
|
parentCmd, exists := cm.commands[normalizedParent]
|
|
if !exists {
|
|
return fmt.Errorf("parent command '%s' not found", parentName)
|
|
}
|
|
|
|
if parentCmd.SubCommands == nil {
|
|
parentCmd.SubCommands = make(map[string]*Command)
|
|
}
|
|
|
|
normalizedSubName := strings.ToLower(subCommand.Name)
|
|
if _, exists := parentCmd.SubCommands[normalizedSubName]; exists {
|
|
return fmt.Errorf("subcommand '%s' already exists under '%s'", subCommand.Name, parentName)
|
|
}
|
|
|
|
parentCmd.SubCommands[normalizedSubName] = subCommand
|
|
return nil
|
|
}
|
|
|
|
// Unregister removes a command from the manager
|
|
func (cm *CommandManager) Unregister(name string) {
|
|
cm.mutex.Lock()
|
|
defer cm.mutex.Unlock()
|
|
|
|
normalizedName := strings.ToLower(name)
|
|
delete(cm.commands, normalizedName)
|
|
}
|
|
|
|
// HasCommand checks if a command is registered
|
|
func (cm *CommandManager) HasCommand(name string) bool {
|
|
cm.mutex.RLock()
|
|
defer cm.mutex.RUnlock()
|
|
|
|
normalizedName := strings.ToLower(name)
|
|
_, exists := cm.commands[normalizedName]
|
|
return exists
|
|
}
|
|
|
|
// GetCommand retrieves a command by name
|
|
func (cm *CommandManager) GetCommand(name string) (*Command, bool) {
|
|
cm.mutex.RLock()
|
|
defer cm.mutex.RUnlock()
|
|
|
|
normalizedName := strings.ToLower(name)
|
|
command, exists := cm.commands[normalizedName]
|
|
return command, exists
|
|
}
|
|
|
|
// ListCommands returns all registered command names
|
|
func (cm *CommandManager) ListCommands() []string {
|
|
cm.mutex.RLock()
|
|
defer cm.mutex.RUnlock()
|
|
|
|
names := make([]string, 0, len(cm.commands))
|
|
for name := range cm.commands {
|
|
names = append(names, name)
|
|
}
|
|
return names
|
|
}
|
|
|
|
// ListCommandsByType returns commands filtered by type
|
|
func (cm *CommandManager) ListCommandsByType(commandType CommandType) []*Command {
|
|
cm.mutex.RLock()
|
|
defer cm.mutex.RUnlock()
|
|
|
|
commands := make([]*Command, 0)
|
|
for _, cmd := range cm.commands {
|
|
if cmd.Type == commandType {
|
|
commands = append(commands, cmd)
|
|
}
|
|
}
|
|
return commands
|
|
}
|
|
|
|
// Execute executes a command with the given context
|
|
func (cm *CommandManager) Execute(ctx *CommandContext) error {
|
|
if ctx == nil {
|
|
return fmt.Errorf("command context cannot be nil")
|
|
}
|
|
|
|
if ctx.CommandName == "" {
|
|
return fmt.Errorf("command name cannot be empty")
|
|
}
|
|
|
|
// Find the command
|
|
normalizedName := strings.ToLower(ctx.CommandName)
|
|
cm.mutex.RLock()
|
|
command, exists := cm.commands[normalizedName]
|
|
cm.mutex.RUnlock()
|
|
|
|
if !exists {
|
|
return fmt.Errorf("unknown command: %s", ctx.CommandName)
|
|
}
|
|
|
|
// Check admin level requirements
|
|
if ctx.AdminLevel < command.RequiredLevel {
|
|
return fmt.Errorf("insufficient privileges for command '%s' (required: %d, have: %d)",
|
|
command.Name, command.RequiredLevel, ctx.AdminLevel)
|
|
}
|
|
|
|
// Check for subcommands
|
|
if len(ctx.Arguments) > 0 && command.SubCommands != nil {
|
|
normalizedSubName := strings.ToLower(ctx.Arguments[0])
|
|
if subCommand, exists := command.SubCommands[normalizedSubName]; exists {
|
|
// Execute subcommand
|
|
subCtx := &CommandContext{
|
|
Context: ctx.Context,
|
|
CommandType: subCommand.Type,
|
|
CommandName: subCommand.Name,
|
|
Arguments: ctx.Arguments[1:], // Remove subcommand name from arguments
|
|
RawArguments: strings.TrimSpace(strings.Join(ctx.Arguments[1:], " ")),
|
|
AdminLevel: ctx.AdminLevel,
|
|
RequiredLevel: subCommand.RequiredLevel,
|
|
Client: ctx.Client,
|
|
Player: ctx.Player,
|
|
Target: ctx.Target,
|
|
Zone: ctx.Zone,
|
|
Messages: make([]CommandMessage, 0),
|
|
Results: make(map[string]any),
|
|
}
|
|
|
|
// Check subcommand admin level
|
|
if ctx.AdminLevel < subCommand.RequiredLevel {
|
|
return fmt.Errorf("insufficient privileges for subcommand '%s %s' (required: %d, have: %d)",
|
|
command.Name, subCommand.Name, subCommand.RequiredLevel, ctx.AdminLevel)
|
|
}
|
|
|
|
return subCommand.Handler(subCtx)
|
|
}
|
|
}
|
|
|
|
// Execute main command
|
|
return command.Handler(ctx)
|
|
}
|
|
|
|
// ParseCommand parses a raw command string into a CommandContext
|
|
func (cm *CommandManager) ParseCommand(rawCommand string, client ClientInterface) *CommandContext {
|
|
// Remove leading slash if present
|
|
rawCommand = strings.TrimSpace(rawCommand)
|
|
rawCommand = strings.TrimPrefix(rawCommand, "/")
|
|
|
|
// Split into command and arguments
|
|
parts := strings.Fields(rawCommand)
|
|
if len(parts) == 0 {
|
|
return nil
|
|
}
|
|
|
|
commandName := parts[0]
|
|
arguments := parts[1:]
|
|
rawArguments := strings.TrimSpace(strings.Join(arguments, " "))
|
|
|
|
// Determine admin level
|
|
adminLevel := AdminLevelPlayer
|
|
if client != nil {
|
|
adminLevel = client.GetAdminLevel()
|
|
}
|
|
|
|
// Create context
|
|
ctx := &CommandContext{
|
|
Context: context.Background(),
|
|
CommandName: commandName,
|
|
Arguments: arguments,
|
|
RawArguments: rawArguments,
|
|
AdminLevel: adminLevel,
|
|
Client: client,
|
|
Messages: make([]CommandMessage, 0),
|
|
Results: make(map[string]any),
|
|
}
|
|
|
|
// Set additional context fields if client is available
|
|
if client != nil {
|
|
ctx.Player = client.GetPlayer()
|
|
ctx.Zone = client.GetZone()
|
|
}
|
|
|
|
// Determine command type based on name
|
|
ctx.CommandType = cm.determineCommandType(commandName)
|
|
|
|
return ctx
|
|
}
|
|
|
|
// determineCommandType determines the command type based on the command name
|
|
func (cm *CommandManager) determineCommandType(commandName string) CommandType {
|
|
// Check if command exists and get its type
|
|
cm.mutex.RLock()
|
|
defer cm.mutex.RUnlock()
|
|
|
|
normalizedName := strings.ToLower(commandName)
|
|
if command, exists := cm.commands[normalizedName]; exists {
|
|
return command.Type
|
|
}
|
|
|
|
// Default to player command if not found
|
|
return CommandTypePlayer
|
|
}
|