diff --git a/OtterGui b/OtterGui
index 4ef03980..6969f4c0 160000
--- a/OtterGui
+++ b/OtterGui
@@ -1 +1 @@
-Subproject commit 4ef03980803cdf5a253c041d6ed1ef26d9a9b938
+Subproject commit 6969f4c05b2fab57e0dc30ae249be7c23cd81fa4
diff --git a/Penumbra/Collections/Manager/ActiveCollections.cs b/Penumbra/Collections/Manager/ActiveCollections.cs
index 86d36abd..be46f02b 100644
--- a/Penumbra/Collections/Manager/ActiveCollections.cs
+++ b/Penumbra/Collections/Manager/ActiveCollections.cs
@@ -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);
}
/// Set and create an active collection, can be used to set Default, Current, Interface, Special, or Individual collections.
@@ -318,7 +318,7 @@ public class ActiveCollections : ISavable, IDisposable
}
else if (collectionType is not CollectionType.Temporary)
{
- _saveService.QueueSave(this);
+ _saveService.DelaySave(this);
}
}
diff --git a/Penumbra/Collections/Manager/CollectionEditor.cs b/Penumbra/Collections/Manager/CollectionEditor.cs
index 4000f504..c2b84256 100644
--- a/Penumbra/Collections/Manager/CollectionEditor.cs
+++ b/Penumbra/Collections/Manager/CollectionEditor.cs
@@ -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);
}
diff --git a/Penumbra/Collections/Manager/CollectionStorage.cs b/Penumbra/Collections/Manager/CollectionStorage.cs
index 4980ea71..7a60aaec 100644
--- a/Penumbra/Collections/Manager/CollectionStorage.cs
+++ b/Penumbra/Collections/Manager/CollectionStorage.cs
@@ -194,14 +194,14 @@ public class CollectionStorage : IReadOnlyList, IDisposable
var any = collection.UnusedSettings.Count > 0;
((Dictionary)collection.UnusedSettings).Clear();
if (any)
- _saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
+ _saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
}
/// Remove a specific setting for not currently-installed mods from the given collection.
public void CleanUnavailableSetting(ModCollection collection, string? setting)
{
if (setting != null && ((Dictionary)collection.UnusedSettings).Remove(setting))
- _saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
+ _saveService.DelaySave(new ModCollectionSave(_modStorage, collection));
}
///
@@ -292,7 +292,7 @@ public class CollectionStorage : IReadOnlyList, IDisposable
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
DirectoryInfo? newDirectory)
{
- switch (type)
+ switch (type)
{
case ModPathChangeType.Added:
foreach (var collection in this)
@@ -304,7 +304,7 @@ public class CollectionStorage : IReadOnlyList, 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, 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));
}
}
}
diff --git a/Penumbra/Collections/Manager/InheritanceManager.cs b/Penumbra/Collections/Manager/InheritanceManager.cs
index 93dee89f..0d8bfe3c 100644
--- a/Penumbra/Collections/Manager/InheritanceManager.cs
+++ b/Penumbra/Collections/Manager/InheritanceManager.cs
@@ -88,7 +88,7 @@ public class InheritanceManager : IDisposable
var parent = inheritor.DirectlyInheritsFrom[idx];
((List)inheritor.DirectlyInheritsFrom).RemoveAt(idx);
((List)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)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)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);
}
diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs
index 697d7a7d..f71e4d32 100644
--- a/Penumbra/Configuration.cs
+++ b/Penumbra/Configuration.cs
@@ -135,7 +135,7 @@ public class Configuration : IPluginConfiguration, ISavable
/// Save the current configuration.
public void Save()
- => _saveService.QueueSave(this);
+ => _saveService.DelaySave(this);
/// Contains some default values or boundaries for config values.
public static class Constants
diff --git a/Penumbra/Mods/Manager/ModFileSystem.cs b/Penumbra/Mods/Manager/ModFileSystem.cs
index 62daa9fb..79554797 100644
--- a/Penumbra/Mods/Manager/ModFileSystem.cs
+++ b/Penumbra/Mods/Manager/ModFileSystem.cs
@@ -77,7 +77,7 @@ public sealed class ModFileSystem : FileSystem, 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, IDisposable, ISavable
break;
case ModPathChangeType.Moved:
- _saveService.QueueSave(this);
+ _saveService.DelaySave(this);
break;
case ModPathChangeType.Reloaded:
// Nothing
diff --git a/Penumbra/Services/SaveService.cs b/Penumbra/Services/SaveService.cs
index 2445392e..557cc4fc 100644
--- a/Penumbra/Services/SaveService.cs
+++ b/Penumbra/Services/SaveService.cs
@@ -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); });
+ }
+
+ /// Queue a delayed save with the standard delay for after the delay is over.
+ public void DelaySave(ISavable value)
+ => DelaySave(value, StandardDelay);
+
+ /// Queue a delayed save for after the delay is over.
+ public void DelaySave(ISavable value, TimeSpan delay)
+ {
+ var file = value.ToFilename(FileNames);
+ _framework.RegisterDelayed($"{value.GetType().Name} ## {file}", () => { ImmediateSave(value); }, delay);
}
/// Immediately trigger a save.
diff --git a/Penumbra/UI/Tabs/DebugTab.cs b/Penumbra/UI/Tabs/DebugTab.cs
index ae8b047d..75b90d29 100644
--- a/Penumbra/UI/Tabs/DebugTab.cs
+++ b/Penumbra/UI/Tabs/DebugTab.cs
@@ -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 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()