Repository Wiki
nikkigallery/Whimbox

运行时架构/rpc通信与前后端边界

本文描述运行时 RPC 通信层如何把前端请求映射为 Python 侧的脚本、配置、后台任务、地图遮罩和微信服务操作,并定义请求校验、结果序列化、配置持久化以及异步服务调用的边界。本文依据任务中提供的 Source grounding 实现片段编写;运行时上下文未提供仓库相对路径和 File Reference Base URL,因此无法生成可验证的文件行号链接。

Purpose and Scope

本页覆盖 RPC 方法分派层及其与前端之间的数据边界,重点包括:

  • script.* 方法:脚本路径、宏的查询、删除和刷新。
  • config.* 方法:配置读取、元数据查询、批量或单项更新。
  • one_dragon.flow.* 与 custom_flow.*:默认步骤和自定义步骤流的读取与更新。
  • background.*:后台任务状态查询和功能开关。
  • map_mask.*:游戏窗口状态、地图标签、可见点、配对登录和遮罩设置。
  • weixin.*:微信登录、监控、状态和断开连接。
  • 参数解析、布尔值转换、配置路径约束、缓存和错误边界。

本页不展开 scripts_manager、global_config、background_manager、map_mask_service、weixin_service 或 custom_flow 的内部实现;它们在这里作为 RPC 层调用的下游服务。若需要了解这些服务如何执行脚本、保存配置、管理窗口或访问外部服务,应查看相应的服务专题页面。

Overview

RPC 层的核心职责是接收统一形态的 method 和 params,按命名空间把请求交给专门的处理器,并把领域对象转换成前端可消费的字典、列表或布尔值。实现使用多个按领域划分的处理函数,而不是一个包含全部业务逻辑的巨大分派器:

  • handle_script_method 负责 script. 方法。
  • handle_config_method 负责 config.、one_dragon.flow. 和 custom_flow. 方法。
  • handle_background_method 负责后台任务方法。
  • handle_map_mask_method 负责地图遮罩及游戏窗口状态方法。
  • handle_weixin_method 是异步处理器,负责微信服务方法。

每个处理器都使用 UNHANDLED 作为未识别方法的哨兵值。这样调用方可以继续尝试其他处理器,而不是把“当前领域不负责”误认为成功结果。领域处理器只在方法匹配时返回业务结果;输入不合法时直接抛出 ValueError。

Architecture

由于源代码片段没有提供具体模块路径、外围 RPC server 或前端调用文件,以下架构只表示已在片段中确认的类、函数和调用关系:

  • 前端边界传入 method 和 params。
  • 领域处理器根据方法名分派。
  • 配置、脚本、后台、地图遮罩和微信服务分别位于下游。
  • handle_weixin_method 通过 await 调用异步服务;其他处理器在片段中是同步函数。

组件职责

组件已确认职责输出边界
handle_script_method查询、删除、刷新脚本及宏脚本信息、循环数据、删除结果或刷新快照
handle_config_method配置读写、配置元数据、流程配置配置值、元数据列表、保存结果、流程状态
handle_background_method查询和更新后台功能开关运行状态、功能状态和功能元数据
handle_map_mask_method地图遮罩、窗口坐标、标签和配对账户操作窗口状态、点位、标签、登录或设置结果
handle_weixin_method微信登录和监控生命周期异步服务结果或同步状态
global_config提供配置读取、设置和保存能力RPC 层只依赖其公开调用结果
scripts_manager提供脚本查询、删除和重新初始化RPC 层负责序列化和事件通知
background_manager提供后台任务运行及 feature 状态RPC 层负责联动启动和停止
map_mask_service执行地图遮罩领域操作RPC 层负责参数解析和类型约束
weixin_service执行微信登录、监控和断开RPC 层负责同步/异步调用边界

Method Dispatch Boundary

脚本方法

handle_script_method 支持以下方法:

