Multicast RPC not firing

Hi all, I seem to be having issues understand why my multicast RPC isn’t firing on my connected client (I think that’s the issue anyways). My understanding was that if I wanted to fire off some sound and particle FX when I fire my gun, I’d want to go like: Function-> Server_Function->Multi_Function and the Multi_Function would actually contain the code to fire off the FX. The problem is that it doesn’t appear to work for me and I don’t know why. Here’s some code as well as an accompanying video. As you can see, the Listening Server can see it’s own and the connected client’s actions, but the Client can see neither.

CharacterBase.cpp

void ACharacterBase::PrimaryAttack_Pull()
{

	GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Client called Primary attack, calling Server RPC")));
	Server_PrimaryAttack_Pull();
	
}

void ACharacterBase::Server_PrimaryAttack_Pull_Implementation()
{
	GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Server calling Multicast primary attack RPC")));
	Multi_PrimaryAttack_Pull();
}

void ACharacterBase::Multi_PrimaryAttack_Pull_Implementation()
{

	GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Multicast fired on %s"), *UEnum::GetValueAsString(GetRemoteRole())));
	if (EquippedWeapon)
		EquippedWeapon->PullTrigger();
	
}

void ACharacterBase::PrimaryAttack_Release()
{
	Server_PrimaryAttack_Release();
}

void ACharacterBase::Server_PrimaryAttack_Release_Implementation()
{
	Multi_PrimaryAttack_Release();
}

void ACharacterBase::Multi_PrimaryAttack_Release_Implementation()
{
	if (EquippedWeapon)
		EquippedWeapon->ReleaseTrigger();
}

GunBase.cpp

void AGunBase::PullTrigger_Implementation()
{

	if (GetWorldTimerManager().TimerExists(BurstRetriggerHandle) || CurMagazine <= 0 || bReloading)
		return;
	BulletsFired = 0;
	GetWorldTimerManager().SetTimer(FireHandle, this, &AGunBase::Fire, 60/FireRate, true, 0);
	GetWorldTimerManager().SetTimer(BurstRetriggerHandle, BurstRetriggerDelay, false);
}

void AGunBase::ReleaseTrigger_Implementation()
{
	if (BulletsFired >= BurstAmount)
		GetWorldTimerManager().ClearTimer(FireHandle);
}

