From 11c94d462f9bfb4a0b4adebfbcfead9e8394c7a6 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:22:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(setting):=20=E6=B7=BB=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=95=B0=E6=8D=AE=E7=89=88=E6=9C=AC=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 IVersioned 接口用于定义版本信息 - 实现设置数据自动迁移机制,支持版本升级转换 - 添加 SettingsMigration 接口用于处理不同版本间的数据转换 - 在 SettingsModel 中集成迁移功能,加载时自动执行版本迁移 - 优化设置数据管理,新增 AllData 方法获取所有设置数据 - 重构代码结构,添加清晰的方法分组注释块 --- .../versioning/IVersioned.cs | 25 ++++ .../setting/ISettingsMigration.cs | 42 ++++++ GFramework.Game/setting/SettingsModel.cs | 138 ++++++++++++------ 3 files changed, 160 insertions(+), 45 deletions(-) create mode 100644 GFramework.Core.Abstractions/versioning/IVersioned.cs create mode 100644 GFramework.Game.Abstractions/setting/ISettingsMigration.cs diff --git a/GFramework.Core.Abstractions/versioning/IVersioned.cs b/GFramework.Core.Abstractions/versioning/IVersioned.cs new file mode 100644 index 0000000..ea39d70 --- /dev/null +++ b/GFramework.Core.Abstractions/versioning/IVersioned.cs @@ -0,0 +1,25 @@ +// 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.Core.Abstractions.versioning; + +/// +/// 定义具有版本信息的接口 +/// +public interface IVersioned +{ + /// + /// 获取对象的版本号 + /// + int Version { get; } +} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/setting/ISettingsMigration.cs b/GFramework.Game.Abstractions/setting/ISettingsMigration.cs new file mode 100644 index 0000000..ee8388c --- /dev/null +++ b/GFramework.Game.Abstractions/setting/ISettingsMigration.cs @@ -0,0 +1,42 @@ +// 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.setting; + +/// +/// 定义设置数据迁移接口,用于处理不同版本设置数据之间的转换 +/// +public interface ISettingsMigration +{ + /// + /// 获取要迁移的设置类型 + /// + Type SettingsType { get; } + + /// + /// 获取源版本号(迁移前的版本) + /// + int FromVersion { get; } + + /// + /// 获取目标版本号(迁移后的版本) + /// + int ToVersion { get; } + + /// + /// 执行设置数据迁移操作 + /// + /// 需要迁移的旧版设置数据 + /// 迁移后的新版设置数据 + ISettingsSection Migrate(ISettingsSection oldData); +} \ No newline at end of file diff --git a/GFramework.Game/setting/SettingsModel.cs b/GFramework.Game/setting/SettingsModel.cs index 6608f89..4f9f7f3 100644 --- a/GFramework.Game/setting/SettingsModel.cs +++ b/GFramework.Game/setting/SettingsModel.cs @@ -1,4 +1,5 @@ -using GFramework.Core.extensions; +using GFramework.Core.Abstractions.versioning; +using GFramework.Core.extensions; using GFramework.Core.model; using GFramework.Game.Abstractions.setting; @@ -11,87 +12,91 @@ public class SettingsModel : AbstractModel, ISettingsModel { private readonly Dictionary _applicators = new(); private readonly Dictionary _dataSettings = new(); + private readonly Dictionary<(Type type, int from), ISettingsMigration> _migrations = new(); + private ISettingsPersistence? _persistence; + // ----------------------------- + // Data + // ----------------------------- + /// - /// 获取或创建数据设置 + /// 获取指定类型的设置数据实例,如果不存在则创建新的实例 /// - /// 设置数据类型,必须实现ISettingsData接口并具有无参构造函数 + /// 设置数据类型,必须实现ISettingsData接口并提供无参构造函数 /// 指定类型的设置数据实例 public T GetData() where T : class, ISettingsData, new() { var type = typeof(T); + if (_dataSettings.TryGetValue(type, out var data)) + return (T)data; - // 尝试从现有字典中获取已存在的设置数据 - if (_dataSettings.TryGetValue(type, out var existing)) - return (T)existing; - - // 创建新的设置数据实例并存储到字典中 var created = new T(); _dataSettings[type] = created; return created; } /// - /// 获取所有已注册的可应用设置 + /// 获取所有设置数据的枚举集合 /// - /// 所有可应用设置的枚举集合 - public IEnumerable AllApplicators() - { - return _applicators.Values; - } + /// 所有设置数据的枚举集合 + public IEnumerable AllData() + => _dataSettings.Values; + + // ----------------------------- + // Applicator + // ----------------------------- /// - /// 注册可应用设置(必须手动注册) + /// 获取所有设置应用器的枚举集合 /// - /// 可应用设置的类型,必须继承自class和IApplyAbleSettings - /// 要注册的可应用设置实例 - /// 返回当前设置模型实例,支持链式调用 - public ISettingsModel RegisterApplicator(T applicator) where T : class, IApplyAbleSettings + /// 所有设置应用器的枚举集合 + public IEnumerable AllApplicators() + => _applicators.Values; + + /// + /// 注册设置应用器到模型中 + /// + /// 设置应用器类型,必须实现IApplyAbleSettings接口 + /// 要注册的设置应用器实例 + /// 当前设置模型实例,支持链式调用 + public ISettingsModel RegisterApplicator(T applicator) + where T : class, IApplyAbleSettings { - var type = typeof(T); - _applicators[type] = applicator; + _applicators[typeof(T)] = applicator; return this; } /// - /// 获取已注册的可应用设置 + /// 获取指定类型的设置应用器实例 /// - /// 可应用设置类型,必须实现IApplyAbleSettings接口 - /// 找到的可应用设置实例,如果未找到则返回null + /// 设置应用器类型,必须实现IApplyAbleSettings接口 + /// 指定类型的设置应用器实例,如果不存在则返回null public T? GetApplicator() where T : class, IApplyAbleSettings { - var type = typeof(T); - return _applicators.TryGetValue(type, out var applicator) - ? (T)applicator + return _applicators.TryGetValue(typeof(T), out var app) + ? (T)app : null; } - /// - /// 获取所有设置数据 - /// - /// 所有设置数据的枚举集合 - public IEnumerable AllData() - { - return _dataSettings.Values; - } + // ----------------------------- + // Section lookup + // ----------------------------- /// - /// 尝试获取指定类型的设置节 + /// 尝试获取指定类型的设置节 /// /// 要查找的设置类型 /// 输出参数,找到的设置节实例 - /// 如果找到设置节则返回true,否则返回false + /// 如果找到对应类型的设置节则返回true,否则返回false public bool TryGet(Type type, out ISettingsSection section) { - // 首先在数据设置字典中查找 if (_dataSettings.TryGetValue(type, out var data)) { section = data; return true; } - // 然后在应用器字典中查找 if (_applicators.TryGetValue(type, out var applicator)) { section = applicator; @@ -102,9 +107,49 @@ public class SettingsModel : AbstractModel, ISettingsModel return false; } + // ----------------------------- + // Migration + // ----------------------------- /// - /// 初始化并加载指定类型的设置数据 + /// 注册设置迁移器到模型中 + /// + /// 要注册的设置迁移器实例 + /// 当前设置模型实例,支持链式调用 + public ISettingsModel RegisterMigration(ISettingsMigration migration) + { + _migrations[(migration.SettingsType, migration.FromVersion)] = migration; + return this; + } + + /// + /// 如果需要的话,对设置节进行版本迁移 + /// + /// 待检查和迁移的设置节 + /// 迁移后的设置节 + private ISettingsSection MigrateIfNeeded(ISettingsSection section) + { + if (section is not IVersioned versioned) + return section; + + var type = section.GetType(); + var current = section; + + while (_migrations.TryGetValue((type, versioned.Version), out var migration)) + { + current = migration.Migrate(current); + versioned = (IVersioned)current; + } + + return current; + } + + // ----------------------------- + // Load / Init + // ----------------------------- + + /// + /// 异步初始化设置模型,加载指定类型的设置数据 /// /// 要初始化的设置类型数组 public async Task InitializeAsync(params Type[] settingTypes) @@ -116,7 +161,7 @@ public class SettingsModel : AbstractModel, ISettingsModel type.GetConstructor(Type.EmptyTypes) == null) continue; - // 使用反射调用泛型方法 LoadAsync + // Load() var method = typeof(ISettingsPersistence) .GetMethod(nameof(ISettingsPersistence.LoadAsync))! .MakeGenericMethod(type); @@ -124,14 +169,17 @@ public class SettingsModel : AbstractModel, ISettingsModel var task = (Task)method.Invoke(_persistence, null)!; await task; - var loaded = (ISettingsData)((dynamic)task).Result; - _dataSettings[type] = loaded; + var loaded = (ISettingsSection)((dynamic)task).Result; + + // ★ 关键:迁移 + var migrated = MigrateIfNeeded(loaded); + + _dataSettings[type] = (ISettingsData)migrated; } } - /// - /// 初始化方法,用于执行模型的初始化逻辑 + /// 初始化方法,用于获取设置持久化服务 /// protected override void OnInit() {