Replication

In this guide we will take a look at replication; we will explain what replication is, how and when to use it and by examples, we will go through the most common replication issues and also touch on debugging your blueprints.

Parts of this guide will re-iterate and quote the Unreal documentation about replication but instead of simply linking to the replication documentation for replication, we will go over replication with practical examples and try to eliminate as much tech-lingo as possible.

Server/client structure

Before we start, we do need to cover the basic concept of what a SERVER, DEDICATED SERVER, LISTEN SERVER, and CLIENT are. These are fundamentals that, if you don’t have a firm grasp of this will make it much harder to perform replication work. You might or might not run into any issues, but if you aren’t aware of these concepts, odds are that you will be shooting in the dark.

The full Unreal documentation can be found here for reference: https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/Networking/Overview/

No matter how you play the game, you are running on a server.

In the image to the right, we show you the two typical methods of checking if something is running on a server; the isServer and isDedicatedServer branch checks.

Let’s go over how these work and how to use them properly.

This is a problem when testing though, because unless you’re willing to make a build each time you want to test something and throw it on a server, how can you debug how things work in the editor?

Server checks in blueprints

We will here go over server checks in blueprints by means of a number of examples. First, let’s deal with DEDICATED SERVERS.

Dedicated Server using “is Server” check

For this example, we will be playing in the editor with “Dedicated Server” turned on, and we will use “Number of players: 2”

Dedicated Server using “is Dedicated Server” check

This should return the exact same output, and sure enough, it does:

The reason, of course, is that “Dedicated Server” is a server. Next, let’s switch to Listen servers.

Listen server using “is Server” check

Hold on. We’re using two clients. Where is the other client? The answer is that the first client here is the server, because in LISTEN SERVER mode, the first client is also the host of the game.

Listen server using “is Dedicated Server” check

Those look the same!?

No - look closer. In the first example, where we ran a listen server using the “is Server” check, the text placed after BeginPlay but after IsServer is returning TRUE, but in this case, it’s returning FALSE. That means that in a Listen server environment, using IsDedicatedServer will return incorrect information.

Aaargh! How do I solve this?!

The answer

In order to make things work BOTH in a dedicated server AND a listen server environment, you need to make sure your logic covers both of these cases .

At this point, it is a good idea to point out that you almost never need to account for those three cases at once though, and usually you just need to have one of the two checks;

However - in order to demonstrate the flow on clients and servers, below we have an example of logic flow to cover all the cases.

Now, looking at the outputs, playing in DEDICATED mode looks like this (removing the text that was run right after BeginPlay):

And the output for playing in LISTEN mode looks like this:

What this means, in effect, is that we can simplify it by saying this:

So I can use that, right? Problem solved!

Well, not quite - the above example is just to demonstrate what runs where. The trouble comes from the sequence node, which is it’s own little beast to deal with. Sequences will run through the first sequence, then the next, and so your execution sequence will not quite be in step. This is why a lot of blueprints do isServer/isDedicatedServer checks when it matters. In addition to this, the presence of DELAYs in the blueprint causes further havoc. Instead - use the above example as a reference when you need to check what is being run where, not as a global solution for how to implement large pieces of blueprint scripts.

So how can I split the execution path?

You can’t. You can only hope to control what is being run where by isServer/isDedicatedServer checks when it matters, and use replication of properties and RPCs to ensure clients get what they need. Otherwise, this guide would stop right here. Sorry. You need to learn replication.

What is replication?

The Unreal Engine documentation ( https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Actors/ ) describes replication as:

The server will maintain a list of actors, and will update the client periodically so that the client will maintain a close approximation of each actor (that is marked to be replicated).

Actors are updated in two main ways:

The main difference between property replication and RPCs is that properties are replicated automatically, any time they change, while RPCs are only replicated when executed. RPCs (Remote Procedure Calls) are functions that are called locally, but executed remotely on another m achine (separate from the calling machine).

Another difference is that replicated variables do not have any guarantee of being replicated in the same order as they are being set, while RPC calls do (if you call two RPC functions on the client, you know they will be executed in the same order on the server, while OnRep_Notify functions might not).

The “Property updates” mentioned above can further be separated into various categories, such as actor replication, component replication and variable replication. We will go through these properties in detail below, but the most common type of replication is actor replication, RP Cs and variable replications.

