Conversation
BlameTwo
commented
Apr 6, 2026
- 重构V2核心
- 删除游戏增加二次确认对话框
- 增强异步性能
新核心合并至开发分支
There was a problem hiding this comment.
Pull request overview
该 PR 主要在现有启动器/工具中引入“V2 核心”与配套 UI(V2 游戏页、V2 选择目录对话框、删除资源二次确认/进度对话框),并在 Waves.Core 中新增事件发布/进度跟踪与新下载/解压相关实现,以提升异步性能与可维护性。
Changes:
- 新增 V2 GameContext(Waves/Punish)及其 DI 注册、页面入口与导航项
- 新增/调整下载、预下载、安装资源流程的事件发布与进度跟踪(
GameEventPublisher/GameProgressTracker等) - 安装/卸载流程增强:生成卸载快照、卸载时检测进程、删除资源对话框等
Reviewed changes
Copilot reviewed 104 out of 105 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/WutheringWavesTool/ViewModel/GameViewModels/KuroGameContextViewModelV2/KuroGameContextViewModelV2.StartGame.cs | V2 启动/更新/预下载入口命令 |
| src/WutheringWavesTool/ViewModel/GameViewModels/KuroGameContextViewModelV2/KuroGameContextViewModelV2.Predownloader.cs | V2 预下载卡片与控制命令 |
| src/WutheringWavesTool/ViewModel/GameViewModels/KuroGameContextViewModelV2/KuroGameContextViewModelV2.Downloader.cs | V2 下载进度/暂停/取消/限速命令 |
| src/WutheringWavesTool/ViewModel/GameViewModels/KuroGameContextViewModelV2/KuroGameContextViewModelV2.Common.cs | V2 集合替换判定辅助方法 |
| src/WutheringWavesTool/ViewModel/GameViewModels/KuroGameContextViewModel/KuroGameContextViewModel.Downloader.cs | 旧 VM 下载事件类型适配(BottomText) |
| src/WutheringWavesTool/ViewModel/GameViewModels/GameContextsV2/WavesV2GameContextViewModel.cs | Waves V2 页面 VM 壳 |
| src/WutheringWavesTool/ViewModel/GameViewModels/GameContextsV2/PunishV2GameContextViewModel.cs | Punish V2 页面 VM 壳 |
| src/WutheringWavesTool/ViewModel/DialogViewModels/SelectGameFolderViewModelV2.cs | V2 选择游戏 EXE + 磁盘信息展示 |
| src/WutheringWavesTool/ViewModel/DialogViewModels/DeleteFileViewModel.cs | 删除资源二次确认/进度 VM |
| src/WutheringWavesTool/Services/PageService.cs | 注册 V2 游戏页与 VM |
| src/WutheringWavesTool/Services/DialogManager.cs | 新增 V2 对话框入口与删除资源对话框;修复 _dialog 清理 |
| src/WutheringWavesTool/Services/Contracts/IDialogManager.cs | DialogManager 接口扩展(V2 + 删除资源) |
| src/WutheringWavesTool/Services/AppContext.cs | 启动时初始化 V2 GameContext |
| src/WutheringWavesTool/Pages/ShellPage.xaml.cs | 导航动画包含 V2 游戏页 |
| src/WutheringWavesTool/Pages/ShellPage.xaml | NavigationView 增加 V2 游戏入口项 |
| src/WutheringWavesTool/Pages/GamePages/WavesV2GamePage.xaml.cs | Waves V2 页面 code-behind |
| src/WutheringWavesTool/Pages/GamePages/PunishV2GamePage.xaml.cs | Punish V2 页面 code-behind |
| src/WutheringWavesTool/Pages/Dialogs/SelectGameFolderDialogV2.xaml.cs | 选择游戏目录 V2 对话框封装 |
| src/WutheringWavesTool/Pages/Dialogs/SelectGameFolderDialogV2.xaml | 选择游戏目录 V2 UI |
| src/WutheringWavesTool/Pages/Dialogs/SelectDownoadGameDialogV2.xaml.cs | 选择下载目录 V2 对话框逻辑 |
| src/WutheringWavesTool/Pages/Dialogs/SelectDownoadGameDialogV2.xaml | 选择下载目录 V2 UI |
| src/WutheringWavesTool/Pages/Dialogs/DeleteFileDialog.xaml.cs | 删除资源对话框封装 |
| src/WutheringWavesTool/Pages/Dialogs/DeleteFileDialog.xaml | 删除资源对话框 UI |
| src/WutheringWavesTool/Models/ServerDisplay.cs | 增加 V2 服务器/上下文列表 |
| src/WutheringWavesTool/Instance.cs | DI 注册 V2 VM / V2 对话框 / 删除对话框 |
| src/WutheringWavesTool/Haiyu.csproj | 包版本调整与新增 XAML/资源编译项 |
| src/WutheringWavesTool/Converter/DownloadState/StepConverter.cs | 新增步骤 OK 颜色/可见性转换器 |
| src/WutheringWavesTool/Controls/Styles/ToolTipStyle.xaml | BOM/头部微调 |
| src/WutheringWavesTool/Controls/Styles/StorageRing.xaml | 引入 StorageRing 样式资源 |
| src/WutheringWavesTool/Controls/Styles/NavigationView.xaml | NavItem 模板支持 InfoBadge |
| src/WutheringWavesTool/Controls/StorageRing/ThicknessCheck.cs | StorageRing 辅助枚举 |
| src/WutheringWavesTool/Controls/StorageRing/StorageRing.Properties.cs | StorageRing 依赖属性实现 |
| src/WutheringWavesTool/Controls/StorageRing/RingShape/RingShape.Properties.cs | RingShape 依赖属性实现 |
| src/WutheringWavesTool/Common/StorageControlsHelpers.cs | StorageRing/Bar 数学辅助函数 |
| src/WutheringWavesTool/Behaviors/HoverOpenCloseFlyoutBehavior.cs | Hover 打开/关闭 Flyout 行为 |
| src/WutheringWavesTool/App.xaml | 资源合并:引入 StorageRing,注释 ToolTipStyle |
| src/WutheringWavesTool.slnx | 从解决方案移除 Haiyu.Run 项目 |
| src/Waves.Core/Waves.cs | DI 注册 V2 GameContext 与事件发布器 |
| src/Waves.Core/Services/GameProgressTracker.cs | 新增事件驱动的进度跟踪器 |
| src/Waves.Core/Services/GameEventPublisher.cs | 新增 Channel 分发的事件发布器 |
| src/Waves.Core/Models/KrDiffDecompressResult.cs | 新增 krdiff 解压结果数据结构 |
| src/Waves.Core/Models/IProgressSetup.cs | 新增步骤执行接口抽象 |
| src/Waves.Core/Models/GameContextStatus.cs | 调整字段顺序/保留预下载状态字段 |
| src/Waves.Core/Models/GameContextOutputArgs.cs | 扩展输出参数:步骤/单文件/预下载/ZipSpeed 等 |
| src/Waves.Core/Models/Enums/InstallGameResourceType.cs | 新增安装资源类型枚举 |
| src/Waves.Core/Models/Enums/GameContextActionType.cs | 调整/新增动作类型(BottomText/ZipDecompress/PublishStep 等) |
| src/Waves.Core/Models/DownloadUpdateFolderConfig.cs | 新增更新下载目录配置模型 |
| src/Waves.Core/Models/DownloadActiveItemItem.cs | 新增活跃文件/步骤项模型 |
| src/Waves.Core/INI/Ini.cs | 字符串/注释行缩进调整(编码仍异常) |
| src/Waves.Core/Helpers/BuildFileHelper.cs | 新增磁盘可用空间获取方法 |
| src/Waves.Core/GameContext/KuroGameContextBase/KuroGameContextBase.Predownload.cs | DownloadState CancelToken 类型适配 |
| src/Waves.Core/GameContext/KuroGameContextBase/KuroGameContextBase.GameStart.cs | 命名空间/结构整理与启动逻辑重排 |
| src/Waves.Core/GameContext/KuroGameContextBase/KuroGameContextBase.Downloader.cs | 删除阶段输出类型改为 BottomText |
| src/Waves.Core/GameContext/KuroGameContextBase/Commons/DownloadMethod.cs | 调整 Md5 校验移动文件阶段输出类型 |
| src/Waves.Core/GameContext/KuroGameContextBase/Commons/DownloadData.cs | 移除 GetVerifyLimit(逻辑迁移/废弃) |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/KuroGameContextBaseV2.Setup.cs | V2 步骤列表占位 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/KuroGameContextBaseV2.Launcher.cs | V2 Launcher/资源索引拉取实现 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/KuroGameContextBaseV2.Download.cs | V2 下载/修复入口与 CDN 测试逻辑 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/WriteGameResourceConfig.cs | 下载完成/取消后的配置写入 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/UIActionSetup.cs | 删除空占位类 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/InstallUpdateResource.cs | 安装更新资源占位 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/InstallKrZipResource.cs | Zip 解压安装步骤实现 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/InstallKrdiffResource.cs | krdiff 安装步骤实现 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/InstallKrdiffGroupResource.cs | krdiff group 安装步骤实现 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/FilesAction/MoveFileResource.cs | 批量移动文件步骤实现 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/FilesAction/DeleteFileResource.cs | 批量删除文件步骤占位 |
| src/Waves.Core/GameContext/KruoGameContextBaseV2/Common/DownloadAndVerifyResource.cs | 并行下载+校验核心实现 |
| src/Waves.Core/GameContext/GameContextFactory.cs | 新增 V2 Context 工厂方法 |
| src/Waves.Core/GameContext/ContextsV2/WavesMainGameContextV2.cs | Waves V2 Context 定义 |
| src/Waves.Core/GameContext/ContextsV2/PunishMainGameContextV2.cs | Punish V2 Context 定义 |
| src/Waves.Core/Contracts/IGameContextV2.cs | V2 Context 接口定义 |
| src/Waves.Core/Contracts/Events/IGameEventSubscription.cs | 事件订阅 token 接口 |
| src/Waves.Core/Contracts/Events/IGameEventPublisher.cs | 事件发布器接口 |
| src/Waves.Core/Common/ProcessScan.cs | 清理 Debug 输出与注释 |
| src/Waves.Core/Common/DownloadState.cs | CancelToken 改为 CTS;暂停/恢复更新 IsActive |
| src/Waves.Core/Common/Downloads/VerifyTask.cs | 新增/重构校验实现 |
| src/Waves.Core/Common/Downloads/UnZipTask.cs | 新增 Zip 解压实现与进度上报 |
| src/Waves.Core/Common/Downloads/DownloadTask.cs | 新增分片/全量下载实现与进度上报 |
| src/Waves.Core/Common/Downloads/DiffDecompressTask.cs | 新增 krdiff 解压任务封装 |
| src/Waves.Core/Common/Downloads/DeleteFileTask.cs | 删除任务占位 |
| src/Waves.Core/Common/DownloadExtension.cs | 新增参数校验/事件发布扩展(新语法) |
| src/Waves.Core/Common/DiffDecompressManagerV2.cs | 新增共享内存驱动的 hpatchz 进度读取 |
| src/Waves.Core/Common/CDNSpeedTester.cs | CDN 测速能力增强与 API 调整 |
| src/Setup/Project.WPFSetup/Views/UninstallView.xaml | Loaded 触发命令(Behaviors) |
| src/Setup/Project.WPFSetup/ViewModels/UserViewModels/UninstallViewModel.cs | 卸载完成提示 + Loaded 时检测并关闭进程 |
| src/Setup/Project.WPFSetup/ViewModels/UserViewModels/InstallViewModel.cs | 安装路径选择/创建目录逻辑优化 |
| src/Setup/Project.WPFSetup/Project.WPFSetup.csproj | 引入 System.Text.Json |
| src/Setup/Project.WPFSetup/Common/Setups/UninstallDeleteInstallFolderSetup.cs | 卸载时按快照删除文件 |
| src/Setup/Project.WPFSetup/Common/Setups/DeleteInstallFolderSetup.cs | 删除安装目录逻辑改为按快照清理 |
| src/Setup/Project.WPFSetup/Common/Setups/DecompressionSetup.cs | 解压时生成卸载快照(unstall.dat) |
| src/Setup/Project.WPFSetup/Common/SetupProperty.cs | HelpLink 指向 issues |
| src/Project.WPFUninstaller/MainWindow.xaml.cs | code-behind 继承声明调整 |
| src/KuroGameDownloadProgram/Program.cs | 使用 V2 Context 的控制台下载/安装测试程序 |
| src/KuroGameDownloadProgram/KuroGameDownloadProgram.csproj | TFM 改为 net10.0-windows;复制 hpatchz.exe |
| src/Haiyu.Run/Program.cs | 删除空项目入口 |
| src/Haiyu.Run/Haiyu.Run.csproj | 删除空项目工程文件 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| : base(config, name) { } | ||
|
|
||
| public override Type ContextType => typeof(WavesMainGameContextV2); | ||
| public override GameType GameType => GameType.Punish; |
There was a problem hiding this comment.
WavesMainGameContextV2 的 GameType 返回了 GameType.Punish,这会导致“鸣潮V2”上下文被当成战双处理(例如启动参数、资源/配置选择、UI 展示)。这里应返回对应的 Waves 类型。
| public override GameType GameType => GameType.Punish; | |
| public override GameType GameType => GameType.Waves; |
| <ProgressBar | ||
| Grid.Row="1" | ||
| MaxHeight="{x:Bind ViewModel.MaxTotal, Mode=OneWay}" | ||
| HorizontalAlignment="Stretch" | ||
| Value="{x:Bind ViewModel.Current, Mode=OneWay}" /> |
There was a problem hiding this comment.
DeleteFileDialog 的 ProgressBar 使用了 MaxHeight 绑定 MaxTotal。MaxHeight 只影响控件布局高度,无法设置进度条上限,导致进度显示不会按总量变化。这里应绑定到 ProgressBar 的 Maximum(并按需设置 Height)。
| var uninstallDat =Path.Combine(installFolder, "unstall.dat"); | ||
| if (File.Exists(uninstallDat)) | ||
| { | ||
| try | ||
| var reader = File.OpenText(uninstallDat); | ||
| var list = JsonSerializer.Deserialize<List<string>>(await reader.ReadToEndAsync()); | ||
| while (true) | ||
| { | ||
| if (!Directory.Exists(directoryPath)) | ||
| { | ||
| Console.WriteLine("指定的目录不存在。"); | ||
| return true; | ||
| } | ||
| foreach ( | ||
| var file in Directory.GetFiles( | ||
| directoryPath, | ||
| "*.*", | ||
| SearchOption.TopDirectoryOnly | ||
| ) | ||
| ) | ||
| try | ||
| { | ||
| if (file.StartsWith(setupProperty.UninstallName)) | ||
| { | ||
| continue; | ||
| } | ||
| try | ||
| { | ||
| File.Delete(file); | ||
| Console.WriteLine($"已删除文件: {file}"); | ||
| } | ||
| catch (Exception ex) | ||
| foreach (var file in list) | ||
| { | ||
| Console.WriteLine($"无法删除文件 {file}: {ex.Message}"); | ||
| if (File.Exists(file)) | ||
| { | ||
| File.Delete(file); | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| foreach ( | ||
| var dir in Directory.GetDirectories( | ||
| directoryPath, | ||
| "*", | ||
| SearchOption.TopDirectoryOnly | ||
| ) | ||
| ) | ||
| catch | ||
| { | ||
| try | ||
| { | ||
| Directory.Delete(dir, true); | ||
| } | ||
| catch (Exception) { } | ||
| } | ||
| } |
There was a problem hiding this comment.
这里对 uninstall.dat 的删除采用 while(true)+空 catch 的方式:一旦遇到权限/占用等异常会无限重试并卡死卸载流程。同时 reader 未 Dispose 也可能导致文件一直被占用。建议改为有限次数重试/退避并在 finally 中释放 reader,必要时将失败项记录并继续。
| if (status.IsPause || GameContext.ProdDownloadState.IsPaused) | ||
| { | ||
| await this.GameContext.ResumeDownloadAsync(); | ||
| this.PreDownloadIcon = "\uE768"; | ||
| } | ||
| else | ||
| { | ||
| await this.GameContext.PauseDownloadAsync(); | ||
| this.PreDownloadIcon = "\uE768"; | ||
| } |
There was a problem hiding this comment.
StartDownloadProdGameResource 中暂停/恢复两条分支都把 PreDownloadIcon 设置为同一个 Glyph(\uE768)。这会导致 UI 无法反映当前状态(暂停 vs 恢复)。建议分别设置为对应的“暂停/播放”图标并与现有 Predownload 卡片逻辑保持一致。
| if (!Param.CheckParam<string>("diffFolderPath", out var diffFolderPath)) | ||
| { | ||
| return false; | ||
| } | ||
| if(!Param.CheckParam<string>("gameBaseFolder",out var gameBaseFolder)) | ||
| this.krdiffs = krdiffs!; | ||
| this.diffFolderPath = diffFolderPath!; | ||
| this.gameBaseFolder = gameBaseFolder!; | ||
| return true; |
There was a problem hiding this comment.
CheckAsync 里对 "gameBaseFolder" 的 CheckParam 缺少大括号并且没有在失败时 return false;当前写法会在参数不存在时仍继续赋值,导致 gameBaseFolder 为空并在后续磁盘空间检查/路径拼接时抛异常。请补齐条件块并在校验失败时返回。
| public void SetParam(Dictionary<string, object> param, IGameEventPublisher gameEventPublisher) | ||
| { | ||
| this.Param = param; | ||
| this.GameEventPublisher = GameEventPublisher; | ||
| } |
There was a problem hiding this comment.
SetParam(Dictionary<string, object>, IGameEventPublisher) 中把 this.GameEventPublisher 赋值成了 GameEventPublisher(自身属性),而不是传入的 gameEventPublisher。这样会导致 Publish 调用时 publisher 仍为 null。应改为使用方法参数赋值。
| public void SetParam(Dictionary<string, object> param, GameEventPublisher gameEventPublisher) | ||
| { | ||
| this.Param = param; | ||
| this.GameEventPublisher = GameEventPublisher; | ||
| } |
There was a problem hiding this comment.
SetParam(Dictionary<string, object>, GameEventPublisher) 中把 this.GameEventPublisher 赋值成了 GameEventPublisher(自身属性),导致注入的 publisher 丢失,后续 Publish 会是 null 或旧实例。这里应赋值为传入的 gameEventPublisher(并建议参数类型用 IGameEventPublisher 以保持一致)。
| public static double GetInterpolatedAngle(double startAngle, double endAngle, double valueAngle) | ||
| { | ||
| // Linear interpolation formula (lerp): GetInterpolatedAngle = (startAngle + valueAngle) * (endAngle - startAngle) | ||
| return (startAngle + valueAngle) * (endAngle - startAngle); | ||
| } |
There was a problem hiding this comment.
GetInterpolatedAngle 的线性插值公式实现不正确:当前返回 (startAngle + valueAngle) * (endAngle - startAngle),会随着 startAngle 增大而放大比例,且在 valueAngle=0 时不等于 startAngle。这里应使用标准 lerp 形式 startAngle + valueAngle * (endAngle - startAngle)。