--- front: hard: 进阶 time: 25分钟 --- # 数据包基本运用 ## 介绍 本章 也可以说是发包的介绍 如何拦截数据包进行篡改 来达到效果 或者是直接发送一个数据包 让客户端收到 ![](../images/0_30.png) ## 篡改数据包 如上图所示 我们有两种类型的事件 1. PacketReceiveEvent 玩家发送给服务端的数据包 2. PacketSendEvent 服务端发送给客户端的数据包 ## 例子 ```kotlin @SubscribeEvent fun onPacketPlayOutEntityEquipment(event: PacketSendEvent) { if (event.packet.name != "PacketPlayOutEntityEquipment") { return } val item = event.packet.read>>("slots") ?: return val copy = mutableListOf>() item.forEach { val events = PacketReadItemEvent(event.player, toBukkit(it.second)) events.call() copy.add(MoJangPair(it.first, toNMSCopy(events.itemStack))) } event.packet.write("slots", copy) } ``` ## 发送数据包 我们需要构造一个数据包的对象 以发送一个虚拟头颅方块的数据给玩家为例子 ```kotlin val parse = MojangsonParser.parse("""{Owner:{Id:"014df015-7eba-4ad0-a0e0-83164b7a45f2",Properties:{textures:[{Value:"方块的贴图"}]},Name:"自定义方块"},Rot:${rot}b,x:${x},y:${y},z:${z},id:"minecraft:skull",SkullType:3b}""".trimIndent()) val packetPlayOutTileEntityData = PacketPlayOutTileEntityData( BlockPosition(loc.blockX, loc.blockY, loc.blockZ), 4, parse ) player.sendPacket(packetPlayOutTileEntityData) ``` ## 复杂的例子 **发送 BossBar 实现** 注意:这里我们减少了一些对于数据包本身的叙述(例如如何寻找我要的包的类型,这些应该是必备技能),仅介绍 TabooLib 方法。 根据常识可知,我们应该发送 PacketPlayOutBoss 这个数据包。我们去寻找这两个包的构造参数。(这里我使用反编译的办法) 1.16 及以下的版本的参数如下: ![](../images/0_31.png) 1.17 及以上的版本的参数如下: (这就是 TabooLib 带给我们的自信,我们可以直接使用反混淆版的服务端,由此我们可以很清楚的看到 id、operation 等字段) 可知 1.16 及以下版本有一个无参构造函数,1.17+ 没有(有一个私有构造函数,不方便)。对于没有无参构造函数的类,我们可以通过 TabooLib Reflex 提供的函数 `Class#unsafeInstance()` 来快速获得一个实例。 ![](../images/0_32.png) 我们可以看到在 1.17+ 中,我们使用的全是反混淆(Mojang Mapping)的字段名。不用担心,TabooLib 会自动帮我们处理。 外部调用方法: NMS.INSTANCE.sendBossBar() 使用到的别名如下: ```kotlin // 1.16 typealias NMS16PacketPlayOutBoss = net.minecraft.server.v1_16_R3.PacketPlayOutBoss typealias NMS16PacketPlayOutBossAction = net.minecraft.server.v1_16_R3.PacketPlayOutBoss.Action typealias NMS16BossBattleBarColor = net.minecraft.server.v1_16_R3.BossBattle.BarColor typealias NMS16BossBattleBarStyle = net.minecraft.server.v1_16_R3.BossBattle.BarStyle typealias CraftChatMessage16 = org.bukkit.craftbukkit.v1_16_R3.util.CraftChatMessage // Universal typealias NMSPacketPlayOutBoss = net.minecraft.network.protocol.game.PacketPlayOutBoss typealias NMSBossBattleBarColor = net.minecraft.world.BossBattle.BarColor typealias NMSBossBattleBarStyle = net.minecraft.world.BossBattle.BarStyle typealias CraftChatMessage19 = org.bukkit.craftbukkit.v1_19_R3.util.CraftChatMessage ``` 可以看到,1.16 以下的版本中,我们只写了一套 1.16 的实现,实际上在 1.15、1.14 等版本也可运行,因为 TabooLib 会自动帮我们处理 nmsProxy 内的跨版本和混淆表。 **数据包监听器** TabooLib 有数据包监听器,我们可以监听数据包发送和接收。 数据包不会自动处理跨版本和混淆表。 使用方法: ```kotlin /** * 数据包接收 */ @SubscribeEvent fun e(e: PacketReceiveEvent) { // 随便拿一个数据包举例子 if (e.packet.name == "PacketPlayInSetCreativeSlot") { // 读 val nmsItem = e.packet.read("b")!! // 写 e.packet.write("b", nmsItem) } } /** * 数据包发送 */ @SubscribeEvent fun e(e: PacketSendEvent) { // 随便拿一个数据包举例子 if (e.packet.name == "PacketPlayOutOpenWindowMerchant") { // 读 val merchant = e.packet.read("b")!! // 写 e.packet.write("b", NMS.INSTANCE.adaptMerchantRecipe(merchant, e.player)) } } ```