602723113
本帖最后由 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而导致样本中无法利用的数据过多的情况。

三、调用 ProtocolLibPacketListener

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

相信大家都已经看过 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#getReceivingWhitelistPacketAdapter#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

速食冻橘
终于有关于这方面的教程了!!!莫老赛高!!!

Rothes
本帖最后由 Rothes 于 2021-8-18 14:45 编辑

提醒一下 PacketType.values() 里面包含新版本/旧版本中已经弃用的数据包,如果ProtocolLib监听它们会指向其它的数据包,造成重复监听,这样会增加计算量。 实际上只要使用ProtocolLib监听,它就会解析里面的field
实际编写中应该也不会这么做的

602723113
Rothes 发表于 2021-8-18 14:44
提醒一下 PacketType.values() 里面包含新版本/旧版本中已经弃用的数据包,如果ProtocolLib监听它们会指向 ...

文章旨在生产环境中查找相关数据包,在实际使用中也确实不会这也写

cioyenn
莫老发帖了?

TengyuanDao
感谢楼主大大

az860325
感谢教程~~

70148162013
讲的非常详细,我会努力的