mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 19:03:29 +08:00
feat(settings): 重构设置系统架构以支持数据仓库模式
- 为 ILoadableFrom 接口添加 XML 文档注释 - 重命名 UnifiedSettingsRepository 为 UnifiedSettingsDataRepository - 将仓库基类从 IDataRepository 替换为更具体的 ISettingsDataRepository - 为 UnifiedSettingsDataRepository 添加完整的 XML 文档注释 - 在 SettingsModel 中使用 ISettingsDataRepository 替代 IDataRepository - 修改 SettingsModel 中的应用器存储结构从 ConcurrentBag 到 ConcurrentDictionary - 添加 LoadAllAsync 方法以支持批量加载所有设置数据 - 优化 SettingsModel 初始化逻辑以使用批量加载提高性能 - 为 AudioSettings、GraphicsSettings 和 LocalizationSettings 添加 LoadFrom 实现 - 将设置数据版本属性改为私有只读以防止外部修改 - 更新 SettingsSystem 接口约束以匹配新的抽象层设计 - 添加 GetApplicator 泛型方法以支持获取特定类型的应用器 - 实现 Reset 泛型方法以支持重置指定类型的设置数据 - [release ci]
This commit is contained in:
parent
970b8d3b96
commit
7d581f07ca
@ -13,7 +13,15 @@
|
||||
|
||||
namespace GFramework.Core.Abstractions.data;
|
||||
|
||||
/// <summary>
|
||||
/// 定义从指定类型数据源加载数据的接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据源的类型</typeparam>
|
||||
public interface ILoadableFrom<in T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 从指定的数据源加载数据到当前对象
|
||||
/// </summary>
|
||||
/// <param name="source">用作数据源的对象,类型为T</param>
|
||||
void LoadFrom(T source);
|
||||
}
|
||||
@ -29,6 +29,7 @@ public interface IDataRepository : IUtility
|
||||
Task<T> LoadAsync<T>(IDataLocation location)
|
||||
where T : class, IData, new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 异步保存数据到指定位置
|
||||
/// </summary>
|
||||
|
||||
34
GFramework.Game.Abstractions/data/ISettingsDataRepository.cs
Normal file
34
GFramework.Game.Abstractions/data/ISettingsDataRepository.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
namespace GFramework.Game.Abstractions.data;
|
||||
|
||||
/// <summary>
|
||||
/// 定义设置数据仓库接口,用于管理应用程序设置数据的存储和检索
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该接口继承自IDataRepository,专门用于处理配置设置相关的数据操作
|
||||
/// </remarks>
|
||||
public interface ISettingsDataRepository : IDataRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步加载所有设置项
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 返回一个包含所有设置键值对的字典,其中键为设置名称,值为对应的设置数据对象
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// 此方法将从数据源中异步读取所有可用的设置项,并将其组织成字典格式返回
|
||||
/// </remarks>
|
||||
Task<IDictionary<string, IData>> LoadAllAsync();
|
||||
}
|
||||
@ -35,9 +35,19 @@ public interface ISettingsModel : IModel
|
||||
/// <summary>
|
||||
/// 注册设置应用器
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置数据类型,必须实现ISettingsData接口且具有无参构造函数</typeparam>
|
||||
/// <param name="applicator">要注册的设置应用器</param>
|
||||
/// <returns>当前设置模型实例,支持链式调用</returns>
|
||||
ISettingsModel RegisterApplicator(IResetApplyAbleSettings applicator);
|
||||
ISettingsModel RegisterApplicator<T>(IResetApplyAbleSettings applicator) where T : class, ISettingsData, new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的设置应用器
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要获取的设置应用器类型,必须继承自IResetApplyAbleSettings</typeparam>
|
||||
/// <returns>设置应用器实例,如果不存在则返回null</returns>
|
||||
T? GetApplicator<T>() where T : class, IResetApplyAbleSettings;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有设置应用器
|
||||
@ -80,8 +90,15 @@ public interface ISettingsModel : IModel
|
||||
/// <returns>异步操作任务</returns>
|
||||
Task ApplyAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 重置指定类型的设置
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要重置的设置类型,必须实现IResettable接口并具有无参构造函数</typeparam>
|
||||
void Reset<T>() where T : class, ISettingsData, new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 重置所有设置数据与应用器
|
||||
/// </summary>
|
||||
void ResetAll();
|
||||
}
|
||||
}
|
||||
@ -16,9 +16,9 @@ public interface ISettingsSystem : ISystem
|
||||
/// <summary>
|
||||
/// 应用指定类型的设置(泛型版本)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型,必须是class且实现IApplyAbleSettings接口</typeparam>
|
||||
/// <typeparam name="T">设置类型,必须是class且实现IResetApplyAbleSettings接口</typeparam>
|
||||
/// <returns>表示异步操作的任务</returns>
|
||||
Task Apply<T>() where T : class, IApplyAbleSettings;
|
||||
Task Apply<T>() where T : class, IResetApplyAbleSettings;
|
||||
|
||||
/// <summary>
|
||||
/// 保存所有设置
|
||||
@ -31,7 +31,7 @@ public interface ISettingsSystem : ISystem
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型,必须继承自class并实现IPersistentApplyAbleSettings接口</typeparam>
|
||||
/// <returns>表示异步操作的任务</returns>
|
||||
Task Reset<T>() where T : class, IResetApplyAbleSettings, new();
|
||||
Task Reset<T>() where T : class, ISettingsData, IResetApplyAbleSettings, new();
|
||||
|
||||
/// <summary>
|
||||
/// 重置所有设置
|
||||
|
||||
@ -34,10 +34,29 @@ public class AudioSettings : ISettingsData
|
||||
/// <summary>
|
||||
/// 获取或设置设置数据的版本号
|
||||
/// </summary>
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
public int Version { get; private set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置数据最后修改的时间
|
||||
/// </summary>
|
||||
public DateTime LastModified { get; } = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的数据源加载音频设置
|
||||
/// </summary>
|
||||
/// <param name="source">包含设置数据的源对象</param>
|
||||
public void LoadFrom(ISettingsData source)
|
||||
{
|
||||
// 检查数据源是否为音频设置类型
|
||||
if (source is not AudioSettings audioSettings)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 将源数据中的各个音量设置复制到当前对象
|
||||
MasterVolume = audioSettings.MasterVolume;
|
||||
BgmVolume = audioSettings.BgmVolume;
|
||||
SfxVolume = audioSettings.SfxVolume;
|
||||
Version = audioSettings.Version;
|
||||
}
|
||||
}
|
||||
@ -33,10 +33,29 @@ public class GraphicsSettings : ISettingsData
|
||||
/// <summary>
|
||||
/// 获取或设置设置数据的版本号
|
||||
/// </summary>
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
public int Version { get; private set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置数据最后修改的时间
|
||||
/// </summary>
|
||||
public DateTime LastModified { get; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的数据源加载图形设置
|
||||
/// </summary>
|
||||
/// <param name="source">要从中加载设置的源数据对象</param>
|
||||
public void LoadFrom(ISettingsData source)
|
||||
{
|
||||
// 检查源数据是否为GraphicsSettings类型,如果不是则直接返回
|
||||
if (source is not GraphicsSettings settings)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 将源设置中的属性值复制到当前对象
|
||||
Fullscreen = settings.Fullscreen;
|
||||
ResolutionWidth = settings.ResolutionWidth;
|
||||
ResolutionHeight = settings.ResolutionHeight;
|
||||
Version = settings.Version;
|
||||
}
|
||||
}
|
||||
@ -37,10 +37,29 @@ public class LocalizationSettings : ISettingsData
|
||||
/// <summary>
|
||||
/// 获取或设置设置数据的版本号
|
||||
/// </summary>
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
public int Version { get; private set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置数据最后修改的时间
|
||||
/// </summary>
|
||||
public DateTime LastModified { get; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的数据源加载本地化设置
|
||||
/// </summary>
|
||||
/// <param name="source">要从中加载设置的源对象</param>
|
||||
/// <remarks>
|
||||
/// 该方法仅处理类型为LocalizationSettings的对象,
|
||||
/// 如果源对象不是LocalizationSettings类型,则直接返回不执行任何操作
|
||||
/// </remarks>
|
||||
public void LoadFrom(ISettingsData source)
|
||||
{
|
||||
if (source is not LocalizationSettings settings)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Language = settings.Language;
|
||||
Version = settings.Version;
|
||||
}
|
||||
}
|
||||
@ -23,16 +23,16 @@ namespace GFramework.Game.data;
|
||||
/// <summary>
|
||||
/// 使用单一文件存储所有设置数据的仓库实现
|
||||
/// </summary>
|
||||
public class UnifiedSettingsRepository(
|
||||
public class UnifiedSettingsDataRepository(
|
||||
IStorage? storage,
|
||||
IRuntimeTypeSerializer? serializer,
|
||||
DataRepositoryOptions? options = null,
|
||||
string fileName = "settings.json")
|
||||
: AbstractContextUtility, IDataRepository
|
||||
: AbstractContextUtility, ISettingsDataRepository
|
||||
{
|
||||
private UnifiedSettingsFile? _file;
|
||||
private readonly SemaphoreSlim _lock = new(1, 1);
|
||||
private readonly DataRepositoryOptions _options = options ?? new DataRepositoryOptions();
|
||||
private UnifiedSettingsFile? _file;
|
||||
private bool _loaded;
|
||||
private IRuntimeTypeSerializer? _serializer = serializer;
|
||||
private IStorage? _storage = storage;
|
||||
@ -45,16 +45,16 @@ public class UnifiedSettingsRepository(
|
||||
|
||||
private UnifiedSettingsFile File =>
|
||||
_file ?? throw new InvalidOperationException("UnifiedSettingsFile not set.");
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_storage ??= this.GetUtility<IStorage>()!;
|
||||
_serializer ??= this.GetUtility<IRuntimeTypeSerializer>()!;
|
||||
}
|
||||
// =========================
|
||||
// IDataRepository
|
||||
// =========================
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载指定位置的数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型,必须继承自IData接口</typeparam>
|
||||
/// <param name="location">数据位置信息</param>
|
||||
/// <returns>加载的数据对象</returns>
|
||||
public async Task<T> LoadAsync<T>(IDataLocation location)
|
||||
where T : class, IData, new()
|
||||
{
|
||||
@ -66,6 +66,13 @@ public class UnifiedSettingsRepository(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步保存数据到指定位置
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数据类型,必须继承自IData接口</typeparam>
|
||||
/// <param name="location">数据位置信息</param>
|
||||
/// <param name="data">要保存的数据对象</param>
|
||||
/// <returns>异步操作任务</returns>
|
||||
public async Task SaveAsync<T>(IDataLocation location, T data)
|
||||
where T : class, IData
|
||||
{
|
||||
@ -81,13 +88,22 @@ public class UnifiedSettingsRepository(
|
||||
this.SendEvent(new DataSavedEvent<T>(data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定位置的数据是否存在
|
||||
/// </summary>
|
||||
/// <param name="location">数据位置信息</param>
|
||||
/// <returns>如果数据存在则返回true,否则返回false</returns>
|
||||
public async Task<bool> ExistsAsync(IDataLocation location)
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
return File.Sections.ContainsKey(location.Key);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定位置的数据
|
||||
/// </summary>
|
||||
/// <param name="location">数据位置信息</param>
|
||||
/// <returns>异步操作任务</returns>
|
||||
public async Task DeleteAsync(IDataLocation location)
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
@ -101,7 +117,11 @@ public class UnifiedSettingsRepository(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 批量保存多个数据项到存储
|
||||
/// </summary>
|
||||
/// <param name="dataList">包含数据位置和数据对象的枚举集合</param>
|
||||
/// <returns>异步操作任务</returns>
|
||||
public async Task SaveAllAsync(
|
||||
IEnumerable<(IDataLocation location, IData data)> dataList)
|
||||
{
|
||||
@ -120,6 +140,24 @@ public class UnifiedSettingsRepository(
|
||||
this.SendEvent(new DataBatchSavedEvent(valueTuples.ToList()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载所有存储的数据项
|
||||
/// </summary>
|
||||
/// <returns>包含所有数据项的字典,键为数据位置键,值为数据对象</returns>
|
||||
public async Task<IDictionary<string, IData>> LoadAllAsync()
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
return File.Sections.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => Serializer.Deserialize<IData>(kv.Value)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_storage ??= this.GetUtility<IStorage>()!;
|
||||
_serializer ??= this.GetUtility<IRuntimeTypeSerializer>()!;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Internals
|
||||
@ -154,7 +192,6 @@ public class UnifiedSettingsRepository(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将缓存中的所有数据保存到统一文件
|
||||
/// </summary>
|
||||
@ -14,38 +14,30 @@ namespace GFramework.Game.setting;
|
||||
/// - 编排 Settings Applicator 的 Apply 行为
|
||||
/// </summary>
|
||||
public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
where TRepository : class, IDataRepository
|
||||
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 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 readonly ConcurrentDictionary<(Type type, int from), ISettingsMigration> _migrations = new();
|
||||
private IDataLocationProvider? _locationProvider;
|
||||
|
||||
private IDataRepository Repository =>
|
||||
_repository ?? throw new InvalidOperationException("IDataRepository not initialized.");
|
||||
private ISettingsDataRepository? _repository;
|
||||
|
||||
private ISettingsDataRepository DataRepository =>
|
||||
_repository ?? throw new InvalidOperationException("ISettingsDataRepository 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
|
||||
// =========================
|
||||
|
||||
@ -57,7 +49,7 @@ public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
return (T)_data.GetOrAdd(typeof(T), _ => new T());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IEnumerable<ISettingsData> AllData()
|
||||
{
|
||||
return _data.Values;
|
||||
@ -70,9 +62,10 @@ public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
/// <summary>
|
||||
/// 注册设置应用器
|
||||
/// </summary>
|
||||
public ISettingsModel RegisterApplicator(IResetApplyAbleSettings applicator)
|
||||
public ISettingsModel RegisterApplicator<T>(IResetApplyAbleSettings applicator)
|
||||
where T : class, ISettingsData, new()
|
||||
{
|
||||
_applicators.Add(applicator);
|
||||
_applicators[typeof(T)] = applicator;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -81,7 +74,7 @@ public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
/// </summary>
|
||||
public IEnumerable<IResetApplyAbleSettings> AllApplicators()
|
||||
{
|
||||
return _applicators;
|
||||
return _applicators.Values;
|
||||
}
|
||||
|
||||
// =========================
|
||||
@ -94,6 +87,135 @@ public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置指定类型的可重置对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要重置的对象类型,必须是class类型,实现IResettable接口,并具有无参构造函数</typeparam>
|
||||
public void Reset<T>() where T : class, ISettingsData, new()
|
||||
{
|
||||
var data = GetData<T>();
|
||||
data.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置所有设置
|
||||
/// </summary>
|
||||
public void ResetAll()
|
||||
{
|
||||
foreach (var data in _data.Values)
|
||||
data.Reset();
|
||||
|
||||
foreach (var applicator in _applicators)
|
||||
applicator.Value.Reset();
|
||||
}
|
||||
|
||||
/// <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>()!;
|
||||
}
|
||||
|
||||
private ISettingsData MigrateIfNeeded(ISettingsData data)
|
||||
{
|
||||
if (data is not IVersionedData versioned)
|
||||
@ -119,88 +241,4 @@ public class SettingsModel<TRepository> : AbstractModel, ISettingsModel
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
using GFramework.Core.extensions;
|
||||
using GFramework.Core.system;
|
||||
using GFramework.Game.Abstractions.data;
|
||||
using GFramework.Game.Abstractions.setting;
|
||||
using GFramework.Game.setting.events;
|
||||
|
||||
@ -9,12 +8,9 @@ namespace GFramework.Game.setting;
|
||||
/// <summary>
|
||||
/// 设置系统,负责管理和应用各种设置配置
|
||||
/// </summary>
|
||||
public class SettingsSystem<TRepository>(IDataRepository? repository)
|
||||
: AbstractSystem, ISettingsSystem where TRepository : class, IDataRepository
|
||||
public class SettingsSystem : AbstractSystem, ISettingsSystem
|
||||
{
|
||||
private ISettingsModel _model = null!;
|
||||
private IDataRepository? _repository = repository;
|
||||
private IDataRepository Repository => _repository ?? throw new InvalidOperationException("Repository is not set");
|
||||
|
||||
/// <summary>
|
||||
/// 应用所有设置配置
|
||||
@ -29,9 +25,9 @@ public class SettingsSystem<TRepository>(IDataRepository? repository)
|
||||
/// <summary>
|
||||
/// 应用指定类型的设置配置
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置配置类型,必须是类且实现ISettingsSection接口</typeparam>
|
||||
/// <typeparam name="T">设置配置类型,必须是类且实现IResetApplyAbleSettings接口</typeparam>
|
||||
/// <returns>完成的任务</returns>
|
||||
public Task Apply<T>() where T : class, IApplyAbleSettings
|
||||
public Task Apply<T>() where T : class, IResetApplyAbleSettings
|
||||
{
|
||||
var applicator = _model.GetApplicator<T>();
|
||||
return applicator != null
|
||||
@ -45,7 +41,7 @@ public class SettingsSystem<TRepository>(IDataRepository? repository)
|
||||
/// <returns>完成的任务</returns>
|
||||
public async Task SaveAll()
|
||||
{
|
||||
await Repository.SaveAllAsync(_model.AllData());
|
||||
await _model.SaveAllAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -63,7 +59,7 @@ public class SettingsSystem<TRepository>(IDataRepository? repository)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">设置类型,必须实现IPersistentApplyAbleSettings接口且具有无参构造函数</typeparam>
|
||||
/// <returns>异步任务</returns>
|
||||
public async Task Reset<T>() where T : class, IResetApplyAbleSettings, new()
|
||||
public async Task Reset<T>() where T : class, ISettingsData, IResetApplyAbleSettings, new()
|
||||
{
|
||||
_model.Reset<T>();
|
||||
await Apply<T>();
|
||||
@ -76,7 +72,6 @@ public class SettingsSystem<TRepository>(IDataRepository? repository)
|
||||
protected override void OnInit()
|
||||
{
|
||||
_model = this.GetModel<ISettingsModel>()!;
|
||||
_repository ??= this.GetUtility<TRepository>()!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user