using FishNet.Managing.Server; using FishNet.Object.Helping; using FishNet.Serializing; using FishNet.Transporting; using System.Collections.Generic; 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 Dictionary(); #endregion /// /// Initializes RpcLinks. This will only call once even as host. /// private void InitializeOnceRpcLinks() { if (NetworkManager.IsServer) { /* 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. foreach (uint rpcHash in _observersRpcDelegates.Keys) { if (!MakeLink(rpcHash, RpcType.Observers)) return; } //TargetRpcs. foreach (uint rpcHash in _targetRpcDelegates.Keys) { if (!MakeLink(rpcHash, RpcType.Target)) return; } //ReconcileRpcs. foreach (uint rpcHash in _reconcileRpcDelegates.Keys) { if (!MakeLink(rpcHash, RpcType.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, RpcType rpcType) { if (serverManager.GetRpcLink(out ushort linkIndex)) { _rpcLinks[rpcHash] = new RpcLinkType(linkIndex, rpcType); 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. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private PooledWriter CreateLinkedRpc(RpcLinkType link, PooledWriter methodWriter, Channel channel) { int rpcHeaderBufferLength = GetEstimatedRpcHeaderLength(); int methodWriterLength = methodWriter.Length; //Writer containing full packet. PooledWriter writer = WriterPool.GetWriter(rpcHeaderBufferLength + methodWriterLength); writer.WriteUInt16(link.LinkIndex); //Write length only if reliable. if (channel == Channel.Reliable) writer.WriteLength(methodWriter.Length); //Data. writer.WriteArraySegment(methodWriter.GetArraySegment()); 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) { PooledWriter rpcLinkWriter = WriterPool.GetWriter(); foreach (KeyValuePair item in _rpcLinks) { //RpcLink index. rpcLinkWriter.WriteUInt16(item.Value.LinkIndex); //Hash. rpcLinkWriter.WriteUInt16((ushort)item.Key); //True/false if observersRpc. rpcLinkWriter.WriteByte((byte)item.Value.RpcType); } writer.WriteBytesAndSize(rpcLinkWriter.GetBuffer(), 0, rpcLinkWriter.Length); rpcLinkWriter.Dispose(); } } }