Actor spawned to streaming level crashes in nav tick on set level invisible

Setup:

UE 4.12.5

Spawning actors (enemies) into streaming level “global_gameplay”

our ‘pause game’ sets the “global_gameplay” level invisible

see callstack below

reason:
when a level is set invisible, it’s World is set to nullptr

(see UWorld::UpdateLevelStreamingInner in World.cpp ln~2507)

we put our navmesh in the Persistent Level (see forum post here)

The navmesh updates the engine-created nav components. Since it is in the persistent level that is still visible and updating, it tries to tick, accessing the components owned by the enemy actors. When those access GetWorld(), it reaches through via: path component → enemy actor → level → nullptr

crash!

note: we set the active levels invisible instead of using game pause because VR still needs to have entire systems and gameplay still active, even during menus - and especially because latent blueprint nodes (like level streaming) will not execute while paused

callstack:

UE4Editor_Engine!UWorld::GetTimerManager() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\classes\engine\world.h:2780]
UE4Editor_AIModule!UPathFollowingComponent::AbortMove() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\aimodule\private\navigation\pathfollowingcomponent.cpp:333]
UE4Editor_AIModule!UAITask_MoveTo::FinishMoveTask() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\aimodule\private\tasks\aitask_moveto.cpp:67]
UE4Editor_AIModule!UAITask_MoveTo::OnPathEvent() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\aimodule\private\tasks\aitask_moveto.cpp:303]
UE4Editor_AIModule!TBaseUObjectMethodDelegateInstance<0,UAITask_MoveTo,void __cdecl(FNavigationPath * __ptr64,enum ENavPathEvent::Type)>::ExecuteIfSafe() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\public\delegates\delegateinstancesimpl_variadics.inl:852]
UE4Editor_Engine!TBaseMulticastDelegate<void,FNavigationPath * __ptr64,enum ENavPathEvent::Type>::Broadcast() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\public\delegates\delegatesignatureimpl_variadics.inl:921]
UE4Editor_Engine!ANavigationData::TickActor() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\ai\navigation\navigationdata.cpp:296]
UE4Editor_Engine!FActorTickFunction::ExecuteTick() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\actor.cpp:111]
UE4Editor_Engine!FTickFunctionTask::DoTask() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\ticktaskmanager.cpp:262]
UE4Editor_Engine!TGraphTask<FTickFunctionTask>::ExecuteTask() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\public\async\taskgraphinterfaces.h:999]
UE4Editor_Core!FNamedTaskThread::ProcessTasksNamedThread() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\private\async\taskgraph.cpp:932]
UE4Editor_Core!FNamedTaskThread::ProcessTasksUntilQuit() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\private\async\taskgraph.cpp:679]
UE4Editor_Core!FTaskGraphImplementation::WaitUntilTasksComplete() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\core\private\async\taskgraph.cpp:1776]
UE4Editor_Engine!FTickTaskSequencer::ReleaseTickGroup() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\ticktaskmanager.cpp:530]
UE4Editor_Engine!FTickTaskManager::RunTickGroup() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\ticktaskmanager.cpp:1432]
UE4Editor_Engine!UWorld::RunTickGroup() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\leveltick.cpp:704]
UE4Editor_Engine!UWorld::Tick() [d:\build\++ue4+release-4.12+compile\sync\engine\source\runtime\engine\private\leveltick.cpp:1197]

I’ve created a workaround function that I call to fix up issues with this pause method:

for this particular crash, I have to manually pause/unpause the nav mesh:

for (TActorIterator<ARecastNavMesh> ActorItr(GetWorld()); ActorItr; ++ActorItr)
{
	ARecastNavMesh* recastNav = *ActorItr;
	recastNav->SetActorTickEnabled(!inSetPaused);
}

edit: needed to add some additional handling to fix followup rare crashes on unpause

for(auto enemy : enemyList)
{
	AAIController *aic = static_cast<AAIController*>(enemy->GetController());
	aic->StopMovement();
	aic->GetGameplayTasksComponent()->EndAllResourceConsumingTasksOwnedBy(*aic);

	// plus some logic to prevent them from restarting a move before the pause actually takes hold, and to reinitialize movement behavior when returning from pause
}

