"""分层可导入、核心契约、消息号无碰撞 — 骨架期测试的真实现版。""" import pbot from pbot.codec.frame import Frame, build_frame, parse_frame from pbot.game.messages import CsMsg, ScMsg from pbot.types import PointCode, pack_point, unpack_point from pbot.world.mirror import WorldMirror from pbot.world.update import ( WorldUpdater, body_json, decode_body_variants, y_with_flag, ) def sc(mid: int, recog: int, p1: int, p2: int, p3: int, body: bytes = b"") -> "Frame": """构造服务器帧(测试辅助): build → parse 得 Frame。""" return parse_frame(build_frame(mid, recog, p1, p2, p3, body)) def test_top_level_exports_are_importable() -> None: """Given: 包顶层声明公共契约 When: 逐个访问 Then: 全部存在。""" for name in pbot.__all__: assert getattr(pbot, name) is not None def test_pack_point_roundtrip() -> None: """Given: 平面坐标 When: pack 后 unpack Then: 还原原值 (Y<<16|X 布局)。""" code: PointCode = pack_point(289, 615) assert code == (615 << 16) | 289 assert unpack_point(code) == (289, 615) def test_sc_and_cs_msg_ids_are_unique_within_each_table() -> None: """Given: 两个消息号表 When: 取值集合 Then: 各自内部无重复值。""" sc_values = [m.value for m in ScMsg] cs_values = [m.value for m in CsMsg] assert len(sc_values) == len(set(sc_values)) assert len(cs_values) == len(set(cs_values)) def test_all_game_layers_importable() -> None: """Given: 分层架构 When: 自底向上导入每层 Then: 无循环依赖错误。""" from pbot import bot, codec, game, login, transport, world assert bot is not None and codec is not None assert game is not None and login is not None assert transport is not None and world is not None def test_cs_walk_layout_matches_capture() -> None: """Given: 实测帧布局 When: 检查关键消息号 Then: 与 backup 实测值一致。""" assert CsMsg.WALK == 3011 assert CsMsg.RUN == 3013 assert CsMsg.ATTACK == 3014 assert CsMsg.HEARTBEAT == 60000 assert ScMsg.BATCH == -1 def test_y_flag_bit_masked() -> None: """Given: Y 带 bit11 标志(2048) When: _y_with_flag Then: 掩掉标志位。 实测(quest_bot3 SC51 解析): bit11 是编码标志非坐标 — 旧实现把它当坐标 导致出生坐标整体读错(如 (23,25) 读成 (25,0))。 """ assert y_with_flag(2048 + 25) == 25 assert y_with_flag(25) == 25 def test_body_json_decode6_variant() -> None: """Given: encode6(JSON) body(SC11 同款) When: body_json Then: 解出 dict。""" from pbot.codec.bitbuf import encode6 raw = b'{"UserID":"123","X":10,"Y":20}' f = sc(11, 10, 20, 0, 0, body=encode6(raw)) j = body_json(f) assert j["UserID"] == "123" and j["X"] == 10 def test_body_variants_zlib_wrapped() -> None: """Given: zlib(encode6(JSON)) body When: decode_body_variants Then: 含明文 JSON。""" import zlib from pbot.codec.bitbuf import encode6 inner = encode6(b'{"MapName":"xx"}') blob = zlib.compress(inner) variants = decode_body_variants(blob) assert b'{"MapName":"xx"}' in variants def test_sc51_sets_walk_position() -> None: """Given: SC51(recog=22, p1=26, body=MapName) When: apply Then: 位置=SC51 坐标(cap_live2 定案: 真客户端首步起点=(22,26) 与 SC51 一致, SC50.p2 是回家位墙格不采信)。""" f = sc(51, 22, 26, 2048, 0, body=b'{"MapName":"xx","MapId":"0"}') world = WorldMirror() world.me.x, world.me.y = 0, 1 # SC50 的回家位(墙格) WorldUpdater().apply(world, f) assert world.me.map_name == "xx" assert world.me.map_id == "0" assert (world.me.x, world.me.y) == (22, 26) # SC51 覆盖为真实位置 assert any(e.kind == "map" for e in world.events) def test_sc28_pull_back_updates_authority() -> None: """Given: 主玩家在(20,26), SC28(recog=21,p1=27,p2=dir) When: apply Then: 权威位更新为 (21,27)(SC28 布局 recog=X, p1=Y, p2=dir — 盟重探针定案)。""" world = WorldMirror() world.me.x, world.me.y = 20, 26 f = sc(28, 21, 27, 4, 0) WorldUpdater().apply(world, f) assert (world.me.x, world.me.y) == (21, 27) assert world.me.dir == 4 def test_sc28_zero_frame_ignored() -> None: """Given: SC28 (0,0) 清位帧 When: apply Then: 忽略, 坐标不变。""" world = WorldMirror() world.me.x, world.me.y = 20, 26 f = sc(28, 0, 0, 0, 0) WorldUpdater().apply(world, f) assert (world.me.x, world.me.y) == (20, 26) def test_sc11_self_echo_confirms_move() -> None: """Given: 主玩家 uid=u1 在(20,26), SC11 body.UserID=u1 recog=21,p1=2048+27 When: apply Then: 权威坐标更新 (21,27) — 自回显即服务器确认移动。""" from pbot.codec.bitbuf import encode6 world = WorldMirror() world.me.uid = "u1" world.me.x, world.me.y = 20, 26 body = encode6(b'{"UserID":"u1"}') f = sc(11, 21, 2048 + 27, 0, 0, body=body) WorldUpdater().apply(world, f) assert (world.me.x, world.me.y) == (21, 27) assert any(e.kind == "self_move_confirmed" for e in world.events) def test_sc53_hp_pipe_format() -> None: """Given: SC53 body=encode6("u1&95&100&30&50|1") When: apply Then: HP=95/100。""" from pbot.codec.bitbuf import encode6 world = WorldMirror() world.me.uid = "u1" f = sc(53, 0, 0, 0, 0, body=encode6(b"u1&95&100&30&50|1")) WorldUpdater().apply(world, f) assert (world.me.hp, world.me.max_hp) == (95, 100)