[Bug with fix] - Actor attached to another actor doesn't update replicated location

Not sure if this has been fixed yet, but found a bug where an actor attached to another actor doesn’t update its replicated location on clients after the attached actor has been moved on the server.

Repo steps: Create two actors in a level on a server with bReplicates and bReplicateMovement set to true. Attach one actor to another. On the attached actor call SetActorTransform() on the server changing its current location. Notice on the client the location never changes.

I have fixed the issue on my build by changing…

void AActor::OnRep_AttachmentReplication()
{
	if (AttachmentReplication.AttachParent)
	{
		if ()
		{
			USceneComponent* AttachParentComponent = (AttachmentReplication.AttachComponent ? AttachmentReplication.AttachComponent : AttachmentReplication.AttachParent->GetRootComponent());

			if (AttachParentComponent)
			{
				->RelativeLocation = AttachmentReplication.LocationOffset;
				->RelativeRotation = AttachmentReplication.RotationOffset;
				->RelativeScale3D = AttachmentReplication.RelativeScale3D;
				->AttachToComponent(AttachParentComponent, FAttachmentTransformRules::KeepRelativeTransform,  AttachmentReplication.AttachSocket);
			}
		}
	}
	// etc...
}

to…

void AActor::OnRep_AttachmentReplication()
{
	if (AttachmentReplication.AttachParent)
	{
		if ()
		{
			USceneComponent* AttachParentComponent = (AttachmentReplication.AttachComponent ? AttachmentReplication.AttachComponent : AttachmentReplication.AttachParent->GetRootComponent());

			if (AttachParentComponent)
			{
				->RelativeLocation = AttachmentReplication.LocationOffset;
				->RelativeRotation = AttachmentReplication.RotationOffset;
				->RelativeScale3D = AttachmentReplication.RelativeScale3D;

				if(AttachParentComponent != ->GetAttachParent())
				{
					->AttachToComponent(AttachParentComponent, FAttachmentTransformRules::KeepRelativeTransform,  AttachmentReplication.AttachSocket);
				}
				else
				{
					->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate);
				}
			}
		}
	}
	// etc...
}

Hey ,

I am not seeing the issue you are describing. I am able to spawn two Actors, attach one mesh from ActorB to ActorA and then when I move ActorA, ActorB mesh follows.

Here is the project I used to debug the issue:

WASD to move

Left mouse button to spawn Actors

Left mouse button to move Actors once spawned.

Hi,

Thanks for making a test project to make repoing this easier. I went ahead and updated the projects code to reflect the bug I am seeing. Basically on load it should spawn the two actors into the world. Then when a pawn presses the shoot button the attached actor should move to above its head. Notice on the server the box updates to either pawns location when shoot is pressed, but doesn’t on the client. Anyway let me know if there are anymore issues and I’ll do my best to help. Download

Hey ,

First, thank you for the report but this is not a bug with the Unreal Engine. Your issue comes from trying to move the child Actor and expecting the parent Actor to move along with it. This isn’t how UE4’s attachment / hierarchy system works. You will always be able to move child components without the Parent following around.

If you change your move code to:

GameMode->ParentActor->SetActorLocation( GetActorLocation( ) + FVector( 0.f, 0.f, 256.f ) );

This will have the ParentActor move and then because the AttachedActor is a child of it, will follow it.

Hey,

I think you misunderstand. I am not trying to move the parent actor at all. Only ever trying to move the child actor. There is indeed a bug here, please test the project I uploaded again. The code does not ever move the parent actor when the shoot button is pressed. Nor does it intend to. It only ever tells the attached actor to move. Thing is the attached actors location never correctly updates on the client. The actor you see moving on the server when shoot button is pressed is the attached actor, not the parent actor. If you look at the code when shoot is called you see…

GameMode->AttachedActor->SetActorLocation( GetActorLocation( ) + FVector( 0.f, 0.f, 256.f ) );

I do not want the parent actor to move and there is no code supplied to tell it to move. Only want the attached actor to move on both the server and the client. In ReboBug project I provided it shows exactly this happening. I am not sure how I can explain this any better haha.

Sorry about that. I assumed a bit more than maybe I should of. So what you are seeing is still not a bug but I’ll explain more as to why.

The networking model UE4 uses keeps the GameMode server side only. This is to have a place for all game critical data to be stored and to always be correct; as in no simulated function calls or predictive models. This makes the GameMode great for things like Kills, Assists, etc. It is not so great when it comes to spawning Actors that are simulated because the Actor then doesn’t have any way to be simulated to other clients, as when you update something from the GameMode it is again, server side only, preventing that path of simulation from happening on a function call like SetActorLocation. This is why you were only seeing the server change it.

This is why in the original project, I avoided using the GameMode to manage the Attached / Parent Actors. Now, if you want to use these Actors in the GameMode, you will just have to explicitly tell all the Clients to update the position of the Actor(s) when the server does.

Attached is your project with an update to do that.

I understand that the GameMode is server side only and that has nothing to do with this bug. By setting bReplicates = true and spawning the actor on the server (eg. GameMode since its server only) when spawned the actor automatically replicates to all clients no matter where it was spawned from. If you noticed when testing both actors spawn correctly on the server and the client. Therefore it is working exactly as intended. It does not matter where the actor is spawned from as long as its spawned on the server and breplicates is true it will spawn for the client.

Anyway I tested your project and while your code does work it is a hack and does not fix the bug I am talking about. Would it be possible to have the person who wrote the function AActor::OnRep_AttachmentReplication() or someone who is familiar with it to take a look at this post and check out what I am saying?

I’m going to try to explain what is happening again, but more in-depth. First off both actors are set to replicate and set to replicate movement. This means the that actors transform should always be updated on the server and server only. Never should it update on the client. This can cause bugs where the actors become out of sync. It should all be handled server side. Now if you take the RepoBug project I posted earlier and comment out the line AttachedActor->AttachToActor( ParentActor, FAttachmentTransformRules::KeepWorldTransform ); in the GameMode you will see that when pressing shoot on the server and client then everything updates as it should, No bug. When the attach actor is attached to the parent and shoot is press it does not update on the clients view. Definitely a bug.

I will continue the post below since out of characters on this comment.

Now if you look at the code I provided in the original post you see

                 ->RelativeLocation = AttachmentReplication.LocationOffset;
                 ->RelativeRotation = AttachmentReplication.RotationOffset;
                 ->RelativeScale3D = AttachmentReplication.RelativeScale3D;
                 ->AttachToComponent(AttachParentComponent, FAttachmentTransformRules::KeepRelativeTransform,  AttachmentReplication.AttachSocket);

Notice that this code is in a function called void AActor::OnRep_AttachmentReplication(). Meaning that this attachment is getting its local transform replicated on the client. Problem is the transform is set (eg. RelativeLocation, Rotation, scale) then it tries to attach if not already attached, but it never tells the component to update its transform to the values that where just set. So now the RelativeLocation no longer matches where the mesh currently is on the client. This is a bug. By calling ->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate); this effectively moves the mesh to the transform values that where just set and replicated over to the client. Please have someone familiar with this code read this and if for some reason they still believe this is a not a bug I will give up haha.

Hello,

Since you have a fix for the issue you’re experiencing, feel free to put in a pull request for our developers to consider implementing into the engine. An accepted pull request will earn you a place in the engine credits as well as help other users who may be experiencing the same issue.

Thanks for your report.

Have a great day

the code above also solves an issue where a pawn is attached to another pawn and wont replicate unless the first pawn is moving.

by using the above code, the child pawn replicates on its own without the first pawn ever needing to move. this issue still persists on 4.20.2