[UE5] Multiplayer Basics: Seeing the Same World with Replication and RPCs

Created: 2026-07-20

An introduction to UE5 multiplayer using only Blueprint. Covers the model where the server is authoritative and clients just display, testing with two PIE windows, Replicated variables and RepNotify, and Server RPCs. Build a button two players can press and a shared door, and learn to fix the classic "it only works on my screen" bug.

You want to walk around the same world with a friend. When one of you presses a button, the door should open on the other person's screen too. Multiplayer sounds intimidating, but UE has multiplayer built in from the start, and Blueprint alone gets you to "two people seeing the same thing."

That said, approaching it with single-player instincts will trip you up every time. You hit the wall of "it works on my screen but doesn't reach my friend." That wall is really a gap in understanding who owns the game state. This article covers the basic multiplayer model and walks through building a shared door, including how to fix that classic bug. We won't touch advanced online optimization; we'll take our time getting to "two people see the same thing."

Two screens showing the same door, where a button pressed on one is reflected on the other, alongside a soft blue clay figure

What You'll Learn

  • The model where the server is authoritative and clients just display
  • Testing two-player with two PIE windows
  • Distributing values to everyone with Replicated variables and RepNotify
  • Who's executing what: Has Authority and Server RPCs
  • Hands-on: a button two players can press and a door they share
  • How to fix the classic "it only moves on my screen" bug

Sponsored

The Server Is Authoritative, Clients Just Display

The first mental switch in multiplayer is this one. The real game state lives on the server, and clients are only showing a reflection of it.

Whether the door is open, how much health the enemy has: those "real values" are on the server. Each player's screen (the client) is only displaying values the server sent. If a client changes a value on its own, that's a lie visible only on that screen, and nobody else sees it.

Diagram with a central server holding the correct state and two clients receiving and displaying it

The easiest setup for testing is a Listen Server, where one of the players also acts as the server. You don't need a dedicated server machine; one person hosts and the other joins.

Once this model clicks, most multiplayer bugs start looking like the same single cause: a value was changed on the client without going through the server.

Testing with Two PIE Windows

You can test two-player right inside the editor. There are two settings.

  • Number of Players: how many players launch at once. Set it to 2.
  • Net Mode: what configuration to launch in. Set it to Play As Listen Server.
Setting Number of Players to 2 and Net Mode to Listen Server in the Play button menu launches two windows

Configure these in the menu next to the Play button (the three-dot menu) and hit Play, and two windows launch. One is the server and a player (the host), the other is a client. Put them side by side and develop while watching whether what happens in one shows up in the other.

For multiplayer, this "compare two windows" workflow is the basic verification method. Build with only one window open and you won't notice things failing to sync.

Syncing Variables: Replicated and RepNotify

Replication is the mechanism that distributes server-owned values to all clients.

First, make the actor itself eligible for syncing. Check Replicates in Class Defaults. For actors that move (characters and the like), also check Replicate Movement to sync their position automatically.

Next, configure each variable you want distributed. Pick Replication in the variable's details.

  • Replicated: whenever the value changes, it's automatically distributed to all clients.
  • RepNotify: on top of being distributed, a designated function (OnRep_VariableName) is called on the client side.
Flow diagram showing that enabling an actor's Replicates and setting a variable to RepNotify distributes the server's change to all clients and calls the OnRep function

RepNotify is handy for anything where "when the value changes, the visuals should update too," like a health bar. OnRep_Health is called on each client the instant the value arrives, so redrawing the bar there keeps everyone's screen in sync.

The important part is that only the server may change a Replicated variable. Change it on a client and nothing gets distributed.

Sponsored

Who's Executing: Authority and RPCs

In multiplayer Blueprints, the same graph runs on both the server and clients. So you have to stay aware of which side is running right now.

Has Authority (or Switch Has Authority) tells you. Having Authority means the server; not having it means a client. When you want something to run only on the server, branch on Has Authority.

So how do you get an input that happened on a client (a button press) to the server? That's what RPCs (Remote Procedure Calls) are for. An RPC is a way to have a function executed on the other side, and there are three kinds.

