RPCs
Overview
RPCs in Fusion are fire-and-forget messages sent between clients.
Unlike state synchronization, which continuously replicates property values, RPCs deliver discrete events: damage notifications, chat messages, ability activations and other one-shot actions.
Rpc Structure
C++
class Rpc {
public:
uint64_t Id; // RPC identifier
RpcFlags Flags; // Delivery flags (opaque)
PlayerId OriginPlayer; // Sender
PlayerId TargetPlayer; // Recipient (0 = all)
ObjectId TargetObject; // Target object ((0,0) = broadcast)
uint16_t TargetComponent; // Component index on target object
uint64_t DescriptorTypeHash; // Type descriptor hash for routing
uint64_t EventHash; // Method/event hash for dispatch
Data Bytes; // Serialized payload
bool IsInternal() const; // True if Id is in [1, 1023]
};
Key Fields
| Field | Purpose |
|---|---|
Id |
Unique RPC identifier. IDs 1-1023 are reserved for SDK internal RPCs. User RPCs use IDs >= 1024. |
TargetPlayer |
Specific player to receive the RPC. 0 for all players, or a PlayerId for targeted delivery. |
TargetObject |
The networked object this RPC is addressed to. ObjectId(0,0) indicates a broadcast RPC. |
DescriptorTypeHash |
Hash identifying the type/class of the RPC handler. Used for routing to the correct receiver. |
EventHash |
Hash identifying the specific method to call. Combined with DescriptorTypeHash, uniquely identifies the handler. |
Bytes |
Opaque payload containing serialized arguments. |
Creating and Sending RPCs
CreateUserRpc
C++
Rpc Client::CreateUserRpc(
uint64_t id, // RPC ID (must be >= 1024)
PlayerId targetPlayer, // 0 = all, specific PlayerId = targeted
ObjectId targetObject, // Object target or (0,0) for broadcast
uint64_t DescriptorTypeHash, // Type hash for routing
uint64_t EventHash, // Method hash for dispatch
const char* data, // Serialized payload bytes
size_t dataLength // Payload length
);
This constructs an Rpc struct with the local player as OriginPlayer.
The data parameter should contain pre-serialized arguments.
SendUserRpc
C++
bool Client::SendUserRpc(const Rpc& rpc);
Queues the RPC for delivery.
RPCs are batched and sent during the next UpdateFrameEnd() cycle.
Returns true if the RPC was accepted.
Complete Example
C++
// 1. Serialize payload
WriteBuffer payload;
payload.Int(damageAmount);
payload.UIntVar(targetEntityId);
Data payloadData = payload.Take();
// 2. Create the RPC
Rpc rpc = client->CreateUserRpc(
1024, // User RPC ID
0, // All players
targetObjectId, // Object-targeted
CRC64("DamageHandler", strlen("DamageHandler")), // Type hash
CRC64("OnDamage", strlen("OnDamage")), // Method hash
reinterpret_cast<const char*>(payloadData.Ptr),
payloadData.Length
);
// 3. Send
client->SendUserRpc(rpc);
// 4. Clean up payload
payloadData.Free();
Receiving RPCs
All incoming RPCs arrive through a single broadcaster:
C++
Broadcaster<void(Rpc&)> OnRpc;
Subscribe to receive RPCs:
C++
subs += client->OnRpc.Subscribe([](SharedMode::Rpc& rpc) {
if (rpc.IsInternal()) return; // Skip SDK-internal RPCs
if (rpc.TargetObject.IsSome()) {
// Object-targeted RPC: route to the specific object
dispatch_to_object(rpc);
} else {
// Broadcast RPC: deliver to all registered receivers
dispatch_broadcast(rpc);
}
});
Routing by Hashes
DescriptorTypeHash and EventHash together identify the handler:
DescriptorTypeHashidentifies the class or script type (e.g.,CRC64("PlayerController")).EventHashidentifies the method within that type (e.g.,CRC64("on_damage_received")).
The integration layer computes these hashes at registration time and matches them against incoming RPCs.
Reading the Payload
C++
void handle_damage_rpc(SharedMode::Rpc& rpc) {
ReadBuffer reader(rpc.Bytes);
int32_t damage = reader.Int();
uint32_t entityId = reader.UIntVar();
// Apply damage...
}
ID Ranges
| Range | Owner | Purpose |
|---|---|---|
| 1 - 1023 | SDK internal | Scene change, object priority, etc. |
| 1024+ | User | Application-defined RPCs |
Internal RPC Constants
C++
constexpr uint64_t RPC_InternalMinId = 1;
constexpr uint64_t RPC_InternalMaxId = 1023;
constexpr uint64_t RPC_InternalSceneChange = 1; // Scene transition
constexpr uint64_t RPC_InternalObjectPriority = 2; // AOI priority
Internal RPCs are processed by the SDK before reaching OnRpc.
The IsInternal() method identifies them.
User code should never send RPCs with IDs in the internal range.
Target Filtering
Player Targeting
| TargetPlayer Value | Behavior |
|---|---|
0 |
Delivered to all players |
Specific PlayerId |
Delivered only to that player |
MasterClientPlayerId (0xFFFFFFFF) |
Delivered to the current master client |
ObjectOwnerPlayerId (0xFFFFFFFF - 2) |
Delivered to the owner of TargetObject |
Object Targeting
| TargetObject Value | Behavior |
|---|---|
ObjectId(0, 0) (.IsNone()) |
Broadcast RPC -- not object-targeted |
Valid ObjectId (.IsSome()) |
Delivered to the specific networked object |
RpcFlags
C++
struct RpcFlags {
uint32_t _value;
static RpcFlags Read(ReadBuffer& reader);
static void Write(WriteBuffer& writer, const RpcFlags& rpc);
};
Flags are packed into a single uint32_t and serialized/deserialized via the buffer helpers.
The flag values are opaque to user code -- they control internal delivery semantics.
Wire Format
The Rpc struct has built-in serialization for the SDK's packet protocol:
C++
static Rpc Rpc::Read(ReadBuffer& reader);
static void Rpc::Write(WriteBuffer& writer, const Rpc& rpc);
These are used internally by the SDK for packet construction and parsing.
User code does not need to call them directly.
Delivery Guarantees
RPCs are sent through Fusion's Notify protocol, which provides ordered, reliable delivery with acknowledgment tracking.
Lost packets are detected and retransmitted.
This means:
- RPCs are guaranteed to arrive (eventually).
- RPCs from the same sender arrive in order.
- Duplicate delivery does not occur.
Common Mistakes
| Mistake | Symptom |
|---|---|
| Using RPC ID < 1024 | Conflicts with SDK internal RPCs |
Not checking IsInternal() |
Processing SDK-internal RPCs as user RPCs |
Sending RPCs before IsRunning() |
RPCs silently dropped |
Not freeing payload Data after Take() |
Memory leak |
| Mismatched read/write order in payload | Corrupted arguments |
Related
- {VersionPath}/manual/architecture -- CRC64 for hash generation
- {VersionPath}/manual/scene-management -- Scene change uses internal RPC
- {VersionPath}/manual/time -- When RPCs are sent and received in the frame loop