Hi VictorRachels,

To be clear, you have resolved the crash you are experiencing on your end?

sorry, no, what I have is a workaround, not a resolution. There is still a bug in the nav ticking system that causes a crash.
If I still wanted other actors to continue navigating, the workaround above would not be sufficient.

Can you post the full crash logs here? They can be found at \Unreal Projects\PROJECTNAME\saved\logs. Additionally, are you using blueprints or c++ for your project? Have you tried adding an IsValid check before the navmesh being called is used to prevent the Null value from being called?

Sure - attached new log with the workaround disabled.

Project is mixed blueprints/c++
If you notice in the callstack above, there is no user code in the crash - so adding an IsValid check is not relevant.

We use a MoveToLocationOrActor node using a goal location. When the game is running normally, this functions properly.

When the level containing the AI actors to ‘hidden’ while the actor is executing the above move, the crash occurs.

see:

UPathFollowingComponent::AbortMove on line ~333 of PathFollowingComponent.cpp:

GetWorld()->GetTimerManager().ClearTimer(WaitingForPathTimer);

note the non-validated use of GetWorld()

What steps can I take to reproduce this on my end in a clean, blank project with no additional content?

suggested setup:

New persistent level A
New streaming level S1
Add nav mesh to A.
create two basic actor locations p1 and p2 sufficiently far apart within the nav mesh.

blueprints:

on keypress 1: Spawn a creature in level S1 at pos p1 (store as E1)
on keypress 2: E1 calls MoveToLocationOrActor pos p2
on keypress 3: E1 calls MoveToLocationOrActor pos p1
on keypress 4: Set S1  'Set Visible' to false
on keypress 5: Set S1  'Set Visible' to true

this should allow you to send the enemy back and forth.

while E1 is not moving, 4,5 should work fine.

while E1 is moving, 4 would crash

Unfortunately I have not been able to reproduce this error on my end. Do you have a sample project where I can see this error occurring?

Hi VictorRachels,

We have not heard from you in several days. I am marking this as answered for tracking purposes. If you are still experiencing this error, please comment with the requested information.

sorry, our deadlines are pretty tight, I can’t afford the time to recreate a project with this bug right now.

If you send me the project you made with the above guidelines, I can probably tweak it as needed to reproduce the crash.

Hi VictorRachels,

I have uploaded the project to filedropper here, please look through and see if there is something different about my setup from yours.

needs:

 * streaming level (you have the set visible calls, but no actual streaming level)
 * nav mesh setup (put the bounds in the streaming level, then build and move the recast nav to the persistent level) 
 * ai movement setup  - the behavior tree you have is kind of spazzing out - this doesn't really need a behavior tree (or the complexity I see).  Just have the input find all actors of class aipawn; for each: get aicontroller, moveToActor.  Use two different input keys, with two different Note actors.
 * make sure you are spawning the actor into the streaming level.  We do this via source code and the spawn parameters OverrideLevel, but just setting the input pin 'owner' to an object that is in the streaming level (ie, a note) seems like it should work.

https://drive.google.com/open?id=0B1jHOHiiKmAvajl2YUhfbnFNbG8
No interaction needed, just let the blueprint magic work.
It will eventually crash on the ‘resume move’, this isn’t the abort move callstack above, but the access is the same. (getworld)

The project has not crashed following the steps above. Is there a specific set of steps I should take with the content provided? Do I need to press the input keys in a specific order or wait a specific amount of time before the crash occurs?

give it a little more time. while in our main project it happens fairly commonly, it took some time for this example to hit the reproduce case. Each cycle also spawns another ai to stress the system more. It took 5-10 minutes for it to crash for me - but it would eventually always crash. (hence my setting up the auto pathing). If you want to play with it, the Z key will spawn an additional ai. 1,2 will move the ai to the associated location. 3 will hide the streaming level. 4 will show it. I did not test using input along with the automatic systems, but it should probably be fine.

Thank you for submitting a bug report. I have reproduced this issue and logged a report for it here. You can track the report’s status as the issue is reviewed by our development staff.