Basics

Fusion RPC Targets

Every Fusion RPC carries an EFusionRPCTarget selector (declared in FusionHelpers.h) that decides who receives the call. Targets are explicit because shared authority does not have a built-in "the server" recipient — the RPC has to say what it means by destination.

Target Receivers Requires networked source actor
SendToAllClients Every peer in the room, including the caller No
SendToObjectOwner The resolved owner of the source actor Yes — source must have a UFusionActorComponent
SendToMasterClient The current Master Client. Re-resolved on the receiving side No
SendToEveryoneElse Every peer except the caller No

SendToObjectOwner is the only target that requires the source actor to be networked through a UFusionActorComponent. The "owner" is the actor's resolved Fusion owner, computed per the rules in Ownership. The other targets do not need a networked source — they route based on room membership, not per-actor ownership.

Defining a Fusion RPC in Blueprints

Blueprint RPCs use the UFusionRPCFunctionNode custom K2 node, exposed in the BP editor with the display name Call RPC With Hook. The node lives in the PhotonFusionEditor module and wraps a Blueprint Custom Event together with a target selection. It is the only Blueprint API needed for sending an RPC — there is no separate Send Fusion RPC node.

Keep the receiving Custom Event signature inside the BP only — the Call RPC With Hook node serializes arguments via UFusionHelpers::AddParamToBuffer under the hood, so the parameter types follow the supported-argument list below. A function signature change to the Custom Event automatically updates the wire format because the node re-binds to the current event each compile.

Defining a Fusion RPC in C++

C++ RPCs use the SEND_FUSIONRPC macro defined in FusionMacros.h. Mark the call-site function with SEND_FUSIONRPC(target), add FUSION_BODY() to the class body, and the PhotonFusionUbtPlugin UHT generator emits the dispatcher into a <Header>.fusion.gen.cpp. Implement a matching _Receive function with the same signature for the receive-side logic.

C++

// In the header:
SEND_FUSIONRPC(EFusionRPCTarget::SendToObjectOwner)
void ApplyDamage(float Damage, FVector HitLocation);

void ApplyDamage_Receive(float Damage, FVector HitLocation);

// In the .cpp:
void AMyActor::ApplyDamage_Receive(float Damage, FVector HitLocation)
{
    Health -= Damage;
    LastHitLocation = HitLocation;
}

The full macro expansion, the generated _Receive thunk, and the FUSION_BODY() placement rules are covered in C++ Integration.

Reliability and Ordering Semantics

Fusion RPCs are reliable and in-order per (caller, target object). Two RPCs sent from the same caller to the same target object arrive in the order they were sent, and neither is silently dropped. The guarantee is provided by FusionCore's transport layer — UFusionNetDriver is a thin pass-through that does not inspect Unreal's Reliable / Unreliable UFUNCTION flags.

The ordering scope is narrow on purpose. RPCs from different senders, or from the same sender to different target objects, are not ordered relative to each other. If two clients both call an RPC on the same target, the target sees them in some order — possibly different per receiver — but the guarantee only constrains the within-(sender, target) sequence.

The RPC ordering guarantee is distinct from state-replication ordering. OnRep_* callbacks on replicated properties have their own batching rules — see Replicated Properties. Do not assume that an RPC and a property change written in the same tick arrive in any particular relative order.

RPCs are not replayed for late joiners. A client that joins after the RPC fired never sees it. Anything that must survive a join belongs in a replicated property on a networked actor, not in an RPC — see Replicated Properties for the persistent path.

Behavior Under Owner and Master Client Changes

SendToObjectOwner resolves the owner at send time, using the current UFusionActorComponent owner state. The resolved owner is the int32 player id returned by GetResolvedOwner(Actor) — see Ownership for how that resolution works.

A Server RPC called on an actor this peer does not own silently attempts a send through FusionCore. UFusionNetDriver::ProcessRemoteFunction does not check ownership before dispatch, and UFusionClient::SendCustomRPC queues the send anyway with no Unreal-side warning or early return. Guard call sites that are sensitive to this.

