新增FishNet文档

This commit is contained in:
2026-09-20 15:53:11 +08:00
parent c403145db9
commit 179c282405
431 changed files with 15476 additions and 0 deletions
@@ -0,0 +1,2 @@
# Networked GameObjects and Scripts
@@ -0,0 +1,240 @@
---
description: >-
NetworkBehaviours are a fundamental part of networking which allow you to
easily synchronize data and access network related information.
---
# NetworkBehaviour
When inheriting from [NetworkBehaviours ](../../../fishnet-building-blocks/components/network-behaviour-components/)you are indicating that your script will utilize the network in some way. Once a NetworkBehaviour script is added to an object the [NetworkObject](../../../fishnet-building-blocks/components/network-object.md) component will automatically be attached.
NetworkBehaviours are essential for [Remote Procedure Calls](../network-communication/remote-procedure-calls.md), [Synchronizing](../network-communication/synchronizing/), and having access to vital network information.
***
## Properties <a href="#properties" id="properties"></a>
There are several public properties available within NetworkBehaviour, many of which you will use regularly. Most of the properties are only available after the object has been initialized. See the [callbacks](network-behaviour-guides.md#server-and-host-1) section for more information on initialization.
Commonly used properties are:
* **IsClientInitialized** will be true if acting as a client and object is network initialized.
* **IsServerInitialized** will be true if acting as the server and object is network initialized.
* **IsOwner** is true if you are a client, and the owner of the object.
* **HasAuthority** is true if you are the owner, or if server and there is no owner.
To view all properties please visit the [NetworkBehaviour API](https://fish-networking.com/FishNet/api/api/FishNet.Object.NetworkBehaviour.html#properties).
{% hint style="info" %}
When accessing a NetworkBehaviour property or method consider using the 'base' keyword to show your intentions.
For example, base.IsOwner
{% endhint %}
***
## Callbacks <a href="#callbacks" id="callbacks"></a>
{% embed url="https://www.youtube.com/watch?v=goH4CX-aG-E" %}
{% hint style="success" %}
You do not need to include the base.XYZ() calls when implementing callbacks.
{% endhint %}
Like Properties, there are a large number of available methods. The methods you will be using commonly are known as callbacks. If you are interested in all available methods view the [methods section under API](https://fish-networking.com/FishNet/api/api/FishNet.Object.NetworkBehaviour.html#methods).
Most callbacks have a server and client version, and each has great potential. To use a callback you must inherit from NetworkBehaviour and override the callback you wish to use.
```csharp
// Example of implementing an override.
public override void OnStartServer()
{
// This is run when the server initializes the object.
}
```
The execution order of spawn related callbacks is constant, but some callbacks may occur at any time. Included is a chart to help you remember callback order.
{% hint style="warning" %}
Unity Awake and OnEnable callbacks will always occur before any network activity.
{% endhint %}
<figure><picture><source srcset="../../../.gitbook/assets/network-behaviour-callbacks-dark.svg" media="(prefers-color-scheme: dark)"><img src="../../../.gitbook/assets/network-behaviour-callbacks-light.svg" alt=""></picture><figcaption><p>Created by Winterbolt</p></figcaption></figure>
To begin we will cover the server side callbacks.
### **OnStartNetwork**
In some instances you will need to initialize for both server and client. You can save some code and time by using OnStartNetwork instead of the OnStart for Client and Server. It's important to remember that OnStartNetwork will only call once, even if you are clientHost.
```csharp
public override void OnStartNetwork()
{
/* If you wish to check for ownership inside
* this method do not use base.IsOwner, use
* the code below instead. This difference exist
* to support a clientHost condition. */
if (base.Owner.IsLocalClient)
SetupCamera();
}
```
### **OnStartServer**
```csharp
public override void OnStartServer()
{
/* This callback is performed first. It occurs
* when the object is initialized on the server.
* Once called Owner, ObjectId, and much more are
* already set.
* OnStartServer is best used for initializing
* server aspects of your script, such as getting
* components only the server would need.
* It can also be useful for setting values based on
* the current state of your game. If you change synchronized
* values within this method, such as SyncTypes,
* those changes will be delivered to clients
* when this object spawns for them.
* Eg: perhaps you want to set a players name, which is
* a SyncVar. You can do that here and it will be set
* for clients when the object spawns on their side. */
/* When using OnStartServer keep in mind that observers have
* not yet been built for this object. If you were to send an ObserversRpc
* for example it would not be delivered to any clients. You can however
* still use an ObserversRpc and set BufferLast to true if you wish
* clients to get it when the object is spawned for them. Another option
* is to use OnSpawnServer, displayed below, and send a TargetRpc to the
* connection which the object is spawning. */
}
```
{% hint style="info" %}
When clientHost base.IsOwner will not return true, even with the owner set. This is because IsOwner is a client-side check, and the client has not yet initialized the object.
When you need to check 'IsOwner' on OnStartServer use **base.Owner.IsLocalClient**.
{% endhint %}
### **OnOwnershipServer**
```csharp
public override void OnOwnershipServer(NetworkConnection prevOwner)
{
/* This is received when the server is giving ownership
* to a client. If a client is gaining
* ownership when the object is spawned OnOwnershipServer
* will be called immediately after OnStartServer. prevOwner
* is the connection which just lost ownership. If ownership is changed
* at runtime this callback also occurs. */
}
```
### **OnSpawnServer**
```csharp
public override void OnSpawnServer(NetworkConnection connection)
{
/* This callback occurs after a spawn message for this
* object has been sent out to a client. For example: if
* this object will be visible to five clients, then this
* callback will occur five times, where the connection
* parameter will be for each client.
* Primarily you will use this callback to send
* tailored communications to the client
* the object is being spawned for. */
}
```
### **OnDespawnServer**
```csharp
public override void OnDespawnServer(NetworkConnection connection)
{
/* OnDespawnServer is similar to OnSpawnServer, except this is
* called right before an object is despawned for a client.
* This method can be used to send information to the client
* before they receive the despawn message. You may even
* send object orientated communications for this object
* such as SyncTypes, and RPCs. */
}
```
{% hint style="info" %}
When the server despawns a NetworkObject any pending synchronize changes will be sent out with the despawn message. This ensures clients will get the latest data, even if timed, before the object is despawned for them.
{% endhint %}
### **OnStopServer**
```csharp
public override void OnStopServer()
{
/* This is the last callback for server side.
* OnStopServer will be called immediately before
* the object is deinitialized. At this time it is
* too late to send communications specific to this
* object for clients.
* This callback may be used for any number of things but
* could be useful for resetting synchronized values
* such as SyncTypes when a scene object is despawned. */
}
```
Next are the client callbacks. These callbacks will always occur after the server callbacks, even when acting as host. For example, a callback sequence might look like this: OnStartServer, OnOwnershipServer, OnSpawnServer, OnStartClient, OnOwnershipClient.
For the most part client callbacks are the same as the server ones, except they occur only if the client connection is started. Only key differences will be included in the descriptions of each callback.
### **OnStartClient**
```csharp
public override void OnStartClient()
{
/* This is called on each client when the object
* becomes visible to them. Networked values such as
* Owner, ObjectId, and SyncTypes will already be
* synchronized prior to this callback. */
}
```
{% hint style="info" %}
Any buffered remote procedure calls will occur immediately after OnStartClient.
{% endhint %}
{% hint style="info" %}
If you are coming from Mirror instead of using OnLocalPlayer use OnStartClient with a base.IsOwner check.
{% endhint %}
### **OnOwnershipClient**
```csharp
public override void OnOwnershipClient(NetworkConnection prevOwner)
{
/* Current owner can be found by using base.Owner. prevOwner
* contains the connection which lost ownership. Value will be
* -1 if there was no previous owner. */
}
```
### **OnStopClient**
```csharp
public override void OnStopClient()
{
/* Like the server variant this is called right
* before the object is deinitialized. It is too
* late to send communications to the server
* for this object after this callback has occurred. */
}
```
### **OnStopNetwork**
Like OnStartNetwork, this method can be used to save lines of code when needing to deinitialize for both server and client stop. OnStopNetwork will also only be called once even when clientHost.
```csharp
public override void OnStopNetwork()
{
/* This will call after OnStopServer
* and OnStopClient. */
}
```
@@ -0,0 +1,43 @@
---
description: >-
Details on the different types of NetworkObjects that will be referenced
throughout the guides.
---
# NetworkObjects
## NetworkObject
Any GameObject with a [**NetworkObject**](../../../../fishnet-building-blocks/components/network-object.md) component on it will be considered a "NetworkObject" in these guides going forward.\
\
Review the [**NetworkObject**](../../../../fishnet-building-blocks/components/network-object.md) component Page for details on the various settings for a **NetworkObject.**
When you are to add a [**NetworkBehaviour**](../../../../fishnet-building-blocks/components/network-behaviour-components/README.md) component to your prefabs or scene objects the NetworkBehaviour will search for a NetworkObject component on the same object, or within parent objects. If a NetworkObject is not found then one will be added automatically to the top-most object.
## Spawned NetworkObject
NetworkObjects that are Instantiated and Spawned using ther ServerManager.Spawn() method will be considered a "Spawned NetworkObject".\
\
"IsSpawned" Property will be marked True internally on the NetworkObject Component.
## Scene NetworkObject
Any **NetworkObject** that exists as part of the Scene aka - never instantiated/spawned into the scene, will be considered a "**Scene NetworkObject**" in these guides going forward.\
\
"IsSceneObject" property will be marked true on the attached [**NetworkObject**](../../networked-gameobjects-and-scripts/networkobjects/README.md/) component internally.
## Global NetworkObject
Any **NetworkObject** with their bool "IsGlobal" marked "true" either in the inspector or with code will be considered a "**GlobalNetworkObject**" in the guides going forward.
**GlobalNetworkObjects** will automatically be put into the ("DontDestroyOnLoad") scene when instantiated on the server, and when spawned on the clients.
{% hint style="info" %}
Scene objects cannot be marked as global. All global objects must be instantiated and spawned.
{% endhint %}
## Nested NetworkObject
Any [**NetworkObject**](../../networked-gameobjects-and-scripts/networkobjects/README.md) that are a child of another **NetworkObject** will considered a "**Nested NetworkObject**" in these guides going forward.\
\
"IsNested" property will be marked true on the attached **NetworkObject** component internally.
@@ -0,0 +1,44 @@
# Nested NetworkObjects
When a NetworkObject is beneath another NetworkObject it is considered nested. Fish-Networking allows you to nest NetworkObjects both in the scene and prefabs.
While spawning a root NetworkObject any active nested NetworkObjects will also be spawned. The nested NetworkObjects will experience the same callbacks as root, and share the same information such as IsOwner.
You may also have nested NetworkObjects deactivated in the scene or prefab and spawn them later.
{% hint style="warning" %}
When a nested NetworkObject is spawned after the root has already been spawned, the owner information is not automatically assumed the same as the root. You must specify if a client should gain ownership while calling Spawn.
{% endhint %}
Between instantiating the prefab and spawning the root NetworkObject you may make changes to nested NetworkObjects. This includes SyncTypes, and even the active state of the object. These changes are automatically synchronized when the root is spawned.
For example, if my prefab looks like this:
![](../../../../.gitbook/assets/nested-network-enabled.png)
But I change the activate state of _NestedNob_ to disabled before server spawning as such:
![](../../../../.gitbook/assets/nested-network-disabled.png)
RootNob will be spawned over the network with NestedNob disabled / not spawned.
```csharp
// An example of changing the enabled state for NestedNob.
public GameObject MyPrefab;
private void SpawnPrefab()
{
GameObject go = Instantiate(MyPrefab);
GameObject nestedNob = go.transform.GetChild(0);
nestedNob.SetActive(false);
// MyPrefab will spawn with NestedNob disabled for server and all clients
// until you spawn it at a later time.
base.Spawn(go);
}
```
{% hint style="warning" %}
Nested NetworkObjects may be spawned and despawned but they should not be detached. This feature is being examined for a future update.
There is an exception for nested scene NetworkObjects; they may be detached.
{% endhint %}
@@ -0,0 +1,57 @@
---
description: Information about how to spawn and despawn objects on the network.
---
# Spawning and Despawning
For objects to exist over the network they must have a NetworkObject component on them, and **must be spawned using the server**. To spawn an object it must first be instantiated, and then passed into a Spawn method. There are a variety of Spawn methods to access for your convenience, all of which provide the same outcome.
{% hint style="warning" %}
Networked addressable prefabs must be registered with the NetworkManager differently. See [Addressables](../../addressables.md) guide for more information on this.
{% endhint %}
**Spawning without an owner** is done by passing in _null_ for the owner, or simply leaving the argument out.
```csharp
GameObject go = Instantiate(_yourPrefab);
InstanceFinder.ServerManager.Spawn(go, null);
```
```csharp
GameObject go = Instantiate(_yourPrefab);
InstanceFinder.ServerManager.Spawn(go);
```
**Spawning with ownership**
```csharp
GameObject go = Instantiate(_yourPrefab);
InstanceFinder.ServerManager.Spawn(go, ownerConnection);
```
You may also access the spawn method within any script that inherits NetworkBehaviour, or by accessing the NetworkObject.
```csharp
GameObject go = Instantiate(_yourPrefab);
base.Spawn(go, ownerConnection); // networkBehaviour.
// or
networkObject.Spawn(go, ownerConnection); // referencing a NetworkObject.
```
**Despawning** can be accessed the same ways as spawning. Through a NetworkBehaviour script, a reference NetworkObject, or through the ServerManager directly.
```csharp
base.Despawn(); // networkBehaviour. Despawns the NetworkObject.
networkObject.Despawn(); // referencing a NetworkObject.
InstanceFinder.ServerManager.Despawn(gameObject); // through ServerManager.
```
When despawning you may also choose to pool the object rather than destroy it. There are optional parameters available to change this behavior.
```csharp
base.Despawn(DespawnType.Pool); // pools the object instead of destroying it.
```
You can check if an object is spawned at anytime using the _IsSpawned_ property within NetworkBehaviour, or NetworkObject.
**Scene Objects** are spawned and despawned like instantiated objects, except you pass in the reference of the already instantiated/placed scene object. A scene object becomes disabled rather than destroyed when despawned.
@@ -0,0 +1,94 @@
---
description: Information about how to use object pooling for networked objects.
---
# Object Pooling
Fish-Networking has built-in functionality for Object pooling that will allow the server and client to keep instances of loaded prefabs in memory for later use. This could potentially provide better spawning performance for clients and server.
## General
When Despawning [Spawned NetworkObjects](../networkobjects/#spawned-networkobject) using FishNet, instead of destroying the object you may just want to disable it and store it to be used again at a later time. This describes what Object Pooling is.\
\
FishNet has a default implementation that will allow the objects you have instantiated to be disabled and pooled, instead of being destroyed. This functionality works for clients and server.\
\
You can also Pre-warm assets for later use, which is discussed later in this guide.
{% hint style="warning" %}
Reminder that [Scene NetworkObjects](../networkobjects/#scene-networkobject) do not get added to the Object Pool and are already disabled instead of destroyed when despawned.
{% endhint %}
## Setup
As mentioned on the [NetworkManager](../../../../fishnet-building-blocks/components/managers/network-manager.md) component page, there is an assignable field labeled _ObjectPoo&#x6C;**.**_ You may assign any script which inherits from the **ObjectPool** class.\
\
By Default when examining the NetworkManager in the Editor nothing will be assigned to this field, however when you enter play mode the NetworkManager will automatically populate it with the default implementation, and attach the script to the NetworkManager GameObject.
See below for screen captures of the NetworkManager.
<figure><img src="../../../../.gitbook/assets/object-pool-unassigned.png" alt="Editor Mode"><figcaption><p>Editor Mode - Nothing will be assigned, unless you implement your own Object Pool</p></figcaption></figure>
<figure><img src="../../../../.gitbook/assets/object-pool-automatically-assigned.png" alt=""><figcaption><p>Play Mode - Default Object Pool automatically assigned</p></figcaption></figure>
{% hint style="info" %}
By default the object pool is enabled, but your network objects will only use the pool if the default despawn behavior is modified, or through the despawn call. See below for examples to both of these.
{% endhint %}
### Default despawn behavior
On the [NetworkObject](../../../../fishnet-building-blocks/components/network-object.md) component you can set what the default despawn behavior is for the object where the script is placed.\
\
This setting is set to "Destroy" by default, so make sure to switch this over to "Pool" if you want Fish-Networking to automatically use the default object pool.
<figure><img src="../../../../.gitbook/assets/network-object-despawn-type-pool.png" alt=""><figcaption></figcaption></figure>
### Manual despawn behavior
You can manually change the despawn behavior through code for specific situations.
```csharp
// When calling FishNet's Despawn method from any location, you can pass an enum
// parameter to deviate from the default behavior.
ServerManager.Despawn(nob, DespawnType.Pool);
```
### Spawning NetworkObjects
When using the object pool you will want to retrieve NetworkObject from it prior to network spawning them. Doing so will pull from the pool rather than instantiate new objects.
```csharp
// There are many overrides which allow for a variety of information.
// You can use GameObjects, NetworkObjects, PrefabIds, CollectionIds,
// spawn positions, and more.
NetworkObject nob = NetworkManager.GetPooledInstantiated(...);
// Spawn normally.
ServerManager.Spawn(nob);
```
If you are certain you do not wish to use the object pool on a specific object you can still use the code above and simply set the Default Despawn Type to Destroy on the NetworkObject, or Instantiate and spawn normally.
## Pre-Warming the ObjectPool
If you want to manually store Network Objects to the ObjectPool prior to needing them at run-time you may do so through the NetworkManager API.
Here is a very basic implementation of pre-warming the ObjectPool.
```csharp
[SerializeField]
private NetworkObject _nobPrefab;
private void Start()
{
/// <summary>
/// Instantiates a number of objects and adds them to the pool.
/// </summary>
/// <param name="prefab">Prefab to cache.</param>
/// <param name="count">Quantity to spawn.</param>
/// <param name="asServer">True if storing prefabs for the server collection.</param>
InstanceFinder.NetworkManager.CacheObjects(_nobPrefab, 100, IsServer);
}
```
## Custom implementation
FishNet allows for the user to implement their own method of object pooling. First create your own class inheriting from the **ObjectPool** class. Place your new class component in your scene, typically directly on the NetworkManager object. Then assign your component to the _ObjectPool_ field on the NetworkManager.
@@ -0,0 +1,33 @@
---
description: >-
Predicted spawning an object allows a client to spawn an object locally and
begin using networked features immediately, including RPCs.
---
# Predicted Spawning
{% hint style="warning" %}
Currently predicted spawns cannot be nested during the spawning process. We are looking to remove this limitation throughout the development of version 4.
{% endhint %}
The term predicted spawning also means predicted despawning, where the client may despawn the object locally while awaiting for a server response.
### Global settings
By default predicted spawning is disabled. To enable predicted spawning you must adjust settings on your ServerManager. If you do not already have a ServerManager then you must add one to your NetworkManager object to change settings.
The enabled state of this feature can be set by checking **Allow Predicted Spawning** on the ServerManager. You may also adjust the **Reserved Object Ids** value. To learn more of these visit the [ServerManager component](../../../../fishnet-building-blocks/components/managers/server-manager.md) section.
### Settings per object
Even with predicted spawning enabled globally NetworkObjects must be set to also allow predicted spawning or despawning. This ensures that only the objects you specify may use this feature, allows you to limit predicted spawning the capabilities for the object, and lets you use custom code to further validate or control predicted spawning.
For prefabs or scene objects you wish to allow predicted spawning on add the [PredictedSpawn component](../../../../fishnet-building-blocks/components/prediction/predictedspawn.md) to them.
{% hint style="info" %}
You can inherit the PredictedSpawn component for overrides to customize predicted spawning.
{% endhint %}
### Using Predicted Spawning
Once enabled globally and per your desired objects predicted spawning and despawning may be used on clients as though they were the server. All of the same spawn and despawn methods work, including even specifying who may own the object when spawned.
@@ -0,0 +1,141 @@
---
description: >-
Spawn Payloads allow the sending additional information along with an object's
Spawn call.
---
# Spawn Payloads
[NetworkBehaviours](../network-behaviour-guides.md) have support for writing and reading extra data and sending it along with the spawn payloads for the network object. This can be quite useful for syncing an initial state of the object.
To use this, you need to override the NetworkBehaviour's `WritePayload` and `ReadPayload` methods.
{% hint style="warning" %}
When writing and reading, the order and amount of data written and read is important and **MUST** be the same.
{% endhint %}
## Write Payload
The first parameter of the **WritePayload** method needs to be a [NetworkConnection](../../server-and-client-identification/networkconnections.md), this represents which client the spawn is going to happen for and is provided by FishNet when it calls the method.
The second parameter should be of the type [Writer](https://fish-networking.com/FishNet/api/api/FishNet.Serializing.Writer.html), this will represent a reference to the Writer being used and you will use it to add data to the payload.
For example you can do the following:
```csharp
public override void WritePayload(NetworkConnection connection, Writer writer)
{
// Writes the string stored in the class variable _playerName to the spawn payload.
writer.WriteString(_playerName);
// Writes the integer 42 to the payload.
writer.WriteInt32(42);
}
```
{% hint style="success" %}
You can view the API for this method [here](https://fish-networking.com/FishNet/api/api/FishNet.Object.NetworkBehaviour.html#FishNet_Object_NetworkBehaviour_WritePayload_FishNet_Connection_NetworkConnection_FishNet_Serializing_Writer_).
{% endhint %}
## Read Payload
The first parameter of the **ReadPayload** method needs to be a [NetworkConnection](../../server-and-client-identification/networkconnections.md), this represents which client the spawn is happening for and is provided by FishNet when it calls the method.
The second parameter should be of the type [Reader](https://fish-networking.com/FishNet/api/api/FishNet.Serializing.Reader.html), this will represent a reference to the Reader being used and you will use it to get data out of the payload.
For example you can do the following:
```csharp
public override void ReadPayload(NetworkConnection connection, Reader reader)
{
// Reads a string from the payload. Since a string was written first, it must be read first too.
_playerName = reader.ReadStringAllocated();
// Reads an integer from the payload.
_playerScore = reader.ReadInt32();
}
```
{% hint style="success" %}
You can view the API for this method [here](https://fish-networking.com/FishNet/api/api/FishNet.Object.NetworkBehaviour.html#FishNet_Object_NetworkBehaviour_ReadPayload_FishNet_Connection_NetworkConnection_FishNet_Serializing_Reader_).
{% endhint %}
## More advanced example
{% code title="SpawnPayloadExample.cs" lineNumbers="true" %}
```csharp
public class SpawnPayloadExample : NetworkBehaviour
{
private ArraySegment<byte> _cachedPayload = ArraySegment<byte>.Empty;
public override void WritePayload(NetworkConnection connection, Writer writer)
{
/* If you are using predicted spawning and want to write the
* payload if you are the spawning client, this might help! */
bool isPredictedSpawner = NetworkObject.PredictedSpawner.IsLocalClient;
/* It is worth noting that the server will still call WritePayload when
* spawning the object for clients, even if a predicted spawn.
* Should a client send a predicted spawn the server will receive ReadPayload
* for the predicted spawn, and the server will also be given an opportunity to
* send a payload to other clients which are not the predicted spawner. If you wish to
* forward a payload from a predicted spawner cache the values locally, and
* send them here.
*
* Here's an example of what that might look like. */
if (isPredictedSpawner)
{
// As the predicted spawner let's write our own payload!
}
// If not the predicted spawner, we are the server sending a payload to other clients.
else
{
// Write our cached payload, or don't, send whatever you want!
// Check if not empty first.
if (_cachedPayload != ArraySegment<byte>.Empty)
writer.WriteArraySegmentAndSize(_cachedPayload);
}
}
public override void ReadPayload(NetworkConnection connection, Reader reader)
{
/* You can also check if the payload is coming from the predicted spawner as well.
*
* ReadPayload would be first called on the server, and if the connection IsValid, we know
* that the connection is a predicted spawner, as that is the only time connection would be
* valid on the server. */
bool isFromPredictedSpawner = IsServerStarted && connection.IsValid;
/* Another way using a similar workflow to WritePayload. */
bool isFromPredictedSpawner = IsServerStarted && connection == NetworkObject.PredictedSpawner;
/* When the connection is not valid, as noted in the parameter comments, this means
* the server is the sender. */
bool isFromServer = !connection.IsValid;
/* If you're the server and wish to cache a payload received from a predicted spawner,
* this is the place to do it. We are caching into an arraysegment named _cachedPayload --
* see WritePayload on how to forward this to other clients. */
if (isFromPredictedSpawner)
{
int readerStart = reader.Position;
// Do all your reads as you would.
int readAmount = reader.Position - readerStart;
// Let's use a pooled array so we don't allocate.
byte[] payload = ArrayPool<byte>.Shared.Rent(readAmount);
/* Copy into payload array from where the reader started, up to to
* the calculated readAmount. */
Array.Copy(reader.GetBuffer(), sourceIndex: readerStart, payload, destinationIndex: 0, readAmount);
_cachedPayload = new(payload, 0, readAmount);
// /* You will probably want to return the rented array when OnStop callbacks occur. */
// if (_cachedPayload != ArraySegment<byte>.Empty)
// ArrayPool<byte>.Shared.Return(_cachedPayload.Array);
}
}
}
```
{% endcode %}