Custom NetSerialize on USTRUCT

I am attempting to create a 3dArray struct with a custom NetSerializer that compresses the array first, send the compressed data, then decompressed at the destination. This is what I have so far.

USTRUCT()
struct F3DArray
{
	GENERATED_USTRUCT_BODY()

	F3DArray()
	{
		Data.Init(false, FMath::Pow(32, 3));
	}

	bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
	{
		UE_LOG(LogTemp, Warning, TEXT("Replicate"));
		bOutSuccess = true;
		if (Ar.IsSaving())
		{
			TArray<uint8> Compressed;
			TArray<uint8> UncompressedBuffer;
			FMemoryWriter ArWriter(UncompressedBuffer);
			ArWriter << Blocks;

			const int32 UncompressedSize = UncompressedBuffer.Num();
			const int32 HeaderSize = sizeof(int32);

			Compressed.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedSize));

			int32 CompressedSize = Compressed.Num() - HeaderSize;
			uint8* DestBuffer = Compressed.GetData();
			FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
			DestBuffer += HeaderSize;

			FCompression::CompressMemory(ECompressionFlags::COMPRESS_ZLIB, DestBuffer, CompressedSize, UncompressedBuffer.GetData(), UncompressedSize);

			Compressed.SetNum(CompressedSize + HeaderSize, false);

			Ar << Compressed;
			return true;
		}
		else if (Ar.IsLoading())
		{
			TArray<uint8> Compressed;
			Ar << Compressed;

			TArray<uint8> UncompressedBuffer;
			int32 UncompressedSize = 0;
			const int32 HeaderSize = sizeof(int32);
			uint8* SrcBuffer = Compressed.GetData();
			FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
			SrcBuffer += HeaderSize;
			const int32 CompressedSize = Compressed.Num() - HeaderSize;

			UncompressedBuffer.AddZeroed(UncompressedSize);

			FCompression::UncompressMemory(ECompressionFlags::COMPRESS_ZLIB, UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
			FMemoryReader ArReader(UncompressedBuffer);

			ArReader << Data;
			return true;
		}

		return false;
	}

private:

	TArray<bool> Data;
};
template<>
struct TStructOpsTypeTraits<F3DArray> : public TStructOpsTypeTraitsBase
{
	enum
	{
		WithNetSerializer = true,
	};
};

So, what happens is this - I have some generic Actor in my scene that has this struct as a member variable. I have set it to ReplicateUsing a function. This function IS being called as I have it printing a log statement to the console. The “Data” array is not being replicated though.

Here are some code snippets to show the implementation of a some of the things just mentioned:

	// These are in the actors class
    UPROPERTY(ReplicatedUsing = OnRep_MarkUpdate)
	F3DArray Data;

	UFUNCTION()
	void OnRep_MarkUpdate();

// This is in the actor cpp

void AReplicateTest::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	DOREPLIFETIME(AReplicateTest, Data);
}

void AReplicateTest::OnRep_MarkUpdate()
{
	UE_LOG(LogTemp, Warning, TEXT("Replicated"));
}

And OnRep_MarkUpdate() is being called as I do see the log print out from that - I do NOT however see any logging from the NetSerialize() function included in the USTRUCT. I have also test the compression on it’s own else where, it works fine. I may be incorrectly using the FArchive in NetSerialize() though…

Also, if i place a break point in the OnRep_MarkUpdate() function it does break there, but if I put a break point in the NetSerlialize() function it is NOT hit.

1 Like

Trying to increase unreals actor movement replication to 3 decimals and overriding FRepMovement.
For debug I tried breakpoints, prints and logs in NetSerialize within my struct FRepMovementAdv and functions that are called from NetSerialize. No chance getting anything to work, as you decribed already. Gues those functions are called from another thread? But does it realy matter what thread it executes on in terms of breakpoints?

This may sound weird, but have you tried TStructOpsTypeTraitsBase2 instead of TStructOpsTypeTraitsBase?

In my case I was missing it completely. Though it enough for the parent structure to have it.
Thanks.

template<>
struct TStructOpsTypeTraits<FRepMovementAdv> : public TStructOpsTypeTraitsBase2<FRepMovementAdv>
{
	enum
	{
		WithNetSerializer = true,
	};
};

Not certain if it’s related to your problem, S0rn0, but you’ve forgotten the call to Super::GetLifetimeReplicatedProps(), what might cause all kinds of weird behaviour.