Files
netease-modsdk-wiki/docs/mcguide/28-电脑网络游戏/课程4:常见第三方库教程/31-TabooLib/1-基础技术/11-数据包基本运用.md
2025-12-02 15:32:53 +08:00

115 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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<List<MoJangPair<EnumItemSlot, ItemStack>>>("slots") ?: return
val copy = mutableListOf<MoJangPair<EnumItemSlot, ItemStack>>()
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<T>#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<Any>("b")!!
// 写
e.packet.write("b", nmsItem)
}
}
/**
* 数据包发送
*/
@SubscribeEvent
fun e(e: PacketSendEvent) {
// 随便拿一个数据包举例子
if (e.packet.name == "PacketPlayOutOpenWindowMerchant") {
// 读
val merchant = e.packet.read<Any>("b")!!
// 写
e.packet.write("b", NMS.INSTANCE.adaptMerchantRecipe(merchant, e.player))
}
}
```