Multiplayer in Blueprints

Choose your OS:

Unreal Engine 4 provides a lot of multiplayer functionality out of the box, and it's easy set up a basic Blueprint game that works over a network. It's easy to dive in and start playing mutliplayer. Most of the logic to make basic multiplayer work is thanks to the built-in networking support in the Character class, and its CharacterMovementComponent, which the Third Person template project uses.

Gameplay framework review

To add multiplayer functionality to your game, it's important to understand the roles of the major gameplay classes that are provided by the engine and how they work together - and especially, how they work in a multiplayer context:

  • GameInstance

  • GameMode

  • GameState

  • Pawn (and Character, which inherits from Pawn)

  • PlayerController

  • PlayerState

See the Gameplay Framework documentation for more information - but at least keep in mind the following tips when designing your game for multiplayer are:

  • The GameInstance exists for the duration of the engine's session, meaning that it is created when the engine starts up and not destroyed or replaced until the engine shuts down. A separate GameInstance exists on the server and on each client, and these instances do not communicate with each other. Because the GameInstance exists outside of the game session and is the only game structure that exists across level loads, it is a good place to store certain types of persistent data, such as lifetime player statistics (e.g. total number of games won), account information (e.g. locked/unlocked status of special items), or even a list of maps to rotate through in a competitive game like Unreal Tournament.

  • The GameMode object only exists on the server. It generally stores information related to the game that clients do not need to know explicitly. For example, if a game has special rules like "rocket launchers only", the clients may not need to know this rule, but when randomly spawning weapons around the map, the server needs to know to pick only from the "rocket launcher" category.

  • The GameState exists on the server and the clients, so the server can use replicated variables on the GameState to keep all clients up-to-date on data about the game. Information that is of interest to all players and spectators, but isn't associated with any one specific player, is ideal for GameState replication. As an example, a baseball game could replicate each team's score and the current inning via the GameState.

  • One PlayerController exists on each client per player on that machine. These are replicated between the server and the associated client, but are not replicated to other clients, resulting in the server having PlayerControllers for every player, but local clients having only the PlayerControllers for their local players. PlayerControllers exist while the client is connected, and are associated with Pawns, but are not destroyed and respawned like Pawns often are. They are well-suited to communicating information between clients and servers without replicating this information to other clients, such as the server telling the client to ping its minimap in response to a game event that only that player detects.

  • A PlayerState will exist for every player connected to the game on both the server and the clients. This class can be used for replicated properties that all clients, not just the owning client, are interested in, such as the individual player's current score in a free-for-all game. Like the PlayerController, they are associated with individual Pawns, and are not destroyed and respawned when the Pawn is.

  • Pawns (including Characters) will also exist on the server and on all clients, and can contain replicated variables and events. The decision of whether to use the PlayerController, the PlayerState, or the Pawn for a certain variable or event will depend on the situation, but the main thing to keep in mind is that the PlayerController and PlayerState will persist as long as the owning player stays connected to the game and the game doesn't load a new level, whereas a Pawn may not. For example, if a Pawn dies during gameplay, it will usually be destroyed and replaced with a new Pawn, while the PlayerController and PlayerState will continue to exist and will be associated with the new Pawn once it finishes spawning. The Pawn's health, therefore, would be stored on the Pawn itself, since that is specific to the actual instance of the Pawn and should be reset when the Pawn is replaced with a new one.

Actor replication

replicates.png

The core of the networking technology in UE4 is actor replication. An actor with its "Replicates" flag set to true will automatically be synchronized from the server to clients who are connected to that server. An important point to understand is that actors are only replicated from the server to the clients - it's not possible to have an actor replicate from a client to the server. Of course, clients still need to be able to send data to the server, and they do this through replicated "Run on server" events.

See this How to Replicate Actors guide for a walkthrough of a concrete example, as well as the Actor Replication documentation.

Authority

For every actor in the world, one of the connected players is considered to have authority over that actor. For every actor that exists on the server, the server has authority over that actor - including all replicated actors. As a result, when the Has Authority function is run on a client, and the target is an actor that was replicated to him, it will return false. You can also use the Switch Has Authority convenience macro as a quick way to branch for different server and client behavior in replicated actors.

switch_has_authority.png

Variables

In the details panel of variables on your actors, there is a Replication drop-down that lets you control how your variables are replicated, if at all.

variable_replication.png

Option

Description

None

This is the default for new variables, and means the value will not be sent over the network to clients.

Replicated

When the server replicates this actor, it will send this variable to clients. The value of the variable on the receiving client will update automatically, so that the next time it's accessed, it will reflect what the value was on the server. Of course, when playing over a real-world network, the updates will be delayed by an amount of time dependent on the network's latency. Remember that replicated variables only go in one direction, from the server to the client! To send data from a client to the server, see the "Events" section.

RepNotify

The variable will be replicated as in the Replicated option, but in addition, an OnRep_<variable name> function will be created in your blueprint. This function will be called by the engine automatically on the client and the server whenever the value of this variable changes. You're free to implement this function however you'd like, as needed by your game. onrep.png

Many of the variables in the engine's built-in classes already have replication enabled, so that many features work automatically in a multiplayer context.

See this Replicating Variables guide for a walkthrough of a concrete example of variable replication, as well as the Property Replication documentation.

Spawning and destroying

When a replicated actor is spawned on the server, this is communicated to clients, and they will also automatically spawn a copy of that actor. But since, in general, replication doesn't occur from clients to the server, if a replicated actor is spawned on a client, that actor will only exist on the client that spawned it. Neither the server nor any other client will receive a copy of the actor. The spawning client will, however, have authority over the actor. This can still be useful for things like cosmetic actors that don't really have an effect on gameplay, but for actors that do have an effect on gameplay and should be replicated, it's best to make sure they are spawned on the server.

