641 lines
28 KiB
C++
641 lines
28 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ReplicationSystemServerClientTestFixture.h"
|
|
#include "ReplicationSystemTestFixture.h"
|
|
|
|
#include "Iris/ReplicationSystem/ChangeMaskUtil.h"
|
|
#include "Iris/ReplicationSystem/ReplicationOperations.h"
|
|
#include "Iris/ReplicationSystem/ReplicationOperationsInternal.h"
|
|
#include "Iris/Serialization/NetBitStreamWriter.h"
|
|
#include "Iris/Serialization/NetBitStreamReader.h"
|
|
#include "Iris/Serialization/NetSerializer.h"
|
|
#include "Iris/Serialization/InternalNetSerializationContext.h"
|
|
#include "Iris/ReplicationState/PropertyReplicationState.h"
|
|
#include "Iris/ReplicationSystem/ReplicationTypes.h"
|
|
#include "Misc/MemStack.h"
|
|
|
|
namespace UE::Net::Private
|
|
{
|
|
|
|
struct FTestReplicationOperationsFixture : public FReplicationSystemTestFixture
|
|
{
|
|
FTestReplicationOperationsFixture() : FReplicationSystemTestFixture()
|
|
, TempLinearAllocator(nullptr)
|
|
{
|
|
}
|
|
|
|
template<typename T>
|
|
void VerifyAs(const T& ExpectedValue, const FPropertyReplicationState& State, uint32 StateIndex)
|
|
{
|
|
T TempValue;
|
|
State.GetPropertyValue(StateIndex, &TempValue);
|
|
UE_NET_ASSERT_EQ(ExpectedValue, TempValue);
|
|
}
|
|
|
|
virtual void SetUp() override
|
|
{
|
|
FReplicationSystemTestFixture::SetUp();
|
|
TempLinearAllocator = new FMemStackBase();
|
|
}
|
|
|
|
virtual void TearDown() override
|
|
{
|
|
delete TempLinearAllocator;
|
|
FReplicationSystemTestFixture::TearDown();
|
|
}
|
|
|
|
FMemStackBase& GetTempAllocator() { return *TempLinearAllocator; }
|
|
|
|
alignas(16) uint8 ChangeMaskBuffer[32];
|
|
FNetBitStreamWriter ChangeMaskWriter;
|
|
FMemStackBase* TempLinearAllocator;
|
|
|
|
ChangeMaskStorageType* GetChangeMaskStorage() { return reinterpret_cast<ChangeMaskStorageType*>(ChangeMaskBuffer); }
|
|
|
|
FNetBitStreamWriter* GetChangeMaskWriter(uint32 BitCount)
|
|
{
|
|
check((BitCount >> 3) < sizeof(ChangeMaskBuffer));
|
|
FMemory::Memset(ChangeMaskBuffer, 0);
|
|
ChangeMaskWriter.InitBytes(ChangeMaskBuffer, sizeof(ChangeMaskBuffer));
|
|
|
|
return &ChangeMaskWriter;
|
|
}
|
|
};
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsFixture, CanPollAndCopyPropertyData)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 5U;
|
|
constexpr uint32 IrisComponentCount = 3U;
|
|
constexpr uint32 DynamicStateComponentCount = 3U;
|
|
|
|
UTestReplicatedIrisObject* TestObject = CreateObjectWithDynamicState(PropertyComponentCount, IrisComponentCount, DynamicStateComponentCount);
|
|
|
|
// Create NetRefHandle for the CreatedHandle,
|
|
FNetRefHandle CreatedHandle = ReplicationBridge->BeginReplication(TestObject);
|
|
|
|
auto&& GetPropertyReplicationStateChecked = [](const FReplicationFragment* Fragment)
|
|
{
|
|
check(Fragment && EnumHasAnyFlags(Fragment->GetTraits(), EReplicationFragmentTraits::HasPropertyReplicationState));
|
|
return static_cast<const FPropertyReplicationFragment*>(Fragment)->GetPropertyReplicationState();
|
|
};
|
|
|
|
// Src state for root
|
|
const FPropertyReplicationState& State = *GetPropertyReplicationStateChecked(TestObject->ReplicationFragments[0]);
|
|
const FPropertyReplicationState& ComponentState = *GetPropertyReplicationStateChecked(TestObject->Components[0]->ReplicationFragments[1]);
|
|
|
|
// Verify that we got default values in ReplicationStates
|
|
VerifyAs<int32>(0, State, 0);
|
|
VerifyAs<int32>(0, State, 1);
|
|
VerifyAs<int8>(0, State, 2);
|
|
|
|
// Verify components
|
|
VerifyAs<int32>(0, ComponentState, 0);
|
|
|
|
// Verify iris native component
|
|
UE_NET_ASSERT_EQ(0, TestObject->IrisComponents[0]->ReplicationState.GetIntA());
|
|
UE_NET_ASSERT_EQ(0, TestObject->IrisComponents[0]->ReplicationState.GetIntB());
|
|
UE_NET_ASSERT_EQ(0, TestObject->IrisComponents[0]->ReplicationState.GetIntC());
|
|
|
|
// Verify dynamic state component
|
|
constexpr uint32 DynamicStateComponentIndex = DynamicStateComponentCount/2;
|
|
UE_NET_ASSERT_EQ(TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntArray.Num(), 0);
|
|
UE_NET_ASSERT_EQ(TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntStaticArray[6], int8(0));
|
|
|
|
// Set some values in the root and in each type of component
|
|
TestObject->IntA = 1;
|
|
TestObject->Components[0]->IntA = 2;
|
|
TestObject->IrisComponents[0]->ReplicationState.SetIntA(3);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntArray.SetNum(1);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntArray[0] = 4711;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntStaticArray[5] = 43;
|
|
|
|
// Poll data to update property replication state
|
|
FReplicationInstanceOperations::PollAndCopyPropertyData(ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle));
|
|
|
|
// Verify values after poll
|
|
VerifyAs<int32>(1, State, 0);
|
|
VerifyAs<int32>(0, State, 1);
|
|
VerifyAs<int8>(0, State, 2);
|
|
|
|
// Verify components
|
|
VerifyAs<int32>(2, ComponentState, 0);
|
|
|
|
UE_NET_ASSERT_EQ(3, TestObject->IrisComponents[0]->ReplicationState.GetIntA());
|
|
|
|
UE_NET_ASSERT_EQ(TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntArray.Num(), 1);
|
|
UE_NET_ASSERT_EQ(TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntArray[0], 4711);
|
|
UE_NET_ASSERT_EQ(TestObject->DynamicStateComponents[DynamicStateComponentIndex]->IntStaticArray[5], int8(43));
|
|
|
|
// Destroy the handle
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsFixture, CanQuantizeAndDequantize)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 12U;
|
|
constexpr uint32 IrisComponentCount = 3U;
|
|
constexpr uint32 DynamicStateComponentCount = 3U;
|
|
|
|
UTestReplicatedIrisObject* TestObject = CreateObjectWithDynamicState(PropertyComponentCount, IrisComponentCount, DynamicStateComponentCount);
|
|
|
|
// Create NetRefHandle for the CreatedHandle,
|
|
FNetRefHandle CreatedHandle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* ProtocolA = ReplicationSystem->GetReplicationProtocol(CreatedHandle);
|
|
|
|
// Set some values
|
|
TestObject->IntA = 1;
|
|
TestObject->IntC = 2;
|
|
TestObject->StructD.NotReplicatedIntA = 'A';
|
|
TestObject->StructD.IntB = 'B';
|
|
TestObject->Components[0]->IntA = 3;
|
|
TestObject->Components[11]->IntA = 4;
|
|
TestObject->IrisComponents[0]->ReplicationState.SetIntA(5);
|
|
TestObject->IrisComponents[2]->ReplicationState.SetIntA(6);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray.SetNumUninitialized(3);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[0] = 1;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[1] = 2;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[2] = 3;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntStaticArray[4] = 127;
|
|
|
|
// Poll data to update property replication state
|
|
FReplicationInstanceOperations::PollAndCopyPropertyData(ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle));
|
|
|
|
// Quantize to buffer
|
|
const uint32 BufferSize = 1024;
|
|
const uint8 ValueToSet = 0xCD;
|
|
|
|
// Need to be able to handle worst case alignment
|
|
UE_NET_ASSERT_GE(BufferSize, ProtocolA->InternalTotalSize + ProtocolA->InternalTotalAlignment);
|
|
|
|
uint8 Buffer[BufferSize];
|
|
FMemory::Memset(Buffer, ValueToSet, BufferSize);
|
|
uint8* AlignedBuffer = Align(Buffer, ProtocolA->InternalTotalAlignment);
|
|
// The part of the buffer that will hold the internal state needs to be cleared due to the presence of dynamic state
|
|
FMemory::Memzero(AlignedBuffer, ProtocolA->InternalTotalSize);
|
|
|
|
FNetSerializationContext SerializationContext;
|
|
FInternalNetSerializationContext InternalContext;
|
|
SerializationContext.SetInternalContext(&InternalContext);
|
|
SerializationContext.SetIsInitState(true);
|
|
|
|
// Copy data from test object to buffer
|
|
FReplicationInstanceOperations::Quantize(SerializationContext, AlignedBuffer, GetChangeMaskWriter(ProtocolA->ChangeMaskBitCount), ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle), ProtocolA);
|
|
|
|
// Verify that we did not overshoot
|
|
UE_NET_ASSERT_EQ(ValueToSet, AlignedBuffer[ProtocolA->InternalTotalSize]);
|
|
|
|
// Create second object
|
|
UTestReplicatedIrisObject* TestObjectB = CreateObjectWithDynamicState(PropertyComponentCount, IrisComponentCount, DynamicStateComponentCount);
|
|
ReplicationBridge->BeginReplication(TestObjectB);
|
|
const FReplicationProtocol* ProtocolB = ReplicationSystem->GetReplicationProtocol(TestObjectB->NetRefHandle);
|
|
UE_NET_ASSERT_EQ(ProtocolA, ProtocolB);
|
|
|
|
// Push state data to ObjectB
|
|
FReplicationInstanceOperations::DequantizeAndApply(SerializationContext, GetTempAllocator(), GetChangeMaskStorage(), ReplicationBridge->GetReplicationInstanceProtocol(TestObjectB->NetRefHandle), AlignedBuffer, ProtocolB);
|
|
|
|
// check if we did get the expected values
|
|
UE_NET_ASSERT_EQ(1, TestObjectB->IntA);
|
|
UE_NET_ASSERT_EQ((int8)2, TestObjectB->IntC);
|
|
UE_NET_ASSERT_EQ_MSG(0, TestObjectB->StructD.NotReplicatedIntA, "Property that isn't replicated has been overwritten");
|
|
UE_NET_ASSERT_EQ(int32('B'), TestObjectB->StructD.IntB);
|
|
UE_NET_ASSERT_EQ(3, TestObjectB->Components[0]->IntA);
|
|
UE_NET_ASSERT_EQ(4, TestObjectB->Components[11]->IntA);
|
|
|
|
FTestReplicatedIrisComponent* Comp0 = TestObjectB->IrisComponents[0].Get();
|
|
FTestReplicatedIrisComponent* Comp2 = TestObjectB->IrisComponents[2].Get();
|
|
|
|
UE_NET_ASSERT_EQ(5, Comp0->ReplicationState.GetIntA());
|
|
UE_NET_ASSERT_EQ(6, Comp2->ReplicationState.GetIntA());
|
|
|
|
for (uint32 DynamicComponentIt = 0; DynamicComponentIt < DynamicStateComponentCount - 1; ++DynamicComponentIt)
|
|
{
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicComponentIt]->IntArray.Num(), 0);
|
|
}
|
|
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray.Num(), 3);
|
|
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[0], 1);
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[1], 2);
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[2], 3);
|
|
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntStaticArray[4], int8(127));
|
|
|
|
// Free dynamic state
|
|
FReplicationProtocolOperations::FreeDynamicState(SerializationContext, AlignedBuffer, ProtocolA);
|
|
|
|
// Destroy the handle
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsFixture, CanSerializeAndDeserialize)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 12U;
|
|
constexpr uint32 IrisComponentCount = 3U;
|
|
constexpr uint32 DynamicStateComponentCount = 3U;
|
|
|
|
UTestReplicatedIrisObject* TestObject = CreateObjectWithDynamicState(PropertyComponentCount, IrisComponentCount, DynamicStateComponentCount);
|
|
|
|
// Create NetRefHandle for the CreatedHandle,
|
|
FNetRefHandle CreatedHandle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* ProtocolA = ReplicationSystem->GetReplicationProtocol(CreatedHandle);
|
|
|
|
// Set some values
|
|
TestObject->IntA = 1;
|
|
TestObject->IntC = 2;
|
|
TestObject->Components[0]->IntA = 3;
|
|
TestObject->Components[11]->IntA = 4;
|
|
TestObject->IrisComponents[0]->ReplicationState.SetIntA(5);
|
|
TestObject->IrisComponents[2]->ReplicationState.SetIntA(6);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray.SetNumUninitialized(3);
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[0] = 1;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[1] = 2;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[2] = 3;
|
|
TestObject->DynamicStateComponents[DynamicStateComponentCount - 1]->IntStaticArray[4] = 127;
|
|
|
|
// Poll data to update property replication state
|
|
FReplicationInstanceOperations::PollAndCopyPropertyData(ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle));
|
|
|
|
// Quantize to buffer
|
|
const uint32 BufferSize = 1024;
|
|
const uint8 ValueToSet = 0xFE;
|
|
|
|
UE_NET_ASSERT_GT(BufferSize, ProtocolA->InternalTotalSize);
|
|
|
|
uint8 Buffer[2][BufferSize];
|
|
FMemory::Memset(Buffer, ValueToSet, 2*BufferSize);
|
|
uint8* AlignedBuffer = Align(Buffer[0], ProtocolA->InternalTotalAlignment);
|
|
uint8* AlignedBufferB = Align(Buffer[1], ProtocolA->InternalTotalAlignment);
|
|
FMemory::Memzero(AlignedBuffer, ProtocolA->InternalTotalSize);
|
|
FMemory::Memzero(AlignedBufferB, ProtocolA->InternalTotalSize);
|
|
|
|
// Copy data from test object to buffer
|
|
FNetSerializationContext SerializationContext;
|
|
FInternalNetSerializationContext InternalContext;
|
|
SerializationContext.SetInternalContext(&InternalContext);
|
|
SerializationContext.SetIsInitState(true);
|
|
FReplicationInstanceOperations::Quantize(SerializationContext, AlignedBuffer, GetChangeMaskWriter(ProtocolA->ChangeMaskBitCount), ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle), ProtocolA);
|
|
|
|
// Verify that we did not overshoot
|
|
UE_NET_ASSERT_EQ(ValueToSet, AlignedBuffer[ProtocolA->InternalTotalSize]);
|
|
|
|
// Create second object
|
|
UTestReplicatedIrisObject* TestObjectB = CreateObjectWithDynamicState(PropertyComponentCount, IrisComponentCount, DynamicStateComponentCount);
|
|
ReplicationBridge->BeginReplication(TestObjectB);
|
|
const FReplicationProtocol* ProtocolB = ReplicationSystem->GetReplicationProtocol(TestObjectB->NetRefHandle);
|
|
UE_NET_ASSERT_EQ(ProtocolA, ProtocolB);
|
|
|
|
|
|
// Serialize state data
|
|
uint8 BitStreamBuffer[BufferSize];
|
|
|
|
FNetBitStreamWriter Writer;
|
|
{
|
|
|
|
Writer.InitBytes(BitStreamBuffer, BufferSize);
|
|
|
|
FNetSerializationContext Context(&Writer);
|
|
Context.SetInternalContext(&InternalContext);
|
|
Context.SetIsInitState(true);
|
|
|
|
FReplicationProtocolOperations::SerializeWithMask(Context, GetChangeMaskStorage(), AlignedBuffer, ProtocolA);
|
|
|
|
Writer.CommitWrites();
|
|
|
|
FReplicationProtocolOperations::FreeDynamicState(SerializationContext, AlignedBuffer, ProtocolA);
|
|
}
|
|
|
|
// Deserialize state data
|
|
{
|
|
FNetBitStreamReader Reader;
|
|
Reader.InitBits(BitStreamBuffer, Writer.GetPosBits());
|
|
|
|
FNetSerializationContext Context(&Reader);
|
|
Context.SetInternalContext(&InternalContext);
|
|
Context.SetIsInitState(true);
|
|
|
|
// Reset changemask buffer
|
|
FMemory::Memset(ChangeMaskBuffer, 0u);
|
|
|
|
// Read data into AlignedBuffer
|
|
FReplicationProtocolOperations::DeserializeWithMask(Context, GetChangeMaskStorage(), AlignedBufferB, ProtocolB);
|
|
|
|
// Push deserialized state data to ObjectB
|
|
FReplicationInstanceOperations::DequantizeAndApply(Context, GetTempAllocator(), GetChangeMaskStorage(), ReplicationBridge->GetReplicationInstanceProtocol(TestObjectB->NetRefHandle), AlignedBufferB, ProtocolB);
|
|
}
|
|
|
|
// check if we did get the expected values
|
|
UE_NET_ASSERT_EQ(1, TestObjectB->IntA);
|
|
UE_NET_ASSERT_EQ((int8)2, TestObjectB->IntC);
|
|
UE_NET_ASSERT_EQ(3, TestObjectB->Components[0]->IntA);
|
|
UE_NET_ASSERT_EQ(4, TestObjectB->Components[11]->IntA);
|
|
|
|
FTestReplicatedIrisComponent* Comp0 = TestObjectB->IrisComponents[0].Get();
|
|
FTestReplicatedIrisComponent* Comp2 = TestObjectB->IrisComponents[2].Get();
|
|
|
|
UE_NET_ASSERT_EQ(5, TestObjectB->IrisComponents[0]->ReplicationState.GetIntA());
|
|
UE_NET_ASSERT_EQ(6, TestObjectB->IrisComponents[2]->ReplicationState.GetIntA());
|
|
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray.Num(), 3);
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[0], 1);
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[1], 2);
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntArray[2], 3);
|
|
|
|
UE_NET_ASSERT_EQ(TestObjectB->DynamicStateComponents[DynamicStateComponentCount - 1]->IntStaticArray[4], int8(127));
|
|
|
|
FReplicationProtocolOperations::FreeDynamicState(SerializationContext, AlignedBuffer, ProtocolA);
|
|
FReplicationProtocolOperations::FreeDynamicState(SerializationContext, AlignedBufferB, ProtocolA);
|
|
|
|
// Destroy the handle
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, PreAndPostNetReceivedOnlyCalledOnce)
|
|
{
|
|
// Create replicated object
|
|
UTestReplicatedIrisObject* ServerTestObject = Server->CreateObject(1, 0);
|
|
Server->ReplicationBridge->BeginReplication(ServerTestObject);
|
|
|
|
// Set some values
|
|
ServerTestObject->Components[0]->IntA = 3;
|
|
ServerTestObject->Components[0]->IntB = 3;
|
|
|
|
// Add a client
|
|
FReplicationSystemTestClient* Client = CreateClient();
|
|
|
|
// Spawn the replicated instance
|
|
Server->UpdateAndSend({ Client });
|
|
|
|
// Find the client instance and make sure it replicated
|
|
UTestReplicatedIrisObject* ClientTestObject = Cast<UTestReplicatedIrisObject>(Client->ReplicationBridge->GetReplicatedObject(ServerTestObject->NetRefHandle));
|
|
UE_NET_ASSERT_NE(ClientTestObject, nullptr);
|
|
UE_NET_ASSERT_EQ(3, ClientTestObject->Components[0]->IntA);
|
|
UE_NET_ASSERT_EQ(3, ClientTestObject->Components[0]->IntB);
|
|
|
|
// Check NetReceive callbacks
|
|
UE_NET_ASSERT_EQ(1U, ClientTestObject->Components[0]->CallCounts.PreNetReceiveCounter);
|
|
UE_NET_ASSERT_EQ(1U, ClientTestObject->Components[0]->CallCounts.PostNetReceiveCounter);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, RepNotifyIsCalledForSimpleProperty)
|
|
{
|
|
// Create replicated object
|
|
UTestReplicatedIrisObject* ServerTestObject = Server->CreateObject(1, 0);
|
|
Server->ReplicationBridge->BeginReplication(ServerTestObject);
|
|
|
|
// Set some values
|
|
ServerTestObject->Components[0]->IntB = 3;
|
|
|
|
// Add a client
|
|
FReplicationSystemTestClient* Client = CreateClient();
|
|
|
|
// Spawn the replicated instance
|
|
Server->UpdateAndSend({ Client });
|
|
|
|
// Find the client instance and make sure it replicated
|
|
UTestReplicatedIrisObject* ClientTestObject = Cast<UTestReplicatedIrisObject>(Client->ReplicationBridge->GetReplicatedObject(ServerTestObject->NetRefHandle));
|
|
UE_NET_ASSERT_NE(ClientTestObject, nullptr);
|
|
UE_NET_ASSERT_EQ(3, ClientTestObject->Components[0]->IntB);
|
|
|
|
// Check that we got exactly one RepNotify call
|
|
UE_NET_ASSERT_EQ(ClientTestObject->Components[0]->CallCounts.IntBRepNotifyCounter, 1U);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsFixture, InitPropertyIsNotUpdatedWhenInitStateIsExcluded)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 1;
|
|
UTestReplicatedIrisObject* TestObject = CreateObject(PropertyComponentCount, 0);
|
|
|
|
// Create NetRefHandle for the CreatedHandle,
|
|
FNetRefHandle CreatedHandle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* Protocol = ReplicationSystem->GetReplicationProtocol(CreatedHandle);
|
|
|
|
// Set some values
|
|
TestObject->Components[0]->IntB ^= 1;
|
|
|
|
// Poll data to update property replication state
|
|
FReplicationInstanceOperations::PollAndCopyPropertyData(ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle));
|
|
|
|
// Quantize to buffer
|
|
uint8* AlignedBuffer = (uint8*)(GetTempAllocator()).Alloc(Protocol->InternalTotalSize, Protocol->InternalTotalAlignment);
|
|
|
|
// Copy data from test object to buffer
|
|
FNetSerializationContext NetContext;
|
|
NetContext.SetIsInitState(false);
|
|
FReplicationInstanceOperations::Quantize(NetContext, AlignedBuffer, GetChangeMaskWriter(Protocol->ChangeMaskBitCount), ReplicationBridge->GetReplicationInstanceProtocol(TestObject->NetRefHandle), Protocol);
|
|
|
|
// Create second object
|
|
UTestReplicatedIrisObject* TestObjectB = CreateObject(PropertyComponentCount, 0);
|
|
ReplicationBridge->BeginReplication(TestObjectB);
|
|
|
|
// Check that we've not yet gotten any RepNotify calls
|
|
UE_NET_ASSERT_EQ(TestObjectB->Components[0]->CallCounts.IntBRepNotifyCounter, 0U);
|
|
|
|
// Push state data to ObjectB
|
|
FReplicationInstanceOperations::DequantizeAndApply(NetContext, GetTempAllocator(), GetChangeMaskStorage(), ReplicationBridge->GetReplicationInstanceProtocol(TestObjectB->NetRefHandle), AlignedBuffer, Protocol);
|
|
|
|
// Check that the IntB property has not been modified
|
|
UE_NET_ASSERT_EQ(TestObjectB->Components[0]->IntB, 0);
|
|
|
|
// Check that we did not get a RepNotify call for the init property.
|
|
UE_NET_ASSERT_EQ(TestObjectB->Components[0]->CallCounts.IntBRepNotifyCounter, 0U);
|
|
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
ReplicationBridge->EndReplication(TestObjectB);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, RepNotifyIsCalledExactlyOnceForArrays)
|
|
{
|
|
// Create replicated object
|
|
UTestReplicatedIrisObject* ServerTestObject = Server->CreateObjectWithDynamicState(0, 0, 1);
|
|
Server->ReplicationBridge->BeginReplication(ServerTestObject);
|
|
|
|
// Set some values
|
|
ServerTestObject->DynamicStateComponents[0]->IntArray.SetNum(4);
|
|
ServerTestObject->DynamicStateComponents[0]->IntArray[0] = 1;
|
|
ServerTestObject->DynamicStateComponents[0]->IntArray[1] = 1;
|
|
ServerTestObject->DynamicStateComponents[0]->IntArray[2] = 7;
|
|
ServerTestObject->DynamicStateComponents[0]->IntArray[3] = 4;
|
|
|
|
ServerTestObject->DynamicStateComponents[0]->IntStaticArray[1] = 5;
|
|
ServerTestObject->DynamicStateComponents[0]->IntStaticArray[5] = 1;
|
|
|
|
// Add a client
|
|
FReplicationSystemTestClient* Client = CreateClient();
|
|
|
|
// Spawn the replicated instance
|
|
Server->UpdateAndSend({ Client });
|
|
|
|
// Find the client instance and make sure it replicated
|
|
UTestReplicatedIrisObject* ClientTestObject = Cast<UTestReplicatedIrisObject>(Client->ReplicationBridge->GetReplicatedObject(ServerTestObject->NetRefHandle));
|
|
UE_NET_ASSERT_NE(ClientTestObject, nullptr);
|
|
UE_NET_ASSERT_EQ(4, ClientTestObject->DynamicStateComponents[0]->IntArray.Num());
|
|
|
|
// Check that we got exactly one RepNotify call per array
|
|
UE_NET_ASSERT_EQ(ClientTestObject->DynamicStateComponents[0]->CallCounts.IntArrayRepNotifyCounter, 1U);
|
|
UE_NET_ASSERT_EQ(ClientTestObject->DynamicStateComponents[0]->CallCounts.IntStaticArrayRepNotifyCounter, 1U);
|
|
}
|
|
|
|
class FTestReplicationOperationsForObjectsFixture : public FTestReplicationOperationsFixture
|
|
{
|
|
protected:
|
|
virtual void SetUp() override
|
|
{
|
|
CvarIrisPushModel = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Iris.PushModelMode"));
|
|
check(CvarIrisPushModel != nullptr && CvarIrisPushModel->IsVariableInt());
|
|
PushModelMode = CvarIrisPushModel->GetInt();
|
|
CvarIrisPushModel->Set(1, ECVF_SetByCode);
|
|
|
|
FTestReplicationOperationsFixture::SetUp();
|
|
}
|
|
|
|
virtual void TearDown() override
|
|
{
|
|
FTestReplicationOperationsFixture::TearDown();
|
|
|
|
CvarIrisPushModel->Set(PushModelMode, ECVF_SetByCode);
|
|
}
|
|
|
|
private:
|
|
IConsoleVariable* CvarIrisPushModel = nullptr;
|
|
int PushModelMode = 0;
|
|
};
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsForObjectsFixture, StaleObjectPointerIsUpdated)
|
|
{
|
|
// Create two objects, one which is referencing the other. Verify reference gets updated when first object is destroyed.
|
|
UTestReplicatedIrisObject* Object0 = CreateObject();
|
|
FNetRefHandle HandleToObject0 = ReplicationBridge->BeginReplication(Object0);
|
|
|
|
UTestReplicatedIrisObject* Object1 = CreateObject();
|
|
constexpr uint32 ObjectReferenceComponentCount = 1;
|
|
Object1->AddComponents(UTestReplicatedIrisObject::FComponents{0,0,0,0,ObjectReferenceComponentCount});
|
|
Object1->ObjectReferenceComponents[0]->RawObjectPtrRef = Object0;
|
|
FNetRefHandle HandleToObject1 = ReplicationBridge->BeginReplication(Object1);
|
|
|
|
const FReplicationInstanceProtocol* InstanceProtocol1 = ReplicationBridge->GetReplicationInstanceProtocol(HandleToObject1);
|
|
const FReplicationProtocol* Protocol1 = ReplicationSystem->GetReplicationProtocol(HandleToObject1);
|
|
|
|
// Quantize to buffer
|
|
uint8* StateBuffers1[] =
|
|
{
|
|
(uint8*)(GetTempAllocator()).Alloc(Protocol1->InternalTotalSize, Protocol1->InternalTotalAlignment),
|
|
(uint8*)(GetTempAllocator()).Alloc(Protocol1->InternalTotalSize, Protocol1->InternalTotalAlignment),
|
|
};
|
|
for (uint8* StateBuffer : StateBuffers1)
|
|
{
|
|
FMemory::Memzero(StateBuffer, Protocol1->InternalTotalSize);
|
|
}
|
|
|
|
FNetSerializationContext SerializationContext;
|
|
FInternalNetSerializationContext InternalContext(ReplicationSystem);
|
|
SerializationContext.SetInternalContext(&InternalContext);
|
|
SerializationContext.SetIsInitState(true);
|
|
|
|
// Copy data from test object to buffer
|
|
ReplicationSystem->NetUpdate(0.033f);
|
|
FReplicationInstanceOperations::Quantize(SerializationContext, StateBuffers1[0], GetChangeMaskWriter(Protocol1->ChangeMaskBitCount), InstanceProtocol1, Protocol1);
|
|
ReplicationSystem->PostSendUpdate();
|
|
|
|
// Destroy first object and invalidate references to it.
|
|
ReplicationBridge->EndReplication(Object0);
|
|
DestroyObject(Object0);
|
|
constexpr bool bPerformFullPurge = false;
|
|
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, bPerformFullPurge);
|
|
|
|
ReplicationSystem->NetUpdate(0.033f);
|
|
FReplicationInstanceOperations::Quantize(SerializationContext, StateBuffers1[1], GetChangeMaskWriter(Protocol1->ChangeMaskBitCount), InstanceProtocol1, Protocol1);
|
|
ReplicationSystem->PostSendUpdate();
|
|
|
|
// Compare the two state buffers. Despite that we haven't manually changed any object references we expect them to differ thanks to garbage collection support.
|
|
UE_NET_ASSERT_FALSE(FReplicationProtocolOperationsInternal::IsEqualQuantizedState(SerializationContext, StateBuffers1[0], StateBuffers1[1], Protocol1));
|
|
}
|
|
|
|
class FTestReplicationOperationsOnInitStateFixture : public FTestReplicationOperationsFixture
|
|
{
|
|
protected:
|
|
static const FReplicationStateDescriptor* GetFirstInitStateDescriptor(const FReplicationProtocol* Protocol)
|
|
{
|
|
for (const FReplicationStateDescriptor* StateDescriptor : MakeArrayView(Protocol->ReplicationStateDescriptors, Protocol->ReplicationStateCount))
|
|
{
|
|
if (EnumHasAnyFlags(StateDescriptor->Traits, EReplicationStateTraits::InitOnly))
|
|
{
|
|
return StateDescriptor;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsOnInitStateFixture, StateWithInitTraitExists)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 2;
|
|
UTestReplicatedIrisObject* TestObject = CreateObject(PropertyComponentCount, 0);
|
|
|
|
const FNetRefHandle Handle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* Protocol = ReplicationSystem->GetReplicationProtocol(Handle);
|
|
uint32 InitStateCount = 0;
|
|
for (const FReplicationStateDescriptor* StateDescriptor : MakeArrayView(Protocol->ReplicationStateDescriptors, Protocol->ReplicationStateCount))
|
|
{
|
|
InitStateCount += EnumHasAnyFlags(StateDescriptor->Traits, EReplicationStateTraits::InitOnly);
|
|
}
|
|
// There should be one init state per component having an init property
|
|
UE_NET_ASSERT_EQ(InitStateCount, PropertyComponentCount);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsOnInitStateFixture, InitStateHasNoChangeMask)
|
|
{
|
|
constexpr uint32 PropertyComponentCount = 1;
|
|
UTestReplicatedIrisObject* TestObject = CreateObject(PropertyComponentCount, 0);
|
|
|
|
const FNetRefHandle Handle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* Protocol = ReplicationSystem->GetReplicationProtocol(Handle);
|
|
const FReplicationStateDescriptor* InitStateDescriptor = GetFirstInitStateDescriptor(Protocol);
|
|
|
|
UE_NET_ASSERT_EQ(InitStateDescriptor->ChangeMaskBitCount, uint16(0));
|
|
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
class FTestReplicationOperationsOnConnectionFilterStateFixture : public FTestReplicationOperationsFixture
|
|
{
|
|
protected:
|
|
};
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsOnConnectionFilterStateFixture, ProtocolHasLifetimeConditionalsTrait)
|
|
{
|
|
UTestReplicatedIrisObject::FComponents Components;
|
|
Components.ConnectionFilteredComponentCount = 2;
|
|
UTestReplicatedIrisObject* TestObject = CreateObject();
|
|
TestObject->AddComponents(Components);
|
|
|
|
const FNetRefHandle Handle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* Protocol = ReplicationSystem->GetReplicationProtocol(Handle);
|
|
|
|
UE_NET_ASSERT_TRUE(EnumHasAnyFlags(Protocol->ProtocolTraits, EReplicationProtocolTraits::HasLifetimeConditionals));
|
|
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
UE_NET_TEST_FIXTURE(FTestReplicationOperationsOnConnectionFilterStateFixture, StateWitHasLifetimeConditionalsTraitExists)
|
|
{
|
|
UTestReplicatedIrisObject::FComponents Components;
|
|
Components.ConnectionFilteredComponentCount = 2;
|
|
UTestReplicatedIrisObject* TestObject = CreateObject();
|
|
TestObject->AddComponents(Components);
|
|
|
|
const FNetRefHandle Handle = ReplicationBridge->BeginReplication(TestObject);
|
|
const FReplicationProtocol* Protocol = ReplicationSystem->GetReplicationProtocol(Handle);
|
|
uint32 LifetimeConditionalsStateCount = 0;
|
|
for (const FReplicationStateDescriptor* StateDescriptor : MakeArrayView(Protocol->ReplicationStateDescriptors, Protocol->ReplicationStateCount))
|
|
{
|
|
LifetimeConditionalsStateCount += EnumHasAnyFlags(StateDescriptor->Traits, EReplicationStateTraits::HasLifetimeConditionals);
|
|
}
|
|
|
|
UE_NET_ASSERT_EQ(LifetimeConditionalsStateCount, Components.ConnectionFilteredComponentCount);
|
|
|
|
ReplicationBridge->EndReplication(TestObject);
|
|
}
|
|
|
|
}
|