Another good source of information can be found here: https://cedric-neukirchen.net/Downloads/Compendium/UE4_Network_Compendium_by_Cedric_eXi_Neukirchen.pdf

Why is replication a thing?

If you’ve run into a situation where your work functions in the editor and in single-player but as soon as you run it on a server and it breaks down - you’re running into a replication problem that needs to be solved.

Replication in a single-player setting isn’t needed simply because as a single player you will act both as the server and the client at the same time. However, as soon as you invite another player, or start playing on a dedicated server, replication comes into effect. Suffice to say that at the end of the day: If you’re creating blueprints, it’s likely you will need knowledge of replication sooner or later.

So - why?

The reason is that while it would be technically possible to have everything happen on the server as well as the client, the truth is that this would cause way too much data to be trafficked back and forth. Particles, GUI, sounds and other effects (for example) are simply not required to have on the server - it would just slow it down. At the same time - certain collision calculations and state maintenances need to happen on the server instead of the client - otherwise both the server and the client would be way overworked having to deal with literally everything that is happening in the game-world.

The reason tethering is a concept in Conan Exiles (and other games like it) is because otherwise, the first client hosting the game would need to hold the server information for the entire world at the same time as the client data.

How does replication work?

A short word on Relevancy

Before we head into how replication works, it’s a good idea to know about another concept called “ Relevancy ”. A level can be huge, and at any time a player can only see a small fraction of the Actors in that level. Most of the other Actors in the level are not visible, are not audible, and have no significant effect on the player. The set of Actors that a server deems are visible to (or are capable of affecting a client) are relevant for that client.

The reason this touches on replication is that unless an actor is relevant to a client, it can’t replicate to the client. For example - let’s say that you have an actor that causes a volcano to go off in a distance and the idea is that you should be able to see that from across the entire game-world.

The actor is likely placed on top of the volcano and may even be present in a level with a level-bounds set to encompasses the entire world - but that actor is still not relevant to a client that is outside of relevancy range of it.

In addition to this - if your actor has a hidden root-component and it is also set to not have collision, that also makes the actor not relevant . While this situation isn’t a very common one, it is good to know about this. The full Unreal documentation about relevancy can be found here: https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/Networking/Actors/Relevancy/

In short: Actors need to be relevant to clients in order to replicate.

Authority

Replication is the name for the process of synchronizing data and procedure calls between clients and servers, and so the first important thing to determine, is who has authority over a particular actor. Typically, this would and should be the server (exceptions do exist - such as Actors spawned locally on the client side like the HUD). - certainly for actors that are placed in the world.

The " Has Authority " node is used to check if the script is being executed on a machine that has Network Authority (in most use cases, this would mean Server) or if its being executed on a remote machine (in most use cases, this would be the Client)

If you're uncertain about the authority of the actor, you can implement a branching node on BeginPlay and hook it up to a branch with a break-point so you can see this in the editor. We typically do not use HasAuthority but rather do isServer/isDedicatedServer checks for the sake of clarity.

Server-to-client

The server knows a property of an actor and needs to push an update to the client. An example of this would be an actor spawning on the server that also needs to be visible on the client, or an actor that shifts location from one place to another, or an actor setting a variable that the client needs to know about. These different scenarios have different implementation setups but the core idea is the same: “The server knows something is going on and needs to inform clients about it”.

Client-to-client? / Client-to-Server?

There isn’t a way for clients to send data to other clients without the server being involved. What most people think about when they think about client-to-client replication is in fact either Multicast RPCs executed by the server, or On-Rep-Notify variables set on players that enter the range of other players.

An example of this would be a player wielding a sword that has a particle attached to it. The particle is also shown to other players around them, but this is (likely) an On-Rep-Notify event that has set a variable that then becomes relevant to other clients as they come close enough.

Another example would be a player controller executing a particular action, such as an attack or an interaction. However - these signals do go through the server as well, executing functions along the way before being pushed to other clients.

Note : Replicated variables have to be set on the server to have any replication effect. If you set a replicated variable on the client, it will only have an effect locally on the client, and OnRepNotifies won’t be triggered on the server or the other clients.

How to set up replication

