Using the Replicated SubObjects List

Using the Replicated SubObjects List

This article was written by Alex K

Currently, replicating components and subobjects relies on the virtual function AActor::ReplicateSubobjects. For actors with replicated subobjects, this function must be overridden, with actors needing to manually call ReplicateSubobject and ReplicateSubobjects on each of their replicated components/subobjects. Recently added to the engine is a new system for handling replicated components and subobjects, which eliminates the need for actors to implement this virtual function and manually replicate individual subobjects.

Actors now have new methods that register subobjects to a list on the owning Actor or ActorComponent, with the replication of these registered subobjects being handled automatically by the actor channel. This new system allows for a ELifetimeCondition to be specified for subobjects when they are registered, offering greater control over when and where subobjects are replicated without having to custom implement this logic in ReplicateSubobjects.

Using this new system is very straightforward:

  1. Set bReplicateUsingRegisteredSubObjectList = true for the class.
  2. Call AddReplicatedSubObject in ReadyForReplication, BeginPlay, or when creating a new subobject. (ReadyForReplication is called between InitComponent and BeginPlay, and registering a component here allows it to call RPCs early inside of the component’s BeginPlay.)
  3. Call RemoveReplicatedSubObject when modifying or deleting a subobject. (This step is very important, as unless the reference is removed, the list will still reference the pointer of a subobject that has been changed or marked for destruction, causing a crash when the object is garbage collected.)

For example:

{
    bReplicateUsingRegisteredSubObjectList = true;
}

void AMyActor::CreateMyClass()
{
    MySubObject= NewObject<UMySubObjectClass >();
    MySubObject->Counter = 10;
    AddReplicatedSubObject(MySubObject);
}

void AMyActor::CreateMyDerivedClass()
{
    If  (MySubObject)
    {
        RemoveReplicatedSubObject(MySubObject);
    }
 
    MySubObject = NewObject<UMyDerivedSubObjectClass>();
    AddReplicatedSubObject(MySubObject);
}

void AMyActor::ReplicateSubobjects(...)
{
    //deprecated and not called anymore
}

When converting existing code, the “net.SubObjects.CompareWithLegacy” CVar can be set to compare the new list with the old method at runtime, triggering an ensure if any differences are detected.

Replicated ActorComponents using this system should be handled the same way, as these are also just replicated subobjects. To set a replication condition for an ActorComponent, the owning actor class should implement AllowActorComponentToReplicate and have it return the ELifetimeCondition desired for the specific component. To change a component’s condition after BeginPlay, SetReplicatedComponentNetCondition can be called to do so directly. (You should also make sure that AllowActorComponentToReplicate will return the new condition, as it could otherwise be reset if UpdateAllReplicatedComponents is ever called on the actor.)

For example:

ELifetimeCondition AMyWeaponClass::AllowActorComponentToReplicate(const UActorComponent* ComponentToReplicate) const
{
    // Don’t replicate some components while the object is on the ground.
    if (!bIsInInventory)
    { 
        if (IsA<UDamageComponent>(ComponentToReplicate))
        {
            return COND_Never;
        }
    }
 
    return COND_None;
}
 
void AMyWeaponClass::OnPickup()
{
    // Now replicate the component to all
    SetReplicatedComponentNetCondition(UDamageComponent, COND_None);
    bIsInInventory = true;
}

ActorComponents can also have their own replicated subobjects list, and they use the same API as Actors for registering/unregistering these objects. These subobjects within an ActorComponent can have replication conditions as well, but it’s worth noting that the owning component must be replicated to a connection before the conditions of its replicated subobjects are checked. For instance, if a subobject has the “Owner Only” condition, it will never be replicated if it is registered to a component that uses the “Skip Owner’’ condition.

This new system also supports the creation of custom replication conditions for subobjects, via the NetConditionGroupManager and COND_NetGroup. To implement and use a replication group:

  1. Register a subobject with COND_NetGroup.
  2. Create a FName to represent the condition. For example: FName NetGroupCheatMaster(TEXT(“NetGroup_CheatMaster”))
  3. Add the subobject to the group. For example: FNetConditionGroupManager::RegisterSubObjectInGroup(MyCheatSubObject, NetGroupCheatMaster)
  4. Via the clients’ PlayerController, add any clients that subobjects in the group should be replicated to. For example: PlayerControllerCheatOwner->IncludeInNetConditionGroup(NetGroupCheatMaster)

Subobjects and PlayerControllers can be part of multiple groups at once, with a subobject being replicated to a client if it is a part of at least one of that client’s groups.

While the server will certainly need to maintain these lists, actors/components on clients should also maintain their subobject list locally, particularly for any actors with local authority. This is especially important if a project is recording replays on clients, as actors on a client will temporarily be swapped to a local authority role when recording the actor into a replay. Because of this, any replay recorded actors should maintain their subobject list on the client, regardless of their local NetRole.

If the subobject is a replicated property, one way to make managing the subobject list on clients easier is to use a repnotify function for the property. Clients can use this OnRep to know when the subobject has changed, removing the old reference from the subobject list and adding the new one.

It’s also worth noting that while removing a subobject from the list on the server does result in none of that object’s replicated properties being sent to clients, the subobject’s pointer will still be net-referenceable until the object itself is marked as garbage. Once the server detects the UObject is invalid, it will notify the clients to delete the subobject on the next net update.

  • Finally, this new system does not support RepKeys for subobjects. Instead, it is recommended to use push-model replication for the subobject’s replicated properties, as using push-model with this new system should be just as, if not more, efficient than using RepKeys.

Get more answers on the Knowledge Base!

Hello,I follow the document to use the new system,but it’s not work for me,if directly add Replicate Subobject,the engine crashed because of assert: Is Support For Networking.I also try to override UObject derived class’ s function(Is Support For Networking,return true)
Engine stop crash,but UObject not replicate to client.
really thanks

Unreal Engine 5.1 ReplicateSubobjectList Not Working - Programming & Scripting / C++ - Epic Developer Community Forums