Weird replication problem if replicated property set to class default

Thanks, BrUnO!

What’s the best place to do this on the server? I thought doing it in PostLogin would work, so popped this code in GameModeBase’s PostLogin(APlayerController NewPlayer)*



TArray<AActor*>Reps;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AReplicationTest::StaticClass(), Reps);
for (auto& Actor : Reps)
{
    if (bDebugPrint) UE_LOG(LogTemp, Display, TEXT("%s [Client %i] AReplicationTest %s (ReplicationTest: %i)"), __FUNCTIONW__, GPlayInEditorID, *Actor->GetName(), Cast<AReplicationTest>(Actor)->ReplicationTest);

    if (NewPlayer->GetNetDriver())
    {
         for (UNetConnection* Connection : NewPlayer->GetNetDriver()->ClientConnections)
         {
            UActorChannel* Channel = Connection->FindActorChannelRef(Actor);

            if (Channel != nullptr)
               Channel->ReplicateActor();
         }
     }
}


(*ActorChannels *is a private in 4.24, so I replaced that with FindActorChannelRef, which I think is the right function?)

*Channel *always seems to be nullptr there and so it never gets to the ReplicateActor part.

I also tried in the PlayerController’s **BeginPlay **but this was the same. The only place I could get Channel to be not nullptr is in the **Tick **(after having been connected for a while), but it still didn’t replicate or ever trigger the onRep. I’m guessing it’s still assuming the values have’t changed and the client has the correct values, and so doesn’t replicate?

If that isn’t working then it’s because the changes made for Fortnite :slight_smile:
I don’t know about alternative methods.

The Channel will be null unless the actor is currently network relevant and non-dormant for that connection (since channels are closed and reopened as actors go in and out of relevancy and dormancy), so it’s an unreliable method really.

It’s hard to know exactly what’s occuring here, but if ForceNetUpdate() and ForcePropertyCompare() don’t fix the issue, then the next logical explanation is the Server simply thinks the Client is up-to-date (since all of those functions result in the same thing ultimately). You may be able to drop some breakpoints in RepLayout.cpp to try and get some deeper understanding as to why, but it’s going to take some deep digging I suspect.

As another workaround, and something a bit more reliable - my suggestion would be to remove the door actors from the level, and instead create a “Door Spawner” object instead. The Spawner will spawn a new actor at runtime Server-Side, and set properties accordingly. I do this quite a lot in our current game as the different lifetimes of pre-placed actors vs runtime-spawned ones in the networking can create some issues for us, so it’s just easier to place a spawner which ensures the same code path is used for all those actors.

BTW - I certainly think it would be worth submitting a bug report with a small repro project to Epic. It might take some time, but if there is an underlying issue they’ll be able to fix it quicker. (Also post it here if you do so, I’d be interested to test it out)

Thanks for the suggestion. I’ll give this a try and see if it helps.

I did actually build a project to add to my bug report this morning. Attaching it to this post.

Contains C++ versions as well as Blueprint (since it happens in both) and some test maps (each containing 3 instances of the replication test actor) to demonstrate the issue:

  • _0_0_0_to_0_0_0: Editor has values set to 0,0,0 and shows correctly that no replication occurs when the value is set to 0,0,0
  • _0_1_2_to_0_1_2: Editor has values set to 0,1,2 and shows correctly that no replication occurs when the value is set to 0,1,2
  • **_0_1_2_to_0_0_0: **Editor has values set to 0,1,2 and shows failed replication on two of the actors when values are set to 0,0,0
  • **_0_1_2_to_3_4_5: **Editor has values set to 0,1,2 and shows correctly that replication occurs when values are set to 3,4,5

There’s also an actor ReplicationTestMinusOne which has the ReplicationTest property defaulting to -1. Replication works correctly in the above maps with this example.

BP_ReplicationTest is a blueprint version of the actor which shows that the same replication problem occurs with blueprints.

There’s also a folder which demonstrates how it works okay with a delay between setting the ReplicationTests to 0,0,0 in the TestWithDelay folder. I test this using the experimental ‘Allow late joining’ option. I’ll run the server+client 1 and then join a client 2 once the existing server and client have reported their values to the console.

The C++ classes all log to console and I filter with AReplicationTest. BP test prints to screen.

If its any use I downloaded your project and tried it in the latest 4.26 preview and it is still occurring, so it hasn’t been accidentally fixed or anything

I hate to bump, but I just tried this on 4.26 and still having this problem. I submitted a bug report back when I first had this issue.

Is replication just broken in this scenario? How are other people handling things such as the replication of lock status on doors that default to locked (or inventories that don’t default to empty and are emptied when the client joins the server)?

Year and a half later and this bug still exists in UE5.0.1, unfortunately.

One solution I did find was to use GameMode::PostLogin() to tell any incoming clients the state of things on the server (via a client reliable RPC). This does mean that you’re constantly sending the full state of your level to the client on connect so not ideal. Would be great if replication didn’t have this bug and could update the client.

If any actor is placed on map and some properties are instance editable and changed from its default values when editing map but when playing game set back to their defaults than server will not replicate these properties. I think this is becouse server it making decision about replication comparing property with its default value and not with its actual instance value set on map by level designer. I don’t know. Maybe it would be possible to give some hint to server during UNetDriver::ServerReplicateActors() that is calling to some PreReplicate() functions of our actor to replicate property anyway?

My simple workaround is to create child bluprint class with different property values and to switch actor to its child. This way all properties can have default values.

Or, if possible, I set bNetLoadOnClient to false so that actor is not loaded from map but spawned be server.