"""验证 protodef 解释器能真跑协议——用真实握手字节。""" import sys, os, json, struct sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from mcfold.protodef import ProtoDef, Reader, Writer, ProtoError, for_phase DATA='/home/newqqagent/plugin_data/mc/runtime/node_modules/minecraft-data/minecraft-data/data/pc/1.20/protocol.json' def load(): d=json.load(open(DATA)) return d def pd_for(d, phase, side): return for_phase(d, phase, side) def test_varint_roundtrip(): """varint 是 MC 最核心的原语;含负数回绕。""" load() for v in [0,1,127,128,255,300,25565,2097151,-1,-2147483648,2147483647]: w=Writer(); w.varint(v) got=Reader(w.getvalue()).varint() assert got==v, f"varint {v} -> {got}" print(" ✓ varint 往返(含负数/边界)") def test_pstring(): load() for s in ["", "abc", "中文名字", "x"*300]: w=Writer(); w.pstring(s) assert Reader(w.getvalue()).pstring()==s print(" ✓ pstring 往返(含 UTF-8/长串)") def test_real_handshake(): """构造真实的 handshake+login_start 字节并解出(最硬的检验)。""" d=load() pd=pd_for(d,'handshaking','toServer') # handshaking toServer packet_set_protocol w=Writer() w.varint(0x00) # packet id (handshaking) w.varint(763) # protocol version = 1.20.1 w.pstring("127.0.0.1") # server address w.write(struct.pack(">H", 25565)) # port w.varint(2) # next state = login raw=w.getvalue() r=Reader(raw) pkt=pd.read(d['handshaking']['toServer']['types']['packet'], r) assert pkt['name']=='set_protocol', pkt p = pkt['params'] # switch 的参数落在 params 下 assert p['protocolVersion']==763, p assert p['serverHost']=='127.0.0.1', p assert p['serverPort']==25565, p assert p['nextState']==2, p assert r.remaining()==0, f"有残留字节: {r.remaining()}" print(f" ✓ 真实 handshake 解码: proto={p['protocolVersion']} host={p['serverHost']}:{p['serverPort']} nextState={p['nextState']}") def test_login_success_decode(): """packet_success: uuid + username + properties(真实形状)。""" d=load() pd=pd_for(d,'login','toClient') w=Writer() w.varint(0x02) # login toClient packet_success id w.write(bytes(range(16))) # uuid w.pstring("BotName") w.varint(0) # properties count = 0 r=Reader(w.getvalue()) pkt=pd.read(d['login']['toClient']['types']['packet'], r) assert pkt['name']=='success', pkt p = pkt['params'] assert p['username']=='BotName', p assert p['properties']==[], p print(f" ✓ login success 解码: uuid={p['uuid'][:8]}… username={p['username']}") def test_play_packet_mapper(): """play 阶段 111 个包由 mapper 分发 —— 验证能正确路由。""" d=load() pd=pd_for(d,'play','toClient') w=Writer(); w.varint(0x00) # 0x00 = bundle_delimiter(无字段) r=Reader(w.getvalue()) pkt=pd.read(d['play']['toClient']['types']['packet'], r) assert pkt['name']=='bundle_delimiter', pkt print(f" ✓ play 包路由: 0x00 -> {pkt['name']}") # 0x10 = declare_commands(我们做 mod 命令发现要用的包) w2=Writer(); w2.varint(0x10) r2=Reader(w2.getvalue()) try: pkt2=pd.read(d['play']['toClient']['types']['packet'], r2) print(f" ✓ declare_commands 路由: 0x10 -> {pkt2.get('name')}") except ProtoError as e: print(f" ~ declare_commands 需要更多字节(预期,因为只喂了 id): {str(e)[:40]}") def test_no_overshoot(): """越界必须报错而不是静默读错 —— 这是我实测 mineflayer 静默失真的对照。""" d=load() pd=pd_for(d,'login','toClient') r=Reader(b"\x05") # varint 说 5 字节字符串,实际没有 try: pd.read("string", r); assert False, "应当抛错" except ProtoError as e: print(f" ✓ 越界显式报错(不静默): {str(e)[:50]}") if __name__=="__main__": for fn in [test_varint_roundtrip, test_pstring, test_real_handshake, test_login_success_decode, test_play_packet_mapper, test_no_overshoot]: fn() print("\nprotodef 解释器:全部通过")