This commit is contained in:
Soreepeong 2023-12-01 20:55:46 +09:00
parent 34e3adb3f2
commit 35f4ff5c94
9 changed files with 226 additions and 217 deletions

View file

@ -1,4 +1,6 @@
using System.Collections.Generic; using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Configuration.Internal; using Dalamud.Configuration.Internal;
using Dalamud.Game.Inventory.InventoryChangeArgsTypes; using Dalamud.Game.Inventory.InventoryChangeArgsTypes;
@ -22,21 +24,20 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory
{ {
private static readonly ModuleLog Log = new("GameInventory"); private static readonly ModuleLog Log = new("GameInventory");
private readonly List<InventoryEventArgs> allEvents = new();
private readonly List<InventoryItemAddedArgs> addedEvents = new(); private readonly List<InventoryItemAddedArgs> addedEvents = new();
private readonly List<InventoryItemRemovedArgs> removedEvents = new(); private readonly List<InventoryItemRemovedArgs> removedEvents = new();
private readonly List<InventoryItemChangedArgs> changedEvents = new(); private readonly List<InventoryItemChangedArgs> changedEvents = new();
private readonly List<InventoryItemMovedArgs> movedEvents = new(); private readonly List<InventoryItemMovedArgs> movedEvents = new();
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly Framework framework = Service<Framework>.Get(); private readonly Framework framework = Service<Framework>.Get();
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get(); private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get();
private readonly GameInventoryType[] inventoryTypes; private readonly GameInventoryType[] inventoryTypes;
private readonly GameInventoryItem[]?[] inventoryItems; private readonly GameInventoryItem[]?[] inventoryItems;
[ServiceManager.ServiceConstructor] [ServiceManager.ServiceConstructor]
private GameInventory() private GameInventory()
{ {
@ -59,10 +60,10 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory
public event IGameInventory.InventoryChangedDelegate? ItemRemoved; public event IGameInventory.InventoryChangedDelegate? ItemRemoved;
/// <inheritdoc/> /// <inheritdoc/>
public event IGameInventory.InventoryChangedDelegate? ItemMoved; public event IGameInventory.InventoryChangedDelegate? ItemChanged;
/// <inheritdoc/> /// <inheritdoc/>
public event IGameInventory.InventoryChangedDelegate? ItemChanged; public event IGameInventory.InventoryChangedDelegate? ItemMoved;
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()
@ -111,7 +112,7 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory
Log.Error(e, "Exception during {argType} callback", arg.Type); Log.Error(e, "Exception during {argType} callback", arg.Type);
} }
} }
private void OnFrameworkUpdate(IFramework framework1) private void OnFrameworkUpdate(IFramework framework1)
{ {
for (var i = 0; i < this.inventoryTypes.Length; i++) for (var i = 0; i < this.inventoryTypes.Length; i++)
@ -152,124 +153,135 @@ internal class GameInventory : IDisposable, IServiceType, IGameInventory
} }
// Was there any change? If not, stop further processing. // Was there any change? If not, stop further processing.
// Note that... // Note that this.movedEvents is not checked; it will be populated after this check.
// * this.movedEvents is not checked; it will be populated after this check.
// * this.allEvents is not checked; it is a temporary list to be used after this check.
if (this.addedEvents.Count == 0 && this.removedEvents.Count == 0 && this.changedEvents.Count == 0) if (this.addedEvents.Count == 0 && this.removedEvents.Count == 0 && this.changedEvents.Count == 0)
return; return;
try // Broadcast InventoryChangedRaw.
InvokeSafely(
this.InventoryChangedRaw,
new DeferredReadOnlyCollection<InventoryEventArgs>(
this.addedEvents.Count +
this.removedEvents.Count +
this.changedEvents.Count,
() => Array.Empty<InventoryEventArgs>()
.Concat(this.addedEvents)
.Concat(this.removedEvents)
.Concat(this.changedEvents)));
// Resolve changelog for item moved, from 1 added + 1 removed event.
for (var iAdded = this.addedEvents.Count - 1; iAdded >= 0; --iAdded)
{ {
// Broadcast InventoryChangedRaw, if necessary. var added = this.addedEvents[iAdded];
if (this.InventoryChangedRaw is not null) for (var iRemoved = this.removedEvents.Count - 1; iRemoved >= 0; --iRemoved)
{ {
this.allEvents.Clear(); var removed = this.removedEvents[iRemoved];
this.allEvents.EnsureCapacity( if (added.Item.ItemId != removed.Item.ItemId)
this.addedEvents.Count continue;
+ this.removedEvents.Count
+ this.changedEvents.Count);
this.allEvents.AddRange(this.addedEvents);
this.allEvents.AddRange(this.removedEvents);
this.allEvents.AddRange(this.changedEvents);
InvokeSafely(this.InventoryChangedRaw, this.allEvents);
}
// Resolve changelog for item moved, from 1 added + 1 removed event. this.movedEvents.Add(new(removed, added));
for (var iAdded = this.addedEvents.Count - 1; iAdded >= 0; --iAdded)
// Remove the reinterpreted entries.
this.addedEvents.RemoveAt(iAdded);
this.removedEvents.RemoveAt(iRemoved);
break;
}
}
// Resolve changelog for item moved, from 2 changed events.
for (var i = this.changedEvents.Count - 1; i >= 0; --i)
{
var e1 = this.changedEvents[i];
for (var j = i - 1; j >= 0; --j)
{ {
var added = this.addedEvents[iAdded]; var e2 = this.changedEvents[j];
for (var iRemoved = this.removedEvents.Count - 1; iRemoved >= 0; --iRemoved) if (e1.Item.ItemId != e2.Item.ItemId || e1.Item.ItemId != e2.Item.ItemId)
{ continue;
var removed = this.removedEvents[iRemoved];
if (added.Item.ItemId != removed.Item.ItemId)
continue;
this.movedEvents.Add(new(removed, added)); // move happened, and e2 has an item
if (!e2.Item.IsEmpty)
// Remove the reinterpreted entries. this.movedEvents.Add(new(e1, e2));
this.addedEvents.RemoveAt(iAdded);
this.removedEvents.RemoveAt(iRemoved); // move happened, and e1 has an item
break; if (!e1.Item.IsEmpty)
} this.movedEvents.Add(new(e2, e1));
// Remove the reinterpreted entries. Note that i > j.
this.changedEvents.RemoveAt(i);
this.changedEvents.RemoveAt(j);
break;
} }
}
// Resolve changelog for item moved, from 2 changed events. // Log only if it matters.
for (var i = this.changedEvents.Count - 1; i >= 0; --i) if (this.dalamudConfiguration.LogLevel <= LogEventLevel.Verbose)
{ {
var e1 = this.changedEvents[i];
for (var j = i - 1; j >= 0; --j)
{
var e2 = this.changedEvents[j];
if (e1.Item.ItemId != e2.Item.ItemId || e1.Item.ItemId != e2.Item.ItemId)
continue;
// move happened, and e2 has an item
if (!e2.Item.IsEmpty)
this.movedEvents.Add(new(e1, e2));
// move happened, and e1 has an item
if (!e1.Item.IsEmpty)
this.movedEvents.Add(new(e2, e1));
// Remove the reinterpreted entries. Note that i > j.
this.changedEvents.RemoveAt(i);
this.changedEvents.RemoveAt(j);
break;
}
}
// Log only if it matters.
if (this.dalamudConfiguration.LogLevel >= LogEventLevel.Verbose)
{
foreach (var x in this.addedEvents)
Log.Verbose($"{x}");
foreach (var x in this.removedEvents)
Log.Verbose($"{x}");
foreach (var x in this.changedEvents)
Log.Verbose($"{x}");
foreach (var x in this.movedEvents)
Log.Verbose($"{x} (({x.SourceEvent}) + ({x.TargetEvent}))");
}
// Broadcast InventoryChanged, if necessary.
if (this.InventoryChanged is not null)
{
this.allEvents.Clear();
this.allEvents.EnsureCapacity(
this.addedEvents.Count
+ this.removedEvents.Count
+ this.changedEvents.Count
+ this.movedEvents.Count);
this.allEvents.AddRange(this.addedEvents);
this.allEvents.AddRange(this.removedEvents);
this.allEvents.AddRange(this.changedEvents);
this.allEvents.AddRange(this.movedEvents);
InvokeSafely(this.InventoryChanged, this.allEvents);
}
// Broadcast the rest.
foreach (var x in this.addedEvents) foreach (var x in this.addedEvents)
InvokeSafely(this.ItemAdded, x); Log.Verbose($"{x}");
foreach (var x in this.removedEvents) foreach (var x in this.removedEvents)
InvokeSafely(this.ItemRemoved, x); Log.Verbose($"{x}");
foreach (var x in this.changedEvents) foreach (var x in this.changedEvents)
InvokeSafely(this.ItemChanged, x); Log.Verbose($"{x}");
foreach (var x in this.movedEvents) foreach (var x in this.movedEvents)
InvokeSafely(this.ItemMoved, x); Log.Verbose($"{x} (({x.SourceEvent}) + ({x.TargetEvent}))");
} }
finally
// Broadcast the rest.
InvokeSafely(
this.InventoryChanged,
new DeferredReadOnlyCollection<InventoryEventArgs>(
this.addedEvents.Count +
this.removedEvents.Count +
this.changedEvents.Count +
this.movedEvents.Count,
() => Array.Empty<InventoryEventArgs>()
.Concat(this.addedEvents)
.Concat(this.removedEvents)
.Concat(this.changedEvents)
.Concat(this.movedEvents)));
foreach (var x in this.addedEvents)
InvokeSafely(this.ItemAdded, x);
foreach (var x in this.removedEvents)
InvokeSafely(this.ItemRemoved, x);
foreach (var x in this.changedEvents)
InvokeSafely(this.ItemChanged, x);
foreach (var x in this.movedEvents)
InvokeSafely(this.ItemMoved, x);
// We're done using the lists. Clean them up.
this.addedEvents.Clear();
this.removedEvents.Clear();
this.changedEvents.Clear();
this.movedEvents.Clear();
}
/// <summary>
/// A <see cref="IReadOnlyCollection{T}"/> view of <see cref="IEnumerable{T}"/>, so that the number of items
/// contained within can be known in advance, and it can be enumerated multiple times.
/// </summary>
/// <typeparam name="T">The type of elements being enumerated.</typeparam>
private class DeferredReadOnlyCollection<T> : IReadOnlyCollection<T>
{
private readonly Func<IEnumerable<T>> enumerableGenerator;
public DeferredReadOnlyCollection(int count, Func<IEnumerable<T>> enumerableGenerator)
{ {
this.addedEvents.Clear(); this.enumerableGenerator = enumerableGenerator;
this.removedEvents.Clear(); this.Count = count;
this.changedEvents.Clear();
this.movedEvents.Clear();
} }
public int Count { get; }
public IEnumerator<T> GetEnumerator() => this.enumerableGenerator().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.enumerableGenerator().GetEnumerator();
} }
} }
@ -313,10 +325,10 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven
public event IGameInventory.InventoryChangedDelegate? ItemRemoved; public event IGameInventory.InventoryChangedDelegate? ItemRemoved;
/// <inheritdoc/> /// <inheritdoc/>
public event IGameInventory.InventoryChangedDelegate? ItemMoved; public event IGameInventory.InventoryChangedDelegate? ItemChanged;
/// <inheritdoc/> /// <inheritdoc/>
public event IGameInventory.InventoryChangedDelegate? ItemChanged; public event IGameInventory.InventoryChangedDelegate? ItemMoved;
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()
@ -325,15 +337,15 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven
this.gameInventoryService.InventoryChangedRaw -= this.OnInventoryChangedRawForward; this.gameInventoryService.InventoryChangedRaw -= this.OnInventoryChangedRawForward;
this.gameInventoryService.ItemAdded -= this.OnInventoryItemAddedForward; this.gameInventoryService.ItemAdded -= this.OnInventoryItemAddedForward;
this.gameInventoryService.ItemRemoved -= this.OnInventoryItemRemovedForward; this.gameInventoryService.ItemRemoved -= this.OnInventoryItemRemovedForward;
this.gameInventoryService.ItemMoved -= this.OnInventoryItemMovedForward;
this.gameInventoryService.ItemChanged -= this.OnInventoryItemChangedForward; this.gameInventoryService.ItemChanged -= this.OnInventoryItemChangedForward;
this.gameInventoryService.ItemMoved -= this.OnInventoryItemMovedForward;
this.InventoryChanged = null; this.InventoryChanged = null;
this.InventoryChangedRaw = null; this.InventoryChangedRaw = null;
this.ItemAdded = null; this.ItemAdded = null;
this.ItemRemoved = null; this.ItemRemoved = null;
this.ItemMoved = null;
this.ItemChanged = null; this.ItemChanged = null;
this.ItemMoved = null;
} }
private void OnInventoryChangedForward(IReadOnlyCollection<InventoryEventArgs> events) private void OnInventoryChangedForward(IReadOnlyCollection<InventoryEventArgs> events)
@ -341,16 +353,16 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven
private void OnInventoryChangedRawForward(IReadOnlyCollection<InventoryEventArgs> events) private void OnInventoryChangedRawForward(IReadOnlyCollection<InventoryEventArgs> events)
=> this.InventoryChangedRaw?.Invoke(events); => this.InventoryChangedRaw?.Invoke(events);
private void OnInventoryItemAddedForward(GameInventoryEvent type, InventoryEventArgs data) private void OnInventoryItemAddedForward(GameInventoryEvent type, InventoryEventArgs data)
=> this.ItemAdded?.Invoke(type, data); => this.ItemAdded?.Invoke(type, data);
private void OnInventoryItemRemovedForward(GameInventoryEvent type, InventoryEventArgs data) private void OnInventoryItemRemovedForward(GameInventoryEvent type, InventoryEventArgs data)
=> this.ItemRemoved?.Invoke(type, data); => this.ItemRemoved?.Invoke(type, data);
private void OnInventoryItemMovedForward(GameInventoryEvent type, InventoryEventArgs data)
=> this.ItemMoved?.Invoke(type, data);
private void OnInventoryItemChangedForward(GameInventoryEvent type, InventoryEventArgs data) private void OnInventoryItemChangedForward(GameInventoryEvent type, InventoryEventArgs data)
=> this.ItemChanged?.Invoke(type, data); => this.ItemChanged?.Invoke(type, data);
private void OnInventoryItemMovedForward(GameInventoryEvent type, InventoryEventArgs data)
=> this.ItemMoved?.Invoke(type, data);
} }

