diff --git a/GFramework.Core.Abstractions/serializer/IRuntimeTypeSerializer.cs b/GFramework.Core.Abstractions/serializer/IRuntimeTypeSerializer.cs
new file mode 100644
index 0000000..9545de7
--- /dev/null
+++ b/GFramework.Core.Abstractions/serializer/IRuntimeTypeSerializer.cs
@@ -0,0 +1,37 @@
+// 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.serializer;
+
+///
+/// 运行时类型序列化器接口,继承自ISerializer接口
+/// 提供基于运行时类型的对象序列化和反序列化功能
+///
+public interface IRuntimeTypeSerializer : ISerializer
+{
+ ///
+ /// 将指定对象序列化为字符串
+ ///
+ /// 要序列化的对象
+ /// 对象的运行时类型
+ /// 序列化后的字符串表示
+ string Serialize(object obj, Type type);
+
+ ///
+ /// 将字符串数据反序列化为指定类型的对象
+ ///
+ /// 要反序列化的字符串数据
+ /// 目标对象的运行时类型
+ /// 反序列化后的对象实例
+ object Deserialize(string data, Type type);
+}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/serializer/ISerializer.cs b/GFramework.Core.Abstractions/serializer/ISerializer.cs
similarity index 94%
rename from GFramework.Game.Abstractions/serializer/ISerializer.cs
rename to GFramework.Core.Abstractions/serializer/ISerializer.cs
index f562b2d..dade5cb 100644
--- a/GFramework.Game.Abstractions/serializer/ISerializer.cs
+++ b/GFramework.Core.Abstractions/serializer/ISerializer.cs
@@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.utility;
-namespace GFramework.Game.Abstractions.serializer;
+namespace GFramework.Core.Abstractions.serializer;
///
/// 定义序列化器接口,提供对象序列化和反序列化的通用方法
diff --git a/GFramework.Core.Abstractions/storage/IStorage.cs b/GFramework.Core.Abstractions/storage/IStorage.cs
index 8216ca8..1d833e6 100644
--- a/GFramework.Core.Abstractions/storage/IStorage.cs
+++ b/GFramework.Core.Abstractions/storage/IStorage.cs
@@ -68,4 +68,11 @@ public interface IStorage : IUtility
///
/// 要删除的键
void Delete(string key);
+
+ ///
+ /// 异步删除指定键的存储项
+ ///
+ /// 要删除的键
+ /// 表示异步操作的Task
+ Task DeleteAsync(string key);
}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/setting/IPersistentApplyAbleSettings.cs b/GFramework.Game.Abstractions/setting/IPersistentApplyAbleSettings.cs
new file mode 100644
index 0000000..05479e5
--- /dev/null
+++ b/GFramework.Game.Abstractions/setting/IPersistentApplyAbleSettings.cs
@@ -0,0 +1,20 @@
+// 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 IPersistentApplyAbleSettings : ISettingsData, IApplyAbleSettings;
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs b/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs
index 2c19640..56673db 100644
--- a/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs
+++ b/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs
@@ -32,9 +32,4 @@ public interface ISettingsPersistence : IContextUtility
/// 保存所有设置数据
///
Task SaveAllAsync(IEnumerable allData);
-
- ///
- /// 加载所有已知类型的设置数据
- ///
- Task> LoadAllAsync(IEnumerable knownTypes);
}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/setting/SettingsData.cs b/GFramework.Game.Abstractions/setting/SettingsData.cs
deleted file mode 100644
index 996e2e5..0000000
--- a/GFramework.Game.Abstractions/setting/SettingsData.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Reflection;
-
-namespace GFramework.Game.Abstractions.setting;
-
-///
-/// 设置数据抽象基类,提供默认的 Reset() 实现
-///
-public abstract class SettingsData : ISettingsData
-{
- ///
- /// 重置设置为默认值
- /// 使用反射将所有属性重置为它们的默认值
- ///
- public virtual void Reset()
- {
- var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (var prop in properties)
- {
- if (!prop.CanWrite || !prop.CanRead) continue;
-
- var defaultValue = GetDefaultValue(prop.PropertyType);
- prop.SetValue(this, defaultValue);
- }
- }
-
- ///
- /// 获取指定类型的默认值
- ///
- /// 要获取默认值的类型
- /// 类型的默认值
- private static object? GetDefaultValue(Type type)
- {
- return type.IsValueType ? Activator.CreateInstance(type) : null;
- }
-}
\ No newline at end of file
diff --git a/GFramework.Game/serializer/JsonSerializer.cs b/GFramework.Game/serializer/JsonSerializer.cs
index 30aaafb..925ce31 100644
--- a/GFramework.Game/serializer/JsonSerializer.cs
+++ b/GFramework.Game/serializer/JsonSerializer.cs
@@ -1,4 +1,4 @@
-using GFramework.Game.Abstractions.serializer;
+using GFramework.Core.Abstractions.serializer;
using Newtonsoft.Json;
namespace GFramework.Game.serializer;
diff --git a/GFramework.Game/setting/SettingsModel.cs b/GFramework.Game/setting/SettingsModel.cs
index 64add79..ce63df4 100644
--- a/GFramework.Game/setting/SettingsModel.cs
+++ b/GFramework.Game/setting/SettingsModel.cs
@@ -1,4 +1,5 @@
-using GFramework.Core.model;
+using GFramework.Core.extensions;
+using GFramework.Core.model;
using GFramework.Game.Abstractions.setting;
namespace GFramework.Game.setting;
@@ -10,6 +11,7 @@ public class SettingsModel : AbstractModel, ISettingsModel
{
private readonly Dictionary _applicators = new();
private readonly Dictionary _dataSettings = new();
+ private ISettingsPersistence? _persistence;
///
/// 获取或创建数据设置
@@ -40,6 +42,13 @@ public class SettingsModel : AbstractModel, ISettingsModel
{
var type = typeof(T);
_applicators[type] = applicator;
+
+ // 如果这个应用设置同时也是数据设置,也注册到数据字典中
+ if (applicator is ISettingsData data)
+ {
+ _dataSettings[type] = data;
+ }
+
return this;
}
@@ -88,9 +97,41 @@ public class SettingsModel : AbstractModel, ISettingsModel
/// 包含所有设置节的可枚举集合
public IEnumerable All()
{
- // 合并数据设置和应用器设置的所有值
- return _dataSettings.Values
- .Concat(_applicators.Values.Cast());
+ // 使用 HashSet 去重(避免同时实现两个接口的设置被重复返回)
+ var sections = new HashSet();
+
+ foreach (var applicator in _applicators.Values)
+ sections.Add(applicator);
+
+ foreach (var data in _dataSettings.Values)
+ sections.Add(data);
+
+ return sections;
+ }
+
+ ///
+ /// 初始化并加载指定类型的设置数据
+ ///
+ public async Task InitializeAsync(params Type[] settingTypes)
+ {
+ foreach (var type in settingTypes)
+ {
+ if (!typeof(ISettingsData).IsAssignableFrom(type) ||
+ !type.IsClass ||
+ type.GetConstructor(Type.EmptyTypes) == null)
+ continue;
+
+ // 使用反射调用泛型方法 LoadAsync
+ var method = typeof(ISettingsPersistence)
+ .GetMethod(nameof(ISettingsPersistence.LoadAsync))!
+ .MakeGenericMethod(type);
+
+ var task = (Task)method.Invoke(_persistence, null)!;
+ await task;
+
+ var loaded = (ISettingsData)((dynamic)task).Result;
+ _dataSettings[type] = loaded;
+ }
}
@@ -99,5 +140,6 @@ public class SettingsModel : AbstractModel, ISettingsModel
///
protected override void OnInit()
{
+ _persistence = this.GetUtility();
}
}
\ No newline at end of file
diff --git a/GFramework.Game/setting/SettingsPersistence.cs b/GFramework.Game/setting/SettingsPersistence.cs
index 7990082..63a3edf 100644
--- a/GFramework.Game/setting/SettingsPersistence.cs
+++ b/GFramework.Game/setting/SettingsPersistence.cs
@@ -64,7 +64,7 @@ public class SettingsPersistence : AbstractContextUtility, ISettingsPersistence
public async Task DeleteAsync() where T : class, ISettingsData
{
var key = GetKey();
- _storage.Delete(key);
+ await _storage.DeleteAsync(key);
this.SendEvent(new SettingsDeletedEvent(typeof(T)));
await Task.CompletedTask;
}
@@ -86,38 +86,6 @@ public class SettingsPersistence : AbstractContextUtility, ISettingsPersistence
this.SendEvent(new SettingsBatchSavedEvent(dataList));
}
- ///
- /// 异步加载所有已知类型的设置数据
- ///
- /// 已知设置数据类型的集合
- /// 类型与对应设置数据的字典映射
- public async Task> LoadAllAsync(IEnumerable knownTypes)
- {
- var result = new Dictionary();
- var allSettings = new List();
-
- foreach (var type in knownTypes)
- {
- var key = GetKey(type);
-
- if (!await _storage.ExistsAsync(key)) continue;
- // 使用反射调用泛型方法
- var method = typeof(IStorage)
- .GetMethod(nameof(IStorage.ReadAsync))!
- .MakeGenericMethod(type);
-
- var task = (Task)method.Invoke(_storage, [key])!;
- await task;
-
- var loaded = (ISettingsData)((dynamic)task).Result;
- result[type] = loaded;
- allSettings.Add(loaded);
- }
-
- this.SendEvent(new SettingsAllLoadedEvent(allSettings));
- return result;
- }
-
protected override void OnInit()
{
_storage = this.GetUtility()!;
diff --git a/GFramework.Game/setting/SettingsSystem.cs b/GFramework.Game/setting/SettingsSystem.cs
index fd2318c..a132a08 100644
--- a/GFramework.Game/setting/SettingsSystem.cs
+++ b/GFramework.Game/setting/SettingsSystem.cs
@@ -16,12 +16,10 @@ public class SettingsSystem : AbstractSystem, ISettingsSystem
/// 应用所有设置配置
///
/// 完成的任务
- public Task ApplyAll()
+ public async Task ApplyAll()
{
// 遍历所有设置配置并尝试应用
- foreach (var section in _model.All()) TryApply(section);
-
- return Task.CompletedTask;
+ foreach (var section in _model.All()) await TryApplyAsync(section);
}
///
@@ -39,13 +37,12 @@ public class SettingsSystem : AbstractSystem, ISettingsSystem
///
/// 设置配置类型
/// 完成的任务
- public Task Apply(Type settingsType)
+ public async Task Apply(Type settingsType)
{
- if (!_model.TryGet(settingsType, out var section))
- return Task.CompletedTask;
-
- TryApply(section);
- return Task.CompletedTask;
+ if (_model.TryGet(settingsType, out var section))
+ {
+ await TryApplyAsync(section);
+ }
}
///
@@ -53,14 +50,16 @@ public class SettingsSystem : AbstractSystem, ISettingsSystem
///
/// 设置配置类型集合
/// 完成的任务
- public Task Apply(IEnumerable settingsTypes)
+ public async Task Apply(IEnumerable settingsTypes)
{
// 去重后遍历设置类型,获取并应用对应的设置配置
foreach (var type in settingsTypes.Distinct())
+ {
if (_model.TryGet(type, out var section))
- TryApply(section);
-
- return Task.CompletedTask;
+ {
+ await TryApplyAsync(section);
+ }
+ }
}
///
@@ -75,20 +74,20 @@ public class SettingsSystem : AbstractSystem, ISettingsSystem
/// 尝试应用可应用的设置配置
///
/// 设置配置对象
- private void TryApply(ISettingsSection section)
+ private async Task TryApplyAsync(ISettingsSection section)
{
if (section is not IApplyAbleSettings applyAbleSettings) return;
+
this.SendEvent(new SettingsApplyingEvent(section));
try
{
- applyAbleSettings.Apply();
+ await applyAbleSettings.Apply();
this.SendEvent(new SettingsAppliedEvent(section, true));
}
catch (Exception ex)
{
this.SendEvent(new SettingsAppliedEvent(section, false, ex));
- throw;
}
}
}
\ No newline at end of file
diff --git a/GFramework.Game/storage/FileStorage.cs b/GFramework.Game/storage/FileStorage.cs
index e740497..3d019b1 100644
--- a/GFramework.Game/storage/FileStorage.cs
+++ b/GFramework.Game/storage/FileStorage.cs
@@ -1,7 +1,7 @@
using System.Collections.Concurrent;
using System.IO;
using System.Text;
-using GFramework.Game.Abstractions.serializer;
+using GFramework.Core.Abstractions.serializer;
using GFramework.Game.Abstractions.storage;
namespace GFramework.Game.storage;
@@ -33,26 +33,6 @@ public sealed class FileStorage : IFileStorage
Directory.CreateDirectory(_rootPath);
}
- #region Delete
-
- ///
- /// 删除指定键的存储项
- ///
- /// 存储键
- public void Delete(string key)
- {
- var path = ToPath(key);
- var keyLock = _keyLocks.GetOrAdd(path, _ => new object());
-
- lock (keyLock)
- {
- if (File.Exists(path))
- File.Delete(path);
- }
- }
-
- #endregion
-
///
/// 清理文件段字符串,将其中的无效文件名字符替换为下划线
///
@@ -105,6 +85,31 @@ public sealed class FileStorage : IFileStorage
#endregion
+ #region Delete
+
+ ///
+ /// 删除指定键的存储项
+ ///
+ /// 存储键
+ public void Delete(string key)
+ {
+ var path = ToPath(key);
+ var keyLock = _keyLocks.GetOrAdd(path, _ => new object());
+
+ lock (keyLock)
+ {
+ if (File.Exists(path))
+ File.Delete(path);
+ }
+ }
+
+ public Task DeleteAsync(string key)
+ {
+ return Task.Run(() => Delete(key));
+ }
+
+ #endregion
+
#region Exists
///
diff --git a/GFramework.Game/storage/ScopedStorage.cs b/GFramework.Game/storage/ScopedStorage.cs
index 9498ac2..0a82ca7 100644
--- a/GFramework.Game/storage/ScopedStorage.cs
+++ b/GFramework.Game/storage/ScopedStorage.cs
@@ -95,6 +95,16 @@ public sealed class ScopedStorage(IStorage inner, string prefix) : IScopedStorag
inner.Delete(Key(key));
}
+ ///
+ /// 异步删除指定键
+ ///
+ /// 要删除的键
+ /// 异步操作任务
+ public async Task DeleteAsync(string key)
+ {
+ await inner.DeleteAsync(Key(key));
+ }
+
///
/// 为给定的键添加前缀
///
diff --git a/GFramework.Godot/setting/GodotAudioSettings.cs b/GFramework.Godot/setting/GodotAudioSettings.cs
index b33aa4b..7c1bbef 100644
--- a/GFramework.Godot/setting/GodotAudioSettings.cs
+++ b/GFramework.Godot/setting/GodotAudioSettings.cs
@@ -6,10 +6,10 @@ namespace GFramework.Godot.setting;
///
/// Godot音频设置实现类,用于应用音频配置到Godot音频系统
///
-/// 音频设置对象,包含主音量、背景音乐音量和音效音量
+/// 音频设置对象,包含主音量、背景音乐音量和音效音量
/// 音频总线映射对象,定义了不同音频类型的总线名称
-public class GodotAudioSettings(AudioSettings audioSettings, AudioBusMapSettings audioBusMapSettings)
- : IApplyAbleSettings
+public class GodotAudioSettings(AudioSettings settings, AudioBusMapSettings audioBusMapSettings)
+ : IPersistentApplyAbleSettings
{
///
/// 应用音频设置到Godot音频系统
@@ -17,12 +17,17 @@ public class GodotAudioSettings(AudioSettings audioSettings, AudioBusMapSettings
/// 表示异步操作的任务
public Task Apply()
{
- SetBus(audioBusMapSettings.Master, audioSettings.MasterVolume);
- SetBus(audioBusMapSettings.Bgm, audioSettings.BgmVolume);
- SetBus(audioBusMapSettings.Sfx, audioSettings.SfxVolume);
+ SetBus(audioBusMapSettings.Master, settings.MasterVolume);
+ SetBus(audioBusMapSettings.Bgm, settings.BgmVolume);
+ SetBus(audioBusMapSettings.Sfx, settings.SfxVolume);
return Task.CompletedTask;
}
+ public void Reset()
+ {
+ audioBusMapSettings.Reset();
+ }
+
///
/// 设置指定音频总线的音量
///
diff --git a/GFramework.Godot/setting/GodotGraphicsSettings.cs b/GFramework.Godot/setting/GodotGraphicsSettings.cs
index 0e27c91..6c8c9fe 100644
--- a/GFramework.Godot/setting/GodotGraphicsSettings.cs
+++ b/GFramework.Godot/setting/GodotGraphicsSettings.cs
@@ -7,7 +7,7 @@ namespace GFramework.Godot.setting;
/// Godot图形设置应用器
///
/// 图形设置配置对象
-public class GodotGraphicsSettings(GraphicsSettings settings) : IApplyAbleSettings
+public class GodotGraphicsSettings(GraphicsSettings settings) : IPersistentApplyAbleSettings
{
///
/// 应用图形设置到Godot引擎
@@ -40,4 +40,9 @@ public class GodotGraphicsSettings(GraphicsSettings settings) : IApplyAbleSettin
await Task.CompletedTask;
}
+
+ public void Reset()
+ {
+ settings.Reset();
+ }
}
\ No newline at end of file
diff --git a/GFramework.Godot/storage/GodotFileStorage.cs b/GFramework.Godot/storage/GodotFileStorage.cs
index cff6d56..7ac1568 100644
--- a/GFramework.Godot/storage/GodotFileStorage.cs
+++ b/GFramework.Godot/storage/GodotFileStorage.cs
@@ -1,8 +1,8 @@
using System.Collections.Concurrent;
using System.IO;
using System.Text;
+using GFramework.Core.Abstractions.serializer;
using GFramework.Core.Abstractions.storage;
-using GFramework.Game.Abstractions.serializer;
using GFramework.Godot.extensions;
using Godot;
using FileAccess = Godot.FileAccess;
@@ -44,6 +44,7 @@ public sealed class GodotFileStorage : IStorage
lock (keyLock)
{
+ // 处理Godot文件系统路径的删除操作
if (path.IsGodotPath())
{
if (FileAccess.FileExists(path))
@@ -53,6 +54,7 @@ public sealed class GodotFileStorage : IStorage
throw new IOException($"Failed to delete Godot file: {path}, error: {err}");
}
}
+ // 处理标准文件系统路径的删除操作
else
{
if (File.Exists(path)) File.Delete(path);
@@ -63,6 +65,16 @@ public sealed class GodotFileStorage : IStorage
_keyLocks.TryRemove(path, out _);
}
+ ///
+ /// 异步删除指定键对应的文件
+ ///
+ /// 存储键
+ /// 异步任务
+ public async Task DeleteAsync(string key)
+ {
+ await Task.Run(() => Delete(key));
+ }
+
#endregion
#region Helpers