/// <summary> /// Sends a named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientId">The client to send the message to</param> /// <param name="stream">The message stream containing the data</param> /// <param name="networkChannel">The channel to send the data on</param> public static void SendNamedMessage(string name, ulong clientId, Stream stream, NetworkChannel networkChannel = NetworkChannel.Internal) { ulong hash = 0; switch (NetworkManager.Singleton.NetworkConfig.RpcHashSize) { case HashSize.VarIntTwoBytes: hash = name.GetStableHash16(); break; case HashSize.VarIntFourBytes: hash = name.GetStableHash32(); break; case HashSize.VarIntEightBytes: hash = name.GetStableHash64(); break; } using (var messageBuffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(messageBuffer)) { writer.WriteUInt64Packed(hash); messageBuffer.CopyFrom(stream); InternalMessageSender.Send(clientId, NetworkConstants.NAMED_MESSAGE, networkChannel, messageBuffer); PerformanceDataManager.Increment(ProfilerConstants.NamedMessageSent); } }
/// <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; } }
internal static void Send(byte messageType, NetworkChannel networkChannel, ulong clientIdToIgnore, NetworkBuffer messageBuffer) { messageBuffer.PadBuffer(); using (var buffer = MessagePacker.WrapMessage(messageType, messageBuffer)) { #if !UNITY_2020_2_OR_NEWER NetworkProfiler.StartEvent(TickType.Send, (uint)buffer.Length, networkChannel, NetworkConstants.MESSAGE_NAMES[messageType]); #endif for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) { if (NetworkManager.Singleton.ConnectedClientsList[i].ClientId == clientIdToIgnore || (NetworkManager.Singleton.IsServer && NetworkManager.Singleton.ConnectedClientsList[i].ClientId == NetworkManager.Singleton.ServerClientId)) { continue; } NetworkManager.Singleton.NetworkConfig.NetworkTransport.Send(NetworkManager.Singleton.ConnectedClientsList[i].ClientId, new ArraySegment <byte>(buffer.GetBuffer(), 0, (int)buffer.Length), networkChannel); ProfilerStatManager.BytesSent.Record((int)buffer.Length); PerformanceDataManager.Increment(ProfilerConstants.ByteSent, (int)buffer.Length); } #if !UNITY_2020_2_OR_NEWER NetworkProfiler.EndEvent(); #endif } }
internal void Send(byte messageType, NetworkChannel networkChannel, List <ulong> clientIds, NetworkBuffer messageBuffer) { if (clientIds == null) { Send(messageType, networkChannel, messageBuffer); return; } messageBuffer.PadBuffer(); using (var buffer = MessagePacker.WrapMessage(messageType, messageBuffer)) { #if !UNITY_2020_2_OR_NEWER NetworkProfiler.StartEvent(TickType.Send, (uint)buffer.Length, networkChannel, NetworkConstants.MESSAGE_NAMES[messageType]); #endif for (int i = 0; i < clientIds.Count; i++) { if (m_NetworkManager.IsServer && clientIds[i] == m_NetworkManager.ServerClientId) { continue; } m_NetworkManager.NetworkConfig.NetworkTransport.Send(clientIds[i], new ArraySegment <byte>(buffer.GetBuffer(), 0, (int)buffer.Length), networkChannel); ProfilerStatManager.BytesSent.Record((int)buffer.Length); PerformanceDataManager.Increment(ProfilerConstants.ByteSent, (int)buffer.Length); } #if !UNITY_2020_2_OR_NEWER NetworkProfiler.EndEvent(); #endif } }
public void HandleUnnamedMessage(ulong clientId, Stream stream) { PerformanceDataManager.Increment(ProfilerConstants.UnnamedMessageReceived); ProfilerStatManager.UnnamedMessage.Record(); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleUnnamedMessage.Begin(); #endif NetworkManager.CustomMessagingManager.InvokeUnnamedMessage(clientId, stream); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleUnnamedMessage.End(); #endif }
internal static void HandleUnnamedMessage(ulong clientId, Stream stream) { PerformanceDataManager.Increment(ProfilerConstants.NumberOfUnnamedMessages); ProfilerStatManager.UnnamedMessage.Record(); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleUnnamedMessage.Begin(); #endif CustomMessagingManager.InvokeUnnamedMessage(clientId, stream); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleUnnamedMessage.End(); #endif }
/// <summary> /// Generic Sending Method for Internal Messages ADD_OBJECT and DESTROY_OBJECT /// </summary> internal void InternalMessagesSendAndFlush(bool isListening) { foreach (RpcFrameQueueItem queueItem in m_InternalMLAPISendQueue) { var poolStream = queueItem.NetworkBuffer; switch (queueItem.QueueItemType) { case RpcQueueContainer.QueueItemType.CreateObject: { if (isListening) { foreach (ulong clientId in queueItem.ClientNetworkIds) { m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.ADD_OBJECT, queueItem.NetworkChannel, poolStream); } PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } case RpcQueueContainer.QueueItemType.DestroyObject: { if (isListening) { foreach (ulong clientId in queueItem.ClientNetworkIds) { m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.DESTROY_OBJECT, queueItem.NetworkChannel, poolStream); } PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } } poolStream.Dispose(); } m_InternalMLAPISendQueue.Clear(); }
/// <summary> /// Sends unnamed message to a list of clients /// </summary> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="buffer">The message stream containing the data</param> /// <param name="networkChannel">The channel to send the data on</param> public static void SendUnnamedMessage(List <ulong> clientIds, NetworkBuffer buffer, NetworkChannel networkChannel = NetworkChannel.Internal) { if (!NetworkManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send unnamed messages to multiple users as a client"); } return; } InternalMessageSender.Send(NetworkConstants.UNNAMED_MESSAGE, networkChannel, clientIds, buffer); PerformanceDataManager.Increment(ProfilerConstants.UnnamedMessageSent); }
/// <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 } }
/// <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 } }
internal static void HandleNamedMessage(ulong clientId, Stream stream) { PerformanceDataManager.Increment(ProfilerConstants.NumberOfNamedMessages); ProfilerStatManager.NamedMessage.Record(); #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleNamedMessage.Begin(); #endif using (var reader = PooledNetworkReader.Get(stream)) { ulong hash = reader.ReadUInt64Packed(); CustomMessagingManager.InvokeNamedMessage(hash, clientId, stream); } #if DEVELOPMENT_BUILD || UNITY_EDITOR s_HandleNamedMessage.End(); #endif }
internal static void Send(ulong clientId, byte messageType, NetworkChannel networkChannel, NetworkBuffer messageBuffer) { messageBuffer.PadBuffer(); if (NetworkManager.Singleton.IsServer && clientId == NetworkManager.Singleton.ServerClientId) { return; } using (var buffer = MessagePacker.WrapMessage(messageType, messageBuffer)) { NetworkProfiler.StartEvent(TickType.Send, (uint)buffer.Length, networkChannel, NetworkConstants.MESSAGE_NAMES[messageType]); NetworkManager.Singleton.NetworkConfig.NetworkTransport.Send(clientId, new ArraySegment <byte>(buffer.GetBuffer(), 0, (int)buffer.Length), networkChannel); ProfilerStatManager.BytesSent.Record((int)buffer.Length); PerformanceDataManager.Increment(ProfilerConstants.ByteSent, (int)buffer.Length); #if !UNITY_2020_2_OR_NEWER NetworkProfiler.EndEvent(); #endif } }
/// <summary> /// Converts the stream to a PerformanceQueueItem and adds it to the receive queue /// </summary> /// <param name="clientId"></param> /// <param name="stream"></param> /// <param name="receiveTime"></param> internal static void RpcReceiveQueueItem(ulong clientId, Stream stream, float receiveTime, RpcQueueContainer.QueueItemType queueItemType) { if (NetworkManager.Singleton.IsServer && clientId == NetworkManager.Singleton.ServerClientId) { return; } ProfilerStatManager.RpcsRcvd.Record(); PerformanceDataManager.Increment(ProfilerConstants.NumberOfRPCsReceived); #if DEVELOPMENT_BUILD || UNITY_EDITOR switch (queueItemType) { case RpcQueueContainer.QueueItemType.ServerRpc: s_RpcReceiveQueueItemServerRpc.Begin(); break; case RpcQueueContainer.QueueItemType.ClientRpc: s_RpcReceiveQueueItemClientRpc.Begin(); break; } #endif var rpcQueueContainer = NetworkManager.Singleton.RpcQueueContainer; rpcQueueContainer.AddQueueItemToInboundFrame(queueItemType, receiveTime, clientId, (NetworkBuffer)stream); #if DEVELOPMENT_BUILD || UNITY_EDITOR switch (queueItemType) { case RpcQueueContainer.QueueItemType.ServerRpc: s_RpcReceiveQueueItemServerRpc.End(); break; case RpcQueueContainer.QueueItemType.ClientRpc: s_RpcReceiveQueueItemClientRpc.End(); break; } #endif }
/// <summary> /// Sends the named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="stream">The message stream containing the data</param> /// <param name="networkChannel">The channel to send the data on</param> public static void SendNamedMessage(string name, List <ulong> clientIds, Stream stream, NetworkChannel networkChannel = NetworkChannel.Internal) { ulong hash = 0; switch (NetworkManager.Singleton.NetworkConfig.RpcHashSize) { case HashSize.VarIntTwoBytes: hash = name.GetStableHash16(); break; case HashSize.VarIntFourBytes: hash = name.GetStableHash32(); break; case HashSize.VarIntEightBytes: hash = name.GetStableHash64(); break; } using (var messageBuffer = PooledNetworkBuffer.Get()) using (var writer = PooledNetworkWriter.Get(messageBuffer)) { writer.WriteUInt64Packed(hash); messageBuffer.CopyFrom(stream); if (!NetworkManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send named messages to multiple users as a client"); } return; } InternalMessageSender.Send(NetworkConstants.NAMED_MESSAGE, networkChannel, clientIds, messageBuffer); PerformanceDataManager.Increment(ProfilerConstants.NamedMessageSent); } }
/// <summary> /// SendFrameQueueItem /// Sends the RPC Queue Item to the specified destination /// </summary> /// <param name="queueItem">Information on what to send</param> private void SendFrameQueueItem(RpcFrameQueueItem queueItem) { switch (queueItem.QueueItemType) { case RpcQueueContainer.QueueItemType.ServerRpc: { m_RpcQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(queueItem.NetworkId, queueItem.MessageData, queueItem.NetworkChannel); //For each packet sent, we want to record how much data we have sent PerformanceDataManager.Increment(ProfilerConstants.ByteSent, (int)queueItem.StreamSize); PerformanceDataManager.Increment(ProfilerConstants.RpcSent); ProfilerStatManager.BytesSent.Record((int)queueItem.StreamSize); ProfilerStatManager.RpcsSent.Record(); break; } case RpcQueueContainer.QueueItemType.ClientRpc: { foreach (ulong clientid in queueItem.ClientNetworkIds) { m_RpcQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(clientid, queueItem.MessageData, queueItem.NetworkChannel); //For each packet sent, we want to record how much data we have sent PerformanceDataManager.Increment(ProfilerConstants.ByteSent, (int)queueItem.StreamSize); ProfilerStatManager.BytesSent.Record((int)queueItem.StreamSize); } //For each client we send to, we want to record how many RPCs we have sent PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length); ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length); break; } } }
/// <summary> /// Sends a unnamed message to a specific client /// </summary> /// <param name="clientId">The client to send the message to</param> /// <param name="buffer">The message stream containing the data</param> /// <param name="networkChannel">The channel tos end the data on</param> public static void SendUnnamedMessage(ulong clientId, NetworkBuffer buffer, NetworkChannel networkChannel = NetworkChannel.Internal) { InternalMessageSender.Send(clientId, NetworkConstants.UNNAMED_MESSAGE, networkChannel, buffer); PerformanceDataManager.Increment(ProfilerConstants.UnnamedMessageSent); }
internal static void HandleNetworkVariableUpdate(List <INetworkVariable> networkVariableList, Stream stream, ulong clientId, NetworkBehaviour logInstance, NetworkManager networkManager) { using (var reader = PooledNetworkReader.Get(stream)) { for (int i = 0; i < networkVariableList.Count; i++) { ushort varSize = 0; if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { varSize = reader.ReadUInt16Packed(); if (varSize == 0) { continue; } } else { if (!reader.ReadBool()) { continue; } } if (networkManager.IsServer && !networkVariableList[i].CanClientWrite(clientId)) { if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"Client wrote to {nameof(NetworkVariable)} without permission. => {(logInstance != null ? ($"{nameof(NetworkObjectId)}: {logInstance.NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {logInstance.NetworkObject.GetNetworkBehaviourOrderIndex(logInstance)} - VariableIndex: {i}") : string.Empty)}"); } stream.Position += varSize; continue; } //This client wrote somewhere they are not allowed. This is critical //We can't just skip this field. Because we don't actually know how to dummy read //That is, we don't know how many bytes to skip. Because the interface doesn't have a //Read that gives us the value. Only a Read that applies the value straight away //A dummy read COULD be added to the interface for this situation, but it's just being too nice. //This is after all a developer fault. A critical error should be fine. // - TwoTen if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError($"Client wrote to {nameof(NetworkVariable)} without permission. No more variables can be read. This is critical. => {(logInstance != null ? ($"{nameof(NetworkObjectId)}: {logInstance.NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {logInstance.NetworkObject.GetNetworkBehaviourOrderIndex(logInstance)} - VariableIndex: {i}") : string.Empty)}"); } return; } long readStartPos = stream.Position; networkVariableList[i].ReadField(stream, NetworkTickSystem.NoTick, NetworkTickSystem.NoTick); PerformanceDataManager.Increment(ProfilerConstants.NetworkVarUpdates); ProfilerStatManager.NetworkVarsRcvd.Record(); if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { if (stream is NetworkBuffer networkBuffer) { networkBuffer.SkipPadBits(); } if (stream.Position > (readStartPos + varSize)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"Var update read too far. {stream.Position - (readStartPos + varSize)} bytes. => {(logInstance != null ? ($"{nameof(NetworkObjectId)}: {logInstance.NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {logInstance.NetworkObject.GetNetworkBehaviourOrderIndex(logInstance)} - VariableIndex: {i}") : string.Empty)}"); } stream.Position = readStartPos + varSize; } else if (stream.Position < (readStartPos + varSize)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"Var update read too little. {(readStartPos + varSize) - stream.Position} bytes. => {(logInstance != null ? ($"{nameof(NetworkObjectId)}: {logInstance.NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {logInstance.NetworkObject.GetNetworkBehaviourOrderIndex(logInstance)} - VariableIndex: {i}") : string.Empty)}"); } stream.Position = readStartPos + varSize; } } } } }
public void Send(string testMessage) { PerformanceDataManager.Increment(ProfilerConstants.TransportTestData); }