Minecraft(我的世界)中文论坛

 找回密码
 注册(register)
查看: 2221|回复: 7

[插件开发教程] [UD]更方便,更快捷的命令注册方式

[复制链接]
发表于 2017-8-6 16:08:12 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 魂葬 于 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源码。下面是一些心得。        
07211016_Rnff.png
这是存在于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下的第一个构造函数。   
2.png
而KillCommand的构造函数是被  
3.png
这个调用的。不难看出它是调用了某一个类的自身register方法。而这个类就是org.bukkit.command.SimpleCommandMap。而SimpleCommandMap是被Bukkit一开始调用过的。要获得SimpleCommandMap只能在CraftServer下的方法getCommandMap()获得   
   4.png
5.png
但是在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那我们要怎样继续操作呢?     
10.png
还是这里,它调用了SimpleCommandMap的register方法。
11.png    
它首先调用了第一个register,再由第一个regiser调用第二个。   
不难看出调用register这个方法的两个参数的类型分别为String和Command。   
那我们首先先从Command下手。   
因为Command是一个抽象类,不建议通过直接new的方式来获取Command对象,这样不太方便管理。代码也显得冗长。所以我建议新建一个类来继承它。        
12.png
继承这个抽象类后,在系统的引导下它会让你生成两个构造函数中的一个和一个必要的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)方法中。   
20.png
再通过PluginDescriptionFile下的commands这个Field查到它被调用的地方,最后在org.bukkit.plugin.SimplePluginManager下的enablePlugin(Plugin plugin)方法中找到。   
21.png
它调用了SimpleCommandMap的registerAll方法。     
30.png
所以不难看出那个string类型的对象应该是plugin.getDescription().getName()。而plugin则是一个JavaPlugin对象,所以获得commandMap后可以通过register(this.getDescroption().getName()(PS:当然这是在继承JavaPlugin类中调用,用其他类调用则要获得JavaPlugin的对象),Command对象)来注册指令。    然后我又去看了plugin.getDescription().getName()到底是什么。     
40.png
不难看出,plugin.getDescription().getName()这个方法返回值应该是plugin.yml的name的那一项,并且如果name中含有空格则会被替换为“_”。   
最后我在列举一下使用commandMap来注册指令比通过plugin.yml来注册指令的好处。
1.你不用再担心会在plugin.yml中写错格式,写中文担心乱码的问题了。
2.使用Command类下面独有的execute,在多个指令同时存在时你不用在想使用plugin.yml而通过onCommand方法判断是不是其中某一个指令,而是直接独立使用execute来实现。     
最后,谢谢大家阅读这篇帖子。希望帖子内容会对你们有用哦~

来自群组: Unknown Domain

评分

参与人数 6人气 +9 金粒 +70 收起 理由
zide888@qq.com + 2 nb。
Eldon + 2 + 30 Bukkit: 不按照基本法来你这样啊 naive.
q805705693 + 2 + 20 神乎其技,不服不行!
zghh008 + 20 Ssssssssssssssssssss
Rain_Effect + 2 神乎其技,不服不行!
qianji20000420 + 1 向dalao势力低头。

查看全部评分

发表于 2017-8-26 10:52:58 | 显示全部楼层
essentials正是用这种方法注册指令的,毕竟人家指令挺多的哈哈
回复

使用道具 举报

发表于 2017-8-29 19:36:58 | 显示全部楼层
ddyy163 发表于 2017-8-26 10:52
essentials正是用这种方法注册指令的,毕竟人家指令挺多的哈哈

哇。你有可能不了解ess。ess不是通过这种方式注册的。在它的plugin.yml中仍然有command子节点。
回复

使用道具 举报

发表于 2017-8-31 09:39:14 | 显示全部楼层
zghh008 发表于 2017-8-29 19:36
哇。你有可能不了解ess。ess不是通过这种方式注册的。在它的plugin.yml中仍然有command子节点。 ...

可能我记错了,之前看其他插件源代码的时候发现的
回复

使用道具 举报

发表于 2017-10-29 13:31:08 | 显示全部楼层
没看明白,能不能把要操作的分出来?
回复

使用道具 举报

发表于 2018-3-4 18:05:32 | 显示全部楼层
布局太多反而看迷了,直接说方法最好
回复

使用道具 举报

发表于 2018-6-19 22:06:27 | 显示全部楼层
本帖最后由 Aeva 于 2018-6-19 22:09 编辑

为什么不使用getDeclaredMethod("getCommandMap")直接获得方法而是通过循环?这是我唯一不理解的地方 编号1037.jpg java初学者,求解答
回复

使用道具 举报

发表于 2018-7-25 11:00:10 | 显示全部楼层
Aeva 发表于 2018-6-19 22:06
为什么不使用getDeclaredMethod("getCommandMap")直接获得方法而是通过循环?这是我唯一不理解的地方java初 ...

通过循环的话,可以最大限度的调试误差,不像getDeclaredMethod("getCommandMap")直接获得方法一样有误差便无法读出。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 手机版本

GMT+8, 2018-10-19 18:44 , Processed in 0.133977 second(s), 12 queries , Memcache On.

"Minecraft"以及"我的世界"为Mojang Synergies AB的商标。本站与Mojang以及微软公司没有从属关系。

© 2010-2017 我的世界中文论坛 版权所有。本站原创图文内容版权属于原创作者,未经许可不得转载。

快速回复 返回顶部 返回列表