Replicated Subobjects

Learn how to replicate any UObject-derived class and the replicated properties they contain.

Choose your operating system:

Windows

macOS

Linux

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
  1. Set the property bReplicateUsingRegisteredSubObjectList = true for your Actor class.

    AMyActor::AMyActor()
    {
        bReplicateUsingRegisteredSubObjectList = true;
    }
  2. Call AddReplicatedSubObject in ReadyForReplication, 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 between InitComponent and BeginPlay. Registering a component here allows it to call Remote Procedure Calls (RPCs) early inside of the component's BeginPlay.

  3. 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

  1. Register your subobject with the COND_NetGroup condition.

  2. Create an FName that will represent a replication condition.

    FName NetGroupAlpha(TEXT("NetGroup_Alpha"))
  3. Add the desired subobject to the replication group.

    FNetConditionGroupManager::RegisterSubObjectInGroup(MyAlphaSubObject, NetGroupAlpha)
  4. 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.