本帖最后由 914554688wyt 于 2016-8-20 12:18 编辑
0.序章
*什么是Bukkit
[以下为本人口胡时间]
从前minecraft官方推出了一款服务端,这里暂且成为官服.
但这个官服呢,又和mc的尿性一样,没有开源(混淆了),而且对外兼容性差! (不能兼容mod,插件那时还没有)
于是呢,就有一个团队,他们把官服给强势反混淆(人工的)了一遍,然后在其中强力插入♂某些需要的内容,然后封装做成现在的bukkitAPI供我们调用. 于是就有了现在的插件....
虽说反混淆了,但是仍有许多方法名是没改过来的,所以如果你去反编译一下net.minecraft.server包的话,大概看到的方法名都是a,b,c,d......
[以下为正文]
Bukkit About Us
读前准备
本教程不考虑没有java基础的初学者。
换句话说,本教程要求所有的阅读者都掌握Java语法且清晰的了解以下概念
[题外话:我就是半吊子从其他语言边写插件边转过来的,然后现在还得恶补一番.]
标有*的为非必须,但推荐阅读相关资料.
粗体的为必须中的必须,必须掌握并熟练运用
还需要什么
对mc与编程的热情.(必须): 经常需要看一大坨别人的代码,如果你没有这个耐心与逻辑思维,学学倒是可以..
坚强的,耐挫的决心. : 论如何调试代码? 都说是试... 很多时候你没法直接定位错误在哪,这个时候该怎么办?.....
对mc游戏内容的了解 : 这个不解释
JDK与一个IDE: JDK7或者JDK8, IDE推荐Eclipse和IntellijIDEA
一个bukkit的JavaDoc : Bukkit 1.10.2-R0.1-SNAPSHOT API
若有写的不好的地方,欢迎各位斧正.
如果你已经阅读完毕这些东西并认为自己可以继续阅读,那你可以往下翻页了!
0.序章
*什么是Bukkit
[以下为本人口胡时间]
从前minecraft官方推出了一款服务端,这里暂且成为官服.
但这个官服呢,又和mc的尿性一样,没有开源(混淆了),而且对外兼容性差! (不能兼容mod,插件那时还没有)
于是呢,就有一个团队,他们把官服给强势反混淆(人工的)了一遍,然后在其中强力插入♂某些需要的内容,然后封装做成现在的bukkitAPI供我们调用. 于是就有了现在的插件....
虽说反混淆了,但是仍有许多方法名是没改过来的,所以如果你去反编译一下net.minecraft.server包的话,大概看到的方法名都是a,b,c,d......
[以下为正文]
Bukkit About Us
读前准备
本教程不考虑没有java基础的初学者。
换句话说,本教程要求所有的阅读者都掌握Java语法且清晰的了解以下概念
[题外话:我就是半吊子从其他语言边写插件边转过来的,然后现在还得恶补一番.]
标有*的为非必须,但推荐阅读相关资料.
粗体的为必须中的必须,必须掌握并熟练运用