View file

@ -3,7 +3,6 @@
/// <summary> /// <summary>
/// Class representing a item's changelog state. /// Class representing a item's changelog state.
/// </summary> /// </summary>
[Flags]
public enum GameInventoryEvent public enum GameInventoryEvent
{ {
/// <summary> /// <summary>
@ -11,24 +10,24 @@ public enum GameInventoryEvent
/// You should not see this value, unless you explicitly used it yourself, or APIs using this enum say otherwise. /// You should not see this value, unless you explicitly used it yourself, or APIs using this enum say otherwise.
/// </summary> /// </summary>
Empty = 0, Empty = 0,
/// <summary> /// <summary>
/// Item was added to an inventory. /// Item was added to an inventory.
/// </summary> /// </summary>
Added = 1 << 0, Added = 1,
/// <summary> /// <summary>
/// Item was removed from an inventory. /// Item was removed from an inventory.
/// </summary> /// </summary>
Removed = 1 << 1, Removed = 2,
/// <summary> /// <summary>
/// Properties are changed for an item in an inventory. /// Properties are changed for an item in an inventory.
/// </summary> /// </summary>
Changed = 1 << 2, Changed = 3,
/// <summary> /// <summary>
/// Item has been moved, possibly across different inventories. /// Item has been moved, possibly across different inventories.
/// </summary> /// </summary>
Moved = 1 << 3, Moved = 4,
} }

