文档 / 功能介绍

AetherGate 本地游戏自动查找机制分析

速览

AetherGate 在「首次启动」时弹出的「自动查找本地游戏」功能,其核心实现是 GameSelector.xaml.cs 中的 AutoSearchInstalledGames() 方法。

该方法的查找依据是 Windows 注册表,具体读取的是 米哈游官方启动器 HoYoPlay(HYP)写入的注册表项中的 GameInstallPath 值,而不是全盘扫描磁盘文件,也不是基于游戏 exe 的 MD5 校验。

  • 国服游戏注册表根键:HKEY_CURRENT_USER\Software\miHoYo\HYP\1_1\{gameBiz}
  • 国际服游戏注册表根键:HKEY_CURRENT_USER\Software\Cognosphere\HYP\1_0\{gameBiz}

其中 {gameBiz} 为游戏业务标识,例如 hk4e_cn(原神国服)、hkrpg_global(星穹铁道国际服)、nap_cn(绝区零国服)等。


一、首次启动的触发流程

整个「首次启动自动查找」并不是一个独立后台任务,而是由 UI 引导链路触发的:

  1. 程序入口 App.xaml.csOnLaunched 调用 AppConfig.CheckEnviromentAsync()
  2. 首次运行判定AppConfig.Configuration.csCheckEnviromentAsync 中:若未配置 UserDataFolder(第一次启动),弹出 WelcomeWindow 让用户选择数据目录并做环境检查(WebView2 / WebP 解码器 / 写入权限)。
  3. 主窗口创建 通过验证后创建 MainWindow,其内容为 MainView.xaml 中托管的 GameSelector 用户控件。
  4. GameSelector 初始化 GameSelector.xaml.cs 构造函数调用 InitializeGameSelector()InitializeGameIconsArea()
  5. 自动弹出引导提示GameSelector.xaml.cs#L232-L235 中:当 GameBizIcons.Count == 0 && CurrentGameBizIcon is null(首次启动没有任何已选游戏)时,自动打开 TeachTip_SelectGame
  6. 提示中的「自动查找」按钮 GameSelector.xaml#L74-L88 这个 TeachingTip 的 ActionButton 绑定到 AutoSearchInstalledGamesCommand,按钮文案为「自动查找」。
  7. 用户点击后 执行 AutoSearchInstalledGames(),完成注册表扫描与结果写入。

此外,在「已安装游戏」展开面板的头部还有一个常驻的「自动查找」按钮 GameSelector.xaml#L327-L338Button_AutoSearch),用户可随时再次触发该扫描。


二、核心查找逻辑:AutoSearchInstalledGames()

源码位于 GameSelector.xaml.cs#L893-L949,逻辑分两层:

第 1 层:优先复用已记录的安装路径

对每一个来自 HoYoPlay 缓存游戏列表(GetCachedGameInfos())的游戏:

  • 调用 GameLauncherService.GetGameInstallPath(gameBiz),该方法在 GameLauncherService.cs#L60-L81 中读取 AppConfig 中持久化的 install_path_{biz} 配置项。
  • 若该路径 Directory.Exists 为真,或虽不存在但标记为可移动存储(GetGameInstallPathRemovable),则视为已找到,直接加入选中列表,跳过注册表查询。

第 2 层:注册表回退查找

当配置中无有效路径时,按服务器类型构造注册表键并读取:

国服 (Server == "cn"):
    HKEY_CURRENT_USER\Software\miHoYo\HYP\1_1\{gameBiz}
    读取值名: GameInstallPath

国际服 (Server == "global"):
    HKEY_CURRENT_USER\Software\Cognosphere\HYP\1_0\{gameBiz}
    读取值名: GameInstallPath

读取实现为 Registry.GetValue(key, "GameInstallPath", null) as string

若读出的路径 Directory.Exists 为真,则:

  • 通过 AppConfig.SetGameInstallPath(gameBiz, path) 写回配置持久化;
  • 将该 gameBiz 追加到结果字符串(逗号分隔)。

循环结束后:

  • AppConfig.SelectedGameBizs = sb.ToString().TrimEnd(',') 写入选中游戏集合;
  • 调用 InitializeGameSelector() 刷新 UI;
  • 若未固定则调用 Pin() 固定选择器面板。

注:{gameBiz} 直接使用业务标识本身(如 hk4e_cnhkrpg_globalnap_cn),与 HoYoPlay 启动器在注册表中为每个游戏创建的子键命名一致。


三、Bilibili 服的特殊处理

代码对 Bilibili 渠道服做了显式处理 GameSelector.xaml.cs#L903-L906

if (item.IsBilibiliServer())
{
    gameBiz = $"{gameBiz.Game}_bilibili";   // 例如 hk4e_bilibili
}

随后注册表分支判定 gameBiz.Server is "cn" / "global"

  • hk4e_bilibiliServer"bilibili",既非 "cn" 也非 "global",因此 key 保持为空字符串,跳过注册表查找

结论:Bilibili 服游戏无法被自动查找,因为 HoYoPlay 启动器不为 B 站渠道服单独写 GameInstallPath 注册表项。B 站服需要用户通过「定位游戏」手动指定文件夹(见下文「手动定位」)。


四、注册表键来源说明