还需要什么
对mc与编程的热情.(必须): 经常需要看一大坨别人的代码,如果你没有这个耐心与逻辑思维,学学倒是可以..
坚强的,耐挫的决心. : 论如何调试代码? 都说是试... 很多时候你没法直接定位错误在哪,这个时候该怎么办?.....
对mc游戏内容的了解 : 这个不解释
JDK与一个IDE: JDK7或者JDK8, IDE推荐Eclipse和IntellijIDEA
一个bukkit的JavaDoc : Bukkit 1.10.2-R0.1-SNAPSHOT API
若有写的不好的地方,欢迎各位斧正.
如果你已经阅读完毕这些东西并认为自己可以继续阅读,那你可以往下翻页了!
本帖最后由 914554688wyt 于 2016-8-25 17:26 编辑
1.创建一个空白插件
安装好jdk和IDE之后,我们就可以准备写第一个插件了.
[文中非关键步骤我都只是一笔带过,如果看不明白可以去baidu,Google,或者找别人的教程看看有没有说明.
总是依赖别人替你做好所有事情永远也不是一个好码农]
1.首先新建java项目, "MyFirstPlugin".
2.右键项目,属性,将编码设置为UTF-8, 并在"java构建路径"中将服务端的jar文件添加到引用的库.
3.
右键项目,新建plugin.yml文件,按如下格式
plugin的g漏掉了.... 不要在意这个文件用于让bukkit识别这是一个插件.bukkit加载插件最先会读取这个文件,获取主类路径,然后再一路加载下去
需要遵循Yaml语法.
name - 插件名称 (推荐全字母)
main - 一个类的完整类名,这个类,需要继承Bukkit中的JavaPlugin类,又称插件的主类
version - 能表示插件版本的字符串.
特别容易错的地方有: 冒号后面没加空格, 主类路径打错,作死用中文 值可以用
错误plugin.yml在开服时会有一段报错并导致插件不加载,就像这样: Invaild plugin.yml.
4.
打开项目,在源文件夹(\src)下,新建包com.github.MrWub.MyFirstPlugin, 在这个包下新建类MyFirstPlugin.
那么我们就说MyFirstPlugin类的完整类名为com.github.MrWub.MyFirstPlugin.MyFirstPlugin
plugin.yml的main项必须与该类完整类名一致,就像我所写的一样.
5.
使得主类继承bukkit中的JavaPlugin类
这时候出现了编译错误,有可能是因为JavaPlugin类未导入. 可使用IDE的快速补全解决.
这类问题以后非特殊情况不再特殊说明.
6.重写onEnable()与onDisable()方法
Bukkit会在启用某插件时调用 (继承JavaPlugin类的) 主类的onEnable()方法,禁用某插件时调用onDisable()方法.
**小括号内为注释与补充,可以忽略**
**中括号内为题外话**
重写这两个方法,这样我们就可以让他在启用与禁用时做我们想做♂的事情.
其实还有onLoad()方法,这个方法仅在开服加载插件时调用一次.
(开服时,插件会被加载并启用,
reload时,插件会先被禁用,再被启用,
关服时,插件会被禁用.)
[推荐大家去使用反编译工具(例如jdgui)看一下JavaPlugin类的源码,这样印象会深很多.
其实不止是javaplugin的,其他很多常用类都可以看看...]
7.在启用时向控制台输出信息
: 这不很简单吗,在onEnable()中添加System.out.println()?
不不不 我们有更高级的方法 this.getLogger().info("msg"); 这是一个定义在javaplugin类中的方法.这个方法能够记录一条日志信息并在控制台即时显示.
不过,这玩意不支持颜色代码.这个之后会讲.
8.完工!
确认你的插件没有编译错误,确认plugin.yml编写正确后,选择项目,导出jar文件(不是可运行的!)
将得到的jar文件放到服务端的plugins文件夹中,运行服务器吧!
1.创建一个空白插件
安装好jdk和IDE之后,我们就可以准备写第一个插件了.
[文中非关键步骤我都只是一笔带过,如果看不明白可以去baidu,Google,或者找别人的教程看看有没有说明.
总是依赖别人替你做好所有事情永远也不是一个好码农]
1.首先新建java项目, "MyFirstPlugin".
2.右键项目,属性,将编码设置为UTF-8, 并在"java构建路径"中将服务端的jar文件添加到引用的库.

3.
右键项目,新建plugin.yml文件,按如下格式

plugin的g漏掉了.... 不要在意这个文件用于让bukkit识别这是一个插件.bukkit加载插件最先会读取这个文件,获取主类路径,然后再一路加载下去
需要遵循Yaml语法.
name - 插件名称 (推荐全字母)
main - 一个类的完整类名,这个类,需要继承Bukkit中的JavaPlugin类,又称插件的主类
version - 能表示插件版本的字符串.
特别容易错的地方有: 冒号后面没加空格, 主类路径打错,
错误plugin.yml在开服时会有一段报错并导致插件不加载,就像这样: Invaild plugin.yml.
4.
打开项目,在源文件夹(\src)下,新建包com.github.MrWub.MyFirstPlugin, 在这个包下新建类MyFirstPlugin.
那么我们就说MyFirstPlugin类的完整类名为com.github.MrWub.MyFirstPlugin.MyFirstPlugin
plugin.yml的main项必须与该类完整类名一致,就像我所写的一样.
5.
使得主类继承bukkit中的JavaPlugin类

这时候出现了编译错误,有可能是因为JavaPlugin类未导入. 可使用IDE的快速补全解决.

这类问题以后非特殊情况不再特殊说明.
6.重写onEnable()与onDisable()方法

