Verify failed in MarkActorComponentForNeededEndOfFrameUpdate

Hi,

We have a random failed verify in our multiplayer game : in UWorld::MarkActorComponentForNeededEndOfFrameUpdate, verify(ComponentsThatNeedEndOfFrameUpdate.RemoveSwap(Component) == 1) proves false during a ServerTravel (seamless) once in a while on a client. Any idea what this could mean ?

Thanks,

François

Hello, I am also currently getting this as well. It’s happening on a client during a level transition. An actor, which is a child of our weapon, is getting a tick and setting its visibility. This verify only triggers on the client though, not for the other player (the server). I’m going to look into it more but if anyone has any hints they would be appreciated.

We are seeing this behavior still in a 4.11 build. Same repro – client during a seamless travel. Anyone figure out how to avoid/fix it other than commenting out the assert?

Hello,

Here is my fix (my code is between the markers [HS][FK]) :

bool AActor::Rename( const TCHAR* InName, UObject* NewOuter, ERenameFlags Flags )
{
	if (NewOuter)
	{
		RegisterAllActorTickFunctions(false, true); // unregister all tick functions
		UnregisterAllComponents();
	}

	bool bSuccess = Super::Rename( InName, NewOuter, Flags );

	if (NewOuter && NewOuter->IsA<ULevel>())
	{
		UWorld* World = NewOuter->GetWorld();
		if (World && World->bIsWorldInitialized)
		{
			RegisterAllComponents();
		}
		RegisterAllActorTickFunctions(true, true); // register all tick functions
		//[HS][FK] 12/02/2016 Fix bug moteur (verify qui pète de temps en temps dans MarkActorComponentForNeededEndOfFrameUpdate lors d'un seamless travel)
		if (World)
		{
			for (UActorComponent* pComp : OwnedComponents)
			{
				if (pComp != NULL)
					World->UpdateActorComponentEndOfFrameUpdateState(pComp);
			}
		}
		//[\HS][FK]
	}

	return bSuccess;
}

The rename, when changing the level and world of actors during a seemless travel, was forgettings to setup the ComponentsThatNeedEndOfFrameUpdate member accordingly.

Thanks for posting your solution. I ended up tracking it down and went a slightly different way. I copied the ComponentsThatNeedEndOfFrameUpdate sets from the old world to the new one in CopyWorldData. This way the end of frame render update presumably will still be honored. I’m not 100% sure if it is still valid to do that in a tick that spans worlds, but it seemed like it could be better to ensure it processes rather than discarding it. If someone from Epic could comment on what the preferred solution would be, that would be appreciated.

Thank you both! We’re migrating to seamless travel and we were running into this too.

Our AI pawns are apparently coming with us into the transition level. …This seems counter to what the documentation says should happen, but that’s what the code is doing.

The pawns cause a crash when they’re finally destroyed, dirtying their attachments which in turn attempt to queue themselves for another update at the end of the frame.

To follow up, in case any Epic folks get a chance to look at this in the future, here’s what’s happening in our case:

We have AI Pawns with 1p components (the Pawns can become possessed by the player) flagged as bOnlyOwnerSee. These components have their own child 1p components (e.g. magazine meshes on weapons).

When the AI Pawn is moved to the transition World, it registers all of its components with the new World, resulting in transform updates. Transforming the parent components triggers updates on their child components… that haven’t yet registered with the new World. These child components call UWorld::MarkActorComponentForNeededEndOfFrameUpdate() on the old World, which sets their MarkedForEndOfFrameUpdateState to “Marked” and adds them to the old World’s ComponentsThatNeedEndOfFrameUpdate array.

Shortly thereafter, the actor channel gets shut down and the Pawn is Destroyed. During destruction, the Pawn’s Owner is set to NULL. Unfortunately, this happens before the Pawn’s components are removed. So when MarkOwnerRelevantComponentsDirty() gets called, the 1p (bOnlyOwnerSee) components get their Render States dirtied. This marks them for an end of frame recreate.

This has to be done on the game thread (state “MarkedForGameThread”). UWorld::MarkActorComponentForNeededEndOfFrameUpdate() attempts to undo the component’s current state (“Marked”) by removing the component from the ComponentsThatNeedEndOfFrameUpdate array. But it was never added to that array in the transition World, so the game asserts.

I haven’t fixed this issue in our build yet. Seems to me there are a few possible solutions, including those already mentioned:

  • Call UWorld::UpdateActorComponentEndOfFrameUpdateState() for each 1p component to reset the state to “Unmarked” at the end of Rename(), thereby preventing the code from attempting to remove the component from the array.
  • Copy the ComponentsThatNeedEndOfFrameUpdate array to the transition World, so the code executes successfully.
  • Remove the assert. :stuck_out_tongue:
  • Remove the components from the actor before setting the Owner to NULL, either within the Destroy call (which could cause other side effects?) or within the affected Actor (would have to do this per Actor class?).
  • Set the Owner to NULL before moving the Actor to the new level, preventing the Owner from changing during destruction.
  • Register all subcomponents with the new World before triggering UpdateComponentToWorld().
  • Prevent dirtying of Actor’s components during destruction.

Or perhaps there’s a better solution I’m not thinking of?

I agree with Eric: if someone from Epic could recommend a solution, that would be greatly appreciated!

Thanks!

– Update: Upon further investigation, realized I was making an assumption about why the component was in the array of the old World. Updated to reflect what I found.

Okay, so the “child” 1p weapon components were attached to the Pawn, but were owned by another Actor (a Weapon). The Pawn was being moved into the new World before the Weapon, so when the Pawn move triggered the Weapon’s components to update, they registered their update request with the old World. If the 1p components had been owned by the Pawn, all would’ve been fine because AActor::Rename() unregisters all components from the old World before registering any of them with the new one.

The fix I’m considering is adding:
MarkedForEndOfFrameUpdateState = 0;
to UActorComponent::UnregisterComponent(). This is functionally the same as calling UpdateActorComponentEndOfFrameUpdateState() at the end of Rename(), but it saves the extra loop through the components.

Any official word from Epic on a better fix (or even just letting us know that “we’re doing it wrong”) would still be much appreciated. :slight_smile:

Had to take a break on this issue for a while. Coming back to it, setting the state to 0 in UnregisterComponent() causes a crash when launching the editor.

A fix that seems to work is adding a call to CurrentWorld->SendAllEndOfFrameUpdates() in FSeamlessTravelHandler::Tick() right before the call to CopyWorldData().