Trying assign TMap to TMap - crashes

(If it’s wrong section, forgive me :D)
Hello everyone,

I’d appriciate any help on this matter that could lead to resolve the problem
Staright to the poin, when I trying assign TMap to TMap by using = operator it randomly(yes randomly, 50/50 it works) causes a crash with follows crash report(pasted below)

How it works is TechTree class is “discovering” new tech by using latent pending action, once it finish TechTree FinishResearching function is called then after adding new recipies to the array, other objects that are on scene have bound function to delegate that comes from TechTree, once TechTree add stuff, it calls a delegate(dynamic multicast) - then all the objects update thier recipies for crafting in ActorComponent ← So this ActorComponent is responsible to hold UObject instances of recipies in Array, but the fun begins here, when I try assign TMap = TMap(TMap is variable inside recipe) it clearly causing the problem, but I can’t understand why.

In constructor of UObject every TMap is initalized with {} and they are marked as UPROPERTY

What I been trying:
-Iterating through every single position in Map and check for validation because structure of this map is <UObject(Key), float(Value)>
-Instead of assign operator using Append function that comes from TMap(Empty Map append filled Map)

(TechTree is a LocalPlayer subsystem)

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0xffffffffffffffff

VCRUNTIME140
UE4Editor_xxx!TSet<TTuple<TSubclassOf<UResource_Base_C>,float>,TDefaultMapHashableKeyFuncs<TSubclassOf<UResource_Base_C>,float,0>,FDefaultSetAllocator>::operator=() [E:\UE_4.26\Engine\Source\Runtime\Core\Public\Containers\Set.h:342]
UE4Editor_xxx!UCraftingComponent::AddNewRecipesFromTechTree() [E:\xxx\Source\xxx\Private\CraftingComponent.cpp:417]
UE4Editor_CoreUObject!UFunction::Invoke() [D:\Build++UE4\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\Class.cpp:5588]
UE4Editor_CoreUObject!UObject::ProcessEvent() [D:\Build++UE4\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1992]
UE4Editor_xxx!TMulticastScriptDelegate::ProcessMulticastDelegate() [E:\UE_4.26\Engine\Source\Runtime\Core\Public\UObject\ScriptDelegates.h:488]
UE4Editor_xxx!UTechTreeSubsystem::ResearchTechFinished() [E:\xxx\Source\xxx\Private\TechTreeSubsystem.cpp:280]
UE4Editor_xxx!FResearchingTech::UpdateOperation() [E:\xxx\Source\xxx\Public\TechTreeSubsystem.h:248]
UE4Editor_Engine!FLatentActionManager::TickLatentActionForObject() [D:\Build++UE4\Sync\Engine\Source\Runtime\Engine\Private\LatentActionManager.cpp:185]
UE4Editor_Engine!FLatentActionManager::ProcessLatentActions() [D:\Build++UE4\Sync\Engine\Source\Runtime\Engine\Private\LatentActionManager.cpp:144]
UE4Editor_Engine!UWorld::Tick() [D:\Build++UE4\Sync\Engine\Source\Runtime\Engine\Private\LevelTick.cpp:1518]
UE4Editor_UnrealEd!UEditorEngine::Tick() [D:\Build++UE4\Sync\Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp:1720]
UE4Editor_UnrealEd!UUnrealEdEngine::Tick() [D:\Build++UE4\Sync\Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp:426]
UE4Editor!FEngineLoop::Tick() [D:\Build++UE4\Sync\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:4836]
UE4Editor!GuardedMain() [D:\Build++UE4\Sync\Engine\Source\Runtime\Launch\Private\Launch.cpp:169]
UE4Editor!GuardedMainWrapper() [D:\Build++UE4\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:137]
UE4Editor!WinMain() [D:\Build++UE4\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:268]
UE4Editor!__scrt_common_main_seh() [d:\agent_work\5\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
kernel32
ntdll

1 Like

Using UObjects as keys in arrays (even when TMap is marked as a UProperty) is dodgy. I’d look for another way of doing it.

Having said that, compile your code in debug mode and look at the array and the key values when the exception triggers a break point.

If you’re in the position where you are having to design code that maintains lists like that, it can be useful (and produce more optimal code) if you try and work out another way.

In this instance it could be more optimal (depending on your objectives) to just have your actors have a reference to the recipe maintainer class itself and not have it’s own list.

2 Likes

Hello, can you post some code snippets so we know exactly what we’re talking about here? Thank you! :sweat_smile:

1 Like

TMultiMap<FString, TSharedPtr<UCraftingRecipeSingle, ESPMode::ThreadSafe>> NewRecipesForObjects; ← this is Multimap that holds FString(What is name of object(not this generated by unreal) and SingleRecipe that Holds In/Out(As TMaps), Time to craft and CraftingDescription (below how this class is declared) after discover new recipe
(This MultiMap is existing globaly in TechTree class)

class UCraftingRecipeSingle : public UObject
{
GENERATED_BODY()
public:

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TMap<TSubclassOf<UResource_Base_C>, float> In;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TMap<TSubclassOf<UResource_Base_C>, float> Out;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
float TimeToCraft;

UPROPERTY(EditDefaultsOnly, meta=(MultiLine = true))
FText CraftingDescription;

UPROPERTY(BlueprintReadOnly)
float Progress;

UPROPERTY(BlueprintReadOnly)
bool bFinish;

UCraftingRecipeSingle()
{
	In.Empty(0);
	Out.Empty(0);
	TimeToCraft = 1.f;
	CraftingDescription = FText::FromString("No Description");
	Progress = 0.f;
	bFinish = false;
}

}

And there is code where crash occurs

if (TechSubsystem.IsValid())
{
	TArray<TSharedPtr<UCraftingRecipeSingle, ESPMode::ThreadSafe>> Recipies = TechSubsystem->GetNewRecipesForObject(OwningWarehouse->BasicDetails.ObjectName.ToString());

	for (TSharedPtr<UCraftingRecipeSingle, ESPMode::ThreadSafe> Recipe : Recipies)
	{
		if (!CraftingRecipes.ContainsByPredicate([Recipe](const UCraftingRecipeSingle* InnerRecipe)
		{
			return InnerRecipe->Equals(Recipe.Get());
		}) && Recipe->IsValidLowLevel() && OwningWarehouse->IsValidLowLevel())
		{
			UCraftingRecipeSingle* NewRecipe = NewObject<UCraftingRecipeSingle>(OwningWarehouse, NAME_None, RF_NoFlags);
			if(NewRecipe->IsValidLowLevel())
			{
				NewRecipe->In = Recipe->In;  //Here crash occurs(417 line)            
				NewRecipe->Out = Recipe->Out;  //Or here, is "random"
				NewRecipe->TimeToCraft = Recipe->TimeToCraft;
				NewRecipe->CraftingDescription = Recipe->CraftingDescription;
				CraftingRecipes.Add(NewRecipe);
			}
		}
	}
	OnAvailableCraftingUpdate.Broadcast();
}

TechTreeSubsystem is WeakObjPtr inside crafting component, SharedPtr is for MultiMap that stores FString as Key(name of object - not this generated by unreal) and SharedPtr object since MultiMap cannot be declared as UProperty
So simply code above is doing - creating NewObject from class UCraftingRecipeSingle and assigning values from currently in loop recipe, then adding it to pool of available crafts for this component, crash usualy happen when I try assign one TMap to anyother

Well, design of lists in this case is Parents->Childs multi to multi
So the structure looks like:
Head recipe → Childs
so for all Childs attached to the head recipe, head recipe become parent that is required to discover before child itself can be discovered, so for example in setup we can have 2 parents having the same childs in setup, so childs have to 2 parents to be discovered itself before child can be discovered ← This in code works flawlessly, only problem is when I try to read it later… (Configuration of TechTree list is done by using DataTables, structs after read from data table is converted into UCraftingRecipeSingle class and then held in TechTree globaly)
How messy it could sounds, it works very well unless I try to read some values from UCraftingRecipeSingle (TMap in this case)

Probably my mistake somewhere, but I can’t find it

That’s something I have never tried - are you sure a “if(!test(){return x;}) && test2) {}” doesn’t just return true all the time?

1 Like

This is definition of Equals

{

	if (!CraftingRecipe->IsValidLowLevel()) return false;

	if (!this->In.OrderIndependentCompareEqual(CraftingRecipe->In)) return false;

	if (!this->Out.OrderIndependentCompareEqual(CraftingRecipe->Out)) return false;

	if (!FMath::IsNearlyEqual(this->TimeToCraft, CraftingRecipe->TimeToCraft)) return false;

	return true;
}

this keyword means InnerRecipe and CraftingRecipe is our Recipe that we catch into lambda

But even if it returns true there are checks if Recipe itself is valid, and even if somehow it fails then empty TMap should be assigned right? Istead I getting crash, from otherhand using invalid pointer could crash but crash logs indicates
E:\UE_4.26\Engine\Source\Runtime\Core\Public\Containers\Set.h:342

whenever you do a TMap= other TMap it will be creating new objects as the keys - are you sure that’s what you wanting?

Well, in this case I wanted to create “available” crafts for this certain object that will be unchangable, and in widget for example if is clicked button with assigned recipe, we creating object based on this that counts progression, marks as finish etc.

Like that:
TechTree → MultiMap
Holds for Factory 1:
->Recipe_1
->Recipe_2
->Recipe_3
for Factory 2:
->Recipe_15
->Recipe_9
for Factory 3:
->Recipe_99

Then each crafting component have it own array holding these values, if for example we invoke delegate to update and Factory_3 will invoke his function bound to this delegate, so it will hold inside Recipe_99; same scenerio for other objects that will implement crafting, so then widgets have easy access to what can be crafted by this object

That was the idea behind this way of implementation

TechTree and CraftingComponent holds information as UCraftingRecipeSingle class that can be read by rest of functionality like displaying etc

If you set a break point just before the TMap copy code, what do the values in the original look like?

Hey sorry for late response, seems like I resolved the problem, SharedPtr for some reason doesn’t work with UObjects as I thought, instead I changed it to TWeakObjPtr and it seems like the objects holds information inside correctly now. Additionaly I’ll follow your advice and look for overall healthy solution for the system, in fact I can hold information in structures instead of objects what should be healthier for the system

1 Like