Skip to content

Latest commit

 

History

History
252 lines (202 loc) · 10.3 KB

1-4_如何编写命令.md

File metadata and controls

252 lines (202 loc) · 10.3 KB

上一节 下一节

第一章 第四节 如何编写命令

参与编写者: MagicLu550

建议学习时间: 30分钟

学习要点: 学习自己创建一个简易的命令,了解指令和指令映射,了解SimpleCommand的使用

其实创建一个简易的命令很简单,我们可以在PluginBase中重写onCommand方法,进行equals识别,比如

public class PluginMain extends PluginBase{
            //... 其他
    @Override
    // args指后面的那一串参数,直接取出即可,注意先检查length,label其实就是指令名称,也可以用command的信息,这个是个人习惯
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if("hello".equals(command.getName())){ // 识别指令名称
                this.getLogger().info("hello!");
                return true;
        }
        return false;
    }
}

nukkit的原生命令也是很多基于Command创建的.很多项目是需求指令 以使得用户和您的项目操作,也就是作为一个 接口(Interface) ,接下来我们介绍以下nukkit的命令。

nukkit的命令的父类是Command,它有很多的构造方法

    public Command(String name) {
        this(name, "", null, new String[0]);
    }

    public Command(String name, String description) {
        this(name, description, null, new String[0]);
    }

    public Command(String name, String description, String usageMessage) {
        this(name, description, usageMessage, new String[0]);
    }

    public Command(String name, String description, String usageMessage, String[] aliases) {
        this.commandData = new CommandData();
        this.name = name.toLowerCase(); // Uppercase letters crash the client?!?
        this.nextLabel = name;
        this.label = name;
        this.description = description;
        this.usageMessage = usageMessage == null ? "/" + name : usageMessage;
        this.aliases = aliases;
        this.activeAliases = aliases;
        this.timing = Timings.getCommandTiming(this);
        this.commandParameters.put("default", new CommandParameter[]{new CommandParameter("args", CommandParamType.RAWTEXT, true)});
    }

我们可以知道,nukkit的命令都是toLowerCase的,即小写,也就是说,命令不区分大小写。 对于命令如何存储,服务端如何识别一个命令,我们从源码中找到以下资料:

--

命令的区分标识是fallBackPrefix+:+label,默认为指令的名称,一般fallBackPrefix都写""

这一块知识缺点将会有其他人补充[1]

代码依据 - SimpleCommandMap.java

