运行时架构/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_path | name、target、type、count、show_default | 调用 scripts_manager.query_path,固定 return_one=False | 每项包含 info 与 loops |
script.query_macro | name、is_play_music、show_default | 调用 scripts_manager.query_macro,固定返回多项 | 每项包含 info |
script.delete | 必填 name,category 必须为 path、macro 或 music | path 使用 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_statemap_mask.get_labelsmap_mask.prepare_pointsmap_mask.set_enabledmap_mask.set_selected_labelsmap_mask.get_visible_pointsmap_mask.get_user_statusmap_mask.set_pearpal_regionmap_mask.submit_pearpal_loginmap_mask.refresh_pearpal_user_statemap_mask.disconnect_pearpal_usermap_mask.clear_pearpal_loginmap_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 推断:
- Python
bool→boolean。 list→array。int或float→number。- 字符串值为
true/false(忽略大小写)→boolean。 - 去掉一次小数点和负号后仍为数字 →
number。 - 其他情况 →
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 错误”无法从现有材料确认。
配置更新流程
- 读取
updates。 - 如果存在,校验它是列表,逐项校验为字典。
- 每项通过
_apply_config_update检查路径必须是两段。 - 调用
global_config.set(section, key, value)。 - 全部更新完成后调用一次
global_config.save()。 - 保存返回假值时抛出
ValueError("config save failed")。 - 成功返回
{ok: true}。
后台功能联动流程
- 把 feature key 转换为
BackgroundFeature。 - 更新
background_manager中的开关。 - 检查所有 feature 是否至少有一个启用。
- “有启用且未运行”时启动任务。
- “无启用且正在运行”时停止任务。
- 返回完整后台状态。
地图窗口坐标流程
- 检查
HANDLE_OBJ.is_alive()。 - 不存活时尝试
refresh_handle()。 - 再次判断窗口是否找到。
- 找到时读取窗口矩形、缩放因子、前台状态和最小化状态。
- 将缩放因子小于等于零的情况归一化为
1.0。 - 以物理像素坐标返回窗口信息,并把 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
前端应遵循以下已由实现明确的边界:
- 使用方法名作为稳定的 RPC 操作标识,不要把服务类名暴露为协议的一部分。
- 对配置读取区分 section 路径和
Section.key路径;配置更新始终使用两段路径。 - 对
map_mask.get_game_window_state的x/y/width/height按物理像素解释,并根据dpiScale自行完成 DIP 转换。 map_mask.set_enabled发送 JSON 布尔值,而不是字符串;其他使用_coerce_bool的接口不能据此推断所有接口都接受字符串。- 自定义步骤的
type只使用path或macro,并始终提供非空id。 - 处理
UNHANDLED对应的外围错误时,不要把它当成业务成功;具体错误封装需要查阅外围 RPC server。 - 微信登录和监控操作使用异步调用模型,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所体现的异步边界,并明确调用方的等待语义。
Related Links
运行时上下文未提供可验证的仓库路径、文件引用基地址或 sibling catalog 页面路径,因此本页不生成可能失效的源文件链接或相关页面链接。当前可确认的相关实现入口仅包括任务中提供的 Source grounding 片段中出现的处理器、服务对象和配置对象。