From e594d59986123c438a364daaef908097e13a9409 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Sat, 2 Dec 2023 12:58:55 +0900 Subject: [PATCH] Enable tracking only when there exists a subscriber --- Dalamud/Game/Inventory/GameInventory.cs | 433 +++++++++--------- .../InventoryComplexEventArgs.cs | 2 +- .../InventoryEventArgs.cs | 2 +- .../InventoryItemAddedArgs.cs | 2 +- .../InventoryItemChangedArgs.cs | 2 +- .../InventoryItemMergedArgs.cs | 2 +- .../InventoryItemMovedArgs.cs | 2 +- .../InventoryItemRemovedArgs.cs | 2 +- .../InventoryItemSplitArgs.cs | 2 +- .../Internal/Windows/Data/DataWindow.cs | 1 + .../Windows/Data/GameInventoryTestWidget.cs | 163 +++++++ Dalamud/Plugin/Services/IGameInventory.cs | 2 +- 12 files changed, 381 insertions(+), 234 deletions(-) create mode 100644 Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs diff --git a/Dalamud/Game/Inventory/GameInventory.cs b/Dalamud/Game/Inventory/GameInventory.cs index fba950c09..9a0388113 100644 --- a/Dalamud/Game/Inventory/GameInventory.cs +++ b/Dalamud/Game/Inventory/GameInventory.cs @@ -2,15 +2,13 @@ using System.Collections.Generic; using System.Linq; -using Dalamud.Configuration.Internal; -using Dalamud.Game.Inventory.InventoryChangeArgsTypes; +using Dalamud.Game.Inventory.InventoryEventArgTypes; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal; using Dalamud.Plugin.Services; -using Serilog.Events; - namespace Dalamud.Game.Inventory; /// @@ -18,9 +16,10 @@ namespace Dalamud.Game.Inventory; /// [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -internal class GameInventory : IDisposable, IServiceType, IGameInventory +internal class GameInventory : IDisposable, IServiceType { - private static readonly ModuleLog Log = new(nameof(GameInventory)); + private readonly List subscribersPendingChange = new(); + private readonly List subscribers = new(); private readonly List addedEvents = new(); private readonly List removedEvents = new(); @@ -32,120 +31,58 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); - [ServiceManager.ServiceDependency] - private readonly DalamudConfiguration dalamudConfiguration = Service.Get(); - private readonly GameInventoryType[] inventoryTypes; private readonly GameInventoryItem[]?[] inventoryItems; + private bool subscribersChanged; + [ServiceManager.ServiceConstructor] private GameInventory() { this.inventoryTypes = Enum.GetValues(); this.inventoryItems = new GameInventoryItem[this.inventoryTypes.Length][]; - - this.framework.Update += this.OnFrameworkUpdate; - - // Separate log logic as an event handler. - this.InventoryChanged += events => - { - if (this.dalamudConfiguration.LogLevel > LogEventLevel.Verbose) - return; - - foreach (var e in events) - { - if (e is InventoryComplexEventArgs icea) - Log.Verbose($"{icea}\n\t├ {icea.SourceEvent}\n\t└ {icea.TargetEvent}"); - else - Log.Verbose($"{e}"); - } - }; } - /// - public event IGameInventory.InventoryChangelogDelegate? InventoryChanged; - - /// - public event IGameInventory.InventoryChangelogDelegate? InventoryChangedRaw; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemAdded; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemRemoved; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemChanged; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemMoved; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemSplit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemMerged; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemAddedExplicit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemRemovedExplicit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemChangedExplicit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemMovedExplicit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemSplitExplicit; - - /// - public event IGameInventory.InventoryChangedDelegate? ItemMergedExplicit; - /// public void Dispose() { - this.framework.Update -= this.OnFrameworkUpdate; - } - - private static void InvokeSafely( - IGameInventory.InventoryChangelogDelegate? cb, - IReadOnlyCollection data) - { - try + lock (this.subscribersPendingChange) { - cb?.Invoke(data); - } - catch (Exception e) - { - Log.Error(e, "Exception during batch callback"); + this.subscribers.Clear(); + this.subscribersPendingChange.Clear(); + this.subscribersChanged = false; + this.framework.Update -= this.OnFrameworkUpdate; } } - private static void InvokeSafely(IGameInventory.InventoryChangedDelegate? cb, InventoryEventArgs arg) + /// + /// Subscribe to events. + /// + /// The event target. + public void Subscribe(GameInventoryPluginScoped s) { - try + lock (this.subscribersPendingChange) { - cb?.Invoke(arg.Type, arg); - } - catch (Exception e) - { - Log.Error(e, "Exception during {argType} callback", arg.Type); + this.subscribersPendingChange.Add(s); + this.subscribersChanged = true; + if (this.subscribersPendingChange.Count == 1) + this.framework.Update += this.OnFrameworkUpdate; } } - private static void InvokeSafely(IGameInventory.InventoryChangedDelegate? cb, T arg) - where T : InventoryEventArgs + /// + /// Unsubscribe from events. + /// + /// The event target. + public void Unsubscribe(GameInventoryPluginScoped s) { - try + lock (this.subscribersPendingChange) { - cb?.Invoke(arg); - } - catch (Exception e) - { - Log.Error(e, "Exception during {argType} callback", arg.Type); + if (!this.subscribersPendingChange.Remove(s)) + return; + this.subscribersChanged = true; + if (this.subscribersPendingChange.Count == 0) + this.framework.Update -= this.OnFrameworkUpdate; } } @@ -193,18 +130,40 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory if (this.addedEvents.Count == 0 && this.removedEvents.Count == 0 && this.changedEvents.Count == 0) return; + // Make a copy of subscribers, to accommodate self removal during the loop. + if (this.subscribersChanged) + { + bool isNew; + lock (this.subscribersPendingChange) + { + isNew = this.subscribersPendingChange.Any() && !this.subscribers.Any(); + this.subscribers.Clear(); + this.subscribers.AddRange(this.subscribersPendingChange); + this.subscribersChanged = false; + } + + // Is this the first time (resuming) scanning for changes? Then discard the "changes". + if (isNew) + { + this.addedEvents.Clear(); + this.removedEvents.Clear(); + this.changedEvents.Clear(); + return; + } + } + // Broadcast InventoryChangedRaw. // Same reason with the above on why are there 3 lists of events involved. - InvokeSafely( - this.InventoryChangedRaw, - new DeferredReadOnlyCollection( - this.addedEvents.Count + - this.removedEvents.Count + - this.changedEvents.Count, - () => Array.Empty() - .Concat(this.addedEvents) - .Concat(this.removedEvents) - .Concat(this.changedEvents))); + var allRawEventsCollection = new DeferredReadOnlyCollection( + this.addedEvents.Count + + this.removedEvents.Count + + this.changedEvents.Count, + () => Array.Empty() + .Concat(this.addedEvents) + .Concat(this.removedEvents) + .Concat(this.changedEvents)); + foreach (var s in this.subscribers) + s.InvokeChangedRaw(allRawEventsCollection); // Resolve moved items, from 1 added + 1 removed event. for (var iAdded = this.addedEvents.Count - 1; iAdded >= 0; --iAdded) @@ -291,58 +250,32 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory } } + // Create a collection view of all events. + var allEventsCollection = new DeferredReadOnlyCollection( + this.addedEvents.Count + + this.removedEvents.Count + + this.changedEvents.Count + + this.movedEvents.Count + + this.splitEvents.Count + + this.mergedEvents.Count, + () => Array.Empty() + .Concat(this.addedEvents) + .Concat(this.removedEvents) + .Concat(this.changedEvents) + .Concat(this.movedEvents) + .Concat(this.splitEvents) + .Concat(this.mergedEvents)); + // Broadcast the rest. - InvokeSafely( - this.InventoryChanged, - new DeferredReadOnlyCollection( - this.addedEvents.Count + - this.removedEvents.Count + - this.changedEvents.Count + - this.movedEvents.Count + - this.splitEvents.Count + - this.mergedEvents.Count, - () => Array.Empty() - .Concat(this.addedEvents) - .Concat(this.removedEvents) - .Concat(this.changedEvents) - .Concat(this.movedEvents) - .Concat(this.splitEvents) - .Concat(this.mergedEvents))); - - foreach (var x in this.addedEvents) + foreach (var s in this.subscribers) { - InvokeSafely(this.ItemAdded, x); - InvokeSafely(this.ItemAddedExplicit, x); - } - - foreach (var x in this.removedEvents) - { - InvokeSafely(this.ItemRemoved, x); - InvokeSafely(this.ItemRemovedExplicit, x); - } - - foreach (var x in this.changedEvents) - { - InvokeSafely(this.ItemChanged, x); - InvokeSafely(this.ItemChangedExplicit, x); - } - - foreach (var x in this.movedEvents) - { - InvokeSafely(this.ItemMoved, x); - InvokeSafely(this.ItemMovedExplicit, x); - } - - foreach (var x in this.splitEvents) - { - InvokeSafely(this.ItemSplit, x); - InvokeSafely(this.ItemSplitExplicit, x); - } - - foreach (var x in this.mergedEvents) - { - InvokeSafely(this.ItemMerged, x); - InvokeSafely(this.ItemMergedExplicit, x); + s.InvokeChanged(allEventsCollection); + s.Invoke(this.addedEvents); + s.Invoke(this.removedEvents); + s.Invoke(this.changedEvents); + s.Invoke(this.movedEvents); + s.Invoke(this.splitEvents); + s.Invoke(this.mergedEvents); } // We're done using the lists. Clean them up. @@ -388,29 +321,15 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory #pragma warning restore SA1015 internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInventory { + private static readonly ModuleLog Log = new(nameof(GameInventoryPluginScoped)); + [ServiceManager.ServiceDependency] private readonly GameInventory gameInventoryService = Service.Get(); /// /// Initializes a new instance of the class. /// - public GameInventoryPluginScoped() - { - this.gameInventoryService.InventoryChanged += this.OnInventoryChangedForward; - this.gameInventoryService.InventoryChangedRaw += this.OnInventoryChangedRawForward; - this.gameInventoryService.ItemAdded += this.OnInventoryItemAddedForward; - this.gameInventoryService.ItemRemoved += this.OnInventoryItemRemovedForward; - this.gameInventoryService.ItemMoved += this.OnInventoryItemMovedForward; - this.gameInventoryService.ItemChanged += this.OnInventoryItemChangedForward; - this.gameInventoryService.ItemSplit += this.OnInventoryItemSplitForward; - this.gameInventoryService.ItemMerged += this.OnInventoryItemMergedForward; - this.gameInventoryService.ItemAddedExplicit += this.OnInventoryItemAddedExplicitForward; - this.gameInventoryService.ItemRemovedExplicit += this.OnInventoryItemRemovedExplicitForward; - this.gameInventoryService.ItemChangedExplicit += this.OnInventoryItemChangedExplicitForward; - this.gameInventoryService.ItemMovedExplicit += this.OnInventoryItemMovedExplicitForward; - this.gameInventoryService.ItemSplitExplicit += this.OnInventoryItemSplitExplicitForward; - this.gameInventoryService.ItemMergedExplicit += this.OnInventoryItemMergedExplicitForward; - } + public GameInventoryPluginScoped() => this.gameInventoryService.Subscribe(this); /// public event IGameInventory.InventoryChangelogDelegate? InventoryChanged; @@ -457,20 +376,7 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven /// public void Dispose() { - this.gameInventoryService.InventoryChanged -= this.OnInventoryChangedForward; - this.gameInventoryService.InventoryChangedRaw -= this.OnInventoryChangedRawForward; - this.gameInventoryService.ItemAdded -= this.OnInventoryItemAddedForward; - this.gameInventoryService.ItemRemoved -= this.OnInventoryItemRemovedForward; - this.gameInventoryService.ItemChanged -= this.OnInventoryItemChangedForward; - this.gameInventoryService.ItemMoved -= this.OnInventoryItemMovedForward; - this.gameInventoryService.ItemSplit -= this.OnInventoryItemSplitForward; - this.gameInventoryService.ItemMerged -= this.OnInventoryItemMergedForward; - this.gameInventoryService.ItemAddedExplicit -= this.OnInventoryItemAddedExplicitForward; - this.gameInventoryService.ItemRemovedExplicit -= this.OnInventoryItemRemovedExplicitForward; - this.gameInventoryService.ItemChangedExplicit -= this.OnInventoryItemChangedExplicitForward; - this.gameInventoryService.ItemMovedExplicit -= this.OnInventoryItemMovedExplicitForward; - this.gameInventoryService.ItemSplitExplicit -= this.OnInventoryItemSplitExplicitForward; - this.gameInventoryService.ItemMergedExplicit -= this.OnInventoryItemMergedExplicitForward; + this.gameInventoryService.Unsubscribe(this); this.InventoryChanged = null; this.InventoryChangedRaw = null; @@ -488,45 +394,122 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven this.ItemMergedExplicit = null; } - private void OnInventoryChangedForward(IReadOnlyCollection events) - => this.InventoryChanged?.Invoke(events); + /// + /// Invoke . + /// + /// The data. + internal void InvokeChanged(IReadOnlyCollection data) + { + try + { + this.InventoryChanged?.Invoke(data); + } + catch (Exception e) + { + Log.Error( + e, + "[{plugin}] Exception during {argType} callback", + Service.GetNullable()?.FindCallingPlugin(new(e))?.Name ?? "(unknown plugin)", + nameof(this.InventoryChanged)); + } + } - private void OnInventoryChangedRawForward(IReadOnlyCollection events) - => this.InventoryChangedRaw?.Invoke(events); + /// + /// Invoke . + /// + /// The data. + internal void InvokeChangedRaw(IReadOnlyCollection data) + { + try + { + this.InventoryChangedRaw?.Invoke(data); + } + catch (Exception e) + { + Log.Error( + e, + "[{plugin}] Exception during {argType} callback", + Service.GetNullable()?.FindCallingPlugin(new(e))?.Name ?? "(unknown plugin)", + nameof(this.InventoryChangedRaw)); + } + } + + // Note below: using List instead of IEnumerable, since List has a specialized lightweight enumerator. - private void OnInventoryItemAddedForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemAdded?.Invoke(type, data); + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemAdded, this.ItemAddedExplicit, events); + + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemRemoved, this.ItemRemovedExplicit, events); + + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemChanged, this.ItemChangedExplicit, events); + + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemMoved, this.ItemMovedExplicit, events); + + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemSplit, this.ItemSplitExplicit, events); + + /// + /// Invoke the appropriate event handler. + /// + /// The data. + internal void Invoke(List events) => + Invoke(this.ItemMerged, this.ItemMergedExplicit, events); + + private static void Invoke( + IGameInventory.InventoryChangedDelegate? cb, + IGameInventory.InventoryChangedDelegate? cbt, + List events) where T : InventoryEventArgs + { + foreach (var evt in events) + { + try + { + cb?.Invoke(evt.Type, evt); + } + catch (Exception e) + { + Log.Error( + e, + "[{plugin}] Exception during untyped callback for {evt}", + Service.GetNullable()?.FindCallingPlugin(new(e))?.Name ?? "(unknown plugin)", + evt); + } - private void OnInventoryItemRemovedForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemRemoved?.Invoke(type, data); - - private void OnInventoryItemChangedForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemChanged?.Invoke(type, data); - - private void OnInventoryItemMovedForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemMoved?.Invoke(type, data); - - private void OnInventoryItemSplitForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemSplit?.Invoke(type, data); - - private void OnInventoryItemMergedForward(GameInventoryEvent type, InventoryEventArgs data) - => this.ItemMerged?.Invoke(type, data); - - private void OnInventoryItemAddedExplicitForward(InventoryItemAddedArgs data) - => this.ItemAddedExplicit?.Invoke(data); - - private void OnInventoryItemRemovedExplicitForward(InventoryItemRemovedArgs data) - => this.ItemRemovedExplicit?.Invoke(data); - - private void OnInventoryItemChangedExplicitForward(InventoryItemChangedArgs data) - => this.ItemChangedExplicit?.Invoke(data); - - private void OnInventoryItemMovedExplicitForward(InventoryItemMovedArgs data) - => this.ItemMovedExplicit?.Invoke(data); - - private void OnInventoryItemSplitExplicitForward(InventoryItemSplitArgs data) - => this.ItemSplitExplicit?.Invoke(data); - - private void OnInventoryItemMergedExplicitForward(InventoryItemMergedArgs data) - => this.ItemMergedExplicit?.Invoke(data); + try + { + cbt?.Invoke(evt); + } + catch (Exception e) + { + Log.Error( + e, + "[{plugin}] Exception during typed callback for {evt}", + Service.GetNullable()?.FindCallingPlugin(new(e))?.Name ?? "(unknown plugin)", + evt); + } + } + } } diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryComplexEventArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryComplexEventArgs.cs index c44bfb991..95d7e8238 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryComplexEventArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryComplexEventArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being affected across different slots, possibly in different containers. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryEventArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryEventArgs.cs index 8197e28f5..198e0395b 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryEventArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryEventArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Abstract base class representing inventory changed events. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemAddedArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemAddedArgs.cs index 45a35739a..ceb64c6f9 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemAddedArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemAddedArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being added to an inventory. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemChangedArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemChangedArgs.cs index 191cfa1d8..372418793 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemChangedArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemChangedArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an items properties being changed. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMergedArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMergedArgs.cs index 0f088f24b..d7056356e 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMergedArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMergedArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being merged from two stacks into one. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMovedArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMovedArgs.cs index 6a59d1304..8d0bbca17 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMovedArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemMovedArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being moved from one inventory and added to another. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemRemovedArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemRemovedArgs.cs index fe40c870b..5677e3cc4 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemRemovedArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemRemovedArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being removed from an inventory. diff --git a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemSplitArgs.cs b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemSplitArgs.cs index 2a3d41c09..5f717cf60 100644 --- a/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemSplitArgs.cs +++ b/Dalamud/Game/Inventory/InventoryEventArgTypes/InventoryItemSplitArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes; +namespace Dalamud.Game.Inventory.InventoryEventArgTypes; /// /// Represents the data associated with an item being split from one stack into two. diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs index e9d4152a5..20c3d6d01 100644 --- a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs @@ -33,6 +33,7 @@ internal class DataWindow : Window new FateTableWidget(), new FlyTextWidget(), new FontAwesomeTestWidget(), + new GameInventoryTestWidget(), new GamepadWidget(), new GaugeWidget(), new HookWidget(), diff --git a/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs new file mode 100644 index 000000000..c19f56654 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; + +using Dalamud.Configuration.Internal; +using Dalamud.Game.Inventory; +using Dalamud.Game.Inventory.InventoryEventArgTypes; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Logging.Internal; + +using ImGuiNET; + +using Serilog.Events; + +namespace Dalamud.Interface.Internal.Windows.Data; + +/// +/// Tester for . +/// +internal class GameInventoryTestWidget : IDataWindowWidget +{ + private static readonly ModuleLog Log = new(nameof(GameInventoryTestWidget)); + + private GameInventoryPluginScoped? scoped; + private bool standardEnabled; + private bool rawEnabled; + + /// + public string[]? CommandShortcuts { get; init; } = { "gameinventorytest" }; + + /// + public string DisplayName { get; init; } = "GameInventory Test"; + + /// + public bool Ready { get; set; } + + /// + public void Load() => this.Ready = true; + + /// + public void Draw() + { + if (Service.Get().LogLevel > LogEventLevel.Information) + { + ImGuiHelpers.SafeTextColoredWrapped( + ImGuiColors.DalamudRed, + "Enable LogLevel=Information display to see the logs."); + } + + using var table = ImRaii.Table(this.DisplayName, 3, ImGuiTableFlags.SizingFixedFit); + if (!table.Success) + return; + + ImGui.TableNextColumn(); + ImGui.TextUnformatted("Standard Logging"); + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(this.standardEnabled)) + { + if (ImGui.Button("Enable##standard-enable") && !this.standardEnabled) + { + this.scoped ??= new(); + this.scoped.InventoryChanged += ScopedOnInventoryChanged; + this.standardEnabled = true; + } + } + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(!this.standardEnabled)) + { + if (ImGui.Button("Disable##standard-disable") && this.scoped is not null && this.standardEnabled) + { + this.scoped.InventoryChanged -= ScopedOnInventoryChanged; + this.standardEnabled = false; + if (!this.rawEnabled) + { + this.scoped.Dispose(); + this.scoped = null; + } + } + } + + ImGui.TableNextRow(); + + ImGui.TableNextColumn(); + ImGui.TextUnformatted("Raw Logging"); + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(this.rawEnabled)) + { + if (ImGui.Button("Enable##raw-enable") && !this.rawEnabled) + { + this.scoped ??= new(); + this.scoped.InventoryChangedRaw += ScopedOnInventoryChangedRaw; + this.rawEnabled = true; + } + } + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(!this.rawEnabled)) + { + if (ImGui.Button("Disable##raw-disable") && this.scoped is not null && this.rawEnabled) + { + this.scoped.InventoryChangedRaw -= ScopedOnInventoryChangedRaw; + this.rawEnabled = false; + if (!this.standardEnabled) + { + this.scoped.Dispose(); + this.scoped = null; + } + } + } + + ImGui.TableNextRow(); + + ImGui.TableNextColumn(); + ImGui.TextUnformatted("All"); + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(this.standardEnabled && this.rawEnabled)) + { + if (ImGui.Button("Enable##all-enable")) + { + this.scoped ??= new(); + if (!this.standardEnabled) + this.scoped.InventoryChanged += ScopedOnInventoryChanged; + if (!this.rawEnabled) + this.scoped.InventoryChangedRaw += ScopedOnInventoryChangedRaw; + this.standardEnabled = this.rawEnabled = true; + } + } + + ImGui.TableNextColumn(); + using (ImRaii.Disabled(this.scoped is null)) + { + if (ImGui.Button("Disable##all-disable")) + { + this.scoped?.Dispose(); + this.scoped = null; + this.standardEnabled = this.rawEnabled = false; + } + } + } + + private static void ScopedOnInventoryChangedRaw(IReadOnlyCollection events) + { + var i = 0; + foreach (var e in events) + Log.Information($"[{++i}/{events.Count}] Raw: {e}"); + } + + private static void ScopedOnInventoryChanged(IReadOnlyCollection events) + { + var i = 0; + foreach (var e in events) + { + if (e is InventoryComplexEventArgs icea) + Log.Information($"[{++i}/{events.Count}] {icea}\n\t├ {icea.SourceEvent}\n\t└ {icea.TargetEvent}"); + else + Log.Information($"[{++i}/{events.Count}] {e}"); + } + } +} diff --git a/Dalamud/Plugin/Services/IGameInventory.cs b/Dalamud/Plugin/Services/IGameInventory.cs index cd289bc54..a1b1114d7 100644 --- a/Dalamud/Plugin/Services/IGameInventory.cs +++ b/Dalamud/Plugin/Services/IGameInventory.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Dalamud.Game.Inventory; -using Dalamud.Game.Inventory.InventoryChangeArgsTypes; +using Dalamud.Game.Inventory.InventoryEventArgTypes; namespace Dalamud.Plugin.Services;