Пример #1
0
            public void NetworkUpdate(NetworkUpdateStage updateStage)
            {
                switch (updateStage)
                {
                case NetworkUpdateStage.Initialization:
                    UpdateCallbacks.OnInitialization();
                    break;

                case NetworkUpdateStage.EarlyUpdate:
                    UpdateCallbacks.OnEarlyUpdate();
                    break;

                case NetworkUpdateStage.FixedUpdate:
                    UpdateCallbacks.OnFixedUpdate();
                    break;

                case NetworkUpdateStage.PreUpdate:
                    UpdateCallbacks.OnPreUpdate();
                    break;

                case NetworkUpdateStage.Update:
                    UpdateCallbacks.OnUpdate();
                    break;

                case NetworkUpdateStage.PreLateUpdate:
                    UpdateCallbacks.OnPreLateUpdate();
                    break;

                case NetworkUpdateStage.PostLateUpdate:
                    UpdateCallbacks.OnPostLateUpdate();
                    break;
                }
            }
 /// <summary>
 /// QueueHistoryFrame Constructor
 /// </summary>
 /// <param name="queueType">type of queue history frame (Inbound/Outbound)</param>
 public RpcQueueHistoryFrame(QueueFrameType queueType, NetworkUpdateStage updateStage, int maxClients = 512)
 {
     m_MaximumClients    = maxClients;
     m_QueueFrameType    = queueType;
     m_CurrentQueueItem  = new RpcFrameQueueItem();
     m_StreamUpdateStage = updateStage;
 }
        // INetworkUpdateSystem
        public void NetworkUpdate(NetworkUpdateStage updateStage)
        {
            ProcessAndFlushRpcQueue(RpcQueueProcessingTypes.Receive, updateStage);

            if (updateStage == NetworkUpdateStage.PostLateUpdate)
            {
                ProcessAndFlushRpcQueue(RpcQueueProcessingTypes.Send, updateStage);
            }
        }
 public void NetworkUpdate(NetworkUpdateStage updateStage)
 {
     switch (updateStage)
     {
     case NetworkUpdateStage.EarlyUpdate:
         UpdateNetworkTick();
         break;
     }
 }
 /// <summary>
 /// QueueHistoryFrame Constructor
 /// </summary>
 /// <param name="queueType">Inbound or Outbound</param>
 /// <param name="updateStage">Network Update Stage this RpcQueueHistoryFrame is assigned to</param>
 /// <param name="maxClients">maximum number of clients</param>
 /// <param name="maxStreamBounds">maximum size of the message stream an RPC can have (defaults to 1MB)</param>
 public RpcQueueHistoryFrame(QueueFrameType queueType, NetworkUpdateStage updateStage, int maxClients = 512, int maxStreamBounds = 1 << 20)
 {
     //The added 512 is the Queue History Frame header information, leaving room to grow
     m_MaxStreamBounds   = maxStreamBounds + 512;
     m_MaximumClients    = maxClients;
     m_QueueFrameType    = queueType;
     m_CurrentQueueItem  = new RpcFrameQueueItem();
     m_StreamUpdateStage = updateStage;
 }
Пример #6
0
        /// <summary>
        /// ProcessReceiveQueue
        /// Public facing interface method to start processing all RPCs in the current inbound frame
        /// </summary>
        public void ProcessReceiveQueue(NetworkUpdateStage currentStage)
        {
            bool advanceFrameHistory = false;
            var  rpcQueueContainer   = NetworkManager.Singleton.RpcQueueContainer;

            if (rpcQueueContainer != null)
            {
#if DEVELOPMENT_BUILD || UNITY_EDITOR
                s_ProcessReceiveQueue.Begin();
#endif
                var currentFrame = rpcQueueContainer.GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, currentStage);
                var nextFrame    = rpcQueueContainer.GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, currentStage, true);
                if (nextFrame.IsDirty && nextFrame.HasLoopbackData)
                {
                    advanceFrameHistory = true;
                }

                if (currentFrame != null && currentFrame.IsDirty)
                {
                    var currentQueueItem = currentFrame.GetFirstQueueItem();
                    while (currentQueueItem.QueueItemType != RpcQueueContainer.QueueItemType.None)
                    {
                        advanceFrameHistory = true;

                        if (rpcQueueContainer.IsTesting())
                        {
                            Debug.Log($"RPC invoked during the {currentStage} update stage.");
                        }

                        NetworkManager.InvokeRpc(currentQueueItem);
                        ProfilerStatManager.RpcsQueueProc.Record();
                        PerformanceDataManager.Increment(ProfilerConstants.NumberOfRPCQueueProcessed);
                        currentQueueItem = currentFrame.GetNextQueueItem();
                    }

                    //We call this to dispose of the shared stream writer and stream
                    currentFrame.CloseQueue();
                }

                if (advanceFrameHistory)
                {
                    rpcQueueContainer.AdvanceFrameHistory(RpcQueueHistoryFrame.QueueFrameType.Inbound);
                }

