Blueprint Custom Events as RPCs¶
A replicated Custom Event is not "calling a function on another computer." It is sending a one-way network message through a replicated actor or actor component, then letting the receiving machine run that event locally.
The one-minute version¶
- In Blueprint, an RPC is usually a Custom Event whose Details panel Replicates setting is changed to Run on Server, Run on Owning Client, or Multicast.
- The actor must be a replicated actor. For component RPCs, the owning actor must replicate and the component must be set up for networking too.
- RPCs are one-way. They do not return a value across the network. Send inputs with the event, then send the result back through replicated state, RepNotify, or a separate client RPC.
- A client can call Run on Server only from an actor that is owned by that client's connection. A placed door, pickup, or enemy is usually not owned by the client touching it.
- Reliable means "keep resending until acknowledged." It does not mean "free," "instant," or "safe to call every frame."
- Use RPCs for requests and transient events. Use replicated variables for durable state that late clients, relevancy changes, and UI can read later.
What an RPC actually is¶
A normal Custom Event runs where it is called. A replicated Custom Event asks Unreal's networking layer to deliver the call to a different machine, according to the event's replication mode and the actor's ownership.
At beginner altitude, the pipeline looks like this:
pseudocode of the relationship - not engine source
Blueprint calls CustomEvent_ServerFire on BP_PlayerCharacter
-> Unreal checks whether this actor/component is allowed to send that RPC
-> the call is serialized into network data for the right connection
-> the receiving machine executes CustomEvent_ServerFire in its local copy
-> the event graph after that node runs on the receiving machine
The event did not run "inside the other player's Blueprint." It ran on that machine's local copy of the replicated actor. That distinction matters because each world has its own objects, widgets, cameras, and component instances.
How a Custom Event becomes an RPC¶
For a Blueprint actor:
Class defaults:
-> Replicates = true
Event Graph:
-> Add Custom Event, for example ServerTryInteract
-> select the Custom Event node
-> Details panel -> Graph -> Replicates:
Not Replicated
Run on Server
Run on Owning Client
Multicast
-> optionally check Reliable
-> add input parameters the receiver needs
Then call the event like any other Blueprint event. The same node name appears in your graph; the replication setting changes where the event is allowed to execute.
Use names that say where the event is meant to run:
| Prefix | Example | Meaning |
|---|---|---|
Server |
ServerTryInteract(TargetActor) |
A client request that the server validates. |
Client |
ClientShowHitMarker() |
Server tells one owning client to do local presentation. |
Multicast |
MulticastPlayImpactFX(Location, Surface) |
Server tells relevant clients about a transient event. |
The prefix is only a naming convention, but it saves real debugging time. A
node called FireWeapon gives no clue which machine should run it.
The three Blueprint RPC modes¶
| Mode | Who should usually call it | Where it executes | Beginner use |
|---|---|---|---|
| Run on Server | Owning client, or the server itself | Server copy of that actor | "I pressed Interact/Fire/Ready. Please validate and change authoritative state." |
| Run on Owning Client | Server | The client connection that owns this actor | "Show this private UI/camera/audio result to that one player." |
| Multicast | Server | Server and clients for which the actor is relevant | "Play a short cosmetic event for everyone currently seeing this actor." |
Blueprint does not expose the newer C++ Remote RPC mode. For Blueprint
beginners, the practical set is Server, Owning Client, and Multicast.
Ownership: why the Server RPC silently does nothing¶
The classic bug:
Client overlaps BP_Door
-> client calls BP_Door.ServerOpenDoor
-> nothing happens on the server
The door is replicated, but the door is not owned by that client. The client does not get to send Server RPCs through every actor it can see. The client can normally send Server RPCs through its own PlayerController, possessed Pawn, or another replicated actor whose Owner chain resolves to that player's connection.
Use an owned path:
Client presses Interact on owned PlayerController or Pawn
-> Run on Server: ServerTryInteract(TargetActor)
-> server validates target distance, line of sight, state, and permission
-> server calls a normal function/interface on BP_Door
-> server changes replicated door state
-> clients receive the result
That shape keeps the request on an owned actor and the authority on the server. It also avoids making every world object owned by whoever last touched it.
What counts as an owned path?¶
| Actor | Can this client's Server RPC usually go through it? | Why |
|---|---|---|
| This client's PlayerController | Yes | It is the connection's control object. |
| This client's possessed Pawn / Character | Yes | It is owned through the player's controller. |
| Another player's pawn | No | That pawn belongs to a different connection. |
| A placed door, pickup, enemy, or objective actor | Usually no | World actors are normally server-owned or unowned. |
| A weapon/equipment actor | Maybe | Only if it replicates and its Owner is set to the owning player path. |
| A widget | No | Widgets are local UI and are not replicated actors. |
Set Owner can matter for networking, but do not use it as a random fix. Set
ownership when the actor is genuinely associated with a player's connection,
such as that player's inventory item or equipped weapon. A shared door should
usually stay server-owned and receive requests through the player's owned actor.
No return values: design it as a message¶
An RPC is asynchronous network communication. By the time the server receives a client request, validates it, changes state, and sends results back, the original Blueprint call has already continued locally. A return pin would pretend that the answer exists immediately on the caller, so Unreal does not offer one.
Use this instead:
Client:
call ServerTryBuyItem(ItemId)
optionally show "pending" presentation
Server RPC:
validate money, inventory space, and item definition
if accepted:
change replicated Money and Inventory
else:
call ClientShowBuyFailed(Reason)
Clients:
RepNotify / UI refresh reacts when replicated values arrive
The request is an RPC. The durable result is replicated state. A private error message can be a Run on Owning Client RPC.
Reliable vs unreliable¶
Reliable RPCs are resent until the receiver acknowledges them. They also carry ordering consequences: a backed-up reliable stream can delay later reliable traffic. This is why "Reliable" should feel like a budget, not a default.
Use Reliable for low-frequency requests or notifications that must arrive for the feature to make sense:
- player confirms ready;
- player presses a menu purchase button;
- server tells the owning client that a match invite was accepted;
- one-time ability activation request, after client-side rate limiting.
Prefer Unreliable for frequent or disposable events:
- aim updates every tick;
- repeated cosmetic impact pings;
- footstep/muzzle/audio spam where the next update matters more than the old one;
- anything called from Tick or a held input without a rate limit.
Reliable is not validation. The server still needs to check distance, cooldown, ammo, authority, item ownership, and any other gameplay rule before trusting a client request.
Patterns that work¶
Interacting with a door¶
Owned pawn/controller:
Input Interact
-> line trace locally for responsiveness
-> ServerTryInteract(HitActor)
ServerTryInteract:
validate HitActor is still interactable and close enough
call interface Interact on HitActor
Door on server:
set bOpen = true (RepNotify)
Clients:
OnRep_Open plays the door timeline locally
Firing a replicated projectile¶
Client:
Input Fire
-> play local muzzle flash/camera shake if desired
-> ServerFire(AimTransform)
ServerFire:
validate cooldown, ammo, weapon, aim origin
spawn replicated projectile
decrement replicated ammo
Clients:
receive projectile and ammo updates
Private client feedback¶
Server:
player took confirmed damage
-> reduce replicated Health
-> ClientShowDamageIndicator(Direction)
Owning client:
ClientShowDamageIndicator
-> local widget/camera/audio feedback
The health belongs in replicated state. The screen indicator belongs to the owning client's local presentation.
When it fails, and what failure looks like¶
Most Blueprint RPC failures do not give you a dramatic crash. The event simply does not execute on the machine you expected.
Common causes:
- The actor does not replicate. The event is marked as an RPC, but there is no network actor path to carry it.
- The component path is not network-ready. The actor replicates, but the component with the event is not configured for replicated component behavior.
- The client does not own the actor. Server RPCs from unowned actors are dropped.
- You called a Client RPC from a client and expected the server to send it. Client RPCs are normally server-to-owning-client messages.
- You called Multicast from a client. It runs locally on that client instead of reaching everyone.
- The actor is not relevant to a client. Server multicast reaches relevant clients for that actor, not a guaranteed global audience forever.
- The event is reliable but spammed. The reliable queue can become a new problem instead of fixing packet loss.
Print from inside the event with the actor name and net mode. If the print does not appear on the expected machine, debug ownership and authority before debugging the gameplay logic inside the event.
Lookalikes - which one do I want?¶
| Tool | Use when | Not for |
|---|---|---|
| Run on Server RPC | Owning client asks the server to do authoritative work. | Getting an immediate return value or bypassing validation. |
| Run on Owning Client RPC | Server tells one player to show private local presentation. | Public state everyone needs to know. |
| Multicast RPC | Server sends a transient event to relevant clients now. | Durable state, late joiners, or anything that must be reconstructable later. |
| Replicated variable | Clients need the current value. | One-time cosmetic pulses with no lasting state. |
| RepNotify | Clients should run local presentation when a replicated value arrives. | Client-to-server requests. |
| Blueprint Interface | You need a local contract between objects in the same world. | Crossing the network by itself. |
| Event Dispatcher | One local object broadcasts to local listeners. | Replicating an event to another machine. |
The quick path is to make every multiplayer action a Multicast because "all players should see it." The concrete cost is that state becomes a pile of one-time events: late joiners, relevancy changes, dropped unreliable calls, and UI refreshes have nothing durable to read. Replicate the state first; use RPCs for requests or transient presentation.
Going deeper¶
- Replication basics - the broader actor, variable, RPC, relevancy, and widget rules this page builds on.
- Multiplayer roles - which objects exist on the server, owning client, and other clients.
- Ownership and lifetime - why networking owner is not the same as component ownership, attachment, or possession.
- First weapon fire pattern and impact cleanup - a full fire request shape with cooldowns, damage, feedback, and cleanup.
- Spawn Actor from Class - why server-spawned gameplay actors replicate more predictably than client-only spawns.
- Official docs: Remote Procedure Calls, Actor Owner and Owning Connection, Networking Overview, and Replicated Object Execution Order.
- Engine source (requires engine access - we do not reproduce it here):
start with
AActor::CallRemoteFunctioninEngine/Source/Runtime/Engine/Private/Actor.cpp,UActorComponent::CallRemoteFunctioninEngine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp, andUNetDriver::ProcessRemoteFunctionForChannelinEngine/Source/Runtime/Engine/Private/NetDriver.cpp.