Crash on accessing a TMap

I’m writing a plugin which connects NavVolume actors together so that they can be navigated between. To do this, I’m making a navigator actor class, which has a TMap with the NavVolume-NavVolume connections (as UObject) as the Key and the cost to travel the connection as the TMap Element.

My project is set up such that I have:
One Navigator actor which contains the connections and their costs in a:

UPROPERTY(SaveGame)
TMap<UNavEdge*, float> edgeMap;

where UNavEdge is a UObject containing a:

UPROPERTY(SaveGame)
AVASFVVolume* edgeTuple[2];

All this seems to work as I wanted, and once I’ve added a number of connections to the TMap, I can see them represented by some debug lines between the volumes in the editor.
However, when I save the level and quit the editor, and then load it all back up: I can see the debug lines fine still.

If I then want to change the TMap, I call reset on it first, because I want to start with a blank TMap. However, then when I call .Add() to add my new connection to the TMap, I get a Exception thrown: read access violation. this was 0x3B0.

I have a debug log function which prints out each key and element of the TMap (via a CreateConstIterator), but this also crashes with:

Exception thrown at 0x00007FFFC93C28B9 (UE4Editor-VASFVPlugin.dll) in UE4Editor.exe: 0xC0000005: Access violation reading location 0x00000000000003D8.

Am I not using .Reset correctly?

That error means that you are trying to access a nullptr, that is a pointer that has not been set to anything. As such you have to check if edgeMap is not null before using it. Then you have to find out how it got there as a nullptr.

From the TMap documentation:

The Reset function acts like a Empty() call but does not free the memory that was previously used by the elements:

FruitMap.Reset();
// FruitMap == [<invalid>, <invalid>, <invalid>]

So when you try to iterate over your reset TMap, you are deferencing a null pointer (the key). This should account for the second error you listed. To fix this, use Empty() instead. You can pass an optional parameter to it that lets you set slack for your TMap to avoid reallocation. See the documentation linked above.

As for your first error, it might have something to do with the invalid memory from Reset() as well. See if Empty() fixes that problem too.

EDIT: this was only part of the solution. See continuing conversation below.

Hi thanks!

I changed to .Empty(), but I still get crashes when trying to access the TMap. I’ve simplified it down and now just log out the .Num() of the emptied TMap, which crashes with:
Exception thrown: read access violation.
this was nullptr.

The same happens if I change back to using Reset(), though I see why you suggested Empty and I think you’re correct with that given my scenario

I can use .Empty fine as long as I am still in the same session. It’s when I save the map, quit the editor and reload it all that I’m getting the crash when I call my functions. So something relevant must be happening when I re-load, or something important isn’t being saved?

Actually, if I temporarily remove the .Empty entirely, I still get the access violation crash in a loaded level.

Right now I’m totally confused HA!

I replaced a call from a different class which was accessing the TMap and calling .Empty on it. Now the navigator class has a public function to clear its TMap.

I can call that clearing function fine, and it uses .Empty to clear the TMap. Before and after the .Empty the function prints out to debug the .Num of the TMap, and those show the size goes down to 0. Great!

Later, a function ‘Connect’ in the navigator class gets called which takes two navigation volumes and will try to add them to the TMap. At the beginning of that function I again print out the .Num of the TMap, and I get the read access violation then! Even though the Num was accessed fine earlier in the stack.

ResetEdgeMap(), where this is fine

And later in the same call stack, Connect(), where this is null and terrible :stuck_out_tongue:

Looking at your screenshots (love the titles btw), I’m not sure the problem is with your TMap. The root of the problem seems to be your instance of AVolumeNavigator becoming null. Somewhere between calling ResetEdgeMap and Connect, something happens to your AVolumeNavigator object that causes it to become null. Since it becomes null, it’s impossible to read the edgeMap. Where are you calling these functions? What are you doing between calling ResetEdgeMap and Connect?

I’m not sure if the text will come out at this resolution, but here’s a vid of the execution: TMap Crash - YouTube

From my plugin button I’m calling:
FVASFVPluginEdModeToolkit::CreateNavGraphButtonClicked() which calls:
UPluginSettings::CreateNavGraph() - which calls:
AVolumeNavigator::ResetEdgeMap() - which works fine and empties the TMap and returns void.
And then CreateNavGraph iterates through a manager actor class and calls:
AVolumeManager::CreateNavGraph() on each manager.
Each manager (theres only one in the video level) iterates through the volumes it manages, and if that volume has another volume in its TArray of connections:
AVolumeNavigator::Connect() is called with the volume, and its connected volume as parameters.

That final function is where the editor crashes, saying that AVolumeNavigator is null

here’s a code dump if you can help!
FVASFVPluginEdModeToolkit http://pastebin.com/sZN7w7Bx
UPluginSettings http://pastebin.com/sr0YQgRt
AVolumeNavigator http://pastebin.com/mihHRdVZ
AVolumeManager http://pastebin.com/b1gdeLvM

I really can’t see how AVolumeNavigator becomes null in this situation
:frowning:

I think the problem is that you’re looking at two different members, both named volumeNavigator. The one in the plugin settings is valid, which is why you’re able to call ResetEdgeMap on it without issue. However, the volumeNavigator that’s a member of AVolumeManager is not valid, which is why Connect fails. From what I can tell, the one in UPluginSettings is being set in InitialisePluginSettings, I assume when you load the plugin. For whatever reason, the current volume manager does not have a valid volumeNavigator.

Thanks so much Geoffrey! I found that the pointer to volumeNavigator in AVolumeManager.h was not set to be a UPROPERTY(SaveGame) from your advice!