The situation is similar for destroying replicated actors: if the server destroys one, all clients will destroy their respective copies as well. Clients are free to destroy actors for which they have authority - that is, actors they have spawned themselves - since these are not replicated to other players and wouldn't have any affect on them. If a client tries to destroy an actor for which he is not the authority, the destroy request will be ignored. The key point here is the same for spawning actors: if you need to destroy a replicated actor, destroy it on the server.

Event replication

In Blueprints, in addition to replicating actors and their variables, you can also run events across the clients and the server.

See this Replicating Functions guide for a walkthrough of a concrete example, as well as the RPCs documentation.

You may also see the term RPC (Remote Procedure Call). If so, just be aware that replicated events in Blueprints essentially compile down to RPCs inside the engine - and this is what they're usually called in C++.

Ownership

An important concept to understand when working on multiplayer, and especially with replicated events, is which connection is considered to be the owner of a particular actor or component . For our purposes, know that "Run on server" events can only be invoked from actors (or their components) which the client owns. Usually, this means you can only send "Run on server" events from the following actors, or from a component of one of the actors:

  • The client's PlayerController itself,

  • A Pawn that the client's PlayerController possesses, or

  • The client's PlayerState.

Likewise, for a server sending "Run on owning client" events, those events should also be invoked on one of these actors. Otherwise, the server won't know which client to send the event to, and it will only run on the server!

Events

In the details panel of your custom events, you can set how the event is replicated, if at all.

event_replication.png

Option

Description

Not Replicated

This is the default, and means there will be no replication for this event. If it is invoked on a client, it will only run on that client, and if it is invoked on the server, it will only run on the server.

Multicast

If a multicast event is invoked on the server, it will be replicated to all connected clients - regardless of which connection owns the target object. If a client invokes a multicast event, it will be treated as if it wasn't replicated, and it will only run on the client that invoked it.

Run on Server

If this event is invoked from the server, it will only run on the server. If it's invoked from a client, with a target that the client owns, it will be replicated to and run on the server. "Run on Server" events are the primary method for clients to send data to the server.

Run on Owning Client

If invoked from the server, this event will run on the client who owns the target actor. Since the server can own actors itself, a "Run on Owning Client" event may actually run on the server, despite its name. If invoked from a client, the event will be treated as if it isn't replicated, and it will only run on the client that invoked it.

The following tables illustrate how the different replication modes affect where an event is run, based on how it is invoked.

If the event is invoked from the server, given the target in the left-hand column, it will run on...

Not replicated

Multicast

Run on Server

Run on Owning Client

Client-owned target

Server

Server and all clients

Server

Target's owning client

Server-owned target

Server

Server and all clients

Server

Server

Unowned target

Server

Server and all clients

Server

Server

If the event is invoked from a client, given the target in the left-hand column, it will run on...

Not replicated

Multicast

Run on Server

Run on Owning Client

Target owned by invoking client

Invoking client

Invoking client

Server

Invoking client

Target owned by a different client

Invoking client

Invoking client

Dropped

Invoking client

Server-owned target

Invoking client

Invoking client

Dropped

Invoking client

Unowned target

Invoking client

Invoking client

Dropped

Invoking client

As you can see from the table above, any events that are invoked from a client and that are not set to Run on Server are treated as if they are not replicated.

Sending a replicated event from the client to the server is the only way to communicate information from a client to the server, since general actor replication is designed to be server-to-client only.

Also, note that multicast events can only be sent from the server. Because of Unreal's client-server model, a client isn't directly connected to any of the other clients, he's only connected to the server. Therefore, a client is unable to send a multicast event directly to the other clients, and must only communicate with the server. You can emulate this behavior, however, by using two replicated events: one Run on server event, and one Multicast event. The implementation of the Run on server event can perform validation, if desired, and then call the multicast event. The implementation of the multicast event would perform the logic that you'd like to run for all connected players. As an example that doesn't perform any validation at all, see the following image:

forward_multicast.png

Join-in-progress considerations

One thing to keep in mind when using replicated events to communicate game state changes is how they interact with a game that supports join-in-progress. If a player joins a game in progress, any replicated events that occurred before the join will not be executed for the new player. The takeaway here is that if you want your game to work well with join-in-progress, it's usually best to synchronize important gameplay data via replicated variables. A pattern that comes up pretty often is that a client performs some action in the world, notifies the server about the action via a "Run on server" event, and in the implementation of that event, the server updates some replicated variables based on the action. Then the other clients, who did not perform the action, still see the result of the action via the replicated variables. In addition, any clients who join-in-progress after the action has occurred will also see the correct state of the world, since they receive the most recent value of the replicated variables from the server. If the server had instead only sent an event, the join-in-progress players wouldn't know about the action that was performed!

Reliability

For any replicated event, you can choose whether it is Reliable or Unreliable.

Reliable events are guaranteed to reach their destination (assuming the ownership rules above are followed), but they introduce more bandwidth, and potentially latency, to meet this guarantee. Try to avoid sending reliable events too often, such as on every tick, since the engine's internal buffer of reliable events may overflow - when this happens, the associated player will be disconnected!

Unreliable events work as their name implies - they may not reach their destination, in case of packet loss on the network, or if the engine determines there is a lot of higher-priority traffic it needs to send, for example. As a result, unreliable events use less bandwidth than reliable events, and they can be called safely more often.