C++ replication using condition COND_OwnerOnly not working cause of ownership issues

Hi all,

I’m looking into working with a DOREPLIFETIME_CONDITION

DOREPLIFETIME_CONDITION(APickup, bShowPickupName, COND_OwnerOnly);

I seem to have something wrong with ownership, since the OnRep of bShowPickupName is not running.

I’m using a listen server for the project.

I have APickup actor whichis a BP_Pickups derived from Pickup C++ class.
The pickup is dropped in the level through the editor and not spawned from within a class.

When the player’s collision sphere overlaps with the pickup’s collision box I run this:

if(HasAuthority()){
		bool ImplementsInterface = UKismetSystemLibrary::DoesImplementInterface(OtherActor, UInteractableActorInterface::StaticClass());
		if (ImplementsInterface){
			APickup* pickup = Cast<APickup>(OtherActor);  
			if(pickup){
				pickup->SetShowPickupName(true);
			}
		}
	}

The SetShowPickupName is the setter for the replicated bShowPickupName bool.
After this is changed, the replication’s OnRep should be called for the owner.
Since none of the players see the pickupname, something with ownership must be wrong.

I tested it without the Condition, and then the OnRep is called. So it has to do with the ownership.

What am I missing to make sure the pickup that is overlapped with the player is the owner of the actual player that overlaps with it?

Kind regards,

Nick

a) the pickup is most likely spawned on the server (as it should), so the owner of the pick up is the server. bShowPickupName in that case will only be replicated to the server

b) your use of in interface wrong and goes against everything that they are used for.

SetShowPickupName should be an interface function and called that way. Casts cause a hard reference to be formed.


           if(UKismetSystemLibrary::DoesImplementInterface(OtherActor, UInteractableActorInterface::StaticClass()))
            {
				// c++ can be skipped if you are not using implementation
				if (IInteractableActorInterface* inter = Cast<IInteractableActorInterface>(OtherActor)) {
					inter->SetShowPickupName_Implementation(true);
				}
				// blueprint (need overides implemented from within bp)
				if (OtherActor->GetClass()->ImplementsInterface(UInteractableActorInterface::StaticClass()))
				{
					IInteractableActorInterface::Execute_SetShowPickupName(OtherActor, true);
				}			
            }

Hi!

So you mean that objects that are dragged into the Level in Unreal will automatically be server owned? That would indeed be best…

From all the documentation I read, it was my assumption that the server has the Pickup object, but also the clients get a copy of it?

But then how, on overlap, do I only show the pickup’s name on the specific client’s screen?
Since the ownership of the Pickup actor will always be the server.


About the interface:

The interface has a SetItemNameVisibility(bool NewVisibility) function.

There might be an issue in the order of my functions:

In the PlayerCharacter’s Overlap I call the

Pickup->SetShowPickupName( true );

Here is what happens in the Pickup.cpp:

void APickup::SetShowPickupName(bool NewShowPickupName){
	bShowPickupName = NewShowPickupName;
     // -> Replication, so OnRep should be called
}

void APickup::OnRep_bShowPickupName(){
	SetItemNameVisibility();
}

void APickup::SetItemNameVisibility(){
	Widget->SetVisibility(bShowPickupName);
}

I’m depending on the Replicates to only call an OnRep to the Owner of the pickup actor.

DOREPLIFETIME_CONDITION(APickup, bShowPickupName, COND_OwnerOnly);

And then from there actually call the Interface function.

Is this completely wrong?
That would explain why it doesn’t work…

Are you sure you actually need to show it for the owner only, or do you want to just show the name for the person who is currently able to interact with the object?.

I just want to be sure to narrow down the amount of code needed.

Is this supposed to be targeted loot for a specific player only?

If you just want to show the names to the interacting person
showNameMP.zip (62.4 KB)

Edit: Use WASD to move and mouse to look. Name shows up on trace overlap in front of camera.

When the player gets close to the Pickup, the name of the Pickup (for example, medkit, candybar, … ) is shown above the Mesh of the Pickup actor.

My Pickup Actor has a UserWidget containing the name of the Pickup item.
When the player overlaps I want to set the widget’s visibility to true.

I’m not sure if I’ll use it to show the item’s name in the end, but maybe more like ‘Press e to take this item’, ‘Press f to open t he chest’. This text should also only appear for the player actually overlapping with the chest / pickup item.

I’ll take a look now at your code.

Thank you so much already!

Nick

Wow, not even using replication or RPC’s haha.

I’m going to try to get the blueprint logic in C++ and hopefully also get it to work.

Fingers crossed.

Hi 3dRaven,

I’ve been trying to convert it to C++, but I’ve encounterd an issue
What I’ve got:

For testing reasons, this code is executed when I press the ‘E’ key.
I changed the sphere trace to a LineTrace by Profile.

bool IsHit = UKismetSystemLibrary::LineTraceSingleByProfile(
			GetWorld(),
			StartLocation, 
			EndLocation, 
			FName(TEXT("Pickup_Profile")), true, actorsToIgnore, EDrawDebugTrace::Persistent, HitResult, true, FLinearColor::Red, FLinearColor::Green, 10.0f );

	if(IsHit){
		InteractableActor = HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractableActorInterface::StaticClass()) ? HitResult.GetActor() : nullptr;
		if (InteractableActor != nullptr) 
		{	
			if (InteractableActor->GetClass()->ImplementsInterface(UInteractableActorInterface::StaticClass()))
			{
				//IInteractableActorInterface::Execute_SetShowPickupName(InteractableActor, true);
				InteractableActor->SetItemNameVisibility(true);
				// A cast is necessary here to call the function in C++.
			}
		}
	}

In your example you call the Blueprint function and go from there.
However in C++ to call the interface function, an actual cast is needed to perform it.

