Replicated Subobjects in Unreal Engine (UE) provide a way to replicate any UObject-derived class and the replicated properties they contain. The previous system for replicating components and subobjects uses the virtual function AActor::ReplicateSubobjects
. With the new system, actors now have methods that register subobjects to a list on the owning Actor or Actor Component, with the replication of these registered subobjects handled automatically by the actor channel.
We explain both systems below. First, we walk through two examples that use the ReplicateSubobjects
function. Then, we introduce the new Registered Subobjects List and walk through some code samples outlining its usage. Lastly, additional topics are discussed such as Complex Replication Conditions and maintaining Registered Subobjects Lists on clients.
Replicate Subobjects Overview
The previous system for replicating components and subobjects relies on the virtual function AActor::ReplicateSubobjects
. For actors with replicated subobjects, this function needs to be overridden, with actors having to manually call ReplicateSubobject
and ReplicateSubobjects
on each of their replicated components or subobjects. Consider the following example:
Code Sample
class AMyActor : public AActor
{
UPROPERTY(Replicated)
UMySubObjectClass* MySubObject;
}
class UMySubObjectClass : public UObject
{
UPROPERTY(Replicated)
int32 Counter = 0;
}
void AMyActor::CreateMyClass()
{
MySubObject = NewObject<UMySubObjectClass>();
MySubObject->Counter = 10;
}
void AMyActor::ReplicateSubobjects(...)
{
Super::ReplicateSubobjects(...);
Channel->ReplicateSubobject(MySubObject); // Becomes a subobject here
}
Walkthrough
In the above code sample, the actor makes the content of MySubObject
a subobject in the ReplicateSubobjects
function. At this stage, the pointer is net-referenceable. The Counter
variable is then replicated to clients whenever the actor is replicated. If we did not make MySubObject
a subobject via Channel->ReplicateSubobject(MySubObject)
, the MySubObject
variable would always be null
on clients.
Code Sample
class UMyDerivedSubObjectClass : public UMySubObjectClass
{
UProperty(Replicated)
float Timer;
}
void AMyActor::CreateMyDerivedClass()
{
MySubObject = NewObject<UMyDerivedSubObjectClass>();
MySubObject->Counter = 100;
Cast<UMyDerivedSubObjectClass>(MySubObject)->Timer = 60;
}
Walkthrough
Suppose CreateMyDerivedClass()
is called after CreateMyClass()
. The new pointer becomes a replicated subobject the next time ReplicateSubObjects
is called. On the client side, the MySubObject
variable changes and is now of type UMyDerivedSubObjectClass
and both its Timer
and Counter
variables are replicated to the client.
Registered Subobjects List Overview
Actors now have new methods that register subobjects to a list on the owning Actor or Actor Component, with the replication of these registered subobjects handled automatically by the actor channel. The Registered Subobjects List allows for a ELifetimeCondition
to be specified for subobjects when they are registered. This process offers greater control over when and where subobjects are replicated without requiring the user to implement this logic in ReplicateSubobjects
. The need for actors to implement the virtual function AActor::ReplicateSubobjects
and manually replicate individual subobjects is also eliminated.
Use the Registered Subobjects List
The following code sample outlines how to enable the Registered Subobject List.
Code Sample
AMyActor::AMyActor()
{
bReplicateUsingRegisteredSubObjectList = true;
}
void AMyActor::CleanupSubobject()
{
if (MySubobject)
{
RemoveReplicatedSubobject(MySubObject);
}
}
void AMyActor::CreateMyClass()
{
CleanupSubobject();
MySubObject= NewObject<UMySubObjectClass>();
MySubObject->Counter = 10;
AddReplicatedSubObject(MySubObject);
}
void AMyActor::CreateMyDerivedClass()
{
CleanupSubobject();
MySubObject = NewObject<UMyDerivedSubObjectClass>();
AddReplicatedSubObject(MySubObject);
}
void AMyActor::ReplicateSubobjects(...)
{
//deprecated and not called anymore
}
Walkthrough
-
Set the property
bReplicateUsingRegisteredSubObjectList = true
for your Actor class.AMyActor::AMyActor() { bReplicateUsingRegisteredSubObjectList = true; }
-
Call
AddReplicatedSubObject
inReadyForReplication
,BeginPlay
, or when creating a new subobject. There are a few things to keep in mind when using Replicated Subobjects in an Actor Component class. Within an Actor Component class,ReadyForReplication
is called betweenInitComponent
andBeginPlay
. Registering a component here allows it to call Remote Procedure Calls (RPCs) early inside of the component'sBeginPlay
. -
Call
RemoveReplicatedSubObject
whenever you modify or delete a subobject.void AMyActor::CleanupSubObject() { if (MySubObject) { RemoveReplicatedSubObject(MySubObject) } }
This last step is very important. Unless the reference is removed, the list still contains a raw pointer to the subobject changed or marked for destruction. As a result, this causes a crash after the object is garbage collected.
When converting existing code, the net.SubObjects.CompareWithLegacy
Console Variable (CVar) can be set to compare the new list with the old method at runtime. This triggers an ensure statement if any differences are detected.
Replicated Actor Components
Replicated Actor Components using this system are handled the same way as above since they are also replicated subobjects. To set a replication condition for an Actor Component, the owning actor class must implement AllowActorComponentToReplicate
and return the ELifetimeCondition
desired for the specific component. SetReplicatedComponentNetCondition
can be called to directly change a component's condition after BeginPlay
.
Make sure that AllowActorComponentToReplicate
returns the new condition; otherwise, the condition will be reset if UpdateAllReplicatedComponents
is ever called on the actor.
Use Replicated Subobjects with Actor Components
Code Sample
ELifetimeCondition AMyWeaponClass::AllowActorComponentToReplicate(const UActorComponent* ComponentToReplicate) const
{
// Do not replicate some components while the object is on the ground.
if (!bIsInInventory)
{
if (IsA<UDamageComponent>(ComponentToReplicate))
{
return COND_Never;
}
}
Super::AllowActorComponentToReplicate(ComponentToReplicate);
}
void AMyWeaponClass::OnPickup()
{
// Now replicate the component to all
SetReplicatedComponentNetCondition(UDamageComponent, COND_None);
bIsInInventory = true;
}
Walkthrough
In the above example, the owning actor class is AMyWeaponClass
. We want to set a replication condition for the UActorComponent ComponentToReplicate
based on whether or not the weapon is currently in an actor's inventory. To accomplish this, the owning actor class AMyWeaponClass
implements AllowComponentToReplicate
.
While the weapon is on the ground, it is not in an actor's inventory. Therefore, we do not want to replicate components that will cause damage. The ELifetimeCondition
returned in this case is COND_Never
which specifies that these components will never be replicated. When we want to change the damage component's condition, such as when the weapon is picked up, SetReplicatedComponentCondition
is called directly, setting the replication condition to COND_None
meaning the component is always replicated.
For more information on the list of conditions supported for ELifetimeCondition
, see Conditional Property Replication.
Actor Components Subobjects List
Actor Components can also have their own replicated subobjects list They use the same API as Actors for registering and unregistering their subobjects. Subobjects within an Actor Component can have replication conditions as well.
The owning component must be replicated to a connection before the conditions of its replicated subobjects are checked. For example, if a subobject has the COND_OwnerOnly
condition, it will never be replicated if it is registered to a component that uses the COND_SkipOwner
condition.
Complex Replication Conditions
The Replicated Subobjects system supports the creation of custom replication conditions for subobjects. This is accomplished through the NetConditionGroupManager
and COND_NetGroup
. Subobjects and Player Controllers can be part of multiple groups at the same time. In that case, a subject is replicated to a client if it is a part of at least one of the client's groups.
Implement and Use a Replication Group
- Register your subobject with the
COND_NetGroup
condition. -
Create an FName that will represent a replication condition.
FName NetGroupAlpha(TEXT("NetGroup_Alpha"))
-
Add the desired subobject to the replication group.
FNetConditionGroupManager::RegisterSubObjectInGroup(MyAlphaSubObject, NetGroupAlpha)
-
Add the clients we want to replicate the subobject to in the same group. This is done using the client's
PlayerController
.PlayerControllerAlphaOwner->IncludeInNetConditionGroup(NetGroupAlpha)
The client of PlayerControllerAlphaOwner
now receives this special subobject whenever the owning actor is replicated to that connection.
Client Subobjects List
While the server must maintain a Replicated Subobjects List, actors and components on clients should also maintain their subobject list locally. This is particularly important if a project is recording replays on clients. In this case, actors on a client are temporarily swapped to a local authority role when recording the actor into a replay. As a result, any replay recorded actors should maintain their subobject list on the client regardless of their local NetRole.
If the subobject in question is a replicated property, managing the subobject list on clients can be made easier by using a RepNotify function for the property. Clients can use the RepNotify to identify when the subobject has changed so that the old subobject pointer can be removed and the new one can be added.
While removing a subobject from the list on the server results in none of that object's replicated properties being sent to clients, the subobject's pointer is still net-referenceable until the UObject itself is marked as garbage. Once the server detects that the UObject is invalid, it will notify the clients to delete the subobject locally on the next reflection update.
The Replicated Subobjects List system does not support UActorChannel::KeyNeedsToReplicate()
. We recommend using push-model replication for the subobect's replicated properties instead. Using push-model replication with the new system should be at least as efficient as using RepKeys.