本帖最后由 狡诈师 于 2021-1-16 14:01 编辑

相关链接



对于ProtocolLib用法我摸索了很久才慢慢懂了( 苦了娃 ),不过还不完全掌握
从这里你可以学到:
  • 了解ProtocolLib作用
  • 了解Wiki的格式表
  • 实践发送包-> 发送数据包盔甲架
  • 实践监听包-> 交互数据包盔甲架

阅读前
  • 请具备 Java or Kotlin 基础知识



了解ProtocolLib作用
Wiki 可以看到ProtocolLib可以做些什么

比如[ SPAWN ENTITY ],向某个玩家显示该实体而其它玩家没有看到(也就是说发送了[生成实体"SPAWN ENTITY"]包给某个玩家客户端,而服务器和未收到该包的玩家客户端不知道这个实体的存在)

引用 @梨子 https://www.mcbbs.net/thread-568714-1-1.html
  • 给玩家发送一个伪造的数据包
  • 让服务端认为接收到了一个玩家发送的数据包
  • 监听/修改 客户端发送给服务端 的数据包
  • 监听/修改 服务端发送给客户端 的数据包
补充
  • 实际上,你可以不需要ProtocolLib进行发送数据包,但是你会受到版本限制,而ProtocolLib更新很快,而且兼容超多版本,并且提供了Wiki详细的数据包描述
  • 拦截数据包
  • 还有什么..?

发送欺骗服务端/客户端数据包



了解Wiki的格式表
看看Wiki数据包描述,以 [SPAWN ENTITY] 为例
该数据包目的:发送生成实体的数据包( :纯发包的实体不能通过Bukkit获取实体,毕竟服务端不知道它的存在)

首栏解析
  • Packet ID:在?还需要解释吗
  • State:状态(这里是Play,其它还有Login等等)
  • Bound To:发送目标(这里指发送给客户端)
  • Field Name:字段名称
  • Field Type:字段类型
  • Notes:显而易见, 别跟我说你不懂,雾
字段名称与字段类型
下面 必须用到 ,你可以想象,你要是不填,会发生什么事
  • Entity ID [实体ID,唯一,否则直接覆盖] -> VarInt[int类型]
  • Object UUID -> UUID (懂的都懂)
  • Type [实体类型] -> 请在此处查找相关实体类型: https://wiki.vg/Entity_metadata#Mobs
  • X [位置的X值] -> Double类型
  • Y [位置的Y值] -> Double类型
  • Z [位置的Z值] - > Double类型
剩下的将只是默认值, 一般不需要 自己设置
  • Pitch [向量] -> Angle值
  • Yaw [向量] -> Angle值
  • Data [数据] -> Int值,含义取决于 Type 值,相关内容请看: https://wiki.vg/Object_Data
  • Velocity X [速度] -> Short值
  • Velocity Y [速度] -> Short值
  • Velocity Z [速度] -> Short值



实践发送包,以发送数据包盔甲架为例
既然了解了格式,那么如何使用呢?

2020/10/17 :本文基于 1.16.X预览版 (部分 不适用 于旧版)
Kotlin
  • 我用Kotlin不反对吧? (这样我可以防止Java复制党,滑稽)
  • 如果你是Java使用者,实际上你还是能看得出来,诸如 integers 在Java中是 getIntegers
  1. fun spawnAS(player: Player, entityID: Int, loc: Location) { // 构造方法,在Java中是void型
  2. val packet = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY) // 示例一个PacketContainer
  3. packet.modifier.writeDefaults() // 写入默认值

  4. packet.integers.write(0, entityID) // 对integers的第0个写入实体ID,注意这里,实体ID不要重叠,否则会被覆盖
  5. packet.uuiDs.write(0, UUID.randomUUID()) // 对UUID的第0个写入UUID值,这里使用了随机生成UUID值
  6. packet.entityTypeModifier.write(0, EntityType.ARMOR_STAND) 对entityType的第0个写入实体类型,这里写入了盔甲架类型
  7. packet.doubles.write(0, loc.x) // 对Double的第0个写入位置的X值,不写的话,你知道它在哪个角落躲着吗
  8. packet.doubles.write(1, loc.y) // 对Double的第1个写入位置的Y值
  9. packet.doubles.write(2, loc.z) // 对Double的第2个写入位置的Z值,这些顺序参照格式表(自己回去看格式表,第三个出现的Double就是位置的Z值)

  10. sendServerPacket(player, packet) // 发送数据包
  11. }

  12. fun sendServerPacket(player: Player, packet: PacketContainer) { // 发送数据包方法
  13. ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
  14. }
复制代码



这里我补充下数据包类型 PacketType 内容吧
https://ci.dmulloy2.net/job/ProtocolLib/javadoc/com/comphenix/protocol/PacketType.html
  • ForceAsync:数据包是否必须异步处理
  • Handshake:握手时发送的数据包
  • Login:登录时发送和接收的数据包
  • Play:在游戏时发送和接收的数据包
  • Protocol:不同的协议或连接状态
  • Sender:数据包类型的发送者
  • Status:[不太懂the multiplayer menu指什么菜单 ? ]

  • Client:来自客户端的数据包
  • Server:来自服务端的数据包




数据包监听器

若要监听服务器向客户端发送的数据包
Java
首先在onEbale中实例化
  1. private ProtocolManager protocolManager;

  2. public void onLoad() {
  3. protocolManager = ProtocolLibrary.getProtocolManager();
  4. }
复制代码
  1. protocolManager.addPacketListener(
  2. new PacketAdapter(主类, ListenerPriority.NORMAL, /*监听器级别*/
  3. PacketType.Play.Server.NAMED_SOUND_EFFECT/*数据包类型,具体看wiki*/) {
  4. @Override // 覆写发送的包,必须是onPacketSending,不然叫什么覆写
  5. public void onPacketSending(PacketEvent event) { // 监听服务器发送数据包
  6. if (event.getPacketType() == PacketType.Play.Server.NAMED_SOUND_EFFECT) { // 判断数据包类型,若是音乐数据包
  7. event.setCancelled(true); // 取消该事件,也就是说,服务器发送的音乐数据包被被拦截,客户端不会收到该数据包
  8. }
  9. }
  10. });
复制代码
这个event事件包含的方法你可以通过doc看:
  • https://ci.dmulloy2.net/job/ProtocolLib/javadoc/com/comphenix/protocol/events/PacketEvent.html
这方面梨子讲解过了,我就不重复了:
  • https://www.mcbbs.net/thread-568714-1-1.html

当然,你可以监听客户端接收的数据包
这里以 交互数据包盔甲架 为例子
Kotlin (喂?Java呢?你看上面不就行吗,只是有一些不同罢了 防伸手党滑稽
和上面一样,需要被onEnable调用
在此之前,请翻阅wiki: https://wiki.vg/Protocol#Interact_Entity
  1. protocolManager.getProtocolManager().addPacketListener(object :
  2. PacketAdapter(主类,
  3. ListenerPriority.NORMAL,
  4. PacketType.Play.Client.USE_ENTITY) { // 数据包类型为交互实体
  5. override fun onPacketReceiving(ev: PacketEvent) { // 覆写接收的包,必须是onPacketReceiving,不然叫什么覆写
  6. val packet = ev.packet // 获得数据包
  7. packet.integers.read(0) // 读取数据包
  8. if (packet.entityUseActions.values[0] != EnumWrappers.EntityUseAction.INTERACT) return // 判断交互动作
  9. // 做你想做的事情
  10. }
  11. })
复制代码

注意 值得一提的是,若数据包盔甲架关闭了碰撞箱,是不会被交互,无法监听到






来自群组: Server CT