方法入参行为返回形态
script.query_pathname、target、type、count、show_default调用 scripts_manager.query_path,固定 return_one=False每项包含 info 与 loops
script.query_macroname、is_play_music、show_default调用 scripts_manager.query_macro,固定返回多项每项包含 info
script.delete必填 name,category 必须为 path、macro 或 musicpath 使用 delete_path,其他类别使用 delete_macro{deleted: ...}
script.refresh无特殊参数初始化脚本字典并发出 event.scripts.changed{ok: true, ...snapshot}

查询路径时,如果 count 是字符串,会尝试转换为整数;转换失败抛出 ValueError("count must be a number")。这说明前端可以传递数字字符串,但不能传递任意不可解析文本。

脚本信息使用 _serialize_script_info:没有 info 时输出空字典;调用 model_dump() 失败时也退化为空字典。路径记录的 loops 则直接对每个循环调用 model_dump()。因此脚本信息和循环信息的容错策略并不完全相同:前者显式保护序列化失败,后者要求记录中的循环对象支持模型序列化。

配置和流程方法

handle_config_method 同时承载普通配置和 OneDragon/custom flow 配置:

方法主要行为
config.get按 Section 或 Section.key 读取配置,默认路径为 OneDragon。
config.meta从 DEFAULT_CONFIG 生成配置项元数据,并加载设置选项和材料选项。
config.update支持单项更新或 updates 列表更新,保存失败时抛出异常。
one_dragon.flow.get返回默认步骤、前置自定义步骤和后置自定义步骤。
one_dragon.flow.update校验并保存默认步骤及前后置自定义步骤,然后返回最新状态。
custom_flow.get直接返回 get_custom_flow_state()。
custom_flow.update使用 save_custom_flow_state 保存流程和活动流程 ID。

配置读取路径只允许一段或两段:Section 返回整个 section,Section.key 返回具体键;超过两段会抛出 ValueError("path must be in 'Section' or 'Section.key' format")。配置更新更严格,只允许 Section.key。

config.update 的批量形式要求 updates 是列表,且每个成员必须是字典;单项形式直接读取顶层 path 和 value。所有更新完成后只调用一次 global_config.save(),因此同一请求内的多项更新具有“集中保存”的边界,但底层保存是否具备事务性无法从片段确认。

后台任务方法

background.get 返回三类信息:

  • running:background_manager.is_running()。
  • features:以 BackgroundFeature.value 为键的开关状态。
  • feature_items:从 DEFAULT_CONFIG["BackgroundTask"] 中筛选出确实属于 BackgroundFeature 的配置项,并附带键、标签、描述和推断类型。

background.set 支持单项和批量更新。每个 feature key 必须能转换为 BackgroundFeature,否则抛出 ValueError("invalid feature: ...")。更新后执行运行状态联动:至少一个 feature 启用且任务未运行时启动;所有 feature 都禁用且任务正在运行时停止。该判断位于 RPC 层,因此前端不需要分别发送启动或停止命令。

地图遮罩方法

handle_map_mask_method 首先要求方法名以 map_mask. 开头,否则立即返回 UNHANDLED。已确认的方法包括:

  • map_mask.get_game_window_state
  • map_mask.get_labels
  • map_mask.prepare_points
  • map_mask.set_enabled
  • map_mask.set_selected_labels
  • map_mask.get_visible_points
  • map_mask.get_user_status
  • map_mask.set_pearpal_region
  • map_mask.submit_pearpal_login
  • map_mask.refresh_pearpal_user_state
  • map_mask.disconnect_pearpal_user
  • map_mask.clear_pearpal_login
  • map_mask.set_hide_awarded

map_mask.get_game_window_state 通过 HANDLE_OBJ 查找窗口。如果句柄当前不存活,先调用 refresh_handle();找不到窗口时返回零尺寸、dpiScale=1.0、isForeground=false 和 isMinimized=false。返回的 coordinateSpace 固定为 physical,并明确说明 Win32 返回的是物理屏幕像素,物理像素到 DIP 的转换由 Electron 侧负责。这是前后端坐标边界中最重要的约束之一:Python 侧不应提前把坐标转换成前端 DIP。

