Event when replicated actor from server is received by the client?

In short, I am spawning actors from client input to keep delay down, but I need it to “merge” with the server-replicated version when the update arrives. The flow would be client input -> spawn local actor -> send message to server -> server spawns replicated actor to all clients -> instigator client receives replicated actor from server -> instigator client deletes local actor (and ideally some more logic here to make sure there isn’t a snapping effect when the local is deleted and the replicated appears, but that’s another matter).

Is there any functionality built into UE4 to notify when a server-spawned actor has been replicated to a client? I’ve seen onrep_movement as well as onrep variables while snooping around in Actor.h, both of which could be used to accomplish the above, but I was wondering if there is a cleaner inbuilt way of simply being notified when the replicated actor is received, it seems weird that this functionality exists for variables but not for actors as a whole.

1 Like

You could use BeginPlay(), PostNetInit() etc. Any of those are fine.

Unreal Tournament does something similar for predicted projectiles, whereby it simply replaces the closest projectile of matching class and owner when a replicated one is received. Obviously it’s more complex than this, the game fast-forwards projectiles to account for ping etc, but that’s the general gist.

1 Like

I somehow managed to completely gloss over the fact that BeginPlay() would indeed be called on the server-spawned actor when the client first receives it. No wonder I couldn’t find an event for it being received when BeginPlay() (and more) does just that.

Thanks, that’s exactly what I needed and had right under my nose all along.

Have similar issue. We have InventoryComponent with default items. Items are actors. Default items spawn on server side of InventoryComponent and added to inventory. When item has been added to inventory multicast will called to invoke clients callback e.g. put flashlight at skeletal mesh socket.

We have solved that with fixup hack,

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

	if (!GetOwner()->HasAuthority()) return;

	for (auto DefaultItemClass : DefaultItems)
	{
		const auto Item = GetOwner()->GetWorld()->SpawnActor(DefaultItemClass);
		AddItem(Item);
	}
}

void UInventoryComponent::AddItem(AActor* Item)
{
	if (HasItem(Item)) return;

	AddReplicatedSubObject(Item);
	Item->SetOwner(GetOwner());

	Items.Add(Item);

	MulticastReliable_ItemAdded(Item);
}

void UInventoryComponent::MulticastReliable_ItemAdded_Implementation(AActor* Item)
{
	if (!Item)
	{
		// Clients get nullptr cause race condition: actor replication finished after RPC called
		return;
	}

	IInventoryItemInterface::Execute_OnInventoryAdded(Item, this);
}
void AFlashlightItem::BeginPlay()
{
	Super::BeginPlay();

	FixupInventoryBelonging();
}

void AFlashlightItem::FixupInventoryBelonging()
{
	// Dirty hack to solve race condition of UInventoryComponent::MulticastReliable_ItemAdded_Implementation(AActor* Item)
	if (!GetOwner()) return;

	OnInventoryAdded_Implementation(Cast<APawn>(GetOwner())->GetComponentByClass<UInventoryComponent>());
}