Server actor spawn not replicating spawn request to clients

Hi Again

I’m trying to do something fairly simple, spawn an actor on the server, and get the clients to receive a local copy (which I’ll tear off once I get the !#£$ spawn bit working). I have an class that extends from AActor. Its abstract, and I create the actual class in a blueprint, and the blueprint gets added to my character class as a “BloodTrailClass”.

Everything I’ve read says that if I set the bReplicates or SetReplicates(true) then it should replicate. I’ve put bAlwaysRelevant so that it should go to every client.

No matter what I try, the actor only spawns on the server and is never replicated to the client. Any advice on what I’ve done wrong or how to debug it (would spawn failures get logged somewhere?) would be great.

.h file

#pragma once

#include "FFBloodTrailGib.generated.h"

UCLASS(Abstract, BlueprintType)
class FIREFIGHT_API AFFBloodTrailGib : public AActor

	/** gib mesh */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Gib)
	UStaticMeshComponent* Mesh;

	/** Sphere collision component */
	UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
	USphereComponent* CollisionComp;

	/** Projectile movement component */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement)
	class UProjectileMovementComponent* ProjectileMovement;

	/** particle effect generated as a a trail for this gib*/
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Effects)
	UParticleSystem* TrailEffect;

	void BeginPlay();

	void EffectTimer();

	UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "Create Trail effect"))
	void CreateTrailEffect();

	void OnPhysicsCollision(AActor* OtherActor, UPrimitiveComponent* HitComponent, FVector NormalImpulse, const FHitResult& Hit);

.cpp file

// Fill out your copyright notice in the Description page of Project Settings.

#include "FireFight.h"
#include "FFProjectileMovementComponent.h"
#include "FFBloodTrailGib.h"

AFFBloodTrailGib::AFFBloodTrailGib(const FObjectInitializer& PCIP)
: Super(PCIP)
	bAlwaysRelevant = true;
	bReplicates = true;

	PrimaryActorTick.bCanEverTick = true;

	Mesh = PCIP.CreateOptionalDefaultSubobject<UStaticMeshComponent>(this, FName(TEXT("Mesh")));
	if (Mesh != NULL)
		Mesh->bReceivesDecals = false;
		Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore);
		Mesh->OnComponentHit.AddDynamic(this, &AFFBloodTrailGib::OnPhysicsCollision);
	RootComponent = Mesh;

	CollisionComp = PCIP.CreateOptionalDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));
	if (CollisionComp != NULL)
		CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");			// Collision profiles are defined in DefaultEngine.ini
		CollisionComp->OnComponentHit.AddDynamic(this, &AFFBloodTrailGib::OnPhysicsCollision);
		CollisionComp->bTraceComplexOnMove = true;
		RootComponent = CollisionComp;

	InitialLifeSpan = 3.0f;

	// Use a ProjectileMovementComponent to govern this projectile's movement
	ProjectileMovement = PCIP.CreateDefaultSubobject<UFFProjectileMovementComponent>(this, TEXT("ProjectileComp"));
	ProjectileMovement->UpdatedComponent = CollisionComp;
	ProjectileMovement->InitialSpeed = 3000.f;
	ProjectileMovement->MaxSpeed = 3000.f;
	ProjectileMovement->bRotationFollowsVelocity = true;
	ProjectileMovement->bShouldBounce = false;


void AFFBloodTrailGib::BeginPlay()
	GetWorldTimerManager().SetTimer(this, &AFFBloodTrailGib::EffectTimer, (FMath::FRand() * 0.4f) + 0.02f, true);

void AFFBloodTrailGib::EffectTimer()
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("Blood Trail Tick")));
// TODO: Add a decal on the floor each time this tick happens.

void AFFBloodTrailGib::OnPhysicsCollision(AActor* OtherActor, UPrimitiveComponent* HitComponent, FVector NormalImpulse, const FHitResult& Hit)
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT(" Blood Trail collide - %s"),  *OtherActor->GetHumanReadableName()));

The spawn call

if (Role == ROLE_Authority)
		checkSlow(BloodTrailClass != NULL);

		// try and fire a projectile
		const FVector SpawnLocation = Weapon->GetFireStartLoc();//GetActorLocation();// +NewRelHitLocation;
		const FRotator SpawnRotation = ShotRot;

		FActorSpawnParameters SpawnParams;
		SpawnParams.bNoCollisionFail = true;
		SpawnParams.Owner = this->GetOwner();

		AFFBloodTrailGib* Gib = GetWorld()->SpawnActor<AFFBloodTrailGib>(BloodTrailClass, SpawnLocation, SpawnRotation, SpawnParams);

So more debugging (yey for blueprints). I’m pretty sure now that the actor is getting spawned on the client, but the timer is only getting activated on the server.

Does anyone have any suggestions for how I’d get the client to start a timer once the actor is received. UDK used to have a postnetbeginplay which was called on the client + server, but I cant find anything like it when googling for unrealengine equivalents.

nope. back to the first idea. Conclusive proof only the server is getting a copy. I removed all the code so it only has a mesh, and then added the mesh. The mesh appears on the server, but not on any cleints. So the client isn’t getting a copy of the spawned actor. Can anyone help with client spawning.

I do it similarly to the way ShooterGame does it, get ShooterGame for marketplace and looked at ShooterWeapon_Projectile.cpp

Seriously, it’s an amazing resource for things like this!

I would but its not in there. The unreal marketplace doesnt scale very well :$. Could you paste the file in here so I can have a look or send it to me privately?

NB. I already have client side effects, but what I want is an always relevant effect. This effect will leave decals on the level so even those people that didn’t see the hit effect will see the results when they arrive later on. Were you suggesting the shooter game as a way to generate blood effects or a way to ensure an always relevant actor is spawned?