KindWhere it runsPurpose
ServerOn the serverSends client input to the server
ClientOn one specific clientNotifies just that player from the server
MulticastOn the server and all clientsShows effects or sounds to everyone
Arrows showing the three RPC directions: Server RPC from client to server, Client RPC from server to one player, and Multicast from server to everyone

RPCs also come in Reliable / Unreliable flavors. Reliable means "guaranteed to arrive"; Unreliable means "may get dropped, but it's cheap." Make important communication you can't afford to lose, like opening a door, Reliable. Use Unreliable for light information sent every frame.

🚨 Call Server RPCs on an Actor You Own

This is the condition everyone trips over first. You can't call a Server RPC from just any Actor.

A comparison showing a Server RPC called from a level-placed shared door being ignored for lack of an owner, versus one called from your own character reaching the server

A Server RPC from a client only reaches the server when the Actor you called it on is owned by that client. Call it on an Actor with no owner and it's silently discarded — no error, nothing.

Called onOwnerDoes the Server RPC arrive?
Your own Character / PlayerControllerThat clientYes
A door or switch placed in the levelNobody❌ Discarded
Another player's CharacterA different client❌ Discarded

Which means creating Server_OpenDoor inside the door's Blueprint won't work. You have to route it through the character you're controlling.

When the symptom is "nothing happens, and there's no error either," check this first. The hands-on in this article is built to satisfy this condition from the start.

The basic flow is this. The client asks the server via a Server RPC on its own character → the server changes a Replicated variable → the change is distributed to everyone. Keep to that one-way street and syncing behaves.

Sponsored

Hands-On: A Door Two Players Share

Let's build a concrete door that two players share.

A switch in a co-op puzzle, a shared door in a sandbox, a gimmick in an online co-op action game. "One player presses it and it opens on both screens" has the same structure in all of them. Here we'll build a case where the player steps on a button and the door opens on both screens.

Here's what it looks like running. Step on the button in either of the two windows and the door opens in both. Conversely, write the variable directly without going through a Server RPC and it only opens for the person who pressed it. That contrast is how you check your understanding of the model.

Example illustration contrasting the door opening on both screens regardless of which window steps on it, with the failure where writing the variable directly opens it only on the stepper's side

Setup

We'll use two Blueprints. Because of the ownership condition from the previous section, the RPC lives on the player's character.