GameRegistry.cs 中定义了两套相关常量,注意区分用途:

  • 本功能实际使用的(HYP 新启动器)GamePath_HYP_cn = HKEY_CURRENT_USER\SOFTWARE\miHoYo\HYP\1_1GamePath_HYP_os = HKEY_CURRENT_USER\SOFTWARE\Cognosphere\HYP\1_0AutoSearchInstalledGames 中拼接的键正是这两个根键 + {gameBiz} 子键。
  • 旧版独立启动器的游戏配置键:如 GamePath_hk4e_cn = HKEY_CURRENT_USER\Software\miHoYo\原神 等。这些通过 GameBiz.GetGameRegistryKey() 暴露,但仅被 GameSettingServiceGameAccountServiceGameNoticeService 用于读取游戏内设置/账号/公告,不参与安装路径的自动查找。

五、路径持久化与可移动存储

  • 持久化位置AppConfig.Setting.csSetGameInstallPath 以键名 install_path_{biz} 写入配置(便携版写入 exe 同级 config.ini,安装版写入注册表 HKCU\Software\AetherGate,详见 AppConfig.Configuration.cs)。
  • 可移动存储相对路径GameLauncherService.GetRelativePathIfInRemovableStorage 当检测到路径位于 USB / 可移动设备时,存为相对路径,并在 install_path_removable_{biz} 标记 true,便于设备拔出后仍保留配置。
  • 失效清理GetGameInstallPath 若路径不存在且非可移动存储,会自动清空该配置项(ChangeGameInstallPath(gameBiz, null)),避免脏数据。

六、查找后的「已安装」校验

自动查找只负责定位安装目录;判断游戏是否真实存在/获取版本,使用以下方法(均在 GameLauncherService.cs):

  • 本地版本 GetLocalGameVersionAsync:读取 {installPath}/config.ini,正则匹配 game_version=(.+)
  • exe 是否存在 IsGameExeExistsAsync:检查 {installPath}/{exe_name}
  • 各游戏 exe 名GameLauncherService.cs#L227-L242):

- 原神国服 / B 站服:YuanShen.exe - 原神国际服:GenshinImpact.exe - 星穹铁道:StarRail.exe - 崩坏 3:BH3.exe - 绝区零:ZenlessZoneZero.exe

  • 占用空间统计 GameSelector.InitializeInstalledGamesAsync 递归枚举安装目录文件计算实际占用,并通过 FileIdInfo 去重统计硬链接节省的空间(针对多服共享文件场景)。

七、未参与自动查找的相关机制

为避免误解,以下机制虽然存在于代码或 API 中,但未用于本「自动查找本地游戏」功能:

  1. GameScanInfo / getGameScanInfo 接口GameScanInfo.cs 定义了「不同版本游戏 exe 的 MD5 列表」结构,HoYoPlayClient.GetGameScanInfosAsync 提供了从 HoYoPlay API 拉取该信息的客户端方法。但全工程检索显示,GetGameScanInfosAsync 在 AetherGate 主项目中从未被调用。也就是说,基于 exe MD5 的版本识别/校验通道已预留但尚未启用,本功能并不依赖它。
  2. 旧版独立启动器注册表项HKCU\Software\miHoYo\原神 等键仅用于读取游戏内设置 / 账号 / 公告,不用于安装路径发现(见第四节)。
  3. 手动「定位游戏」GameLauncherPage.LocateGameAsyncGameLauncherSettingDialog.LocateGameAsync 提供文件夹选择器,由用户手工指定安装目录,属于手动补充手段,非自动查找。

八、整体流程图

  1. 首次启动 → CheckEnviromentAsyncWelcomeWindow(数据目录 + 环境检查)
  2. → 创建 MainWindow / MainView / GameSelector
  3. InitializeGameIconsArea 发现无已选游戏 → 自动弹出 TeachTip_SelectGame
  4. → 用户点击「自动查找」→ AutoSearchInstalledGames()
  5. → 遍历 HoYoPlay 缓存游戏列表:

- 先查 AppConfig 中的 install_path_{biz},有效则直接采用 - 否则查注册表 HKCU\Software\miHoYo\HYP\1_1\{biz}(国服)或 HKCU\Software\Cognosphere\HYP\1_0\{biz}(国际服)的 GameInstallPath - 路径存在则写回 AppConfig 并加入选中集合

  1. AppConfig.SelectedGameBizs 写入 → InitializeGameSelector() 刷新 → Pin() 固定
  2. 后续启动时第 3 步条件不再满足,不再自动弹提示;用户可通过常驻「自动查找」按钮再次触发。

九、小结

  • 查找依据:HoYoPlay 官方启动器写入注册表的 GameInstallPath非磁盘扫描、非 MD5 校验
  • 触发方式:首次启动时无已选游戏自动弹出引导提示,用户点击「自动查找」按钮执行;之后可手动触发。
  • 覆盖范围:仅覆盖国服(miHoYo HYP 1_1)与国际服(Cognosphere HYP 1_0)两个渠道;Bilibili 服不在自动查找覆盖范围内
  • 前置依赖:用户须先用官方 HoYoPlay 启动器安装过游戏,注册表中才会存在对应 GameInstallPath;否则自动查找无果,需走手动「定位游戏」流程。
  • 健壮性:路径失效会自动清理;可移动存储支持相对路径持久化;多服共享文件通过文件 ID 去重统计空间。
最后更新:2026-08-27 · 阅读 4406 次