// 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. using System.Globalization; using GFramework.Core.Abstractions.Storage; using GFramework.Core.Utility; using GFramework.Game.Abstractions.Data; using GFramework.Game.Storage; namespace GFramework.Game.Data; /// /// 基于槽位的存档仓库实现 /// /// 存档数据类型 public class SaveRepository : AbstractContextUtility, ISaveRepository where TSaveData : class, IData, new() { private readonly SaveConfiguration _config; private readonly IStorage _rootStorage; /// /// 初始化存档仓库 /// /// 存储实例 /// 存档配置 public SaveRepository(IStorage storage, SaveConfiguration config) { ArgumentNullException.ThrowIfNull(storage); ArgumentNullException.ThrowIfNull(config); _config = config; _rootStorage = new ScopedStorage(storage, config.SaveRoot); } /// /// 检查指定槽位是否存在存档 /// /// 存档槽位编号 /// 如果存档存在返回true,否则返回false public async Task ExistsAsync(int slot) { var storage = GetSlotStorage(slot); return await storage.ExistsAsync(_config.SaveFileName); } /// /// 加载指定槽位的存档 /// /// 存档槽位编号 /// 存档数据对象,如果不存在则返回新实例 public async Task LoadAsync(int slot) { var storage = GetSlotStorage(slot); if (await storage.ExistsAsync(_config.SaveFileName)) return await storage.ReadAsync(_config.SaveFileName); return new TSaveData(); } /// /// 保存存档到指定槽位 /// /// 存档槽位编号 /// 要保存的存档数据 public async Task SaveAsync(int slot, TSaveData data) { var slotPath = $"{_config.SaveSlotPrefix}{slot}"; // 确保槽位目录存在 if (!await _rootStorage.DirectoryExistsAsync(slotPath)) await _rootStorage.CreateDirectoryAsync(slotPath); var storage = GetSlotStorage(slot); await storage.WriteAsync(_config.SaveFileName, data); } /// /// 删除指定槽位的存档 /// /// 存档槽位编号 public async Task DeleteAsync(int slot) { var storage = GetSlotStorage(slot); await storage.DeleteAsync(_config.SaveFileName); } /// /// 列出所有有效的存档槽位 /// /// 包含所有有效存档槽位编号的只读列表,按升序排列 public async Task> ListSlotsAsync() { // 列举所有槽位目录 var directories = await _rootStorage.ListDirectoriesAsync(); var slots = new List(); foreach (var dirName in directories) { // 检查目录名是否符合槽位前缀 if (!dirName.StartsWith(_config.SaveSlotPrefix, StringComparison.Ordinal)) continue; // 提取槽位编号 var slotStr = dirName[_config.SaveSlotPrefix.Length..]; if (!int.TryParse(slotStr, CultureInfo.InvariantCulture, out var slot)) continue; // 直接检查存档文件是否存在,避免重复创建 ScopedStorage var saveFilePath = $"{dirName}/{_config.SaveFileName}"; if (await _rootStorage.ExistsAsync(saveFilePath)) slots.Add(slot); } return slots.OrderBy(x => x).ToList(); } /// /// 获取指定槽位的存储对象 /// /// 存档槽位编号 /// 对应槽位的存储对象 private IStorage GetSlotStorage(int slot) { return new ScopedStorage(_rootStorage, $"{_config.SaveSlotPrefix}{slot}"); } /// /// 初始化逻辑 /// protected override void OnInit() { } }