[Bug] Error when setting index through state on pickup in Module State Switcher

Cheo

Active member
Hello, this is yet again one of those issues that may sound a little niche and complicated at first but please bear with me !

So I have a component on my character that activates a list of states on start, and one of them is meant to activate an index preset on some weapons' Module State Switcher modules. Here is a simple example with the state Index0 enabling a preset that sets the module's index to 0 :

Capture d’écran (2473).png

If the weapon is already part of the character's loadout, then there's nothing wrong. However if the weapon is obtained through a pickup or manually through code, then we get this error :
 
C#:
[Exception] InvalidOperationException: Collection was modified; enumeration operation may not execute.
System.Collections.Generic.HashSet`1+Enumerator[T].MoveNext() at <a790f10e63dd4341b206460141e07e75>:0

StateManager.InitializeInternal() at /Opsive/Shared/StateSystem/StateManager.cs:157
 155:  if (m_ActiveCharacterStates.TryGetValue(characterGameObject, out var activeStates)) {
 156:      if (activeStates.Count > 0) {
--> 157:          foreach (var stateName in activeStates) {
 158:              SetState(stateGameObject, stateName, true);
 159:          }

StateManager.Initialize() at /Opsive/Shared/StateSystem/StateManager.cs:64
  62:  public static void Initialize(GameObject gameObject, IStateOwner owner, State[] states)
  63:  {
-->  64:      Instance.InitializeInternal(gameObject, owner, states);
  65:  }

StateObject.Initialize() at /Opsive/Shared/StateSystem/StateObject.cs:35
  33:      m_StateBoundGameObject = gameObject;
  34:      if (Application.isPlaying && gameObject != null) {
-->  35:          StateManager.Initialize(gameObject, this, m_States);
  36:      }
  37:  }

BoundStateObject.Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Bindings/StateObjectBinding.cs:33
  31:  public override void Initialize(GameObject gameObject)
  32:  {
-->  33:      base.Initialize(gameObject);
  34:      InitializeStateObjectBindings();
  35:  }

ActionModule.Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/ActionModule.cs:177
 176:  // Initialize the states.
--> 177:  Initialize(GameObject);
 178:  InitializeInternal();

ActionModule.Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/ActionModule.cs:161
 160:      m_ModuleGroup = moduleGroup;
--> 161:      Initialize(itemAction);
 162:  }

Opsive.UltimateCharacterController.Items.Actions.Modules.ActionModuleGroup`1[T].OnModuleAdded() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/ActionModuleGroup.cs:502
 500:  // Only initialize the module at runtime.
 501:  if (Application.isPlaying) {
--> 502:      module.Initialize(m_CharacterItemAction, this);
 503:  }

