地圖官方 WebSocket 客戶端實現

返回:文件索引 / README

概述

在保留油猴指令碼轉發相容模式的同時,專案內整合終末地官方地圖的 wss://ws.skland.com/ws/v1/game/endfield/map WebSocket 客戶端。 每個賬號(玩家角色)需要獨立的 hg/check credential 才能建立連線。 憑證可填寫在任務直接輸入(content 欄位)或儲存在賬號配置頁(map_contents 中),專案自動執行 OAuth 換取流程。

憑證輸入方式

方式一:任務直接輸入

ItemNavigatorTask.default_config['content'] —— 直接填入 hg/check 介面返回的 data.content 字串。

方式二:賬號配置頁(推薦)

AccountConfigTab 中的"地圖同步 content"區域,每個賬號儲存一份 data.content。 資料持久化在 configs/account_scoped_overrides.jsonmap_contents 欄位中。

憑證解析優先順序

  1. 任務 content 非空 → 直接使用
  2. 任務 地图账号 非空 → 從 map_contents 讀取該賬號的 content
  3. 任務當前登入賬號 context → 從 map_contents 讀取;觸發任務自身沒有 context 時,還會讀取 executor 當前執行任務的 current_account_id/current_user

不帶憑證時的回退

content地图账号 均為空時,自動啟動舊的本地 WS 服務端模式(監聽 ws://127.0.0.1:3001),相容油猴指令碼或其他外部來源。

OAuth → WebSocket 登入全鏈路

flowchart TD
    A[hg/check data.content] --> B[POST Hypergryph OAuth grant]
    B --> C[取得 oauth code]
    C --> D[POST zonai.skland.com/web/v1/user/auth/generate_cred_by_code]
    D --> E[取得 cred / sign token / userId]
    E --> F[GET user 和 player binding]
    F --> G[解析默认终末地角色]
    G --> H[GET websocket token]
    H --> I[连接官方地图 WS endpoint]
    I --> J[发送 type=1 token 鉴权]

HTTP 簽名演算法

headers = {
    "platform": "3",
    "vName": "1.0.0",
    "timestamp": str(timestamp),
    "dId": device_id or "",
}

sign_payload = path + (query if GET else body) + timestamp
compact_headers = {"platform":"3","timestamp":"...","dId":"...","vName":"1.0.0"}
sign_payload += json.dumps(compact_headers, separators=(",", ":"))

digest = hmac.new(sign_token.encode(), sign_payload.encode(), sha256).hexdigest()
sign = md5(digest.encode()).hexdigest()
headers["sign"] = sign

timestamp 處理

clientTime = 換取 cred 時的本地時間戳;serverTime = 換取 cred 時的伺服器響應 timestamp。 之後每次簽名:adjusted = serverTime + (now - clientTime),確保時間戳隨流逝時間同步推進。

WebSocket 協議

sequenceDiagram
    participant C as ok-ef WS Client
    participant S as skland WS
    C->>S: type=1 token 鉴权
    S-->>C: type=2 auth 成功
    loop 每 10 秒
        C->>S: type=3 心跳
    end
    loop 鉴权后每 5 秒
        C->>S: type=1011 roleId/serverId 初始化/刷新
        S-->>C: type=1012 pos/mapId/levelId
    end
    S-->>C: type=6 token 过期
    C->>S: type=1 新 token 鉴权
type 方向 說明
1 C→S token 鑑權:{token: wss_token}
2 S→C auth 成功確認
3 C→S 心跳(每 10s)
6 S→C token 過期(code=10002),需重新獲取 ws token 後發 type=1
1011 C→S 初始化/重新整理:{roleId, serverId}(鑑權後每 5s 傳送一次)
1012 S→C 位置資料:{data: {pos: {x,y,z}, mapId, levelId}}

客戶端收到的位置資料通過 _push_ws_payload() 放入統一佇列,與本地 WS 服務端模式共用同一套消費邏輯。

角色解析規則

  1. 先請求 /web/v1/user;再請求 /api/v1/game/player/binding。若後者失敗且 cred 響應帶 userId,則以 uid=userId 重試。
  2. 優先讀取 data.gameMap.endfield;缺失時在 data.list 中查詢 appCode == "endfield"
  3. bindingList 優先選擇 isDefault 項,否則取第一項。
  4. 角色優先取該項的 defaultRole,否則取 roles[0];最終必須同時有 roleIdserverId

多賬號架構

flowchart TD
    A[AccountConfigTab 保存地图同步 content] --> B[account_scope_store.set_account_map_content]
    B --> C[configs/account_scoped_overrides.json]
    C --> D[map_contents account_id -> content]
    E[ItemNavigatorTask 地图账号配置] --> F[get_account_map_content]
    G[当前任务账号上下文] --> F
    D --> F
    F --> H[官方地图 WS 凭证解析]

資料流

flowchart TD
    A[account_registry] --> B[account_id]
    C[accounts] --> B
    D[map_contents] --> B
    B --> E[读取账号任务覆盖]
    B --> F[读取地图同步 content]
configs/account_scoped_overrides.json
├── map_contents          ← 账号 → hg/check content 映射
│   ├── "acc_xxx": "data.content string"
│   └── ...
├── accounts              ← 任务级覆盖(已有)
└── account_registry      ← 账号 ID 注册表

相關 API (account_scope_store.py)

  • get_account_map_content(account, account_name="") → str
  • set_account_map_content(account, content) → None
  • 內部 _resolve_account_id_for_read/write() 處理 ID/使用者名稱解析

UI (AccountConfigTab.py)

  • 重新構建賬戶下拉時,從 map_contents 鍵集合中也拉入賬號列表
  • 地圖 content 編輯區:單行 LineEdit,隨當前賬號配置統一儲存

安全退出機制

1. 遊戲視窗退出檢測

_is_game_window_alive() 檢查 win32gui.IsWindow(hwnd) && win32gui.IsWindowVisible(hwnd)

  • WS 客戶端主迴圈每輪收包前檢查,視窗不存在則 return 退出協程,不會自動重連。
  • ItemNavigatorTask.run() 第一行也檢查,失效時呼叫 _cleanup_navigator_runtime() 清理所有 WS 資源和箭頭。

2. 消費者空閒超時

導航任務每次通過 _recv_ws_position_payload()_recv_ws_position_payload_or_cached() 讀取位置時,更新 _map_ws_last_consume_at = time.time()。 WS 客戶端執行緒檢查 _map_ws_should_stop_for_idle_consumer(),若超過 _map_ws_consumer_idle_timeout(初始化預設 10s)未被讀取,則主動退出。每次從佇列取得新位置,或在佇列為空時返回有效快取位置,都會重新整理消費時間。 解決 executor disable 任務後 WS 執行緒繼續空轉的問題。

ItemNavigatorTask 關鍵變更

移除的功能

  • support_multi_account 標記 —— 不再參與多賬號覆蓋 UI
  • 舊版 JSON/Cookie/key=value 格式憑證解析

新增配置項

配置鍵 型別 說明
content str 可選。直接填寫 hg/check data.content
地图账号 dropdown 可選。從賬號配置頁選擇已儲存 content 的賬號

run() 流程

flowchart TD
    A[ItemNavigatorTask.run] --> B{游戏窗口是否存在}
    B -->|否| C[清理 WS 和箭头]
    B -->|是| D[读取 content 或地图账号 content]
    D --> E{有凭证}
    E -->|是| F[停止本地 WS 服务]
    F --> G[启动官方地图 WS 客户端]
    E -->|否| H[停止官方 WS 客户端]
    H --> I[启动本地 WS 服务]
    G --> J[读取位置 payload 或缓存]
    I --> J
    J --> K[匹配物品点位并绘制箭头]
    K --> L[处理标记按键]
    L --> M[延迟保存 marked_points.json]

相關檔案

檔案 職責
src/tasks/mixin/ws_position_mixin.py WS 客戶端核心:OAuth 換取、HTTP 簽名、WS 協議、退出控制
src/tasks/trigger/ItemNavigatorTask.py 導航任務:憑證解析、位置消費、箭頭渲染、標記邏輯
src/tasks/account/account_scope_store.py 持久化:map_contents 欄位的讀寫、賬號解析
src/gui/AccountConfigTab.py UI:賬號配置頁,包含地圖 content 編輯

相關文件:物品導航與即時檢測 / 賬號配置使用者指南

在 GitHub 查看來源 ↗ · 頁面產生時間: 2026年8月10日