mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
feat: also use reliable storage for plugin configs
This commit is contained in:
parent
125034155b
commit
1d8b579b04
5 changed files with 94 additions and 62 deletions
|
|
@ -436,6 +436,10 @@ internal sealed class DalamudConfiguration : IServiceType, IDisposable
|
||||||
{
|
{
|
||||||
deserialized =
|
deserialized =
|
||||||
JsonConvert.DeserializeObject<DalamudConfiguration>(text, SerializerSettings);
|
JsonConvert.DeserializeObject<DalamudConfiguration>(text, SerializerSettings);
|
||||||
|
|
||||||
|
// If this reads as null, the file was empty, that's no good
|
||||||
|
if (deserialized == null)
|
||||||
|
throw new Exception("Read config was null.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using Dalamud.Utility;
|
using Dalamud.Storage;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Dalamud.Configuration;
|
namespace Dalamud.Configuration;
|
||||||
|
|
@ -33,22 +33,36 @@ public sealed class PluginConfigurations
|
||||||
/// <param name="pluginName">Plugin name.</param>
|
/// <param name="pluginName">Plugin name.</param>
|
||||||
public void Save(IPluginConfiguration config, string pluginName)
|
public void Save(IPluginConfiguration config, string pluginName)
|
||||||
{
|
{
|
||||||
Util.WriteAllTextSafe(this.GetConfigFile(pluginName).FullName, SerializeConfig(config));
|
Service<ReliableFileStorage>.Get()
|
||||||
|
.WriteAllText(this.GetConfigFile(pluginName).FullName, SerializeConfig(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load plugin configuration.
|
/// Load plugin configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginName">Plugin name.</param>
|
/// <param name="pluginName">Plugin name.</param>
|
||||||
|
/// <param name="workingPluginId">WorkingPluginID of the plugin.</param>
|
||||||
/// <returns>Plugin configuration.</returns>
|
/// <returns>Plugin configuration.</returns>
|
||||||
public IPluginConfiguration? Load(string pluginName)
|
public IPluginConfiguration? Load(string pluginName, Guid workingPluginId)
|
||||||
{
|
{
|
||||||
var path = this.GetConfigFile(pluginName);
|
var path = this.GetConfigFile(pluginName);
|
||||||
|
|
||||||
if (!path.Exists)
|
IPluginConfiguration? config = null;
|
||||||
return null;
|
try
|
||||||
|
{
|
||||||
|
Service<ReliableFileStorage>.Get().ReadAllText(path.FullName, text =>
|
||||||
|
{
|
||||||
|
config = DeserializeConfig(text);
|
||||||
|
if (config == null)
|
||||||
|
throw new Exception("Read config was null.");
|
||||||
|
}, workingPluginId);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
return DeserializeConfig(File.ReadAllText(path.FullName));
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ public sealed class DalamudPluginInterface : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// this shouldn't be a thing, I think, but just in case
|
// this shouldn't be a thing, I think, but just in case
|
||||||
return this.configs.Load(this.plugin.InternalName);
|
return this.configs.Load(this.plugin.InternalName, this.plugin.Manifest.WorkingPluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
using Dalamud.Utility;
|
||||||
using PInvoke;
|
using PInvoke;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
|
|
@ -32,7 +33,6 @@ public class ReliableFileStorage : IServiceType, IDisposable
|
||||||
/// Initializes a new instance of the <see cref="ReliableFileStorage"/> class.
|
/// Initializes a new instance of the <see cref="ReliableFileStorage"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="vfsDbPath">Path to the VFS.</param>
|
/// <param name="vfsDbPath">Path to the VFS.</param>
|
||||||
[ServiceManager.ServiceConstructor]
|
|
||||||
public ReliableFileStorage(string vfsDbPath)
|
public ReliableFileStorage(string vfsDbPath)
|
||||||
{
|
{
|
||||||
var databasePath = Path.Combine(vfsDbPath, "dalamudVfs.db");
|
var databasePath = Path.Combine(vfsDbPath, "dalamudVfs.db");
|
||||||
|
|
@ -113,7 +113,7 @@ public class ReliableFileStorage : IServiceType, IDisposable
|
||||||
this.db.Update(file);
|
this.db.Update(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteFileReliably(path, bytes);
|
Util.WriteAllBytesSafe(path, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -253,39 +253,6 @@ public class ReliableFileStorage : IServiceType, IDisposable
|
||||||
this.db.Dispose();
|
this.db.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteFileReliably(string path, byte[] bytes)
|
|
||||||
{
|
|
||||||
ArgumentException.ThrowIfNullOrEmpty(path);
|
|
||||||
|
|
||||||
// Open the temp file
|
|
||||||
var tempPath = path + ".tmp";
|
|
||||||
|
|
||||||
using var tempFile = Kernel32
|
|
||||||
.CreateFile(tempPath.AsSpan(),
|
|
||||||
new Kernel32.ACCESS_MASK(Kernel32.FileAccess.FILE_GENERIC_READ | Kernel32.FileAccess.FILE_GENERIC_WRITE),
|
|
||||||
Kernel32.FileShare.None,
|
|
||||||
null,
|
|
||||||
Kernel32.CreationDisposition.CREATE_ALWAYS,
|
|
||||||
Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL,
|
|
||||||
Kernel32.SafeObjectHandle.Null);
|
|
||||||
|
|
||||||
if (tempFile.IsInvalid)
|
|
||||||
throw new Win32Exception();
|
|
||||||
|
|
||||||
// Write the data
|
|
||||||
var bytesWritten = Kernel32.WriteFile(tempFile, new ArraySegment<byte>(bytes));
|
|
||||||
if (bytesWritten != bytes.Length)
|
|
||||||
throw new Exception($"Could not write all bytes to temp file ({bytesWritten} of {bytes.Length})");
|
|
||||||
|
|
||||||
if (!Kernel32.FlushFileBuffers(tempFile))
|
|
||||||
throw new Win32Exception();
|
|
||||||
|
|
||||||
tempFile.Close();
|
|
||||||
|
|
||||||
if (!MoveFileEx(tempPath, path, MoveFileFlags.MovefileReplaceExisting | MoveFileFlags.MovefileWriteThrough))
|
|
||||||
throw new Win32Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replace possible non-portable parts of a path with portable versions.
|
/// Replace possible non-portable parts of a path with portable versions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -300,20 +267,6 @@ public class ReliableFileStorage : IServiceType, IDisposable
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
#pragma warning disable SA1201
|
|
||||||
private enum MoveFileFlags
|
|
||||||
#pragma warning restore SA1201
|
|
||||||
{
|
|
||||||
MovefileReplaceExisting = 0x00000001,
|
|
||||||
MovefileWriteThrough = 0x00000008,
|
|
||||||
}
|
|
||||||
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
|
||||||
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName,
|
|
||||||
MoveFileFlags dwFlags);
|
|
||||||
|
|
||||||
private class DbFile
|
private class DbFile
|
||||||
{
|
{
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using PInvoke;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Utility;
|
namespace Dalamud.Utility;
|
||||||
|
|
@ -618,12 +619,58 @@ public static class Util
|
||||||
/// <param name="text">The text to write.</param>
|
/// <param name="text">The text to write.</param>
|
||||||
public static void WriteAllTextSafe(string path, string text)
|
public static void WriteAllTextSafe(string path, string text)
|
||||||
{
|
{
|
||||||
var tmpPath = path + ".tmp";
|
WriteAllTextSafe(path, text, Encoding.UTF8);
|
||||||
if (File.Exists(tmpPath))
|
}
|
||||||
File.Delete(tmpPath);
|
|
||||||
|
|
||||||
File.WriteAllText(tmpPath, text);
|
/// <summary>
|
||||||
File.Move(tmpPath, path, true);
|
/// Overwrite text in a file by first writing it to a temporary file, and then
|
||||||
|
/// moving that file to the path specified.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the file to write to.</param>
|
||||||
|
/// <param name="text">The text to write.</param>
|
||||||
|
/// <param name="encoding">Encoding to use.</param>
|
||||||
|
public static void WriteAllTextSafe(string path, string text, Encoding encoding)
|
||||||
|
{
|
||||||
|
WriteAllBytesSafe(path, encoding.GetBytes(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overwrite data in a file by first writing it to a temporary file, and then
|
||||||
|
/// moving that file to the path specified.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the file to write to.</param>
|
||||||
|
/// <param name="bytes">The data to write.</param>
|
||||||
|
public static void WriteAllBytesSafe(string path, byte[] bytes)
|
||||||
|
{
|
||||||
|
ArgumentException.ThrowIfNullOrEmpty(path);
|
||||||
|
|
||||||
|
// Open the temp file
|
||||||
|
var tempPath = path + ".tmp";
|
||||||
|
|
||||||
|
using var tempFile = Kernel32
|
||||||
|
.CreateFile(tempPath.AsSpan(),
|
||||||
|
new Kernel32.ACCESS_MASK(Kernel32.FileAccess.FILE_GENERIC_READ | Kernel32.FileAccess.FILE_GENERIC_WRITE),
|
||||||
|
Kernel32.FileShare.None,
|
||||||
|
null,
|
||||||
|
Kernel32.CreationDisposition.CREATE_ALWAYS,
|
||||||
|
Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL,
|
||||||
|
Kernel32.SafeObjectHandle.Null);
|
||||||
|
|
||||||
|
if (tempFile.IsInvalid)
|
||||||
|
throw new Win32Exception();
|
||||||
|
|
||||||
|
// Write the data
|
||||||
|
var bytesWritten = Kernel32.WriteFile(tempFile, new ArraySegment<byte>(bytes));
|
||||||
|
if (bytesWritten != bytes.Length)
|
||||||
|
throw new Exception($"Could not write all bytes to temp file ({bytesWritten} of {bytes.Length})");
|
||||||
|
|
||||||
|
if (!Kernel32.FlushFileBuffers(tempFile))
|
||||||
|
throw new Win32Exception();
|
||||||
|
|
||||||
|
tempFile.Close();
|
||||||
|
|
||||||
|
if (!MoveFileEx(tempPath, path, MoveFileFlags.MovefileReplaceExisting | MoveFileFlags.MovefileWriteThrough))
|
||||||
|
throw new Win32Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -762,4 +809,18 @@ public static class Util
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
#pragma warning disable SA1201
|
||||||
|
private enum MoveFileFlags
|
||||||
|
#pragma warning restore SA1201
|
||||||
|
{
|
||||||
|
MovefileReplaceExisting = 0x00000001,
|
||||||
|
MovefileWriteThrough = 0x00000008,
|
||||||
|
}
|
||||||
|
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
||||||
|
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName,
|
||||||
|
MoveFileFlags dwFlags);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue