non-Actor Subobject replication

I’m trying to replicate a hierarchy of subobjects within a single actor and running into issues where the replicated subobjects outer is always the base AActor of the hierarchy since the replicated client subobject is constructed
inside UActorChannel::ReadContentBlockHeader line 2156 : SubObj = ConstructObject< UObject >( SubObjClass, Actor );
I don’t want my subobjects outer to be the actor but its direct component outer as it is on the server. If this is expected behavior than I may have to pass a reference to the component into the subchild and replicate that, but it seems quite odd that the outer pointer would ever be out of sync.
My type hierarchy:

Parent Actor MyActor

  • MyComponent A with the following member
    UPROPERTY(EditAnywhere, editinline, Replicated, Category = “Test”)
    TArray<class UTestSubChild*> ReplicatedSubChildren;

UCLASS(BlueprintType, editinlinenew, ClassGroup = Test)
class UTestSubChild : public UObject

The instances of UTestSubChild are created at editor time and are serialized. The instance of MyActor is placed in the level and properly created on the server and then replicated, but the client versions of UTestSubChild are different as mentioned above.

For replicating the UTestSubChild and supporting RPC calls I followed my steps in the second post here:
which I’ll repeat for clarity:

  • Implement IsSupportedForNetworking in UTestSubChild
  • Implement GetFunctionCallspace in UTestSubChild
  • Implement CallRemoteFunction in UTestSubChild so RPCs are supported with the proper NetDriver for UTestSubChild
  • Implement ReplicateSubobjects in Component A and loop over my array of UTestSubChild calling ReplicateSubObject on the passed in Actor Channel with each instance of UTestSubChild
  • Implement GetLifetimeReplicatedProps in Component A and rep the respective array UTestSubChild

I really wish there was a solid overview of uobject replication outside the normal one-level actor and component relationship. I have outlined my issues and problems in a few different answerhub posts and the above forum post, but have yet to receive any responses.
The only available references on this topic are:

It doesn’t look like I’ll be able to use editinlinenew as was used in UE3 for creating instances of types at editor time and having them replicated on the server. I really would just like this confirmed above all else.
I have reviewed the experimental AbilitySystem code and it appears their method of defining archetypes of a specific class or blueprint types and then instantiating that class/type at runtime may be a solution that could work with my desired type hierarchy, but I have my doubts.

You are correct - we don’t support an arbitrary hierarchy of subobjects for replication: only a ‘flat’ setup of 1 actor with many subobjects.

Let me think on this… it may not be that hard to support. Like you point out, we kind of just hardcode that the subobject’s outer is always the actor. We could have a slightly different system that allows nested data replicators in other data replicators. Doesn’t seem that far fetched, but I’ll need to look at some code tomorrow.

Alright sounds good, looking forward to the followup.

I think this nested subobjects is technically feasible. The simplest approach would be to serialize the subobject’s outer along with object class. Something like this in ::BeginContentBlock

Bunch << Obj;

UClass *ObjClass = Obj->GetClass();
Bunch << ObjClass;

UObject ObjOuter = Obj->GetOuter();
Bunch << ObjOuter;

(Of course, you’d have to update ::ReadContentBlock to pull this out and pass it into the ConstructObject call). It would also probably be better to serialize a single bit to indicate if the outer was the actor (similiar to how ::BeginContentBlock serializes a bit if the content block is about the owning actor itself). This optimizes the common case, with only 1 bit overhead to ‘normal’ subobjects and nested subobjects would have 33 bits of overhead. A more optimized system could probably be devised, but this would be the easiest to implement.

Some assertions/security checks would have to be updated too. Instead of checking that the object’s outer is the actor, it would want to follow the object’s outer chain and assert the actor is somewhere in there (not necessarily the first one).

From there you’d need to make sure that all your nested subpbjects get Channel->ReplicateSubobject called on them. This could be done by moving ReplicateSubobjects from Actor/ActorComponent to UObject. Or you could just leave it up to your Actor class to traverse the entire subobject tree and manually call it.

I think that would get you pretty close. The only other thing that might trip you up is I am not sure how the editinline/editinlineNew will play with our packagemap. What we would hope to happen is that the subobjects all have stable names and can be referenced by their full path. Check if ::IsNameStableForNetworking returns true for your UTestObjects that were created inline. IF they are, I think you’ll be good. If not, then we’ll need to figure out a different way to test this.

Let me clarify too - that is my take on things. I may try taking a stab at it down the road but don’t have any immediate plans to implement this feature. Officially, subobjects can only replicate in the flat structure described above.

Thanks for the reply.

The serializing of the outer def seems doable. I already have my actor/components/etc calling their own replicatesubobject internals.

I haven’t looked into the packagemap and stable name related code yet, so that indeed could cause some issues.