Why is accessing this struct crashing the editor?

I have a struct that retains data on every room in the game, including the occupants of said room. I’m trying to use a TMap as an ad-hoc map of the world, and among other things I want to iterate through a room’s occupants and spawn them (if the level is loading) or despawn them (if it’s unloading). The following looks right to be, but it crashes every time DepopulateRoom() is called:

USTRUCT()
struct FRoomData{
	GENERATED_USTRUCT_BODY()

		UPROPERTY(BlueprintReadOnly)
		FString name;

		UPROPERTY(BlueprintReadOnly)
		int32 ID;
		
		UPROPERTY(BlueprintReadOnly)
		TArray<AMCharacter*> occupants;
		TMap<float, FCharacterSheet> SpawnRules; //characters FCharacterSheet who must spawn at float time
		bool operator == (const FRoomData& src) const{
			return (ID == src.ID);
		}
};


TMap<ARoom*, FRoomData> CharacterLocations; //TMap retaining references to data struct for each room in the game, organized by room 

void UPopulationManager::DepopulateRoom(ARoom* room){
	for (int i = 0; i < CharacterLocations.FindOrAdd(room).occupants.Num(); i++){ //Iterate through every actor in ARoom*, shift it from active pool to inactive pool, and delete it
		MakeInactive(CharacterLocations[room].occupants[i]->characterData->characterSheet);
		CharacterLocations[room].occupants[i]->Destroy(); //Despawn actor
	}
}

I’m not sure what I’ve done wrong here; I start iterating through CharacterLocations with TMap.FindOrAdd(key), so I know I’m not trying to access a key that doesn’t exist in the map, but I know the problem is something in DepopulateRoom.

Edited to add: here’s the crashlog, sorry for not including it originally. :slight_smile:

Unknown exception - code 00000001 (first/second chance not available)

"Fatal error: [File:D:\BuildFa

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\misc\outputdevice.cpp:354]
UE4Editor_CoreUObject!FGCCollector::HandleObjectReference() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\private\uobject\garbagecollection.cpp:409]
UE4Editor_CoreUObject!FSimpleObjectReferenceCollectorArchive::operator<<() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\public\uobject\garbagecollection.h:462]
UE4Editor_CoreUObject!UObjectProperty::SerializeItem() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\private\uobject\propertyobject.cpp:32]
UE4Editor_CoreUObject!UMapProperty::SerializeItem() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\private\uobject\propertymap.cpp:394]
UE4Editor_CoreUObject!FArchiveRealtimeGC::ProcessObjectArray() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\private\uobject\garbagecollection.cpp:868]
UE4Editor_CoreUObject!TGraphTask::ExecuteTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\public\async\taskgraphinterfaces.h:797]
UE4Editor_Core!FTaskThread::ProcessTasks() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:539]
UE4Editor_Core!FTaskThread::ProcessTasksUntilQuit() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:340]
UE4Editor_Core!FTaskThread::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:690]
UE4Editor_Core!FRunnableThreadWin::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\windows\windowsrunnablethread.cpp:74]

The specific error quoted in the text log was:

Fatal error: [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.9\Engine\Source\Runtime\CoreUObject\Private\UObject\GarbageCollection.cpp] [Line: 412]
Invalid object in GC: 0x00000096adb28100, ReferencingObject: PopulationManager /Temp/Autosaves/Game/TemporaryAssets/UEDPCPrototypeLevel.PrototypeLevel:PersistentLevel.WorldManager_C_0.Population Manager, ReferencingProperty: NULL

Since it’s GC, I assume it’s directly linked to my Destroy() call, but taking a second look at the code I quoted above I’m not sure where it would be calling Destroy() on a null pointer.

Can you post a crash log of the error it throws?

I second posting a crash log, but one thing you can try for debugging is to split some of your crazy long access chains like this:

haracterLocations.FindOrAdd(room).occupants.Num()

And this:

MakeInactive(CharacterLocations[room].occupants[i]->characterData->characterSheet);

Once you separate them you can just pop in a breakpoint or logs and figure out where it’s going awry.

If you’re unsure where your logs are, they should be in your projects UnrealEngine\Engine\Saved\Logs directory, and they should have relevant crash logging.

Sure! Sorry I didn’t think of that with the original post, it should’ve been obvious. :slight_smile: It’s edited into the original post!

I did some more tinkering and played with breakpoints, and it’s definitely this line that’s causing the crash:

CharacterLocations[room].occupants[i]->Destroy();

Given that CharacterLocations is a TMap, is there any way I can expose it in the editor? VisibleAnywhere doesn’t let me actually view the structs it’s holding, but it would be helpful if I could directly look at its values to see if it was somehow getting a null value from someone.

You have a NULL reference exception, you should check against nullptr whenever using pointer type variables.

CharacterLocations.FindOrAdd(room).occupants.Num() - Wouldnt an iterator be better than a for loop, does it invalidate an iterator if you use one?

CharacterLocations[room].occupants[i]->characterData->characterSheet - I think this needs to be broken down, is there anything NULL in here thats causing an explosive situation.

CharacterLocations[room].occupants[i]->Destroy(); - you can refactor this to reuse the line above it, theyre both pointing to the same object

Okay, I narrowed it down- apparently the TMap is empty when I run the for loop and try to iterate through its entries. I can prevent the crash by just checking if (CharacterLocations.Num()>0), but I can’t figure out why the room itself is deleting itself before my code executes. I call PopulateRoom and DepopulateRoom through BeginPlay and BeginDestroy, like this:

void ARoom::BeginDestroy()
{
	Super::BeginDestroy();

	if (worldManager != nullptr){
		worldManager->populationManager->DepopulateRoom(this);
	}
}

Am I correct that, since I’m calling the function in BeginDestroy, the instance of ARoom that calls it should still exist when DepopulateRoom executes?

Hey HInoue-

It’s likely that the call to the Super of BeginDestory is what is causing your issue. The Super is what calls the parent implementation of BeginDestory which is where everything is marked for delete and cleanup so when it reached the DepopulateRoom() call it no longer knows what to call it on. Making your call to DepopulateRoom() before calling Super::BeginDestroy() should help.

Cheers

I’m not sure… if I comment the Super out, even if the code that originally caused the crashing is also commented out, trying to run the game at all, even in a standalone window, instantly crashes UE.