Skip to content

Commit a823723

Browse files
committed
Add invocation order documentation
1 parent b3ea00a commit a823723

File tree

3 files changed

+285
-26
lines changed

3 files changed

+285
-26
lines changed

com.unity.netcode.gameobjects/Documentation~/advanced-topics/message-system/rpc.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,116 @@ There are a few other parameters that can be passed to either the `Rpc` attribut
215215
| `Target` | Runtime override destination for the RPC. (See above for more details.) Populating this value will throw an exception unless either the `SendTo` value for the RPC is `SendTo.SpecifiedInParams`, or `AllowTargetOverride` is `true`.<br /><br />Default: `null` |
216216
| `LocalDeferMode` | Overrides the `DeferLocal` value. `DeferLocalMode.Defer` causes this particular invocation of this RPC to be deferred until the next frame even if `DeferLocal` is `false`, while `DeferLocalMode.SendImmediate` causes the RPC to be executed immediately on the local machine even if `DeferLocal` is `true`. `DeferLocalMode.Default` does whatever the `DeferLocal` value in the attribute is configured to do.<br /><br />Options: `DeferLocalMode.Default`, `DeferLocalMode.Defer`, `DeferLocalMode.SendImmediate`<br />Default: `DeferLocalMode.Default` |
217217

218+
## Invocation order
219+
220+
Rpc message sent with `RpcDelivery.Reliable` will be sent and invoked on other game clients in the same order as they were called on the local game client.
221+
222+
```csharp
223+
[Rpc(SendTo.Server)]
224+
public void OpenDoorRpc(int doorId, RpcParams rpcParams)
225+
{
226+
Debug.Log($"client {rpcParams.Receive.SenderClientId} has opened door {doorId}");
227+
228+
// Server can handle door opening here
229+
}
230+
231+
[Rpc(SendTo.Server)]
232+
void OpenChestRPC(int chestId, RpcParams rpcParams)
233+
{
234+
Debug.Log($"client {rpcParams.Receive.SenderClientId} has opened chest {chestId}");
235+
236+
// Server can handle door opening here
237+
}
238+
239+
void Update()
240+
{
241+
if (IsClient && Input.GetKeyDown(KeyCode.O))
242+
{
243+
OpenDoorRpc(1)
244+
OpenDoorRpc(2)
245+
OpenChestRpc(5)
246+
}
247+
248+
// Other clients will log:
249+
//
250+
// "client 1 has opened door 1"
251+
// "client 1 has opened door 2"
252+
// "client 1 has opened chest 5"
253+
}
254+
```
255+
256+
> [!Warning]
257+
> Invocation order is not guaranteed with nested RPC invocations that include targets that may invoke locally. Invocation order is also not guaranteed when using `RpcDelivery.Unreliable`
258+
259+
### Deferring local invocation
260+
261+
Invoking an RPC from within another RPC introduces the risk that the local RPC may invoke before messages are sent to other game clients. This will result in the RPC message for the inner RPC invocation being sent before the message for the outer RPC.
262+
263+
```csharp
264+
[Rpc(SendTo.Everyone)]
265+
public void TryOpenDoorRpc(int doorId, RpcParams rpcParams)
266+
{
267+
Debug.Log($"client {rpcParams.Receive.SenderClientId} is trying to open door {doorId}");
268+
269+
if (HasAuthority) {
270+
// Authority handles opening the door here
271+
272+
// If the authority is invoking TryOpenDoorRpc locally before the authority has sent TryOpenDoorRpc to other clients, OpenDoorRpc will be sent before TryOpenDoorRpc.
273+
OpenDoorRpc(doorId);
274+
}
275+
}
276+
277+
[Rpc(SendTo.Everyone)]
278+
public void OpenDoorRpc(int doorId, RpcParams rpcParams)
279+
{
280+
Debug.Log($"client {rpcParams.Receive.SenderClientId} marked door {doorId} as open");
281+
}
282+
283+
void Update()
284+
{
285+
if (Input.GetKeyDown(KeyCode.O))
286+
{
287+
// Invocation of TryOpenDoorRpc and OpenDoorRpc may be inverted depending on the context in which TryOpenDoorRpc is invoked
288+
TryOpenDoorRpc(20);
289+
}
290+
}
291+
```
292+
293+
Use the RPC `LocalDeferMode` to resolve issue. Configuring the RPC to be deferred when invoked locally will ensure that any outer RPC messages are always sent before the inner function is invoked.
294+
295+
```csharp
296+
// An RPC can be configured to defer the local invocation in the attribute definition
297+
[Rpc(SendTo.Everyone, DeferLocal = true)]
298+
public void TryOpenDoorRpc(int doorId, RpcParams rpcParams = default)
299+
{
300+
Debug.Log($"client {rpcParams.Receive.SenderClientId} is trying to open door {doorId}");
301+
302+
if (HasAuthority) {
303+
304+
// Authority handles opening the door here
305+
306+
// Defer mode can also be passed in at the call site.
307+
OpenDoorRpc(doorId, LocalDeferMode.Defer);
308+
}
309+
}
310+
311+
[Rpc(SendTo.Everyone)]
312+
public void OpenDoorRpc(int doorId, RpcParams rpcParams = default)
313+
{
314+
Debug.Log($"client {rpcParams.Receive.SenderClientId} marked door {doorId} as open");
315+
}
316+
317+
void Update()
318+
{
319+
if (Input.GetKeyDown(KeyCode.O))
320+
{
321+
// TryOpenDoorRpc is defined with DeferLocal
322+
// DeferLocal ensures that RPC messages are sent to all other targets before the RPC is invoked locally
323+
TryOpenDoorRpc(20);
324+
}
325+
}
326+
```
327+
218328
## Additional resources
219329

