保存宏时同步写入该职业模块依赖快照

宏页保存原先只更新 keymap 和插件,模块快照仍停留在旧宏。改为走与配置页相同的回调,按当前职业捕获并保存模块。

Co-authored-by: Wayne-Arasaka <waynebian01@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-09-01 13:36:21 +00:00
parent be584d2b8c
commit aa3f0d79a5
3 changed files with 22 additions and 18 deletions

View File

@@ -104,7 +104,7 @@ wow_process.txt 目标游戏进程名列表;构建时复制,运行期间每
- [UI/ClassConfigEditorControl.cs](UI/ClassConfigEditorControl.cs):左侧职业列表 + 右侧按专精切换的六页编辑器(状态/光环/冷却/队伍/技能列表/物品列表),状态字段用 `ClassStateCatalog` 驱动的 `ComboBoxColumn`
- [UI/ClassMacrosEditorControl.cs](UI/ClassMacrosEditorControl.cs):左侧职业列表 + 右侧三页编辑器(动态宏/静态宏/特殊宏),偏移提示显示槽位编号计算。
- 两个编辑器均接受 `Func<string?>` 项目路径解析器 + `Func<string, Task<string?>>` 保存回调,由 `MainForm` 在构造时注入。保存流程:编辑器调 `Store.Save()` → 传入已保存文件路径 → 重新生成 config/keymap → 单文件部署游戏 → 重启运行时;部署失败返回说明,但不回滚本地文件。
- 两个编辑器均接受 `Func<string?>` 项目路径解析器 + `Func<string, int, Task<ClassConfigPostSaveResult>>` 保存回调,由 `MainForm` 在构造时注入。保存流程:编辑器调 `Store.Save()` 同步保存该职业模块依赖快照 → 传入已保存文件路径 → 重新生成 config/keymap → 单文件部署游戏 → 重启运行时;部署失败返回说明,但不回滚本地文件。
- 配置更新的多个入口通过任务尾队列串行执行;运行时重启会等待该队列稳定,主窗口关闭也会等待正在写盘的转换和部署完成。新增同步入口必须继续走这条队列。
## UI 约定

View File

@@ -10,7 +10,7 @@ namespace Shigure;
public sealed class ClassMacrosEditorControl : UserControl
{
private readonly Func<string?> _resolveClassMacrosPath;
private readonly Func<string, Task<string?>> _updateConfigAsync;
private readonly Func<string, int, Task<ClassConfigPostSaveResult>> _updateConfigAsync;
private readonly ListBox _classList = new();
private readonly Label _pathLabel = new();
@@ -43,7 +43,7 @@ public sealed class ClassMacrosEditorControl : UserControl
public ClassMacrosEditorControl(
Func<string?> resolveClassMacrosPath,
Func<string, Task<string?>> updateConfigAsync)
Func<string, int, Task<ClassConfigPostSaveResult>> updateConfigAsync)
{
_resolveClassMacrosPath = resolveClassMacrosPath;
_updateConfigAsync = updateConfigAsync;
@@ -1020,6 +1020,12 @@ public sealed class ClassMacrosEditorControl : UserControl
return;
}
if (_currentClassId is not { } classId)
{
MessageBox.Show("请先选择职业。", "宏", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
var localSaved = false;
try
{
@@ -1028,17 +1034,18 @@ public sealed class ClassMacrosEditorControl : UserControl
localSaved = true;
SetDirty(false);
_statusLabel.Text = "本地 Lua 已保存,正在更新配置并同步游戏…";
var syncIssue = await _updateConfigAsync(_document.FilePath);
var updateResult = await _updateConfigAsync(_document.FilePath, classId);
if (IsDisposed)
{
return;
}
if (!string.IsNullOrWhiteSpace(syncIssue))
if (!string.IsNullOrWhiteSpace(updateResult.AddonSyncIssue))
{
_statusLabel.Text = "本地已保存并更新配置,但游戏同步失败";
MessageBox.Show(
$"本地 Lua 已保存config/keymap 已更新,但游戏插件同步未完成:\n{syncIssue}",
$"本地 Lua、该职业的 {updateResult.SavedModuleCount} 个模块及 config/keymap 已保存,"
+ $"但游戏插件同步未完成:\n{updateResult.AddonSyncIssue}",
"游戏同步失败",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
@@ -1046,13 +1053,16 @@ public sealed class ClassMacrosEditorControl : UserControl
return;
}
_statusLabel.Text = "已保存并更新配置";
_statusLabel.Text = $"已保存宏及该职业的 {updateResult.SavedModuleCount} 个模块";
UpdateOffsetHint();
var warningText = updateResult.ModuleWarnings.Count == 0
? string.Empty
: $"\n\n有 {updateResult.ModuleWarnings.Count} 个模块未携带依赖,详情见日志。";
MessageBox.Show(
"请在游戏内重载界面, /reload",
$"已一并保存该职业的 {updateResult.SavedModuleCount} 个模块。{warningText}\n\n请在游戏内重载界面, /reload",
"保存成功",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
updateResult.ModuleWarnings.Count == 0 ? MessageBoxIcon.Information : MessageBoxIcon.Warning);
}
catch (Exception ex)
{

View File

@@ -185,7 +185,7 @@ public sealed class MainForm : Form, IMessageFilter
_classConfigEditor.DirtyStateChanged += dirty => _statusForm.SetPageDirty(SettingsPage.Config, dirty);
_classMacrosEditor = new ClassMacrosEditorControl(
() => Path.Combine(_addonSyncService.SourceRoot, "core", "classmacros.lua"),
UpdateConfigAfterSaveAsync);
UpdateClassConfigAfterSaveAsync);
_statusForm.AttachMacrosEditor(_classMacrosEditor);
_classMacrosEditor.DirtyStateChanged += dirty => _statusForm.SetPageDirty(SettingsPage.Macros, dirty);
_statusForm.FormClosing += (_, _) =>
@@ -1497,12 +1497,6 @@ public sealed class MainForm : Form, IMessageFilter
}
}
private async Task<string?> UpdateConfigAfterSaveAsync(string savedAddonFilePath)
{
var result = await QueueProjectConfigUpdateAsync(savedAddonFilePath);
return DescribeAddonSyncIssue(result.AddonSync);
}
private async Task<ClassConfigPostSaveResult> UpdateClassConfigAfterSaveAsync(
string savedAddonFilePath,
int classId)
@@ -1512,7 +1506,7 @@ public sealed class MainForm : Form, IMessageFilter
if (moduleResult.Errors.Count > 0)
{
throw new InvalidOperationException(
$"配置已更新,但该职业有 {moduleResult.Errors.Count} 个模块保存失败;"
$"该职业有 {moduleResult.Errors.Count} 个模块保存失败;"
+ $"已成功保存 {moduleResult.SavedCount} 个模块。详情见日志。");
}
@@ -1551,7 +1545,7 @@ public sealed class MainForm : Form, IMessageFilter
}
var className = ClassNames.GetClassAndSpecName(classId, null).ClassName ?? $"职业{classId}";
AppendLog($"保存{className}配置时已同步保存 {savedCount}/{modules.Count} 个该职业模块。");
AppendLog($"已同步保存 {savedCount}/{modules.Count} 个{className}模块。");
foreach (var warning in warnings)
{
AppendLog($"模块依赖保存提示: {warning}");