2 minute read

This article is a collaboration with WizardCell and on it, you’ll learn how DORM_Initial works and why you should be careful with it.

Introduction

If we read the description of the DORM_Initial dormancy setting, we see what follows:

This actor is initially dormant for all connection if it was placed in map.

Which made me understand that this setting would work like DORM_DormantAll just with a couple extra optimizations. But it isn’t the case, let’s see what are the implications of using this setting.

DORM_DormantAll versus DORM_Initial

When an Actor becomes relevant to a connection, all the properties of said Actor are sent to the connection, no matter whether the Actor is DORM_DormantAll or DORM_Awake. That means that the state the client receives from the Actor will be consistent of what the server sees about it.

However, that is not the case of DORM_Initial Actors, as they will not send any C++ replicated property if a connection becomes relevant:

Dormancy Initial issue

Watch out! Blueprint replicated properties ignore Actor dormancy settings.

How to use DORM_Initial

So shall I simply not use DORM_Initial? No, you should! DORM_Initial Actors won’t be considered for replication at all even under relevancy, and sometimes that can be a huge optimization.

To ensure incoming connections get the new values over DORM_Initial Actors, you should call FlushNetDormancy on them before updating their properties. This will make the Actor to become DORM_DormantAll but only when strictly needed.

Flush Dormancy node

What about calling ForceNetUpdate to also flush the dormancy?

Be very careful with this, as ForceNetUpdate also calls FlushNetDormancy but its guarded with a NetDriver check:

void AActor::ForceNetUpdate()
{
	...

	if (GetLocalRole() == ROLE_Authority)
	{
		// ForceNetUpdate on the game net driver only if we are the authority...
		if (NetDriver && NetDriver->GetNetMode() < ENetMode::NM_Client) // ... and not a client
		{
			NetDriver->ForceNetUpdate(this);

			if (NetDormancy > DORM_Awake)
			{
				FlushNetDormancy();
			}
		}
	}

	...
}

So, if the NetDriver is Null at the moment you call ForceNetUpdate*, the Actor will never call FlushNetDormancy, causing it to stay in a DORM_Initial state. For that reason it is always recommended to explicitly call FlushNetDormancy when dealing with DORM_Initial Actors.

*Worlds don’t get a NetDriver until UWorld::Listen is called.

Conclusion

Thanks for reading!

Let’s hope everything is clear! Feel free to reach us out if there are questions! And I encourage every reader to check WizardCell’s articles.

If you want to know more about Dormancy, visit Epic’s official documentation!

Enjoy, WizardCell and vori.