#if DEVELOPMENT_BUILD || UNITY_EDITOR
                s_ProcessReceiveQueue.End();
#endif
            }
        }
        // Update is called once per frame
        private void Update()
        {
            if (NetworkManager.Singleton.IsListening && EnableTesting && m_ClientReceivedRpc)
            {
                //Reset this for the next sequence of rpcs
                m_ClientReceivedRpc = false;

                //As long as testing isn't completed, keep testing
                if (!IsTestComplete() && m_StagesSent.Count < m_MaxStagesSent)
                {
                    m_LastUpdateStage = m_ServerParams.Send.UpdateStage;
                    m_StagesSent.Add(m_LastUpdateStage);

                    PingMySelfServerRpc(m_StagesSent.Count, m_ServerParams);

                    switch (m_ServerParams.Send.UpdateStage)
                    {
                    case NetworkUpdateStage.Initialization:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.EarlyUpdate;
                        break;

                    case NetworkUpdateStage.EarlyUpdate:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.FixedUpdate;
                        break;

                    case NetworkUpdateStage.FixedUpdate:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreUpdate;
                        break;

                    case NetworkUpdateStage.PreUpdate:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.Update;
                        break;

                    case NetworkUpdateStage.Update:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PreLateUpdate;
                        break;

                    case NetworkUpdateStage.PreLateUpdate:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.PostLateUpdate;
                        break;

                    case NetworkUpdateStage.PostLateUpdate:
                        m_ServerParams.Send.UpdateStage = NetworkUpdateStage.Initialization;
                        break;
                    }
                }
            }
        }
Пример #8
0
        /// <summary>
        /// ProcessReceiveQueue
        /// Public facing interface method to start processing all RPCs in the current inbound frame
        /// </summary>
        public void ProcessReceiveQueue(NetworkUpdateStage currentStage, bool isTesting)
        {
            bool advanceFrameHistory = false;

            if (!ReferenceEquals(m_RpcQueueContainer, null))
            {
#if DEVELOPMENT_BUILD || UNITY_EDITOR
                s_ProcessReceiveQueue.Begin();
#endif
                var currentFrame = m_RpcQueueContainer.GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, currentStage);
                var nextFrame    = m_RpcQueueContainer.GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, currentStage, true);
                if (nextFrame.IsDirty && nextFrame.HasLoopbackData)
                {
                    advanceFrameHistory = true;
                }

                if (currentFrame != null && currentFrame.IsDirty)
                {
                    var currentQueueItem = currentFrame.GetFirstQueueItem();
                    while (currentQueueItem.QueueItemType != RpcQueueContainer.QueueItemType.None)
                    {
                        advanceFrameHistory = true;

                        if (!isTesting)
                        {
                            m_NetworkManager.InvokeRpc(currentQueueItem);
                        }

                        ProfilerStatManager.RpcsQueueProc.Record();
                        PerformanceDataManager.Increment(ProfilerConstants.RpcQueueProcessed);
                        currentQueueItem = currentFrame.GetNextQueueItem();
                    }

                    //We call this to dispose of the shared stream writer and stream
                    currentFrame.CloseQueue();
                }

                if (advanceFrameHistory)
                {
                    m_RpcQueueContainer.AdvanceFrameHistory(RpcQueueHistoryFrame.QueueFrameType.Inbound);
                }

#if DEVELOPMENT_BUILD || UNITY_EDITOR
                s_ProcessReceiveQueue.End();
