Skip to content

Commit

Permalink
NetworkBehaviour updates (#1313) (#1314)
Browse files Browse the repository at this point in the history
  • Loading branch information
jabbacakes authored Jul 29, 2024
1 parent 2bf18a7 commit 0e9d178
Show file tree
Hide file tree
Showing 37 changed files with 228 additions and 224 deletions.
2 changes: 1 addition & 1 deletion docs/advanced-topics/client-anticipation.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ The `OnReanticipate` event can also be used for other purposes, such as "forward

## `OnReanticipate` event

`NetworkBehaviour` has a virtual method called `OnReanticipate`. When server data is received for an `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform`, it's rolled back immediately, setting its anticipated state. During each frame in which a server update for any `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform` is received (after **all** such operations have been performed and **all** objects are rolled back to their server state), each `NetworkObject` that had any rollbacks calls the `OnReanticipate` method on **all** of its `NetworkBehaviour`s.
`NetworkBehaviour` has a virtual method called `OnReanticipate`. When server data is received for an `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform`, it's rolled back immediately, setting its anticipated state. During each frame in which a server update for any `AnticipatedNetworkVariable` or `AnticipatedNetworkTransform` is received (after **all** such operations have been performed and **all** objects are rolled back to their server state), each NetworkObject that had any rollbacks calls the `OnReanticipate` method on **all** of its `NetworkBehaviour`s.

If you need to do any reanticipation to update the anticipated state of any of these variables or transforms, this method is where you will do it. `OnReanticipate` takes as its only parameter a `double` providing the amount of time, in seconds, that the object has been rolled back (which corresponds to the round-trip time of the current batch of responses received from the server). This value can be used to calculate the difference between what the server value is, and what the anticipated client value should be, and apply that change.

Expand Down
4 changes: 2 additions & 2 deletions docs/advanced-topics/inscene_parenting_player.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ public class ParentPlayerToInSceneNetworkObject : NetworkBehaviour
}
```

You should place this script on your in-scene placed `NetworkObject` (that is, the first `GameObject`) and do the parenting from it to avoid any timing issues of when it's spawned or the like. It only runs the script on the server-host side since parenting is server authoritative.
You should place this script on your in-scene placed NetworkObject (that is, the first `GameObject`) and do the parenting from it to avoid any timing issues of when it's spawned or the like. It only runs the script on the server-host side since parenting is server authoritative.


:::note
Remove any parenting code you might have had from your player Prefab before using the above script. Depending upon your project's goals, you might be parenting all players under the same in-scene placed `NetworkObject` or you might intend to have each player parenting unique. If you want each player to be parented under a unique in-scene placed `NetworkObject` then you will need to have the same number of in-scene placed `NetworkObject`s as your maximum allowed players per game session. The above example will only parent all players under the same in-scene placed `NetworkObject`. You can extend the above example by migrating the scene event code into an in-scene placed `NetworkObject` that manages the parenting of players (i,e. name it something like `PlayerSpawnManager`) as they connect, make the `SetPlayerParent` method public, and add all in-scene placed `NetworkObject`s to a public list of GameObjects that the `PlayerSpawnManager` will reference and assign player's to as they connect while also freeing in-scene placed `NetworkObject`s as players disconnect during a game session.
Remove any parenting code you might have had from your player Prefab before using the above script. Depending upon your project's goals, you might be parenting all players under the same in-scene placed NetworkObject or you might intend to have each player parenting unique. If you want each player to be parented under a unique in-scene placed NetworkObject then you will need to have the same number of in-scene placed NetworkObjects as your maximum allowed players per game session. The above example will only parent all players under the same in-scene placed NetworkObject. You can extend the above example by migrating the scene event code into an in-scene placed NetworkObject that manages the parenting of players (i,e. name it something like `PlayerSpawnManager`) as they connect, make the `SetPlayerParent` method public, and add all in-scene placed NetworkObjects to a public list of GameObjects that the `PlayerSpawnManager` will reference and assign player's to as they connect while also freeing in-scene placed NetworkObjects as players disconnect during a game session.
:::

:::important
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-topics/message-system/clientrpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ The host is both a client and a server. If a host invokes a client RPC, it trigg
<ImageSwitcher
lightImageSrc="/sequence_diagrams/RPCs/ClientRPCs_ClientHosts_CalledByClientHost.png?text=LightMode"
darkImageSrc="/sequence_diagrams/RPCs/ClientRPCs_ClientHosts_CalledByClientHost_Dark.png?text=DarkMode"/>
<figcaption>Hosts can invoke client RPCs on `NetworkObjects`. If broadcasting to all clients, the RPC will be immediately invoked on the host and placed in the local queue. At the end of the frame, the client RPC will be sent to the remote clients. When a remote client receives the client RPC it's executed on the client's local cloned instance of the same `NetworkObject`.</figcaption>
<figcaption>Hosts can invoke client RPCs on `NetworkObjects`. If broadcasting to all clients, the RPC will be immediately invoked on the host and placed in the local queue. At the end of the frame, the client RPC will be sent to the remote clients. When a remote client receives the client RPC it's executed on the client's local cloned instance of the same NetworkObject.</figcaption>
</figure>

:::warning
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-topics/message-system/reliabilty.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void MyReliableServerRpc() { /* ... */ }
void MyUnreliableServerRpc() { /* ... */ }
```