Bukkit会在启用某插件时调用 (继承JavaPlugin类的) 主类的onEnable()方法,禁用某插件时调用onDisable()方法.
**小括号内为注释与补充,可以忽略**
**中括号内为题外话**
重写这两个方法,这样我们就可以让他在启用与禁用时做我们想做♂的事情.
其实还有onLoad()方法,这个方法仅在开服加载插件时调用一次.
(开服时,插件会被加载并启用,
reload时,插件会先被禁用,再被启用,
关服时,插件会被禁用.)
[推荐大家去使用反编译工具(例如jdgui)看一下JavaPlugin类的源码,这样印象会深很多.
其实不止是javaplugin的,其他很多常用类都可以看看...]
7.在启用时向控制台输出信息

不不不 我们有更高级的方法 this.getLogger().info("msg"); 这是一个定义在javaplugin类中的方法.这个方法能够记录一条日志信息并在控制台即时显示.
不过,这玩意不支持颜色代码.这个之后会讲.
8.完工!
确认你的插件没有编译错误,确认plugin.yml编写正确后,选择项目,导出jar文件(不是可运行的!)
将得到的jar文件放到服务端的plugins文件夹中,运行服务器吧!
?????
excuse me????
nms得到的途径可不是正规途径得到的
后来Bukkit被dmca你以为为啥呢-.-
excuse me????
nms得到的途径可不是正规途径得到的
后来Bukkit被dmca你以为为啥呢-.-
本帖最后由 754503921 于 2018-8-11 09:38 编辑
2.创建命令
论如何创建一个安全,优雅的命令
1.无报错,正常运行.
2.必须考虑权限问题.
3.使用简洁,参数顺序符合逻辑思维
创建命令的步骤如下.
1.在plugin.yml中注册命令,按如下格式
如何默认给玩家权限.
2.重写onCommand()方法
执行命令时会被bukkit调用的方法.
[其实整个框架你可以理解为有一层平面,然后从这个平面上垂下来一条绳子,你可以在绳子上挂东西,当上层需要的时候就把绳子收上去拿走东西... ]
可选择直接在主类中重写,也可以选择开一个命令执行类MyExecutor extends CommandExecutor,
然后在主类中getCommand("myhome").setExecutor(new 执行类()); 在这个类中重写onCommand();
onCommand()的形式是
public boolean onCommand(CommandSender sender, Command cmd, String label,String[] args) {
/* 其中sender为命令发送者, 必定是Player实例或ConsoleSender实例.
cmd Command实例,暂时无用.
label 相当于主命令去掉/ ,如输入/gamemode 1,则label参数就为"gamemode".
args 命令参数数组,如输入/gamemode 1 test 则args={"1","test"}.
*/
return false; //返回值为false时,会提示玩家plugin.yml中usage的内容.
}
3.在onCommand()中填写内容
假设我们现在要新建一个命令,当他运行的时候给玩家发送一行聊天框信息,这个命令只允许玩家执行
5.导出运行试试吧!
未完待续
强烈建议论坛支持Markdown语法!
版主留:你的 command 少了个 s,已编辑
2.创建命令
论如何创建一个安全,优雅的命令
1.无报错,正常运行.
2.必须考虑权限问题.
3.使用简洁,参数顺序符合逻辑思维
创建命令的步骤如下.
1.在plugin.yml中注册命令,按如下格式
如何默认给玩家权限.
2.重写onCommand()方法
执行命令时会被bukkit调用的方法.
[其实整个框架你可以理解为有一层平面,然后从这个平面上垂下来一条绳子,你可以在绳子上挂东西,当上层需要的时候就把绳子收上去拿走东西... ]
可选择直接在主类中重写,也可以选择开一个命令执行类MyExecutor extends CommandExecutor,
然后在主类中getCommand("myhome").setExecutor(new 执行类()); 在这个类中重写onCommand();
onCommand()的形式是
public boolean onCommand(CommandSender sender, Command cmd, String label,String[] args) {
/* 其中sender为命令发送者, 必定是Player实例或ConsoleSender实例.
cmd Command实例,暂时无用.
label 相当于主命令去掉/ ,如输入/gamemode 1,则label参数就为"gamemode".
args 命令参数数组,如输入/gamemode 1 test 则args={"1","test"}.
*/
return false; //返回值为false时,会提示玩家plugin.yml中usage的内容.
}
3.在onCommand()中填写内容
假设我们现在要新建一个命令,当他运行的时候给玩家发送一行聊天框信息,这个命令只允许玩家执行

