Child actors are not properly destroyed on clients

Hi there, I am having exactly the same issue. A few things to add,

  1. The child actor (the actor that is attached to the main actor within a ChildActorComponent) will not be destroyed on client no matter its bReplicates is true or false.
  2. If the child actor is not marked as bReplicated, even if do a call in the main actor’s Event Destroyed of ChildActor->GetChildActor->DestroyActor, it won’t be destroyed.
  3. Marking the child actor as replicated would make the trick above work.
  4. This seemed to be caused by the child actor’s role being ROLE_None regardless of its replication flag.

I can’t speak for Shihai, because the case he provided is slightly different, but as far as my issue and provided example project goes, this has nothing to do with order of operations.

I have provided detailed information about the source of the issue in my opening post. It’s definitely caused by the “DESTROYED_” prefix, which is clearly added to combat some other problem.

The reason why adding a brief delay is fixing the issue for you in this simple example, is that apparently client succeeds in opening ActorChannel for the child actor, before it is destroyed and therefore renamed.

This isn’t a sustainable solution in a normal environment. Attempting to delay destroying any child actor that is saved on a level, before all the clients travelling with the server fully join in and open all ActorChannels, sound like a giant pain, but is theoretically achievable. However, all clients that join during an active game session, would be forever doomed to see child actors that have been destroyed on a server way before.

Steps to reproduce:

  1. Create an actor with a child actor.
  2. Set both of them to replicate.
  3. Place them on the scene.
  4. Script the main actor, to be destroyed immediately on server (in its BeginPlay or level’s BeginPlay).
  5. Run the game for 2 players in PIE.

Effect:

On the server, both main and child actors are destroyed. However, on the client, the child actor still exists.

Reason:

UChildActorComponent::DestroyChildActor has 2 assumptions, that work against each other.

  1. The destruction is executed only on the server.
  2. Prior to destruction, an actor is renamed with DESTROYED_Foo_CHILDACTOR name format.

Because of this, if an ActorChannel for this actor already exists (the actor was destroyed after the client joined in), it will be properly closed in UNetDriver::NotifyActorDestroyed. However, if it doesn’t, a new ActorChannel will be later created on client, thanks to the DestroyedStartupOrDormantActors list, but it will fail to link itself with any actor (because of the renaming) and will end up deleting nothing.

The issue can be worked around, using NetLoadOnClient = false on the child actor, but it creates unnecessary overhead of creating and deleting extra actors on startup, and significantly complicates scripting workflow, because, on clients, it kills the guarantee, that child actors exist in the main’s actor BeginPlay.

Hey Czyzyk,

Could you please clarify step 1? Are you referring to adding a child actor component to the actor?

If this is the case, then I’m not seeing the same results on my end. If you could create a simplified test project, that would be great, as it’s possible there is something I’m overlooking.

Thanks

Hi Sean,

I am referring to creating 2 blueprints, one of them containing a ChildActorComponent set to spawn the other one. I’m attaching a very simple [test project][1], which has 100% repro on my side and a screenshot of how it looks for me, after launching

.

Sorry for the delay. I spent some more time testing this, and it appears if you add a brief delay (.5 seconds or so), the child actor will destroy properly. This leads me to believe that it is an order of operations issue, meaning that it’s possible the actor hasn’t been fully initialized when you are attempting to delete it, which is causing the child actor to remain on the client. If it is this issue, unfortunately would require a large overhaul of the current system to resolve. At this point, I’d recommend trying to add a brief delay in to see if that resolves the issue for you guys. If not, let me know and I can continue to investigate.

You’re correct, my apologies for overlooking that information. I’ve gone ahead and logged a report for our developers to have a look at:

Thanks for your patience and for your information.

Have a great day