ReplicatedMovement and becoming relevant

In my network game, players can change the pawn they possess, and those pawns remain in the world unpossess. The world is large enough that those pawns can become non-relevant.

If have a problem where a player possessing pawn A, possesses pawn B, then re-possesses pawn A, then possesses pawn C (which is far away), then moves towards pawn A. Pawn A gets replicated to the player, but its ReplicatedMovement structure contains zeros (it is correct on the server) and just after the pawn is created through replication it gets a “PostNetReceiveLocationAndRotation” retnotify, causing it to move to 0,0,0.

If the player doesn’t possess pawn B, instead going from A to C, then moving back towards A, things are better - the ReplicatedMovement structure still contains all zeros, but no RepNotify is called, so the object doesn’t move.

Why is this Repnotify being called without the data in the structure from the server?

For more info - while the player possesses pawn B, pawn A has its movement disabled and is attached to pawn B (player is driving a vehicle). Could this attach and detach - or movement disable - be the cause of the different behavior?

I’ve tracked down the problem to AttachmentReplication. When you attach the pawn to another actor, it fills in the AttachmentReplication structure. When it is unattached it clears the AttachParent member, but not the other members (which are ignored because there’s no parent.)
When the pawn becomes relevant, it doesn’t send the ReplicatedMovement structure to the client (because nothing has changed and it has a custom == operator) but it does send the AttachmentReplication structure (because members of it are changed from default, even though the AttachParent is null, so they are ignored). This causes PostNetReceiveLocationAndRotation to be called, which moves the object to 0,0,0 (as that’s what’s in the empty ReplicatedMovement structure).
My current fix is to fully empty AttachmentReplication rather than just setting AttachParent to NULL. Better solutions might be to always send ReplicatedMovement initially, or change AttachReplication to ignore changes to members when considering for replication if AttachParent isn’t set.

Hi Tim,

Sorry for the late reply. It sounds like you’ve found the root issue, and that makes sense. I’m wondering if we should also set ReplicateMovement inside PackageMapClient.cpp when we first create the actor, to make it properly reflect what was on the server at the time of sending. On the send, we purposely update the shadow state version of ReplicatedMovement, so that it won’t send again (unless the actor moves again from initially opening the channel).

So maybe try something like this after spawning the actor:

Actor = Connection->Driver->GetWorld()->SpawnActorAbsolute(Archetype->GetClass(), FTransform(Rotation, Location), SpawnInfo );

if (bSerializeLocation)
{
    Actor->ReplicatedMovement.Location = Location;
}

if (bSerializeRotation)
{
    Actor->ReplicatedMovement.Rotation = Rotation;
}

if (bSerializeVelocity)
{
    Actor->ReplicatedMovement.LinearVelocity = Velocity;
}

Let me know if this does/does not make sense, or if it doesn’t work!