#endif
            }
        }
        /// <summary>
        /// SetLoopBackFrameItem
        /// ***Temporary fix for host mode loopback RPC writer work-around
        /// Sets the next frame inbond buffer as the loopback queue history frame in the current frame's outbound buffer
        /// </summary>
        /// <param name="updateStage"></param>
        public void SetLoopBackFrameItem(NetworkUpdateStage updateStage)
        {
            //Get the next frame's inbound queue history frame
            var loopbackHistoryframe = GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, updateStage, true);

            //Get the current frame's outbound queue history frame
            var rpcQueueHistoryItem = GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate, false);

            if (rpcQueueHistoryItem != null)
            {
                rpcQueueHistoryItem.LoopbackHistoryFrame = loopbackHistoryframe;
            }
            else
            {
                UnityEngine.Debug.LogError("Could not find the outbound QueueHistoryFrame!");
            }
        }
Пример #10
0
        private static void RunNetworkUpdateStage(NetworkUpdateStage updateStage)
        {
            UpdateStage = updateStage;

            var sysArr = s_UpdateSystem_Arrays[updateStage];
            int arrLen = sysArr.Length;

            for (int curIdx = 0; curIdx < arrLen; curIdx++)
            {
                var curSys = sysArr[curIdx];
                if (curSys == null)
                {
                    // null terminator
                    break;
                }

                curSys.NetworkUpdate(updateStage);
            }
        }
        /// <summary>
        /// Will process the RPC queue and then move to the next available frame
        /// </summary>
        /// <param name="queueType">Inbound or Outbound</param>
        /// <param name="currentUpdateStage">Network Update Stage assigned RpcQueueHistoryFrame to be processed and flushed</param>
        public void ProcessAndFlushRpcQueue(RpcQueueProcessingTypes queueType, NetworkUpdateStage currentUpdateStage)
        {
            bool isListening = !ReferenceEquals(NetworkManager, null) && NetworkManager.IsListening;

            switch (queueType)
            {
            case RpcQueueProcessingTypes.Receive:
            {
                m_RpcQueueProcessor.ProcessReceiveQueue(currentUpdateStage, m_IsTestingEnabled);
                break;
            }

            case RpcQueueProcessingTypes.Send:
            {
                m_RpcQueueProcessor.ProcessSendQueue(isListening);
                break;
            }
            }
        }
