// 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()
{
}
}