Download

[4.9.2] OnRep_Structure replicated multiple times when set once?

I have a USTRUCT that contains TArrays of Actors and other USTRUCTs:


USTRUCT(BlueprintType)
struct FComponentInitData
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FShipSubSectionData> SubSectionData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> SubSectionActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FEngineData> EngineData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> EngineActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FShieldData> ShieldData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> ShieldActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FWeaponData> WeaponData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> WeaponActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FSensorData> SensorData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> SensorActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FPowerSupplyData> PowerSupplyData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> PowerSupplyActor;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<struct FCargoHoldData> CargoHoldData;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)	TArray<AActor*> CargoHoldActor;
};

I have made a local variable(LocalInitData) in my SERVER function of this USTRUCT, and set its values as needed.
Then at the end of the SERVER function, I set a global replicated variable(ReppedInitData) of this USTRUCT to replicate it to the CLIENT.

In CLIENT, the OnRep_ReppedInitData() is called multiple times. The number of calls is not the same per “Play Stand Alone Game” test.
Some times it is called 6 times, sometimes more, sometimes less.

I have double and triple checked my code, and placed debug messages to be certain. ReppedInitData is only ever set ONCE on the SERVER.

Why does the CLIENT receive multiple OnRep_ReppedInitData() calls when Ive only set the value once?
How can I ensure it only gets called once?

I’ve had the same issue recently, although in 4.15. Took me a while to dig it, but that’s what I found and how I “fixed” it:

Apparently when you are sending bigger structs across the network (I’ve had an array, few fields and another struct, then inside that struct another array and a few fields, including pointers to actors), the engine may send it in more than 1 packet (that’s my interpretation). You get OnRep multiple times, in my case with the exact same data, which I believe is invalid behavior. Sometimes because there’s so many changes to this struct that it needs to send a lot, sometimes because there are many other actors replicating in the given network driver tick that this structure did not fit and has been delayed for next tick (to not stall the thread ? I guess). In my case it’s been sent twice to the client (rarely, but it indeed happened). Your struct is possibly much bigger than mine, and you have arrays of structs which I did not, so it might be sent more times. There’s been a thread about corrupt structs that I’ve had, but I don’t have the link here, I’ll update when I get to the PC with the link :slight_smile:

I “solved” the issue by reducing the amount of sent data to the bare minimum (which is something you’d do anyway for performance / reducing lag / jitter reasons). So I send enum, int and a pointer to actor, everything else is reacquired by deterministic functions on the client so I know there will never be a de-synchronization event. However, important thing to note (for whoever may read it in the future) - that fixed the main issue (that the structure was sent twice, with the exact same data, field by field), although I still sometimes got the struct replicate twice. It was because on host I spawned the actor and instantly after sent the pointer to the client, and the struct arrived faster than the spawned actor to the client, thus the pointer was null. What’s nice however, is that in such a case the engine will onrep again, when the spawned actor gets to the client through Flush() method. So I ended up checking if the pointer is valid and now I get the replication once, in every case.

Hope I helped, at least a little bit.

EDIT: Here’s the link I wrote about.