void AGunBase::SpawnBullet_Implementation()
{
	UAISense_Hearing::ReportNoiseEvent(GetWorld(), GetActorLocation(), 1.0f, GetOwner(), 0.0f);
	APawn* OwningPawn = Cast<APawn>(GetOwner());
	if (ACharacterBase* OwningChar = Cast<ACharacterBase>(GetOwner()))
	{
		OwningChar->GetMesh()->GetAnimInstance()->Montage_Play(OwningChar->FiringAnim);
	}
	
	if (APlayerCharacter* PlayerChar = Cast<APlayerCharacter>(GetOwner()))
	{
		if (FireAnimation1P) PlayerChar->GetMesh1P()->GetAnimInstance()->Montage_Play(FireAnimation1P);
		if (APlayerController* PC = Cast<APlayerController>(PlayerChar->GetController())) PC->ClientPlayForceFeedback(FireFeedback);
	}
	
	for (int i = 0; i < MultiShot; ++i)
	{
		if (ProjectileClass)
		{
			FVector Location = Mesh->DoesSocketExist("Muzzle") ? Mesh->GetSocketLocation("Muzzle") : GetActorLocation() + GetActorForwardVector()*50000.0f;
			FRotator Rotation;
			if (OwningPawn)
			{
				Rotation = OwningPawn->GetBaseAimRotation() + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);
			} else
			{
				Rotation = GetActorRotation();
			}
			FActorSpawnParameters ActorSpawnParameters;
			ActorSpawnParameters.Owner = this;
			ActorSpawnParameters.Instigator = Cast<ACharacterBase>(this->GetOwner());
			GetWorld()->SpawnActor(ProjectileClass, &Location, &Rotation, ActorSpawnParameters);
		} else
		{
			FHitResult Hit;
			FVector TraceStart;
			FRotator EyeRotation;
			if (OwningPawn && OwningPawn->GetController())
			{
				OwningPawn->GetController()->GetPlayerViewPoint(TraceStart, EyeRotation);
				EyeRotation = EyeRotation + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);
				//OwningPawn->GetActorEyesViewPoint(TraceStart, EyeRotation);
				//EyeRotation = OwningPawn->GetBaseAimRotation() + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);

			} else
			{
				TraceStart = GetActorLocation();
				EyeRotation = GetActorRotation();
			}
			
			TArray<AActor*> ActorsToIgnore;
			ActorsToIgnore.Add(this);
			ActorsToIgnore.Add(GetOwner());
			// The actual bullet trace, with a width for accuracy forgiveness
			//UKismetSystemLibrary::SphereTraceSingle(GetWorld(), TraceStart, TraceEnd, 20, UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Camera), false, ActorsToIgnore, EDrawDebugTrace::None, Hit, true, FLinearColor::Red, FLinearColor::Green, 5);
			AController* EventInstigator = nullptr;
			if (OwningPawn)
			{
				EventInstigator = OwningPawn->GetController();
			}
			UMyCustomBlueprintFunctionLibrary::FireHitScanBullet(Hit, GetWorld(), ActorsToIgnore, TraceStart, EyeRotation.Vector(), Range, FalloffCurve, Damage, Force, this, EventInstigator);
			if (Mesh->DoesSocketExist("Muzzle") && TrailPFX)
			{
				FVector TrailEnd = (Hit.bBlockingHit) ? Hit.ImpactPoint : Hit.TraceEnd;
				UNiagaraComponent* TrailPFXComponent = UNiagaraFunctionLibrary::SpawnSystemAttached(TrailPFX, Mesh, "Muzzle", FVector(0,0,0), FRotator(0,0,0), EAttachLocation::SnapToTarget, true);
				TrailPFXComponent->SetVectorParameter("BeamEnd", TrailEnd);
			}
			if (Hit.bBlockingHit)
			{
				if (HitSound) UGameplayStatics::PlaySoundAtLocation(GetWorld(), HitSound, Hit.Location);
				if (ImpactDecal && !Cast<IDamageableInterface>(Hit.GetActor()))
				{
					FVector Location = Hit.ImpactPoint;
					FRotator Rotation = Hit.Normal.Rotation() + FRotator(-90, 0, 0);
					AActor* Decal = GetWorld()->SpawnActor(ImpactDecal, &Location, &Rotation);
					Decal->AttachToComponent(Hit.GetComponent(), FAttachmentTransformRules::KeepWorldTransform);
					//UGameplayStatics::SpawnDecalAttached(GetWorld(), FVector(10,10,10), ) //Perhaps optimize this in the future
				}
			}
		}
	}
	BulletsFired++;
	if (BulletsFired==BurstAmount)
	{
		ReleaseTrigger();
	}
	SpawnMuzzleFX();
}

void AGunBase::SpawnMuzzleFX_Implementation()
{
	if (APlayerCharacter* Char = Cast<APlayerCharacter>(GetOwner()))
		if (FiringCameraShake && Char->IsLocallyControlled())
			Cast<APlayerController>(Char->GetController())->PlayerCameraManager->StartCameraShake(FiringCameraShake, 1, ECameraShakePlaySpace::CameraLocal);
	if (FiringSound)
		UGameplayStatics::SpawnSoundAttached(FiringSound, GetRootComponent());
	if (Mesh->DoesSocketExist("Muzzle") && MuzzlePFX && !ScopeActive)
		UNiagaraFunctionLibrary::SpawnSystemAttached(MuzzlePFX, Mesh, "Muzzle", FVector(0,0,0), FRotator(0,0,0), EAttachLocation::SnapToTarget, true);
}

Also, do I need to do the Function->Server_Function->Multi_Function calls in my GunBase if its been initiated by the multicast in the CharacterBase anyways?

only the server can call a multicast so the client need to request a server RPC to fire.
and of course you need ownership as well

That’s what im doing (I think). When the possessed character presses his primary attack key, it calls the server version of that function, which then calls the multicast. The owner of the gun is the character, which would be fine, right?

sounds right, also make sure the gun is replicated and spawned by server. In case the gun is the problem try calling the event on the player character