map_mask.set_enabled 要求 enabled 必须是真正的布尔值;set_selected_labels 要求标签 ID 是列表,并把每一项转成字符串。get_visible_points 对 viewport 使用 MapMaskViewport.from_dict,对 map_name 和 label_ids 使用可选字符串解析器。set_hide_awarded 则使用更宽松的 _coerce_bool,支持布尔值、true/1/yes/on 等字符串。

微信方法

handle_weixin_method 是异步函数。它支持:

  • weixin.login.start → await weixin_service.start_login()。
  • weixin.login.poll → await weixin_service.poll_login()。
  • weixin.status.get → weixin_service.get_status()。
  • weixin.monitor.start → await weixin_service.start_monitor()。
  • weixin.monitor.stop → await weixin_service.stop_monitor()。
  • weixin.disconnect → await weixin_service.disconnect()。

登录、监控启动/停止和断开连接均通过异步边界执行;状态读取在片段中是同步调用。前端调用方必须把这些方法视为可能需要等待的 RPC 操作,不能根据方法名统一假设所有结果都是同步返回。

Configuration Metadata and Type Inference

config.meta 不直接把原始配置对象暴露给前端,而是按 DEFAULT_CONFIG 生成具有 key、description 和 type 的元数据。类型由 _infer_config_type 推断:

  1. Python bool → boolean。
  2. list → array。
  3. int 或 float → number。
  4. 字符串值为 true/false(忽略大小写)→ boolean。
  5. 去掉一次小数点和负号后仍为数字 → number。
  6. 其他情况 → string。

设置选项从 ASSETS_PATH/setting_options.json 延迟加载并缓存;材料选项从 ASSETS_PATH/material.json 延迟加载,使用 JSON 对象的键列表并缓存。文件不存在、JSON 无效或读取出现其他异常时,设置选项降级为空字典,材料选项降级为空列表。缓存没有失效机制,因此运行期间修改资源文件不会自动反映到后续的 config.meta 响应中。

对 jihua_cost、jihua_cost_2 和 jihua_cost_3,材料选项会覆盖普通设置选项。这是一个明确的前端元数据优先级规则:这些键的可选值来自 material.json 的顶层键。

OneDragon Flow Normalization

OneDragon 流程由三类数据组成:默认步骤、前置自定义步骤和后置自定义步骤。默认步骤的合法键集合来自 DEFAULT_CONFIG["OneDragonDefaultSteps"];更新未知键会抛出 ValueError("invalid default step: ...")。

自定义步骤必须满足:

  • 是字典。
  • id 非空,且会被转换并去除首尾空白。
  • type 只能是 path 或 macro。
  • script_name 被转换为字符串;缺失时为空字符串。
  • enabled 通过 _coerce_bool 处理,缺失时默认为启用。

读取后置步骤时,优先读取 OneDragonPostCustomSteps。如果没有后置步骤,才回退到旧的 OneDragonCustomSteps;但当已经存在前置步骤时,不再把旧配置作为后置步骤返回。更新前置或后置步骤后,会把旧的 OneDragonCustomSteps.items 清空,以完成从旧布局到新布局的迁移边界。

Core Flow

请求分派流程

前端请求至少包含方法名和参数字典。领域处理器按顺序检查方法名;匹配则执行校验和服务调用,不匹配则返回 UNHANDLED。片段没有提供外围总分派器,因此“多个处理器如何排列以及最终如何把 UNHANDLED 转为 RPC 错误”无法从现有材料确认。

