Skip to content

自定义命令开发

Bngeuit 采用注解式命令系统,支持主子命令结构、别名和命名空间。开发者可以通过简单的注解来注册自定义命令。

@Command 注解

@Command 注解用于标记命令类,定义命令的基本信息:

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Command {
    String name();                  // 主命令名
    String[] aliases() default {};  // 别名
    String description() default "";
    String usage() default "";
}

参数说明

参数类型必填说明
nameString主命令名(如 "test"
aliasesString[]命令别名(如 {"t", "tst"}
descriptionString命令描述
usageString用法说明

@CommandBody 注解

@CommandBody 注解用于标记命令处理方法:

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CommandBody {
    String name() default "";  // 子命令名(空 = 主命令本身)
    String desc() default "";
}

参数说明

参数类型必填说明
nameString子命令名(空字符串表示主命令本身)
descString子命令描述

方法签名

所有命令处理方法必须遵循以下签名:

java
void execute(CommandSender sender, String[] args)
  • sender - 命令发送者(用于回复消息)
  • args - 命令参数数组

命令格式

<主命令> [子命令] [参数...]
  • @CommandBodyname = "" → 输入主命令时触发
  • @CommandBodyname = "xxx" → 输入 主命令 xxx 时触发

完整示例

基础命令

java
import cn.org.bukkit.bngeuit.command.Command;
import cn.org.bukkit.bngeuit.command.CommandBody;
import cn.org.bukkit.bngeuit.command.CommandSender;
import cn.org.bukkit.bngeuit.command.GroupCommandSender;

@Command(name = "test", aliases = {"t"}, description = "测试命令")
public class TestCommand {

    @CommandBody  // 主命令:test
    public void execute(CommandSender sender, String[] args) {
        sender.sendMessage("参数: " + String.join(" ", args));
    }

    @CommandBody(name = "info", desc = "查看信息")  // 子命令:test info
    public void info(CommandSender sender, String[] args) {
        sender.sendMessage("TestPlugin v1.0");
    }
}

使用效果

test              → execute(),无子命令
test hello world  → execute(),args = ["hello", "world"]
test info         → info()
t info            → 别名 "t" = "test"

CommandSender

所有命令方法都接收 CommandSender 作为第一参数,用于回复命令来源:

方法返回类型说明
getSenderId()String发送者标识(群 OpenID / 用户 OpenID / CONSOLE
sendMessage(String)void向命令来源发送回复

实现类

实现场景说明
GroupCommandSender群聊getSenderId() 即群 OpenID,sendMessage() 回复到群
C2CCommandSender私聊getSenderId() 即用户 OpenID,sendMessage() 回复到私聊
ConsoleSender控制台回复输出到 System.out,全局单例,经 Bngeuit.getConsoleSender() 获取

示例

java
@CommandBody
public void execute(CommandSender sender, String[] args) {
    if (sender instanceof GroupCommandSender) {
        // 来自群聊
        sender.sendMessage("来自群 " + sender.getSenderId());
    } else if (sender instanceof C2CCommandSender) {
        // 来自私聊
        sender.sendMessage("来自私聊 " + sender.getSenderId());
    } else if (sender instanceof ConsoleSender) {
        // 来自控制台
        sender.sendMessage("来自控制台");
    }
}

命令命名空间

每个命令归属一个插件,支持 插件名:命令 精确指定,用于解决同名命令冲突:

  • 插件命令 → 命名空间 = 插件名(小写),例如 myplugin:test
  • 内置命令(plugin == null)→ 保留命名空间 bngeuit,例如 bngeuit:stop

解析规则(大小写不敏感):

  1. 插件名:命令 / bngeuit:命令 精确命中 → 使用该命令
  2. 否则回退到普通命令名(无冒号输入按普通命令匹配)
java
myplugin:test info      # 精确指定某插件的 test 命令
bngeuit:stop            # 内置命令
test info               # 无冒号,按普通命令解析

手动派发命令

框架外部可通过 Bngeuit.dispatchCommand(sender, commandLine) 以字符串形式执行任意命令(在调度器 sync 线程执行,与事件处理串行):

java
Bngeuit.dispatchCommand(Bngeuit.getConsoleSender(), "stop");
Bngeuit.dispatchCommand(Bngeuit.getConsoleSender(), "plugin reload myplugin");

自动注册

插件 JAR 中的 @Command 类会被自动扫描并注册到 插件名: 命名空间,无需手动操作。

注册流程

  1. 框架启动时,PluginManager 扫描所有已加载插件的 JAR 文件
  2. 找到带有 @Command 注解的类
  3. 扫描类中带有 @CommandBody 注解的方法
  4. 将命令注册到命令管理器,命名空间为插件名(小写)

注意事项

  • 命令类必须是公共的(public
  • 命令处理方法必须是公共的(public
  • 方法签名必须符合 void execute(CommandSender sender, String[] args)
  • 避免命令名冲突,建议使用插件特有的前缀

最佳实践

1. 命令命名规范

java
// ✅ 好的命名
@Command(name = "myplugin", aliases = {"mp"}, description = "我的插件命令")

// ❌ 避免通用名称,容易冲突
@Command(name = "test", aliases = {"t"})

2. 子命令组织

java
@Command(name = "myplugin", description = "我的插件")
public class MyPluginCommand {

    @CommandBody  // myplugin
    public void help(CommandSender sender, String[] args) {
        sender.sendMessage("使用方法: myplugin <子命令>");
    }

    @CommandBody(name = "reload", desc = "重载配置")
    public void reload(CommandSender sender, String[] args) {
        // 重载逻辑
        sender.sendMessage("配置已重载");
    }

    @CommandBody(name = "info", desc = "查看信息")
    public void info(CommandSender sender, String[] args) {
        sender.sendMessage("MyPlugin v1.0.0");
    }
}

3. 权限控制

java
@CommandBody(name = "admin", desc = "管理员命令")
public void admin(CommandSender sender, String[] args) {
    // 检查是否为控制台
    if (!(sender instanceof ConsoleSender)) {
        sender.sendMessage("此命令只能在控制台使用");
        return;
    }
    
    // 管理员逻辑
    sender.sendMessage("管理员操作已执行");
}

4. 参数验证

java
@CommandBody(name = "set", desc = "设置配置")
public void set(CommandSender sender, String[] args) {
    if (args.length < 2) {
        sender.sendMessage("用法: myplugin set <key> <value>");
        return;
    }
    
    String key = args[0];
    String value = args[1];
    
    // 设置配置
    sender.sendMessage("已设置 " + key + " = " + value);
}