本帖最后由 602723113 于 2021-8-18 15:00 编辑


关于 ProtocolLib 监听系统的使用与研究
MCBBS ComplexStudio 602723113

一、写在开头
1.为什么写这个教程
别问,问就是不知道它发的什么包。

2.这个教程能干什么
能让你减少问人的情况。

3.注意事项
本文所使用的 ProtocolLib 版本为 4.7.0 ,并已在 config.yml 内将 debug 进行开启。

二、使用 ProtocolLib 自带的指令

1.指令介绍
在ProtocolLib中,有一指令名为 /packet ,其指令格式见下方代码。
  1. /packet <add/remove/names> <phase> <side> [idStart-idEnd] [details]
复制代码
在上方的代码中,共有五个部分,其含义为

add
将所需要的包添加进监听当中
remove
将不需要的包移除监听
names
通过指令后面的参数查看所对应的包的名字

表1 第一部分的内容解释

phase: 此处的phase其实是指你想要监听的包是处于什么阶段,例如login, play。
side: 其实就是指包是从哪个方向发来的,有 client 和 server。
idStart-idEnd: 指定所需要监听的包的id。
details: 可以填 true或false,区别就是监听时候是否把包的细节给展示出来。

2.实验过程

我们在指令里面输入下方代码。
  1. /packet add play client 15-17 true
复制代码
意思是 监听 在 Play 阶段中 由 Client 发过来的 15号 至 17号 的数据包,并展开其详细内容。 具体效果可见图1

图1 正在监听15-17号的数据包
其他的实验内容:
  • 监听 Play 阶段下由客户端发送的所有数据包
  1. /packet add play client
复制代码
  • 监听 Login 阶段下由服务器发送的所有数据包
  1. /packet add login server
复制代码
3.实验结果
在经过实验后,我们可以得出如何使用 packet 指令来监听数据包的 一般方法 ,但是在这个方法内所得到的结果内会不断出现类似 PacketPlayInPosition 一直发送 的情况,会使得实际使用中有诸多不便。

4.实验改进
在本次实验中,我们选择使用 /packet remove 的方法将部分不重要的数据包移除监听,以下为我们选择需要过滤的数据包。
  • class=PacketPlayInTabComplete, id=6
  • class=PacketPlayInKeepAlive, id=15
  • class=PacketPlayInPosition, id=17
  • class=PacketPlayInPositionLook, id=18]
  • class=PacketPlayInLook, id=19


使用的方法:
  1. /packet add play client
  2. /packet remove play client 6
  3. /packet remove play client 15
  4. /packet remove play client 17
复制代码
5.实际结果


玩家在行进过程中,不再会因为 输入指令 或 更新Location而导致样本中无法利用的数据过多的情况。

三、调用 ProtocolLib PacketListener

在这里讲解一下怎么以代码的方式对数据包进行监听

相信大家都已经看过 andylizi 的教程 [Ni]ProtocolLib怎么玩|使用ProtocolLib发包或收包|突破Bukkit限制|只有想不到系列

在该教程中,andylizi 清晰的讲述了 如何使用ProtocolLib来收发包,监听包 的操作

那么这里有一个问题,我们要 怎么监听所有的数据包 呢?

答案便在这里 https://ci.dmulloy2.net/job/Prot ... ketType.html#values ()

这个方法返回的是 ProtocolLib 里 PacketType 的所有的数据包的 代器对象

而我们看回 PacketAdapter 的构造方法里面,有一个正好可以传入一个迭代器对象的构造方法。

1. 与之前相比的不同点
在以前我们监听数据包时,我们通常会调用 PacketAdapter$AdapterParameteters 里面的方法,在这里我们则不需要使用该方法,或者等待以后的版本什么时候 params 支持传入迭代器 了再考虑,我们可以先写出下方的代码
  1. ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(this, PacketType.values()) {
  2. @Override
  3. public void onPacketReceiving(PacketEvent event) {
  4. System.out.println("RECEIVING: " + event.getPacketType().toString());
  5. }
  6. @Override
  7. public void onPacketSending(PacketEvent event) {
  8. System.out.println("SENDING: " + event.getPacketType().toString());
  9. }
  10. });
复制代码
在实际情况中则会出现以下的效果


2.添加黑名单
与指令不同的是,我们也想 过滤 掉一些我们不需要的数据包,这个时候我们可以重写 PacketAdapter#getReceivingWhitelist PacketAdapter#getSendingWhitelist 这两个方法
我们看到下面的代码

  1. // 不接受的包的黑名单
  2. List<PacketType> blackList = new ArrayList<>();
  3. blackList.add(PacketType.Play.Client.POSITION);
  4. blackList.add(PacketType.Play.Client.LOOK);
  5. blackList.add(PacketType.Play.Client.KEEP_ALIVE);
  6. blackList.add(PacketType.Play.Client.POSITION_LOOK);
  7. blackList.add(PacketType.Play.Server.REL_ENTITY_MOVE);
  8. blackList.add(PacketType.Play.Server.REL_ENTITY_MOVE_LOOK);
  9. blackList.add(PacketType.Play.Server.ENTITY_HEAD_ROTATION);
  10. blackList.add(PacketType.Play.Server.ENTITY_TELEPORT);
  11. blackList.add(PacketType.Play.Server.ENTITY_VELOCITY);
  12. // 白名单的数据包
  13. List<PacketType> packets = new ArrayList<>();
  14. // 对黑名单进行过滤
  15. PacketType.values().forEach(packetType -> {
  16. if (!blackList.contains(packetType)) {
  17. packets.add(packetType);
  18. }
  19. });
  20. ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(this, PacketType.values()) {
  21. @Override
  22. public void onPacketReceiving(PacketEvent event) {
  23. System.out.println("RECEIVING: " + event.getPacketType().toString());
  24. }
  25. @Override
  26. public void onPacketSending(PacketEvent event) {
  27. System.out.println("SENDING: " + event.getPacketType().toString());
  28. }
  29. @Override
  30. public ListeningWhitelist getReceivingWhitelist() {
  31. // 建造白名单
  32. return ListeningWhitelist.newBuilder()
  33. .types(packets)
  34. .build();
  35. }
  36. @Override
  37. public ListeningWhitelist getSendingWhitelist() {
  38. // 建造白名单
  39. return ListeningWhitelist.newBuilder()
  40. .types(packets)
  41. .build();
  42. }
  43. });
复制代码
由于 PacketAdapter 只支持添加白名单而不是黑名单,所以我们要事先弄出白名单

实际效果
可以看到在上方的代码中,样本中少了许多无用的数据包被记录

四、最后的结论 与启示

实验最后,我们不难发现指令和代码的两种方法 各有益处 ,使用者可以 各取所需 ,用 最适合实际情况 的方法进行 灵活使用 。下方为笔者所写的两种方法的优缺点分析。


优点
缺点
方便快捷,可以快速查出数据包行为
可自定义度相较于代码程度较低
表1 指令优缺点分析


优点
缺点
自定义程度高,可直接监听 所有 Phase 的数据包,不需要打许多的指令
对于黑名单的支持不是特别的友好,需要打很多的数据包代码
表2 代码优缺点分析


身体健康,健康长寿。
某小散户
2021年8月18日

来自群组: Complex Studio