Is this the way to go then in C++, or is there a better way to do this?

For now I want to stay on the C++ side for performance reasons and simplicity of keeping everything at the same side.
Although I don’t want to call it simple, since C++ documentation is way harder to find then Blueprint.

Another related question

In your example you are constantly running Sphere traces to check for a blocking actor and go from there.

What would be the best solution for a player walking to a pickup. Then the name of the pickup shows. When the player presses E, the pickup is destroyed.

Solution 1:
Have, like in your example, a sphere line trace constantly run.
When a hit occurs, the name is shown.
When the player also presses E, we check if the InteractableActor != nullptr.
If it’s not, then somehow we need to check with the server if it’s valid, then we can go further from that actor and destroy it.

Solution 2:
Have the sphere lin trace exactly the same for showing the name.
When the player presses E, perform a line trace on the client to check if there is a hit.
If there is a hit, Server RPC the server to validate it.
If valid, send a multicast RPC to all clients to remove the object.

The example you gave me, used a linetrace to get the actor.
Would the same thing work with overlaps? (OnBeginOverlap, OnEndOverlap)
Cause when doing that, either everyone sees the pickupname or only the server.
Except when using a Client RPC, but using line traces, no replication stuff is actually needed which is better…

c++ version only

showNameMP_2.zip (56.1 KB)

The cast in c++ is needed to invoke the interface. But it’s a cast to the interface and not the heavy object so it’s just empty functions (very lightweight memory-wise)

You could do an RPC and do for instance a sphere cast where the center of the screen is / under the mouse cursor if visible. No need for multi-casting. If you destroy a replicated actor on the server it will update it’s presence (or in this case lack of) to the relevant clients.

You could do that with overlaps. In theory it would take less resources, but you would still need to update the overlapping object’s position and rotation relative to the camera.

Wow, you’re blowing my mind here haha.

So casting to an interface is actually very smart indeed. It all makes sence when you explain it.

Thank you so much for this c++ project.


The Pickup class has a private UWidgetComponent wc where you do everything with.
In my project I just have a WidgetComponent variable and do everything with that.
Is this for performance or security reasons?


Also the interfaces always have the virtual function which the Pickup implements + the none - implementation function.
(example: bool isShowingName(); in UInteractableActorInterface)
Is this necessary for Unreal / C++?
I know for RPC’s you need the actual _Implementation, but I didn’t know that interfaces might also require this.
Or are they not necessary? (I removed them and compiled, and all still worked, but maybe something does actually break with it)


Doing it with overlaps still gives me some issues though.

Enabling overlaps for every clients, makes it that for every client in the game, the overlap is triggered, even though only 1 player actually overlaps.
Because of that the text shows for all clients.
Using HasAuthority() will result in only the server showing the text.
The only time I got it to actually work was using a client RPC.
I’ll play with it a bit more to try and find a solution.

Thank you so much for all the help!

With the overlaps I guess it depends on the way you decide to implement it
a) client overlaps a “trigger” sphere on the pickup item => triggers message
b) clients has attacked trigger volume to itself and overlaps pickup item.

Either way the interaction between player and item should be that when being picked up the whoever processes the pickup needs to know about the other actor.

The actor initiating the interaction needs to call the server telling them who is being picked up and by whom.

That way the server knows
a) I can add the picked up item to the players inventory.
b) I can now destroy said item.

You can also do it as a server RPC where you can check if the player can actually pick up the item server side. This can be done in with a server RPC with validation.

FUNCTION(Server, Reliable, WithValidation)

Much like the _implementation variant there is a _validate version of the function with a bool return.
The server side test will determine if the player can do it, it doubles as anti-cheat. So if someone is cheating it will kick them. (up to you if you want to use it)

In the logic I have now, the Player has a collision sphere in front of him and the Pickup has a Collision Box. (So option b).

Using a Pickup collision profile, only pickups will be registered as overlap.
I set the collision OnBegin/OnEnd - overlap (AddDynamic) inside a HasAuthority so only the server checks for overlaps.

It would be more logic if the client itself does this, but since the server would need to actually see itself that the overlap is actually there, for validation, doing it instant was better I thought.
Not really sure if that’s actually the case.
Then if there’s a collision I used a client RPC to set the visibility to true.

I’ve seen an example where the client does a Line trace and if there is a hit, it sends a server RPC and then the server needs to perform the same linetrace again for validation.

In your project, the linetrace happens for every client and works perfect.
But inside an overlap event it’s a bit more tricky it seems.

If I don’t check the Overlaps for HasAuthority, all clients will see the text.
If I do set the HasAuthority, only the server will see the text.
The Overlap event seems tPreformatted texto trigger for all.

the following code, must be in some kind of statement that will only call if for the actual player running into the pickup item:

void AMasterCharacter::TriggerBeginOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{

InteractableActor = OtherActor->GetClass()->ImplementsInterface(UInteractableActorInterface::StaticClass()) ? OtherActor : nullptr;
	if (InteractableActor != nullptr) 
	{	
		if (IInteractableActorInterface* Inter = Cast<IInteractableActorInterface>(InteractableActor))  //This type of cast is better then Cast to Pickup.
		{                                                                                               //lightweight memory-wise since only empty functions in interface
			Inter->SetShowPickupName_Implementation(true);
		}
	}
}

I’ve tried loads of ways, but nothing works:

if (UGameplayStatics::GetPlayerController(GetWorld(), 0)->IsLocalController()){
if(HasAuthority()){
if(OtherActor->IsOwnedBy(UGameplayStatics::GetPlayerController(this, 0))){
if(GetNetMode() == ENetMode::NM_Client){

I might just stick with the Sphere linetrace and on interact (press E), use a Server RPC.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.