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; }
/// <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; } } } }
/// <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!"); } }
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; } } }
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); }
/// <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; } } }
/// <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)); }