配置更新流程

  1. 读取 updates。
  2. 如果存在,校验它是列表,逐项校验为字典。
  3. 每项通过 _apply_config_update 检查路径必须是两段。
  4. 调用 global_config.set(section, key, value)。
  5. 全部更新完成后调用一次 global_config.save()。
  6. 保存返回假值时抛出 ValueError("config save failed")。
  7. 成功返回 {ok: true}。

后台功能联动流程

  1. 把 feature key 转换为 BackgroundFeature。
  2. 更新 background_manager 中的开关。
  3. 检查所有 feature 是否至少有一个启用。
  4. “有启用且未运行”时启动任务。
  5. “无启用且正在运行”时停止任务。
  6. 返回完整后台状态。

地图窗口坐标流程

  1. 检查 HANDLE_OBJ.is_alive()。
  2. 不存活时尝试 refresh_handle()。
  3. 再次判断窗口是否找到。
  4. 找到时读取窗口矩形、缩放因子、前台状态和最小化状态。
  5. 将缩放因子小于等于零的情况归一化为 1.0。
  6. 以物理像素坐标返回窗口信息,并把 DPI 计算为 round(scale_factor * 96)。

Data and Persistence Boundary

本 RPC 层不直接声明数据库模型。已确认的持久化入口是:

  • global_config.save():普通配置及 OneDragon 流程配置保存。
  • save_custom_flow_state(...):自定义流程状态保存。
  • scripts_manager.init_scripts_dict():刷新脚本内存快照,并通过 emit_event 发布变化事件。

脚本刷新返回的事件名为 event.scripts.changed,事件负载是脚本快照加上 source: "manual"。这说明脚本刷新不仅改变查询结果,还通过事件边界通知其他运行时组件。

Error Handling and Edge Cases

场景行为
未知 RPC 方法返回 UNHANDLED,不执行领域操作。
script.delete 缺少名称抛出 name is required。
script.delete 类别非法抛出类别约束错误。
路径不存在或配置键不存在分别抛出 section/key not found。
配置路径层级错误读取和更新分别执行一段/两段约束。
批量更新不是列表抛出 updates must be a list。
批量项不是对象抛出 update item must be object。
配置保存失败抛出 config save failed。
地图 viewport 不是对象抛出 viewport must be an object。
标签列表不是列表抛出 label_ids must be a list 或对应 selected label 错误。
地图 enabled 不是布尔值set_enabled 拒绝该输入。
自定义步骤格式错误拒绝非对象、空 ID 或非法类型。
配置元数据资源读取失败使用空选项降级,不使整个元数据请求失败。
窗口句柄失效尝试刷新;仍不存在时返回 found=false 的安全状态。

异常类型在片段中主要是 ValueError;没有看到统一错误码、HTTP 状态映射、日志记录、重试或超时策略。因此这些外围行为属于未确认信息,不应由前端根据本页推断。

Concurrency and Operational Considerations

  • handle_weixin_method 的异步调用边界表明微信登录和监控操作可能包含等待过程;具体并发互斥、取消和超时策略位于 weixin_service,当前材料不足以确认。
  • _setting_options_cache 和 _material_options_cache 是模块级缓存。片段没有显式锁,因此多线程环境下的首次加载安全性取决于运行时模型及 Python 调度;是否运行在单线程事件循环中无法确认。
  • background.set 的启动/停止判断依赖“更新后的所有 feature 状态”。若多个请求并发修改,最终运行状态的一致性取决于 background_manager 是否提供内部同步,片段没有展示该实现。
  • config.update 在多项更新后统一保存,但没有事务回滚代码;如果底层保存失败,内存中的 global_config 是否已改变不能从片段确认。
  • script.refresh 会触发事件广播;事件消费者、事件顺序和失败传播策略没有在片段中提供。

API Reference

handle_script_method(method: str, params: Dict[str, Any]) -> Any

处理脚本命名空间方法。识别的方法包括 script.query_path、script.query_macro、script.delete 和 script.refresh。未识别时返回 UNHANDLED;输入验证失败时抛出 ValueError。