View file

@ -12,11 +12,6 @@ namespace Dalamud.Game.Inventory;
[StructLayout(LayoutKind.Explicit, Size = StructSizeInBytes)] [StructLayout(LayoutKind.Explicit, Size = StructSizeInBytes)]
public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem> public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
{ {
/// <summary>
/// An empty instance of <see cref="GameInventoryItem"/>.
/// </summary>
internal static readonly GameInventoryItem Empty = default;
/// <summary> /// <summary>
/// The actual data. /// The actual data.
/// </summary> /// </summary>
@ -104,7 +99,7 @@ public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
/// Gets the array of materia types. /// Gets the array of materia types.
/// </summary> /// </summary>
public ReadOnlySpan<ushort> Materia => new(Unsafe.AsPointer(ref Unsafe.AsRef(in this.InternalItem.Materia[0])), 5); public ReadOnlySpan<ushort> Materia => new(Unsafe.AsPointer(ref Unsafe.AsRef(in this.InternalItem.Materia[0])), 5);
/// <summary> /// <summary>
/// Gets the array of materia grades. /// Gets the array of materia grades.
/// </summary> /// </summary>
@ -119,8 +114,8 @@ public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
/// <summary> /// <summary>
/// Gets the glamour id for this item. /// Gets the glamour id for this item.
/// </summary> /// </summary>
public uint GlmaourId => this.InternalItem.GlamourID; public uint GlamourId => this.InternalItem.GlamourID;
/// <summary> /// <summary>
/// Gets the items crafter's content id. /// Gets the items crafter's content id.
/// NOTE: I'm not sure if this is a good idea to include or not in the dalamud api. Marked internal for now. /// NOTE: I'm not sure if this is a good idea to include or not in the dalamud api. Marked internal for now.
@ -163,6 +158,6 @@ public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
/// <inheritdoc cref="object.ToString"/> /// <inheritdoc cref="object.ToString"/>
public override string ToString() => public override string ToString() =>
this.IsEmpty this.IsEmpty
? "<Empty>" ? "no item"
: $"Item #{this.ItemId} at slot {this.InventorySlot} in {this.ContainerType}"; : $"item #{this.ItemId} at slot {this.InventorySlot} in {this.ContainerType}";
} }

View file

@ -9,17 +9,17 @@ public enum GameInventoryType : ushort
/// First panel of main player inventory. /// First panel of main player inventory.
/// </summary> /// </summary>
Inventory1 = 0, Inventory1 = 0,
/// <summary> /// <summary>
/// Second panel of main player inventory. /// Second panel of main player inventory.
/// </summary> /// </summary>
Inventory2 = 1, Inventory2 = 1,
/// <summary> /// <summary>
/// Third panel of main player inventory. /// Third panel of main player inventory.
/// </summary> /// </summary>
Inventory3 = 2, Inventory3 = 2,
/// <summary> /// <summary>
/// Fourth panel of main player inventory. /// Fourth panel of main player inventory.
/// </summary> /// </summary>
@ -40,32 +40,32 @@ public enum GameInventoryType : ushort
/// Crystal container. /// Crystal container.
/// </summary> /// </summary>
Crystals = 2001, Crystals = 2001,
/// <summary> /// <summary>
/// Mail container. /// Mail container.
/// </summary> /// </summary>
Mail = 2003, Mail = 2003,
/// <summary> /// <summary>
/// Key item container. /// Key item container.
/// </summary> /// </summary>
KeyItems = 2004, KeyItems = 2004,
/// <summary> /// <summary>
/// Quest item hand-in inventory. /// Quest item hand-in inventory.
/// </summary> /// </summary>
HandIn = 2005, HandIn = 2005,
/// <summary> /// <summary>
/// DamagedGear container. /// DamagedGear container.
/// </summary> /// </summary>
DamagedGear = 2007, DamagedGear = 2007,
/// <summary> /// <summary>
/// Examine window container. /// Examine window container.
/// </summary> /// </summary>
Examine = 2009, Examine = 2009,
/// <summary> /// <summary>
/// Doman Enclave Reconstruction Reclamation Box. /// Doman Enclave Reconstruction Reclamation Box.
/// </summary> /// </summary>
@ -75,22 +75,22 @@ public enum GameInventoryType : ushort
/// Armory off-hand weapon container. /// Armory off-hand weapon container.
/// </summary> /// </summary>
ArmoryOffHand = 3200, ArmoryOffHand = 3200,
/// <summary> /// <summary>
/// Armory head container. /// Armory head container.
/// </summary> /// </summary>
ArmoryHead = 3201, ArmoryHead = 3201,
/// <summary> /// <summary>
/// Armory body container. /// Armory body container.
/// </summary> /// </summary>
ArmoryBody = 3202, ArmoryBody = 3202,
/// <summary> /// <summary>
/// Armory hand/gloves container. /// Armory hand/gloves container.
/// </summary> /// </summary>
ArmoryHands = 3203, ArmoryHands = 3203,
/// <summary> /// <summary>
/// Armory waist container. /// Armory waist container.
/// <remarks> /// <remarks>
@ -98,42 +98,42 @@ public enum GameInventoryType : ushort
/// </remarks> /// </remarks>
/// </summary> /// </summary>
ArmoryWaist = 3204, ArmoryWaist = 3204,
/// <summary> /// <summary>
/// Armory legs/pants/skirt container. /// Armory legs/pants/skirt container.
/// </summary> /// </summary>
ArmoryLegs = 3205, ArmoryLegs = 3205,
/// <summary> /// <summary>
/// Armory feet/boots/shoes container. /// Armory feet/boots/shoes container.
/// </summary> /// </summary>
ArmoryFeets = 3206, ArmoryFeets = 3206,
/// <summary> /// <summary>
/// Armory earring container. /// Armory earring container.
/// </summary> /// </summary>
ArmoryEar = 3207, ArmoryEar = 3207,
/// <summary> /// <summary>
/// Armory necklace container. /// Armory necklace container.
/// </summary> /// </summary>
ArmoryNeck = 3208, ArmoryNeck = 3208,
/// <summary> /// <summary>
/// Armory bracelet container. /// Armory bracelet container.
/// </summary> /// </summary>
ArmoryWrist = 3209, ArmoryWrist = 3209,
/// <summary> /// <summary>
/// Armory ring container. /// Armory ring container.
/// </summary> /// </summary>
ArmoryRings = 3300, ArmoryRings = 3300,
/// <summary> /// <summary>
/// Armory soul crystal container. /// Armory soul crystal container.
/// </summary> /// </summary>
ArmorySoulCrystal = 3400, ArmorySoulCrystal = 3400,
/// <summary> /// <summary>
/// Armory main-hand weapon container. /// Armory main-hand weapon container.
/// </summary> /// </summary>
@ -143,17 +143,17 @@ public enum GameInventoryType : ushort
/// First panel of saddelbag inventory. /// First panel of saddelbag inventory.
/// </summary> /// </summary>
SaddleBag1 = 4000, SaddleBag1 = 4000,
/// <summary> /// <summary>
/// Second panel of Saddlebag inventory. /// Second panel of Saddlebag inventory.
/// </summary> /// </summary>
SaddleBag2 = 4001, SaddleBag2 = 4001,
/// <summary> /// <summary>
/// First panel of premium saddlebag inventory. /// First panel of premium saddlebag inventory.
/// </summary> /// </summary>
PremiumSaddleBag1 = 4100, PremiumSaddleBag1 = 4100,
/// <summary> /// <summary>
/// Second panel of premium saddlebag inventory. /// Second panel of premium saddlebag inventory.
/// </summary> /// </summary>
@ -163,52 +163,52 @@ public enum GameInventoryType : ushort
/// First panel of retainer inventory. /// First panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage1 = 10000, RetainerPage1 = 10000,
/// <summary> /// <summary>
/// Second panel of retainer inventory. /// Second panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage2 = 10001, RetainerPage2 = 10001,
/// <summary> /// <summary>
/// Third panel of retainer inventory. /// Third panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage3 = 10002, RetainerPage3 = 10002,
/// <summary> /// <summary>
/// Fourth panel of retainer inventory. /// Fourth panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage4 = 10003, RetainerPage4 = 10003,
/// <summary> /// <summary>
/// Fifth panel of retainer inventory. /// Fifth panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage5 = 10004, RetainerPage5 = 10004,
/// <summary> /// <summary>
/// Sixth panel of retainer inventory. /// Sixth panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage6 = 10005, RetainerPage6 = 10005,
/// <summary> /// <summary>
/// Seventh panel of retainer inventory. /// Seventh panel of retainer inventory.
/// </summary> /// </summary>
RetainerPage7 = 10006, RetainerPage7 = 10006,
/// <summary> /// <summary>
/// Retainer equipment container. /// Retainer equipment container.
/// </summary> /// </summary>
RetainerEquippedItems = 11000, RetainerEquippedItems = 11000,
/// <summary> /// <summary>
/// Retainer currency container. /// Retainer currency container.
/// </summary> /// </summary>
RetainerGil = 12000, RetainerGil = 12000,
/// <summary> /// <summary>
/// Retainer crystal container. /// Retainer crystal container.
/// </summary> /// </summary>
RetainerCrystals = 12001, RetainerCrystals = 12001,
/// <summary> /// <summary>
/// Retainer market item container. /// Retainer market item container.
/// </summary> /// </summary>
@ -218,32 +218,32 @@ public enum GameInventoryType : ushort
/// First panel of Free Company inventory. /// First panel of Free Company inventory.
/// </summary> /// </summary>
FreeCompanyPage1 = 20000, FreeCompanyPage1 = 20000,
/// <summary> /// <summary>
/// Second panel of Free Company inventory. /// Second panel of Free Company inventory.
/// </summary> /// </summary>
FreeCompanyPage2 = 20001, FreeCompanyPage2 = 20001,
/// <summary> /// <summary>
/// Third panel of Free Company inventory. /// Third panel of Free Company inventory.
/// </summary> /// </summary>
FreeCompanyPage3 = 20002, FreeCompanyPage3 = 20002,
/// <summary> /// <summary>
/// Fourth panel of Free Company inventory. /// Fourth panel of Free Company inventory.
/// </summary> /// </summary>
FreeCompanyPage4 = 20003, FreeCompanyPage4 = 20003,
/// <summary> /// <summary>
/// Fifth panel of Free Company inventory. /// Fifth panel of Free Company inventory.
/// </summary> /// </summary>
FreeCompanyPage5 = 20004, FreeCompanyPage5 = 20004,
/// <summary> /// <summary>
/// Free Company currency container. /// Free Company currency container.
/// </summary> /// </summary>
FreeCompanyGil = 22000, FreeCompanyGil = 22000,
/// <summary> /// <summary>
/// Free Company crystal container. /// Free Company crystal container.
/// </summary> /// </summary>
@ -253,102 +253,102 @@ public enum GameInventoryType : ushort
/// Housing exterior appearance container. /// Housing exterior appearance container.
/// </summary> /// </summary>
HousingExteriorAppearance = 25000, HousingExteriorAppearance = 25000,
/// <summary> /// <summary>
/// Housing exterior placed items container. /// Housing exterior placed items container.
/// </summary> /// </summary>
HousingExteriorPlacedItems = 25001, HousingExteriorPlacedItems = 25001,
/// <summary> /// <summary>
/// Housing interior appearance container. /// Housing interior appearance container.
/// </summary> /// </summary>
HousingInteriorAppearance = 25002, HousingInteriorAppearance = 25002,
/// <summary> /// <summary>
/// First panel of housing interior inventory. /// First panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems1 = 25003, HousingInteriorPlacedItems1 = 25003,
/// <summary> /// <summary>
/// Second panel of housing interior inventory. /// Second panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems2 = 25004, HousingInteriorPlacedItems2 = 25004,
/// <summary> /// <summary>
/// Third panel of housing interior inventory. /// Third panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems3 = 25005, HousingInteriorPlacedItems3 = 25005,
/// <summary> /// <summary>
/// Fourth panel of housing interior inventory. /// Fourth panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems4 = 25006, HousingInteriorPlacedItems4 = 25006,
/// <summary> /// <summary>
/// Fifth panel of housing interior inventory. /// Fifth panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems5 = 25007, HousingInteriorPlacedItems5 = 25007,
/// <summary> /// <summary>
/// Sixth panel of housing interior inventory. /// Sixth panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems6 = 25008, HousingInteriorPlacedItems6 = 25008,
/// <summary> /// <summary>
/// Seventh panel of housing interior inventory. /// Seventh panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems7 = 25009, HousingInteriorPlacedItems7 = 25009,
/// <summary> /// <summary>
/// Eighth panel of housing interior inventory. /// Eighth panel of housing interior inventory.
/// </summary> /// </summary>
HousingInteriorPlacedItems8 = 25010, HousingInteriorPlacedItems8 = 25010,
/// <summary> /// <summary>
/// Housing exterior storeroom inventory. /// Housing exterior storeroom inventory.
/// </summary> /// </summary>
HousingExteriorStoreroom = 27000, HousingExteriorStoreroom = 27000,
/// <summary> /// <summary>
/// First panel of housing interior storeroom inventory. /// First panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom1 = 27001, HousingInteriorStoreroom1 = 27001,
/// <summary> /// <summary>
/// Second panel of housing interior storeroom inventory. /// Second panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom2 = 27002, HousingInteriorStoreroom2 = 27002,
/// <summary> /// <summary>
/// Third panel of housing interior storeroom inventory. /// Third panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom3 = 27003, HousingInteriorStoreroom3 = 27003,
/// <summary> /// <summary>
/// Fourth panel of housing interior storeroom inventory. /// Fourth panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom4 = 27004, HousingInteriorStoreroom4 = 27004,
/// <summary> /// <summary>
/// Fifth panel of housing interior storeroom inventory. /// Fifth panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom5 = 27005, HousingInteriorStoreroom5 = 27005,
/// <summary> /// <summary>
/// Sixth panel of housing interior storeroom inventory. /// Sixth panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom6 = 27006, HousingInteriorStoreroom6 = 27006,
/// <summary> /// <summary>
/// Seventh panel of housing interior storeroom inventory. /// Seventh panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom7 = 27007, HousingInteriorStoreroom7 = 27007,
/// <summary> /// <summary>
/// Eighth panel of housing interior storeroom inventory. /// Eighth panel of housing interior storeroom inventory.
/// </summary> /// </summary>
HousingInteriorStoreroom8 = 27008, HousingInteriorStoreroom8 = 27008,
/// <summary> /// <summary>
/// An invalid value. /// An invalid value.
/// </summary> /// </summary>