159 private boolean registerAlias(Command command, boolean isAlias, String fallbackPrefix, String label) {
160      this.knownCommands.put(fallbackPrefix + ":" + label, command);

--

存储指令的容器是实现CommandMap接口的,即SimpleCommandMap,我们可以通过

        this.getServer().getCommandMap();

得到CommandMap.

Command,CommandMap有很多方法

  1. Command

    1. 根构造方法的参数为

      String name, String description, String usageMessage, String[] aliases
              
              第一个name是指令名称,最终会转换为全小写
              
              第二个description是指令介绍,用于给玩家查看使用的
              
              第三个usageMessage就是当玩家对命令使用错误,返回的信息
              
              第四个aliases就是指令的别名,指令可以有多个别名
      

      当然,也有简化的构造方法,可以根据你的需求任意调用,这里不多阐述,其他的都是为默认值

    2. Command的主要属性 如同command的构造方法一样,command我们需要了解的属性基本也就是这四个。其他属性将会 在nukkit原理解析的时候讲解

    3. Command的比较常用的方法

      1. boolean execute(CommandSender commandSender, String label, String[] strings)

        这个方法是需要开发者自行实现的方法,当指令被触发,就会执行execute里的代码 它的参数我们在第二章提到了

      2. String getName()

        这个可以获取指令的名称

      其余方法我们将会在后期附件讲解到,如果有想要知悉的其他方法,我们会另外在这里做补充, 或者您认为常用的,也可以pull request添加进去

  2. CommandMap

    1. boolean register(String fallbackPrefix, Command command) 可以注册指令,fallbackPrefix是前缀,用于服务端存储命令对象的标识 nukkit的本地命令的fallbackPrefix为nukkit command则为你的自定义命令对象
    2. void registerAll(String fallbackPrefix, List<? extends Command> commands) 这个可以一次性注册多个指令
    3. boolean dispatch(CommandSender sender, String cmdLine) 这个调用一个命令,cmdLine就是日常所输入的命令
    4. void registerSimpleCommands(Object object) 这个是调用简单指令,通过注解实现的指令对象,我们后面将会演示如何使用它。

nukkit官方后来推出一系列简化操作,如SimpleCommand,SimpleConfig等,我们这里解释以下SimpleCommand

SimpleCommand运用了注解,同样通过 反射 实现的,我们可以看到官方的源码来探讨它的使用

SimpleCommandMap.java

@Override
    public void registerSimpleCommands(Object object) {
        for (Method method : object.getClass().getDeclaredMethods()) {
            cn.nukkit.command.simple.Command def = method.getAnnotation(cn.nukkit.command.simple.Command.class);
            if (def != null) {
                SimpleCommand sc = new SimpleCommand(object, method, def.name(), def.description(), def.usageMessage(), def.aliases());

                Arguments args = method.getAnnotation(Arguments.class);
                if (args != null) {
                    sc.setMaxArgs(args.max());
                    sc.setMinArgs(args.min());
                }

                CommandPermission perm = method.getAnnotation(CommandPermission.class);
                if (perm != null) {
                    sc.setPermission(perm.value());
                }

                if (method.isAnnotationPresent(ForbidConsole.class)) {
                    sc.setForbidConsole(true);
                }

                CommandParameters commandParameters = method.getAnnotation(CommandParameters.class);
                if (commandParameters != null) {
                    Map<String, CommandParameter[]> map = Arrays.stream(commandParameters.parameters())
                            .collect(Collectors.toMap(Parameters::name, parameters -> Arrays.stream(parameters.parameters())
                                    .map(parameter -> new CommandParameter(parameter.name(), parameter.type(), parameter.optional()))
                                    .distinct()
                                    .toArray(CommandParameter[]::new)));

                    sc.commandParameters.putAll(map);
                }

                this.register(def.name(), sc);
            }
        }
    }

很显然,简易命令必须要有@Command注解在方法上,方法上标记一些内容,当然,最终只是把一个类拆解,分为多个命令 对象注册(SimpleCommand),最终也继承自Command。SimpleCommand提供了对于参数最大和最小的限制。

SimpleCommand.java

@Override
    public boolean execute(CommandSender sender, String commandLabel, String[] args) {
        if (this.forbidConsole && sender instanceof ConsoleCommandSender) {
            this.sendInGameMessage(sender);
            return false;
        } else if (!this.testPermission(sender)) {
            return false;
        } else if (this.maxArgs != 0 && args.length > this.maxArgs) {
            this.sendUsageMessage(sender);
            return false;
        } else if (this.minArgs != 0 && args.length < this.minArgs) {
            this.sendUsageMessage(sender);
            return false;
        }

        boolean success = false;

        try {
            //这里执行我们的命令
            success = (Boolean) this.method.invoke(this.object, sender, commandLabel, args);
        } catch (Exception exception) {
            Server.getInstance().getLogger().logException(exception);
        }

        if (!success) {
            this.sendUsageMessage(sender);
        }

        return success;
    }

这段代码我们可以知道方法的参数有规范要求的 Object object,CommandSender sender,String label,String[] args object就是我们的命令对象了,通过registerSimpleCommand注册进去的命令对象 其他显而易见,不再多讲,具体如何使用,其实很简单

package net.noyark.www;


import cn.nukkit.command.CommandSender;
import cn.nukkit.command.simple.Arguments;
import cn.nukkit.command.simple.Command;

public class MySimpleCommand {

    @Command(name = "hello",description = "233",usageMessage = "/hello")
    @Arguments(max = 10,min = 0)
    public boolean onHelloCommand(CommandSender sender, String label, String[] args){
        //这里写指令处理代码
        return true;//为布尔类型,否则会发生空指针异常,在先前源码可以分析其返回类型
    }
}

最终通过registerSimpleCommand注册即可.事实上是对Command的封装

command的用户组(这里参考自snake1999的文章)

permissions: #这个标签我们只写一次就ok了
  plugin.hello:
    description: 你好
    default: op #权限组,后期会讲到
commands: #这个标签我们只写一次就ok了
  hello:
    description: 你好
    usage: "/hello"
    permission: plugin.hello

这里我们发现了default,这个的选项有以下几种

  • op,代表服务器管理员,在ops.txt中规定。
  • notop,代表除服务器管理员外的所有玩家。
  • true,代表所有玩家。
  • false,代表空集。如果某个命令对应这个权限,那就没有人能够使用这个命令(控制台除外)。

大家可以根据这些选项来控制指令的使用范围了

参考文献:

上一节 下一节