Improved performance of GetItemWithChildren()
Reduced number of enumerations of `itemWithChildren` inside AddItemWithChildrenToEquipmentSlot()` by converting children to list at start of method Applied additional filtering to child items collection inside `GetContainerItemsWithChildren()`
This commit is contained in:
@@ -230,7 +230,6 @@ public static class ItemExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO: return IEnumerable and update all calling code
|
||||
/// Get an item with its attachments (children)
|
||||
/// </summary>
|
||||
/// <param name="items">List of items (item + possible children)</param>
|
||||
@@ -239,48 +238,63 @@ public static class ItemExtensions
|
||||
/// <returns>list of Item objects</returns>
|
||||
public static List<Item> GetItemWithChildren(this IEnumerable<Item> items, MongoId baseItemId, bool excludeStoredItems = false)
|
||||
{
|
||||
// Use dictionary to make key lookup faster, convert to list before being returned
|
||||
// Convert to list if not already
|
||||
var itemList = items.ToList();
|
||||
OrderedDictionary<MongoId, Item> result = [];
|
||||
|
||||
// Find desired root item
|
||||
var desiredRootItem = itemList.FirstOrDefault(item => item.Id == baseItemId);
|
||||
if (desiredRootItem is null)
|
||||
// Create dict of items by parentId
|
||||
var childrenByParent = new Dictionary<string, List<Item>>(itemList.Count);
|
||||
foreach (var child in itemList)
|
||||
{
|
||||
var key = child.ParentId;
|
||||
if (key is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (childrenByParent.TryGetValue(key, out var list))
|
||||
{
|
||||
list.Add(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenByParent[key] = [child];
|
||||
}
|
||||
}
|
||||
|
||||
// Find root item
|
||||
var root = itemList.FirstOrDefault(i => i.Id == baseItemId);
|
||||
if (root is null)
|
||||
{
|
||||
// Root not found, nothing to return, exit
|
||||
return [];
|
||||
}
|
||||
result.Add(desiredRootItem.Id, desiredRootItem);
|
||||
var rootItemIdString = desiredRootItem.Id.ToString();
|
||||
|
||||
foreach (var item in itemList)
|
||||
var result = new List<Item>();
|
||||
var stack = new Stack<Item>();
|
||||
stack.Push(root);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
if (result.ContainsKey(item.Id))
|
||||
var current = stack.Pop();
|
||||
result.Add(current);
|
||||
|
||||
if (!childrenByParent.TryGetValue(current.Id.ToString(), out var children))
|
||||
{
|
||||
// Already processed, skip
|
||||
// No children, skip to next
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip items with different parentId
|
||||
if (item.ParentId != rootItemIdString)
|
||||
foreach (var child in children)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is stored in parent and disallowed
|
||||
if (excludeStoredItems && item.Location is not null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Item may have children, check
|
||||
foreach (var subItem in GetItemWithChildren(itemList, item.Id))
|
||||
{
|
||||
result.Add(subItem.Id, subItem);
|
||||
// child item has a location property = is stored inside parent
|
||||
if (excludeStoredItems && child.Location is not null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
stack.Push(child);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Values.ToList();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -435,7 +435,7 @@ public class BotGeneratorHelper(
|
||||
/// <param name="rootItemTplId">Root items tpl id</param>
|
||||
/// <param name="itemWithChildren">Item to add</param>
|
||||
/// <param name="inventory">Inventory to add item+children into</param>
|
||||
/// <param name="containersIdFull"></param>
|
||||
/// <param name="containersIdFull">Container Ids with no space for more items</param>
|
||||
/// <returns>ItemAddedResult result object</returns>
|
||||
public ItemAddedResult AddItemWithChildrenToEquipmentSlot(
|
||||
HashSet<EquipmentSlots> equipmentSlots,
|
||||
@@ -446,6 +446,8 @@ public class BotGeneratorHelper(
|
||||
HashSet<string>? containersIdFull = null
|
||||
)
|
||||
{
|
||||
var itemWithChildrenList = itemWithChildren.ToList();
|
||||
|
||||
// Track how many containers are unable to be found
|
||||
var missingContainerCount = 0;
|
||||
foreach (var equipmentSlotId in equipmentSlots)
|
||||
@@ -455,8 +457,8 @@ public class BotGeneratorHelper(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get container to put item into
|
||||
var container = inventory.Items.FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
|
||||
// Get container from inventory to put item into
|
||||
var container = inventory.Items?.FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
|
||||
if (container is null)
|
||||
{
|
||||
missingContainerCount++;
|
||||
@@ -466,7 +468,7 @@ public class BotGeneratorHelper(
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug(
|
||||
$"Unable to add item: {itemWithChildren.FirstOrDefault()?.Template} to bot as it lacks the following containers: {string.Join(",", equipmentSlots)}"
|
||||
$"Unable to add item: {itemWithChildrenList.FirstOrDefault()?.Template} to bot as it lacks the following containers: {string.Join(",", equipmentSlots)}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -494,7 +496,7 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
// Get x/y grid size of item
|
||||
var (itemWidth, itemHeight) = inventoryHelper.GetItemSize(rootItemTplId, rootItemId, itemWithChildren);
|
||||
var (itemWidth, itemHeight) = inventoryHelper.GetItemSize(rootItemTplId, rootItemId, itemWithChildrenList);
|
||||
|
||||
// Iterate over each grid in the container and look for a big enough space for the item to be placed in
|
||||
var currentGridCount = 1;
|
||||
@@ -520,7 +522,7 @@ public class BotGeneratorHelper(
|
||||
);
|
||||
|
||||
// Get root items in container we can iterate over to find out what space is free
|
||||
var containerItemsToCheck = existingContainerItems.Where(x => x.SlotId == slotGrid.Name).ToList();
|
||||
var containerItemsToCheck = existingContainerItems.Where(x => x.SlotId == slotGrid.Name);
|
||||
var containerItemsWithChildren = GetContainerItemsWithChildren(containerItemsToCheck, inventory.Items);
|
||||
|
||||
if (slotGrid.Props is not null)
|
||||
@@ -539,7 +541,7 @@ public class BotGeneratorHelper(
|
||||
// Free slot found, add item
|
||||
if (findSlotResult.Success ?? false)
|
||||
{
|
||||
var parentItem = itemWithChildren.FirstOrDefault(i => i.Id == rootItemId);
|
||||
var parentItem = itemWithChildrenList.FirstOrDefault(i => i.Id == rootItemId);
|
||||
|
||||
// Set items parent to container id
|
||||
if (parentItem is not null)
|
||||
@@ -554,7 +556,7 @@ public class BotGeneratorHelper(
|
||||
};
|
||||
}
|
||||
|
||||
(inventory.Items ?? []).AddRange(itemWithChildren);
|
||||
(inventory.Items ?? []).AddRange(itemWithChildrenList);
|
||||
|
||||
return ItemAddedResult.SUCCESS;
|
||||
}
|
||||
@@ -570,7 +572,7 @@ public class BotGeneratorHelper(
|
||||
// No space in this grid, move to next container grid and try again
|
||||
}
|
||||
|
||||
// if we got to this point, the item couldn't be placed on the container
|
||||
// If we got to this point, the item couldn't be placed on the container
|
||||
if (containersIdFull is null)
|
||||
{
|
||||
continue;
|
||||
@@ -608,14 +610,14 @@ public class BotGeneratorHelper(
|
||||
return result;
|
||||
}
|
||||
|
||||
// Filter out all items without location prop, (child items)
|
||||
var itemsWithoutLocation = inventoryItems.Where(item => item.Location is null);
|
||||
// Get collection of items likely to be children of root items
|
||||
var itemsWithoutLocation = inventoryItems.Where(item => item.Location is null && item.ParentId is not null).ToList();
|
||||
foreach (var rootItem in containerRootItems)
|
||||
{
|
||||
// Check item in container for children, store for later insertion into `containerItemsToCheck`
|
||||
// (used later when figuring out how much space weapon takes up)
|
||||
List<Item> itemsToFilter = [.. itemsWithoutLocation, rootItem];
|
||||
var itemWithChildItems = itemsToFilter.GetItemWithChildren(rootItem.Id);
|
||||
itemsWithoutLocation.Insert(0, rootItem);
|
||||
var itemWithChildItems = itemsWithoutLocation.GetItemWithChildren(rootItem.Id);
|
||||
|
||||
// Item had children, replace existing data with item + its children
|
||||
result.AddRange(itemWithChildItems);
|
||||
|
||||
@@ -63,7 +63,12 @@ public class ItemTests
|
||||
public void GetItemWithChildren_mods_and_inventory_item()
|
||||
{
|
||||
var testData = new List<Item>();
|
||||
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
|
||||
var rootItem = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND,
|
||||
ParentId = new MongoId(),
|
||||
};
|
||||
var childItem = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
@@ -83,7 +88,8 @@ public class ItemTests
|
||||
|
||||
var result = testData.GetItemWithChildren(rootItem.Id, false);
|
||||
|
||||
Assert.AreEqual(result[1].Id, childItem.Id);
|
||||
Assert.Contains(childItem, result);
|
||||
Assert.Contains(childItem2, result);
|
||||
Assert.AreEqual(result.Count, 3);
|
||||
}
|
||||
|
||||
@@ -292,4 +298,30 @@ public class ItemTests
|
||||
Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item2Id).Upd.SpawnedInSession);
|
||||
Assert.AreEqual(true, profile.Inventory.Items.FirstOrDefault(item => item.Id == item3Id).Upd.SpawnedInSession);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItemWithChildren_rootIdNotFound()
|
||||
{
|
||||
var testData = new List<Item>();
|
||||
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
|
||||
var childItem = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_127X33_COPPER,
|
||||
ParentId = rootItem.Id,
|
||||
};
|
||||
var childOfChild = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_26X75_GREEN,
|
||||
ParentId = childItem.Id,
|
||||
};
|
||||
testData.Add(rootItem);
|
||||
testData.Add(childItem);
|
||||
testData.Add(childOfChild);
|
||||
|
||||
var result = testData.GetItemWithChildren(new MongoId(), true);
|
||||
|
||||
Assert.AreEqual(result.Count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user