View file

@ -1,13 +1,12 @@
using System.Diagnostics.CodeAnalysis; namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes;
namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes;
/// <summary> /// <summary>
/// Abstract base class representing inventory changed events. /// Abstract base class representing inventory changed events.
/// </summary> /// </summary>
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1206:Declaration keywords should follow order", Justification = "It literally says <access modifiers>, <static>, and then <all other keywords>. required is not an access modifier.")]
public abstract class InventoryEventArgs public abstract class InventoryEventArgs
{ {
private readonly GameInventoryItem item;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="InventoryEventArgs"/> class. /// Initializes a new instance of the <see cref="InventoryEventArgs"/> class.
/// </summary> /// </summary>
@ -16,7 +15,7 @@ public abstract class InventoryEventArgs
protected InventoryEventArgs(GameInventoryEvent type, in GameInventoryItem item) protected InventoryEventArgs(GameInventoryEvent type, in GameInventoryItem item)
{ {
this.Type = type; this.Type = type;
this.Item = item; this.item = item;
} }
/// <summary> /// <summary>
@ -28,8 +27,11 @@ public abstract class InventoryEventArgs
/// Gets the item associated with this event. /// Gets the item associated with this event.
/// <remarks><em>This is a copy of the item data.</em></remarks> /// <remarks><em>This is a copy of the item data.</em></remarks>
/// </summary> /// </summary>
public GameInventoryItem Item { get; } // impl note: we return a ref readonly view, to avoid making copies every time this property is accessed.
// see: https://devblogs.microsoft.com/premier-developer/avoiding-struct-and-readonly-reference-performance-pitfalls-with-errorprone-net/
// "Consider using ref readonly locals and ref return for library code"
public ref readonly GameInventoryItem Item => ref this.item;
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => $"<{this.Type}> ({this.Item})"; public override string ToString() => $"<{this.Type}> ({this.Item})";
} }

