I am encountering a crash in my mod for Mount & Blade II: Bannerlord when reloading a saved game. The crash is due to a System.Collections.Generic.KeyNotFoundException, which occurs intermittently during gameplay when reloading a saved game. This issue does not appear when starting a new game or loading a save directly without reloading in-game.
To help troubleshoot the issue, I have been debugging the ItemConsumptionBehavior.MakeConsumption method and have identified the problematic line:
I used HarmonyLib to patch the method for debugging purposes. My patch attempts to handle the exception and output debug information:
Despite checking the contents of categoryDemand in the exception handling block and finding that the itemCategory matches, the categoryDemand[itemCategory] line still throws an exception. The comparison between category and itemCategory seems correct, yet it results in a KeyNotFoundException.
if (category == itemCategory)
When I see from the debugging information that category is exactly the same as itemCategory, he returns false
Please let me know if you require any additional information or if there are any suggestions on how to address this issue.
Thank you for your assistance.
- Exception Exception Information:
Type: System.Collections.Generic.KeyNotFoundException
Message: The given key was not present in the dictionary.
Stacktrace:
at TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)
at void TaleWorlds.CampaignSystem.CampaignBehaviors.ItemConsumptionBehavior.MakeConsumption(Town town, Dictionary<ItemCategory, float> categoryDemand, Dictionary<ItemCategory, int> saleLog)
at void TaleWorlds.CampaignSystem.CampaignBehaviors.ItemConsumptionBehavior.MakeConsumptionInTown(Town town, Dictionary<ItemCategory, int> saleLog)
at void TaleWorlds.CampaignSystem.MbEvent<T>.InvokeList(EventHandlerRec<T> list, T t)
at void TaleWorlds.CampaignSystem.CampaignEvents.DailyTickTown(Town town)
at void TaleWorlds.CampaignSystem.CampaignEventDispatcher.DailyTickTown(Town town)
at void TaleWorlds.CampaignSystem.CampaignPeriodicEventManager+PeriodicTicker<T>.PeriodicTickSome(double timeUnitsElapsed)
at void TaleWorlds.CampaignSystem.CampaignPeriodicEventManager.PeriodicDailyTick()
at void TaleWorlds.CampaignSystem.CampaignPeriodicEventManager.TickPeriodicEvents()
at void TaleWorlds.CampaignSystem.Campaign.Tick()
at void TaleWorlds.CampaignSystem.GameState.MapState.OnMapModeTick(float dt)
at void TaleWorlds.CampaignSystem.GameState.MapState.OnTick(float dt)
at void TaleWorlds.Core.GameStateManager.OnTick(float dt)
at void TaleWorlds.Core.Game.OnTick(float dt)
at void TaleWorlds.Core.GameManagerBase.OnTick(float dt)
at void TaleWorlds.MountAndBlade.Module.OnApplicationTick_Patch1(Module this, float dt)
To help troubleshoot the issue, I have been debugging the ItemConsumptionBehavior.MakeConsumption method and have identified the problematic line:
float demand = categoryDemand[itemCategory];
I used HarmonyLib to patch the method for debugging purposes. My patch attempts to handle the exception and output debug information:
using HarmonyLib;
using System;
using System.Collections.Generic;
using TaleWorlds.CampaignSystem.CampaignBehaviors;
using TaleWorlds.CampaignSystem.Extensions;
using TaleWorlds.CampaignSystem.Roster;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.Core;
using TaleWorlds.Library;
namespace xxxxx.Patch
{
[HarmonyPatch(typeof(ItemConsumptionBehavior), "MakeConsumption")]
public static class ItemConsumptionBehavior_Patch
{
public static bool Prefix(Town town, Dictionary<ItemCategory, float> categoryDemand, Dictionary<ItemCategory, int> saleLog)
{
saleLog.Clear();
TownMarketData marketData = town.MarketData;
ItemRoster itemRoster = town.Owner.ItemRoster;
for (int i = itemRoster.Count - 1; i >= 0; i--)
{
try
{
ItemRosterElement elementCopyAtIndex = itemRoster.GetElementCopyAtIndex(i);
ItemObject item = elementCopyAtIndex.EquipmentElement.Item;
int amount = elementCopyAtIndex.Amount;
ItemCategory itemCategory = item.GetItemCategory();
float demand = categoryDemand[itemCategory];
float num = CalculateBudget(town, demand, itemCategory);
if (num > 0.01f)
{
int price = marketData.GetPrice(item, null, false, null);
float num2 = num / (float)price;
if (num2 > (float)amount)
{
num2 = (float)amount;
}
int num3 = MBRandom.RoundRandomized(num2);
if (num3 > amount)
{
num3 = amount;
}
itemRoster.AddToCounts(elementCopyAtIndex.EquipmentElement, -num3);
categoryDemand[itemCategory] = num - num2 * (float)price;
town.ChangeGold(num3 * price);
int num4 = 0;
saleLog.TryGetValue(itemCategory, out num4);
saleLog[itemCategory] = num4 + num3;
}
}
catch (KeyNotFoundException e)
{
ItemRosterElement elementCopyAtIndex = itemRoster.GetElementCopyAtIndex(i);
ItemObject item = elementCopyAtIndex.EquipmentElement.Item;
int amount = elementCopyAtIndex.Amount;
ItemCategory itemCategory = item.GetItemCategory();
foreach (var kvp in categoryDemand)
{
ItemCategory category = kvp.Key; // 键 (ItemCategory)
if (category == itemCategory)
{
Console.WriteLine($"Category: {category}");
}
}
float demand = categoryDemand[itemCategory];
float num = CalculateBudget(town, demand, itemCategory);
}
}
return false;
}
private static float CalculateBudget(Town town, float demand, ItemCategory category)
{
return demand * MathF.Pow(town.GetItemCategoryPriceIndex(category), 0.3f);
}
}
}
Despite checking the contents of categoryDemand in the exception handling block and finding that the itemCategory matches, the categoryDemand[itemCategory] line still throws an exception. The comparison between category and itemCategory seems correct, yet it results in a KeyNotFoundException.
if (category == itemCategory)
When I see from the debugging information that category is exactly the same as itemCategory, he returns false
Please let me know if you require any additional information or if there are any suggestions on how to address this issue.
Thank you for your assistance.
Last edited: