Hi everybody, and thank you for the help.
It’s the first Actor i build for Multiplayer with replication so if you think i should have used another technique please tell me.
I’m running UE5.4 with two player and as net connection i use listen server.
So i’m trying to make a simple Door wich you can interact with to open or close it. When you interact with the door the client call a server RPC named ServerInteract, this RPC update the bOpen attribute of the door actor. This attribute is marked as ReplicatedUsing so its should be replicated on all the client and also call the OnRep function on all the client. This OnRep function play the opening or closing animation based on bOpen state.
My problem is that when i interact with the door from the client 0 wich is also the host/server the replication work perfectly. But when i interact with the client 1 wich isn’t the host but a simple client the ServerInteract RPC isn’t even call even if the owner of the actor has a valid connection.
You can see in the video a test where the logs show clearly my point.
Door.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "../InteractiveActor.h"
#include "Door.generated.h"
UCLASS()
class ESCAPEGAME_API ADoor : public AInteractiveActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ADoor();
// Handle interaction
virtual void ServerInteract_Implementation(APawn* InteractionSender) override;
virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
UFUNCTION(BlueprintCallable)
bool ToggleDoor();
UFUNCTION()
void OnRep_bOpen();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
protected:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Door)
UAnimSequence* DoorOpening_Animation;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Door)
UAnimSequence* DoorClosing_Animation;
UPROPERTY(ReplicatedUsing=OnRep_bOpen ,EditAnywhere, BlueprintReadOnly, Category = Door)
bool bOpen;
public:
UPROPERTY(EditAnywhere, category = Door)
USkeletalMeshComponent* DoorSkeletalMesh;
UPROPERTY(EditAnywhere, category = Door)
USceneComponent* DoorRoot;
};
Door.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Door.h"
#include "Net/UnrealNetwork.h"
// Sets default values
ADoor::ADoor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
DoorRoot = CreateDefaultSubobject<USceneComponent>(TEXT("DoorRoot"));
SetRootComponent(DoorRoot);
DoorSkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>("DoorMesh");
DoorSkeletalMesh->SetupAttachment(DoorRoot);
// Initiate variables
bOpen = false;
bReplicates = true;
}
// Called when the game starts or when spawned
void ADoor::BeginPlay()
{
Super::BeginPlay();
}
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::ServerInteract_Implementation(APawn* player)
{
Super::ServerInteract_Implementation(player);
UE_LOG(LogTemp, Warning, TEXT("Server : ServerInteract"));
ToggleDoor();
}
void ADoor::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ADoor, bOpen);
}
InteractiveActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InteractiveActor.generated.h"
UCLASS(Abstract)
class ESCAPEGAME_API AInteractiveActor : public AActor
{
GENERATED_BODY()
public:
// Interact function
UFUNCTION(Server, Reliable, WithValidation)
virtual void ServerInteract(APawn* InteractionSender);
virtual void ServerInteract_Implementation(APawn* InteractionSender);
virtual bool ServerInteract_Validate(APawn* InteractionSender);
};
InteractiveActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "InteractiveActor.h"
void AInteractiveActor::ServerInteract_Implementation(APawn* InteractionSender)
{
UE_LOG(LogTemp, Warning, TEXT("ServerInteract with %s, Has authority: %s"), *InteractionSender->GetName(), (HasAuthority() ? TEXT("Yes") : TEXT("No")));
}
bool AInteractiveActor::ServerInteract_Validate(APawn* InteractionSender)
{
return true;
}
Player actor interact function wich is called when f is pressed.
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());
if (AEscapeGamePlayerController* controller = Cast<AEscapeGamePlayerController>(GetController()))
{
UE_LOG(LogTemp, Warning, TEXT("Net Rep Responsible Owner: %s"), (HasNetOwner() ? TEXT("Yes") : TEXT("No")));
InteractiveActor->SetOwner(this);
InteractiveActor->ServerInteract(this);
}
} else
{
UE_LOG(LogTemp, Warning, TEXT("Hitted something"));
}
}