Hello,
I have encountered issues with the replication of USceneComponent::AttachParent. Sometimes, the server performs an Attach or Detach on a replicated component, and on the clients, the position is incorrect. I analyzed the netcode behind this and identified five different errors.
Error 1: Client changes the parent without calling AttachToComponent / DetachFromComponent
If a replicated component A attaches to a replicated component B, it is possible that USceneComponent::OnRep_AttachChildren on component B is called before USceneComponent::PostRepNotifies on component A. If this happens, component B will see that child A in its AttachChildren array does not have B as its parent and will call SetAttachParent(this). As a result, OnRep_AttachParent on A will never be called because AttachParent already has the correct value.
This means child A is “attached” without going through AttachToComponent (and DetachFromComponent if it was already attached to another component). These functions contain important events and logic, such as OnAttachmentChanged, bWeldSimulatedBodies, and other functionality.
Why doesn’t OnRep_AttachChildren directly call AttachToComponent(KeepRelative) instead of using SetAttachParent?
Error 2: bWeldSimulatedBodies is not replicated, causing a physics simulation desync
If the server calls AttachToComponent with bWeldSimulatedBodies set to true on a parent inheriting from PrimitiveComponent, the client correctly goes through AttachToComponent, but its bWeldSimulatedBodies value is false because it is not replicated.
This means that if the child component on both the server and client has bSimulatePhysics set to true, it will not be attached and will keep the parent’s world position as its RelativeLocation, simulating physics while the server does not.
Why isn’t this parameter replicated like variables such as bShouldSnapWhenAttached?
The same issue exists for actor replication via AttachmentReplication. We could fix this bug by also replicating bWeldSimulatedBodies in FRepAttachment.
Error 3: KeepWorld instead of KeepRelative on the client
If the server detaches a component with KeepRelative, on the client, in USceneComponent::PostRepNotifies, the component is detached with KeepWorld, which creates a mismatch between the server and the client.
Another bug occurs if the server performs a KeepWorld detach but also modifies the transform at the same time. The client will do a KeepWorld detach, overwriting the new RelativeTransform.
Why perform a detach with KeepWorld instead of KeepRelative? The relative transform is already replicated, so it should be valid at that point.
The same issue exists with actor AttachmentReplication. If they are detached, the client always uses KeepWorld. If ReplicateMovement is true, it’s not a problem because there is an OnRep_ReplicatedMovement afterwards. But for scale, if the server did a KeepRelative, the client will do KeepWorld. Could we replicate KeepRelative or KeepWorld in FRepAttachment?
Error 4: Detach on BeginPlay
If a client joins the game after a component has been detached from its root component, or if two players are launched at the same time but the server detaches on BeginPlay, the client will still have a parent on this component. I do not see OnRep_AttachParent being called at any point, and I haven’t been able to understand why. Perhaps if we had an OnRep_ShouldBeAttached that sets bNetUpdateAttachment to true, the client would correctly go through the Detach in PostRepNotifies.
The same issue exists for actor AttachmentReplication: since the AttachmentReplication struct is not modified compared to its initial state, the client never receives any information and OnRep_AttachmentReplication is never called. Perhaps we should add a bShouldBeAttached set to true by default in FRepAttachment so that the client receives an OnRep of AttachmentReplication at the beginning?
[Attachment Removed]