Reliable RPCs will be received on the remote end in the same order they are sent, but this in-order guarantee only applies to RPCs on the same `NetworkObject`. Different `NetworkObjects` might have reliable RPCs called but executed in different order compared to each other. To put more simply, **in-order reliable RPC execution is guaranteed per `NetworkObject` basis only**. If you determine an RPC is being updated often (that is, several times per second), it _might_ be better suited as an unreliable RPC.
Reliable RPCs will be received on the remote end in the same order they are sent, but this in-order guarantee only applies to RPCs on the same NetworkObject. Different `NetworkObjects` might have reliable RPCs called but executed in different order compared to each other. To put more simply, **in-order reliable RPC execution is guaranteed per NetworkObject basis only**. If you determine an RPC is being updated often (that is, several times per second), it _might_ be better suited as an unreliable RPC.
:::caution
When testing unreliable RPCs on a local network, the chance of an unreliable packet being dropped is reduced greatly (sometimes never). As such, you might want to use [`UnityTransport`'s Simulator Pipeline](https://docs-multiplayer.unity3d.com/transport/current/pipelines#simulator-pipeline) to simulate poor network conditions to better determine how dropped unreliable RPC messages impacts your project.
:::
Expand Down
6 changes: 3 additions & 3 deletions docs/advanced-topics/message-system/serverrpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class SomeNetworkBehaviour : NetworkBehaviour
}
}
```
The above example uses the default [ServerRpc] attribute settings which only allows a client owner (client that owns `NetworkObject` associated with the `NetworkBehaviour` containing the `ServerRpc` method) invocation rights. Any client that isn't the owner won't be allowed to invoke the `ServerRpc`.
The above example uses the default [ServerRpc] attribute settings which only allows a client owner (client that owns NetworkObject associated with the `NetworkBehaviour` containing the `ServerRpc` method) invocation rights. Any client that isn't the owner won't be allowed to invoke the `ServerRpc`.

## ServerRpc Ownership And ServerRpcParams
There are times where you might want any client to have `ServerRpc` invocation rights. You can easily accomplish this by setting the `ServerRpc` attribute's `RequireOwnership` parameter to false like in the example below:
Expand Down Expand Up @@ -142,14 +142,14 @@ The following are a few timing diagrams to help provide additional visual contex
<ImageSwitcher
lightImageSrc="/sequence_diagrams/RPCs/ServerRPCs.png?text=LightMode"
darkImageSrc="/sequence_diagrams/RPCs/ServerRPCs_Dark.png?text=DarkMode"/>
<figcaption>A Client can invoke a server RPC on a `NetworkObject`. The RPC will be placed in the local queue and then sent to the server at the end of the frame. Upon receiving the server RPC, it's executed on the Server's instance of the same `NetworkObject`.</figcaption>
<figcaption>A Client can invoke a server RPC on a NetworkObject. The RPC will be placed in the local queue and then sent to the server at the end of the frame. Upon receiving the server RPC, it's executed on the Server's instance of the same NetworkObject.</figcaption>
</figure>

<figure>
<ImageSwitcher
lightImageSrc="/sequence_diagrams/RPCs/ServerRPCs_ClientHosts_CalledByClient.png?text=LightMode"
darkImageSrc="/sequence_diagrams/RPCs/ServerRPCs_ClientHosts_CalledByClient_Dark.png?text=DarkMode"/>
<figcaption>Clients can invoke server RPCs on Hosts exactly like they can on a Server: the RPC will be placed in the local queue and sent to the Host at the end of the frame. Upon receiving the server RPC, it will be executed on the Host's instance of the same `NetworkObject`.</figcaption>
<figcaption>Clients can invoke server RPCs on Hosts exactly like they can on a Server: the RPC will be placed in the local queue and sent to the Host at the end of the frame. Upon receiving the server RPC, it will be executed on the Host's instance of the same NetworkObject.</figcaption>
</figure>

<figure>
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-topics/networkobject-parenting.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ If you plan on parenting in-scene placed NetworkObject components with a player

When using the `NetworkObject.TrySetParent` or `NetworkObject.TryRemoveParent` methods, the `WorldPositionStays` parameter is synchronized with currently connected and late joining clients. When removing a child from its parent, use the same `WorldPositionStays` value that you used to parent the child. More specifically, when `WorldPositionStays` is false, this applies. However, if you're using the default value of `true`, this isn't required (because it's the default).

When the `WorldPositionStays` parameter in `NetworkObject.TrySetParent` is the default value of `true`, this will preserve the world space values of the child `NetworkObject` relative to the parent. However, sometimes you might want to only preserve the local space values (pick up an object that only has some initial changes to the child's transform when parented). Through a combination of `NetworkObject.TrySetParent` and `NetworkBehaviour.OnNetworkObjectParentChanged` you can accomplish this without the need for a NetworkTransform component. To better understand how this works, it's important to understand the order of operations for both of these two methods:
When the `WorldPositionStays` parameter in `NetworkObject.TrySetParent` is the default value of `true`, this will preserve the world space values of the child NetworkObject relative to the parent. However, sometimes you might want to only preserve the local space values (pick up an object that only has some initial changes to the child's transform when parented). Through a combination of `NetworkObject.TrySetParent` and `NetworkBehaviour.OnNetworkObjectParentChanged` you can accomplish this without the need for a NetworkTransform component. To better understand how this works, it's important to understand the order of operations for both of these two methods:

**Server-Side**

Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-topics/object-pooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You can register your own spawn handlers by including the `INetworkPrefabInstanc
void Destroy(NetworkObject networkObject);
}
```
Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the `NetworkObject` used during spawning and despawning. Because the message to instantiate a new `NetworkObject` originates from a Host or Server, both won't have the Instantiate method invoked. All clients (excluding a Host) will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and a Host or Server spawns the registered/associated `NetworkObject`.
Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the NetworkObject used during spawning and despawning. Because the message to instantiate a new NetworkObject originates from a Host or Server, both won't have the Instantiate method invoked. All clients (excluding a Host) will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and a Host or Server spawns the registered/associated NetworkObject.

The following example is from the Boss Room Sample. It shows how object pooling is used to handle the different projectile objects. In that example, the class `NetworkObjectPool` is the data structure containing the pooled objects and the class `PooledPrefabInstanceHandler` is the handler implementing `INetworkPrefabInstanceHandler`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ description: Brief explanation on using NetworkObject and NetworkBehaviour in Ne

`GameObjects`, `NetworkObjects` and `NetworkBehaviour` aren't serializable types so they can't be used in `RPCs` or `NetworkVariables` by default.

There are two convenience wrappers which can be used to send a reference to a `NetworkObject` or a `NetworkBehaviour` over RPCs or `NetworkVariables`.
There are two convenience wrappers which can be used to send a reference to a NetworkObject or a `NetworkBehaviour` over RPCs or `NetworkVariables`.

## NetworkObjectReference

`NetworkObjectReference` can be used to serialize a reference to a `NetworkObject`. It can only be used on already spawned `NetworkObjects`.
`NetworkObjectReference` can be used to serialize a reference to a NetworkObject. It can only be used on already spawned `NetworkObjects`.

Here is an example of using `NetworkObject` reference to send a target `NetworkObject` over an RPC:
Here is an example of using NetworkObject reference to send a target NetworkObject over an RPC:
```csharp
public class Weapon : NetworkBehaviour
{
Expand Down Expand Up @@ -56,12 +56,12 @@ public class Weapon : NetworkBehaviour
}
```
:::note
The implicit conversion to `NetworkObject` / `GameObject` will result in `Null` if the reference can't be found.
The implicit conversion to NetworkObject / `GameObject` will result in `Null` if the reference can't be found.
:::

## NetworkBehaviourReference

`NetworkBehaviourReference` works similar to `NetworkObjectReference` but is used to reference a specific `NetworkBehaviour` component on a spawned `NetworkObject`.
`NetworkBehaviourReference` works similar to `NetworkObjectReference` but is used to reference a specific `NetworkBehaviour` component on a spawned NetworkObject.

```cs
public class Health : NetworkBehaviour
Expand Down Expand Up @@ -90,6 +90,6 @@ public class Weapon : NetworkBehaviour

