You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python开发Bedrock服务器如何解析二进制包获取ID与字段值?

解析方法说明

你用packet[0]取包ID的逻辑,在已经剥离RakNet底层帧头的游戏层负载数据上是完全正确的——基岩版协议所有游戏层数据包的第一个无符号字节就是包ID,0x01对应的就是登录包。

基岩版数据包没有特殊的字段分隔符,所有字段严格按照协议文档定义的类型、顺序紧密排列成二进制流,解析时必须按顺序逐段读取对应类型的内容,不能靠固定偏移或者分隔符切分。

常见字段类型解析规则

解析时逐字段匹配类型读取即可,常用类型的编码规则如下:

  • 固定长度数值:u8占1字节、u16占2字节、u32占4字节、u64占8字节,游戏层所有多字节固定数值均为小端序;
  • 变长整数(varint/varlong):采用和Protobuf一致的变长编码+ZigZag符号映射,每个字节低7位存有效数据,最高位标记后续是否还有字节,长度1-5字节(varint)/1-10字节(varlong);
  • 字符串/二进制块:先以varuint编码存储内容的字节长度,后面紧跟对应长度的原始字节(字符串为UTF-8编码);
  • 数组/嵌套结构:数组先以varuint存储元素个数,再按顺序逐个解析元素;嵌套结构按相同规则递归解析即可。

Python 原生实现示例

建议先实现一个简单的二进制流读取器维护读取偏移,避免手动计算偏移出错:

import struct

class BedrockPacketReader:
    def __init__(self, payload: bytes):
        self.buf = payload
        self.pos = 0

    def read_u8(self) -> int:
        val = self.buf[self.pos]
        self.pos += 1
        return val

    def read_i32_le(self) -> int:
        val = struct.unpack_from("<i", self.buf, self.pos)[0]
        self.pos += 4
        return val

    def read_varint(self) -> int:
        val = 0
        shift = 0
        while True:
            b = self.buf[self.pos]
            self.pos += 1
            val |= (b & 0x7F) << shift
            shift += 7
            if not (b & 0x80):
                break
        # ZigZag解码转有符号整数
        return (val >> 1) ^ -(val & 1)

    def read_varuint(self) -> int:
        val = 0
        shift = 0
        while True:
            b = self.buf[self.pos]
            self.pos += 1
            val |= (b & 0x7F) << shift
            shift += 7
            if not (b & 0x80):
                break
        return val

    def read_string(self) -> str:
        length = self.read_varuint()
        content = self.buf[self.pos:self.pos+length].decode("utf-8")
        self.pos += length
        return content

    def read_binary(self) -> bytes:
        length = self.read_varuint()
        content = self.buf[self.pos:self.pos+length]
        self.pos += length
        return content

解析0x01登录包的参考逻辑(注意传入的必须是剥离RakNet帧后的游戏层负载):

# 假设game_payload是RakNet层解析完成后输出的游戏层字节数据
reader = BedrockPacketReader(game_payload)
packet_id = reader.read_u8()

if packet_id == 0x01:
    # 以下字段顺序、类型严格匹配你参考的对应版本协议文档即可
    client_protocol = reader.read_varint() # 第一个字段:客户端协议版本
    auth_chain = reader.read_string()      # 第二个字段:身份认证JWT链
    client_public_key = reader.read_binary() # 第三个字段:客户端公钥
    print(f"包ID: 0x{packet_id:02x}")
    print(f"客户端协议版本: {client_protocol}")

常见踩坑

  • 不要直接解析UDP收到的原始字节:基岩版通信基于UDP,外层封装了RakNet协议帧,帧头包含可靠性标记、帧序号、分片信息等内容,必须先完成RakNet层的解析、分片重组,拿到内层的游戏负载后再按游戏协议解析,否则读出来的字段会完全错位。
  • 不要硬编码字段偏移:除了固定长度的数值类型,其余字段都是变长的,必须先读长度标记再读取内容,不能按固定位置切片。
  • 注意版本匹配:基岩版协议随版本迭代会调整字段顺序、类型,解析逻辑必须和你要兼容的客户端版本的协议定义完全对应。

内容的提问来源于stack exchange,提问作者nocyt543

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 00:51:24