Replication: Pointer points to wrong replicated actor

Hi !
I am trying to build a game with dedicated server, with click-to-move mechanics.
The server spawns a pawn, possessed by an AI controller, which is referenced by a Player Controller, so the player can specify targets and pawn moves using path-finder.

For some reason, the pointer to the Server Controlled Pawn is referencing the wrong pawn on all the clients !
So what am I doing wrong here ?

This is the output i get when i run dedicated server + 4 clients:

(part1)
you can see here the pawn & controller names

LogGameMode: AOpenWorldGameMode::InitNewPlayer>> Spawned pawn [ServerPlayerPawnBP_C_0] for controller [367]
LogGameMode: AOpenWorldGameMode::InitNewPlayer>> Spawned pawn [ServerPlayerPawnBP_C_1] for controller [368]
LogGameMode: AOpenWorldGameMode::InitNewPlayer>> Spawned pawn [ServerPlayerPawnBP_C_2] for controller [369]
LogGameMode: AOpenWorldGameMode::InitNewPlayer>> Spawned pawn [ServerPlayerPawnBP_C_3] for controller [370]

And there’s the next part 2 of log, where you can see the replication callbacks are being executed but all the referenced objects are wrong !

LogPlayerController:Warning: AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>> Controller [ClientPlayerController_C_0] ServerControlledPawn is [ServerPlayerPawnBP_C_2] 
LogPlayerController:Warning: AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>> Controller [368] ServerControlledPawn is [ServerPlayerPawnBP_C_0] 
LogPlayerController:Warning: AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>> Controller [369] ServerControlledPawn is [ServerPlayerPawnBP_C_3] 
LogPlayerController:Warning: AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>> Controller [ClientPlayerController_C_0] ServerControlledPawn is [ServerPlayerPawnBP_C_3] 

… and here is the source code:

OpenWorldGameMode.h

UCLASS()
class LEGACYMMOPROJECT_API AOpenWorldGameMode : public AGameModeBase
{
	GENERATED_BODY()
	
public:
	UPROPERTY( EditAnywhere, NoClear, BlueprintReadOnly, Category = Classes )
	TSubclassOf<APlayerPawnBase> DefaultServerPlayerPawnClass;

	UPROPERTY( EditAnywhere, NoClear, BlueprintReadOnly, Category = Classes )
	TSubclassOf<AServerPlayerAIControllerBase> DefaultServerPlayerAIControllerClass;

	AOpenWorldGameMode();

	virtual void PreLogin( const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage ) override;
	virtual FString InitNewPlayer( APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId, const FString& Options, const FString& Portal = TEXT( "" ) ) override;
	virtual void PostLogin( APlayerController* NewPlayer ) override;
};

OpenWorldGameMode.cpp

AOpenWorldGameMode::AOpenWorldGameMode() {
	DefaultPawnClass = nullptr;

	
}

void AOpenWorldGameMode::PreLogin( const FString & Options,
								   const FString & Address,
								   const FUniqueNetIdRepl & UniqueId,
								   FString & ErrorMessage ) {

	Super::PreLogin( Options, Address, UniqueId, ErrorMessage );
	if ( !ErrorMessage.IsEmpty() )
		return;

	// check username/password

}

FString AOpenWorldGameMode::InitNewPlayer( APlayerController * NewPlayerController,
										   const FUniqueNetIdRepl & UniqueId,
										   const FString & Options,
										   const FString & Portal ) {

	FString ret = Super::InitNewPlayer( NewPlayerController, UniqueId, Options, Portal );
	if ( !ret.IsEmpty() )
		return ret;

	AClientPlayerControllerBase* ClientPlayerCtrl = Cast<AClientPlayerControllerBase>( NewPlayerController );

	if ( !ClientPlayerCtrl ) {
		UE_LOG( LogGameMode, Warning, TEXT( "AOpenWorldGameMode::InitNewPlayer>> Cast to AClientPlayerControllerBase failed") );
		return FString::Printf( TEXT( "failed to cast to PlayerController to AClientPlayerControllerBase" ) );
	}

	check(ClientPlayerCtrl->ServerControlledPawn == nullptr);
	check(ClientPlayerCtrl->ServerAIController == nullptr);

	ClientPlayerCtrl->ServerControlledPawn = GetWorld()->SpawnActor<APlayerPawnBase>(DefaultServerPlayerPawnClass, NewPlayerController->StartSpot->GetTransform() );

	UE_LOG( LogGameMode, Log, TEXT( "AOpenWorldGameMode::InitNewPlayer>> Spawned pawn [%s] for controller [%s]" ), *ClientPlayerCtrl->ServerControlledPawn->GetHumanReadableName(), *ClientPlayerCtrl->GetHumanReadableName() );

	if ( ClientPlayerCtrl->ServerControlledPawn == nullptr ) {
		UE_LOG( LogGameMode, Warning, TEXT( "AOpenWorldGameMode::InitNewPlayer>> : SpawnActor<APlayerPawnBase> failed " ) );
		return FString::Printf( TEXT( "failed to spawn %s" ), *GetNameSafe( DefaultServerPlayerPawnClass ) );
	}

	ClientPlayerCtrl->ServerAIController = GetWorld()->SpawnActor<AServerPlayerAIControllerBase>( DefaultServerPlayerAIControllerClass );

	if ( ClientPlayerCtrl->ServerAIController == nullptr ) {
		UE_LOG( LogGameMode, Warning, TEXT( "AOpenWorldGameMode::InitNewPlayer>> : SpawnActor<AServerPlayerAIControllerBase> failed " ) );
		return FString::Printf( TEXT( "failed to spawn %s" ), *GetNameSafe( DefaultServerPlayerAIControllerClass ) );
	}

	ClientPlayerCtrl->ServerAIController->Possess( ClientPlayerCtrl->ServerControlledPawn );
	
	return ret;
}

