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
相关产品推荐
相关产品推荐

