/// <summary> /// AdvanceFrameHistory /// Progresses the current frame to the next QueueHistoryFrame for the QueueHistoryFrame.QueueFrameType. /// All other frames other than the current frame is considered the live rollback history /// </summary> /// <param name="queueType"></param> public void AdvanceFrameHistory(RpcQueueHistoryFrame.QueueFrameType queueType) { int StreamBufferIndex = GetStreamBufferIndex(queueType); if (!QueueHistory.ContainsKey(queueType)) { UnityEngine.Debug.LogError($"You must initialize the {nameof(RpcQueueContainer)} before using MLAPI!"); return; } if (!QueueHistory[queueType].ContainsKey(StreamBufferIndex)) { UnityEngine.Debug.LogError($"{nameof(RpcQueueContainer)} {queueType} queue stream buffer index out of range! [{StreamBufferIndex}]"); return; } foreach (KeyValuePair <NetworkUpdateStage, RpcQueueHistoryFrame> queueHistoryByUpdates in QueueHistory[queueType][StreamBufferIndex]) { var rpcQueueHistoryItem = queueHistoryByUpdates.Value; //This only gets reset when we advanced to next frame (do not reset this in the ResetQueueHistoryFrame) rpcQueueHistoryItem.HasLoopbackData = false; if (rpcQueueHistoryItem.QueueItemOffsets.Count > 0) { if (queueType == RpcQueueHistoryFrame.QueueFrameType.Inbound) { ProfilerStatManager.RpcInQueueSize.Record((int)rpcQueueHistoryItem.TotalSize); PerformanceDataManager.Increment(ProfilerConstants.RpcInQueueSize, (int)rpcQueueHistoryItem.TotalSize); } else { ProfilerStatManager.RpcOutQueueSize.Record((int)rpcQueueHistoryItem.TotalSize); PerformanceDataManager.Increment(ProfilerConstants.RpcOutQueueSize, (int)rpcQueueHistoryItem.TotalSize); } } ResetQueueHistoryFrame(rpcQueueHistoryItem); IncrementAndSetQueueHistoryFrame(rpcQueueHistoryItem); } //Roll to the next stream buffer StreamBufferIndex++; //If we have hit our maximum history, roll back over to the first one if (StreamBufferIndex >= m_MaxFrameHistory) { StreamBufferIndex = 0; } if (queueType == RpcQueueHistoryFrame.QueueFrameType.Inbound) { m_InboundStreamBufferIndex = StreamBufferIndex; } else { m_OutBoundStreamBufferIndex = StreamBufferIndex; } }
/// <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> /// 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> /// GetStreamBufferFrameCount /// Returns how many frames have been processed (Inbound/Outbound) /// </summary> /// <param name="queueType"></param> /// <returns>number of frames procssed</returns> public uint GetStreamBufferFrameCount(RpcQueueHistoryFrame.QueueFrameType queueType) { return(queueType == RpcQueueHistoryFrame.QueueFrameType.Inbound ? m_InboundFramesProcessed : m_OutboundFramesProcessed); }
/// <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); }
/// <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)); }
/// <summary> /// GetStreamBufferIndex /// Returns the queue type's current stream buffer index /// </summary> /// <param name="queueType"></param> /// <returns></returns> private int GetStreamBufferIndex(RpcQueueHistoryFrame.QueueFrameType queueType) { return(queueType == RpcQueueHistoryFrame.QueueFrameType.Inbound ? m_InboundStreamBufferIndex : m_OutBoundStreamBufferIndex); }