View file

@ -6,6 +6,8 @@
/// </summary> /// </summary>
public class InventoryItemChangedArgs : InventoryEventArgs public class InventoryItemChangedArgs : InventoryEventArgs
{ {
private readonly GameInventoryItem oldItemState;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="InventoryItemChangedArgs"/> class. /// Initializes a new instance of the <see cref="InventoryItemChangedArgs"/> class.
/// </summary> /// </summary>
@ -14,7 +16,7 @@ public class InventoryItemChangedArgs : InventoryEventArgs
internal InventoryItemChangedArgs(in GameInventoryItem oldItem, in GameInventoryItem newItem) internal InventoryItemChangedArgs(in GameInventoryItem oldItem, in GameInventoryItem newItem)
: base(GameInventoryEvent.Changed, newItem) : base(GameInventoryEvent.Changed, newItem)
{ {
this.OldItemState = oldItem; this.oldItemState = oldItem;
} }
/// <summary> /// <summary>
@ -29,6 +31,8 @@ public class InventoryItemChangedArgs : InventoryEventArgs
/// <summary> /// <summary>
/// Gets the state of the item from before it was changed. /// Gets the state of the item from before it was changed.
/// <remarks><em>This is a copy of the item data.</em></remarks>
/// </summary> /// </summary>
public GameInventoryItem OldItemState { get; init; } // impl note: see InventoryEventArgs.Item.
public ref readonly GameInventoryItem OldItemState => ref this.oldItemState;
} }