220330
* [RPC parameters](rpc-params.md)

com.unity.netcode.gameobjects/Runtime/Messaging/RpcParams.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ namespace Unity.Netcode
99
public enum LocalDeferMode
1010
{
1111
/// <summary>
12-
/// Uses the default behavior for RPC message handling
12+
/// Uses the default behavior for RPC message handling.
13+
/// The default behavior is <see cref="SendImmediate"/>.
1314
/// </summary>
15+
/// <remarks>
16+
/// If <see cref="RpcAttribute.RpcAttributeParams.DeferLocal"/> is enabled, the behavior of this field wil change to <see cref="Defer"/>.
17+
/// </remarks>
1418
Default,
1519

1620
/// <summary>
@@ -25,7 +29,7 @@ public enum LocalDeferMode
2529
}
2630

2731
/// <summary>
28-
/// Generic RPC. Defines parameters for sending Remote Procedure Calls (RPCs) in the network system
32+
/// Defines parameters for sending Remote Procedure Calls (RPCs) in the network system
2933
/// </summary>
3034
public struct RpcSendParams
3135
{
@@ -55,31 +59,29 @@ public struct RpcSendParams
5559
}
5660

5761
/// <summary>
58-
/// The receive parameters for server-side remote procedure calls
62+
/// The receive parameters for an RPC call
5963
/// </summary>
6064
public struct RpcReceiveParams
6165
{
6266
/// <summary>
63-
/// Server-Side RPC
64-
/// The client identifier of the sender
67+
/// The sender's client identifier
6568
/// </summary>
6669
public ulong SenderClientId;
6770
}
6871

6972
/// <summary>
70-
/// Server-Side RPC
71-
/// Can be used with any sever-side remote procedure call
72-
/// Note: typically this is use primarily for the <see cref="ServerRpcReceiveParams"/>
73+
/// Parameters for an RPC call
74+
/// Can be used with any remote procedure call
7375
/// </summary>
7476
public struct RpcParams
7577
{
7678
/// <summary>
77-
/// The server RPC send parameters (currently a place holder)
79+
/// The RPC send parameters (currently a place holder)
7880
/// </summary>
7981
public RpcSendParams Send;
8082

8183
/// <summary>
82-
/// The client RPC receive parameters provides you with the sender's identifier
84+
/// The RPC receive parameters provides you with the sender's identifier
8385
/// </summary>
8486
public RpcReceiveParams Receive;
8587

0 commit comments

Comments
 (0)