SimpleMoveToLocation doesn't update the rotation in the local client

I started with the TopDown template but it doesn’t seem to be multiplayer ready so I did some digging.


void ATopDownPlayerController::MoveToMouseCursor()
{
	// Trace to see what is under the mouse cursor
	FHitResult Hit;
	GetHitResultUnderCursor(ECC_Visibility, false, Hit);

	if (Hit.bBlockingHit)
	{
		// We hit something, move there
		SetNewMoveDestination(Hit.ImpactPoint);
	}
}

void ATopDownPlayerController::SetNewMoveDestination_Implementation(const FVector DestLocation)
{
	APawn* const Pawn = GetPawn();
	if (Pawn)
	{
    UNavigationSystem* const NavSys = GetWorld()->GetNavigationSystem();
    
		float const Distance = FVector::Dist(DestLocation, Pawn->GetActorLocation());

		// We need to issue move command only if far enough in order for walk animation to play correctly
		if (NavSys && (Distance > 120.0f))
		{
			NavSys->SimpleMoveToLocation(this, DestLocation);
		}
	}
}

void ATopDownPlayerController::OnSetDestinationPressed()
{
	// set flag to keep updating destination until released
	bMoveToMouseCursor = true;
}

void ATopDownPlayerController::OnSetDestinationReleased()
{
	// clear flag to indicate we should stop updating the destination
	bMoveToMouseCursor = false;
}
bool ATopDownPlayerController::SetNewMoveDestination_Validate(const FVector DestLocation)
{
  return true;
}

And I changed the signature to


  UFUNCTION(reliable, server, WithValidation)
  void SetNewMoveDestination(const FVector DestLocation);

Now I want to call SetNewMoveDestination on the client and on the server. I thought I could use the function as a prediction function and when I call

SetNewMoveDestination(Hit.ImpactPoint);

It would be called on the client and the server. And if the client is too much out of sync with the server it just resets the local pawn to the servers location of the pawn.

But what happens is that the local pawn doesn’t receive any rotation update. If I am Pawn1 and Pawn1 moves I don’t get the rotation update but if Pawn2 sees Pawn1 moving he sees Pawn1 with the correct rotation.

Any idea why this happens?

I think the rotation doesn’t get replicated to the owner because it is set to COND_SimulatedOnly.


void ACharacter::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
	Super::GetLifetimeReplicatedProps( OutLifetimeProps );

	DOREPLIFETIME_CONDITION( ACharacter, RepRootMotion,		COND_SimulatedOnly );
	DOREPLIFETIME_CONDITION( ACharacter, RelativeMovement,	COND_SimulatedOnly );

	DOREPLIFETIME( ACharacter, bIsCrouched );
	DOREPLIFETIME( ACharacter, bSimulateGravity );
}

But GetLifetimeReplicatedProps is the strangest thing. I saw the shooter game example implementing this function without declaring it.

How do I use it?

I tried


void ATopDownCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
  Super::GetLifetimeReplicatedProps(OutLifetimeProps);
}

but then I get


error C2509: 'GetLifetimeReplicatedProps' : member function not declared in 'ATopDownCharacter'

There is some magic behind this function that I don’t understand.

Any help is greatly appreciated.

Edit:

It seems GetLifetimeReplicatedProps gets generated once I have one replicated variable in my class. Is this a bug or is it intendend?

Either way if I try to do


void ATopDownCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
  Super::GetLifetimeReplicatedProps(OutLifetimeProps);
  DOREPLIFETIME_CONDITION(ATopDownCharacter, RepRootMotion, COND_OwnerOnly);
}

It will crash with COND_OwnerOnly. It won’t crash with COND_SimulatedOnly. I have literally no idea what I should do now.