Please note that this section only serves to show how to set up replication. Further down, we will cover how to actually use replication properly.

Actor replication

In order to correctly use RPCs and replication of variables, the actor itself needs to be set to replicate.

Important settings

Advanced settings

For the most part, you will not need to change the settings mentioned here, but if you do wish to inform yourself about them, the full Unreal documentation on Actor replication can be found here: https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Actors/ReplicatingActors/

Event Begin Play

Worth noting here is that the Event Begin play starts as soon as an actor becomes relevant to a client. If you attach something like a particle effect on an Event Begin play node on an actor that lasts for a significant amount of time, the Begin Play will trigger (client-side) on anyone coming into network relevancy of the actor, even if the actor has been there for a while - this is because while it has already started on the server once, it hasn’t become relevant (and so therefore not created) on clients coming into relevancy distance of the actor and so the particle effect will trigger once again for other clients coming close enough to it.

Component replication

Most gameplay logic that is done in the Actor class and components usually just represent smaller pieces that make up the Actor. That gameplay logic in the Actor is what replicates, and its results sometimes end up in calls/changes to the components. However, cases exist where properties or events on components themselves need to directly replicate.

Components replicate as part of their owning Actor. The Actor still dictates role, priority, relevancy, culling, etc. Once the Actor is replicating, it may also replicate its components. These components may replicate properties and RPCs in the same way an Actor can.

For blueprints, the most likely scenario is that you will be dealing with static components (components that you have put into the blueprint yourself, such as static meshes, particle effects, and so forth). These components will exist on both server and client no matter what (they are created as part of the blueprint) and for the most part you will not need to interact with this as a setting. If you do, we recommend looking at the full Unreal documentation for component replication that can be found here: https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Actors/Components/

Variable replication

Replicating variables are one of the most used methods of replicating - it may be an object that moves from one place to another, or an object that changes its property when interacted with, or shows a message to the player when in range. These messages and properties on the actor are in most cases variables, and depending on what the clients need, those variables need to replicate (or not). We will cover how to use these different variable types below; the image above is only meant to show you where and how to set up variables for replication.

Variables can be set up as:

Although we will go through practical examples further down, you may find the full Unreal documentation for variable replication useful, and it can be found here: https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Actors/ReplicatingVariables/

Remote Procedure Calls (RPCs)

There are a few requirements that need to be met for RPCs to be completely functional:

If the RPC is being called from server to be executed on a client, only the client who actually owns that Actor will execute the function.

If the RPC is being called from client to be executed on the server, the client must own the Actor that the RPC is being called on (This usually means being called from the player character or player controller).

Multicast RPCs are an exception:

That is a lot of technomological mumbo-jumbo black magic! The long and short of it is this:

RPCs are functions in blueprints. If you want something to happen only on the Client or Server, you can use those replication-settings (we will show a number of examples further down the document for a more in-depth “how to use RPCs” look), and if you need something to be quick-fired to all relevant actors (usually players that can see the actor), use Multicast events.

A note should also be made about the “Reliable” setting. Switching this off makes it so that the execution after the event happens in a single frame. “ Reliable” should only be used for events that require precision . Particles, sounds and other effects should not use “reliable” for this reason.

For Conan Exiles, we’ve also implemented a replication condition called “Global Multicast” that affects relevancy checks as follows:

The full Unreal documentation on RPCs can be found here:

https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/Actors/RPCs/

This is where you really wanted to start

We put that title there because it’s likely the case that you know some of the basics about replication, such as being able to set up nodes and variables. But if you’re running into troubles, this is the best place to start. If you’re a novice to replication, it pays to read the sections above this one as well to get to grips with what replication actually is and what it does.

Determining execution path (authority)

The first thing to do when working with blueprints, especially if they contain multiple events, have parent-classes and/or contain complex scripting, is to determine the execution path. Is the blueprint running things on the server, client or both?

Typically, the best method of dealing with this is to make sure that you are running things on the server, and then replicate required variables and functions to clients when they are needed, instead of running separate execution paths all the time.

As mentioned at the beginning of this document in the section about Server/Client structure, it’s not possible to simply split the execution path and be done with it.

