Page MenuHomeFeedback Tracker

Recursive weight calculation logic not functioning correctly (All Platforms - Since Implementation)
Acknowledged, NormalPublic

Description

The functionality of ItemBase::UpdateWeight is fundamentally flawed.

Details

Severity
Major
Resolution
Open
Reproducibility
Always
Operating System
Windows 10 x64
Category
Scripting
Steps To Reproduce

Place items in a backpack, preferably in pouches or attachments, attach to container (or potentially leave on ground), and then restart the server.

m_Weight will no longer be calculated correctly and its impact on stamina will be excessive.

Additional Information

Whilst I understand the motivation behind the implementation of the recursive update system, specifically the desire to eliminate undesirable inventory traversal by passing the delta change in weight when an item is added/removed from inventory hierarchy, rather than recalculating all tree elements, the system breaks down due to conflicting and overlapping events calling UpdateWeight multiple times per item.

For example; a backpack attached to a persistent non-player container containing items will trigger the following events:

EntityAI::EEInit will be triggered on all items contained in the backpack, adding their values to the weight of the backpack and any parent items. For example, a single Leather Sewing Kit in the backpack weighing 1000g will have its m_Weight value set to 1000g. It will then call UpdateWeight on its parent, adding 1000g to the weight of the backpack it spawns in. Assuming a weight of 3000g for the backpack, this will set m_Weight of the backpack to 4000g. It will also add 1000+4000 to the weight of the parent container.

LeatherSewingKit m_Weight = 1000;
Backpack m_Weight = 4000;
Container m_Weight = 5000;

EntityAI::EECargoIn will then be triggered on the backpack, adding the m_Weight of the item to the backpack again. The backpack's m_Weight will then be 5000g. 5000g will then be added to the m_Weight of the container, which is now 10000g.

LeatherSewingKit m_Weight = 1000;
Backpack m_Weight = 5000;
Container m_Weight = 10000;

EntityAI::EEItemAttached will then fire on the container, gathering the backpack's m_Weight value of 5000, and then call RECURSIVE_ADD on itself, adding another 5000 to the container's m_Weight.

LeatherSewingKit m_Weight = 1000;
Backpack m_Weight = 5000;
Container m_Weight = 15000;

As you can see, the effect is cumulative and exponential as the tree depth and number of events increases.

This issue is masked from the QA process by the fact that the inspect menu interface only displays the raw calculated weight of the item itself without contents, and the inventory menu interface displays the weight as calculated by WeightUpdateType.FULL.

Players have noticed the disparity for quite literally years now, but were unable to quantify the issue. The typical "fix" for this employed by players has been to relog on a regular basis to "fix their stamina", which is ridiculous.

The system needs to be refactored to take into account which items are currently contributing to the weight values of parent items and propagate changes effectively, or re-implement a more basic on-demand hierarchical traversal system triggered by EECargoIn/Out and EEItemAttached/Detached.

Event Timeline

mdc created this task.Jul 7 2022, 7:00 AM
Geez changed the task status from New to Assigned.Jul 7 2022, 12:12 PM
Geez changed the task status from Assigned to Acknowledged.Jul 7 2022, 1:29 PM
Geez added a subscriber: Geez.

Thank you for the report mdc.
This is a known problem and it will be looked into.
Regards,
Geez

mdc added a comment.EditedJul 7 2022, 11:07 PM

Thank you for the report mdc.
This is a known problem and it will be looked into.
Regards,
Geez

Thank you.

As a temporary bandaid, the following gets weight calculation within about 5-10% of expected values in 90% of cases, while using minimal hierarchical updates.

modded class ItemBase extends InventoryItem
{
	override void UpdateWeight(WeightUpdateType updateType = WeightUpdateType.FULL, float weightAdjustment = 0)
	{
		bool loading = !m_Initialized;
		if (loading)
			if (GetHierarchyRootPlayer())
				updateType = WeightUpdateType.RECURSIVE_ADD;
			else
				updateType = WeightUpdateType.FULL;

		super.UpdateWeight(updateType, weightAdjustment);
		
		EntityAI parent;
		if (loading && !GetHierarchyRootPlayer() && Class.CastTo(parent, GetHierarchyParent()) && parent.IsInitialized() && !parent.IsInherited(PlayerBase))
			parent.UpdateWeight(WeightUpdateType.RECURSIVE_ADD, m_Weight);
	}
}