本帖最后由 魂葬 于 2017-8-6 16:25 编辑

一般情况下,大多数插件在Bukkit注册指令中选择的是通过plugin.yml文件配置来注册指令。例如:
  1. name: xxx
  2. main: xxx.xxx.xxx
  3. version: xxx
  4. author: xxx
  5. commands:
  6.   xxx:
复制代码
因为yml对格式要求很严格,少写一个空格或者多写一个空格Bukkit根本读不出来。为了方便对命令的注册我去查了Bukkit源码。下面是一些心得。        

这是存在于Bukkit org.bukkit.command.defaults下的一个Kill指令。在构造函数中它  
  1. super("kill");
  2. this.description = "Commits suicide, only usable as a player";
  3. this.usageMessage = "/kill";
复制代码
这里的super确切的说是调用了org.bukkit.command.Command下的第一个构造函数。   

而KillCommand的构造函数是被  

这个调用的。不难看出它是调用了某一个类的自身register方法。而这个类就是org.bukkit.command.SimpleCommandMap。而SimpleCommandMap是被Bukkit一开始调用过的。要获得SimpleCommandMap只能在CraftServer下的方法getCommandMap()获得   
  

但是在Server下并没有getCommandMap这个方法。所以我们需要通过反射来获得CommandMap进行进一步操作。
  1. private void getCommandMap() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  2.   final Class<?> c = Bukkit.getServer().getClass();
  3.     for (final Method method : c.getDeclaredMethods())
  4.       if (method.getName().equals("getCommandMap"))
  5.         this.commandMap = (CommandMap) method.invoke(Bukkit.getServer(), new Object[0]);
  6.     }
复制代码
当然在这个方法的类中定义了一个commandMap的Field。   
获得了commandMap那我们要怎样继续操作呢?     

还是这里,它调用了SimpleCommandMap的register方法。
   
它首先调用了第一个register,再由第一个regiser调用第二个。   
不难看出调用register这个方法的两个参数的类型分别为String和Command。   
那我们首先先从Command下手。   
因为Command是一个抽象类,不建议通过直接new的方式来获取Command对象,这样不太方便管理。代码也显得冗长。所以我建议新建一个类来继承它。        

继承这个抽象类后,在系统的引导下它会让你生成两个构造函数中的一个和一个必要的execute方法。   
下面我来介绍一下构造函数:   
第一个构造函数只有一个参数。这个参数就如同它本身的名字一样就是这个指令的名字,也就是说要使用这个指令需要输入的指令。如:/kill kill就是它的name。   
第二个构造函数有四个参数。第一个参数同样是它的名字;第二个参数就是他的描述,也就是在使用 /help 你的指令名字 系统会显示出来的字符串;第三个参数就是在execute方法返回值为false的时候系统会显示这个字符串;第四个参数就是该指令的简称。如:假设你设置了k为kill的简称,那么当你输入/k 的时候是等同于输入/kill。   
execute方法也就是和平常通过plugin.yml注册的调用onCommand方法的参数一样。分别为sender,输入的指令(如果是简称则显示简称),参数。   
知道了Command的对象如何获取,那应该如何写入第一个参数string呢?   
(PS:因为本人比较严谨(其实就是强迫症啦),所以我不想乱入几个字符串)   
然后我去查找了加载plugin.yml时的源码。通过JavaPluginLoader下的loadPlugin(File file)方法中一步一步查找到了加载plugin.yml中的command节点的源码。也就是在org.bukkit.plugin.PluginDescriptionFile类下的loadMap(Map<?, ?> map)方法中。   

再通过PluginDescriptionFile下的commands这个Field查到它被调用的地方,最后在org.bukkit.plugin.SimplePluginManager下的enablePlugin(Plugin plugin)方法中找到。   

它调用了SimpleCommandMap的registerAll方法。     

所以不难看出那个string类型的对象应该是plugin.getDescription().getName()。而plugin则是一个JavaPlugin对象,所以获得commandMap后可以通过register(this.getDescroption().getName()(PS:当然这是在继承JavaPlugin类中调用,用其他类调用则要获得JavaPlugin的对象),Command对象)来注册指令。    然后我又去看了plugin.getDescription().getName()到底是什么。     

不难看出,plugin.getDescription().getName()这个方法返回值应该是plugin.yml的name的那一项,并且如果name中含有空格则会被替换为“_”。   
最后我在列举一下使用commandMap来注册指令比通过plugin.yml来注册指令的好处。
1.你不用再担心会在plugin.yml中写错格式,写中文担心乱码的问题了。
2.使用Command类下面独有的execute,在多个指令同时存在时你不用在想使用plugin.yml而通过onCommand方法判断是不是其中某一个指令,而是直接独立使用execute来实现。     
最后,谢谢大家阅读这篇帖子。希望帖子内容会对你们有用哦~

[groupid=1181]Unknown Domain[/groupid]