This is why you will see IsServer and IsDedicatedServer branching nodes in blueprints. If you aren’t familiar with these nodes or get them confused, we covered these at the very top of this document, and you should read that part first before moving on - knowing the difference is crucial and the prime source of confusion for many people.

We should also cover the fact that once we force an execution path, the script logic will follow that path along, no matter the amount of subsequent events you create UNLESS these events are also called from other sources that execute the event differently. An example of this might be a client interacting with an object that triggers an event from both the server and the client by calling an event that was intended to only be called through the server.

An example of execution-path can be found to the right - in this example, we are calling two functions:

Because the Event BeginPlay has a Branch-node after it telling the script to only run on servers, the execution path for any subsequent events will also only run on the server.

However - it is possible for another actor to perform a Cast to this actor and force run “BossSpawn” from both the server and the client, in which case we have a (potential) replication problem on our hands.

There is an additional potential replication issue with this specific implementation ( why is the” Boss FX” event running on the server? ) but we will talk about this in-depth later.

Determining what the client needs to know about

Clients do not need to be aware of most things unless they impact players in a visible or audible fashion. The easiest way to think about this is:

“For this function to work, does the client need to know about it right now?”

Proper RPC-calling

Calling particle effects, sounds and the like through an RPC should be done through a Multicast node, as in the example below

This is a good solution for short (few seconds worth) events. Every player that joins the server or enters the relevancy range after RPC is triggered will miss the action, so you will have to use a different method to set up a long effect (such as On-Rep-Notify).

In this example, we have a boss that spawns and we want to accompany that spawn with some effects that play on clients that are nearby. To that effect, we employ an RPC to call those effects to spawn on the client.

Any logic implemented after the “MulticastFx” node will happen on relevant clients, meaning this is where we have an execution path split. In order to easily make out what is on a server and on a client, we name the Multicast node as such, and separate it out through a Sequence node.

Having the sequence node isn’t a requirement but it can make your blueprints easier to read - especially if you are dealing with blueprints that constantly jump the server-client border.

A word of caution : Since we now have two split execution streams (the server one and the multicast one), be careful about what actually needs to be timed to what. You might be tempted to put in timers in the client execution path but it’s much better to instead keep calling new Multicast events.

An example here could be a sorcerer calling down a lightning strike. The sorcerer spawns, starts a particle effect that lasts for 5 seconds, and then there is a lightning strike on the sorcerer that replaces him with a boss monster. If you have the client execution line on a delay/timer/other delaying method, it might actually go out of synch with the server, and the second particle effect may not occur at the time you want it to. Instead, use multiple Multicast RPCs called from the server. It will also make debugging easier, and the blueprint easier to read.

Debugging

Testing a blueprint in the editor is the best way of finding out if it works or not, and by selecting “Run Dedicated Server” at the bottom of the Play/ options, you can debug how things look and behave in a dedicated server environment.

This is not a foolproof way of testing, however, and you should always try your blueprint with both Run Dedicated Server both on and off to make sure it works in both situations.

Debugging using Breakpoints is probably the most used method of debugging - but it might not always be the best. If you have multiple instances of your blueprint running in your world, sorting out which one is being debugged can be annoying, and it might not be immediately obvious if what you see happening is on the server or the client.

For these situations, you can use PRINT or DWLOG as a debug-tool. By filtering the output log for your messages, you can see if it was a server or a client (or both) that triggered the message.

PRINT will work in the editor only, it will not work in a built game-version.

DWLOG will work in both scenarios but requires a tiny bit more setup, because you will need to select a debug level that is suitable.

Appendix - Timelines

Timelines are good examples of replication, since a lot of replication issues come from jittery movement of objects (typically handled by timelines or location offsets). There is no magic involved in timelines except for that timelines continuously update. In order to reliably replicate the movement of (for example) a static mesh, it’s important to send as little information from the server to the client as possible. In the example below, we are using a timeline to move a static mesh up and down simulating an elevator. The elevator will switch direction but only if it’s reached it’s start or end position, it will replicate properly to nearby players as well as player entering the area after, and in the middle of, movement.

Before we go through this blueprint in detail:

It’s also important to know the assumptions before going into a blueprint such as this, and so the blueprint was approached with this in mind:

Blueprint example

Ok, on to the dissection of the blueprint…