handle_config_method(method: str, params: Dict[str, Any]) -> Any

处理普通配置、OneDragon 流程和自定义流程方法。配置更新会调用 global_config.save();保存失败抛出 ValueError。

handle_background_method(method: str, params: Dict[str, Any]) -> Any

处理后台状态查询和 feature 更新。更新后可能启动或停止 background_manager。

handle_map_mask_method(method: str, params: Dict[str, Any]) -> Any

处理地图遮罩相关方法。窗口状态使用物理像素坐标返回;其他方法把请求参数转换后交给 map_mask_service。

async handle_weixin_method(method: str, params: Dict[str, Any]) -> Any

处理微信登录、监控和断开连接。部分服务调用使用 await,状态读取为同步调用;未识别方法返回 UNHANDLED。

_coerce_bool(value: Any) -> bool

布尔转换规则为:布尔值原样返回;字符串在去空白并转小写后,只有 true、1、yes、on 表示真;其他非字符串值使用 Python 的 bool 转换。

_split_config_path(path: str) -> list[str]

要求输入是非空字符串,按点分隔并丢弃空片段;空路径抛出 path is required。

Frontend Contract Recommendations

前端应遵循以下已由实现明确的边界:

  1. 使用方法名作为稳定的 RPC 操作标识,不要把服务类名暴露为协议的一部分。
  2. 对配置读取区分 section 路径和 Section.key 路径;配置更新始终使用两段路径。
  3. 对 map_mask.get_game_window_state 的 x/y/width/height 按物理像素解释,并根据 dpiScale 自行完成 DIP 转换。
  4. map_mask.set_enabled 发送 JSON 布尔值,而不是字符串;其他使用 _coerce_bool 的接口不能据此推断所有接口都接受字符串。
  5. 自定义步骤的 type 只使用 path 或 macro,并始终提供非空 id。
  6. 处理 UNHANDLED 对应的外围错误时,不要把它当成业务成功;具体错误封装需要查阅外围 RPC server。
  7. 微信登录和监控操作使用异步调用模型,UI 应显示进行中状态并等待结果;具体轮询频率由调用方或服务层决定,片段未规定。

Tests and Verification Status

提供的材料只包含实现片段,没有测试文件、RPC server 注册代码、前端调用代码或接口契约文件。因此无法从当前源材料确认:

  • 处理器的注册顺序。
  • 外围协议是 WebSocket、HTTP、Electron IPC 还是其他传输。
  • 异常到前端错误响应的映射。
  • 并发请求的测试保证。
  • global_config.save、地图服务和微信服务的集成测试覆盖率。

可直接从实现推导并应在测试中覆盖的边界包括:非法配置路径、批量更新类型错误、非法后台 feature、无效地图 viewport、脚本删除类别校验、OneDragon 自定义步骤归一化,以及窗口句柄不存在时的安全默认值。

Extension Points

扩展新的 RPC 方法时,应保持现有边界:

  • 将方法放入对应领域处理器,或新增独立领域处理器。
  • 未匹配的方法必须返回 UNHANDLED,避免吞掉其他处理器的机会。
  • 在进入下游服务前完成类型和结构校验。
  • 对前端可见的模型显式序列化,不直接返回无法跨边界编码的对象。
  • 对会修改持久化状态的批量操作,在所有输入校验完成后再保存。
  • 如果新增坐标接口,必须明确坐标空间,不要让物理像素、DIP 和游戏坐标混用。
  • 如果新增异步服务调用,保持 handle_weixin_method 所体现的异步边界,并明确调用方的等待语义。

运行时上下文未提供可验证的仓库路径、文件引用基地址或 sibling catalog 页面路径,因此本页不生成可能失效的源文件链接或相关页面链接。当前可确认的相关实现入口仅包括任务中提供的 Source grounding 片段中出现的处理器、服务对象和配置对象。

Sources

(1 files)