79186729

Date: 2024-11-13 21:30:13
Score: 0.5
Natty:
Report link

For client to server RPCs, the Owner must be the client attempting to call the RPC.

In general, actors that are placed in a level (such as doors) are never owned by any particular player.

I see you are attempting to SetOwner from the Interact function, but the Owner cannot be set from client-side, it has to be set from Authority, so it has no effect there. It may appear to work (as in, it you print owner after calling SetOwner it will show) but since the Owner is not modified on server-side it will not accept the RPC.

Instead of trying to modify the Owner of level-placed actors, you should rework your code a bit to route the RPCs through a real player-owned class (such as Character), then forward to actor code once you’re on server side.

You can adjust your code pretty easily to do so.

AInteractiveActor does not do the RPC, it just needs an entry point

UFUNCTION()
virtual void OnInteract(APawn* Sender)
{
    if (HasAuthority())
    {
        // server interaction
    }
    else
    {
        // client interaction (optional - can be used for prediction)
    }
}

Move the RPC logic to your Character class instead

UFUNCTION(Server, Reliable, WithValidation)
virtual void ServerInteract(AInteractiveActor* Target);
virtual void ServerInteract_Implementation(AInteractiveActor* Target);
virtual void ServerInteract_Validate(AInteractiveActor* Target);

// in function Interact()
    if (Hit.GetActor()->IsA(AInteractiveActor::StaticClass()))
    {
        AInteractiveActor* InteractiveActor = Cast<AInteractiveActor>(Hit.GetActor());

        if (!HasAuthority())
            InteractiveActor->OnInteract(this);  //optional, can be used for prediction
        
        ServerInteract(InteractiveActor);
    }
// end function Interact

void AEscapeGameCharacter::ServerInteract_Implementation(AInteractiveActor* Target)
{
    if (Target)
        Target->OnInteract(this);
}

This answer was written by Chatouille from UE5 official forum. Thanks to him for his help.

Final character.h

// Handle all interact action from player
void Interact();
UFUNCTION(Server, Reliable)
void ServerInteract(AInteractiveActor* Target);
void ServerInteract_Implementation(AInteractiveActor* Target);

Final character.cpp

void AEscapeGameCharacter::Interact()
{
    UE_LOG(LogTemp, Warning, TEXT("AEscapeGameCharacter::Interact"));


    FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation();
    FVector EndLocation = FirstPersonCameraComponent->GetForwardVector() * 200 + StartLocation;
    FHitResult Hit;
    
    GetWorld()->LineTraceSingleByChannel(Hit, StartLocation, EndLocation, ECC_Visibility);
    DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Red, false, 5, 0, 5);

    if (!Hit.bBlockingHit) { return; }
    
    if (Hit.GetActor()->IsA(AInteractiveActor::StaticClass()))
    {
        AInteractiveActor* InteractiveActor = Cast<AInteractiveActor>(Hit.GetActor());
        
        UE_LOG(LogTemp, Warning, TEXT("Net Rep Responsible Owner: %s"), (HasNetOwner() ? TEXT("Yes") : TEXT("No")));
        ServerInteract(InteractiveActor);
        
    } else
    {
        UE_LOG(LogTemp, Warning, TEXT("Hitted something"));
    }
    
}

void AEscapeGameCharacter::ServerInteract_Implementation(AInteractiveActor* Target)
{
    if (Target)
    {
        Target->OnInteract(this);
    }
}

Final InteractiveActor.h

UFUNCTION(BlueprintCallable)
    virtual void OnInteract(APawn* Sender);

Final Door.h

// Handle interaction
    virtual void OnInteract(APawn* Sender) override;

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

    UFUNCTION()
    bool ToggleDoor();

    UFUNCTION()
    void OnRep_bOpen();

Final Door.cpp

bool ADoor::ToggleDoor()
{
    UE_LOG(LogTemp, Warning, TEXT("ToggleDoor : This actor has %s"), ( HasAuthority() ? TEXT("authority") : TEXT("no authority") ));
    
    bOpen = !bOpen;

    if (HasAuthority())
    {
        // if bOpen changed to true play opening animation if it changed to false play closing animation.
        DoorSkeletalMesh->PlayAnimation(bOpen ? DoorOpening_Animation : DoorClosing_Animation, false);
    }
    
    return bOpen;
}

void ADoor::OnRep_bOpen()
{
    UE_LOG(LogTemp, Warning, TEXT("OnRep_bOpen Has authority : %s"), (HasAuthority() ? TEXT("yes") : TEXT("no")));
    UE_LOG(LogTemp, Warning, TEXT("bOpen : %s"), (bOpen ? TEXT("yes") : TEXT("no")));

    // if bOpen changed to true play opening animation if it changed to false play closing animation.
    DoorSkeletalMesh->PlayAnimation(bOpen ? DoorOpening_Animation : DoorClosing_Animation, false);
}

void ADoor::OnInteract(APawn* Sender)
{
    Super::OnInteract(Sender);

    UE_LOG(LogTemp, Warning, TEXT("Server : ServerInteract"));
    
    ToggleDoor();
}

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

    DOREPLIFETIME(ADoor, bOpen);
}
Reasons:
  • Blacklisted phrase (0.5): Thanks
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: thacaout