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;