Merging using //UE5/Main_to_//UE5/Release-Engine-Staging @14384769

autoresolved files
#rb none

[CL 14384911 by Marcus Wassmer in ue5-main branch]
This commit is contained in:
Marcus Wassmer
2020-09-24 00:43:27 -04:00
parent 6638f126f7
commit 3b81cf8201
13529 changed files with 2378633 additions and 444266 deletions
@@ -113,8 +113,10 @@ float CVar_RepGraph_OutOfRangeDistanceCheckRatio = 0.5f;
static FAutoConsoleVariableRef CVarRepGraphOutOfRangeDistanceCheckRatio(TEXT("Net.RepGraph.OutOfRangeDistanceCheckRatio"), CVar_RepGraph_OutOfRangeDistanceCheckRatio,
TEXT("The ratio of DestructInfoMaxDistance that gives the distance traveled before we reevaluate the out of range destroyed actors list"), ECVF_Default);
static TAutoConsoleVariable<float> CVar_ForceConnectionViewerPriority(TEXT("Net.RepGraph.ForceConnectionViewerPriority"), 1,
TEXT("Force the connection's player controller and viewing pawn as topmost priority."));
int32 CVar_RepGraph_DormancyNode_DisconnectedBehavior = 1;
static FAutoConsoleVariableRef CVarRepGraphDormancyNodeDisconnectedBehavior(TEXT("Net.RepGraph.DormancyNodeDisconnectedBehavior"), CVar_RepGraph_DormancyNode_DisconnectedBehavior, TEXT("This changes how the dormancy node deals with disconnected clients. 0 = ignore. 1 = skip the disconnected client nodes. 2 = lazily destroy the disconnected client nodes"), ECVF_Default);
static TAutoConsoleVariable<float> CVar_ForceConnectionViewerPriority(TEXT("Net.RepGraph.ForceConnectionViewerPriority"), 1, TEXT("Force the connection's player controller and viewing pawn as topmost priority."));
REPGRAPH_DEVCVAR_SHIPCONST(int32, "Net.RepGraph.LogNetDormancyDetails", CVar_RepGraph_LogNetDormancyDetails, 0, "Logs actors that are removed from the replication graph/nodes.");
REPGRAPH_DEVCVAR_SHIPCONST(int32, "Net.RepGraph.LogActorRemove", CVar_RepGraph_LogActorRemove, 0, "Logs actors that are removed from the replication graph/nodes.");
@@ -627,14 +629,19 @@ void UReplicationGraph::RemoveNetworkActor(AActor* Actor)
GlobalActorReplicationInfoMap.Remove(Actor);
for (UNetReplicationGraphConnection* ConnectionManager : Connections)
{
ConnectionManager->ActorInfoMap.RemoveActor(Actor);
QUICK_SCOPE_CYCLE_COUNTER(UReplicationGraph_RemoveNetworkActor_FromConnectionsMap);
for (UNetReplicationGraphConnection* ConnectionManager : Connections)
{
ConnectionManager->ActorInfoMap.RemoveActor(Actor);
}
}
}
void UReplicationGraph::RouteRemoveNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo)
{
QUICK_SCOPE_CYCLE_COUNTER(UReplicationGraph_RouteRemoveNetworkActorToNodes);
// The base implementation just routes to every global node. Subclasses will want a more direct routing function where possible.
for (UReplicationGraphNode* Node : GlobalGraphNodes)
{
@@ -898,8 +905,6 @@ int32 UReplicationGraph::ServerReplicateActors(float DeltaSeconds)
// For Each Connection
// -------------------------------------------------------
FGatheredReplicationActorLists GatheredReplicationListsForConnection;
// Total number of children processed, added to all the connections later for stat tracking purposes.
int32 NumChildrenConnectionsProcessed = 0;
@@ -979,7 +984,7 @@ int32 UReplicationGraph::ServerReplicateActors(float DeltaSeconds)
// GATHER list of ReplicationLists for this connection
// --------------------------------------------------------------------------------------------------------------
GatheredReplicationListsForConnection.Reset();
FGatheredReplicationActorLists GatheredReplicationListsForConnection;
TSet<FName> AllVisibleLevelNames;
ConnectionManager->GetClientVisibleLevelNames(AllVisibleLevelNames);
@@ -1175,7 +1180,8 @@ void UReplicationGraph::ReplicateActorListsForConnections_Default(UNetReplicatio
const float MaxDistanceScaling = PrioritizationConstants.MaxDistanceScaling;
const uint32 MaxFramesSinceLastRep = PrioritizationConstants.MaxFramesSinceLastRep;
for (FActorRepListRawView& List : GatheredReplicationListsForConnection.GetLists(EActorRepListTypeFlags::Default))
const TArray<FActorRepListConstView>& GatheredLists = GatheredReplicationListsForConnection.GetLists(EActorRepListTypeFlags::Default);
for (const FActorRepListConstView& List : GatheredLists)
{
// Add actors from gathered list
NumGatheredActorsOnConnection += List.Num();
@@ -1487,10 +1493,10 @@ void UReplicationGraph::ReplicateActorListsForConnections_FastShared(UNetReplica
// This really isn't ideal. We want to have better ways of tracking and limiting network traffic. This feels pretty hacky in implementation but conceptually is good.
FScopedQueuedBits ScopedQueuedBits(NetConnection->QueuedBits, TotalBitsWritten);
TArray< FActorRepListRawView>& GatheredLists = GatheredReplicationListsForConnection.GetLists(EActorRepListTypeFlags::FastShared);
const TArray<FActorRepListConstView>& GatheredLists = GatheredReplicationListsForConnection.GetLists(EActorRepListTypeFlags::FastShared);
for (int32 ListIdx = 0; ListIdx < GatheredLists.Num(); ++ListIdx)
{
FActorRepListRawView& List = GatheredLists[(ListIdx + FrameNum) % GatheredLists.Num()];
const FActorRepListConstView& List = GatheredLists[(ListIdx + FrameNum) % GatheredLists.Num()];
for (int32 i = 0; i < List.Num(); ++i)
{
// Round robin through the list over multiple frames. We want to avoid sorting this list based on 'time since last rep'. This is a good balance
@@ -1772,8 +1778,6 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat
const bool bWantsToGoDormant = GlobalActorInfo.bWantsToBeDormant;
const FActorRepListRefView& DependentActorList = GlobalActorInfo.GetDependentActorList();
bool bOpenActorChannel = (ActorInfo.Channel == nullptr);
if (bOpenActorChannel)
@@ -1837,7 +1841,8 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat
// ----------------------------
// Dependent actors
// ----------------------------
if (DependentActorList.IsValid())
const FGlobalActorReplicationInfo::FDependantListType& DependentActorList = GlobalActorInfo.GetDependentActorList();
if (DependentActorList.Num() > 0)
{
RG_QUICK_SCOPE_CYCLE_COUNTER(NET_ReplicateActors_DependentActors);
@@ -1896,9 +1901,9 @@ void UReplicationGraph::HandleStarvedActorList(const FPrioritizedRepList& List,
// Update dependent actor's timeout frame
FGlobalActorReplicationInfo& GlobalActorInfo = GlobalActorReplicationInfoMap.Get(RepItem.Actor);
const FActorRepListRefView& DependentActorList = GlobalActorInfo.GetDependentActorList();
const FGlobalActorReplicationInfo::FDependantListType& DependentActorList = GlobalActorInfo.GetDependentActorList();
if (DependentActorList.IsValid())
if (DependentActorList.Num() > 0)
{
const uint32 CloseFrameNum = ActorInfo.ActorChannelCloseFrameNum;
for (AActor* DependentActor : DependentActorList)
@@ -2092,14 +2097,18 @@ bool UReplicationGraph::ProcessRemoteFunction(class AActor* Actor, UFunction* Fu
if (ShouldOpenChannel)
{
// We are within range, we will open a channel now for this actor and call the RPC on it
ConnectionActorInfo.Channel = (UActorChannel *)NetConnection->CreateChannelByName( NAME_Actor, EChannelCreateFlags::OpenedLocally );
ConnectionActorInfo.Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
ConnectionActorInfo.Channel = (UActorChannel*)NetConnection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally);
// Update timeout frame name. We would run into problems if we open the channel, queue a bunch, and then it timeouts before RepGraph replicates properties.
UpdateActorChannelCloseFrameNum(Actor, ConnectionActorInfo, GlobalInfo, ReplicationGraphFrame+1 /** Plus one to error on safe side. RepFrame num will be incremented in the next tick */, NetConnection );
if (ConnectionActorInfo.Channel)
{
ConnectionActorInfo.Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
// If this actor is dormant on the connection, we will force a flushnetdormancy call.
ForceFlushNetDormancy |= ConnectionActorInfo.bDormantOnConnection;
// Update timeout frame name. We would run into problems if we open the channel, queue a bunch, and then it timeouts before RepGraph replicates properties.
UpdateActorChannelCloseFrameNum(Actor, ConnectionActorInfo, GlobalInfo, ReplicationGraphFrame+1 /** Plus one to error on safe side. RepFrame num will be incremented in the next tick */, NetConnection );
// If this actor is dormant on the connection, we will force a flushnetdormancy call.
ForceFlushNetDormancy |= ConnectionActorInfo.bDormantOnConnection;
}
}
}
}
@@ -2738,7 +2747,7 @@ void UReplicationGraphNode::NotifyResetAllNetworkActors()
}
}
void UReplicationGraphNode::RemoveChildNode(UReplicationGraphNode* ChildNode, UReplicationGraphNode::NodeOrdering NodeOrder)
bool UReplicationGraphNode::RemoveChildNode(UReplicationGraphNode* ChildNode, UReplicationGraphNode::NodeOrdering NodeOrder)
{
ensure(ChildNode != nullptr);
@@ -2757,6 +2766,8 @@ void UReplicationGraphNode::RemoveChildNode(UReplicationGraphNode* ChildNode, UR
{
ChildNode->TearDown();
}
return Removed > 0;
}
void UReplicationGraphNode::CleanChildNodes(UReplicationGraphNode::NodeOrdering NodeOrder)
@@ -2813,7 +2824,7 @@ bool FStreamingLevelActorListCollection::RemoveActor(const FNewReplicatedActorIn
{
if (StreamingList.StreamingLevelName == ActorInfo.StreamingLevelName)
{
bRemovedSomething = StreamingList.ReplicationActorList.Remove(ActorInfo.Actor);
bRemovedSomething = StreamingList.ReplicationActorList.RemoveSlow(ActorInfo.Actor);
if (!bRemovedSomething && bWarnIfNotFound)
{
UE_LOG(LogReplicationGraph, Warning, TEXT("Attempted to remove %s from list %s but it was not found. (StreamingLevelName == %s)"), *GetActorRepListTypeDebugString(ActorInfo.Actor), *GetPathNameSafe(Outer), *ActorInfo.StreamingLevelName.ToString() );
@@ -2925,7 +2936,7 @@ bool UReplicationGraphNode_ActorList::NotifyRemoveNetworkActor(const FNewReplica
if (ActorInfo.StreamingLevelName == NAME_None)
{
bRemovedSomething = ReplicationActorList.Remove(ActorInfo.Actor);
bRemovedSomething = ReplicationActorList.RemoveSlow(ActorInfo.Actor);
UE_CLOG(!bRemovedSomething && bWarnIfNotFound, LogReplicationGraph, Warning, TEXT("Attempted to remove %s from list %s but it was not found. (StreamingLevelName == NAME_None)"), *GetActorRepListTypeDebugString(ActorInfo.Actor), *GetFullName());
@@ -3066,7 +3077,7 @@ bool UReplicationGraphNode_ActorListFrequencyBuckets::NotifyRemoveNetworkActor(c
bool bFound = false;
for (FActorRepListRefView& List : NonStreamingCollection)
{
if (List.Remove(ActorInfo.Actor))
if (List.RemoveSlow(ActorInfo.Actor))
{
bRemovedSomething = true;
TotalNumNonStreamingActors--;
@@ -3824,7 +3835,6 @@ void UReplicationGraphNode_ConnectionDormancyNode::ConditionalGatherDormantActor
ConnectionList.RemoveAtSwap(idx);
if (RemovedList)
{
RemovedList->PrepareForWrite();
RemovedList->Add(Actor);
}
@@ -3897,7 +3907,6 @@ void UReplicationGraphNode_ConnectionDormancyNode::NotifyActorDormancyFlush(FAct
FStreamingLevelActorListCollection::FStreamingLevelActors* RemoveList = RemovedStreamingLevelActorListCollection.StreamingLevelLists.FindByKey(ActorInfo.StreamingLevelName);
if (RemoveList)
{
RemoveList->ReplicationActorList.PrepareForWrite();
RemoveList->ReplicationActorList.RemoveFast(Actor);
}
}
@@ -3922,10 +3931,8 @@ void UReplicationGraphNode_ConnectionDormancyNode::OnClientVisibleLevelNameAdd(F
UE_CLOG(CVar_RepGraph_LogNetDormancyDetails, LogReplicationGraph, Display, TEXT(" CurrentAddList: %s"), *AddList->ReplicationActorList.BuildDebugString());
UE_CLOG(CVar_RepGraph_LogNetDormancyDetails, LogReplicationGraph, Display, TEXT(" RemoveList: %s"), *RemoveList->ReplicationActorList.BuildDebugString());
AddList->ReplicationActorList.PrepareForWrite();
AddList->ReplicationActorList.AppendContentsFrom(RemoveList->ReplicationActorList);
RemoveList->ReplicationActorList.PrepareForWrite();
RemoveList->ReplicationActorList.Reset();
}
@@ -3953,6 +3960,40 @@ void UReplicationGraphNode_ConnectionDormancyNode::NotifyResetAllNetworkActors()
float UReplicationGraphNode_DormancyNode::MaxZForConnection = WORLD_MAX;
void UReplicationGraphNode_DormancyNode::CallFunctionOnValidConnectionNodes(FConnectionDormancyNodeFunction Function)
{
enum class DisconnectedClientNodeBehavior
{
AlwaysValid = 0, // Keep calling functions on disconnected client nodes (previous behavior)
Deactivate = 1, // Deactivate the nodes by ignoring them. (wastes memory but doesn't incur a costly destruction)
Destroy = 2, // Destroy the nodes immediately (one time cpu hit)
};
const DisconnectedClientNodeBehavior DisconnectedClientBehavior = (DisconnectedClientNodeBehavior)CVar_RepGraph_DormancyNode_DisconnectedBehavior;
QUICK_SCOPE_CYCLE_COUNTER(UReplicationGraphNode_DormancyNode_ConnectionLoop);
for (FConnectionDormancyNodeMap::TIterator It = ConnectionNodes.CreateIterator(); It; ++It)
{
const FRepGraphConnectionKey RepGraphConnection = It.Key();
const bool bIsActiveConnection = (DisconnectedClientBehavior == DisconnectedClientNodeBehavior::AlwaysValid) || (RepGraphConnection.ResolveObjectPtr() != nullptr);
if (bIsActiveConnection)
{
Function(It.Value());
}
else if (DisconnectedClientBehavior == DisconnectedClientNodeBehavior::Destroy)
{
// The connection is now invalid. Destroy it's corresponding node
UReplicationGraphNode_ConnectionDormancyNode* ConnectionNodeToDestroy = It.Value();
bool bWasRemoved = RemoveChildNode(ConnectionNodeToDestroy, UReplicationGraphNode::NodeOrdering::IgnoreOrdering);
ensureMsgf(bWasRemoved, TEXT("DormancyNode did not find %s in it's child node."), *ConnectionNodeToDestroy->GetName());
//TODO: Release the ActorList in the Node ?
It.RemoveCurrent();
}
}
}
void UReplicationGraphNode_DormancyNode::NotifyResetAllNetworkActors()
{
if (GraphGlobals.IsValid())
@@ -3968,14 +4009,11 @@ void UReplicationGraphNode_DormancyNode::NotifyResetAllNetworkActors()
// Dump our global actor list
Super::NotifyResetAllNetworkActors();
// Reset the per connection nodes
for (auto& MapIt : ConnectionNodes)
auto ResetAllActorsFunction = [](UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode)
{
if (MapIt.Value)
{
MapIt.Value->NotifyResetAllNetworkActors();
}
}
ConnectionNode->NotifyResetAllNetworkActors();
};
CallFunctionOnValidConnectionNodes(ResetAllActorsFunction);
}
void UReplicationGraphNode_DormancyNode::AddDormantActor(const FNewReplicatedActorInfo& ActorInfo, FGlobalActorReplicationInfo& GlobalInfo)
@@ -3986,12 +4024,12 @@ void UReplicationGraphNode_DormancyNode::AddDormantActor(const FNewReplicatedAct
UE_CLOG(CVar_RepGraph_LogNetDormancyDetails > 0 && ConnectionNodes.Num() > 0, LogReplicationGraph, Display, TEXT("GRAPH_DORMANCY: AddDormantActor %s on %s. Adding to %d connection nodes."), *ActorInfo.Actor->GetPathName(), *GetName(), ConnectionNodes.Num());
for (auto& MapIt : ConnectionNodes)
auto AddActorFunction = [ActorInfo](UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode)
{
QUICK_SCOPE_CYCLE_COUNTER(ConnectionDormancyNode_NotifyAddNetworkActor);
UReplicationGraphNode_ConnectionDormancyNode* Node = MapIt.Value;
Node->NotifyAddNetworkActor(ActorInfo);
}
QUICK_SCOPE_CYCLE_COUNTER(ConnectionDormancyNode_NotifyAddNetworkActor);
ConnectionNode->NotifyAddNetworkActor(ActorInfo);
};
CallFunctionOnValidConnectionNodes(AddActorFunction);
// Tell us if this guy flushes net dormancy so we force him back on connection lists
GlobalInfo.Events.DormancyFlush.AddUObject(this, &UReplicationGraphNode_DormancyNode::OnActorDormancyFlush);
@@ -4004,15 +4042,15 @@ void UReplicationGraphNode_DormancyNode::RemoveDormantActor(const FNewReplicated
UE_CLOG(CVar_RepGraph_LogActorRemove>0, LogReplicationGraph, Display, TEXT("UReplicationGraphNode_DormancyNode::RemoveDormantActor %s on %s. (%d connection nodes). ChildNodes: %d"), *GetNameSafe(ActorInfo.Actor), *GetPathName(), ConnectionNodes.Num(), AllChildNodes.Num());
Super::RemoveNetworkActorFast(ActorInfo);
ActorRepInfo.Events.DormancyFlush.RemoveAll(this);
// Update any connection specific nodes
for (auto& MapIt : ConnectionNodes)
auto RemoveActorFunction = [ActorInfo](UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode)
{
UReplicationGraphNode_ConnectionDormancyNode* Node = MapIt.Value;
Node->NotifyRemoveNetworkActor(ActorInfo, false); // Don't warn if not found, the node may have removed the actor itself. Not worth the extra bookkeeping to skip the call.
}
// Don't warn if not found, the node may have removed the actor itself. Not worth the extra bookkeeping to skip the call.
ConnectionNode->NotifyRemoveNetworkActor(ActorInfo, false);
};
CallFunctionOnValidConnectionNodes(RemoveActorFunction);
}
void UReplicationGraphNode_DormancyNode::GatherActorListsForConnection(const FConnectionGatherActorListParameters& Params)
@@ -4038,29 +4076,27 @@ void UReplicationGraphNode_DormancyNode::GatherActorListsForConnection(const FCo
UReplicationGraphNode_ConnectionDormancyNode* UReplicationGraphNode_DormancyNode::GetExistingConnectionNode(const FConnectionGatherActorListParameters& Params)
{
UReplicationGraphNode_ConnectionDormancyNode** ConnectionNodeItem = ConnectionNodes.Find(&Params.ConnectionManager);
UReplicationGraphNode_ConnectionDormancyNode** ConnectionNodeItem = ConnectionNodes.Find(FRepGraphConnectionKey(&Params.ConnectionManager));
return ConnectionNodeItem == nullptr ? nullptr : *ConnectionNodeItem;
}
UReplicationGraphNode_ConnectionDormancyNode* UReplicationGraphNode_DormancyNode::GetConnectionNode(const FConnectionGatherActorListParameters& Params)
{
UReplicationGraphNode_ConnectionDormancyNode** NodePtrPtr = ConnectionNodes.Find(&Params.ConnectionManager);
UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode = nullptr;
if (!NodePtrPtr)
FRepGraphConnectionKey RepGraphConnection(&Params.ConnectionManager);
UReplicationGraphNode_ConnectionDormancyNode** NodePtrPtr = ConnectionNodes.Find(RepGraphConnection);
UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode = NodePtrPtr != nullptr ? *NodePtrPtr : nullptr;
if (ConnectionNode == nullptr)
{
// We dont have a per-connection node for this connection, so create one and copy over contents
ConnectionNode = CreateChildNode<UReplicationGraphNode_ConnectionDormancyNode>();
ConnectionNodes.Add(&Params.ConnectionManager) = ConnectionNode;
ConnectionNodes.Add(RepGraphConnection) = ConnectionNode;
// Copy our master lists to the connection node
ConnectionNode->DeepCopyActorListsFrom(this);
UE_CLOG(CVar_RepGraph_LogNetDormancyDetails > 0, LogReplicationGraph, Display, TEXT("GRAPH_DORMANCY: First time seeing connection %s in node %s. Created ConnectionDormancyNode %s."), *Params.ConnectionManager.GetName(), *GetName(), *ConnectionNode->GetName());
}
else
{
ConnectionNode = *NodePtrPtr;
}
return ConnectionNode;
}
@@ -4089,11 +4125,11 @@ void UReplicationGraphNode_DormancyNode::OnActorDormancyFlush(FActorRepListType
UE_CLOG(CVar_RepGraph_LogNetDormancyDetails > 0 && ConnectionNodes.Num() > 0, LogReplicationGraph, Display, TEXT("GRAPH_DORMANCY: Actor %s Flushed Dormancy. %s. Refreshing all %d connection nodes."), *Actor->GetPathName(), *GetName(), ConnectionNodes.Num());
for (auto& MapIt : ConnectionNodes)
auto DormancyFlushFunction = [Actor](UReplicationGraphNode_ConnectionDormancyNode* ConnectionNode)
{
UReplicationGraphNode_ConnectionDormancyNode* Node = MapIt.Value;
Node->NotifyActorDormancyFlush(Actor);
}
ConnectionNode->NotifyActorDormancyFlush(Actor);
};
CallFunctionOnValidConnectionNodes(DormancyFlushFunction);
}
void UReplicationGraphNode_DormancyNode::ConditionalGatherDormantDynamicActors(FActorRepListRefView& RepList, const FConnectionGatherActorListParameters& Params, FActorRepListRefView* RemovedList, bool bEnforceReplistUniqueness)
@@ -4106,7 +4142,7 @@ void UReplicationGraphNode_DormancyNode::ConditionalGatherDormantDynamicActors(F
{
if (Info->bDormantOnConnection)
{
if (RemovedList && RemovedList->IsValid() && RemovedList->Contains(Actor))
if (RemovedList && RemovedList->Contains(Actor))
{
continue;
}
@@ -4124,7 +4160,6 @@ void UReplicationGraphNode_DormancyNode::ConditionalGatherDormantDynamicActors(F
}
}
RepList.PrepareForWrite();
RepList.ConditionalAdd(Actor);
}
}
@@ -5221,7 +5256,7 @@ void UReplicationGraphNode_GridSpatialization2D::GatherActorListsForConnection(c
}
// Now process the previous dormant list to handle destruction
if (bCellHasChanged && PrevDormantActorList.IsValid())
if (bCellHasChanged)
{
// any previous dormant actors not in the current node dormant list
for (FActorRepListType& Actor : PrevDormantActorList)
@@ -718,7 +718,6 @@ FAutoConsoleCommandWithWorldAndArgs NetRepGraphSetCellSize(TEXT("Net.RepGraph.Sp
FAutoConsoleCommand RepDriverListsAddTestmd(TEXT("Net.RepGraph.Lists.AddTest"), TEXT(""), FConsoleCommandWithArgsDelegate::CreateLambda([](const TArray< FString >& Args)
{
static FActorRepListRefView List;
List.PrepareForWrite(true);
int32 Num = 1;
if (Args.Num() > 0 )
@@ -1055,7 +1054,7 @@ void UReplicationGraphNode::LogNode(FReplicationGraphDebugInfo& DebugInfo, const
void LogActorRepList(FReplicationGraphDebugInfo& DebugInfo, FString Prefix, const FActorRepListRefView& List)
{
if (List.IsValid() == false || List.Num() <= 0)
if (List.Num() <= 0)
{
return;
}
@@ -192,6 +192,7 @@ private:
FPool* Pool = PoolTable.FindByPredicate([&ExpectedMaxSize](const FPool& InPool) { return ExpectedMaxSize <= InPool.ListSize; });
if (!Pool)
{
QUICK_SCOPE_CYCLE_COUNTER(RepList_Pool_Allocation);
if (!ForPreAllocation)
{
UE_LOG(LogReplicationGraph, Warning, TEXT("No pool big enough for requested list size %d. Creating a new pool. (You may want to preallocate a pool of this size or investigate why this size is needed)"), ExpectedMaxSize);
@@ -225,53 +226,6 @@ void FActorRepList::Release()
}
}
void FActorRepListRefView::RequestNewList(int32 NewSize, bool CopyExistingContent)
{
QUICK_SCOPE_CYCLE_COUNTER(RepList_RequestNewList);
FActorRepList* NewList = &GActorListAllocator.RequestList(NewSize > 0 ? NewSize : InitialListSize);
if (CopyExistingContent)
{
FMemory::Memcpy((uint8*)NewList->Data, (uint8*)CachedData, CachedNum * sizeof(FActorRepListType) );
NewList->Num = CachedNum;
}
else
{
repCheck(NewList->Num == 0);
CachedNum = 0;
}
RepList = NewList;
CachedData = RepList->Data;
CachedMax = RepList->Max;
}
void FActorRepListRefView::CopyContentsFrom(const FActorRepListRefView& Source)
{
const int32 NewNum = Source.CachedNum;
FActorRepList* NewList = &GActorListAllocator.RequestList(Source.Num());
FMemory::Memcpy((uint8*)NewList->Data, (uint8*)Source.CachedData, NewNum * sizeof(FActorRepListType) );
NewList->Num = NewNum;
RepList = NewList;
CachedData = NewList->Data;
CachedMax = NewList->Max;
CachedNum = NewNum;
}
void FActorRepListRefView::AppendContentsFrom(const FActorRepListRefView& Source)
{
const int32 NewNum = CachedNum + Source.CachedNum;
if (NewNum > CachedMax)
{
RequestNewList(NewNum, true);
}
FMemory::Memcpy((uint8*)&CachedData[CachedNum], (uint8*)Source.CachedData, Source.CachedNum * sizeof(FActorRepListType));
RepList->Num = NewNum;
CachedNum = NewNum;
}
bool FActorRepListRefView::VerifyContents_Slow() const
{
for (FActorRepListType Actor : *this)
@@ -294,6 +248,20 @@ bool FActorRepListRefView::VerifyContents_Slow() const
return true;
}
FString FActorRepListRefView::BuildDebugString() const
{
FString Str;
if (Num() > 0)
{
Str += GetActorRepListTypeDebugString(RepList[0]);
for (int32 i = 1; i < Num(); ++i)
{
Str += TEXT(", ") + GetActorRepListTypeDebugString(RepList[i]);
}
}
return Str;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
void PrintRepListStats(int32 mode)
{
@@ -475,22 +443,26 @@ void FGlobalActorReplicationInfoMap::AddDependentActor(AActor* Parent, AActor* C
bool bChildIsAlreadyDependant(false);
if (FGlobalActorReplicationInfo* ParentInfo = Find(Parent))
{
bChildIsAlreadyDependant = ParentInfo->DependentActorList.IsValid() && ParentInfo->DependentActorList.Contains(Child);
bChildIsAlreadyDependant = ParentInfo->DependentActorList.Find(Child) != INDEX_NONE;
if (bChildIsAlreadyDependant == false)
{
ParentInfo->DependentActorList.PrepareForWrite();
ParentInfo->DependentActorList.ConditionalAdd(Child);
if (IsActorValidForReplicationGather(Child))
{
ParentInfo->DependentActorList.Add(Child);
}
}
}
bool bChildHadParentAlready(false);
if (FGlobalActorReplicationInfo* ChildInfo = Find(Child))
{
bChildHadParentAlready = ChildInfo->ParentActorList.IsValid() && ChildInfo->ParentActorList.Contains(Parent);
bChildHadParentAlready = ChildInfo->ParentActorList.Find(Parent) != INDEX_NONE;
if (bChildHadParentAlready == false)
{
ChildInfo->ParentActorList.PrepareForWrite();
ChildInfo->ParentActorList.ConditionalAdd(Parent);
if (IsActorValidForReplicationGather(Parent))
{
ChildInfo->ParentActorList.Add(Parent);
}
}
}
@@ -125,8 +125,8 @@ public:
KeepOrder, // Use slower removal but keep the node order intact
};
/** Remove a child node from our list and flag it for destruction */
void RemoveChildNode(UReplicationGraphNode* OutChildNode, UReplicationGraphNode::NodeOrdering NodeOrder=UReplicationGraphNode::NodeOrdering::IgnoreOrdering);
/** Remove a child node from our list and flag it for destruction. Returns if the node was found or not */
bool RemoveChildNode(UReplicationGraphNode* OutChildNode, UReplicationGraphNode::NodeOrdering NodeOrder=UReplicationGraphNode::NodeOrdering::IgnoreOrdering);
/** Remove all null and about to be destroyed nodes from our list */
void CleanChildNodes(UReplicationGraphNode::NodeOrdering NodeOrder);
@@ -445,7 +445,20 @@ public:
private:
TMap<UNetReplicationGraphConnection*, UReplicationGraphNode_ConnectionDormancyNode*> ConnectionNodes;
/** Function called on every ConnectionDormancyNode in our list */
typedef TFunction<void(UReplicationGraphNode_ConnectionDormancyNode*)> FConnectionDormancyNodeFunction;
/**
* Iterates over all ConnectionDormancyNodes and calls the function on those still valid.
* If a RepGraphConnection was torn down since the last iteration, it removes and destroys the ConnectionDormancyNode associated with the Connection.
*/
void CallFunctionOnValidConnectionNodes(FConnectionDormancyNodeFunction Function);
private:
typedef TObjectKey<UNetReplicationGraphConnection> FRepGraphConnectionKey;
typedef TSortedMap<FRepGraphConnectionKey, UReplicationGraphNode_ConnectionDormancyNode*> FConnectionDormancyNodeMap;
FConnectionDormancyNodeMap ConnectionNodes;
};
UCLASS()
@@ -73,6 +73,7 @@ enum class EActorRepListTypeFlags : uint8
{
Default = 0,
FastShared = 1,
Max, // Always keep last
};
// Tests if an actor is valid for replication: not pending kill, etc. Says nothing about wanting to replicate or should replicate, etc.
@@ -209,56 +210,50 @@ struct TActorRepListViewBase
return Str;
}
FActorRepListType* begin() const { return RepList->Data; }
FActorRepListType* end() const { return RepList->Data + RepList->Num; }
private:
FORCEINLINE friend FActorRepListType* begin(const TActorRepListViewBase<PointerType>& View) { return View.RepList->Data; }
FORCEINLINE friend FActorRepListType* end(const TActorRepListViewBase<PointerType>& View) { return View.RepList->Data + View.RepList->Num; }
};
/** A view that maintains ownership/(ref counting) to an actor replication list. */
struct REPLICATIONGRAPH_API FActorRepListRefView : public TActorRepListViewBase<TRefCountPtr<FActorRepList>>
/**
* Holds a list of replicated actors that can be added/removed to.
*/
struct REPLICATIONGRAPH_API FActorRepListRefView
{
/** Ideally, use Reset to set the initial size of the list. But if nothing is set, the first list we request will be of this size */
enum { InitialListSize = 4 } ;
FActorRepListRefView() { }
FActorRepListRefView(FActorRepList& InRepList) { RepList = &InRepList; }
FORCEINLINE FActorRepListType& operator[](int32 idx) const { repCheck(RepList); repCheck(RepList->Max > idx); return RepList->Data[idx]; }
/** Initializes a new list for a given ExpectedMaxSize. Best practice is to call this once to get a good initial size to avoid reallocations/copying. Passing 0 will preserve the current size (if current size is also 0, InitialListSize is used) */
void Reset(int32 ExpectedMaxSize=0)
FActorRepListRefView()
{
if (RepList && RepList->RefCount == 1 && RepList->Max >= ExpectedMaxSize)
}
UE_DEPRECATED(4.27, "This constructor is deprecated since the class does not hold FActorRepList's anymore. ")
FActorRepListRefView(FActorRepList& InRepList)
{
RepList.Reserve(InRepList.Num);
for (int32 i = 0; i < InRepList.Num; ++i)
{
// We can keep using this list
RepList->Num = 0;
CachedNum = 0;
}
else
{
// We must request a new list for this size
RequestNewList(ExpectedMaxSize > 0 ? ExpectedMaxSize : CachedNum, false);
RepList.Add(InRepList.Data[i]);
}
}
/** Prepares the list for modifications. If this list is shared by other RefViews, then you will get a new underlying list to reference. ResetContent determines if the content is cleared or not (regardless of refcount/new list) */
/** Empties the array but does not deallocate the internal memory. Will be resized if the specified max size is bigger than the current max. */
void Reset(int32 ExpectedMaxSize=0)
{
RepList.Reset(ExpectedMaxSize);
}
/** Preallocate the array so it can hold the specified size */
void Reserve(int32 Size)
{
RepList.Reserve(Size);
}
UE_DEPRECATED(4.27, "PrepareForWrite is not needed before calling operations on the RepList anymore. Use Reserve or Reset if you want to preallocate the array to a specific size")
void PrepareForWrite(bool bResetContent=false)
{
if (RepList == nullptr)
{
RequestNewList(InitialListSize, false);
}
else if (RepList->RefCount > 1)
{
// This list we are viewing is shared by others, so request a new one
RequestNewList(CachedNum, !bResetContent);
}
else if(bResetContent)
{
// We already have our own list but need to reset back to 0
RepList->Num = 0;
CachedNum = 0;
}
// empty
}
bool ConditionalAdd(const FActorRepListType& NewElement)
@@ -273,83 +268,137 @@ struct REPLICATIONGRAPH_API FActorRepListRefView : public TActorRepListViewBase<
void Add(const FActorRepListType& NewElement)
{
repCheckf(RepList != nullptr, TEXT("Invalid RepList when calling add new element to a list. Call ::PrepareForWrite or ::Reset before writing!"));
repCheckf(RepList->RefCount == 1, TEXT("Attempting to add new element to a list with >1 RefCount (%d). Call ::PrepareForWrite before writing!"), RepList->RefCount);
if (CachedNum == CachedMax)
{
// We can't add more to the list we are referencing, we need to get a new list and copy the contents over. This is transparent to the caller.
RequestNewList(CachedMax+1, true);
}
CachedData[CachedNum++] = NewElement;
RepList->Num++;
RepList.Add(NewElement);
}
UE_DEPRECATED(4.27, "Remove has been deprecated in favor of RemoveSlow/RemoveFast. RemoveFast is the default recommendation unless you need the list order to be stable or are removing elements inside a RangedFor iteration loop.")
bool Remove(const FActorRepListType& ElementToRemove)
{
int32 idx = IndexOf(ElementToRemove);
if (idx >= 0)
{
RemoveAtImpl(idx);
return true;
}
return false;
return RemoveSlow(ElementToRemove);
}
/** Removes the element quickly but changes the list order */
bool RemoveFast(const FActorRepListType& ElementToRemove)
{
int32 idx = IndexOf(ElementToRemove);
if (idx >= 0)
{
RemoveAtSwap(idx);
return true;
}
return false;
return RepList.RemoveSingleSwap(ElementToRemove) > 0;
}
/** Removes the element but keeps the order intact. Generally not recommended for large lists. */
bool RemoveSlow(const FActorRepListType& ElementToRemove)
{
return RepList.RemoveSingle(ElementToRemove) > 0;
}
void RemoveAtSwap(int32 idx)
{
repCheck(RepList && Num() > idx);
CachedData[idx] = CachedData[CachedNum-1];
CachedNum--;
RepList->Num--;
RepList.RemoveAtSwap(idx);
}
void CopyContentsFrom(const FActorRepListRefView& Source)
{
RepList = Source.RepList;
}
void AppendContentsFrom(const FActorRepListRefView& Source)
{
RepList.Append(Source.RepList);
}
void CopyContentsFrom(const FActorRepListRefView& Source);
void AppendContentsFrom(const FActorRepListRefView& Source);
bool VerifyContents_Slow() const;
int32 Num() const { return CachedNum; }
/** Add contents to TArray/TSet. this is intended for debugging/ease of use */
void AppendToTArray(TArray<FActorRepListType>& OutArray) const
{
OutArray.Append(RepList);
}
void AppendToTSet(TSet<FActorRepListType>& OutSet) const
{
OutSet.Append(RepList);
}
FString BuildDebugString() const;
/**
* Base view functions.
*/
FActorRepListType& operator[](int32 idx) { return RepList[idx]; }
const FActorRepListType& operator[](int32 idx) const { return RepList[idx]; }
TArray<FActorRepListType>::RangedForIteratorType begin() { return RepList.begin(); }
TArray<FActorRepListType>::RangedForConstIteratorType begin() const { return RepList.begin(); }
TArray<FActorRepListType>::RangedForIteratorType end() { return RepList.end(); }
TArray<FActorRepListType>::RangedForConstIteratorType end() const { return RepList.end(); }
UE_DEPRECATED(4.27, "IsValid is deprecated now that you don't need to call PrepareForWrite before doing operations on the list. Use IsEmpty() if you need to skip doing operations on empty lists")
bool IsValid() const
{
return true;
}
bool IsEmpty() const
{
return RepList.Num() <= 0;
}
int32 Num() const
{
return RepList.Num();
}
/** Resets the container and returns the memory it held */
void ResetToNull()
{
RepList.Empty();
}
int32 IndexOf(const FActorRepListType& Value) const
{
return RepList.IndexOfByKey(Value);
}
bool Contains(const FActorRepListType& Value) const
{
return RepList.Contains(Value);
}
private:
/** Cached data from our FActorRepList to avoid looking it up each time */
FActorRepListType* CachedData = nullptr;
int32 CachedNum = 0;
int32 CachedMax = 0;
TArray<FActorRepListType> RepList;
};
void RequestNewList(int32 NewSize, bool bCopyExistingContent);
/**
* Gives temporary read-only access to a FActorRepListRefView by holding a reference to it.
*/
struct REPLICATIONGRAPH_API FActorRepListConstView
{
FActorRepListConstView(const FActorRepListRefView& InListReferenced) :
ListReferenced(InListReferenced)
{}
void RemoveAtImpl(int32 Index)
FActorRepListType operator[](int32 idx) const
{
repCheck(RepList && Num() > Index);
int32 NumToMove = CachedNum - Index - 1;
if (NumToMove)
{
FMemory::Memmove((void*)(&CachedData[Index]), (void*)(&CachedData[Index + 1]), NumToMove * sizeof(FActorRepListType));
}
CachedNum--;
RepList->Num--;
return ListReferenced[idx];
}
int32 Num() const
{
return ListReferenced.Num();
}
TArray<FActorRepListType>::RangedForConstIteratorType begin() const { return ListReferenced.begin(); }
TArray<FActorRepListType>::RangedForConstIteratorType end() const { return ListReferenced.end(); }
private:
const FActorRepListRefView& ListReferenced;
};
/** A read only, non owning (ref counting) view to an actor replication list: essentially a raw pointer and the category of the list. These are only created *from* FActorRepListRefView */
struct REPLICATIONGRAPH_API FActorRepListRawView : public TActorRepListViewBase<FActorRepList*>
struct UE_DEPRECATED(4.27, "Replace this struct with the new FActorRepListConstView struct.") FActorRepListRawView : public TActorRepListViewBase<FActorRepList*>
{
/** Standard ctor: make raw view from ref view */
FActorRepListRawView(const FActorRepListRefView& Source) { RepList = Source.RepList.GetReference(); }
FActorRepListRefView ToRefView() const { return FActorRepListRefView(*RepList); }
REPLICATIONGRAPH_API FActorRepListRawView(const FActorRepListRefView& Source) { /*deprecated*/ }
};
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
@@ -522,16 +571,17 @@ struct FGlobalActorReplicationInfo
}
}
const FActorRepListRefView& GetDependentActorList() { return DependentActorList; }
typedef TArray<FActorRepListType> FDependantListType;
const FGlobalActorReplicationInfo::FDependantListType& GetDependentActorList() { return DependentActorList; }
friend struct FGlobalActorReplicationInfoMap;
private:
/** When this actor replicates, we replicate these actors immediately afterwards (they are not gathered/prioritized/etc) */
FActorRepListRefView DependentActorList;
FDependantListType DependentActorList;
/** When this actor is added to the dependent list of a parent, track the parent here */
FActorRepListRefView ParentActorList;
FDependantListType ParentActorList;
};
/** Templatd struct for mapping UClasses to some data type. The main things this provides is that if a UClass* was not explicitly added, it will climb the class heirachy and find the best match (and then store this for faster lookup next time) */
@@ -718,28 +768,34 @@ struct FGlobalActorReplicationInfoMap
}
/** Removes actor data from map */
int32 Remove(const FActorRepListType& Actor)
int32 Remove(const FActorRepListType& RemovedActor)
{
if (FGlobalActorReplicationInfo* ActorInfo = Find(Actor))
// Clean the references to the removed actor from his dependency chain.
if (FGlobalActorReplicationInfo* RemovedActorInfo = Find(RemovedActor))
{
if (ActorInfo->DependentActorList.IsValid())
// Remove child dependents
for (AActor* ChildActor : RemovedActorInfo->DependentActorList)
{
for (AActor* ChildActor : ActorInfo->DependentActorList)
if (FGlobalActorReplicationInfo* ChildInfo = Find(ChildActor))
{
RemoveDependentActor(Actor, ChildActor);
ChildInfo->ParentActorList.RemoveSingleSwap(RemovedActor);
}
}
if (ActorInfo->ParentActorList.IsValid())
// Remove parent dependents
for (AActor* ParentActor : RemovedActorInfo->ParentActorList)
{
for (AActor* ParentActor : ActorInfo->ParentActorList)
if (FGlobalActorReplicationInfo* ParentInfo = Find(ParentActor))
{
RemoveDependentActor(ParentActor, Actor);
ParentInfo->DependentActorList.RemoveSingleSwap(RemovedActor);
}
}
RemovedActorInfo->DependentActorList.Reset();
RemovedActorInfo->ParentActorList.Reset();
}
return ActorMap.Remove(Actor);
return ActorMap.Remove(RemovedActor);
}
/** Returns ClassInfo for a given class. */
@@ -785,14 +841,12 @@ struct FGlobalActorReplicationInfoMap
{
if (FGlobalActorReplicationInfo* ParentInfo = Find(Parent))
{
ParentInfo->DependentActorList.PrepareForWrite();
ParentInfo->DependentActorList.Remove(Child);
ParentInfo->DependentActorList.RemoveSingleSwap(Child);
}
if (FGlobalActorReplicationInfo* ChildInfo = Find(Child))
{
ChildInfo->ParentActorList.PrepareForWrite();
ChildInfo->ParentActorList.Remove(Parent);
ChildInfo->ParentActorList.RemoveSingleSwap(Parent);
}
}
}
@@ -805,33 +859,25 @@ struct FGlobalActorReplicationInfoMap
return;
}
if (MainActorInfo->ParentActorList.IsValid())
// Remove this actor from all his parents
for (FActorRepListType ParentActor : MainActorInfo->ParentActorList)
{
// Remove this actor from all his parents
for (FActorRepListType ParentActor : MainActorInfo->ParentActorList)
if (FGlobalActorReplicationInfo* ParentInfo = Find(ParentActor))
{
if (FGlobalActorReplicationInfo* ParentInfo = Find(ParentActor))
{
ParentInfo->DependentActorList.PrepareForWrite();
ParentInfo->DependentActorList.Remove(MainActor);
}
ParentInfo->DependentActorList.RemoveSingleSwap(MainActor);
}
MainActorInfo->ParentActorList.Reset();
}
MainActorInfo->ParentActorList.Reset();
if (MainActorInfo->DependentActorList.IsValid())
// Remove all dependant childs from this actor
for (FActorRepListType ChildActor : MainActorInfo->DependentActorList)
{
// Remove all dependant childs from this actor
for (FActorRepListType ChildActor : MainActorInfo->DependentActorList)
if (FGlobalActorReplicationInfo* ChildInfo = Find(ChildActor))
{
if (FGlobalActorReplicationInfo* ChildInfo = Find(ChildActor))
{
ChildInfo->ParentActorList.PrepareForWrite();
ChildInfo->ParentActorList.Remove(MainActor);
}
ChildInfo->ParentActorList.RemoveSingleSwap(MainActor);
}
MainActorInfo->DependentActorList.Reset();
}
MainActorInfo->DependentActorList.Reset();
}
private:
@@ -1163,24 +1209,38 @@ struct REPLICATIONGRAPH_API FGatheredReplicationActorLists
if (CVar_RepGraph_Verify)
List.VerifyContents_Slow();
#endif
repCheck(List.IsValid());
if (List.Num() > 0)
{
OutReplicationLists.FindOrAdd(Flags).Emplace(FActorRepListRawView(List));
ReplicationLists[(uint32)Flags].Emplace(FActorRepListConstView(List));
CachedNum++;
}
}
FORCEINLINE void Reset() { OutReplicationLists.Reset(); CachedNum =0; }
FORCEINLINE int32 NumLists() const { return CachedNum; }
FORCEINLINE void Reset()
{
for (uint32 i = (uint32)EActorRepListTypeFlags::Default; i < (uint32)EActorRepListTypeFlags::Max; ++i)
{
ReplicationLists[i].Reset();
}
CachedNum=0;
}
FORCEINLINE int32 NumLists() const
{
return CachedNum;
}
FORCEINLINE TArray< FActorRepListRawView>& GetLists(EActorRepListTypeFlags ListFlags) { return OutReplicationLists.FindOrAdd(ListFlags); }
FORCEINLINE bool ContainsLists(EActorRepListTypeFlags Flags) { return OutReplicationLists.Contains(Flags); }
FORCEINLINE const TArray<FActorRepListConstView>& GetLists(EActorRepListTypeFlags ListFlags) const
{
return ReplicationLists[(uint32)ListFlags];
}
FORCEINLINE bool ContainsLists(EActorRepListTypeFlags Flags) const
{
return ReplicationLists[(uint32)Flags].Num() > 0;
}
private:
TMap<EActorRepListTypeFlags, TArray< FActorRepListRawView> > OutReplicationLists;
TStaticArray< TArray<FActorRepListConstView>, (uint32)EActorRepListTypeFlags::Max > ReplicationLists;
int32 CachedNum = 0;
};