Add some delayed saves and UI for that.

This commit is contained in:
Ottermandias 2023-05-09 17:15:19 +02:00
parent e8eff51d84
commit 5ba43c1b19
9 changed files with 58 additions and 20 deletions

@ -1 +1 @@
Subproject commit 4ef03980803cdf5a253c041d6ed1ef26d9a9b938
Subproject commit 6969f4c05b2fab57e0dc30ae249be7c23cd81fa4

View file

@ -170,7 +170,7 @@ public class ActiveCollections : ISavable, IDisposable
public void MoveIndividualCollection(int from, int to)
{
if (Individuals.Move(from, to))
_saveService.QueueSave(this);
_saveService.DelaySave(this);
}
/// <summary> Set and create an active collection, can be used to set Default, Current, Interface, Special, or Individual collections. </summary>
@ -318,7 +318,7 @@ public class ActiveCollections : ISavable, IDisposable
}
else if (collectionType is not CollectionType.Temporary)
{
_saveService.QueueSave(this);
_saveService.DelaySave(this);
}
}

View file

@ -205,7 +205,7 @@ public class CollectionEditor
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void InvokeChange(ModCollection changedCollection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx)
{
_saveService.QueueSave(new ModCollectionSave(_modStorage, changedCollection));
_saveService.DelaySave(new ModCollectionSave(_modStorage, changedCollection));
_communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false);
RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx);
}

View file

@ -194,14 +194,14 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
var any = collection.UnusedSettings.Count > 0;
((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings).Clear();
if (any)
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
_saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
}
/// <summary> Remove a specific setting for not currently-installed mods from the given collection. </summary>
public void CleanUnavailableSetting(ModCollection collection, string? setting)
{
if (setting != null && ((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings).Remove(setting))
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
_saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
}
/// <summary>
@ -304,7 +304,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
break;
case ModPathChangeType.Moved:
foreach (var collection in this.Where(collection => collection.Settings[mod.Index] != null))
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
_saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
break;
}
}
@ -319,7 +319,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
foreach (var collection in this)
{
if (collection.Settings[mod.Index]?.HandleChanges(type, mod, groupIdx, optionIdx, movedToIdx) ?? false)
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
_saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
}
}
}

View file

@ -88,7 +88,7 @@ public class InheritanceManager : IDisposable
var parent = inheritor.DirectlyInheritsFrom[idx];
((List<ModCollection>)inheritor.DirectlyInheritsFrom).RemoveAt(idx);
((List<ModCollection>)parent.DirectParentOf).Remove(inheritor);
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances.");
@ -100,7 +100,7 @@ public class InheritanceManager : IDisposable
if (!((List<ModCollection>)inheritor.DirectlyInheritsFrom).Move(from, to))
return;
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}.");
@ -116,7 +116,7 @@ public class InheritanceManager : IDisposable
((List<ModCollection>)parent.DirectParentOf).Add(inheritor);
if (invokeEvent)
{
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_saveService.DelaySave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
}

View file

@ -135,7 +135,7 @@ public class Configuration : IPluginConfiguration, ISavable
/// <summary> Save the current configuration. </summary>
public void Save()
=> _saveService.QueueSave(this);
=> _saveService.DelaySave(this);
/// <summary> Contains some default values or boundaries for config values. </summary>
public static class Constants

View file

@ -77,7 +77,7 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable
private void OnChange(FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
{
if (type != FileSystemChangeType.Reload)
_saveService.QueueSave(this);
_saveService.DelaySave(this);
}
// Update sort order when defaulted mod names change.
@ -112,7 +112,7 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable
break;
case ModPathChangeType.Moved:
_saveService.QueueSave(this);
_saveService.DelaySave(this);
break;
case ModPathChangeType.Reloaded:
// Nothing

View file

@ -29,6 +29,8 @@ public interface ISavable
public class SaveService
{
private static readonly TimeSpan StandardDelay = TimeSpan.FromSeconds(30);
private readonly Logger _log;
private readonly FrameworkManager _framework;
@ -47,7 +49,18 @@ public class SaveService
public void QueueSave(ISavable value)
{
var file = value.ToFilename(FileNames);
_framework.RegisterDelayed(value.GetType().Name + file, () => { ImmediateSave(value); });
_framework.RegisterOnTick($"{value.GetType().Name} ## {file}", () => { ImmediateSave(value); });
}
/// <summary> Queue a delayed save with the standard delay for after the delay is over. </summary>
public void DelaySave(ISavable value)
=> DelaySave(value, StandardDelay);
/// <summary> Queue a delayed save for after the delay is over. </summary>
public void DelaySave(ISavable value, TimeSpan delay)
{
var file = value.ToFilename(FileNames);
_framework.RegisterDelayed($"{value.GetType().Name} ## {file}", () => { ImmediateSave(value); }, delay);
}
/// <summary> Immediately trigger a save. </summary>

View file

@ -10,6 +10,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Widgets;
using Penumbra.Api;
using Penumbra.Collections.Manager;
@ -55,13 +56,14 @@ public class DebugTab : Window, ITab
private readonly CutsceneService _cutsceneService;
private readonly ModImportManager _modImporter;
private readonly ImportPopup _importPopup;
private readonly FrameworkManager _framework;
public DebugTab(StartTracker timer, PerformanceTracker performance, Configuration config, CollectionManager collectionManager,
ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorService actorService,
DalamudServices dalamud, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources,
ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver,
DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache,
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup)
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse, false)
{
IsOpen = true;
@ -92,6 +94,7 @@ public class DebugTab : Window, ITab
_cutsceneService = cutsceneService;
_modImporter = modImporter;
_importPopup = importPopup;
_framework = framework;
}
public ReadOnlySpan<byte> Label
@ -209,10 +212,10 @@ public class DebugTab : Window, ITab
if (table)
{
var importing = _modImporter.IsImporting(out var importer);
PrintValue("Is Importing", importing.ToString());
PrintValue("Importer State", (importer?.State ?? ImporterState.None).ToString());
PrintValue("Is Importing", importing.ToString());
PrintValue("Importer State", (importer?.State ?? ImporterState.None).ToString());
PrintValue("Import Window Was Drawn", _importPopup.WasDrawn.ToString());
PrintValue("Import Popup Was Drawn", _importPopup.PopupWasDrawn.ToString());
PrintValue("Import Popup Was Drawn", _importPopup.PopupWasDrawn.ToString());
ImGui.TableNextColumn();
ImGui.TextUnformatted("Import Batches");
ImGui.TableNextColumn();
@ -234,6 +237,28 @@ public class DebugTab : Window, ITab
}
}
}
using (var tree = TreeNode("Framework"))
{
if (tree)
{
using var table = Table("##DebugFramework", 2, ImGuiTableFlags.SizingFixedFit);
if (table)
{
foreach(var important in _framework.Important)
PrintValue(important, "Immediate");
foreach (var (onTick, idx) in _framework.OnTick.WithIndex())
PrintValue(onTick, $"{idx + 1} Tick(s) From Now");
foreach (var (time, name) in _framework.Delayed)
{
var span = time - DateTime.UtcNow;
PrintValue(name, $"After {span.Minutes:D2}:{span.Seconds:D2}.{span.Milliseconds / 10:D2} (+ Ticks)");
}
}
}
}
}
private void DrawPerformanceTab()