OnComponentBeginOverlap triggers when players walk into a box set on top of the elevator. The Branch immediately afterwards tells the blueprint that we want to consider the rest of the logic only on the server (IsServer) and if the “Elevator Timer Handle” is not already running (if it was running, we don’t want to trigger movement again).

We then Cast to ConanCharacter and get the controller so we can check if it is a valid entity that has stepped on the platform. This will prevent actors such as other blueprints or dead/logged out characters from triggering the logic.

Next, we Set Timer by Event and store the timer variable in “Elevator Timer Handle”. We set the time to check to 1 second because we won’t need a more frequent update in this case . The main reason is to send updates to players that joined during the movement, so they see the same progress as players that have seen it at the beginning.. Finally, we link the timer to the “Update Clients” function that Multicasts a position to clients. The position is taken from the “MeshTimeline” component and we obtain the current time from the server (Get Playback Position), as well as if the direction it is going is up or down (“NextMoveUp”). These are the only two things that the client needs to know about. The “NextMoveUp” boolean is an OnRepNotify that we will deal with shortly.

The MulticastReplicatePosition function is a Multicast (obviously) and this is where we synch up the clients to what the server says is happening. The first thing we need to do is to find out if the timeline is actually currently playing or not. We do this by checking the “MeshTimeline” component with “IsPlaying” and feeding it into a Branch. If it’s NOT playing, we then want to make it play (Entering the box has already told the blueprint at this stage that the behaviour that we’re expecting is to start playing the movement). The first thing we want to do is to set the time in the “MeshTimeline” component to the float that was fed into the MulticastReplicatePosition function. After that, we need to determine if the timeline should play forwards or in reverse, which is the boolean we fed into the MulticastReplicatePosition function. Now the timeline is playing, and it’s doing it in the correct direction, from the place in time where the server has told the client the timeline should start at.

From the Update node on the timeline, we SetRelativeLocation for a mesh (which is our elevator) and thus move it.

When the timeline has Finished, we now need to make sure that the rest of the logic is only executed on the server - this is because we are about to replicate “Next Move Up” and get rid of the Timer Handle, or else the Timer Handle would keep ticking, and the NextMoveUp would stay the same for players that join in later.

On Rep Next Move Up

This function is called on the client after replication, so it’s important that we send as few variables as possible, and only the important ones. The purpose of it is to notify players that join after the movement is done, and to inform about the state of the elevator: up or down.

Thankfully, in this case, we only really need the client to know a single variable; “Next Move Up” - the other data can be fetched from the client MeshTimeline component.

The first thing that we do is to check if the MeshTimeline component is in fact playing. If it’s not moving, we need to replicate the end-location of the two possible situations (Either it’s on the ground or it has moved up). To that effect, we use the “Get Timeline Length” and set that variable as the New Time in the MeshTimeline component.

If “ Next Move Up ” is True, we know that the elevator will move up the next time someone steps on it, and so it must be at position 0. If “Next Move Up” is False, we know that the elevator will move down, and so it must be at the other end of the timeline, so we fetch the length of the timeline and set the New Time to this instead.

Appendix - Destructible Meshes

Destructible meshes are mostly used to display a mesh crumbling to bits; this involves some physics, and is purely a visual effect. Since this is a frequently used actor function, we’re detailing how to create this effect here.

Note that this actor has a Destructible component called “Destructible” that has been set to Visible=False, and Collision=None. That is because we are using only the destructible mesh itself as a reference that we pass into the SpawnActor BPCampDestructibleActor function.

This example should be relatively straight-forward to go through from the “SpawnActor” node and going forward, but let’s have a check at the nodes before that.

Why is the “isDedicatedServer” branch check there?

It’s there to stop the effect from running on a dedicated server. Listen server (hosts) still need to have this displayed, so for the Event Destroyed, we simply need to make sure the logic isn’t running on a Dedicated Server.

Why is the Get Lost Relevancy node there?

This check is here because the Event Destroyed will trigger when network relevancy is lost but in this case, since we’re losing network relevancy to the actor, we also won’t see it, so there’s no need for the logic to trigger, server or not.

In other words: A client-side actor can be destroyed because it lost the relevancy (player walked away from it), not because we defeated it. In this scenario, we don’t spawn the destructible when this is the case.