5.导出运行试试吧!
未完待续
强烈建议论坛支持Markdown语法!
版主留:你的 command 少了个 s,已编辑
吕乐乐 发表于 2016-8-5 20:54
可是我我并不会怎么将在NetBeans IDE 7.2里的插件导出?
变成JAR?
如果我记得没错的话是构建,然后在build文件夹下有.
吕乐乐 发表于 2016-8-6 08:14
谢谢了,不过我发现构建之后.JAVA都构建好了编程class
但是plugin.yml等文件却没有构建,如何修复呢。
以 ...
....netbeans并没有用过... 但肯定有办法的
再不行就手动拖进去呗 \*_*/
本帖最后由 914554688wyt 于 2016-8-6 20:00 编辑
3.配置文件(YAML)
bukkit自带的是snake_yaml库.
不过我们并不需要直接用,Bukkit都给我们封装好了.
1.在plugin.yml旁边新建config.yml, 并在其中写入默认配置(遵循yaml语法)
2.了解如下有关的方法
解释一下"路径":
3.然后....好像就没有了...这些方法已经足以让我们进行配置文件的读写了.
当然yaml的神奇用法远远不止这些,可以去snake_yaml库里看看.
稍微补充一下yaml的基本语法
键:[空格]值
值如果是字符串则需要用'字符串'这样的形式括起来,当然这是在你手动修改的时候(虽然不括也行,但是括起来更规范)
不允许使用tab,每一层缩进为两个空格
list的形式是
key:
- xxx
- xxx
....
3.配置文件(YAML)
bukkit自带的是snake_yaml库.
不过我们并不需要直接用,Bukkit都给我们封装好了.
1.在plugin.yml旁边新建config.yml, 并在其中写入默认配置(遵循yaml语法)
2.了解如下有关的方法

实现ConfigurationSerializable接口的类可以作为set方法的第二个参数 因为实现了该接口的类允许被序列化/反序列化到配置文件或BukkitObjectOutputStream/BukkitObjectInputStream中 如果你说,我只添加了 implements ConfigurationSerializable,需要实现的两个方法我摆个空的在那里,那可不可以做到序列化/反序列化呢? 你说呢?....对于这类问题我不想解答. |
解释一下"路径":
3.然后....好像就没有了...这些方法已经足以让我们进行配置文件的读写了.
当然yaml的神奇用法远远不止这些,可以去snake_yaml库里看看.
稍微补充一下yaml的基本语法
键:[空格]值
值如果是字符串则需要用'字符串'这样的形式括起来,当然这是在你手动修改的时候(虽然不括也行,但是括起来更规范)
不允许使用tab,每一层缩进为两个空格
list的形式是
key:
- xxx
- xxx
....
本帖最后由 914554688wyt 于 2016-8-6 15:18 编辑
4.监听事件
事件是什么
顾名思义,一件事,如: 玩家传送,实体死亡,交互等等等等..
bukkit在NMS中插入了许多不可描述的代码,成功的做成了一套事件触发系统.
监听器是什么
一个方法,bukkit检测到 事件类型与该方法参数类型符合时 会调用
(bukkit会根据注解来获取监听器,根据参数来判断是哪种监听器.)
即:
NMS运作 -> 不可描述的代码通知事件系统发生事件 (又名唤起) -> 事件系统调用监听器.
1.
首先在事件系统中注册监听器,这一步一般在启用时执行
Bukkit.getPluginManager().registerEvents(Listener实例,JavaPlugin实例);
Listener实例 - 监听器必须在一个实现了Listener接口的类中,但实现这个接口其实和没实现没区别...
JavaPlugin实例 - 你的主类.
比如说,我要在主类中创建监听器
又比如说,我觉得在主类中太乱了,我想新开一个类放监听器
没有本质上的区别,仅仅是监听器挪了位
2.
创建监听器,为了让Bukkit在Listener实例中找到你的监听器,这个方法需要添加@EventHandle注解
(他是怎么找到监听器的? 反射,详情请见java.lang.reflect包)
[一个记录着java自带类与方法如何使用的官方规范是必要的,就像这个,JAVA1.6API规范(中文).
更高版本的并没有中文,汉化团队强行弃了一波坑,Eclipse就自带英文的]
就像这样
这个方法内可以做任何你想做的事情
更高级的玩法
EventHandler可以带参数,就像这样
@EventHandler(priority = EventPriority.LOW)
这个代表他的优先级.
若有不同监听器监听同一个事件(这些监听器有可能在别的插件中!)那他们的调用顺序由优先级决定
由低到高执行.
一个良好的插件应该具有良好的兼容性.
请按照你的需要调整优先级,比如你是一个反作弊插件需要监听玩家执行命令,那么你的优先级应该是lowest,即为最先捕捉命令并拦截.
请不要在优先级为MONITOR的方法中修改事件结果,那就无法保证别的插件能监听到你修改后的结果.
反正就最好留下余地,不要把事情做死.[这算是职业道德么233]
[就和你每个方法都final一样恶心]
Cancellable接口
一个实现了Cancellable接口的事件则可以被取消,何为被取消?
比如一个玩家打羊羊君
我把这个事件取消了,那就不会有任何反应,反之羊会受到伤害并被击飞
大部分Bukkit自带事件都实现了这个接口,我们可以通过这两个方法来判断他们是否被取消.
setCancelled(boolean)
isCancelled()
被取消的事件依旧会继续调用接下来的监听器,所以在你捕捉到事件之后先需要判断他是否被取消[虽然总是忘记]
事件中通常会携带这个事件的有关信息,若想得到这些信息则需要到bukkit的javadoc中去找相关的方法.
4.监听事件
事件是什么
顾名思义,一件事,如: 玩家传送,实体死亡,交互等等等等..
bukkit在NMS中插入了许多不可描述的代码,成功的做成了一套事件触发系统.
监听器是什么
一个方法,bukkit检测到 事件类型与该方法参数类型符合时 会调用
(bukkit会根据注解来获取监听器,根据参数来判断是哪种监听器.)
即:
NMS运作 -> 不可描述的代码通知事件系统发生事件 (又名唤起) -> 事件系统调用监听器.
1.
首先在事件系统中注册监听器,这一步一般在启用时执行
Bukkit.getPluginManager().registerEvents(Listener实例,JavaPlugin实例);
Listener实例 - 监听器必须在一个实现了Listener接口的类中,但实现这个接口其实和没实现没区别...
JavaPlugin实例 - 你的主类.
比如说,我要在主类中创建监听器

