本帖最后由 914554688wyt 于 2016-8-19 19:48 编辑
6.关于"数据包".
本章内容在bukkit javadoc中无涉及,
需查看NMS包.
服务端与客户端之间的通讯,全部以数据包来往的方式进行.然而Bukkit并提供没有收发包的API,具体原因嘛...自己脑补一下我也不知道.
这里暂且不涉及到具体通讯方法(Socket,暂时不打算搞)
每一种数据包都被封装为一个类,这个类在net.minecraft.server.版本包中,类名格式为Packet + 类型 + (Out/In) +内容.
类型有四种
Login - 加入服务器时.(与登录插件无关)
Status - Ping与MOTD等
Play - 游戏内容
Handshake - 握手包
Out 是指 传出,即发送给客户端的包
In 是指 收到,即从客户端传出的包
如何不使用别的工具(ProtocolLib说的就是你!)发包
(需要一定的反射知识)
[以下是本人口胡时间]
首先我们想,给玩家发包,应该是在Player类下
然后我们又发现,Player是个抽象类,于是我们找到他的实现类CraftPlayer.
我们发现一个很有可能的成员 playerConnection,但这个成员在CraftPlayer.getHandle()下,
继续顺藤摸瓜,找到getHandle的返回类型EntityPlayer,打开PlayerConnection类,
然后就可以看到在PlayerConnection类下的sendPacket方法!
[以下为正文]
通过不懈的探索与寻找(其实就是强行查找),我们找到了一个发包(至少是名字像)方法
public void sendPacket(final Packet packet)
但在Player类中没有包含getHandle这个方法,只在他的实现类CraftPlayer中有.但所有Player实例实际上也是一个CraftPlayer实例
假设有Player p;
1.这时候就要用到我们的反射大法,首先获取Class<CraftPlayer>实例,然后再获取他的"getHandle"方法,再执行获取EntityPlayer实例
Object entityPlayer = p.getClass().getMethod("get
Handle", new Class[0]).invoke(p, new Object[0]);
可能有人会说,可以强转CraftPlayer然后直接调用getHandle啊
.. 如果出现导入,那么服务端换了版本就会抛ClassNotFound
异常,因为
NMS与CraftBukkit包居心险恶的每个版本的包名不一样
所以说, 你强转CraftPlayer类不需要导入吗?
2.再获取他的playerConnection 字段
Object
playerConnection =
craftPlayer.getClass().getField("playerConnection").get(
craftPlayer);
3.最后获取他的sendPacket方法
playerConnection .getClass().getMethod("sendPacket", new Class[] { getNMSClass("Packet") })
这个getNMSClass,是因为NMS
居心险恶的每个版本的包名不一样,而这个包名的格式是
net.minecraft.server.vX_X_RX
然后bukkit的实现类都在craftbukkit包里,这个包名的格式是
org.bukkit.craftbukkit.
vX_X_RX
这个好办,我们随便获取一个nms或者cb Class,然后取他的包名然后再处理就可以了,就像这样
version = Bukkit.getServer().getClass().getPackage().getName()
.substring(Bukkit.getServer().getClass().getPackage().getName().lastIndexOf('.') + 1);
*这里的getServer()返回的是CraftServer实例
得到版本后,就直接Class.forName(
net.minecraft.server.版本.类名)获取NMS类就可以了.
得到sendPacket方法后,我们还需要处理他的参数Packet.
通过反射获取我们需要的包的构造器, 假如我要发PacketPlayOutTitle包
Object packetTitle = getNmsClass("PacketPlayOutTitle").getConstructor(new Class[] {有序的构造器参数类型的Class实例})
.newInstance(new Object[] { 有序的构造器参数 });
现在来完成最后一步, 执行sendPacket方法
playerConnection .getClass().
getMethod("sendPacket", new Class[] { getNMSClass("Packet") }).invoke(playerConnection , new Object[] { packetTitle });
大功告成,这样你就成功的给玩家发送了一个Packet.
另外,我们还可以使用ProtocolLib进行收发包,监听包的收发等等....
梨子的ProtocolLib教程:
[Ni]ProtocolLib怎么玩|使用ProtocolLib发包或收包|突破Bukkit限制|只有想不到系列
http://www.mcbbs.net/thread-568714-1-1.html
番外
NMS包是不是有混淆? 有些包的字段是不是看不懂?
你需要这个
http://wiki.vg/Protocol
完整的描述了mc数据包每个字段的意义.