[Replication] Targeting System: Setting variable on a character that I do not own, do not want this replicated to any client but my own client

I am currently in the process of building my own tab targeting system in C++. This system currently works in offline mode, but I feel like I’ve hit a wall as far as getting replication to work for “Play as Listen Server” and “Play as Client” PIE modes.

I have a variable on my Character actor called bIsTargeted. If this is true, the Character blueprint is set to make an attached billboard above the character display a texture with an arrow. I want a way to be able to set this variable remotely without other clients (or the character’s owning connection) see this variable change, as it would then display the arrow on their screen.

My first thought was to use DOREPLIFETIME_CONDITION(AMyCharacter, bIsTargeted, COND_SkipOwner); in my GetLifetimeReplicatedProps override, but this ends up displaying the targeting arrow to not only me, but also other clients. Similarly, COND_OwnerOnly allows the owner of the character I’m targeting to see the arrow, but not the client that is targeting that character.

My second thought was to use IsLocalController in conjunction with a Remote Procedure Call (which is located on the Character) to set the variable, but IsLocalController never returns as true.

After some fiddling about and having debug messages display the netmode, remoterole, and localrole, I have (perhaps falsely?) determined that it is not possible to use IsLocalController to set the variable for the client doing the targeting.

//MyPlayerController.cpp
    void AMyPlayerController::TargetNext_Implementation() {
    	if (Cast<AMyPlayerState>(PlayerState))
    	{
    		// Clear targeting visuals from current actor
    		if (Cast<AMyCharacter>(Cast<AMyPlayerState>(PlayerState)->ActionTarget))
    		{
    			Cast<AMyCharacter>(Cast<AMyPlayerState>(PlayerState)->ActionTarget)->SetAsUntargeted(this);
    			
    		}
    
    		// Set targeting visuals for new actor
    		Cast<AMyPlayerState>(PlayerState)->ActionTarget = TargetingComponent->TargetNext();
    		if (Cast<AMyCharacter>(Cast<AMyPlayerState>(PlayerState)->ActionTarget))
    		{
    			Cast<AMyCharacter>(Cast<AMyPlayerState>(PlayerState)->ActionTarget)->SetAsTargeted(this);
    		}
    	}
    
    	EndAutoAttack();
    }

and

//MyCharacter.cpp
void AMyCharacter::SetAsTargeted_Implementation(AMyPlayerController* APC)
{
	if (APC != nullptr)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("APC SetAsTargeted")));
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("APC SetAsTargeted NetMode %d"), APC->GetNetMode()));
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("APC SetAsTargeted LocalRole %d"), APC->GetLocalRole()));
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("APC SetAsTargeted RemoteRole %d"), APC->GetRemoteRole()));
		if (APC->IsLocalPlayerController())
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("APC->IsLocal SetAsTargeted")));
			bIsTargeted = true;
		}
	}
}

Does anyone have any other ideas? I’ve been stuck on this for a week. Perhaps there’s a combination of the above or something I haven’t considered that would work?

I’ve solved the issue (I think).

The SetAsTarget function must be set as Netmulticast and the IsLocalPlayer() check must be done inside of the SetAsTarget function. I had been using Client and Server, didn’t experiment with Netmulticast. I also have DOREPLIFETIME_CONDITION() set as COND_SkipOwner. Hope this helps someone else.