Minecraft数据包乘骑

我们可以不操作NMS实现实体可控乘骑吗?

可以的,主要通过数据包调整

添加监听器

首先对数据包接收进行监听

1
2
3
@SubscribeEvent
fun receive(ev: PacketReceiveEvent) {
}

对数据包处理前确认

1
2
3
4
5
6
if (ev.isCancelled) { // 如果被拦截
return
}
if (ev.packet.name != "PacketPlayInSteerVehicle") {
return
}

反编译分析

目的:获取横向速度、前进速度,跳跃
1.17+

1
2
3
4
5
6
if (ev.isCancelled) { // 如果被拦截
return
}
if (ev.packet.name != "PacketPlayInSteerVehicle") { // 数据包
return
}

从上面分析可知我们需要的是float类型的xxa和zza,以及boolean类型isJumping,从第一个开始数标注了a,b,c,d,e…也就是说我们取出c,d,e就好了

但是,1.17以下版本有所不同,继续反编译分析

1
2
3
4
5
public class PacketPlayInSteerVehicle implements Packet<PacketListenerPlayIn> {
private float a;
private float b;
private boolean c;
private boolean d;

相当简洁,显而易见a,b是我们所需的(float类型容易知道),跳跃是c

总结

版本范围 前进速度 横向速度 跳跃
1.17及以上 c d e
1.16及以下 a b c

赋值变量

1
2
3
val swSpeed = ev.packet.read<Float>(c)?: return // 前进速度(1.17+)
val adSpeed = ev.packet.read<Float>(d)?: return // 横向速度(1.17+)
val jumping = ev.packet.read<Boolean>(e)?: return // 跳跃(1.17+)

为被乘骑的实体通过数据包拦截监听玩家控制wasd赋值数据包事件中玩家的位置

1
val pLoc = ev.player.location

设置被乘骑的实体方向与玩家方向相同

复制了玩家的偏航yaw和俯仰pitch

1
vehicle.setRotation(pLoc.yaw, pLoc.pitch)

为计算运动矢量做准备

需要获取前进方向、横向方向
获取前进方向,这一点很容易,得到玩家的方向即可

1
val dir = pLoc.direction

那么怎么获取横向方向呢?答案是向量积,在API中Vector提供了crossProduct方法
直立的玩家代表了一个向量,这个向量总是垂直于平面,该向量的特征为*[0, ±1, 0]*
图片

根据图中方法,将得到一个指向右侧的横向向量

1
val sideways = dir.clone().crossProduct(Vector(0, -1, 0))

有了前进方向和横向方向后,可以进行矢量计算了前进方向乘积前进速度并对横向方向乘积横向速度

1
val all = dir.multiply(adSpeed).add(sideways.multiply(swSpeed))

然后设置被乘骑的实体的速度

1
vehicle.velocity = vehicle.velocity.add(all)

测试以上代码后会发现这些问题

  • 速度过快
  • 如何跳跃

速度过快

可以对前进方向和横向方向进行除法运算,适当降低速度

1
2
adSpeed/10
swSpeed/5

如何跳跃

对变量all设置向上y坐标,首先判断被乘骑的实体是否在地面,否则给予0.0

1
all.y = (if (jumping && vehicle.isOnGround) 0.5 else 0.0)

为了减缓在空中降落速度,适当调整一下

1
if (!vehicle.isOnGround) all.multiply(0.4)

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@SubscribeEvent
fun receive(ev: PacketReceiveEvent) {
if (ev.isCancelled) return
if (ev.packet.name != "PacketPlayInSteerVehicle") return
val vehicle = ev.player.vehicle?: return
val actionType = vehicle.getMeta("drive")?: return
// 1.16.5及以下分别是a b c
val list = if (MinecraftVersion.isUniversal) listOf("c", "d", "e") else listOf("a", "b", "c")
val swSpeed = ev.packet.read<Float>(list[0])?: return // 前进速度
val adSpeed = ev.packet.read<Float>(list[1])?: return // 横向速度
val jumping = ev.packet.read<Boolean>(list[2])?: return
val pLoc = ev.player.location
vehicle.setRotation(pLoc.yaw, pLoc.pitch)
val forwardDir = pLoc.direction
val sideways = forwardDir.clone().crossProduct(Vector(0, -1, 0))
val total = forwardDir.multiply(adSpeed/10).add(sideways.multiply(swSpeed/5))
if (actionType == ModelManager.ActionType.FLY) {
total.y = if (jumping) 0.5 else 0.0
}else {
total.y = (if (jumping && vehicle.isOnGround) 0.5 else 0.0)
}
if (!vehicle.isOnGround) total.multiply(0.4)
vehicle.velocity = vehicle.velocity.add(total)
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!