Пример #12
0
            public void NetworkUpdate(NetworkUpdateStage updateStage)
            {
                switch (updateStage)
                {
                case NetworkUpdateStage.FixedUpdate:
                    UpdateCallbacks.OnFixedUpdate();
                    break;

                case NetworkUpdateStage.PreUpdate:
                    UpdateCallbacks.OnPreUpdate();
                    break;

                case NetworkUpdateStage.PreLateUpdate:
                    UpdateCallbacks.OnPreLateUpdate();
                    break;

                case NetworkUpdateStage.PostLateUpdate:
                    UpdateCallbacks.OnPostLateUpdate();
                    break;
                }
            }
        public void NetworkUpdate(NetworkUpdateStage updateStage)
        {
            if (updateStage == NetworkUpdateStage.EarlyUpdate)
            {
                if (m_NetworkManager.IsServer)
                {
                    for (int i = 0; i < m_NetworkManager.ConnectedClientsList.Count; i++)
                    {
                        var clientId = m_NetworkManager.ConnectedClientsList[i].ClientId;
                        SendSnapshot(clientId);
                    }
                }
                else
                {
                    SendSnapshot(m_NetworkManager.ServerClientId);
                }

                // DebugDisplayStore(m_Snapshot, "Entries");
                // DebugDisplayStore(m_ReceivedSnapshot, "Received Entries");
            }
        }
        /// <summary>
        /// ProcessAndFlushRPCQueue
        /// Will process the RPC queue and then move to the next available frame
        /// </summary>
        /// <param name="queueType"></param>
        public void ProcessAndFlushRpcQueue(RpcQueueProcessingTypes queueType, NetworkUpdateStage currentUpdateStage)
        {
            if (m_RpcQueueProcessor == null)
            {
                return;
            }

            switch (queueType)
            {
            case RpcQueueProcessingTypes.Receive:
            {
                m_RpcQueueProcessor.ProcessReceiveQueue(currentUpdateStage);
                break;
            }

            case RpcQueueProcessingTypes.Send:
            {
                m_RpcQueueProcessor.ProcessSendQueue();
                break;
            }
            }
        }
        /// <summary>
        /// EndAddQueueItemToOutboundFrame
        /// Signifies the end of this outbound RPC.
        /// We store final MSG size and track the total current frame queue size
        /// </summary>
        /// <param name="writer">writer that was used</param>
        public void EndAddQueueItemToFrame(NetworkWriter writer, RpcQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
        {
            bool getNextFrame = NetworkManager.Singleton.IsHost && queueFrameType == RpcQueueHistoryFrame.QueueFrameType.Inbound;

            var rpcQueueHistoryItem  = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame);
            var loopBackHistoryFrame = rpcQueueHistoryItem.LoopbackHistoryFrame;

            var pbWriter = (PooledNetworkWriter)writer;

            if (pbWriter != rpcQueueHistoryItem.QueueWriter && !getNextFrame)
            {
                UnityEngine.Debug.LogError($"{nameof(RpcQueueContainer)} {queueFrameType} passed writer is not the same as the current {nameof(PooledNetworkWriter)} for the {queueFrameType}!");
            }

            //The total size of the frame is the last known position of the stream
            rpcQueueHistoryItem.TotalSize = (uint)rpcQueueHistoryItem.QueueBuffer.Position;

            long  CurrentPosition = rpcQueueHistoryItem.QueueBuffer.Position;
            ulong BitPosition     = rpcQueueHistoryItem.QueueBuffer.BitPosition;

            //////////////////////////////////////////////////////////////
            //>>>> REPOSITIONING STREAM TO RPC MESSAGE SIZE LOCATION <<<<
            //////////////////////////////////////////////////////////////
            rpcQueueHistoryItem.QueueBuffer.Position = rpcQueueHistoryItem.GetCurrentMarkedPosition();

            long MSGOffset = 8;

            if (getNextFrame && IsUsingBatching())
            {
                MSGOffset += 8;
            }

            //subtracting 8 byte to account for the value of the size of the RPC
            long MSGSize = (long)(rpcQueueHistoryItem.TotalSize - (rpcQueueHistoryItem.GetCurrentMarkedPosition() + MSGOffset));

            if (MSGSize > 0)
            {
                //Write the actual size of the RPC message
                rpcQueueHistoryItem.QueueWriter.WriteInt64(MSGSize);
            }
            else
            {
                UnityEngine.Debug.LogWarning("MSGSize of < zero detected!!  Setting message size to zero!");
                rpcQueueHistoryItem.QueueWriter.WriteInt64(0);
            }

            if (loopBackHistoryFrame != null)
            {
                if (MSGSize > 0)
                {
                    //Point to where the size of the message is stored
                    loopBackHistoryFrame.QueueBuffer.Position = loopBackHistoryFrame.GetCurrentMarkedPosition();

                    //Write the actual size of the RPC message
                    loopBackHistoryFrame.QueueWriter.WriteInt64(MSGSize);

                    if (!IsUsingBatching())
                    {
                        //Write the offset for the header info copied
                        loopBackHistoryFrame.QueueWriter.WriteInt64(1);
                    }
                    else
                    {
                        //Write the offset for the header info copied
                        loopBackHistoryFrame.QueueWriter.WriteInt64(0);
                    }

                    //Write RPC data
                    loopBackHistoryFrame.QueueWriter.WriteBytes(rpcQueueHistoryItem.QueueBuffer.GetBuffer(), MSGSize, (int)rpcQueueHistoryItem.QueueBuffer.Position);

                    //Set the total size for this stream
                    loopBackHistoryFrame.TotalSize = (uint)loopBackHistoryFrame.QueueBuffer.Position;

                    //Add the total size to the offsets for parsing over various entries
                    loopBackHistoryFrame.QueueItemOffsets.Add((uint)loopBackHistoryFrame.QueueBuffer.Position);
                }
                else
                {
                    UnityEngine.Debug.LogWarning("[LoopBack] MSGSize of < zero detected!!  Setting message size to zero!");
                    //Write the actual size of the RPC message
                    loopBackHistoryFrame.QueueWriter.WriteInt64(0);
                }

                rpcQueueHistoryItem.LoopbackHistoryFrame = null;
            }


            //////////////////////////////////////////////////////////////
            //<<<< REPOSITIONING STREAM BACK TO THE CURRENT TAIL >>>>
            //////////////////////////////////////////////////////////////
            rpcQueueHistoryItem.QueueBuffer.Position    = CurrentPosition;
            rpcQueueHistoryItem.QueueBuffer.BitPosition = BitPosition;

            //Add the packed size to the offsets for parsing over various entries
            rpcQueueHistoryItem.QueueItemOffsets.Add((uint)rpcQueueHistoryItem.QueueBuffer.Position);
        }
