# -*- coding: utf-8 -*- """ run_mine998.py —— T04/T05 定案协议的首次无客户端实机验证: 创角(最新区) → 开喝药 → 领锄头(NPC 10041/43 → @func38_get_hzc → CS1003 穿锄) → 原地挖矿(CS3014 skillID=999 @0.68s 面壁) → 统计产物播报/金币。 协议依据: protocol_bot/docs/协议定案-神通开启与五行之力.md 附录A/B。 """ import argparse import json import random import re import sys import time import zlib import requests as rq import urllib3 urllib3.disable_warnings() HERE = __file__.rsplit("/", 1)[0] sys.path.insert(0, HERE) from encode6bitbuf_final import decode6 # noqa: E402 import login_996pc as L # noqa: E402 import cq2_pure_enter as P # noqa: E402 from combat_bot import (CombatBot, enter_game_gate_v2, # noqa: E402 send_init_frames, log) def send_qf(bot, act): body = json.dumps({"UserID": "999999999", "Act": act, "index": 999999999}, separators=(",", ":")).encode() bot.send(1011, 0, 0, 0, bot.sid, body) def click_npc(bot, cfg_id, index): body = json.dumps({"UserID": str(cfg_id), "index": index}, separators=(",", ":")).encode() bot.send(1010, 0, 0, 0, bot.sid, body) def mine_once(bot, px, py, d, mx, my): payload = {"skillID": 999, "dir": d, "srcX": px, "srcY": py, "destX": mx, "destY": my} bot.send(3014, 0, 0, 0, 0, json.dumps(payload, separators=(",", ":")).encode()) def main(): ap = argparse.ArgumentParser() ap.add_argument("-u", "--user", default="longwu998") ap.add_argument("-p", "--password", default="Longwu1988") ap.add_argument("--zone", type=int, required=True) ap.add_argument("--name", default=None) ap.add_argument("--minutes", type=float, default=5.0) ap.add_argument("--mine-until", default="", help="挖矿绝对截止时刻 HH:MM(本地时间); 优先于 --minutes") a = ap.parse_args() name = a.name or ("矿工" + str(random.randint(1000, 9999))) log("[1/7] HTTP登录...") s = rq.Session() j = None for att in range(5): try: j, _ = L.do_login(s, a.user, a.password) if j.get("code") == 200: break except Exception as e: log(f" 异常({att+1}): {str(e)[:60]}") time.sleep(10) if not j or j.get("code") != 200: raise SystemExit("[!] 登录失败") jwt = j["data"]["token"] uid = str((j["data"].get("userInfo") or {}).get("id") or P.UID) log(f"[2/7] 选区 {a.zone}...") j2 = None for att in range(5): try: j2 = P.enter_game(s, jwt, a.zone) if j2.get("code") == 200: break except Exception as e: log(f" 异常({att+1}): {str(e)[:60]}") time.sleep(10) if not j2 or j2.get("code") != 200: raise SystemExit("[!] enter_game 失败") log(f"[3/7] 登录服+创角/复用 {name}...") ok = None for pt in (8041, 8021, 8061, 8001, 8063, 8043): try: r = P.login_gate(a.zone, jwt, name, pt, uid) if r and r[0]: ok = r break except SystemExit as e: log(f" 端口{pt}: {e}") except Exception as e: log(f" 端口{pt}: {str(e)[:50]}") if not ok: raise SystemExit("[!] 所有端口失败") key32, sid, rname, tkey, gp = ok log(f" 角色={rname} 游戏端口={gp}") log("[4/7] 进世界...") g = enter_game_gate_v2(a.zone, rname, tkey, sid, gp, uid) bot = CombatBot(g, sid, rname) P.heartbeat_loop(g, sid, sender=bot.send) t0 = time.time() while time.time() - t0 < 10: bot.pump(0.3) if 658 in bot.frame_log: break send_init_frames(bot, uid) for _ in range(24): bot.pump(0.5) if bot.me["id"] and bot.me.get("mapname"): break if not bot.me["id"]: g.close() raise SystemExit("[!] 未收到 SC50") px, py = bot.me["x"], bot.me["y"] log(f" 主玩家 @({px},{py}) map={bot.me.get('mapname')}") log("[5/7] 开喝药...") st = P.enable_auto_heal(g, sid, want_on=True) log(f" 喝药={st}") time.sleep(1) bot.pump(0.5) state = {"hoe": False, "wait5": False, "over": False, "loot": 0, "gold": 0} def on_frame(mid, recog, p1, p2, p3, body): if mid in (51, 50, 1, 28): log(f" [pos] SC{mid} raw p1={p1} p2={p2} recog={recog} → me=({bot.me['x']},{bot.me['y']})") if mid not in (-1, 100): return try: if body[:2] == b"x\x9c": body = zlib.decompress(body) txt = decode6(body).decode("utf-8", "replace") except Exception: return if '"Index":50001' in txt and "makeindex" in txt: state["hoe"] = True elif "5分钟后" in txt: state["wait5"] = True elif "挖矿活动已结束" in txt: state["over"] = True elif "挖矿获得" in txt and rname in txt: state["loot"] += 1 m = re.search(r"挖矿获得:.*?>([^<>]+) state["gold"]: state["gold"] = int(mg.group(1)) bot.raw_frame_hook = on_frame log("[6/7] 走到矿场总兵(17,23)旁 + 领锄头...") log(f" 视野内NPC: {[(v['aid'],v['name'],v['x'],v['y']) for v in bot.npcs.values()]}") # 目标 (17,24)=总兵(17,23)相邻可走格(0139.map 实证); M2 封速: 移动间隔须≥1.4s guard = 0 while (abs(bot.me["x"] - 17) > 1 or abs(bot.me["y"] - 23) > 1) and guard < 60 and bot.alive: bot.step_toward(17, 24) evs = bot.pump(0.6) acks = [e for e in evs if e[0] == "pull_back"] time.sleep(1.1) guard += 1 if guard % 10 == 0: log(f" 走步{guard}: me=({bot.me['x']},{bot.me['y']}) 拉回={len(acks)}") px, py = bot.me["x"], bot.me["y"] log(f" 现在站位 ({px},{py}) 距总兵(17,23) dx={abs(px-17)} dy={abs(py-23)}") got = False for att in range(10): state["hoe"] = state["wait5"] = state["over"] = False click_npc(bot, 10041, 43) time.sleep(1.2) bot.pump(0.6) send_qf(bot, "@func38_get_hzc") t1 = time.time() while time.time() - t1 < 4: bot.pump(0.3) if state["hoe"] or state["wait5"] or state["over"]: break if state["hoe"]: got = True log(" ✓ 鹤嘴锄已入包(Index:50001)") break if state["wait5"]: wait_s = 65 if att < 6 else 30 log(f" ⏳ 未到开区5分钟, 等{wait_s}s重试...") time.sleep(wait_s) continue if state["over"]: log(" ✗ 挖矿活动已结束(20分钟窗口过)") break log(f" 尝试{att+1}无响应, 5s后重试...") time.sleep(5) if not got and not state["over"]: log(" [!] 未确认入包(可能已领过), 继续穿装备尝试") if state["over"]: g.close() raise SystemExit("[!] 20分钟窗口已过 — 等下一轮新区") log(" CS1003 穿锄头...") bot.send(1003, 0, 0, 0, bot.sid, b"") time.sleep(1) bot.pump(0.5) log("[7/7] 挖矿循环 (0.68s/锄)...") mx, my, d = px, py - 1, 0 if abs(px - 24) <= 1 and abs(py - 30) <= 1: mx, my, d = 24, 29, 0 log(f" 站({px},{py}) 挖({mx},{my}) dir={d}") if a.mine_until: hh, mm = a.mine_until.split(":") nw = time.localtime() dl = time.mktime((nw.tm_year, nw.tm_mon, nw.tm_mday, int(hh), int(mm), 0, 0, 0, -1)) if dl < time.time(): dl += 86400 deadline = dl log(f" 挖矿至 {a.mine_until} ({int(deadline-time.time())}s)") else: deadline = time.time() + a.minutes * 60 swings = 0 while time.time() < deadline and bot.alive: mine_once(bot, px, py, d, mx, my) swings += 1 bot.pump(0.6) time.sleep(0.08) log(f"[完成] 挥锄 {swings} 次 | 自身产物播报 {state['loot']} 条 | 金币累计 {state['gold']}") log(f" 收帧: {sorted(bot.frame_log.items(), key=lambda kv: -kv[1])[:12]}") g.close() if __name__ == "__main__": main()