- 积分
- 3291
- 帖子
- 主题
- 精华
贡献- 份
爱心- 心
- 钻石
- 颗
- 人气
- 点
- 下界之星
- 枚
- 最后登录
- 1970-1-1
- 注册时间
- 2014-1-1
来自:广东 | 本帖最后由 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("getHandle", 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数据包每个字段的意义.
|
|