C++

if (OnlineSubsystem->IsOwner(this))
{
    Server_DoSomething();
}

SendToMasterClient re-resolves the recipient on the receiving side, not on the sender. An in-flight Master Client migration — the original master left while the RPC was on the wire — is delivered to the new host, not bounced. Application code does not need to retry the call across master changes.

Using Built-in Server, Client, and NetMulticast Markup

Fusion routes Unreal's built-in Server, Client, and NetMulticast UFUNCTION markers through UFusionNetDriver, which forwards each call into UFusionOnlineSubsystem::SendCustomRPC with ERPCMode::UnrealRPC. Existing UE projects can use stock RPC markup without rewriting to SEND_FUSIONRPC, although the new C++ path is the recommended one for shared-authority code because it makes the target explicit.

UFUNCTION marker Routes to
UFUNCTION(NetMulticast) SendToAllClients
UFUNCTION(Client) SendToObjectOwner
UFUNCTION(Server) SendToObjectOwner

There is no separate "send to authority" path under Fusion. Server RPCs route to the object owner, because that is the Fusion equivalent of authority — there is no separate server process to route to.

HasAuthority() inside a Fusion-routed Server RPC body returns whatever stock AActor::HasAuthority would return — the plugin does not override it. On Fusion-owned actors, ROLE_Authority is local on the owning peer, so the predicate ends up reading as true on the receiving peer whenever that peer is the object owner. This is incidental, not a contract; prefer OnlineSubsystem->IsOwner(this) for the explicit check, as covered in Ownership.

Supported Argument Types

The RPC parameter marshaller in UFusionFunctionDescriptor::SerializeParams accepts the property kinds enumerated by the UFusionTypeBase::AddProperty overloads: numeric types, bool, string, name, enum, struct, class, and object pointer variants.

EFusionDataTypes Unreal property type
Byteuint8 (FByteProperty)
Intint32 (FIntProperty)
UIntuint32 (FUInt32Property)
Int16 / UInt16int16 / uint16
Int64 / UInt64int64 / uint64
Boolbool (FBoolProperty)
Floatfloat
Doubledouble
VectorFVector
RotatorFRotator
QuatFQuat
NameFName
StringFString
ClassIdUClass* (FClassProperty)
ObjectIdUObject* (FObjectProperty)
ActorIdAActor*
ArrayTArray<T> of any supported type
FusionArrayFFusionNetworkedArray subclasses
StructUSTRUCT with FUSION_BODY()

FObjectProperty, FWeakObjectProperty, and FSoftObjectProperty arguments are encoded via EFusionEncodedObjectType. The encoding carries enough information to resolve the referent on the receive side, but it cannot resurrect a destroyed object. If the referent was destroyed between the send and the receive, the parameter arrives nullptr. RPC handlers must null-check object pointers.

RPCs on the GameState and Global Actors

UFusionOnlineSubsystem::AttachCurrentMap registers AGameStateBase as a Master-Client-owned networked source — unless Fusion.DisableGameStateNetworking is set to true, in which case the GameState is excluded from the auto-attach path. Once attached, RPCs defined on the GameState route like any other object-targeted call.

C++

UCLASS()
class AMyGameState : public AGameStateBase
{
    GENERATED_BODY()
    FUSION_BODY()

    SEND_FUSIONRPC(EFusionRPCTarget::SendToMasterClient)
    void Server_AdvanceMatch();

    void Server_AdvanceMatch_Receive();
};

Do not put RPCs on UGameInstance itself. UGameInstance is process-local — it has no Fusion identity and cannot send or receive RPCs. Host shared state and shared RPCs on the GameState or on a dedicated map-attached actor instead, as covered in Game Classes.

Target the GameState with SendToMasterClient when only the host should react. Target it with SendToAllClients for room-wide fan-out. SendToObjectOwner on the GameState also reaches the Master Client, because that is who owns the GameState — but SendToMasterClient makes the intent clearer for room-scoped decisions.

Back to top