View file

@ -1,11 +1,8 @@
using System.Diagnostics.CodeAnalysis; namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes;
namespace Dalamud.Game.Inventory.InventoryChangeArgsTypes;
/// <summary> /// <summary>
/// Represents the data associated with an item being moved from one inventory and added to another. /// Represents the data associated with an item being moved from one inventory and added to another.
/// </summary> /// </summary>
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1206:Declaration keywords should follow order", Justification = "It literally says <access modifiers>, <static>, and then <all other keywords>. required is not an access modifier.")]
public class InventoryItemMovedArgs : InventoryEventArgs public class InventoryItemMovedArgs : InventoryEventArgs
{ {
/// <summary> /// <summary>
@ -29,7 +26,7 @@ public class InventoryItemMovedArgs : InventoryEventArgs
/// Gets the inventory this item was moved to. /// Gets the inventory this item was moved to.
/// </summary> /// </summary>
public GameInventoryType TargetInventory => this.Item.ContainerType; public GameInventoryType TargetInventory => this.Item.ContainerType;
/// <summary> /// <summary>
/// Gets the slot this item was moved from. /// Gets the slot this item was moved from.
/// </summary> /// </summary>
@ -49,7 +46,7 @@ public class InventoryItemMovedArgs : InventoryEventArgs
/// Gets the associated target event. /// Gets the associated target event.
/// </summary> /// </summary>
internal InventoryEventArgs TargetEvent { get; } internal InventoryEventArgs TargetEvent { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => public override string ToString() =>
$"<{this.Type}> (Item #{this.Item.ItemId}) from (slot {this.SourceSlot} in {this.SourceInventory}) to (slot {this.TargetSlot} in {this.TargetInventory})"; $"<{this.Type}> (Item #{this.Item.ItemId}) from (slot {this.SourceSlot} in {this.SourceInventory}) to (slot {this.TargetSlot} in {this.TargetInventory})";

View file

@ -13,12 +13,12 @@ public class InventoryItemRemovedArgs : InventoryEventArgs
: base(GameInventoryEvent.Removed, item) : base(GameInventoryEvent.Removed, item)
{ {
} }
/// <summary> /// <summary>
/// Gets the inventory this item was removed from. /// Gets the inventory this item was removed from.
/// </summary> /// </summary>
public GameInventoryType Inventory => this.Item.ContainerType; public GameInventoryType Inventory => this.Item.ContainerType;
/// <summary> /// <summary>
/// Gets the slot this item was removed from. /// Gets the slot this item was removed from.
/// </summary> /// </summary>

View file

@ -24,12 +24,12 @@ public interface IGameInventory
/// <param name="type">The event try that triggered this message.</param> /// <param name="type">The event try that triggered this message.</param>
/// <param name="data">Data for the triggered event.</param> /// <param name="data">Data for the triggered event.</param>
public delegate void InventoryChangedDelegate(GameInventoryEvent type, InventoryEventArgs data); public delegate void InventoryChangedDelegate(GameInventoryEvent type, InventoryEventArgs data);
/// <summary> /// <summary>
/// Event that is fired when the inventory has been changed. /// Event that is fired when the inventory has been changed.
/// </summary> /// </summary>
public event InventoryChangelogDelegate InventoryChanged; public event InventoryChangelogDelegate InventoryChanged;
/// <summary> /// <summary>
/// Event that is fired when the inventory has been changed, without trying to interpret two inventory slot changes /// Event that is fired when the inventory has been changed, without trying to interpret two inventory slot changes
/// as a move event as appropriate.<br /> /// as a move event as appropriate.<br />