Files
UnrealEngine/Engine/Plugins/Runtime/ReplicationSystemTestPlugin/Source/Private/Tests/ReplicationSystem/TestReplicationSystem.cpp
2025-05-18 13:04:45 +08:00

2528 lines
98 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ReplicationSystemServerClientTestFixture.h"
#include "HAL/IConsoleManager.h"
#include "Iris/Core/IrisLog.h"
#include "Iris/Metrics/NetMetrics.h"
#include "Iris/ReplicationSystem/ReplicationRecord.h"
#include "Iris/ReplicationSystem/ReplicationSystem.h"
#include "Iris/ReplicationSystem/ReplicationSystemInternal.h"
#include "Iris/Serialization/NetBitStreamReader.h"
#include "Iris/Serialization/NetBitStreamWriter.h"
#include "Logging/LogScopedVerbosityOverride.h"
#include "Misc/ScopeExit.h"
#include "Net/Core/NetToken/NetToken.h"
#include "UObject/CoreNetTypes.h"
namespace UE::Net::Private
{
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySingleObject)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0,0);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server handle now also exists on client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObject->NetRefHandle));
// Destroy the spawned object on server
Server->DestroyObject(ServerObject);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that object now is destroyed on client as well
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObject->NetRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDroppedDestroyed)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0,0);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server handle now also exists on client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObject->NetRefHandle));
// Destroy the spawned object on server
Server->DestroyObject(ServerObject);
// Send and drop packet
Server->UpdateAndSend({Client}, DoNotDeliverPacket);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that object now is destroyed on client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObject->NetRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDestroyWhilePendingCreate)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0,0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Send and drop packet
Server->UpdateAndSend({Client}, DoNotDeliverPacket);
// Destroy
Server->DestroyObject(ServerObject);
// Verify that the object does not exist on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
// Send and drop packet
Server->UpdateAndSend({Client}, DoNotDeliverPacket);
// Send and deliver packet
Server->UpdateAndSend({Client}, DeliverPacket);
// Verify the object still doesn't exist on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDestroyWhileWaitingOnCreate)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0,0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Write packet with create
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Destroy while we are waiting for confirmation
Server->DestroyObject(ServerObject);
// Write packet with destroy
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Drop packet with create
Server->DeliverTo(Client, DoNotDeliverPacket);
// Verify that the object does not exists on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
// Deliver packet with destroy
Server->DeliverTo(Client, DeliverPacket);
// Verify the object still doesn't exist on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDestroyWithDataInFlight)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
Server->GetReplicationSystem()->SetStaticPriority(ServerObjectRefHandle, 1.f);
// Send packet with create
Server->UpdateAndSend({Client});
// Verify that the object exists on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
// Modify some data to mark object dirty
ServerObject->IntA = 13;
// Write a packet with updated data
Server->NetUpdate();
UE_NET_ASSERT_TRUE(Server->SendTo(Client));
Server->PostSendUpdate();
// Destroy while we are waiting for ack on update
Server->DestroyObject(ServerObject);
// Write packet with destroy
Server->NetUpdate();
UE_NET_ASSERT_TRUE(Server->SendTo(Client));
Server->PostSendUpdate();
// Drop and report packet with update as lost
Server->DeliverTo(Client, DoNotDeliverPacket);
// Verify that the object still exists on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
// Deliver packet with destroy
Server->DeliverTo(Client, DeliverPacket);
// Verify that the object is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySubObject)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0,0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn second object on server as a subobject
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server objects now also exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that only the subobject is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroyMultipleSubObjects)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn subobject on server
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server objects now also exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that the subobject is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
// Spawn second object on server as a subobject
ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that second subobject replicated properly to server
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned object on server
Server->DestroyObject(ServerSubObject);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that the second subobjects object is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySubObjectAndDestroyOwner)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn subobject on server
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server handles now also exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// Destroy owner after spawned subobject on server
Server->DestroyObject(ServerObject);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that the subobject is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
// Verify that the root object is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySubObjectAndDestroyOwnerWithDataInFlight)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn subobject on server
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server handles now also exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// Send and drop packet
Server->UpdateAndSend({Client}, DoNotDeliverPacket);
// Destroy owner after we spawned subobject on server
Server->DestroyObject(ServerObject);
// Send and deliver packet
Server->UpdateAndSend({Client}, DeliverPacket);
// Verify that the subobject is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySubObjectWithLostData)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn subobject on server
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that created server objects exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// Send and drop packet
Server->UpdateAndSend({Client}, DoNotDeliverPacket);
// Send and deliver packet
Server->UpdateAndSend({Client}, DeliverPacket);
// Verify that subobject is destroyed on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, ReplicateAndDestroySubObjectPendingCreateConfirmation)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Spawn subobject on server
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
uint32 NumUnAcknowledgedPackets = 0;
// Write a packet
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Destroy the spawned subobject on server
Server->DestroyObject(ServerSubObject);
// We have no data to send but we want to tick ReplicationSystem to capture state change
Server->NetUpdate();
UE_NET_ASSERT_FALSE(Server->SendTo(Client));
Server->PostSendUpdate();
// Drop creation packet
Server->DeliverTo(Client, DoNotDeliverPacket);
// As the second update did not send any data we do not have anything to deliver
//Server->DeliverTo(Client, true);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that the subobject does not exist on the client
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
// The root object should exist on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
}
// In this test we're going to create a subobject after the root has been created on the client. Then create it with a bit of latency and destroy root prior to subobject being created on the client.
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, LateCreatedSubObjectIsDestroyedWithRoot)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(UTestReplicatedIrisObject::FComponents{});
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
// Send and deliver packet
Server->UpdateAndSend({Client});
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
// Spawn subobject
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject(ServerObjectRefHandle, UTestReplicatedIrisObject::FComponents{});
const FNetRefHandle ServerSubObjectRefHandle = ServerSubObject->NetRefHandle;
// Put subobject in WaitOnCreateConfirmation state
Server->NetUpdate();
Server->SendUpdate(Client->ConnectionIdOnServer);
Server->PostSendUpdate();
// Destroy the root object
Server->DestroyObject(ServerObject);
// Write a packet
Server->NetUpdate();
Server->SendUpdate(Client->ConnectionIdOnServer);
Server->PostSendUpdate();
// Deliver first packet
Server->DeliverTo(Client, DeliverPacket);
// Verify the root and subobject are created/still created on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Deliver second packet
Server->DeliverTo(Client, DeliverPacket);
// Verify the root and subobject are created/still created on the client
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerSubObjectRefHandle));
// Update and send
Server->UpdateAndSend({Client});
// Verify the root and subobject are fully destroyed
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerSubObjectRefHandle));
}
// In this test we're going to try destroying an object with thousands of subobjects atomically.
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, CanDestroyObjectHierarchyAtomically)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(UTestReplicatedIrisObject::FComponents{});
const FNetRefHandle ServerObjectRefHandle = ServerObject->NetRefHandle;
constexpr unsigned SubObjectCount = 2001;
TArray<FNetRefHandle> ServerSubObjectRefHandles;
ServerSubObjectRefHandles.Reserve(SubObjectCount);
// Spawn thousands of subobjects over several frames to avoid huge object path.
for (;ServerSubObjectRefHandles.Num() < SubObjectCount;)
{
constexpr uint32 MaxSubObjectCreationCountPerFrame = 15;
for (unsigned It = 0; It < MaxSubObjectCreationCountPerFrame && ServerSubObjectRefHandles.Num() < SubObjectCount; ++It)
{
UReplicatedTestObject* ServerSubObject = Server->CreateSubObject<UReplicatedTestObject>(ServerObjectRefHandle);
ServerSubObjectRefHandles.Add(ServerSubObject->NetRefHandle);
}
Server->UpdateAndSend({Client});
}
// We expect to be done creating the object hierarchy by now. Make sure of it.
UE_NET_ASSERT_FALSE(Server->UpdateAndSend({ Client }));
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(ServerObjectRefHandle));
for (const FNetRefHandle SubObjectRefHandle : ServerSubObjectRefHandles)
{
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(SubObjectRefHandle));
}
// Destroy the root object
Server->DestroyObject(ServerObject);
for (unsigned It = 0, MaxTryCount = SubObjectCount/50; It < MaxTryCount; ++It)
{
const bool bDidSendSomething = Server->UpdateAndSend({ Client });
// Verify the object hierarchy is destroyed as a whole or not at all. Once we've stopped sending data we should have destroyed the object as a whole on the client.
const bool bRootIsResolvable = Client->IsResolvableNetRefHandle(ServerObjectRefHandle);
if (bRootIsResolvable && bDidSendSomething)
{
for (const FNetRefHandle SubObjectRefHandle : ServerSubObjectRefHandles)
{
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(SubObjectRefHandle));
}
}
else
{
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(ServerObjectRefHandle));
for (const FNetRefHandle SubObjectRefHandle : ReverseIterate(ServerSubObjectRefHandles))
{
UE_NET_ASSERT_FALSE(Client->IsResolvableNetRefHandle(SubObjectRefHandle));
}
}
if (!bDidSendSomething)
{
break;
}
}
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestSubObjectDefaultReplicationOrder)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
// Spawn some subobjects
UReplicatedSubObjectOrderObject* ServerSubObject0 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectOrderObject* ServerSubObject1 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectOrderObject* ServerSubObject2 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectOrderObject::RepOrderCounter = 0U;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that objects have replicated
UReplicatedSubObjectOrderObject* ClientSubObject0 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject0->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject1 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject1->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject2 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject2->NetRefHandle));
UE_NET_ASSERT_NE(ClientSubObject0, nullptr);
UE_NET_ASSERT_NE(ClientSubObject1, nullptr);
UE_NET_ASSERT_NE(ClientSubObject2, nullptr);
// Verify that they have replicated in expected order
UE_NET_ASSERT_EQ(ClientSubObject0->LastRepOrderCounter, 1U);
UE_NET_ASSERT_GT(ClientSubObject1->LastRepOrderCounter, ClientSubObject0->LastRepOrderCounter);
UE_NET_ASSERT_GT(ClientSubObject2->LastRepOrderCounter, ClientSubObject1->LastRepOrderCounter);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestSubObjectSpecifiedReplicationOrder)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
// Spawn some subobjects
UReplicatedSubObjectOrderObject* ServerSubObject0 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle);
// Specify Subobject1 to replicate with SubObject0, which means that it will replicate before Subobjet0 is replicated
UReplicatedSubObjectOrderObject* ServerSubObject1 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle, ServerSubObject0->NetRefHandle, UReplicationBridge::ESubObjectInsertionOrder::ReplicateWith);
// Specify SubObect 2 to replicate with no specific order (it will be added to the owner and thus replicate last)
UReplicatedSubObjectOrderObject* ServerSubObject2 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle, ServerSubObject1->NetRefHandle, UReplicationBridge::ESubObjectInsertionOrder::None);
UReplicatedSubObjectOrderObject::RepOrderCounter = 0U;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that objects have replicated
UReplicatedSubObjectOrderObject* ClientSubObject0 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject0->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject1 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject1->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject2 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject2->NetRefHandle));
UE_NET_ASSERT_NE(ClientSubObject0, nullptr);
UE_NET_ASSERT_NE(ClientSubObject1, nullptr);
UE_NET_ASSERT_NE(ClientSubObject2, nullptr);
// Verify that they have replicated in expected order setup earlier
UE_NET_ASSERT_EQ(ClientSubObject1->LastRepOrderCounter, 1U);
UE_NET_ASSERT_EQ(ClientSubObject0->LastRepOrderCounter, 2U);
UE_NET_ASSERT_EQ(ClientSubObject2->LastRepOrderCounter, 3U);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestSubObjectInsertAtStart)
{
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedObjectTestSubObjectCreationOrder* ServerObject = Server->CreateObject<UReplicatedObjectTestSubObjectCreationOrder>();
// Spawn a subobject
UReplicatedSubObjectOrderObject* ServerSubObject0 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle);
// Spawn a subobject and make it replicate before SubObject0
UReplicatedSubObjectOrderObject* ServerSubObject1 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle, ServerSubObject0->NetRefHandle, UReplicationBridge::ESubObjectInsertionOrder::ReplicateWith);
// Spawn a subobject and make it replicate first
UReplicatedSubObjectOrderObject* ServerSubObject2 = Server->CreateSubObject<UReplicatedSubObjectOrderObject>(ServerObject->NetRefHandle, FNetRefHandle::GetInvalid(), UReplicationBridge::ESubObjectInsertionOrder::InsertAtStart);
UReplicatedSubObjectOrderObject::RepOrderCounter = 0U;
// Send and deliver packet
Server->UpdateAndSend({ Client });
// Verify that objects have replicated
UReplicatedSubObjectOrderObject* ClientSubObject0 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject0->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject1 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject1->NetRefHandle));
UReplicatedSubObjectOrderObject* ClientSubObject2 = Cast<UReplicatedSubObjectOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject2->NetRefHandle));
UE_NET_ASSERT_NE(ClientSubObject0, nullptr);
UE_NET_ASSERT_NE(ClientSubObject1, nullptr);
UE_NET_ASSERT_NE(ClientSubObject2, nullptr);
// Verify that they were created in the expected order
UE_NET_ASSERT_EQ(ClientSubObject2->CreationOrder, 1);
UE_NET_ASSERT_EQ(ClientSubObject1->CreationOrder, 2);
UE_NET_ASSERT_EQ(ClientSubObject0->CreationOrder, 3);
// Verify that they have replicated in the expected order
UE_NET_ASSERT_EQ(ClientSubObject2->LastRepOrderCounter, 1U);
UE_NET_ASSERT_EQ(ClientSubObject1->LastRepOrderCounter, 2U);
UE_NET_ASSERT_EQ(ClientSubObject0->LastRepOrderCounter, 3U);
}
class FTestNetTokensFixture : public FReplicationSystemServerClientTestFixture
{
public:
FStringTokenStore* ServerStringTokenStore = nullptr;
FStringTokenStore* ClientStringTokenStore = nullptr;
FReplicationSystemTestClient* Client = nullptr;
const FNetTokenStoreState* ClientRemoteNetTokenState;
const FNetTokenStoreState* ServerRemoteNetTokenState;
FNetToken CreateAndExportNetToken(const FString& TokenString)
{
FNetToken Token = ServerStringTokenStore->GetOrCreateToken(TokenString);
UNetTokenDataStream* NetTokenDataStream = Cast<UNetTokenDataStream>(Server->GetReplicationSystem()->GetDataStream(Client->ConnectionIdOnServer, FName("NetToken")));
if (NetTokenDataStream)
{
NetTokenDataStream->AddNetTokenForExplicitExport(Token);
}
return Token;
}
FNetToken CreateAndExportNetTokenOnClient(const FString& TokenString)
{
FNetToken Token = ClientStringTokenStore->GetOrCreateToken(TokenString);
UNetTokenDataStream* NetTokenDataStream = Cast<UNetTokenDataStream>(Client->GetReplicationSystem()->GetDataStream(Client->LocalConnectionId, FName("NetToken")));
if (NetTokenDataStream)
{
NetTokenDataStream->AddNetTokenForExplicitExport(Token);
}
return Token;
}
virtual void SetUp() override
{
FReplicationSystemServerClientTestFixture::SetUp();
Client = CreateClient();
{
FNetTokenStore* ServerTokenStore = Server->GetReplicationSystem()->GetNetTokenStore();
ServerStringTokenStore = ServerTokenStore->GetDataStore<FStringTokenStore>();
ServerRemoteNetTokenState = ServerTokenStore->GetRemoteNetTokenStoreState(Client->ConnectionIdOnServer);
}
{
FNetTokenStore* ClientTokenStore = Client->GetReplicationSystem()->GetNetTokenStore();
ClientStringTokenStore = ClientTokenStore->GetDataStore<FStringTokenStore>();
ClientRemoteNetTokenState = ClientTokenStore->GetRemoteNetTokenStoreState(Client->LocalConnectionId);
}
}
};
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetToken)
{
// Create token
FString TokenStringA(TEXT("MyStringToken"));
FNetToken StringTokenA = CreateAndExportNetToken(TokenStringA);
// Send and drop packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
{
LOG_SCOPE_VERBOSITY_OVERRIDE(LogNetToken, ELogVerbosity::Fatal);
// Verify that we cannot resolve the token on the client
UE_NET_ASSERT_NE(TokenStringA, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
}
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that we can resolve the token on the client
UE_NET_ASSERT_EQ(TokenStringA, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenResendWithFullPacket)
{
// Create token
FString TokenStringA(TEXT("MyStringToken"));
FNetToken StringTokenA = CreateAndExportNetToken(TokenStringA);
// Send and drop packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Limit packet size
Server->SetMaxSendPacketSize(128U);
// Create a new token that will not fit in the packet and only fit the resend data
FString TokenStringB(TEXT("MyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongString"));
FNetToken StringTokenB = CreateAndExportNetToken(TokenStringB);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that we can resolve the token first token on the client even though second one should not fit
UE_NET_ASSERT_EQ(TokenStringA, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
{
LOG_SCOPE_VERBOSITY_OVERRIDE(LogNetToken, ELogVerbosity::Fatal);
UE_NET_ASSERT_NE(TokenStringB, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
}
// Restore packet size and make sure that we get the second token through
Server->SetMaxSendPacketSize(1024U);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(TokenStringB, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenResendWithFullPacketAfterFirstResend)
{
// Create token
FString TestStringA(TEXT("MyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongString"));
FNetToken StringTokenA = CreateAndExportNetToken(TestStringA);
// Send and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Create a new token that will not fit in the packet and only fit the resend data
FString TestStringB(TEXT("MyOtherLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongStringMyLongString"));
FNetToken StringTokenB = CreateAndExportNetToken(TestStringB);
// Send and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
Server->DeliverTo(Client, false);
Server->DeliverTo(Client, false);
// Verify that tokens has not been received
{
LOG_SCOPE_VERBOSITY_OVERRIDE(LogNetToken, ELogVerbosity::Fatal);
UE_NET_ASSERT_NE(TestStringA, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_NE(TestStringB, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
}
// Send and deliver packet which now should contain two entries in the resend queue
Server->SetMaxSendPacketSize(1024);
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that we can resolve the token
UE_NET_ASSERT_EQ(TestStringA, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_EQ(TestStringB, FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenSequenceTest)
{
const FString TestStrings[] = {
FString(TEXT("TokenA")),
FString(TEXT("TokenB")),
FString(TEXT("TokenC")),
FString(TEXT("TokenD")),
FString(TEXT("TokenE")),
FString(TEXT("TokenF")),
};
const uint32 TokenCount = UE_ARRAY_COUNT(TestStrings);
// Create token
FNetToken StringTokenA = CreateAndExportNetToken(TestStrings[0]);
FNetToken StringTokenB = CreateAndExportNetToken(TestStrings[1]);
// Send packet
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Create token
FNetToken StringTokenC = CreateAndExportNetToken(TestStrings[2]);
// Create token
FNetToken StringTokenD = CreateAndExportNetToken(TestStrings[3]);
// Send packet
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Drop packet
Server->DeliverTo(Client, false);
// Deliver packet
Server->DeliverTo(Client, true);
// Create local tokens
ClientStringTokenStore->GetOrCreateToken(TEXT("LocalTokenA"));
ClientStringTokenStore->GetOrCreateToken(TEXT("LocalTokenB"));
// Send packet with resend data
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(TestStrings[0], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_EQ(TestStrings[1], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_EQ(TestStrings[2], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenC, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_EQ(TestStrings[3], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenD, *ClientRemoteNetTokenState)));
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenResendAndDataInSamePacketTest)
{
const FString TestStrings[] = {
FString(TEXT("TokenA")),
FString(TEXT("TokenB")),
};
const uint32 TokenCount = UE_ARRAY_COUNT(TestStrings);
// Create token
FNetToken StringTokenA = CreateAndExportNetToken(TestStrings[0]);
// Send packet
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// drop data
Server->DeliverTo(Client, false);
// Create token
FNetToken StringTokenB = CreateAndExportNetToken(TestStrings[1]);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(TestStrings[0], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenA, *ClientRemoteNetTokenState)));
UE_NET_ASSERT_EQ(TestStrings[1], FString(ClientStringTokenStore->ResolveRemoteToken(StringTokenB, *ClientRemoteNetTokenState)));
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenAuthority)
{
// Create token
FString TokenStringA(TEXT("MyStringToken"));
FNetToken NonAuthToken = CreateAndExportNetTokenOnClient(TokenStringA);
UE_NET_ASSERT_EQ(NonAuthToken.IsAssignedByAuthority(), false);
// Send from server
Server->UpdateAndSend({Client});
// Send from client
Client->UpdateAndSend(Server);
// We should be able to resolve the token on the server using remote
UE_NET_ASSERT_EQ(TokenStringA, FString(ServerStringTokenStore->ResolveToken(NonAuthToken, ServerRemoteNetTokenState)));
// Find server token.
FNetToken AuthToken = CreateAndExportNetToken(TokenStringA);
// It should be a different token as the server is authoriative
UE_NET_ASSERT_FALSE(AuthToken == NonAuthToken);
// Send from server
Server->UpdateAndSend({Client});
// Client should be able to resolve ServerToken
UE_NET_ASSERT_EQ(TokenStringA, FString(ClientStringTokenStore->ResolveToken(AuthToken, ClientRemoteNetTokenState)));
// If we now try to create a token for the string also received from the authority we expect it to give us the server token and allow us to use that instead of the local exported token.
FNetToken NewClientToken = ClientStringTokenStore->GetOrCreateToken(TokenStringA);
// We expect the tokens to be identical.
UE_NET_ASSERT_TRUE(AuthToken == NewClientToken);
}
UE_NET_TEST_FIXTURE(FTestNetTokensFixture, NetTokenAuthTokenIsNotExportedFromClient)
{
// Create token
FString TokenStringA(TEXT("MyStringToken"));
FNetToken AuthToken = CreateAndExportNetToken(TokenStringA);
UE_NET_ASSERT_EQ(AuthToken.IsAssignedByAuthority(), true);
// Send from server
Server->UpdateAndSend({Client});
// Expect to get auth token
FNetToken ClientExpectedAuthToken = CreateAndExportNetTokenOnClient(TokenStringA);
UE_NET_ASSERT_EQ(ClientExpectedAuthToken.IsAssignedByAuthority(), true);
// Send from client
Client->UpdateAndSend(Server);
// $TODO: Expose some stats that we can query for exports.
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, AddRemoveFromConnectionScopeTest)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Add to group
FNetObjectGroupHandle Group = ReplicationSystem->CreateGroup(NAME_None);
ReplicationSystem->AddToGroup(Group, ServerObject->NetRefHandle);
ReplicationSystem->AddExclusionFilterGroup(Group);
ReplicationSystem->SetGroupFilterStatus(Group, ENetFilterStatus::Allow);
// Start replicating object
// Send packet
// Expected state to be WaitOnCreateConfirmation
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Make sure we have data in flight
++ServerObject->IntA;
// Disallow group to trigger state change from PendingCreateConfirmation->PendingDestroy
ReplicationSystem->SetGroupFilterStatus(Group, ENetFilterStatus::Disallow);
// Expect client to create object
Server->DeliverTo(Client, true);
UE_NET_ASSERT_NE(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle), nullptr);
// Send packet
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Allow group to trigger state to ensure that we restart replication
ReplicationSystem->SetGroupFilterStatus(Group, ENetFilterStatus::Allow);
// Expect client to destroy object
Server->DeliverTo(Client, true);
UE_NET_ASSERT_EQ(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle), nullptr);
// Trigger replication
++ServerObject->IntA;
// Send packet
// WaitOnDestroyConfirmation -> WaitOnCreateConfirmation
Server->UpdateAndSend({ Client });
// Verify that the object got created again
UE_NET_ASSERT_NE(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle), nullptr);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestNetTemporary)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
UTestReplicatedIrisObject* ServerObject1 = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
ServerObject1->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that client has received the data
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObject->IntA);
}
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject1->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_EQ(ServerObject1->IntA, ClientObject->IntA);
}
// Mark the object as a net temporary
ReplicationSystem->SetIsNetTemporary(ServerObject->NetRefHandle);
// Modify the value
ServerObject->IntA = 2;
ServerObject1->IntA = 2;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that client has not received the data for changed temporary
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_NE(ServerObject->IntA, ClientObject->IntA);
}
// Verify that client has received the data for normal object
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject1->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_EQ(ServerObject1->IntA, ClientObject->IntA);
}
// Test Late join
// Add a client
FReplicationSystemTestClient* Client2 = CreateClient();
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client2, true);
Server->PostSendUpdate();
// We should now have the latest state for both objects
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client2->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObject->IntA);
}
// Verify that client has received the data for normal object
{
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client2->GetReplicationBridge()->GetReplicatedObject(ServerObject1->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
UE_NET_ASSERT_EQ(ServerObject1->IntA, ClientObject->IntA);
}
}
// Tests for TearOff
// Test TearOff for existing confirmed object
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffExistingObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to object
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// Modify the value
ServerObject->IntA = 2;
// TearOff the object
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off and that the final state was applied
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test TearOff for new object
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffOnNewlyCreatedObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// We should not have any created objects
const int32 NumObjectsCreatedOnClientBeforeReplication = Client->CreatedObjects.Num();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Set state
ServerObject->IntA = 1;
// TearOff the object before first replication
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Client should have created a object
UE_NET_ASSERT_EQ(NumObjectsCreatedOnClientBeforeReplication + 1, Client->CreatedObjects.Num());
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
// We should be able to get the object from the created objects array to validate the state
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->CreatedObjects[NumObjectsCreatedOnClientBeforeReplication].Get());
// Verify that we replicated the expected state
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
}
// Test TearOff resend for existing confirmed object with no state changes
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffResendForExistingObjectWithoutDirtyState)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, DeliverPacket);
Server->PostSendUpdate();
// Store Pointer to object
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_NE(ClientObjectThatWillBeTornOff, nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// TearOff the object
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and do not deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, DoNotDeliverPacket);
Server->PostSendUpdate();
// The ClientObject should still be found using the NetRefHandle
UE_NET_ASSERT_NE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)), nullptr);
Server->NetUpdate();
Server->SendAndDeliverTo(Client, DeliverPacket);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off
UE_NET_ASSERT_EQ(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)), nullptr);
}
// Test TearOff for new object and resend, this requires creation info to be cached.
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffImmediateOnNewlyCreatedObjectResend)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// We should not have any created objects
const int32 NumObjectsCreatedOnClientBeforeReplication = Client->CreatedObjects.Num();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Set state
ServerObject->IntA = 1;
// TearOff the object before first replication
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and deliver packet
Server->UpdateAndSend({Client}, false);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Client should have created a object
UE_NET_ASSERT_EQ(NumObjectsCreatedOnClientBeforeReplication + 1, Client->CreatedObjects.Num());
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
// We should be able to get the object from the created objects array to validate the state
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->CreatedObjects[NumObjectsCreatedOnClientBeforeReplication].Get());
// Verify that we replicated the expected state
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
}
// Test TearOff for new subobject and resend, this requires creation info to be cached.
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffImmediateOnNewlyCreatedSubObjectResend)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// We should not have any created objects
const int32 NumObjectsCreatedOnClientBeforeReplication = Client->CreatedObjects.Num();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Set state
ServerObject->IntA = 1;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Set state
ServerSubObject->IntA = 1;
// TearOff the subobject before first replication
Server->ReplicationBridge->EndReplication(ServerSubObject, EEndReplicationFlags::TearOff);
// Send and drop
Server->UpdateAndSend({Client}, false);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Client should have created a object + subobject
UE_NET_ASSERT_EQ(NumObjectsCreatedOnClientBeforeReplication + 2, Client->CreatedObjects.Num());
// But as we have torn off the subobject it should no longer be a replicated object
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) != nullptr);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
// We should be able to get the object from the created objects array to validate the state
UTestReplicatedIrisObject* ClientSubObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->CreatedObjects[NumObjectsCreatedOnClientBeforeReplication + 1].Get());
// Verify that we replicated the expected state
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDefferedTearOffOnNewlyCreatedObjectResend)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// We should not have any created objects
const int32 NumObjectsCreatedOnClientBeforeReplication = Client->CreatedObjects.Num();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Set state
ServerObject->IntA = 1;
// TearOff the object before first replication
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// End replication and destroy object
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::Destroy);
// Client should have created a object
UE_NET_ASSERT_EQ(NumObjectsCreatedOnClientBeforeReplication + 1, Client->CreatedObjects.Num());
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
// We should be able to get the object from the created objects array to validate the state
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->CreatedObjects[NumObjectsCreatedOnClientBeforeReplication].Get());
// Verify that we replicated the expected state
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
}
// Test TearOff for existing not yet confirmed object
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffObjectPendingCreateConfirmation)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send packet to get put the object in flight
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// TearOff the object
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Deliver Object (should now be created)
Server->DeliverTo(Client, true);
// Store Pointer to object and verify initial state
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is TornOFf and that the final state was applied
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test TearOff for existing object pending destroy (should do nothing)
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffExistingObjectPendingDestroy)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to object
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// Modify the value
ServerObject->IntA = 2;
// Mark the object for destroy
Server->ReplicationBridge->EndReplication(ServerObject);
// TearOff the object
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is not tornOff and that the final state was not applied as we issued tearoff after ending replication
UE_NET_ASSERT_NE(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test TearOff resend
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffResend)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to object
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// Modify the value
ServerObject->IntA = 2;
// TearOff the object
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Send and deliver packet, in this case the packet containing 2 was lost, but, we did not know that when we
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Object should now be torn-off, so it should not copy the latest state
ServerObject->IntA = 3;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off and that the expected final state was applied
UE_NET_ASSERT_EQ(2, ClientObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test TearOff does not pickup statechanges after tear off
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTornedOffObjectDoesNotCopyStateChanges)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Trigger replication
ServerObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to object
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
// Modify the value
ServerObject->IntA = 2;
// TearOff the object
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Send and drop packet containing the value 2
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Object should now be torn-off, so it should not copy the latest state but instead resend the last copied state (2) along with the tear-off
ServerObject->IntA = 3;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is TornOFf and that the expected final state was applied
UE_NET_ASSERT_EQ(2, ClientObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test TearOff and SubObjects, SubObjects must apply state?
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestImmediateTearOffExistingObjectWithSubObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Trigger replication
ServerObject->IntA = 1;
ServerSubObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to objects
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UTestReplicatedIrisObject* ClientSubObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientSubObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
// Modify the value of subobject only
ServerSubObject->IntA = 2;
// TearOff the object using immediate tear-off
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off and that the final state was applied to subObject
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test TearOff and SubObjects, SubObjects must apply state?
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestImmediateTearOffExistingObjectWithSubObjectDroppedData)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Trigger replication
ServerObject->IntA = 1;
ServerSubObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to objects
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UTestReplicatedIrisObject* ClientSubObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientSubObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
// Modify the value of subobject only
ServerSubObject->IntA = 2;
// TearOff the object using immediate tear-off
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Send and do not deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off and that the final state was applied to subObject
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test creating a subobject after the root is already torn off
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestCreatingSubobjectAfterRootObjectTearOff)
{
//LOG_SCOPE_VERBOSITY_OVERRIDE(LogIrisBridge, ELogVerbosity::Verbose);
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object with subobject on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject();
UTestReplicatedIrisObject* ServerSubObject0 = Server->CreateSubObject(ServerObject->NetRefHandle);
// Send and deliver packet
Server->UpdateAndSend({Client});
// TearOff the object
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Send and deliver packet
Server->NetUpdate();
Server->PostSendUpdate();
{
FTestEnsureScope EnsureScope;
// Add a new subobject. This shouldn't become a replicated subobject of the already torn off root object.
UTestReplicatedIrisObject* ServerSubObject1 = Server->CreateSubObject(ServerObject->NetRefHandle);
UE_NET_ASSERT_EQ(EnsureScope.GetCount(), 0);
// The subobject should not start replicating.
UE_NET_ASSERT_FALSE(ServerSubObject1->NetRefHandle.IsValid());
// Send and deliver packet
Server->UpdateAndSend({ Client });
UE_NET_ASSERT_EQ(EnsureScope.GetCount(), 0);
}
}
// Test dropped creation of subobject dirties owner
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestDroppedCreationForSubobjectDirtiesOwner)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to objects
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Send and do not deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject now is created as expected
UTestReplicatedIrisObject* ClientSubObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientSubObject != nullptr);
}
// Test replicated destroy for not created object
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicatedDestroyForNotCreatedObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Update and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Destroy object
Server->ReplicationBridge->EndReplication(ServerObject);
// Update and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Drop first packet containing creation info for object
Server->DeliverTo(Client, false);
// Deliver second packet that should contain destroy
Server->DeliverTo(Client, true);
// Verify that the object does not exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) == nullptr);
}
// Test replicated SubObjectDestroy for not created subobject
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicatedSubObjectDestroyForNotCreatedObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Replicate object
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Spawn second object on server as a subobject
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerSubObject = Server->CreateSubObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>(ServerObject->NetRefHandle);
// Update and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Destroy subobject
Server->ReplicationBridge->EndReplication(ServerSubObject);
// Update and delay delivery
Server->NetUpdate();
Server->SendTo(Client);
Server->PostSendUpdate();
// Drop first packet containing creation info for subobject
Server->DeliverTo(Client, false);
// Deliver second packet that should contain replicated subobject destroy
Server->DeliverTo(Client, true);
// Verify that the object still exists on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) != nullptr);
// Verify that the subobject does not exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test replicated SubObjectDestroy for filtered out subobject
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicatedSubObjectDestroyForFilteredOutSubObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerSubObject = Server->CreateSubObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>(ServerObject->NetRefHandle);
// Replicate object
Server->UpdateAndSend({Client});
// Verify that the subobject does exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) != nullptr);
// Set condition
Server->ReplicationBridge->SetSubObjectNetCondition(ServerSubObject->NetRefHandle, ELifetimeCondition::COND_Never);
// Destroy subobject
Server->ReplicationBridge->EndReplication(ServerSubObject);
// Replicate object
Server->UpdateAndSend({Client});
// Verify that the object still exists on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) != nullptr);
// Verify that the subobject does not exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test replicated SubObjectDestroy for filtered out subobject
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicatedSubObjectDestroyForFilteredOutSubObjectBeforeSend)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerSubObject = Server->CreateSubObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>(ServerObject->NetRefHandle);
// Set condition
Server->ReplicationBridge->SetSubObjectNetCondition(ServerSubObject->NetRefHandle, ELifetimeCondition::COND_Never);
// Replicate object
Server->UpdateAndSend({Client});
// Verify that the subobject does not exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
// Destroy subobject
Server->ReplicationBridge->EndReplication(ServerSubObject);
// Replicate object
Server->UpdateAndSend({Client});
// Verify that the object still exists on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle)) != nullptr);
// Verify that the subobject does not exist on client
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test tear-off object in PendingCreate state to ensure that tear-off logic works as expected
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffObjectWithNoFragmentsDoesNotTriggerCheckIfPendingCreateWhenDestroyed)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerObject = Server->CreateObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>();
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Tear-off using immediate tear-off
Server->ReplicationBridge->EndReplication(ServerObject, EEndReplicationFlags::TearOff);
// Trigger the next update but avoid sending any data so that we keep the object in the PendingCreation state while we flush the Handles PendingTearOff Array which occurs in PostSendUpdate
Server->NetUpdate();
Server->PostSendUpdate();
}
// Test tear-off subobject in PendingCreate state to ensure that tear-off logic works as expected
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffSubObjectWithNoFragmentsDoesNotTriggerCheckIfPendingCreateWhenDestroyed)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerSubObject = Server->CreateSubObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>(ServerObject->NetRefHandle);
// Update and drop
Server->NetUpdate();
Server->SendAndDeliverTo(Client, false);
Server->PostSendUpdate();
// Tear-off using immediate tear-off
Server->ReplicationBridge->EndReplication(ServerSubObject, EEndReplicationFlags::TearOff);
// Trigger the next update but avoid sending any data so that we keep the sub-object in the PendingCreation state while we flush the Handles PendingTearOff Array which occurs in PostSendUpdate
Server->NetUpdate();
Server->PostSendUpdate();
}
// Test TearOff and SubObjects, SubObjects must apply state?
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffNextUpdateExistingObjectWithSubObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Trigger replication
ServerObject->IntA = 1;
ServerSubObject->IntA = 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to objects
UTestReplicatedIrisObject* ClientObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerObject->IntA, ClientObjectThatWillBeTornOff->IntA);
UTestReplicatedIrisObject* ClientSubObjectThatWillBeTornOff = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientSubObjectThatWillBeTornOff != nullptr);
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
// Modify the value of subobject only
ServerSubObject->IntA = 2;
// TearOff the object using immediate tear-off
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that ClientObject is torn-off and that the final state was applied to subObject
UE_NET_ASSERT_EQ(ServerSubObject->IntA, ClientSubObjectThatWillBeTornOff->IntA);
UE_NET_ASSERT_TRUE(Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject->NetRefHandle)) == nullptr);
}
// Test TearOff and destroy of SubObjects that are still pending create/tearoff
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestTearOffNextUpdateExistingObjectWithSubObjectPendingCreation)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
FNetRefHandleManager* NetRefHandleManager = &ReplicationSystem->GetReplicationSystemInternal()->GetNetRefHandleManager();
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0,0);
// Spawn second object on server as a subobject
UTestReplicatedIrisObject* ServerSubObject = Server->CreateSubObject(ServerObject->NetRefHandle, 0, 0);
// Trigger replication
ServerObject->IntA = 1;
ServerSubObject->IntA = 1;
const FInternalNetRefIndex ServerObjectInternalIndex = NetRefHandleManager->GetInternalIndex(ServerObject->NetRefHandle);
const FInternalNetRefIndex SubObjectObjectInternalIndex = NetRefHandleManager->GetInternalIndex(ServerSubObject->NetRefHandle);
// Trigger presend without send to add the objects to scope
Server->NetUpdate();
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(uint16(1), NetRefHandleManager->GetNetObjectRefCount(ServerObjectInternalIndex));
UE_NET_ASSERT_EQ(uint16(1), NetRefHandleManager->GetNetObjectRefCount(SubObjectObjectInternalIndex));
// TearOff the object this will also tear-off subobject
Server->ReplicationSystem->TearOffNextUpdate(ServerObject->NetRefHandle);
// Update logic, object should be removed from scope but still exist as pending create in
Server->NetUpdate();
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(uint16(1), NetRefHandleManager->GetNetObjectRefCount(ServerObjectInternalIndex));
UE_NET_ASSERT_EQ(uint16(1), NetRefHandleManager->GetNetObjectRefCount(SubObjectObjectInternalIndex));
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Destroy the object
Server->DestroyObject(ServerObject);
// Verify that we no longer have any references to the object
UE_NET_ASSERT_EQ(uint16(0), NetRefHandleManager->GetNetObjectRefCount(ServerObjectInternalIndex));
UE_NET_ASSERT_EQ(uint16(0), NetRefHandleManager->GetNetObjectRefCount(SubObjectObjectInternalIndex));
}
// Test that we can replicate an object with no replicated properties
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicatedObjectWithNoReplicatedProperties)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObjectWithNoReplicatedMembers* ServerObject = Server->CreateObject<UTestReplicatedIrisObjectWithNoReplicatedMembers>();
const FNetRefHandle ServerHandle = ServerObject->NetRefHandle;
UE_NET_ASSERT_TRUE(ServerHandle.IsValid());
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
UTestReplicatedIrisObjectWithNoReplicatedMembers* ClientObject = Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerHandle));
UE_NET_ASSERT_TRUE(ClientObject != nullptr);
// Destroy object
Server->DestroyObject(ServerObject);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
ClientObject = Cast<UTestReplicatedIrisObjectWithNoReplicatedMembers>(Client->GetReplicationBridge()->GetReplicatedObject(ServerHandle));
UE_NET_ASSERT_TRUE(ClientObject == nullptr);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestObjectPollFramePeriod)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UTestReplicatedIrisObject* ServerObject = Server->CreateObject(0, 0);
// Spawn second object on server that later will be added as a dependent object
UObjectReplicationBridge::FRootObjectReplicationParams Params;
Params.PollFrequency = Server->ConvertPollPeriodIntoFrequency(1U);
UTestReplicatedIrisObject* ServerObjectPolledEveryOtherFrame = Server->CreateObject(Params);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Store Pointer to objects and verify state after initial replication
UTestReplicatedIrisObject* ClientObject = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObject->NetRefHandle));
UTestReplicatedIrisObject* ClientObjectPolledEveryOtherFrame = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectPolledEveryOtherFrame->NetRefHandle));
UE_NET_ASSERT_NE(ClientObjectPolledEveryOtherFrame, nullptr);
UE_NET_ASSERT_NE(ClientObject, nullptr);
UE_NET_ASSERT_EQ(ClientObject->IntA, ServerObject->IntA);
UE_NET_ASSERT_EQ(ClientObjectPolledEveryOtherFrame->IntA, ServerObjectPolledEveryOtherFrame->IntA);
// After two value updates it's expected that the polling occurs exactly one time for the object with poll frame period 1 (meaning every other frame).
bool SlowPollObjectHasBeenEqual = false;
bool SlowPollObjectHasBeenInequal = false;
// Update values
ServerObject->IntA += 1;
ServerObjectPolledEveryOtherFrame->IntA += 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
UE_NET_ASSERT_EQ(ClientObject->IntA, ServerObject->IntA);
SlowPollObjectHasBeenEqual |= (ClientObjectPolledEveryOtherFrame->IntA == ServerObjectPolledEveryOtherFrame->IntA);
SlowPollObjectHasBeenInequal |= (ClientObjectPolledEveryOtherFrame->IntA != ServerObjectPolledEveryOtherFrame->IntA);
// Update values
ServerObject->IntA += 1;
ServerObjectPolledEveryOtherFrame->IntA += 1;
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify that both objects now are in sync
UE_NET_ASSERT_EQ(ClientObject->IntA, ServerObject->IntA);
SlowPollObjectHasBeenEqual |= (ClientObjectPolledEveryOtherFrame->IntA == ServerObjectPolledEveryOtherFrame->IntA);
SlowPollObjectHasBeenInequal |= (ClientObjectPolledEveryOtherFrame->IntA != ServerObjectPolledEveryOtherFrame->IntA);
UE_NET_ASSERT_TRUE(SlowPollObjectHasBeenEqual);
UE_NET_ASSERT_TRUE(SlowPollObjectHasBeenInequal);
}
// Test that broken objects can be skipped by client
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestClientCanSkipBrokenObject)
{
UReplicationSystem* ReplicationSystem = Server->ReplicationSystem;
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn objects on server
UTestReplicatedIrisObject* ServerObjectA = Server->CreateObject(0,0);
UTestReplicatedIrisObject* ServerObjectB = Server->CreateObject(0,0);
{
// Setup client to fail to create next remote object
ServerObjectA->bForceFailToInstantiateOnRemote = true;
// Suppress ensure that will occur due to failing to instantiate the object
UReplicatedTestObjectBridge::FSupressCreateInstanceFailedEnsureScope SuppressEnsureScope(*Client->GetReplicationBridge());
// Disable error logging as we know we will fail.
auto IrisLogVerbosity = UE_GET_LOG_VERBOSITY(LogIris);
LogIris.SetVerbosity(ELogVerbosity::NoLogging);
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Restore LogVerbosity
LogIris.SetVerbosity(IrisLogVerbosity);
}
// We expect replication of ObjectA to have failed
{
UTestReplicatedIrisObject* ClientObjectA = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectA->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectA == nullptr);
}
// ObjectB should have been replicated ok
{
UTestReplicatedIrisObject* ClientObjectB = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectB->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectB != nullptr);
}
// Modify both objects to make them replicate again
++ServerObjectA->IntA;
++ServerObjectB->IntA;
// Send and deliver packet to verify that client ignores the broken object
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// We expect replication of ObjectA to have failed
{
UTestReplicatedIrisObject* ClientObjectA = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectA->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectA == nullptr);
}
// Filter out ObjectA to tell the client that the object has gone out of scope
ReplicationSystem->AddToGroup(ReplicationSystem->GetNotReplicatedNetObjectGroup(), ServerObjectA->NetRefHandle);
// Send and deliver packet, the client should now remove the broken object from the list of broken objects
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Enable replication of ObjectA again to try to replicate it to server now that it should succeed
ReplicationSystem->RemoveFromGroup(ReplicationSystem->GetNotReplicatedNetObjectGroup(), ServerObjectA->NetRefHandle);
// Set ObjectA to be able instantiate on client again
ServerObjectA->bForceFailToInstantiateOnRemote = false;
// Client should now be able to instantiate the object
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// We expect replication of ObjectA to have succeeded this time
{
UTestReplicatedIrisObject* ClientObjectA = Cast<UTestReplicatedIrisObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectA->NetRefHandle));
UE_NET_ASSERT_TRUE(ClientObjectA == nullptr);
}
}
// Test that PropertyReplication properly handles partial states during Apply
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestPartialDequantize)
{
// Enable cvars to exercise path that store previous state for OnReps to make sure we exercise path that accumulate dirty changes so that we have a complete state.
IConsoleVariable* CVarUsePrevReceivedStateForOnReps = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Iris.UsePrevReceivedStateForOnReps"));
check(CVarUsePrevReceivedStateForOnReps != nullptr && CVarUsePrevReceivedStateForOnReps->IsVariableBool());
const bool bUsePrevReceivedStateForOnReps = CVarUsePrevReceivedStateForOnReps->GetBool();
CVarUsePrevReceivedStateForOnReps->Set(true, ECVF_SetByCode);
// Make sure we allow partial dequantize
IConsoleVariable* CVarForceFullDequantizeAndApply = IConsoleManager::Get().FindConsoleVariable(TEXT("net.iris.ForceFullDequantizeAndApply"));
check(CVarForceFullDequantizeAndApply != nullptr && CVarForceFullDequantizeAndApply->IsVariableBool());
const bool bForceFullDequantizeAndApply = CVarForceFullDequantizeAndApply->GetBool();
CVarForceFullDequantizeAndApply->Set(false, ECVF_SetByCode);
ON_SCOPE_EXIT
{
// Restore cvars
CVarUsePrevReceivedStateForOnReps->Set(bUsePrevReceivedStateForOnReps, ECVF_SetByCode);
CVarForceFullDequantizeAndApply->Set(bForceFullDequantizeAndApply, ECVF_SetByCode);
};
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn objects on server
UTestReplicatedObjectWithRepNotifies* ServerObjectA = Server->CreateObject<UTestReplicatedObjectWithRepNotifies>();
// Send and deliver packet
Server->NetUpdate();
Server->SendAndDeliverTo(Client, true);
Server->PostSendUpdate();
// Verify assumptions
// Object should exist on client and have default state
UTestReplicatedObjectWithRepNotifies* ClientObjectA = Cast<UTestReplicatedObjectWithRepNotifies>(Client->GetReplicationBridge()->GetReplicatedObject(ServerObjectA->NetRefHandle));
UE_NET_ASSERT_NE(ClientObjectA, nullptr);
UE_NET_ASSERT_EQ(ServerObjectA->IntA, ClientObjectA->IntA);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntAStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntB, ClientObjectA->IntB);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntBStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntC, ClientObjectA->IntC);
// Modify only IntA
ServerObjectA->IntA = 1;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions
// Only IntA should have been modified
UE_NET_ASSERT_EQ(ServerObjectA->IntA, ClientObjectA->IntA);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntAStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntB, ClientObjectA->IntB);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntBStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntC, ClientObjectA->IntC);
// Modify only IntB
ServerObjectA->IntB = 1;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions
// Only IntA should have been modified
UE_NET_ASSERT_EQ(ServerObjectA->IntA, ClientObjectA->IntA);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntAStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntB, ClientObjectA->IntB);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntBStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntC, ClientObjectA->IntC);
// Modify only IntA
ServerObjectA->IntA = 2;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions
// IntA should have been modified, and if everything works correctly PrevIntAStoredInOnRep should be 1
UE_NET_ASSERT_EQ(ServerObjectA->IntA, ClientObjectA->IntA);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntAStoredInOnRep, 1);
UE_NET_ASSERT_EQ(ServerObjectA->IntB, ClientObjectA->IntB);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntBStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntC, ClientObjectA->IntC);
// Verify that we do not apply repnotifies if we do not receive data from server by modifying values on the client and verifying that they do not get overwritten
ServerObjectA->IntB = 2;
ClientObjectA->IntA = -1;
ClientObjectA->PrevIntAStoredInOnRep = -1;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions, since we messed with IntA and PrevIntAStoredInOnRep locally they have the value we set but IntB should be updated according to replicated state
UE_NET_ASSERT_NE(ServerObjectA->IntA, ClientObjectA->IntA);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntAStoredInOnRep, -1);
UE_NET_ASSERT_EQ(ServerObjectA->IntB, ClientObjectA->IntB);
UE_NET_ASSERT_EQ(ClientObjectA->PrevIntBStoredInOnRep, 1);
UE_NET_ASSERT_EQ(ServerObjectA->IntC, ClientObjectA->IntC);
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestNetMetric)
{
{
FNetMetric Metric(50.0);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Double);
}
{
FNetMetric Metric(50.f);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Double);
}
{
FNetMetric Metric;
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::None);
float Value = 100.f;
Metric.Set(Value);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Double);
}
{
FNetMetric Metric(5U);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Unsigned);
}
{
FNetMetric Metric;
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::None);
uint32 Value = 100U;
Metric.Set(Value);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Unsigned);
}
{
FNetMetric Metric(-5);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Signed);
}
{
FNetMetric Metric;
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::None);
Metric.Set(5);
UE_NET_ASSERT_TRUE(Metric.GetDataType() == FNetMetric::EDataType::Signed);
}
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestReplicationRecordStarvation)
{
IConsoleVariable* CVarReplicationRecordStarvationThreshold = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Iris.ReplicationWriterReplicationRecordStarvationThreshold"));
UE_NET_ASSERT_NE(CVarReplicationRecordStarvationThreshold, nullptr);
UE_NET_ASSERT_TRUE(CVarReplicationRecordStarvationThreshold->IsVariableInt());
const int32 PrevReplicationRecordStarvationThreshold = CVarReplicationRecordStarvationThreshold->GetInt();
ON_SCOPE_EXIT
{
CVarReplicationRecordStarvationThreshold->Set(PrevReplicationRecordStarvationThreshold, ECVF_SetByCode);
};
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Set starvation threshold to highest possible
CVarReplicationRecordStarvationThreshold->Set(FReplicationRecord::MaxReplicationRecordCount, ECVF_SetByCode);
// Consume one ReplicationRecord to enter starvation
UReplicatedTestObject* FirstObject = Server->CreateObject(UTestReplicatedIrisObject::FComponents{});
const FNetRefHandle FirstObjectRefHandle = FirstObject->NetRefHandle;
Server->NetUpdate();
Server->SendUpdate(Client->ConnectionIdOnServer);
Server->PostSendUpdate();
// Try creating a second object. This should not succeed but we won't be able to test until we've delivered packets.
UReplicatedTestObject* SecondObject = Server->CreateObject(UTestReplicatedIrisObject::FComponents{});
const FNetRefHandle SecondObjectRefHandle = SecondObject->NetRefHandle;
Server->NetUpdate();
Server->SendUpdate(Client->ConnectionIdOnServer);
Server->PostSendUpdate();
Server->DeliverTo(Client, DeliverPacket);
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(FirstObjectRefHandle));
// The second packet, if any, should not allow object replication due to starvation.
Server->DeliverTo(Client, DeliverPacket);
UE_NET_ASSERT_FALSE(Client->IsResolvableNetRefHandle(SecondObjectRefHandle));
// Now we should be able replicate objects again. Retry sending the second object.
Server->NetUpdate();
Server->SendUpdate(Client->ConnectionIdOnServer);
Server->PostSendUpdate();
// Try destroying the first object. This should not succeed.
Server->DestroyObject(FirstObject);
// Deliver the attempt to create the second object and verify it exists on the client.
Server->DeliverTo(Client, DeliverPacket);
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(SecondObjectRefHandle));
// The second packet, if any, should not allow object destruction due to starvation.
Server->DeliverTo(Client, DeliverPacket);
UE_NET_ASSERT_TRUE(Client->IsResolvableNetRefHandle(FirstObjectRefHandle));
// Now we should be able to destroy objects again. Retry destroying the first object
Server->UpdateAndSend({Client});
UE_NET_ASSERT_FALSE(Client->IsValidNetRefHandle(FirstObjectRefHandle));
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestSubObjectReplicatedDestroyBeforePostNetReceive)
{
// Make sure that bOldImmediateDispatchEndReplicationForSubObjects is set to true
IConsoleVariable* LocalCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Iris.ImmediateDispatchEndReplicationForSubObjects"));
check(LocalCVar != nullptr && LocalCVar->IsVariableBool());
const bool bOldImmediateDispatchEndReplicationForSubObjects = LocalCVar->GetBool();
LocalCVar->Set(true, ECVF_SetByCode);
ON_SCOPE_EXIT
{
// Restore cvars
LocalCVar->Set(bOldImmediateDispatchEndReplicationForSubObjects, ECVF_SetByCode);
};
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
// Spawn some subobjects
UReplicatedSubObjectDestroyOrderObject* ServerSubObject0 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject* ServerSubObject1 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject* ServerSubObject2 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject::RepOrderCounter = 0U;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that objects have replicated
UReplicatedSubObjectDestroyOrderObject* ClientSubObject0 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject0->NetRefHandle));
UReplicatedSubObjectDestroyOrderObject* ClientSubObject1 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject1->NetRefHandle));
UReplicatedSubObjectDestroyOrderObject* ClientSubObject2 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject2->NetRefHandle));
UE_NET_ASSERT_NE(ClientSubObject0, nullptr);
UE_NET_ASSERT_NE(ClientSubObject1, nullptr);
UE_NET_ASSERT_NE(ClientSubObject2, nullptr);
// Verify that they have replicated in expected order
UE_NET_ASSERT_EQ(ClientSubObject0->LastRepOrderCounter, 1U);
UE_NET_ASSERT_GT(ClientSubObject1->LastRepOrderCounter, ClientSubObject0->LastRepOrderCounter);
UE_NET_ASSERT_GT(ClientSubObject2->LastRepOrderCounter, ClientSubObject1->LastRepOrderCounter);
// setup a watch on the client
ClientSubObject2->SetObjectExpectedToBeDestroyed(ClientSubObject1);
// Dirty some data on server and destroy SubObject1
++ServerSubObject2->IntA;
Server->ReplicationBridge->EndReplication(ServerSubObject1, EEndReplicationFlags::Destroy);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions, replicated subobject destroy should have been issued before ClientSubObjects2 s call to PostNetReceive
UE_NET_ASSERT_TRUE(ClientSubObject2->bObjectExistedInPreNetReceive);
UE_NET_ASSERT_FALSE(ClientSubObject2->bObjectExistedInPostNetReceive);
UE_NET_ASSERT_FALSE(ClientSubObject2->ObjectToWatch->IsValid());
}
UE_NET_TEST_FIXTURE(FReplicationSystemServerClientTestFixture, TestSubObjectReplicatedDestroyAfterPostNetReceive)
{
// Make sure that bOldImmediateDispatchEndReplicationForSubObjects is set to false
IConsoleVariable* LocalCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Iris.ImmediateDispatchEndReplicationForSubObjects"));
check(LocalCVar != nullptr && LocalCVar->IsVariableBool());
const bool bOldImmediateDispatchEndReplicationForSubObjects = LocalCVar->GetBool();
LocalCVar->Set(false, ECVF_SetByCode);
ON_SCOPE_EXIT
{
// Restore cvars
LocalCVar->Set(bOldImmediateDispatchEndReplicationForSubObjects, ECVF_SetByCode);
};
// Add a client
FReplicationSystemTestClient* Client = CreateClient();
// Spawn object on server
UReplicatedTestObject* ServerObject = Server->CreateObject(0, 0);
// Spawn some subobjects
UReplicatedSubObjectDestroyOrderObject* ServerSubObject0 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject* ServerSubObject1 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject* ServerSubObject2 = Server->CreateSubObject<UReplicatedSubObjectDestroyOrderObject>(ServerObject->NetRefHandle);
UReplicatedSubObjectDestroyOrderObject::RepOrderCounter = 0U;
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify that objects have replicated
UReplicatedSubObjectDestroyOrderObject* ClientSubObject0 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject0->NetRefHandle));
UReplicatedSubObjectDestroyOrderObject* ClientSubObject1 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject1->NetRefHandle));
UReplicatedSubObjectDestroyOrderObject* ClientSubObject2 = Cast<UReplicatedSubObjectDestroyOrderObject>(Client->GetReplicationBridge()->GetReplicatedObject(ServerSubObject2->NetRefHandle));
UE_NET_ASSERT_NE(ClientSubObject0, nullptr);
UE_NET_ASSERT_NE(ClientSubObject1, nullptr);
UE_NET_ASSERT_NE(ClientSubObject2, nullptr);
// Verify that they have replicated in expected order
UE_NET_ASSERT_EQ(ClientSubObject0->LastRepOrderCounter, 1U);
UE_NET_ASSERT_GT(ClientSubObject1->LastRepOrderCounter, ClientSubObject0->LastRepOrderCounter);
UE_NET_ASSERT_GT(ClientSubObject2->LastRepOrderCounter, ClientSubObject1->LastRepOrderCounter);
// setup a watch on the client
ClientSubObject2->SetObjectExpectedToBeDestroyed(ClientSubObject1);
// Dirty some data on server and destroy SubObject1
++ServerSubObject2->IntA;
Server->ReplicationBridge->EndReplication(ServerSubObject1, EEndReplicationFlags::Destroy);
// Send and deliver packet
Server->UpdateAndSend({Client});
// Verify assumptions, replicated subobject destroy should have been issued after ClientSubObjects2 s call to PostNetReceive
UE_NET_ASSERT_TRUE(ClientSubObject2->bObjectExistedInPreNetReceive);
UE_NET_ASSERT_TRUE(ClientSubObject2->bObjectExistedInPostNetReceive);
UE_NET_ASSERT_FALSE(ClientSubObject2->ObjectToWatch->IsValid());
}
} // end namespace UE::Net::Private