Why my client shooting code execute only locally?

Hello everyone,

I’m working on a top down shooter and currently implementing a shooting system. Everything seems fine until my client starts shooting and only damages other players locally. Server does damage to all clients tho.

Here is my function for shooting:



void AWeapon::Fire()
{
	if (WeaponConfig.currentMagAmmo > 0)
	{
		ABaseHero* Character = Cast<ABaseHero>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
		if (!Character) return;

		FVector Start, End, Direction, MouseWorldLoc, MouseWorldDir;
		APlayerController* Controller = UGameplayStatics::GetPlayerController(GetWorld(), 0);
		
		Controller->DeprojectMousePositionToWorld(MouseWorldLoc, MouseWorldDir);
		
		FVector tempDir = Character->TopDownCameraComp->GetForwardVector();

		FVector tempEnd = MouseWorldDir * 3000.0f + MouseWorldLoc;

		FCollisionQueryParams Params = FCollisionQueryParams(FName(TEXT("My Trace")), true, this);
		Params.bTraceComplex = true;

		FHitResult MouseTraceHit(ForceInit);

		GetWorld()->LineTraceSingleByChannel(MouseTraceHit, MouseWorldLoc, tempEnd, ECC_Pawn, Params);

		Start = TheMesh->GetSocketLocation(TEXT("MuzzleSocket"));
		End = MouseTraceHit.ImpactPoint;

		End.X += FMath::RandRange(-CurrentSpread, CurrentSpread);
		End.Y += FMath::RandRange(-CurrentSpread, CurrentSpread);
		End.Z += FMath::RandRange(-CurrentSpread, CurrentSpread);

		FHitResult Hit(ForceInit);
		
		GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Pawn, Params);
		
		ABaseHero* Hero = Cast<ABaseHero>(Hit.GetActor());
		if (Hero && Hero != Character)
		{
			Hero->TakeHealth(30.0f);
			GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, FString::Printf(TEXT("Health: %.2f"), Hero->GetHealth()));
		}

		DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 3.0f);
		//DrawDebugPoint(GetWorld(), End, 3.0f, FColor::Red, false, 3.0f);

		if (ShotSound)
		{
			UGameplayStatics::PlaySoundAtLocation(GetWorld(), ShotSound, Start);
		}

		if (Role < ROLE_Authority)
		{
			GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("Not server, calling him!"));
			ServerFire();
			GEngine->AddOnScreenDebugMessage(2, 2.0f, FColor::Green, TEXT("Not server, called him!"));
		}
	}
}



And my ServerFire function is:

Weapon.h



/** Ask server to perform a shot */
UFUNCTION(Reliable, Server, WithValidation)
void ServerFire();
virtual void ServerFire_Implementation();
virtual bool ServerFire_Validate();


Weapon.cpp



void AWeapon::ServerFire_Implementation()
{
	Fire();
}


My sprinting and item pickup is implemented absolutely the same way and it works just fine. For some reason it doesn’t work for me here…

Just to clarify: The health is replicated property and works fine because server causes damage properly and replicates to all clients.

Any thoughts?

Try this:

void AWeapon::Fire()
{
    if (HasAuthority() == false)
    {
        GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, TEXT("Not server, calling him!"));
        ServerFire();
        GEngine->AddOnScreenDebugMessage(2, 2.0f, FColor::Green, TEXT("Not server, called him!"));
        return;
    }


    if (WeaponConfig.currentMagAmmo > 0)
    {
        ABaseHero* Character = Cast<ABaseHero>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
        if (!Character) return;


        FVector Start, End, Direction, MouseWorldLoc, MouseWorldDir;
        APlayerController* Controller = UGameplayStatics::GetPlayerController(GetWorld(), 0);
        
        Controller->DeprojectMousePositionToWorld(MouseWorldLoc, MouseWorldDir);
        
        FVector tempDir = Character->TopDownCameraComp->GetForwardVector();


        FVector tempEnd = MouseWorldDir * 3000.0f + MouseWorldLoc;


        FCollisionQueryParams Params = FCollisionQueryParams(FName(TEXT("My Trace")), true, this);
        Params.bTraceComplex = true;


        FHitResult MouseTraceHit(ForceInit);


        GetWorld()->LineTraceSingleByChannel(MouseTraceHit, MouseWorldLoc, tempEnd, ECC_Pawn, Params);


        Start = TheMesh->GetSocketLocation(TEXT("MuzzleSocket"));
        End = MouseTraceHit.ImpactPoint;


        End.X += FMath::RandRange(-CurrentSpread, CurrentSpread);
        End.Y += FMath::RandRange(-CurrentSpread, CurrentSpread);
        End.Z += FMath::RandRange(-CurrentSpread, CurrentSpread);


        FHitResult Hit(ForceInit);
        
        GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Pawn, Params);
        
        ABaseHero* Hero = Cast<ABaseHero>(Hit.GetActor());
        if (Hero && Hero != Character)
        {
            Hero->TakeHealth(30.0f);
            GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, FString::Printf(TEXT("Health: %.2f"), Hero->GetHealth()));
        }


        DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 3.0f);
        //DrawDebugPoint(GetWorld(), End, 3.0f, FColor::Red, false, 3.0f);


        if (ShotSound)
        {
            UGameplayStatics::PlaySoundAtLocation(GetWorld(), ShotSound, Start);
        }
    }
}

Just a quick note. IIRC UGameplayStatics::GetPlayerCharacter and UGameplayStatics::GetPlayerController wont work properly in a multiplayer environment because when you call GetPlayerCharacter or Controller with index 0 on client it will return that client character/controller whereas if you call it on server it will return server character/controller. In that above function, ABaseHero* Character might return the server character.

Didn’t know that. Thanks for clarifying. How should I get my character then?

On the code, just tested it. Still doesn’t work. Actually now it doesn’t even damage the enemy locally.

Here’s what I’m getting:
3ba1d09f70d2e2c79a20e1af3a93c667ba56854b.jpeg
and

Note the debug messages.

Maybe it’s the problem with getting the player character?

Removed the return keyword from the beginning if statement, and tried getting character as GetWorld()->GetFirstPlayerController()->GetCharacter() and it still doesn’t work.