Replicated TArray of UObject, OnRep_ function called two times

Hi, with the use of this wiki A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums
I’ve implemented a class UObject to be replicated. As said in the guide i’ve overridden the method: IsSupportedForNetworking on UObject that simply return true.

In the Actor that hold the data i’ve override the method ReplicateSubobjects.


bool AMyCharacter::ReplicateSubobjects(UActorChannel *Channel, FOutBunch *Bunch, FReplicationFlags *RepFlags){
	bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);

	for (UItem* Item : Items) {
		if (Item ) {
			WroteSomething |= Channel->ReplicateSubobject(Item , *Bunch, *RepFlags);
		}
	}
	return WroteSomething;
}

The UProperty is


UPROPERTY(ReplicatedUsing = OnRep_Items)
	TArray<UItem*> Items;

The problem is when I Add an item to the array, the function OnRep_Items will called 2 times, the first time the item added on server is not present inside the array (on client) the second time the Item is present inside the Array.

When I remove the element the OnRep_Items is called 1 times correctly.

I’ve speculated about this and I’ve come to this, The function is called two times because the replication of TArray and it’s values are not sync.

I’m saying this because it seems to me like if the first call is created only an extra space to fit new value. If I remove the call to “Channel->ReplicateSubobject(Item , *Bunch, *RepFlags);” the OnRep_Items is called one time and only the length of Array is changed.

I’ve to do more to correctly implement a replicated TArray of UObject or this is normal behaviour?

Thanks in advance.

Yeah you’re basically right, the Items array is replicated with the actor, but the actual objects that are referred to are replicated separately. So this means that you will likely first receive a change event on the array with an added nullptr (because the object does not exist client side yet), then another when the object is replicated to the client and the array is updated with the actual pointer. The values inside an array are correctly and immediately replicated, except for pointers, which need to wait for the object they point to to be replicated. In my game I simply ignore any nullptr entries and just wait for them to be replicated before doing any client side logic. You’ll probably find that if you remove an item from the array (without deleting the object it points to on server or client), then adding that object back in it will only call OnRep once with the correct pointer, since the client already knows about that object the server can immediately send the pointer.

Ok understood, thanks for response!!

If anyone else is blocking on this problem, here the solution :

Make a Onrep function proper to the array you want to replicate, with as parameter the same type than the variable. This input will be filled with the old value of the array.

If the old array size is equal to the new one, than it’s the object inside that has replicated.
If the old array size is greater than the new one, than it’s a remove.
If the old array size is lower than the new one, than it’s the array which replicate its size.

void UMyObject::OnRep_MyArray(TArray<UMySubObject*> OldMyArray)
{
	if (OldMyArray.Num() < MyArray.Num()) 
	{
		UE_LOG(LogTemp, Warning, TEXT("The array size increased !"));
	}
	else if(OldMyArray.Num() == MyArray.Num())
	{
		UE_LOG(LogTemp, Warning, TEXT("A UMySubObject replicated"));
	}
	else if(OldMyArray.Num() > MyArray.Num())
	{
		UE_LOG(LogTemp, Warning, TEXT("A UMySubObject was destroyed and removed"));
	}
}