BP_SharedDoor (the door's Actor Blueprint):

ItemSetting
Class DefaultsReplicates = true
Variable bDoorOpenType Boolean, Replication = RepNotify, default false
ButtonBox Collision (reacts on touch)
DoorMeshStatic Mesh (a Cube scaled into a thin door). Opening is expressed as this mesh's relative rotation
Function ApplyDoorStateReads bDoorOpen and sets DoorMesh's relative rotation (the only place visuals update)
Function OpenDoorOn the server, sets bDoorOpen = true and calls ApplyDoorState

BP_PlayerCharacter (the player's character):

ItemSetting
Custom event Server_RequestOpenDoorReplicates = Run on Server, Reliable on. Input Door (type BP_SharedDoor)

Setting bDoorOpen to RepNotify automatically creates the OnRep_bDoorOpen function.

Why funnel visuals through ApplyDoorState. OnRep_bDoorOpen isn't necessarily called on the server itself (the server wrote the value, so it needs no notification). Calling the same function from both "right after writing the value" and "OnRep" keeps the visuals consistent on every machine. Rather than memorizing "OnRep is client-only," just adopt this shape.

Building the Graph

A two-row completed node graph: the top row "Ask the server from your own character" runs On Component Begin Overlap into Cast To BP_PlayerCharacter, Is Locally Controlled and Server_RequestOpenDoor, wrapping into the bottom row "The server validates and writes" with Is Valid, a distance check, OpenDoor and ApplyDoorState

Here's how it's wired.

  1. On BP_SharedDoor's On Component Begin Overlap (touching the button), run Other Actor through Cast To BP_PlayerCharacter
  2. Check that character with Is Locally Controlled in a Branch (only send for the character you're controlling. Skip this and you'll also try to send for other players' characters, and those get discarded)
  3. On True, call that character's Server_RequestOpenDoor (Door = the door itself). The character is yours, so this reaches the server
  4. Inside Server_RequestOpenDoor (running on the server), check that Door passes Is Valid, and that the distance to the door is close enough
  5. If both hold, call the door's OpenDoor, which sets bDoorOpen = true and runs ApplyDoorState
  6. Since bDoorOpen is RepNotify, the value is distributed to all clients and each one's OnRep_bDoorOpen calls ApplyDoorState

Read out as pseudocode, it looks like this.

BP_SharedDoor
On Component Begin Overlap (Button)
  → chara = Cast To BP_PlayerCharacter(Other Actor)
  → Branch (chara.IsLocallyControlled)      // only your own character
      True → chara.Server_RequestOpenDoor(Door = self)

OpenDoor():                                  // only ever called on the server
  bDoorOpen = true                           // only the server writes Replicated variables
  ApplyDoorState()                           // update the server's own visuals too

OnRep_bDoorOpen():                           // called on clients when the value arrives
  ApplyDoorState()

ApplyDoorState():                            // the single place visuals update
  Set Relative Rotation(
      Target = DoorMesh,
      New Rotation = bDoorOpen ? (0, 0, 90) : (0, 0, 0))

BP_PlayerCharacter
Server_RequestOpenDoor(Door)                 // Run on Server / Reliable
  → if Is Valid(Door) is false, do nothing
  → distance check: (Door.GetActorLocation() - GetActorLocation()).Length() < 300.0
  → Door.OpenDoor()

Checking the distance on the server is the point. Not "the client asked, so open it" but the server confirming the conditions itself before opening. That one extra step is your first line of defense against cheating.

Verifying

Play with Number of Players = 2 and Net Mode = Listen Server. Move a player onto the button in one window, and success means the door opens in both windows. Stepping on it from the other window does the same.

Troubleshooting:

  • It only opens on the side that pressed it → You're writing bDoorOpen directly from Overlap without a Server RPC. Values written on a client aren't distributed
  • Nothing happens at all, and there's no error → You're failing the ownership condition. Did you create Server_RequestOpenDoor on the door (BP_SharedDoor)? The RPC goes on the character
  • It doesn't open on the server side (the first window) → You forgot to call ApplyDoorState inside OpenDoor. OnRep isn't necessarily called on the server
  • It doesn't open on the client side → The actor's Replicates is false, or the variable's Replication isn't set

Two things to take away.

  • Call Server RPCs on something you own: doors and switches placed in the level have no owner, so a Server RPC placed there is silently discarded. Always route input through your own Character or PlayerController
  • Funnel visual updates into one place: create ApplyDoorState and call it both right after the server writes the value and from the client's OnRep. With the same function running on every machine, you never get the one-side-only bug

Calculations that decide outcomes, like damage, follow the same idea: damage should be calculated on the server. Clients only receive the result and display it.

Bonus: Good to Know for Later

The Game Framework is the foundation for server/client roles. Which classes exist once on the server and which exist per player is decided by GameMode / GameState / PlayerState. Properties like "GameMode only exists on the server" carry straight over into multiplayer.

A Listen Server is plenty to start. Dedicated servers are something to consider once your scale grows. At the solo-development entry point, learning the syncing model with one player hosting comes first.

Steam integration and matchmaking are a separate topic. Connecting to a friend over the internet requires an Online Subsystem (like Steam) and session management. That's a different layer from Replication, so finish getting syncing right with two windows on one PC first, then tackle it as the next step.

Summary

Multiplayer rests on a single principle. The server owns the state, and clients display it. So client input goes to the server via a Server RPC, the server writes a Replicated variable, and RepNotify distributes it to everyone. Stick to that one-way street and "two people see the same thing" comes together cleanly.

What's the first thing you want two players to share in your game? A door, a score, anything: put it on this one-way street and it will line up on everyone's screen.