又比如说,我觉得在主类中太乱了,我想新开一个类放监听器

没有本质上的区别,仅仅是监听器挪了位
2.
创建监听器,为了让Bukkit在Listener实例中找到你的监听器,这个方法需要添加@EventHandle注解
(他是怎么找到监听器的? 反射,详情请见java.lang.reflect包)
[一个记录着java自带类与方法如何使用的官方规范是必要的,就像这个,JAVA1.6API规范(中文).
更高版本的并没有中文,汉化团队强行弃了一波坑,Eclipse就自带英文的]
就像这样

这个方法内可以做任何你想做的事情
更高级的玩法
EventHandler可以带参数,就像这样
@EventHandler(priority = EventPriority.LOW)
这个代表他的优先级.
若有不同监听器监听同一个事件(这些监听器有可能在别的插件中!)那他们的调用顺序由优先级决定
由低到高执行.
- EventPriority.LOWEST (最先)
- EventPriority.LOW
- EventPriority.NORMAL
- EventPriority.HIGH
- EventPriority.HIGHEST
- EventPriority.MONITOR (最后)
一个良好的插件应该具有良好的兼容性.
请按照你的需要调整优先级,比如你是一个反作弊插件需要监听玩家执行命令,那么你的优先级应该是lowest,即为最先捕捉命令并拦截.
请不要在优先级为MONITOR的方法中修改事件结果,那就无法保证别的插件能监听到你修改后的结果.
反正就最好留下余地,不要把事情做死.[这算是职业道德么233]
[就和你每个方法都final一样恶心]
Cancellable接口
一个实现了Cancellable接口的事件则可以被取消,何为被取消?
比如一个玩家打羊羊君
我把这个事件取消了,那就不会有任何反应,反之羊会受到伤害并被击飞
大部分Bukkit自带事件都实现了这个接口,我们可以通过这两个方法来判断他们是否被取消.
setCancelled(boolean)
isCancelled()
被取消的事件依旧会继续调用接下来的监听器,所以在你捕捉到事件之后先需要判断他是否被取消[虽然总是忘记]
事件中通常会携带这个事件的有关信息,若想得到这些信息则需要到bukkit的javadoc中去找相关的方法.