本帖最后由 andylizi 于 2016-3-20 17:35 编辑
ProtocolLib原帖地址:
DevBukkit SpigotMC
JavaDoc:
作者的CI 最近这个地址似乎出毛病了, 框架里变空白,如果发现这种现象可以去
这里查看
GitHub:
Here
关于版本问题,目前我的结论是: 1.7.9以下使用3.4.0,1.7.9以上使用3.6.5,1.9使用3.6.5或还是测试版的3.7.0-BETA(只能在SpigotMC上下载到)
以前认为ProtocolLib只是管MC通讯协议的,现在发现大部分跟nms有关的操作都涉及到了比如NBT。此贴只讲
- 给玩家发送一个伪造的数据包
- 让服务端认为接收到了一个玩家发送的数据包
- 监听/修改 客户端->服务端 的数据包
- 监听/修改 服务端->客户端 的数据包
- 大概就这些了吧..
其中大部分功能都是我自己摸索出来的,所以有错误欢迎指出不要介意
为了保证您能愉快的阅读下面的内容,请确保您学会了Java面向对象的完整知识
对了,此贴的所有代码都用截图表现,我帮你写好是没用的,自己摸索才是正道。
首先啊,如果你要在插件中使用ProtocolLib(下简称PL)的任意功能,肯定得先在项目中添加PL的依赖,然后在plugin.yml里加入前置
,如果这个插件不是必须使用PL的,那么可以加入软前置
- softdepend: [ProtocolLib]
复制代码。我想这都是废话吧?
发包和创建数据包监听器,都需要通过ProtocolManager来做到
创建数据包监听器、如何操作数据包对象方法是
有两种方式,一个是使用PacketListener接口(就跟Bukkit的Listener差不多),还有一个是PL用来简化PacketListener的PacketAdapter抽象类,这里只介绍第二种
PacketAdapter有很多构造器,最优雅(推荐)的方法是使用PacketAdapter(AdapterParameteters params)。这个AdapterParameteters其实就是一个用于创建参数的Builder。在PacketListener法里,AdapterParameteters表现为ListeningWhitelist,也是使用Builder创建的。初始化一个AdapterParameterers的方法是PacketAdapter.params()。
(这个结构可能有些人看着不习惯,但很“优雅”
)
在AdapterParameteters的链式Builder中,只有plugin和types是必须的。其他的,如果你不显式指定,都有默认值,具体请看javadoc
还有一些方法,要不就是跟已存在的方法是一个功能,要不就是我也不知道啥意思咯,PL的坑这么深,很多方法我还没摸索出来
对了,在讲GamePhase时少了个Handshake表示握手包(请求motd和进入服务器之前发送的包),GamePhase.LOGIN = HANDSHAKE+LOGIN+STATUS就不改图片了。
注意: onPacketReceiving和onPacketSending的重写是可选的。但如果有clientSide,就必须重写onPacketReceiving否则报错。如果有serverSide,必须重写onPacketSending否则报错。
PacketTypePL把目前已知的所有包的类型都包装成了PacketType。PacketType并不代表包本身,只是身份证一样的东西,就像面向对象的Class不能等同实例。
注意PacketType不是一个enum,虽然看起来很像...
PacketType中有很多顾名思义的静态方法,相信都看得懂,我就不讲了。重点要讲的是其中的子类这些就是GamePhase,之前解释过了
上面的4个子类里,每个子类又有2个子类 (子孙满堂啊这又是把GamePhase进一步细分了。
CLIENT或SERVER的里面才是真正的包类型。例如PacketType.Play.Client.FLYING = net.minecraft.server.XXX.PacketPlayInFlying 类
要查看PacketType对应的NMS class可以使用getPacketClass()方法
另外PacketType里还有一个Legacy类,储存了1.6.4之前MC通讯协议大规模改版之前的数据包类型。
如何愉快的使用PacketEvent操作拦截到的数据包我目前搞得懂的方法,除了图中的getPacket()等,还有
如何愉快的获取数据包内的数据首先啊我们要举个梨子。
假如我们要获取的是PacketType.Play.Client.FLYING数据包
在愉快的读取其中的数据之前,我们需要找到这个数据包在NMS中是哪个类。NMS中数据包类名的命名规则如下
net.minecraft.server.XXX.PacketPlayInFlying
Packet = 这是一个数据包
Play = GamePhase不解释
In = 服务端收到的包,也就是客户端发出的包
Flying = 不解释
使用反编译器(我用的是jd-gui)找到这个类,打开(注意这个类只在核心里,bukkitapi里肯定是没有的,不然还要ProtocolLib干嘛!)
红框部分是重点(数据包类中的字段)
我们要弄明白哪个字段对应的是什么意思,储存的是什么内容。如果运气好的话,可以根据名词猜出来,,运气不好被混淆了,那就是abcd了。其中x、y、z、yaw、pitch、hasPos、hasLook都是顾名思义的。但f是什么?这个时候你有两个选择:
借助MCP。MCP一般会把字段的名字还原或加上注释的。不过MCP中数据包类名的格式可有点不一样。梨如PacketPlayInKeepAlive在1.8的MCP里是net.minecraft.network.play.client.C00PacketKeepAlive- wiki.vg/Protocol 里面有MC通讯协议的数据包描述。例如PacketPlayInFlying在这里,它告诉我们
唔,明白了,这么说那个f是onGround咯 - 把未知字段的值输出,根据客户端/服务端正在执行的操作,猜!
然后就好办了
注意:顺序很重要
够简单吧?相信都看得懂
Sending的数据包同理。修改和读取数据包的操作,在发送数据包时也要用到。
刚刚这个数据包里,字段都是基本类型。那如果是NMS中特有的类呢?总不能import吧。其实不用担心,PL都替我们想好了。
几乎所有的NMS类,在PL中都有包装。例如,GameProfile包装为WrappedGameProfile。举个梨子吧
BlockPosition类和ItemStack类(注意这个不是bukkit中的ItemStack而是在net.minecraft.server.XXX包下的ItemStack)都是NMS成员,既然不能import那怎么修改呢?
注意这个BlockPosition可不是nms中的BlockPosition,而是
NMS的包装类。还有比如NMS的IChatBaseComponent可以通过WrappedChatComponent类中的静态方法或构造器创建。所有的包装类都在com.comphenix.protocol.wrappers下,你会发现那里面还可以操作nbt=w=
PacketContainer里提供的方法,可以用于修改目前所有已知数据包的字段。里面还包括数组,List等的StructureModifier。那如果很不幸,有一个类型的字段,这里面没有提供呢?还是有办法——- StructureModifier<T> packet.getSpecificModifier(Class<T> primitiveType)
复制代码 这个方法可以获得任意类型的修改器。举个梨子,例如Pear类型的就是- packet.getSpecificModifier(Pear.class).write(0,new Pear("andylizi"));
复制代码 23333 |
向玩家发送数据包有了前面的铺垫,这个更简单了
还是举个梨子。我们要让玩家看见一个Title。
首先要找到对应的数据包PacketPlayOutTitle
唔,虽然混淆了,但是如果你能熟练使用/title命令,那很简单就可以猜得出来
/title zhouhaha(玩家) subtitle(EnumTitleAction) IChatBaseComponent内容 fadeIn(c) time(d) fadeOut(e)
c = 淡入时间,d = 持续时间(内含淡出淡入),e = 淡出时间
好办了
如果你看不懂,去看监听器那一部分的如何修改数据包部分。跟这个是一样的。
太简单了不是么?
sendServerPacket方法是发给单个玩家的,要把数据包全服广播,换成broadcastServerPacket方法就好了,具体参数自己研究吧 |
让服务端认为接受到了一个玩家发送的数据包把sendServerPacket换为recieveClientPacket,参数都一样。还需要解释么? |
就是这么简单
[groupid=324]上古之石美工工作组[/groupid]