保存配置会将当前职业的所有模块一起保存, 覆盖掉可能冲突项

This commit is contained in:
waynebian01
2026-08-29 17:16:01 +08:00
parent 630004eac9
commit 03d2d2c72e
3 changed files with 91 additions and 12 deletions

View File

@@ -476,9 +476,9 @@ public sealed class ModuleStore
}
/// <summary>
/// 回写导入阶段清理过的依赖快照,并保留模块原有文件位置。
/// 回写已更新的模块依赖快照,并保留模块原有文件位置。
/// </summary>
public ModuleDefinition SaveDependencyCleanup(ModuleDefinition module)
public ModuleDefinition SaveDependenciesInPlace(ModuleDefinition module)
{
Normalize(module);
lock (_gate)
@@ -501,6 +501,8 @@ public sealed class ModuleStore
_modules.Remove(existing);
_modules.Add(module.Clone());
_modules = SortModules(_modules).ToList();
_rejectedModuleIds.Remove(module.Id);
_importIssueModuleIds.Remove(module.Id);
return module.Clone();
}
}

View File

@@ -3,6 +3,11 @@ using System.Globalization;
namespace Shigure;
public sealed record ClassConfigPostSaveResult(
string? AddonSyncIssue,
int SavedModuleCount,
IReadOnlyList<string> ModuleWarnings);
/// <summary>
/// 图形化编辑 Fuyutsui class/*.lua 的 ClassBlocksstates / auras / spells / group
/// 并编辑同文件中的 spellsList。
@@ -10,7 +15,7 @@ namespace Shigure;
public sealed class ClassConfigEditorControl : UserControl
{
private readonly Func<string?> _resolveClassDirectory;
private readonly Func<string, Task<string?>> _updateConfigAsync;
private readonly Func<string, int, Task<ClassConfigPostSaveResult>> _updateConfigAsync;
private readonly ListBox _classList = new();
private readonly ListBox _specList = new();
@@ -75,7 +80,7 @@ public sealed class ClassConfigEditorControl : UserControl
public ClassConfigEditorControl(
Func<string?> resolveClassDirectory,
Func<string, Task<string?>> updateConfigAsync)
Func<string, int, Task<ClassConfigPostSaveResult>> updateConfigAsync)
{
_resolveClassDirectory = resolveClassDirectory;
_updateConfigAsync = updateConfigAsync;
@@ -2759,29 +2764,33 @@ public sealed class ClassConfigEditorControl : UserControl
localSaved = true;
SetDirty(false);
_statusLabel.Text = "本地 Lua 已保存,正在更新配置并同步游戏…";
var syncIssue = await _updateConfigAsync(_currentDocument.FilePath);
var updateResult = await _updateConfigAsync(_currentDocument.FilePath, _currentClassId.Value);
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);
return;
}
_statusLabel.Text = "已保存并更新配置";
_statusLabel.Text = $"已保存配置及该职业的 {updateResult.SavedModuleCount} 个模块";
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

@@ -123,6 +123,11 @@ public sealed class MainForm : Form, IMessageFilter
FuyutsuiKeymapConverter.UpdateResult? Keymap,
FuyutsuiAddonSyncResult AddonSync);
private sealed record ClassModuleSaveResult(
int SavedCount,
IReadOnlyList<string> Warnings,
IReadOnlyList<string> Errors);
internal MainForm(
AppOptions initialOptions,
string baseDirectory,
@@ -175,7 +180,7 @@ public sealed class MainForm : Form, IMessageFilter
_statusForm.AttachModuleEditor(_moduleEditor);
_classConfigEditor = new ClassConfigEditorControl(
() => Path.Combine(_addonSyncService.SourceRoot, "class"),
UpdateConfigAfterSaveAsync);
UpdateClassConfigAfterSaveAsync);
_statusForm.AttachConfigEditor(_classConfigEditor);
_classConfigEditor.DirtyStateChanged += dirty => _statusForm.SetPageDirty(SettingsPage.Config, dirty);
_classMacrosEditor = new ClassMacrosEditorControl(
@@ -310,7 +315,7 @@ public sealed class MainForm : Form, IMessageFilter
{
try
{
_moduleStore.SaveDependencyCleanup(module);
_moduleStore.SaveDependenciesInPlace(module);
cleanedModuleCount++;
}
catch (Exception ex)
@@ -1494,6 +1499,69 @@ public sealed class MainForm : Form, IMessageFilter
return DescribeAddonSyncIssue(result.AddonSync);
}
private async Task<ClassConfigPostSaveResult> UpdateClassConfigAfterSaveAsync(
string savedAddonFilePath,
int classId)
{
var moduleResult = SaveModulesForClass(classId);
var result = await QueueProjectConfigUpdateAsync(savedAddonFilePath);
if (moduleResult.Errors.Count > 0)
{
throw new InvalidOperationException(
$"配置已更新,但该职业有 {moduleResult.Errors.Count} 个模块保存失败;"
+ $"已成功保存 {moduleResult.SavedCount} 个模块。详情见日志。");
}
return new ClassConfigPostSaveResult(
DescribeAddonSyncIssue(result.AddonSync),
moduleResult.SavedCount,
moduleResult.Warnings);
}
private ClassModuleSaveResult SaveModulesForClass(int classId)
{
var modules = _moduleStore.GetModulesForDisplay()
.Where(module => module.Match.ClassId == classId)
.ToList();
var warnings = new List<string>();
var errors = new List<string>();
var savedCount = 0;
foreach (var module in modules)
{
try
{
module.Version = AppInfo.Version;
var warning = _moduleDependencyService.Capture(module);
_moduleStore.SaveDependenciesInPlace(module);
savedCount++;
if (!string.IsNullOrWhiteSpace(warning))
{
warnings.Add($"{module.Name}: {warning}");
}
}
catch (Exception ex)
{
errors.Add($"{module.Name}: {ex.Message}");
}
}
var className = ClassNames.GetClassAndSpecName(classId, null).ClassName ?? $"职业{classId}";
AppendLog($"保存{className}配置时已同步保存 {savedCount}/{modules.Count} 个该职业模块。");
foreach (var warning in warnings)
{
AppendLog($"模块依赖保存提示: {warning}");
}
foreach (var error in errors)
{
AppendLog($"模块依赖保存失败: {error}");
}
RefreshModuleSelector(_lastSnapshot, forceRefresh: true);
RefreshDefaultModuleSelector();
return new ClassModuleSaveResult(savedCount, warnings, errors);
}
private Task<ProjectConfigUpdateResult> QueueProjectConfigUpdateAsync(string? savedAddonFilePath)
{
lock (_configUpdateSync)