void AOpenWorldGameMode::PostLogin( APlayerController * NewPlayer ) {
	Super::PostLogin( NewPlayer );
	AClientPlayerControllerBase* ClientPlayerCtrl = Cast<AClientPlayerControllerBase>( NewPlayer );
	UE_LOG( LogGameMode, Log, TEXT( "AOpenWorldGameMode::PostLogin( %s )>> ServerControlledPawn is [%s]" ), *NewPlayer->GetHumanReadableName(), *ClientPlayerCtrl->ServerControlledPawn->GetHumanReadableName() );
}

ClientPlayerControllerBase.h

UCLASS()
class LEGACYMMOPROJECT_API AClientPlayerControllerBase : public APlayerController
{
	GENERATED_BODY()
public:
	AClientPlayerControllerBase();
	
	UPROPERTY( BlueprintReadOnly, ReplicatedUsing = OnReplicate_ServerControlledPawnReference )
	APlayerPawnBase* ServerControlledPawn;

	//UPROPERTY( BlueprintReadOnly )
	AServerPlayerAIControllerBase* ServerAIController;

	//UFUNCTION( Client, Reliable )
	//void ClientSetControlledPawnReference( APlayerPawnBase* NewServerPawn );

	void GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const;

private:

	AFloatingCamera* FloatingCamera;

	// attach camera to the controlled pawn
	UFUNCTION()
	void OnReplicate_ServerControlledPawnReference();

	// Create Camera actor on first demand
	bool ValidateCameraActor();	
};

ClientPlayerController.cpp

DEFINE_LOG_CATEGORY( LogPlayerController );

AClientPlayerControllerBase::AClientPlayerControllerBase() {
	ServerControlledPawn = nullptr;
	ServerAIController = nullptr;
	FloatingCamera = nullptr;
}

void AClientPlayerControllerBase::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const {
	Super::GetLifetimeReplicatedProps( OutLifetimeProps );
	DOREPLIFETIME_CONDITION( AClientPlayerControllerBase, ServerControlledPawn, COND_None );
}

bool AClientPlayerControllerBase::ValidateCameraActor() {

	if ( !FloatingCamera ) {
		FloatingCamera = (AFloatingCamera*) GetWorld()->SpawnActor( AFloatingCamera::StaticClass() ) ;
		if ( !FloatingCamera ) {
			UE_LOG( LogPlayerController, Warning, TEXT( "AClientPlayerControllerBase::ValidateCameraActor()>> CameraActor spawn failed at controller %s"), *GetHumanReadableName()  );
			return false;
		}

		UE_LOG( LogPlayerController, Log, TEXT( "AClientPlayerControllerBase::ValidateCameraActor()>> CameraActor spawned at controller %s"), *GetHumanReadableName() );
	}

	return true;
}

void AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference() {

	UE_LOG( LogPlayerController, Warning, TEXT( "AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>> Controller [%s] ServerControlledPawn is [%s] " ), *GetHumanReadableName(), *ServerControlledPawn->GetHumanReadableName() );

	if ( !ServerControlledPawn ) {
		UE_LOG( LogPlayerController, Warning, TEXT( "AClientPlayerControllerBase::OnReplicate_ServerControlledPawnReference>>  ServerControlledPawn is null" ) );
		return;
	}

	if ( !ValidateCameraActor() )
		return;

	FloatingCamera->SetupForController( this );
	FloatingCamera->Follow( ServerControlledPawn );

}

My problem was checking the objects by their names, which are not replicated and are not expected to be the same when spawn dynamically. I tested it with custom string variables and it works perfectly. The pointers were right to begin with. Funny no one could point out something so fundamental.