Opsive.UltimateCharacterController.Items.Actions.Modules.ActionModuleGroup`1[T].Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/Modules/ActionModuleGroup.cs:326
 324:              continue;
 325:          }
--> 326:          OnModuleAdded(module);
 327:      }
 328:  }

CharacterItemAction.InitializeModuleGroups() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/CharacterItemAction.cs:222
 220:  GetAllModuleGroups(tempModuleGroupList);
 221:  for (int i = 0; i < tempModuleGroupList.Count; i++) {
--> 222:      tempModuleGroupList[i].Initialize(this);
 223:  }

CharacterItemAction.InitializeAction() at /Opsive/UltimateCharacterController/Scripts/Items/Actions/CharacterItemAction.cs:149
 148:  InitializeActionInternal(force);
--> 149:  InitializeModuleGroups(force);
 151:  m_IsInitialized = true;

CharacterItem.Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/CharacterItem.cs:286
 284:  for (int i = 0; i < m_ItemActions.Length; ++i) {
 285:      m_IDItemActionMap.Add(m_ItemActions[i].ID, m_ItemActions[i]);
--> 286:      m_ItemActions[i].InitializeAction(false);
 287:  }

CharacterItem.Initialize() at /Opsive/UltimateCharacterController/Scripts/Items/CharacterItem.cs:367
 365:          m_Inventory.OnCharacterItemStartInitializing(this);
 366:      } else {
--> 367:          Initialize(false); 
 368:      }
 369:  }

InventoryBase.SpawnCharacterItem() at /Opsive/UltimateCharacterController/Scripts/Inventory/InventoryBase.cs:871
 870:  var instancedCharacterItem = itemGameObject.GetComponent<CharacterItem>();
--> 871:  instancedCharacterItem.Initialize(itemIdentifier);
 873:  characterItemPrefab.gameObject.SetActive(previousActiveState);

InventoryBase.SpawnItemIdentifiersCharacterItem() at /Opsive/UltimateCharacterController/Scripts/Inventory/InventoryBase.cs:781
 780:  if (!foundAvailableCharacterItem) {
--> 781:      SpawnCharacterItem(characterItemPrefab, itemIdentifier);
 782:  }

InventoryBase.AddItemIdentifierAmount() at /Opsive/UltimateCharacterController/Scripts/Inventory/InventoryBase.cs:591
 589:  // If the item is added when it wasn't part of the inventory before.
 590:  if (spawnCharacterItems && newAmount > 0) {
--> 591:      SpawnItemIdentifiersCharacterItem(itemIdentifier, slotID);
 592:  }

InventoryBase.PickupItem() at /Opsive/UltimateCharacterController/Scripts/Inventory/InventoryBase.cs:642
 640:  }
--> 642:  var addedAmount = AddItemIdentifierAmount(itemIdentifier, amount, spawnCharacterItems, slotID);
 643:  var pickedUp = addedAmount != 0;

InventoryBase.AddItemIdentifierAmount() at /Opsive/UltimateCharacterController/Scripts/Inventory/InventoryBase.cs:563
 561:  {
 562:      if (m_AutoSpawnDestroyRuntimeCharacterItems) {
--> 563:          return PickupItem(itemIdentifier, -1, amount, GetCharacterItem(itemIdentifier) == null, false, true, m_AutoSpawnDestroyRuntimeCharacterItems);
 564:      } else {
 565:          return AddItemIdentifierAmount(itemIdentifier, amount, m_AutoSpawnDestroyRuntimeCharacterItems);

CharacterInventoryBridge.OnAddItemToInventory() at /Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/CharacterInventoryBridge.cs:400
 398:  #endif
--> 400:              base.AddItemIdentifierAmount(itemInfo.Item, itemInfo.Amount);
 401:          }

Opsive.Shared.Events.InvokableAction`2[T1,T2].Invoke() at <52329c5bb9d24a17aa3c9cbeab267298>:0

EventHandler.ExecuteEvent[T1,T2]() at <52329c5bb9d24a17aa3c9cbeab267298>:0

