bloxstrap/Bloxstrap/JsonManager.cs
Matt ca36306254
Background updates (#4861)
* add background updating

* add RobloxState

* fix potential race condition with RobloxState

* update ForceRobloxReinstallation in menu

* disable AssertReadOnlyDirectory

* add storage space check

* add logging to IsEligibleForBackgroundUpdate

* add a setting to toggle background updates

* fix mutex names being mixed up

* update string

* update strings

* update strings
2025-03-13 16:05:41 +00:00

114 lines
3.9 KiB
C#

using System.Runtime.CompilerServices;
namespace Bloxstrap
{
public class JsonManager<T> where T : class, new()
{
public T OriginalProp { get; set; } = new();
public T Prop { get; set; } = new();
/// <summary>
/// The file hash when last retrieved from disk
/// </summary>
public string? LastFileHash { get; private set; }
public virtual string ClassName => typeof(T).Name;
public virtual string FileLocation => Path.Combine(Paths.Base, $"{ClassName}.json");
public virtual string LOG_IDENT_CLASS => $"JsonManager<{ClassName}>";
public virtual void Load(bool alertFailure = true)
{
string LOG_IDENT = $"{LOG_IDENT_CLASS}::Load";
App.Logger.WriteLine(LOG_IDENT, $"Loading from {FileLocation}...");
try
{
string contents = File.ReadAllText(FileLocation);
T? settings = JsonSerializer.Deserialize<T>(contents);
if (settings is null)
throw new ArgumentNullException("Deserialization returned null");
Prop = settings;
LastFileHash = MD5Hash.FromString(contents);
App.Logger.WriteLine(LOG_IDENT, "Loaded successfully!");
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Failed to load!");
App.Logger.WriteException(LOG_IDENT, ex);
if (alertFailure)
{
string message = "";
if (ClassName == nameof(Settings))
message = Strings.JsonManager_SettingsLoadFailed;
else if (ClassName == nameof(FastFlagManager))
message = Strings.JsonManager_FastFlagsLoadFailed;
if (!String.IsNullOrEmpty(message))
Frontend.ShowMessageBox($"{message}\n\n{ex.Message}", System.Windows.MessageBoxImage.Warning);
try
{
// Create a backup of loaded file
File.Copy(FileLocation, FileLocation + ".bak", true);
}
catch (Exception copyEx)
{
App.Logger.WriteLine(LOG_IDENT, $"Failed to create backup file: {FileLocation}.bak");
App.Logger.WriteException(LOG_IDENT, copyEx);
}
}
Save();
}
}
public virtual void Save()
{
string LOG_IDENT = $"{LOG_IDENT_CLASS}::Save";
App.Logger.WriteLine(LOG_IDENT, $"Saving to {FileLocation}...");
Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!);
try
{
string contents = JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(FileLocation, contents);
LastFileHash = MD5Hash.FromString(contents);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
App.Logger.WriteLine(LOG_IDENT, "Failed to save");
App.Logger.WriteException(LOG_IDENT, ex);
string errorMessage = string.Format(Resources.Strings.Bootstrapper_JsonManagerSaveFailed, ClassName, ex.Message);
Frontend.ShowMessageBox(errorMessage, System.Windows.MessageBoxImage.Warning);
return;
}
App.Logger.WriteLine(LOG_IDENT, "Save complete!");
}
/// <summary>
/// Is the file on disk different to the one deserialised during this session?
/// </summary>
public bool HasFileOnDiskChanged()
{
return LastFileHash != MD5Hash.FromFile(FileLocation);
}
}
}