[Backpacks] Failed to call hook 'OnInventoryAmmoFind' on plugin 'ItemRetriever v0.6.1'Fixed
Failed to call hook 'OnInventoryAmmoFind' on plugin 'ItemRetriever v0.6.1' (InvalidOperationException: ContainerAdapterEnumerator was not disposed after previous use)
  at Oxide.Plugins.Backpacks+ContainerAdapterCollection.GetEnumerator () [0x0001f] in :0
at Oxide.Plugins.Backpacks+Backpack.FindAmmo (Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect, System.Boolean forItemRetriever) [0x00000] in :0
at Oxide.Plugins.Backpacks.m__2 (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect) [0x0002f] in :0
at Oxide.Plugins.ItemRetriever+SupplierManager.FindPlayerAmmo (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect, System.Boolean beforeInventory) [0x0001c] in :0
at Oxide.Plugins.ItemRetriever.FindPlayerAmmo (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect) [0x00051] in :0
at Oxide.Plugins.ItemRetriever.OnInventoryAmmoFind (PlayerInventory inventory, System.Collections.Generic.List`1[T] collect, Rust.AmmoTypes ammoType) [0x00007] in :0
at Oxide.Plugins.ItemRetriever.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x003a2] in :0
at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in :0
at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in :0
at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in :0

Can you find the first occurrence of this error in your logs (since the plugin last loaded) and provide the full stacktrace from that occurrence? The stacktrace of the first occurrence of this particular error should have additional details that will allow me to root cause the issue immediately.

Hello,

I have the same problem, it happens when I loot ammunition and it is stored directly in the backpack. The error only occurs if I already have ammo of the same type in the backpack.

The first occurrence of the error in my logs is the following:

07:10 [Error] Failed to call hook 'OnInventoryAmmoFind' on plugin 'ItemRetriever v0.6.1' (InvalidOperationException: ContainerAdapterEnumerator was not disposed after previous use)
  at Oxide.Plugins.Backpacks+ContainerAdapterCollection.GetEnumerator () [0x0001f] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.Backpacks+Backpack.FindAmmo (Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect, System.Boolean forItemRetriever) [0x00000] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.Backpacks.<RegisterAsItemSupplier>m__2 (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect) [0x0002f] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.ItemRetriever+SupplierManager.FindPlayerAmmo (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect, System.Boolean beforeInventory) [0x0001c] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.ItemRetriever.FindPlayerAmmo (BasePlayer player, Rust.AmmoTypes ammoType, System.Collections.Generic.List`1[T] collect) [0x00051] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.ItemRetriever.OnInventoryAmmoFind (PlayerInventory inventory, System.Collections.Generic.List`1[T] collect, Rust.AmmoTypes ammoType) [0x00007] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.ItemRetriever.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x003a2] in <3818ebe73fef4bd192c581c1df78bf85>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <23ba99f131254889867c71f0bd137b1d>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <157a94ee66ab4a7991faecd1eb84be3b>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <157a94ee66ab4a7991faecd1eb84be3b>:0 ​

I haven't been able to reproduce this issue yet. I've analyzed the code and can't find any way this error could actually happen. There was one case where the ContainerAdapterEnumerator was not disposed after previous use could happen in a previous version of Backpacks when using softcore mode, which was due to a bug in the code that allowed infinite recursion, but that issue was fixed, and if recursion were taking place, it would be obvious by the stacktrace, but it's not present in the stacktraces provided in this thread.

Earlier, I was thinking that recursion was happening during the first occurrence of this error, and that once it happened, the backpack got into a bad state which would cause this error from then on, so seeing the first stacktrace would reveal the root cause of the recursion. However, upon further analysis, I'm pretty sure recursion wouldn't cause the bad state I was thinking of.

For a more technical explanation, when the code wants to loop through the pages of a backpack (such as, to find ammo), the code looks something like this.

foreach (var containerAdapter in _containerAdapters)
{
    ...
}

That gets basically compiled to this:

var enumerator = _containerAdapters.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        var containerAdapter = enumerator.Current;
        ...
    }
}
finally
{
    enumerator.Dispose();
}

Because it uses a try/finally block, even if an error occurred while looping the code, the finally block is guaranteed to run, ensuring that the enumerator would be disposed anyway, hence this error doesn't seem possible.

If you can determine the exact reproduction steps, such as reload the plugin then do X to produce the error, then I can troubleshoot further. Also, if you are using any alternate framework or compiler, please share that info as well as it could be relevant.



Merged post

I was able to reproduce this issue using a plugin called Ammo HUD that one affected server was using. That plugin currently counts ammo whenever an item is added to or removed from a player inventory, if they have the HUD active, in order to update the displayed amount of reserve ammo. That plugin counts ammo using a vanilla function which Item Retriever hooks into, so it conveniently counts ammo in the Backpack as well. The issue is that when  ammo gets added to the player inventory, if the backpack auto gathers that item, that immediately triggers an Ammo HUD update which inadvertently triggers an ammo recount (Ammo HUD -> Vanilla -> Oxide -> Item Retriever -> Backpacks), which Backpacks does not expect because it's still in the middle of performing the auto gather function.

Originally, I assumed that any recursion of this sort was wrong and should not be supported, but this use case is reasonable enough, so I have developed an update to Backpacks which will resolve this issue by allowing recursion. That being said, this could also be solved in the Ammo HUD plugin by delaying the HUD update by one frame, which ideally is the direction the plugin should go, eventually toward a UI update manager like the one in Backpack Button, to either throttle or debounce the UI updates, to improve server performance when bulk item moves occur.

This has been fixed in Backpacks v3.11.7.

Locked automatically