World object cleanup crashes the game

Try this: just before destroying the AI, call spider->RemoveFromRoot();

In the game I am working on, one of the types of AI I have has group behavior. When the AI is spawned it is added to a group which is managed by a controller for the group running in its own thread. It was working great, except for one problem where if if the state of the application changed while enemies were still alive (player died, application was exited etc), then the game would crash because Unreal garbage collected the individual AI instances before the controller had a chance to properly stop and shut down the thread (so the thread briefly continued to run and tried to access instances which no longer existed). So, I tried to stop the garbage collection for this type of object by calling

SetFlags(RF_RootSet)

However, now whenever I try to run the game from visual studio, whenever one of these state changes happens, visual studio comes up with an exception message saying “ue4editor.exe has triggered a breakpoint”. It triggers on

void UWorld::MarkObjectsPendingKill()
{
	auto MarkObjectPendingKill = [](UObject* Object)
	{
		Object->MarkPendingKill();  // Crashes right here
	};
	ForEachObjectWithOuter(this, MarkObjectPendingKill, true, RF_PendingKill);
}

And provides the following error output

Assertion failed: !IsRooted() [File:d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\coreuobject\public\uobject\UObjectBaseUtility.h] [Line: 83] 

UE4Editor.exe has triggered a breakpoint.

If I run the game from the editor, it straight up crashes the editor. Does anybody know why this is happening and what I need to do to fix it. The code that the individual AI instances go through in the controller is below. Any help would be much appreciated.

void GroupSpiderMobAIController::RegisterSpider(ASpiderMobAI* spider)
{
	spider->SetFlags(RF_RootSet);
	bool spiderAdded = false;
	if (controllers == nullptr) {
		controllers = new TArray<GroupSpiderMobAIController*>();
	}
	for (auto Iter(controllers->CreateIterator()); Iter; Iter++)
	{
		if ((*Iter)->spiderInstances < 3)
		{
			(*Iter)->managedSpiders->Add(spider);
			(*Iter)->spiderInstances++;
			spiderAdded = true;
			spider->SetMyGroup((*Iter));
			break;
		}
	}
	if (spiderAdded == false)
	{
		GroupSpiderMobAIController *newController = new GroupSpiderMobAIController();
		newController->spiderInstances++;
		spider->SetMyGroup(newController);
		newController->managedSpiders->Add(spider);
	}
}

void GroupSpiderMobAIController::RemoveSpider(ASpiderMobAI* spider)
{
	GroupSpiderMobAIController* spiderGroup = spider->GetMyGroup();
	spiderGroup->managedSpiders->Remove(spider);
	spiderGroup->spiderInstances--;
	DestroySpider(spider);
	if (spiderGroup->spiderInstances == 0)
	{
		//free(spiderGroup->managedSpiders);
		spiderGroup->Shutdown();
	}
}

void GroupSpiderMobAIController::DestroySpider(ASpiderMobAI* spider)
{
	if (!spider || !spider->IsValidLowLevel())
	{
		return;
	}
	if (!spider->IsPendingKill())
	{
		spider->RemoveFromRoot();
		spider->Destroy();
		spider->ConditionalBeginDestroy();
	}
}

void GroupSpiderMobAIController::Cleanup()  //A manual cleanup function that I call on the level's end play notification. It has resulted on this crash not happening a few times (but it still crashes most of the time).
{
	for (auto Iter(controllers->CreateIterator()); Iter; Iter++)
	{
		for (auto Iter2((*Iter)->managedSpiders->CreateIterator()); Iter2; Iter2++)
		{
			DestroySpider((*Iter2));
		}
		(*Iter)->Shutdown();
	}
}

void GroupSpiderMobAIController::Shutdown()
{
	if (Runnable)
	{
		Runnable->EnsureCompletion();
		delete Runnable;
		Runnable = NULL;
		controllers->Remove(this);
	}
}

Edit:

Changed the question and the code to reflect further work on the problem and a slightly clearer understanding of what the error actually is.

No luck, still get the same error.

Don’t know why you deleted last quastion, where anwser i writen for it, i guess this fits to this too

In order to prevent random crashes, the UE4 code is containse lot of assert checks, which is check() function and boolean condition in argument (like if), if condition is not true then it crash the engine and logs “Assertion failed” with condition that failed (some has custom messages, but to make things faster it simply shows condition). It does that as code design guarranty that software will be stable if those assert conditions are not meet, in other words it better to crash engine now, then have random harder to debug and without error message crash later.

So in your case UE4 code somewhere checked if your actor is not rooted (rooted most likely means RF_RootSet, which you probably used to prevent actor to be cleaned by GC), it probably is related to fact that RF_PendingKill makes object to be cleaned by GC now, which creates conflicting status of object by marked to be killed and order to not kill it in same time. So unroot your actor and then do pending kill… but i would simply use Destroy()

I don’t know why you really want to mark actor to not be cleaned, actor won’t be collected as long as world that this actor is in exists and when world is destroyed all actors in it are deleted anyway