polygone/Library/PackageCache/com.unity.multiplayer.mlapi@3e3aef6aa0/Runtime/Messaging/RpcQueue/RpcQueueHistoryFrame.cs
2021-08-02 05:44:37 -04:00

251 lines
No EOL
10 KiB
C#

using System.Collections.Generic;
using MLAPI.Serialization.Pooled;
using MLAPI.Transports;
namespace MLAPI.Messaging
{
/// <summary>
/// QueueHistoryFrame
/// Used by the RpcQueueContainer to hold queued RPCs
/// All queued Rpcs end up in a PooledNetworkBuffer within a QueueHistoryFrame instance.
/// </summary>
public class RpcQueueHistoryFrame
{
public enum QueueFrameType
{
Inbound,
Outbound,
}
public bool IsDirty; //Used to determine if this queue history frame has been reset (cleaned) yet
public bool HasLoopbackData; //Used to determine if a dirt frame is dirty because rpcs are being looped back betwen HostClient and HostServer
public uint TotalSize;
public List<uint> QueueItemOffsets;
public PooledNetworkBuffer QueueBuffer;
public PooledNetworkWriter QueueWriter;
public RpcQueueHistoryFrame LoopbackHistoryFrame; //Temporary fix for Host mode loopback work around.
public PooledNetworkReader QueueReader;
private int m_QueueItemOffsetIndex;
private RpcFrameQueueItem m_CurrentQueueItem;
private readonly QueueFrameType m_QueueFrameType;
private int m_MaximumClients;
private long m_CurrentStreamSizeMark;
private NetworkUpdateStage m_StreamUpdateStage; //Update stage specific to RPCs (typically inbound has most potential for variation)
private const int k_MaxStreamBounds = 131072;
private const int k_MinStreamBounds = 0;
/// <summary>
/// GetQueueFrameType
/// Returns whether this is an inbound or outbound frame
/// </summary>
/// <returns></returns>
public QueueFrameType GetQueueFrameType()
{
return m_QueueFrameType;
}
/// <summary>
/// MarkCurrentStreamSize
/// Marks the current size of the stream (used primarily for sanity checks)
/// </summary>
public void MarkCurrentStreamPosition()
{
if (QueueBuffer != null)
{
m_CurrentStreamSizeMark = QueueBuffer.Position;
}
else
{
m_CurrentStreamSizeMark = 0;
}
}
/// <summary>
/// Returns the current position that was marked (to track size of RPC msg)
/// </summary>
/// <returns>m_CurrentStreamSizeMark</returns>
public long GetCurrentMarkedPosition()
{
return m_CurrentStreamSizeMark;
}
/// <summary>
/// GetCurrentQueueItem
/// Internal method to get the current Queue Item from the stream at its current position
/// </summary>
/// <returns>FrameQueueItem</returns>
private RpcFrameQueueItem GetCurrentQueueItem()
{
//Write the packed version of the queueItem to our current queue history buffer
m_CurrentQueueItem.QueueItemType = (RpcQueueContainer.QueueItemType)QueueReader.ReadUInt16();
m_CurrentQueueItem.Timestamp = QueueReader.ReadSingle();
m_CurrentQueueItem.NetworkId = QueueReader.ReadUInt64();
//Clear out any current value for the client ids
m_CurrentQueueItem.ClientNetworkIds = new ulong[0];
//If outbound, determine if any client ids needs to be added
if (m_QueueFrameType == QueueFrameType.Outbound)
{
//Outbound we care about both channel and clients
m_CurrentQueueItem.NetworkChannel = (NetworkChannel)QueueReader.ReadByteDirect();
int NumClients = QueueReader.ReadInt32();
if (NumClients > 0 && NumClients < m_MaximumClients)
{
ulong[] clientIdArray = new ulong[NumClients];
for (int i = 0; i < NumClients; i++)
{
clientIdArray[i] = QueueReader.ReadUInt64();
}
if (m_CurrentQueueItem.ClientNetworkIds == null)
{
m_CurrentQueueItem.ClientNetworkIds = clientIdArray;
}
else
{
m_CurrentQueueItem.ClientNetworkIds = clientIdArray;
}
}
}
m_CurrentQueueItem.UpdateStage = m_StreamUpdateStage;
//Get the stream size
m_CurrentQueueItem.StreamSize = QueueReader.ReadInt64();
//Sanity checking for boundaries
if (m_CurrentQueueItem.StreamSize < k_MaxStreamBounds && m_CurrentQueueItem.StreamSize > k_MinStreamBounds)
{
//Inbound and Outbound message streams are handled differently
if (m_QueueFrameType == QueueFrameType.Inbound)
{
//Get our offset
long Position = QueueReader.ReadInt64();
//Always make sure we are positioned at the start of the stream before we write
m_CurrentQueueItem.NetworkBuffer.Position = 0;
//Write the entire message to the m_CurrentQueueItem stream (1 stream is re-used for all incoming RPCs)
m_CurrentQueueItem.NetworkWriter.ReadAndWrite(QueueReader, m_CurrentQueueItem.StreamSize);
//Reset the position back to the offset so std rpc API can process the message properly
//(i.e. minus the already processed header)
m_CurrentQueueItem.NetworkBuffer.Position = Position;
}
else
{
//Create a byte array segment for outbound sending
m_CurrentQueueItem.MessageData = QueueReader.CreateArraySegment((int)m_CurrentQueueItem.StreamSize, (int)QueueBuffer.Position);
}
}
else
{
UnityEngine.Debug.LogWarning($"{nameof(m_CurrentQueueItem)}.{nameof(RpcFrameQueueItem.StreamSize)} exceeds allowed size ({k_MaxStreamBounds} vs {m_CurrentQueueItem.StreamSize})! Exiting from the current RpcQueue enumeration loop!");
m_CurrentQueueItem.QueueItemType = RpcQueueContainer.QueueItemType.None;
}
return m_CurrentQueueItem;
}
/// <summary>
/// GetNextQueueItem
/// Handles getting the next queue item from this frame
/// If none are remaining, then it returns a queue item type of NONE
/// </summary>
/// <returns>FrameQueueItem</returns>
internal RpcFrameQueueItem GetNextQueueItem()
{
QueueBuffer.Position = QueueItemOffsets[m_QueueItemOffsetIndex];
m_QueueItemOffsetIndex++;
if (m_QueueItemOffsetIndex >= QueueItemOffsets.Count)
{
m_CurrentQueueItem.QueueItemType = RpcQueueContainer.QueueItemType.None;
return m_CurrentQueueItem;
}
return GetCurrentQueueItem();
}
/// <summary>
/// GetFirstQueueItem
/// Should be called the first time a queue item is pulled from a queue history frame.
/// This will reset the frame's stream indices and add a new stream and stream writer to the m_CurrentQueueItem instance.
/// </summary>
/// <returns>FrameQueueItem</returns>
internal RpcFrameQueueItem GetFirstQueueItem()
{
if (QueueBuffer.Position > 0)
{
m_QueueItemOffsetIndex = 0;
QueueBuffer.Position = 0;
if (m_QueueFrameType == QueueFrameType.Inbound)
{
if (m_CurrentQueueItem.NetworkBuffer == null)
{
m_CurrentQueueItem.NetworkBuffer = PooledNetworkBuffer.Get();
}
if (m_CurrentQueueItem.NetworkWriter == null)
{
m_CurrentQueueItem.NetworkWriter = PooledNetworkWriter.Get(m_CurrentQueueItem.NetworkBuffer);
}
if (m_CurrentQueueItem.NetworkReader == null)
{
m_CurrentQueueItem.NetworkReader = PooledNetworkReader.Get(m_CurrentQueueItem.NetworkBuffer);
}
}
return GetCurrentQueueItem();
}
m_CurrentQueueItem.QueueItemType = RpcQueueContainer.QueueItemType.None;
return m_CurrentQueueItem;
}
/// <summary>
/// CloseQueue
/// Should be called once all processing of the current frame is complete.
/// This only closes the m_CurrentQueueItem's stream which is used as a "middle-man" (currently)
/// for delivering the RPC message to the method requesting a queue item from a frame.
/// </summary>
public void CloseQueue()
{
if (m_CurrentQueueItem.NetworkWriter != null)
{
m_CurrentQueueItem.NetworkWriter.Dispose();
m_CurrentQueueItem.NetworkWriter = null;
}
if (m_CurrentQueueItem.NetworkReader != null)
{
m_CurrentQueueItem.NetworkReader.Dispose();
m_CurrentQueueItem.NetworkReader = null;
}
if (m_CurrentQueueItem.NetworkBuffer != null)
{
m_CurrentQueueItem.NetworkBuffer.Dispose();
m_CurrentQueueItem.NetworkBuffer = null;
}
}
/// <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;
}
}
}