How to create deterministic, replicated subobjects at runtime?

The title may be a bit misleading so allow me to explain the setup:

We have a “configurable actor” system whereby a fairly barebones “container” actor is spawned, and then components and subobjects are added to that actor at runtime, based on a replicated ‘configuration’ set by the Server (which is just a replicated UPROPERTY on the actor itself). The system makes garauntees that all those subobjects are spawned on each connection with deterministic names, and are net-addressable/stable. For simplicities sake, the configuration can only be set once (at spawn time), and never changes thereafter (COND_InitialOnly). So far this all works just fine.

However, we now want one of those spawned components to be replicated, and have replicated properties. The issue we are running into is that when the client receives the actor, it first has to async-load the contents of the configuration data before it can apply it. There is a window where the actor has been received and is spawned, but hasn’t yet created those components. All the code surrounding these actors is sympathetic to this.

So far this hasn’t been a problem for things like replicated object references to those components etc. which seem to auto-resolve once we create the objects client-side. However, it is a problem for replicated properties of that object, which trigger errors such as “ReadContentBlockPayload() failing to find/create an object” or “Failed to map GUID” etc. (Iris & Legacy).

What I was hoping for, is a way to mark the object/bunch as if to say “if you can’t find this object right away, wait until the client creates it locally”, and store the received data in before applying it when the object is created client-side. Obviously this would be a special case but I assume there is already partial support for this when it comes to async-loading actors for instance, as the actor may not yet be spawned client-side.

My hope is that marking the objects as stably-named and net addressable would do this naturally. I’ve also tried using SetIsNetStartupComponent() for example, but it doesn’t appear to be referenced anywhere in the engine.

It’s a similar problem I’ve seen before with procedural level generation, where clients generate the level deterministically, and actor references/replicated properties are applied to the deterministically-spawned actors. Is there any reason that same logic can’t be applied to subobjects?

Note that this is only an issue if the client doesn’t spawn the subobject on the same frame. If the configuration is loaded synchronously, all the objects are spawned before the actors’ bunches are read-in, and everything works/resolves just fine. It’s only a problem when there is latency between the replicated object data being received, and the client spawning it.

Steps to Reproduce
N/A

I also found this related post, which is similar to what I’m experiencing. In summary, the object is stably named but doesn’t initially exist on the client. I want a way to tell the engine that it *will* exist, and apply the replicated data etc. when it does.

[Content removed]

Hi,

Because these are marked as stably-named and net addressable, the server expects that these objects will already exist on the client, as these kinds of objects are usually loaded with the level or created as a default subobject. What you’re describing, where the client waits to process an object’s bunches until the object is available, is how async loading is handed for dynamically created replicated objects.

In this case, the most straightforward solution may be to treat these replicated subobjects as dynamically spawned rather than net addressable. The server would create these objects locally and replicate them to the client. When the client receives bunches for the object, it will queue them while the object is loaded/spawned, processing the queued bunches when the object is available.

Thanks,

Alex

Hi Alex,

I notice that actors at least have some mechanism for holding the received data until the target actor is loaded-in, but that doesn’t seem to be possible for subobjects (at least in non-iris replication). I was hoping to leverage that waiting/async mechanism, but it doesn’t look to be possible at least not with considerable changes.

I’ve found a workaround however, at least for our case. Since the client knows ahead of time the names of the objects that will be created by the Server, it can simply wait for the subobjects matching those names to be received. Once received, it can apply the non-replicated configuration locally, then broadcast the completion delegates. With some book-keeping that should be viable.

Thanks anyway!

Hi,

I’m glad you were able to find a workaround! If you have any further questions, please don’t hesitate to reach out.

Thanks,

Alex