#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
using FishNet.Managing.Server;
using FishNet.Object.Helping;
using FishNet.Serializing;
using FishNet.Transporting;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Object
{
public abstract partial class NetworkBehaviour : MonoBehaviour
{
#region Private.
///
/// Link indexes for RPCs.
///
private Dictionary _rpcLinks = new();
#endregion
#region Consts.
///
/// Number of bytes written for each RPCLinks.
///
internal const int RPCLINK_RESERVED_BYTES = 2;
#endregion
///
/// Initializes RpcLinks. This will only call once even as host.
///
private void InitializeRpcLinks()
{
/* Link only data from server to clients. While it is
* just as easy to link client to server it's usually
* not needed because server out data is more valuable
* than server in data. */
/* Links will be stored in the NetworkBehaviour so that
* when the object is destroyed they can be added back
* into availableRpcLinks, within the ServerManager. */
ServerManager serverManager = NetworkManager.ServerManager;
//ObserverRpcs.
if (_observersRpcDelegates != null)
{
foreach (uint rpcHash in _observersRpcDelegates.Keys)
{
if (!MakeLink(rpcHash, PacketId.ObserversRpc))
return;
}
}
//TargetRpcs.
if (_targetRpcDelegates != null)
{
foreach (uint rpcHash in _targetRpcDelegates.Keys)
{
if (!MakeLink(rpcHash, PacketId.TargetRpc))
return;
}
}
//ReconcileRpcs.
if (_reconcileRpcDelegates != null)
{
foreach (uint rpcHash in _reconcileRpcDelegates.Keys)
{
if (!MakeLink(rpcHash, PacketId.Reconcile))
return;
}
}
/* Tries to make a link and returns if
* successful. When a link cannot be made the method
* should exit as no other links will be possible. */
bool MakeLink(uint rpcHash, PacketId packetId)
{
if (serverManager.GetRpcLink(out ushort linkIndex))
{
_rpcLinks[rpcHash] = new(rpcHash, packetId, linkIndex);
return true;
}
else
{
return false;
}
}
}
///
/// Returns an estimated length for any Rpc header.
///
///
private int GetEstimatedRpcHeaderLength()
{
/* Imaginary number for how long RPC headers are.
* They are well under this value but this exist to
* ensure a writer of appropriate length is pulled
* from the pool. */
return 20;
}
///
/// Creates a PooledWriter and writes the header for a rpc.
///
private PooledWriter CreateLinkedRpc(RpcLinkType link, PooledWriter methodWriter, Channel channel)
{
int rpcHeaderBufferLength = GetEstimatedRpcHeaderLength();
int methodWriterLength = methodWriter.Length;
//Writer containing full packet.
PooledWriter writer = WriterPool.Retrieve(rpcHeaderBufferLength + methodWriterLength);
writer.WriteUInt16(link.LinkPacketId);
#if DEVELOPMENT
int written = WriteDebugForValidateRpc(writer, link.RpcPacketId, link.RpcHash);
#endif
//Write length only if reliable.
if (channel == Channel.Reliable)
writer.WriteInt32(methodWriter.Length);
//Data.
writer.WriteArraySegment(methodWriter.GetArraySegment());
#if DEVELOPMENT
WriteDebugLengthForValidateRpc(writer, written);
#endif
return writer;
}
///
/// Returns RpcLinks the ServerManager.
///
private void ReturnRpcLinks()
{
if (_rpcLinks.Count == 0)
return;
ServerManager?.StoreRpcLinks(_rpcLinks);
_rpcLinks.Clear();
}
///
/// Writes rpcLinks to writer.
///
internal void WriteRpcLinks(Writer writer)
{
int rpcLinksCount = _rpcLinks.Count;
if (rpcLinksCount == 0)
return;
writer.WriteNetworkBehaviourId(this);
writer.WriteUInt16((ushort)rpcLinksCount);
foreach (KeyValuePair item in _rpcLinks)
{
//RpcLink index.
writer.WriteUInt16Unpacked(item.Value.LinkPacketId);
//Hash.
writer.WriteUInt16Unpacked((ushort)item.Key);
//True/false if observersRpc.
writer.WriteUInt16Unpacked((ushort)item.Value.RpcPacketId);
}
}
}
}