GeWuYou 970b8d3b96 refactor(settings): 重构设置系统和数据仓库实现
- 将音频和图形设置从 IResettable, IVersioned 迁移到 ISettingsData 接口
- 添加数据位置接口 IDataLocation 和数据位置提供者接口 IDataLocationProvider
- 修改数据仓库实现,使用数据位置替代类型进行数据操作
- 更新数据仓库的加载、保存、删除和存在检查方法以使用数据位置参数
- 重命名 IPersistentApplyAbleSettings 为 IResetApplyAbleSettings 并更新其实现
- 创建 ISettingsData 接口整合设置数据的基础功能
- 更新设置模型实现,统一管理设置数据的生命周期和应用器
- 添加版本化数据接口 IVersionedData 和可从源加载接口 ILoadableFrom
- 实现数据位置到存储键的扩展方法
- 更新数据事件类型以使用数据位置信息
- 重构设置模型的数据加载、保存和应用逻辑
- [skip ci]
2026-01-30 16:48:09 +08:00

206 lines
5.8 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 System.Collections.Concurrent;
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;
namespace GFramework.Game.setting;
/// <summary>
/// 设置模型:
/// - 管理 Settings Data 的生命周期Load / Save / Reset / Migration
/// - 编排 Settings Applicator 的 Apply 行为
/// </summary>
public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
where TRepository : class, IDataRepository
{
private static readonly ILogger Log =
LoggerFactoryResolver.Provider.CreateLogger(nameof(SettingsModel<TRepository>));
// =========================
// Fields
// =========================
private readonly ConcurrentDictionary<Type, ISettingsData> _data = new();
private readonly ConcurrentBag<IResetApplyAbleSettings> _applicators = new();
private readonly ConcurrentDictionary<(Type type, int from), ISettingsMigration> _migrations = new();
private readonly ConcurrentDictionary<Type, Dictionary<int, ISettingsMigration>> _migrationCache = new();
private IDataRepository? _repository;
private IDataLocationProvider? _locationProvider;
private IDataRepository Repository =>
_repository ?? throw new InvalidOperationException("IDataRepository not initialized.");
private IDataLocationProvider LocationProvider =>
_locationProvider ?? throw new InvalidOperationException("IDataLocationProvider not initialized.");
// =========================
// Init
// =========================
protected override void OnInit()
{
_repository ??= this.GetUtility<TRepository>()!;
_locationProvider ??= this.GetUtility<IDataLocationProvider>()!;
}
// =========================
// 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(IResetApplyAbleSettings applicator)
{
_applicators.Add(applicator);
return this;
}
/// <summary>
/// 获取所有设置应用器
/// </summary>
public IEnumerable<IResetApplyAbleSettings> AllApplicators()
{
return _applicators;
}
// =========================
// Migration
// =========================
public ISettingsModel RegisterMigration(ISettingsMigration migration)
{
_migrations[(migration.SettingsType, migration.FromVersion)] = migration;
return this;
}
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;
}
// =========================
// Lifecycle
// =========================
/// <summary>
/// 初始化设置模型:
/// - 加载所有已存在的 Settings Data
/// - 执行必要的迁移
/// </summary>
public async Task InitializeAsync()
{
foreach (var data in _data.Values)
{
try
{
var type = data.GetType();
var location = LocationProvider.GetLocation(type);
if (!await Repository.ExistsAsync(location))
continue;
var loaded = await Repository.LoadAsync<ISettingsData>(location);
var migrated = MigrateIfNeeded(loaded);
// 回填数据(不替换实例)
data.LoadFrom(migrated);
}
catch (Exception ex)
{
Log.Error($"Failed to initialize settings data: {data.GetType().Name}", ex);
}
}
}
/// <summary>
/// 将所有 Settings Data 持久化
/// </summary>
public async Task SaveAllAsync()
{
foreach (var data in _data.Values)
{
try
{
var location = LocationProvider.GetLocation(data.GetType());
await Repository.SaveAsync(location, data);
}
catch (Exception ex)
{
Log.Error($"Failed to save settings data: {data.GetType().Name}", ex);
}
}
}
/// <summary>
/// 应用所有设置
/// </summary>
public async Task ApplyAllAsync()
{
foreach (var applicator in _applicators)
{
try
{
await applicator.Apply();
}
catch (Exception ex)
{
Log.Error($"Failed to apply settings: {applicator.GetType().Name}", ex);
}
}
}
/// <summary>
/// 重置所有设置
/// </summary>
public void ResetAll()
{
foreach (var data in _data.Values)
data.Reset();
foreach (var applicator in _applicators)
applicator.Reset();
}
}