Files
aibis-dream/Assets/Scripts/SaveSystem/SlotFileSystem.cs
T

139 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using AibisDream.Utility;
using Newtonsoft.Json;
using UnityEngine;
namespace AibisDream.SaveSystem
{
/// <summary>
/// 存档槽位目录与文件路径工具。
/// </summary>
public static class SlotDirectory
{
public static string GetSlotPath(int slotIndex)
{
return Path.Combine(ConstRef.SaveFilePath, $"{ConstRef.SaveSlotDirPrefix}{slotIndex}");
}
public static string GetSnapshotPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveSnapshotFileName}.json");
}
public static string GetMetaPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveMetaFileName}.json");
}
public static string GetThumbnailPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveThumbnailFileName}.png");
}
public static string GetLatestSlotIndexPath()
{
return Path.Combine(ConstRef.SaveFilePath, $"{ConstRef.LatestSlotFileName}.json");
}
public static bool Exists(int slotIndex)
{
return File.Exists(GetMetaPath(slotIndex));
}
public static IEnumerable<int> EnumerateExistingSlots()
{
if (!Directory.Exists(ConstRef.SaveFilePath))
{
yield break;
}
for (var i = SlotIndex.Auto; i <= SlotIndex.ManualEnd; i++)
{
if (Exists(i))
{
yield return i;
}
}
}
public static void EnsureSlotDirectory(int slotIndex)
{
Directory.CreateDirectory(GetSlotPath(slotIndex));
}
}
/// <summary>
/// 槽位文件原子写工具。
/// <para>先写入临时文件,再重命名为最终文件,避免写入过程中崩溃导致存档损坏。</para>
/// </summary>
public static class SlotAtomicWriter
{
public static void WriteJson<T>(T obj, string finalPath)
{
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
WriteText(json, finalPath);
}
public static void WriteText(string text, string finalPath)
{
var tempPath = finalPath + ".tmp";
var dir = Path.GetDirectoryName(finalPath);
if (!string.IsNullOrEmpty(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllText(tempPath, text);
CommitTempFile(tempPath, finalPath);
}
public static void WriteBytes(byte[] data, string finalPath)
{
if (data == null)
{
return;
}
var tempPath = finalPath + ".tmp";
var dir = Path.GetDirectoryName(finalPath);
if (!string.IsNullOrEmpty(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllBytes(tempPath, data);
CommitTempFile(tempPath, finalPath);
}
private static void CommitTempFile(string tempPath, string finalPath)
{
const int maxAttempts = 5;
for (var attempt = 0; attempt < maxAttempts; attempt++)
{
try
{
if (File.Exists(finalPath))
{
// Unity 使用的 .NET Standard 2.0 无 File.Move(..., overwrite)Replace 为原子覆盖。
File.Replace(tempPath, finalPath, null);
}
else
{
File.Move(tempPath, finalPath);
}
return;
}
catch (IOException) when (attempt < maxAttempts - 1)
{
Thread.Sleep(20 * (attempt + 1));
}
}
}
}
}