ApplyDamage is not working on network

Hello, I am fairly new to network programming and unreal network. I am able to apply damage from the server but not the client. I have a monster character but I use an actor component for a health component. I looked all over the place, but mostly find instruction with blueprint or what I wrote. I must be missing something. I have attached the code for the HealthComponent and the Weapon that does the damage. I am sorry about the ugly indentation, I could not figure out how to format this.

HealthComponent.cpp



// Sets default values for this component's properties
UHealthComponent::UHealthComponent()
{
MaxHealth = 100.f;

SetIsReplicated(true);
}

// Called when the game starts
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();

Health = MaxHealth;

if (GetOwnerRole() == ROLE_Authority)
{
AActor* MyOwner = GetOwner();
if (MyOwner)
{
MyOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::HandleTakeAnyDamage);
}
}
}

void UHealthComponent::OnRep_Health(float OldHealth)
{
float Damage = Health - OldHealth;

OnHealthChanged.Broadcast(this, Health, Damage, nullptr, nullptr, nullptr);
}

float UHealthComponent::GetHealth() const
{
return Health;
}

/**
* Handle damage done to actor
*/
void UHealthComponent::HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType,
AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0)
{
return;
}

Health = FMath::Clamp(Health-Damage, 0.f, MaxHealth);
if (Health == 0)
{
if (GetOwner())
{
UE_LOG(LogTemp, Warning, TEXT("%s is dead."), *GetOwner()->GetName());
}
}

OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);
}

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

DOREPLIFETIME(UHealthComponent, Health);
}

HealthComponent.h


// OnHealthChanged Event
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MONSTERS_API UHealthComponent : public UActorComponent
{
GENERATED_BODY()

public:
// Sets default values for this component's properties
UHealthComponent();

// Use this to call the sound effect and blood splatter
UPROPERTY(BlueprintAssignable, Category="Events")
FOnHealthChangedSignature OnHealthChanged;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Health Component")
float MaxHealth;

UPROPERTY(ReplicatedUsing=OnRep_Health, EditDefaultsOnly, BlueprintReadOnly, Category="Health Component")
float Health;

UFUNCTION()
void OnRep_Health(float OldHealth);

float GetHealth() const;

protected:
// Called when the game starts
virtual void BeginPlay() override;

UFUNCTION()
void HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
};


Weapon.cpp


AWeapon::AWeapon() {
// Creating Collision Component
BaseCollision = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionVolume"));
SetRootComponent(BaseCollision);

CombatCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("CombatCollision"));
CombatCollision->SetupAttachment(GetRootComponent());
CombatCollision->SetRelativeScale3D(FVector(1,1,2));
CombatCollision->SetRelativeLocation(FVector(0,0,30));

if (HasAuthority())
{
CombatCollision->OnComponentBeginOverlap.AddDynamic(this, &AWeapon::CombatOnOverlapBegin);
CombatCollision->OnComponentEndOverlap.AddDynamic(this, &AWeapon::CombatOnOverlapEnd);
}

// Creating Mesh Component
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(GetRootComponent());
Mesh->SetRelativeScale3D(FVector(1.2,1.2,1.2));
Mesh->SetRelativeLocation(FVector(0,0,6));

Damage = 20.f;

DamageTypeClass = UDamageType::StaticClass();

bReplicates = true;
bNetUseOwnerRelevancy = true;
}

void AWeapon::BeginPlay() {
Super::BeginPlay();

// Handles Collision Parameters
CombatCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
CombatCollision->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);
CombatCollision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
CombatCollision->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}

void AWeapon::EquipWeapon(ACharacter* Char) {
if (Char) {
Mesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore);
Mesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);

Mesh->SetSimulatePhysics(false);

const USkeletalMeshSocket* RightHandSocket = Char->GetMesh()->GetSocketByName(TEXT("WeaponHand"));
if (RightHandSocket) {
RightHandSocket->AttachActor(this, Char->GetMesh());
}
}
}

void AWeapon::DamageMonster(AActor* OtherActor)
{
AMonsterCharacter* Monster = Cast<AMonsterCharacter>(OtherActor);
if (Monster)
{
UE_LOG(LogTemp, Warning, TEXT("Beef Central"));
if (DamageTypeClass)
{
UGameplayStatics::ApplyDamage(Monster, Damage, WeaponInstigator, this, DamageTypeClass);
UE_LOG(LogTemp, Warning, TEXT("After ApplyDamage"));
}
}
}

void AWeapon::CombatOnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
if (OtherActor)
{
DamageMonster(OtherActor);
}
}

void AWeapon::CombatOnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) {

}

void AWeapon::ActivateCollision() {
CombatCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
UE_LOG(LogTemp, Warning, TEXT("Activating Collision"));
}

void AWeapon::DeactivateCollision() {
CombatCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
UE_LOG(LogTemp, Warning, TEXT("Dectivating Collision"));
}

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

DOREPLIFETIME(AWeapon, DamageTypeClass);
DOREPLIFETIME(AWeapon, Damage);
}

Weapon.h


UCLASS()
class MONSTERS_API AWeapon : public AActor
{
GENERATED_BODY()

public:

AWeapon();

/** Base shape collision */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item | Collision")
class USphereComponent* BaseCollision;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item | Combat")
class UBoxComponent* CombatCollision;

// Base Mesh Component
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Item | Mesh")
class UStaticMeshComponent* Mesh;

UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Item | Combat")
float Damage;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Sound")
class USoundCue* SwingSound1;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Sound")
class USoundCue* SwingSound2;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Sound")
class USoundCue* SwingSound3;

// Edited right here
UFUNCTION()
void CombatOnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void CombatOnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

void EquipWeapon(class ACharacter* Char);
void DamageMonster(AActor* OtherActor); // Edited

UFUNCTION(BlueprintCallable)
void ActivateCollision();

UFUNCTION(BlueprintCallable)
void DeactivateCollision();

UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Combat")
TSubclassOf<UDamageType> DamageTypeClass;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
AController* WeaponInstigator;

FORCEINLINE void SetInstigator(AController* Inst) { WeaponInstigator = Inst; }

protected:
virtual void BeginPlay() override;


The story of a Client wanting to shoot in a multiplayer game:

  • Client: “Hey Server, I want to shoot!”
  • Client uses a ServerRPC to call the shoot function on the server.
  • Server: "Alright dude, just give me a sec I’ll have to check if you have ammo and sht… in the meantime play the shooting SFX okay?"*
  • Server validates and continues
  • Server: “You are good, I’m shooting!”.
  • Server handles hit detection, applies damage if it hit an enemy, decrement the ammo from the client’s weapon which will be replicated back to the client.
  • At this point Server can tell the client about the hit result, notify other systems that this client killed someone, etc.

I keep thinking that I am understanding this but I must not be. For my code, I am calling DamageMonster in the Weapon.cpp when the overlap begins. Inside of DamageMonster, there is ApplyDamage. I thought that ApplyDamage and OnAnyDamage, I thought that those were serverRPCs? Would I have to make DamageMonster a serverRPC? Thank you, I have been trying to figure this out for weeks, keeps going right over my head I guess.

1 Like

:rofl: :rofl: :rofl:
You make my day!! :heart: