Deferred RPCs sometimes not working

net.DelayUnmappedRPC=1 enabled. I create a new component like so and then RPC it out to relevant clients.

[Image Removed]

Bear in mind, the Multicast is not a true unreal multicast, its our own function that iterates relevant player controllers and calls a reliable client rpc on each of them.

Most of the time when the client receives the rpc, the pointer to the ItemContextObject is resolved, but occasionally it is null. Is there something I should know here? I assumed that deferring rpcs would mean that it would wait until the object is resolved on the client before sending and generally that is the behaviour we get, but not always.

Within the client RPC:

[Image Removed]

Are there any gotchas about how deferral works? Is it limited to an actor channel? If I RPC through a different actor (such as the player controller) can that result in this unexpected behaviour?

Thanks,

Brenden

Hi,

net.DelayUnmappedRPCs is intended to be used in conjunction with net.AllowAsyncLoading. With both of these enabled, if an object is received and has its NetGUID added to the package map while it asynchronously loads, then any RPCs referencing that object can be delayed during the loading.

However, it is not possible to delay a RPC that references an object that has not yet been received on the client, as the object’s NetGUID has not been added to the package map. If the client receives the RPC’s bunch before the component’s first bunch, which is likely what’s happening here, the RPC will still be executed with a null reference.

In a situation like this, there are a few workarounds you can try. One option is to replicate this reference as a property with an OnRep, as if the updated property value is received before the object exists on the client, the OnRep will be called again once the object has been spawned and the reference has been mapped.

Another common workaround is to have the client send a RPC to notify the server that it has received and spawned the object, so the server can call the client RPC knowing that the object reference will be mapped on the client. Finally, you could also have the client send a RPC telling the server to retry calling the client RPC if it was executed with a null reference (see APlayerController::ClientRestart and APlayerController::ServerCheckClientPossessionReliable for an example).

Thanks,

Alex

Ok I guess I misunderstood the purpose of that flag. It seemed like the server would wait until it knew the client would be able to resolve that object before it would send the rpc.

Is it possible instead to send a persistent handle so that the client can just try to resolve it later? I looked at netguids and I wondered if sending the netguid itself might work since there appears to be a resolution path:

FNetworkGUID NetGUID = NetDriver->GuidCache->GetOrAssignNetGUID(Object);

Is this value the same on server and client? Can I send that netguid to the client and then invoke

UObject* Obj = NetDriver->GuidCache->GetObjectFromNetGUID(NetGUID, /*bIgnoreMustBeMapped=*/true); ?

Hi,

NetGUIDs are how objects are referenced across the network, so when sending an object reference as a RPC parameter, the replication system is essentially already just sending a NetGUID. When the client receives this, the NetGUID is added to the package map, and UPackageMapClient::SerializeObject/InternalLoadObject use it to try and resolve the object reference. You can enable the LogNetPackageMap category to get more info in your logs on this process.

In this case, I believe you could still run into the issue of the RPC’s bunch being received before the bunch containing the new component, so the RPC would be processed before the client has spawned the component. If this happens, GetObjectFromNetGUID would still return nullptr, so you’d still need some custom handling to delay RPC execution on the client until the NetGUID is mapped or to ensure the server only sends the RPC after the client has notified it of having spawned the component.

If you have any further questions, please don’t hesitate to reach out, but please note that support will be limited for the next two weeks due to the company break.

Thanks,

Alex

Right so in theory it would work, but the client after receiving the netguid would have to do something such as periodically poll for the object until it gets a valid ptr.

Is there an event the client could register to for when objects get registered? OnNetObjectRegistered(FGuid ObjectGuid, UObject* TheObject) sort of thing?

Thanks!

Hi,

That’s more or less how the NetDriver handles replicated object reference properties, as it periodically checks to see if these are mapped (UNetDriver::UpdateUnmappedObjects).

I don’t believe there’s any built-in event you could register to, so that would have to be custom implemented. In the case of newly mapped object reference properties, the only callback is the property’s RepNotify if it is using one (see FRepLayout::UpdateUnmappedObjects_r and FGuidReferences::UpdateUnmappedGUIDs).

Thanks,

Alex