GeWuYou af64c4ab3e refactor(setting): 重构设置事件系统
- 移除旧的设置相关事件类包括 SettingsAllLoadedEvent、SettingsBatchChangedEvent、
  SettingsBatchSavedEvent、SettingsChangedEvent、SettingsDeletedEvent 和 SettingsLoadedEvent
- 添加新的设置事件类包括 SettingsAppliedAllEvent、SettingsInitializedEvent、
  SettingsSavedAllEvent 和 SettingsResetEvent
- 在 SettingsModel 中集成新的事件发送机制
- 修改 Initialize 方法以发送 SettingsInitializedEvent 事件
- 更新 Save 方法以发送 SettingsSavedAllEvent 事件
- 修改 Apply 方法以发送 SettingsAppliedAllEvent 事件
- 添加 Reset 功能以发送 SettingsResetEvent 事件
- 添加 ResetAll 功能以发送 SettingsResetAllEvent 事件
- [release ci]
2026-01-31 23:47:24 +08:00

260 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using GFramework.Core.Abstractions.logging;
using GFramework.Core.extensions;
using GFramework.Core.logging;
using GFramework.Core.model;
using GFramework.Game.Abstractions.data;
using GFramework.Game.Abstractions.setting;
using GFramework.Game.setting.events;
namespace GFramework.Game.setting;
/// <summary>
/// 设置模型:
/// - 管理 Settings Data 的生命周期Load / Save / Reset / Migration
/// - 编排 Settings Applicator 的 Apply 行为
/// </summary>
public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider, TRepository? repository)
: AbstractModel, ISettingsModel
where TRepository : class, ISettingsDataRepository
{
private static readonly ILogger Log =
LoggerFactoryResolver.Provider.CreateLogger(nameof(SettingsModel<TRepository>));
private readonly ConcurrentDictionary<Type, IResetApplyAbleSettings> _applicators = new();
// =========================
// Fields
// =========================
private readonly ConcurrentDictionary<Type, ISettingsData> _data = new();
private readonly ConcurrentDictionary<Type, Dictionary<int, ISettingsMigration>> _migrationCache = new();
private readonly ConcurrentDictionary<(Type type, int from), ISettingsMigration> _migrations = new();
private IDataLocationProvider? _locationProvider = locationProvider;
private ISettingsDataRepository? _repository = repository;
private ISettingsDataRepository DataRepository =>
_repository ?? throw new InvalidOperationException("ISettingsDataRepository not initialized.");
private IDataLocationProvider LocationProvider =>
_locationProvider ?? throw new InvalidOperationException("IDataLocationProvider not initialized.");
// =========================
// Data access
// =========================
/// <summary>
/// 获取指定类型的设置数据实例(唯一实例)
/// </summary>
public T GetData<T>() where T : class, ISettingsData, new()
{
return (T)_data.GetOrAdd(typeof(T), _ => new T());
}
public IEnumerable<ISettingsData> AllData()
{
return _data.Values;
}
// =========================
// Applicator
// =========================
/// <summary>
/// 注册设置应用器
/// </summary>
public ISettingsModel RegisterApplicator<T>(T applicator)
where T : class, IResetApplyAbleSettings
{
_applicators[typeof(T)] = applicator;
_data[applicator.DataType] = applicator.Data;
return this;
}
/// <summary>
/// 获取所有设置应用器
/// </summary>
public IEnumerable<IResetApplyAbleSettings> AllApplicators()
{
return _applicators.Values;
}
// =========================
// Migration
// =========================
public ISettingsModel RegisterMigration(ISettingsMigration migration)
{
_migrations[(migration.SettingsType, migration.FromVersion)] = migration;
return this;
}
// =========================
// Lifecycle
// =========================
/// <summary>
/// 初始化设置模型:
/// - 加载所有已存在的 Settings Data
/// - 执行必要的迁移
/// </summary>
public async Task InitializeAsync()
{
IDictionary<string, IData> allData;
try
{
allData = await DataRepository.LoadAllAsync();
}
catch (Exception ex)
{
Log.Error("Failed to load unified settings file.", ex);
return;
}
foreach (var data in _data.Values)
{
try
{
var type = data.GetType();
var location = LocationProvider.GetLocation(type);
if (!allData.TryGetValue(location.Key, out var raw))
continue;
if (raw is not ISettingsData loaded)
continue;
var migrated = MigrateIfNeeded(loaded);
// 回填(不替换实例)
data.LoadFrom(migrated);
}
catch (Exception ex)
{
Log.Error($"Failed to initialize settings data: {data.GetType().Name}", ex);
}
}
this.SendEvent(new SettingsInitializedEvent());
}
/// <summary>
/// 将所有 Settings Data 持久化
/// </summary>
public async Task SaveAllAsync()
{
foreach (var data in _data.Values)
{
try
{
var location = LocationProvider.GetLocation(data.GetType());
await DataRepository.SaveAsync(location, data);
}
catch (Exception ex)
{
Log.Error($"Failed to save settings data: {data.GetType().Name}", ex);
}
}
this.SendEvent(new SettingsSavedAllEvent());
}
/// <summary>
/// 应用所有设置
/// </summary>
public async Task ApplyAllAsync()
{
foreach (var applicator in _applicators)
{
try
{
await applicator.Value.Apply();
}
catch (Exception ex)
{
Log.Error($"Failed to apply settings: {applicator.GetType().Name}", ex);
}
}
this.SendEvent(new SettingsAppliedAllEvent());
}
/// <summary>
/// 重置指定类型的可重置对象
/// </summary>
/// <typeparam name="T">要重置的对象类型必须是class类型实现IResettable接口并具有无参构造函数</typeparam>
public void Reset<T>() where T : class, ISettingsData, new()
{
var data = GetData<T>();
data.Reset();
this.SendEvent(new SettingsResetEvent<T>(data));
}
/// <summary>
/// 重置所有设置
/// </summary>
public void ResetAll()
{
foreach (var data in _data.Values)
data.Reset();
foreach (var applicator in _applicators)
applicator.Value.Reset();
this.SendEvent(new SettingsResetAllEvent(_data.Values));
}
/// <summary>
/// 获取指定类型的设置应用器
/// </summary>
/// <typeparam name="T">要获取的设置应用器类型必须继承自IResetApplyAbleSettings</typeparam>
/// <returns>设置应用器实例如果不存在则返回null</returns>
public T? GetApplicator<T>() where T : class, IResetApplyAbleSettings
{
return _applicators.TryGetValue(typeof(T), out var app)
? (T)app
: null;
}
// =========================
// Init
// =========================
protected override void OnInit()
{
_repository ??= this.GetUtility<TRepository>()!;
_locationProvider ??= this.GetUtility<IDataLocationProvider>()!;
foreach (var type in _data.Keys)
{
var location = _locationProvider.GetLocation(type);
DataRepository.RegisterDataType(location, type);
}
}
private ISettingsData MigrateIfNeeded(ISettingsData data)
{
if (data is not IVersionedData versioned)
return data;
var type = data.GetType();
var current = data;
if (!_migrationCache.TryGetValue(type, out var versionMap))
{
versionMap = _migrations
.Where(kv => kv.Key.type == type)
.ToDictionary(kv => kv.Key.from, kv => kv.Value);
_migrationCache[type] = versionMap;
}
while (versionMap.TryGetValue(versioned.Version, out var migration))
{
current = (ISettingsData)migration.Migrate(current);
versioned = current;
}
return current;
}
}