Пример #16
0
        /// <summary>
        /// Registers a network update system to be executed in a specific network update stage.
        /// </summary>
        public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
        {
            var sysSet = s_UpdateSystem_Sets[updateStage];

            if (!sysSet.Contains(updateSystem))
            {
                sysSet.Add(updateSystem);

                int setLen = sysSet.Count;
                var sysArr = s_UpdateSystem_Arrays[updateStage];
                int arrLen = sysArr.Length;

                if (setLen > arrLen)
                {
                    // double capacity
                    sysArr = s_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2];
                }

                sysSet.CopyTo(sysArr);

                if (setLen < arrLen)
                {
                    // null terminator
                    sysArr[setLen] = null;
                }
            }
        }
Пример #17
0
        /// <summary>
        /// Unregisters a network update system from a specific network update stage.
        /// </summary>
        public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
        {
            var sysSet = s_UpdateSystem_Sets[updateStage];

            if (sysSet.Contains(updateSystem))
            {
                sysSet.Remove(updateSystem);

                int setLen = sysSet.Count;
                var sysArr = s_UpdateSystem_Arrays[updateStage];
                int arrLen = sysArr.Length;

                sysSet.CopyTo(sysArr);

                if (setLen < arrLen)
                {
                    // null terminator
                    sysArr[setLen] = null;
                }
            }
        }
        /// <summary>
        /// GetQueueHistoryFrame
        /// Gets the current queue history frame (inbound or outbound)
        /// </summary>
        /// <param name="frameType">inbound or outbound</param>
        /// <returns>QueueHistoryFrame or null</returns>
        public RpcQueueHistoryFrame GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType frameType, NetworkUpdateStage updateStage, bool getNextFrame = false)
        {
            int StreamBufferIndex = GetStreamBufferIndex(frameType);

            //We want to write into the future/next frame
            if (getNextFrame)
            {
                StreamBufferIndex++;

                //If we have hit our maximum history, roll back over to the first one
                if (StreamBufferIndex >= m_MaxFrameHistory)
                {
                    StreamBufferIndex = 0;
                }
            }

            if (!QueueHistory.ContainsKey(frameType))
            {
                UnityEngine.Debug.LogError("You must initialize the RPCQueueManager before using MLAPI!");
                return(null);
            }

            if (!QueueHistory[frameType].ContainsKey(StreamBufferIndex))
            {
                UnityEngine.Debug.LogError($"{nameof(RpcQueueContainer)} {frameType} queue stream buffer index out of range! [{StreamBufferIndex}]");
                return(null);
            }

            if (!QueueHistory[frameType][StreamBufferIndex].ContainsKey(updateStage))
            {
                UnityEngine.Debug.LogError($"{nameof(RpcQueueContainer)} {updateStage} update type does not exist!");
                return(null);
            }

            return(QueueHistory[frameType][StreamBufferIndex][updateStage]);
        }
        /// <summary>
        /// GetCurrentFrame
        /// Gets the current frame for the Inbound or Outbound queue
        /// </summary>
        /// <param name="qType"></param>
        /// <returns>QueueHistoryFrame</returns>
        public RpcQueueHistoryFrame GetCurrentFrame(RpcQueueHistoryFrame.QueueFrameType qType, NetworkUpdateStage currentUpdateStage)
        {
            if (QueueHistory.ContainsKey(qType))
            {
                int StreamBufferIndex = GetStreamBufferIndex(qType);

                if (QueueHistory[qType].ContainsKey(StreamBufferIndex))
                {
                    if (QueueHistory[qType][StreamBufferIndex].ContainsKey(currentUpdateStage))
                    {
                        return(QueueHistory[qType][StreamBufferIndex][currentUpdateStage]);
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// BeginAddQueueItemToOutboundFrame
        /// Adds a queue item to the outbound queue frame
        /// </summary>
        /// <param name="qItemType">type of rpc (client or server)</param>
        /// <param name="timeStamp">when it was scheduled to be sent</param>
        /// <param name="networkChannel">the channel to send it on</param>
        /// <param name="sourceNetworkId">who is sending the rpc</param>
        /// <param name="targetNetworkIds">who the rpc is being sent to</param>
        /// <returns></returns>
        public PooledNetworkWriter BeginAddQueueItemToFrame(QueueItemType qItemType, float timeStamp, NetworkChannel networkChannel, ulong sourceNetworkId, ulong[] targetNetworkIds,
                                                            RpcQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
        {
            bool getNextFrame = NetworkManager.Singleton.IsHost && queueFrameType == RpcQueueHistoryFrame.QueueFrameType.Inbound;

            var rpcQueueHistoryItem = GetQueueHistoryFrame(queueFrameType, updateStage, getNextFrame);

            rpcQueueHistoryItem.IsDirty = true;

            //Write the packed version of the queueItem to our current queue history buffer
            rpcQueueHistoryItem.QueueWriter.WriteUInt16((ushort)qItemType);
            rpcQueueHistoryItem.QueueWriter.WriteSingle(timeStamp);
            rpcQueueHistoryItem.QueueWriter.WriteUInt64(sourceNetworkId);

            if (queueFrameType != RpcQueueHistoryFrame.QueueFrameType.Inbound)
            {
                rpcQueueHistoryItem.QueueWriter.WriteByte((byte)networkChannel);

                if (targetNetworkIds != null && targetNetworkIds.Length != 0)
                {
                    //In the event the host is one of the networkIds, for outbound we want to ignore it (at this spot only!!)
                    //Get a count of clients we are going to send to (and write into the buffer)
                    var numberOfClients = 0;
                    for (int i = 0; i < targetNetworkIds.Length; i++)
                    {
                        if (NetworkManager.Singleton.IsHost && targetNetworkIds[i] == NetworkManager.Singleton.ServerClientId)
                        {
                            continue;
                        }

                        numberOfClients++;
                    }

                    //Write our total number of clients
                    rpcQueueHistoryItem.QueueWriter.WriteInt32(numberOfClients);

                    //Now write the cliend ids
                    for (int i = 0; i < targetNetworkIds.Length; i++)
                    {
                        if (NetworkManager.Singleton.IsHost && targetNetworkIds[i] == NetworkManager.Singleton.ServerClientId)
                        {
                            continue;
                        }

                        rpcQueueHistoryItem.QueueWriter.WriteUInt64(targetNetworkIds[i]);
                    }
                }
                else
                {
                    rpcQueueHistoryItem.QueueWriter.WriteInt32(0);
                }
            }

            //Mark where we started in the stream to later determine the actual RPC message size (position before writing RPC message vs position after write has completed)
            rpcQueueHistoryItem.MarkCurrentStreamPosition();

            //Write a filler dummy size of 0 to hold this position in order to write to it once the RPC is done writing.
            rpcQueueHistoryItem.QueueWriter.WriteInt64(0);

            if (NetworkManager.Singleton.IsHost && queueFrameType == RpcQueueHistoryFrame.QueueFrameType.Inbound)
            {
                if (!IsUsingBatching())
                {
                    rpcQueueHistoryItem.QueueWriter.WriteInt64(1);
                }
                else
                {
                    rpcQueueHistoryItem.QueueWriter.WriteInt64(0);
                }

                rpcQueueHistoryItem.HasLoopbackData = true; //The only case for this is when it is the Host
            }

            //Return the writer to the invoking method.
            return(rpcQueueHistoryItem.QueueWriter);
        }
 /// <summary>
 /// GetLoopBackWriter
 /// Gets the loop back writer for the history frame (if one exists)
 /// ***Temporary fix for host mode loopback RPC writer work-around
 /// </summary>
 /// <param name="queueFrameType"></param>
 /// <param name="updateStage"></param>
 /// <returns></returns>
 public RpcQueueHistoryFrame GetLoopBackHistoryFrame(RpcQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
 {
     return(GetQueueHistoryFrame(queueFrameType, updateStage, false));
 }