## How NetworkObjectReference & NetworkBehaviourReference work

`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a `NetworkObject` when being sent and on the receiving end retrieve the corresponding ` ` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right `NetworkBehaviour` on the `NetworkObject`.
`NetworkObjectReference` and `NetworkBehaviourReference` are convenience wrappers which serialize the id of a NetworkObject when being sent and on the receiving end retrieve the corresponding ` ` with that id. `NetworkBehaviourReference` sends an additional index which is used to find the right `NetworkBehaviour` on the NetworkObject.

Both of them are structs implementing the `INetworkSerializable` interface.
2 changes: 1 addition & 1 deletion docs/advanced-topics/session-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ You can also decide to clear all data when a session completes or add a timeout

The best way to reconnect players depends on your game. For example, if you use a [Player Object](../basics/networkobject.md#player-objects), a new `Default Player Prefab` automatically spawns when a player connects to the game (including when they reconnect). You can use the player's earlier saved session data to update that object so that it returns to the same state before disconnecting. In those cases, you would need to keep all the important data that you want to restore and map it to the player using your identification system. You can save this data when a player disconnects or update it periodically. You can then use the `OnNetworkSpawn` event on the Player Object's `NetworkBehavior`(s) to get this data and apply it where needed.

In cases where we don't use the Player Object approach and instead manually attribute client ownership to `NetworkObject`(s), we can keep the objects that a player owns when they disconnect, and set the reconnected player as their new owner. To accomplish this, the only data we would need to keep would be the mapping between those objects and their owning player's identifier, then when a player reconnects we can use this mapping to set them as the new owner. This mapping can be as simple as a dictionary mapping the player identifier with the `NetworkObjectId`(s) of the `NetworkObject`(s) they own. Then, in the `OnClientConnectedCallback` from the `NetworkManager`, the server can set the ownership of these objects.
In cases where we don't use the Player Object approach and instead manually attribute client ownership to NetworkObject(s), we can keep the objects that a player owns when they disconnect, and set the reconnected player as their new owner. To accomplish this, the only data we would need to keep would be the mapping between those objects and their owning player's identifier, then when a player reconnects we can use this mapping to set them as the new owner. This mapping can be as simple as a dictionary mapping the player identifier with the `NetworkObjectId`(s) of the NetworkObject(s) they own. Then, in the `OnClientConnectedCallback` from the `NetworkManager`, the server can set the ownership of these objects.

Here is an example from the Boss Room sample, showing some simple session management. The game uses the Player Object approach and a GUID to identify unique players.

Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-topics/ways-to-synchronize.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The Netcode for GameObjects messaging system allows you to send and receive mess

### Remote procedure calls (RPCs)

RPCs are a way of sending an event notification as well as a way of handling direct communication between a server and a client, or between clients and the [distributed authority service](../terms-concepts/distributed-authority.md). This is sometimes useful when the ownership scope of the `NetworkBehavior`, that the remote procedure call is declared within, belongs to the server but you still want one or more clients to be able to communicate with the associated `NetworkObject`.
RPCs are a way of sending an event notification as well as a way of handling direct communication between a server and a client, or between clients and the [distributed authority service](../terms-concepts/distributed-authority.md). This is sometimes useful when the ownership scope of the `NetworkBehavior`, that the remote procedure call is declared within, belongs to the server but you still want one or more clients to be able to communicate with the associated NetworkObject.

Usage examples:

Expand Down
Loading

0 comments on commit 0e9d178

Please sign in to comment.