ItemCollection.NotifyAdd() at /Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemCollection.cs:477
 475:  {
 476:      if (m_Inventory != null && Application.isPlaying) {
--> 477:          EventHandler.ExecuteEvent<ItemInfo, ItemStack>(m_Inventory,
 478:              EventNames.c_Inventory_OnAdd_ItemInfo_ItemStack,
 479:              itemInfo, addedItemStack);

ItemSlotCollection.SetItemAmount() at /Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemSlotCollection.cs:300
 298:  m_ItemsBySlot[slotIndex] = itemInfoAdded.ItemStack;
--> 300:  NotifyAdd(itemToAdd, itemInfoAdded.ItemStack);
 302:  return itemInfoAdded;

ItemSlotCollection.AddItemInternal() at /Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemSlotCollection.cs:250
 248:  }
--> 250:  var setItemInfo = SetItemAmount((amount, itemInfo), slotIndex, true);
 251:  if (setItemInfo.HasValue) {
 252:      return setItemInfo.Value;

ItemSlotCollection.AddItem() at /Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemSlotCollection.cs:216
 214:  public ItemInfo AddItem(ItemInfo itemInfo, int slotIndex)
 215:  {
--> 216:      var itemInfoAdded = AddItemInternal(itemInfo, slotIndex);
 218:      if (itemInfoAdded.Amount < itemInfo.Amount) {

CharacterInventoryBridge.MoveItemToEquippable() at /Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/CharacterInventoryBridge.cs:937
 935:  }
--> 937:  movedItemInfo = equippableSlotCollection.AddItem(itemInfo, slotIndex);
 939:  if (previousItemInSlot.Item != null) {

CharacterInventoryBridge.MoveEquip() at /Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/CharacterInventoryBridge.cs:1017
1015:  {
1016:      if (equip) {
-->1017:          MoveItemToEquippable(itemInfo, equippableItemCollectionSet, slotID);
1018:          Equip(itemInfo,true);
1019:      } else {

CharacterInventoryBridge.MoveEquip() at /Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/CharacterInventoryBridge.cs:1004
1002:  public void MoveEquip(ItemInfo itemInfo, bool equip)
1003:  {
-->1004:      MoveEquip(itemInfo, 0, -1, equip);
1005:  }

InventoryItemPickup.DoItemIdentifierPickupInternal() at /Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/Pickup/InventoryItemPickup.cs:116
 114:      itemInfo = inventorySystemInventory.AddItem(itemInfo);
 115:      if (m_EquipOnPickup && bridgeInventory.EquippableCategory.InherentlyContains(itemInfo.Item)) {
--> 116:          bridgeInventory.MoveEquip(itemInfo,true);
 117:      }
 118:  }

ItemPickupBase.DoItemIdentifierPickup() at /Opsive/UltimateCharacterController/Scripts/Objects/CharacterAssist/ItemPickupBase.cs:136
 134:  {
 135:      EventHandler.ExecuteEvent(character, "OnItemPickupStartPickup");
--> 136:      var result = DoItemIdentifierPickupInternal(character, inventory, slotID, immediatePickup, forceEquip);
 137:      EventHandler.ExecuteEvent(character, "OnItemPickupStopPickup");
 138:      return result;

Pickup.DoPickup() at /Opsive/UltimateCharacterController/Scripts/Character/Abilities/Pickup.cs:332
 330:      }
--> 332:      m_ItemPickup.DoItemIdentifierPickup(m_GameObject, m_Inventory, m_SlotID, true, true);
 333:  } else {
 334:      var objectPickup = m_DetectedObject.GetCachedComponent<IObjectPickup>();

AnimationEventTrigger.NotifyEventListeners() at /Opsive/UltimateCharacterController/Scripts/Utility/AnimationEventTrigger.cs:169
 168:      m_IsWaiting = false;
--> 169:      OnEvent?.Invoke();
 170:  }

AnimationEventTrigger.InvokeScheduledEvent() at /Opsive/UltimateCharacterController/Scripts/Utility/AnimationEventTrigger.cs:156
 155:      OnScheduledEvent?.Invoke();
--> 156:      NotifyEventListeners();
 157:  }

ScheduledEvent.Invoke() at <5be52286c54c47958ace04af73911405>:0

SchedulerBase.Invoke() at <5be52286c54c47958ace04af73911405>:0

SchedulerBase.FixedUpdate() at <5be52286c54c47958ace04af73911405>:0

As you can see in the screenshot I wrote some random state names that don't match anything, but that doesn't change anything, there seems to be an initialization issue. Because of that I need to find a workaround for the modules I want to use. Hope this can be taken a look at, thanks in advance.
 
I started to look at this one and it's a bit complex to reproduce. Can you send a small repro package to support@opsive.com so I can take a look at the cause? Thank you!
 
For anyone reading this, Justin made a fix to the State Manager, the issue should be solved for the next update !
 
Top