Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5b96fb2c0fcfc0845a71925f9375f3a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using MLAPI.Serialization.Pooled;
using MLAPI.Transports;
using Unity.Profiling;
using UnityEngine;
namespace MLAPI.Messaging.Buffering
{
internal static class BufferManager
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
private static ProfilerMarker s_CleanBuffer = new ProfilerMarker($"{nameof(BufferManager)}.{nameof(CleanBuffer)}");
#endif
private static Dictionary<ulong, Queue<BufferedMessage>> s_BufferQueues = new Dictionary<ulong, Queue<BufferedMessage>>();
internal struct BufferedMessage
{
internal ulong SenderClientId;
internal NetworkChannel NetworkChannel;
internal PooledNetworkBuffer NetworkBuffer;
internal float ReceiveTime;
internal float BufferTime;
}
internal static Queue<BufferedMessage> ConsumeBuffersForNetworkId(ulong networkId)
{
if (s_BufferQueues.ContainsKey(networkId))
{
Queue<BufferedMessage> message = s_BufferQueues[networkId];
s_BufferQueues.Remove(networkId);
return message;
}
else
{
return null;
}
}
internal static void RecycleConsumedBufferedMessage(BufferedMessage message)
{
message.NetworkBuffer.Dispose();
}
internal static void BufferMessageForNetworkId(ulong networkId, ulong senderClientId, NetworkChannel networkChannel, float receiveTime, ArraySegment<byte> payload)
{
if (!s_BufferQueues.ContainsKey(networkId))
{
s_BufferQueues.Add(networkId, new Queue<BufferedMessage>());
}
Queue<BufferedMessage> queue = s_BufferQueues[networkId];
var payloadBuffer = PooledNetworkBuffer.Get();
payloadBuffer.Write(payload.Array, payload.Offset, payload.Count);
payloadBuffer.Position = 0;
queue.Enqueue(new BufferedMessage()
{
BufferTime = Time.realtimeSinceStartup,
NetworkChannel = networkChannel,
NetworkBuffer = payloadBuffer,
ReceiveTime = receiveTime,
SenderClientId = senderClientId
});
}
private static List<ulong> s_KeysToDestroy = new List<ulong>();
internal static void CleanBuffer()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_CleanBuffer.Begin();
#endif
foreach (var pair in s_BufferQueues)
{
while (pair.Value.Count > 0 && Time.realtimeSinceStartup - pair.Value.Peek().BufferTime >= NetworkManager.Singleton.NetworkConfig.MessageBufferTimeout)
{
BufferedMessage message = pair.Value.Dequeue();
RecycleConsumedBufferedMessage(message);
}
if (pair.Value.Count == 0)
{
s_KeysToDestroy.Add(pair.Key);
}
}
for (int i = 0; i < s_KeysToDestroy.Count; i++)
{
s_BufferQueues.Remove(s_KeysToDestroy[i]);
}
s_KeysToDestroy.Clear();
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_CleanBuffer.End();
#endif
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c9a727fa87a3a54092293945f782ada
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
using MLAPI.Transports;
namespace MLAPI.Messaging.Buffering
{
internal struct PreBufferPreset
{
public byte MessageType;
public bool AllowBuffer;
public ulong ClientId;
public NetworkChannel NetworkChannel;
public float ReceiveTime;
public ArraySegment<byte> Data;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab6c3ab10d7bdb048a8c1cea7e8950b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,226 @@
using System.Collections.Generic;
using System.IO;
using MLAPI.Configuration;
using MLAPI.Logging;
using MLAPI.Serialization;
using MLAPI.Serialization.Pooled;
using MLAPI.Hashing;
using MLAPI.Profiling;
using MLAPI.Transports;
namespace MLAPI.Messaging
{
/// <summary>
/// The manager class to manage custom messages, note that this is different from the NetworkManager custom messages.
/// These are named and are much easier to use.
/// </summary>
public static class CustomMessagingManager
{
#region Unnamed
/// <summary>
/// Delegate used for incoming unnamed messages
/// </summary>
/// <param name="clientId">The clientId that sent the message</param>
/// <param name="stream">The stream containing the message data</param>
public delegate void UnnamedMessageDelegate(ulong clientId, Stream stream);
/// <summary>
/// Event invoked when unnamed messages arrive
/// </summary>
public static event UnnamedMessageDelegate OnUnnamedMessage;
internal static void InvokeUnnamedMessage(ulong clientId, Stream stream) => OnUnnamedMessage?.Invoke(clientId, stream);
/// <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>
/// 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);
}
#endregion
#region Named
/// <summary>
/// Delegate used to handle named messages
/// </summary>
public delegate void HandleNamedMessageDelegate(ulong sender, Stream payload);
private static Dictionary<ulong, HandleNamedMessageDelegate> s_NamedMessageHandlers16 = new Dictionary<ulong, HandleNamedMessageDelegate>();
private static Dictionary<ulong, HandleNamedMessageDelegate> s_NamedMessageHandlers32 = new Dictionary<ulong, HandleNamedMessageDelegate>();
private static Dictionary<ulong, HandleNamedMessageDelegate> s_NamedMessageHandlers64 = new Dictionary<ulong, HandleNamedMessageDelegate>();
internal static void InvokeNamedMessage(ulong hash, ulong sender, Stream stream)
{
if (NetworkManager.Singleton == null)
{
// We dont know what size to use. Try every (more collision prone)
if (s_NamedMessageHandlers16.ContainsKey(hash))
{
s_NamedMessageHandlers16[hash](sender, stream);
}
if (s_NamedMessageHandlers32.ContainsKey(hash))
{
s_NamedMessageHandlers32[hash](sender, stream);
}
if (s_NamedMessageHandlers64.ContainsKey(hash))
{
s_NamedMessageHandlers64[hash](sender, stream);
}
}
else
{
// Only check the right size.
if (NetworkManager.Singleton.NetworkConfig.RpcHashSize == HashSize.VarIntTwoBytes)
{
if (s_NamedMessageHandlers16.ContainsKey(hash))
{
s_NamedMessageHandlers16[hash](sender, stream);
}
}
else if (NetworkManager.Singleton.NetworkConfig.RpcHashSize == HashSize.VarIntFourBytes)
{
if (s_NamedMessageHandlers32.ContainsKey(hash))
{
s_NamedMessageHandlers32[hash](sender, stream);
}
}
else if (NetworkManager.Singleton.NetworkConfig.RpcHashSize == HashSize.VarIntEightBytes)
{
if (s_NamedMessageHandlers64.ContainsKey(hash))
{
s_NamedMessageHandlers64[hash](sender, stream);
}
}
}
}
/// <summary>
/// Registers a named message handler delegate.
/// </summary>
/// <param name="name">Name of the message.</param>
/// <param name="callback">The callback to run when a named message is received.</param>
public static void RegisterNamedMessageHandler(string name, HandleNamedMessageDelegate callback)
{
s_NamedMessageHandlers16[name.GetStableHash16()] = callback;
s_NamedMessageHandlers32[name.GetStableHash32()] = callback;
s_NamedMessageHandlers64[name.GetStableHash64()] = callback;
}
/// <summary>
/// Unregisters a named message handler.
/// </summary>
/// <param name="name">The name of the message.</param>
public static void UnregisterNamedMessageHandler(string name)
{
s_NamedMessageHandlers16.Remove(name.GetStableHash16());
s_NamedMessageHandlers32.Remove(name.GetStableHash32());
s_NamedMessageHandlers64.Remove(name.GetStableHash64());
}
/// <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>
/// 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);
}
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15c647dad40a44d46886dca112cfd524
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,642 @@
using System;
using System.IO;
using MLAPI.Connection;
using MLAPI.Logging;
using MLAPI.SceneManagement;
using MLAPI.Serialization.Pooled;
using MLAPI.Spawning;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using MLAPI.Configuration;
using MLAPI.Messaging.Buffering;
using MLAPI.Profiling;
using MLAPI.Serialization;
using Unity.Profiling;
namespace MLAPI.Messaging
{
internal static class InternalMessageHandler
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
private static ProfilerMarker s_HandleConnectionRequest = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleConnectionRequest)}");
private static ProfilerMarker s_HandleConnectionApproved = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleConnectionApproved)}");
private static ProfilerMarker s_HandleAddObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObject)}");
private static ProfilerMarker s_HandleDestroyObject = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObject)}");
private static ProfilerMarker s_HandleSwitchScene = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleSwitchScene)}");
private static ProfilerMarker s_HandleClientSwitchSceneCompleted = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleClientSwitchSceneCompleted)}");
private static ProfilerMarker s_HandleChangeOwner = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleChangeOwner)}");
private static ProfilerMarker s_HandleAddObjects = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleAddObjects)}");
private static ProfilerMarker s_HandleDestroyObjects = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleDestroyObjects)}");
private static ProfilerMarker s_HandleTimeSync = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleTimeSync)}");
private static ProfilerMarker s_HandleNetworkVariableDelta = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNetworkVariableDelta)}");
private static ProfilerMarker s_HandleNetworkVariableUpdate = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNetworkVariableUpdate)}");
private static ProfilerMarker s_HandleUnnamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleUnnamedMessage)}");
private static ProfilerMarker s_HandleNamedMessage = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNamedMessage)}");
private static ProfilerMarker s_HandleNetworkLog = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(HandleNetworkLog)}");
private static ProfilerMarker s_RpcReceiveQueueItemServerRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(RpcReceiveQueueItem)}.{nameof(RpcQueueContainer.QueueItemType.ServerRpc)}");
private static ProfilerMarker s_RpcReceiveQueueItemClientRpc = new ProfilerMarker($"{nameof(InternalMessageHandler)}.{nameof(RpcReceiveQueueItem)}.{nameof(RpcQueueContainer.QueueItemType.ClientRpc)}");
#endif
internal static void HandleConnectionRequest(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleConnectionRequest.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
ulong configHash = reader.ReadUInt64Packed();
if (!NetworkManager.Singleton.NetworkConfig.CompareConfig(configHash))
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match");
}
NetworkManager.Singleton.DisconnectClient(clientId);
return;
}
if (NetworkManager.Singleton.NetworkConfig.ConnectionApproval)
{
byte[] connectionBuffer = reader.ReadByteArray();
NetworkManager.Singleton.InvokeConnectionApproval(connectionBuffer, clientId, (createPlayerObject, playerPrefabHash, approved, position, rotation) => { NetworkManager.Singleton.HandleApproval(clientId, createPlayerObject, playerPrefabHash, approved, position, rotation); });
}
else
{
NetworkManager.Singleton.HandleApproval(clientId, NetworkManager.Singleton.NetworkConfig.CreatePlayerPrefab, null, true, null, null);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleConnectionRequest.End();
#endif
}
internal static void HandleConnectionApproved(ulong clientId, Stream stream, float receiveTime)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleConnectionApproved.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
NetworkManager.Singleton.LocalClientId = reader.ReadUInt64Packed();
uint sceneIndex = 0;
Guid sceneSwitchProgressGuid = new Guid();
if (NetworkManager.Singleton.NetworkConfig.EnableSceneManagement)
{
sceneIndex = reader.ReadUInt32Packed();
sceneSwitchProgressGuid = new Guid(reader.ReadByteArray());
}
bool sceneSwitch = NetworkManager.Singleton.NetworkConfig.EnableSceneManagement && NetworkSceneManager.HasSceneMismatch(sceneIndex);
float netTime = reader.ReadSinglePacked();
NetworkManager.Singleton.UpdateNetworkTime(clientId, netTime, receiveTime, true);
NetworkManager.Singleton.ConnectedClients.Add(NetworkManager.Singleton.LocalClientId, new NetworkClient { ClientId = NetworkManager.Singleton.LocalClientId });
void DelayedSpawnAction(Stream continuationStream)
{
using (var continuationReader = PooledNetworkReader.Get(continuationStream))
{
if (!NetworkManager.Singleton.NetworkConfig.EnableSceneManagement || NetworkManager.Singleton.NetworkConfig.UsePrefabSync)
{
NetworkSpawnManager.DestroySceneObjects();
}
else
{
NetworkSpawnManager.ClientCollectSoftSyncSceneObjectSweep(null);
}
uint objectCount = continuationReader.ReadUInt32Packed();
for (int i = 0; i < objectCount; i++)
{
bool isPlayerObject = continuationReader.ReadBool();
ulong networkId = continuationReader.ReadUInt64Packed();
ulong ownerId = continuationReader.ReadUInt64Packed();
bool hasParent = continuationReader.ReadBool();
ulong? parentNetworkId = null;
if (hasParent)
{
parentNetworkId = continuationReader.ReadUInt64Packed();
}
ulong prefabHash;
ulong instanceId;
bool softSync;
if (!NetworkManager.Singleton.NetworkConfig.EnableSceneManagement || NetworkManager.Singleton.NetworkConfig.UsePrefabSync)
{
softSync = false;
instanceId = 0;
prefabHash = continuationReader.ReadUInt64Packed();
}
else
{
softSync = continuationReader.ReadBool();
if (softSync)
{
instanceId = continuationReader.ReadUInt64Packed();
prefabHash = 0;
}
else
{
prefabHash = continuationReader.ReadUInt64Packed();
instanceId = 0;
}
}
Vector3? pos = null;
Quaternion? rot = null;
if (continuationReader.ReadBool())
{
pos = new Vector3(continuationReader.ReadSinglePacked(), continuationReader.ReadSinglePacked(), continuationReader.ReadSinglePacked());
rot = Quaternion.Euler(continuationReader.ReadSinglePacked(), continuationReader.ReadSinglePacked(), continuationReader.ReadSinglePacked());
}
var networkObject = NetworkSpawnManager.CreateLocalNetworkObject(softSync, instanceId, prefabHash, parentNetworkId, pos, rot);
NetworkSpawnManager.SpawnNetworkObjectLocally(networkObject, networkId, softSync, isPlayerObject, ownerId, continuationStream, false, 0, true, false);
Queue<BufferManager.BufferedMessage> bufferQueue = BufferManager.ConsumeBuffersForNetworkId(networkId);
// Apply buffered messages
if (bufferQueue != null)
{
while (bufferQueue.Count > 0)
{
BufferManager.BufferedMessage message = bufferQueue.Dequeue();
NetworkManager.Singleton.HandleIncomingData(message.SenderClientId, message.NetworkChannel, new ArraySegment<byte>(message.NetworkBuffer.GetBuffer(), (int)message.NetworkBuffer.Position, (int)message.NetworkBuffer.Length), message.ReceiveTime, false);
BufferManager.RecycleConsumedBufferedMessage(message);
}
}
}
NetworkSpawnManager.CleanDiffedSceneObjects();
NetworkManager.Singleton.IsConnectedClient = true;
NetworkManager.Singleton.InvokeOnClientConnectedCallback(NetworkManager.Singleton.LocalClientId);
}
}
if (sceneSwitch)
{
UnityAction<Scene, Scene> onSceneLoaded = null;
var continuationBuffer = new NetworkBuffer();
continuationBuffer.CopyUnreadFrom(stream);
continuationBuffer.Position = 0;
void OnSceneLoadComplete()
{
SceneManager.activeSceneChanged -= onSceneLoaded;
NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad = false;
DelayedSpawnAction(continuationBuffer);
}
onSceneLoaded = (oldScene, newScene) => { OnSceneLoadComplete(); };
SceneManager.activeSceneChanged += onSceneLoaded;
NetworkSceneManager.OnFirstSceneSwitchSync(sceneIndex, sceneSwitchProgressGuid);
}
else
{
DelayedSpawnAction(stream);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleConnectionApproved.End();
#endif
}
internal static void HandleAddObject(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleAddObject.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
bool isPlayerObject = reader.ReadBool();
ulong networkId = reader.ReadUInt64Packed();
ulong ownerId = reader.ReadUInt64Packed();
bool hasParent = reader.ReadBool();
ulong? parentNetworkId = null;
if (hasParent)
{
parentNetworkId = reader.ReadUInt64Packed();
}
ulong prefabHash;
ulong instanceId;
bool softSync;
if (!NetworkManager.Singleton.NetworkConfig.EnableSceneManagement || NetworkManager.Singleton.NetworkConfig.UsePrefabSync)
{
softSync = false;
instanceId = 0;
prefabHash = reader.ReadUInt64Packed();
}
else
{
softSync = reader.ReadBool();
if (softSync)
{
instanceId = reader.ReadUInt64Packed();
prefabHash = 0;
}
else
{
prefabHash = reader.ReadUInt64Packed();
instanceId = 0;
}
}
Vector3? pos = null;
Quaternion? rot = null;
if (reader.ReadBool())
{
pos = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked());
rot = Quaternion.Euler(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked());
}
bool hasPayload = reader.ReadBool();
int payLoadLength = hasPayload ? reader.ReadInt32Packed() : 0;
var networkObject = NetworkSpawnManager.CreateLocalNetworkObject(softSync, instanceId, prefabHash, parentNetworkId, pos, rot);
NetworkSpawnManager.SpawnNetworkObjectLocally(networkObject, networkId, softSync, isPlayerObject, ownerId, stream, hasPayload, payLoadLength, true, false);
Queue<BufferManager.BufferedMessage> bufferQueue = BufferManager.ConsumeBuffersForNetworkId(networkId);
// Apply buffered messages
if (bufferQueue != null)
{
while (bufferQueue.Count > 0)
{
BufferManager.BufferedMessage message = bufferQueue.Dequeue();
NetworkManager.Singleton.HandleIncomingData(message.SenderClientId, message.NetworkChannel, new ArraySegment<byte>(message.NetworkBuffer.GetBuffer(), (int)message.NetworkBuffer.Position, (int)message.NetworkBuffer.Length), message.ReceiveTime, false);
BufferManager.RecycleConsumedBufferedMessage(message);
}
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleAddObject.End();
#endif
}
internal static void HandleDestroyObject(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleDestroyObject.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
ulong networkId = reader.ReadUInt64Packed();
NetworkSpawnManager.OnDestroyObject(networkId, true);
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleDestroyObject.End();
#endif
}
internal static void HandleSwitchScene(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleSwitchScene.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
uint sceneIndex = reader.ReadUInt32Packed();
Guid switchSceneGuid = new Guid(reader.ReadByteArray());
var objectBuffer = new NetworkBuffer();
objectBuffer.CopyUnreadFrom(stream);
objectBuffer.Position = 0;
NetworkSceneManager.OnSceneSwitch(sceneIndex, switchSceneGuid, objectBuffer);
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleSwitchScene.End();
#endif
}
internal static void HandleClientSwitchSceneCompleted(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleClientSwitchSceneCompleted.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
NetworkSceneManager.OnClientSwitchSceneCompleted(clientId, new Guid(reader.ReadByteArray()));
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleClientSwitchSceneCompleted.End();
#endif
}
internal static void HandleChangeOwner(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleChangeOwner.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
ulong networkId = reader.ReadUInt64Packed();
ulong ownerClientId = reader.ReadUInt64Packed();
if (NetworkSpawnManager.SpawnedObjects[networkId].OwnerClientId == NetworkManager.Singleton.LocalClientId)
{
//We are current owner.
NetworkSpawnManager.SpawnedObjects[networkId].InvokeBehaviourOnLostOwnership();
}
if (ownerClientId == NetworkManager.Singleton.LocalClientId)
{
//We are new owner.
NetworkSpawnManager.SpawnedObjects[networkId].InvokeBehaviourOnGainedOwnership();
}
NetworkSpawnManager.SpawnedObjects[networkId].OwnerClientId = ownerClientId;
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleChangeOwner.End();
#endif
}
internal static void HandleAddObjects(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleAddObjects.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
ushort objectCount = reader.ReadUInt16Packed();
for (int i = 0; i < objectCount; i++)
{
HandleAddObject(clientId, stream);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleAddObjects.End();
#endif
}
internal static void HandleDestroyObjects(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleDestroyObjects.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
ushort objectCount = reader.ReadUInt16Packed();
for (int i = 0; i < objectCount; i++)
{
HandleDestroyObject(clientId, stream);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleDestroyObjects.End();
#endif
}
internal static void HandleTimeSync(ulong clientId, Stream stream, float receiveTime)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleTimeSync.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
float netTime = reader.ReadSinglePacked();
NetworkManager.Singleton.UpdateNetworkTime(clientId, netTime, receiveTime);
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleTimeSync.End();
#endif
}
internal static void HandleNetworkVariableDelta(ulong clientId, Stream stream, Action<ulong, PreBufferPreset> bufferCallback, PreBufferPreset bufferPreset)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkVariableDelta.Begin();
#endif
if (!NetworkManager.Singleton.NetworkConfig.EnableNetworkVariable)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_DELTA)} received but {nameof(NetworkConfig.EnableNetworkVariable)} is false");
}
return;
}
using (var reader = PooledNetworkReader.Get(stream))
{
ulong networkObjectId = reader.ReadUInt64Packed();
ushort networkBehaviourIndex = reader.ReadUInt16Packed();
if (NetworkSpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
NetworkBehaviour instance = NetworkSpawnManager.SpawnedObjects[networkObjectId].GetNetworkBehaviourAtOrderIndex(networkBehaviourIndex);
if (instance == null)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_DELTA)} message received for a non-existent behaviour. {nameof(networkObjectId)}: {networkObjectId}, {nameof(networkBehaviourIndex)}: {networkBehaviourIndex}");
}
}
else
{
NetworkBehaviour.HandleNetworkVariableDeltas(instance.NetworkVariableFields, stream, clientId, instance);
}
}
else if (NetworkManager.Singleton.IsServer || !NetworkManager.Singleton.NetworkConfig.EnableMessageBuffering)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_DELTA)} message received for a non-existent object with {nameof(networkObjectId)}: {networkObjectId}. This delta was lost.");
}
}
else
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_DELTA)} message received for a non-existent object with {nameof(networkObjectId)}: {networkObjectId}. This delta will be buffered and might be recovered.");
}
bufferCallback(networkObjectId, bufferPreset);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkVariableDelta.End();
#endif
}
internal static void HandleNetworkVariableUpdate(ulong clientId, Stream stream, Action<ulong, PreBufferPreset> bufferCallback, PreBufferPreset bufferPreset)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkVariableUpdate.Begin();
#endif
if (!NetworkManager.Singleton.NetworkConfig.EnableNetworkVariable)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_UPDATE)} update received but {nameof(NetworkConfig.EnableNetworkVariable)} is false");
}
return;
}
using (var reader = PooledNetworkReader.Get(stream))
{
ulong networkObjectId = reader.ReadUInt64Packed();
ushort networkBehaviourIndex = reader.ReadUInt16Packed();
if (NetworkSpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
{
var networkBehaviour = NetworkSpawnManager.SpawnedObjects[networkObjectId].GetNetworkBehaviourAtOrderIndex(networkBehaviourIndex);
if (networkBehaviour == null)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_UPDATE)} message received for a non-existent behaviour. {nameof(networkObjectId)}: {networkObjectId}, {nameof(networkBehaviourIndex)}: {networkBehaviourIndex}");
}
}
else
{
NetworkBehaviour.HandleNetworkVariableUpdate(networkBehaviour.NetworkVariableFields, stream, clientId, networkBehaviour);
}
}
else if (NetworkManager.Singleton.IsServer || !NetworkManager.Singleton.NetworkConfig.EnableMessageBuffering)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_UPDATE)} message received for a non-existent object with {nameof(networkObjectId)}: {networkObjectId}. This delta was lost.");
}
}
else
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
{
NetworkLog.LogWarning($"{nameof(NetworkConstants.NETWORK_VARIABLE_UPDATE)} message received for a non-existent object with {nameof(networkObjectId)}: {networkObjectId}. This delta will be buffered and might be recovered.");
}
bufferCallback(networkObjectId, bufferPreset);
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkVariableUpdate.End();
#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.RpcReceived);
#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
}
internal static void HandleUnnamedMessage(ulong clientId, Stream stream)
{
PerformanceDataManager.Increment(ProfilerConstants.UnnamedMessageReceived);
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
}
internal static void HandleNamedMessage(ulong clientId, Stream stream)
{
PerformanceDataManager.Increment(ProfilerConstants.NamedMessageReceived);
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 HandleNetworkLog(ulong clientId, Stream stream)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkLog.Begin();
#endif
using (var reader = PooledNetworkReader.Get(stream))
{
NetworkLog.LogType logType = (NetworkLog.LogType)reader.ReadByte();
string message = reader.ReadStringPacked();
switch (logType)
{
case NetworkLog.LogType.Info:
NetworkLog.LogInfoServerLocal(message, clientId);
break;
case NetworkLog.LogType.Warning:
NetworkLog.LogWarningServerLocal(message, clientId);
break;
case NetworkLog.LogType.Error:
NetworkLog.LogErrorServerLocal(message, clientId);
break;
}
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleNetworkLog.End();
#endif
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b0dde7481361454ab22f5fca90c4c24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using MLAPI.Configuration;
using MLAPI.Internal;
using MLAPI.Profiling;
using MLAPI.Serialization;
using MLAPI.Transports;
namespace MLAPI.Messaging
{
internal static class InternalMessageSender
{
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
}
}
internal static void Send(byte messageType, NetworkChannel networkChannel, 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.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 static 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 (NetworkManager.Singleton.IsServer && clientIds[i] == NetworkManager.Singleton.ServerClientId) continue;
NetworkManager.Singleton.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
}
}
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
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac70d2c412d5de844a7e2ebdc3fe5123
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,43 @@
using MLAPI.Logging;
using MLAPI.Serialization;
using MLAPI.Configuration;
using MLAPI.Serialization.Pooled;
namespace MLAPI.Internal
{
internal static class MessagePacker
{
// This method is responsible for unwrapping a message, that is extracting the messagebody.
internal static NetworkBuffer UnwrapMessage(NetworkBuffer inputBuffer, out byte messageType)
{
using (var inputHeaderReader = PooledNetworkReader.Get(inputBuffer))
{
if (inputBuffer.Length < 1)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) NetworkLog.LogError("The incoming message was too small");
messageType = NetworkConstants.INVALID;
return null;
}
messageType = inputHeaderReader.ReadByteDirect();
// The input stream is now ready to be read from. It's "safe" and has the correct position
return inputBuffer;
}
}
internal static NetworkBuffer WrapMessage(byte messageType, NetworkBuffer messageBody)
{
var outStream = PooledNetworkBuffer.Get();
using (var outWriter = PooledNetworkWriter.Get(outStream))
{
outWriter.WriteByte(messageType);
outStream.Write(messageBody.GetBuffer(), 0, (int)messageBody.Length);
}
return outStream;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3611708e433eef74c9d8dfb6c311d61e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,51 @@
using System;
namespace MLAPI.Messaging
{
/// <summary>
/// RPC delivery types
/// </summary>
public enum RpcDelivery
{
/// <summary>
/// Reliable delivery
/// </summary>
Reliable = 0,
/// <summary>
/// Unreliable delivery
/// </summary>
Unreliable
}
/// <summary>
/// <para>Represents the common base class for Rpc attributes.</para>
/// </summary>
public abstract class RpcAttribute : Attribute
{
/// <summary>
/// Type of RPC delivery method
/// </summary>
public RpcDelivery Delivery = RpcDelivery.Reliable;
}
/// <summary>
/// <para>Marks a method as ServerRpc.</para>
/// <para>A ServerRpc marked method will be fired by a client but executed on the server.</para>
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class ServerRpcAttribute : RpcAttribute
{
/// <summary>
/// Whether or not the ServerRpc should only be run if executed by the owner of the object
/// </summary>
public bool RequireOwnership = true;
}
/// <summary>
/// <para>Marks a method as ClientRpc.</para>
/// <para>A ClientRpc marked method will be fired by the server but executed on clients.</para>
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class ClientRpcAttribute : RpcAttribute { }
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac745c8131b811e4887a83fa9e65b4af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,225 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using MLAPI.Serialization.Pooled;
using MLAPI.Serialization;
using MLAPI.Configuration;
using MLAPI.Profiling;
using MLAPI.Transports;
namespace MLAPI.Messaging
{
internal class RpcBatcher
{
public class SendStream
{
public NetworkChannel NetworkChannel;
public PooledNetworkBuffer Buffer;
public PooledNetworkWriter Writer;
public bool IsEmpty = true;
public SendStream()
{
Buffer = PooledNetworkBuffer.Get();
Writer = PooledNetworkWriter.Get(Buffer);
}
}
// Stores the stream of batched RPC to send to each client, by ClientId
private readonly Dictionary<ulong, SendStream> k_SendDict = new Dictionary<ulong, SendStream>();
// Used to store targets, internally
private ulong[] m_TargetList = new ulong[0];
// Used to mark longer lengths. Works because we can't have zero-sized messages
private const byte k_LongLenMarker = 0;
private void PushLength(int length, ref PooledNetworkWriter writer)
{
// If length is single byte we write it
if (length < 256)
{
writer.WriteByte((byte)length); // write the amounts of bytes that are coming up
}
else
{
// otherwise we write a two-byte length
writer.WriteByte(k_LongLenMarker); // mark larger size
writer.WriteByte((byte)(length % 256)); // write the length modulo 256
writer.WriteByte((byte)(length / 256)); // write the length divided by 256
}
}
private int PopLength(in NetworkBuffer messageBuffer)
{
int read = messageBuffer.ReadByte();
// if we read a non-zero value, we have a single byte length
// or a -1 error we can return
if (read != k_LongLenMarker)
{
return read;
}
// otherwise, a two-byte length follows. We'll read in len1, len2
int len1 = messageBuffer.ReadByte();
if (len1 < 0)
{
// pass errors back to caller
return len1;
}
int len2 = messageBuffer.ReadByte();
if (len2 < 0)
{
// pass errors back to caller
return len2;
}
return len1 + len2 * 256;
}
/// <summary>
/// FillTargetList
/// Fills a list with the ClientId's an item is targeted to
/// </summary>
/// <param name="queueItem">the FrameQueueItem we want targets for</param>
/// <param name="networkIdList">the list to fill</param>
private static void FillTargetList(in RpcFrameQueueItem queueItem, ref ulong[] networkIdList)
{
switch (queueItem.QueueItemType)
{
// todo: revisit .resize() and .ToArry() usage, for performance
case RpcQueueContainer.QueueItemType.ServerRpc:
Array.Resize(ref networkIdList, 1);
networkIdList[0] = queueItem.NetworkId;
break;
default:
// todo: consider the implications of default usage of queueItem.clientIds
case RpcQueueContainer.QueueItemType.ClientRpc:
// copy the list
networkIdList = queueItem.ClientNetworkIds.ToArray();
break;
}
}
/// <summary>
/// QueueItem
/// Add a FrameQueueItem to be sent
/// </summary>queueItem
/// <param name="queueItem">the threshold in bytes</param>
public void QueueItem(in RpcFrameQueueItem queueItem)
{
FillTargetList(queueItem, ref m_TargetList);
foreach (ulong clientId in m_TargetList)
{
if (!k_SendDict.ContainsKey(clientId))
{
// todo: consider what happens if many clients join and leave the game consecutively
// we probably need a cleanup mechanism at some point
k_SendDict[clientId] = new SendStream();
}
if (k_SendDict[clientId].IsEmpty)
{
k_SendDict[clientId].IsEmpty = false;
k_SendDict[clientId].NetworkChannel = queueItem.NetworkChannel;
switch (queueItem.QueueItemType)
{
// 8 bits are used for the message type, which is an NetworkConstants
case RpcQueueContainer.QueueItemType.ServerRpc:
k_SendDict[clientId].Writer.WriteByte(NetworkConstants.SERVER_RPC); // MessageType
break;
case RpcQueueContainer.QueueItemType.ClientRpc:
k_SendDict[clientId].Writer.WriteByte(NetworkConstants.CLIENT_RPC); // MessageType
break;
}
}
// write the amounts of bytes that are coming up
PushLength(queueItem.MessageData.Count, ref k_SendDict[clientId].Writer);
// write the message to send
k_SendDict[clientId].Writer.WriteBytes(queueItem.MessageData.Array, queueItem.MessageData.Count, queueItem.MessageData.Offset);
ProfilerStatManager.BytesSent.Record(queueItem.MessageData.Count);
ProfilerStatManager.RpcsSent.Record();
PerformanceDataManager.Increment(ProfilerConstants.ByteSent, queueItem.MessageData.Count);
PerformanceDataManager.Increment(ProfilerConstants.RpcSent);
}
}
public delegate void SendCallbackType(ulong clientId, SendStream messageStream);
public delegate void ReceiveCallbackType(NetworkBuffer messageStream, RpcQueueContainer.QueueItemType messageType, ulong clientId, float receiveTime);
/// <summary>
/// SendItems
/// Send any batch of RPC that are of length above threshold
/// </summary>
/// <param name="thresholdBytes"> the threshold in bytes</param>
/// <param name="sendCallback"> the function to call for sending the batch</param>
public void SendItems(int thresholdBytes, SendCallbackType sendCallback)
{
foreach (KeyValuePair<ulong, SendStream> entry in k_SendDict)
{
if (!entry.Value.IsEmpty)
{
// read the queued message
int length = (int)k_SendDict[entry.Key].Buffer.Length;
if (length >= thresholdBytes)
{
sendCallback(entry.Key, entry.Value);
// clear the batch that was sent from the SendDict
entry.Value.Buffer.SetLength(0);
entry.Value.Buffer.Position = 0;
entry.Value.IsEmpty = true;
ProfilerStatManager.RpcBatchesSent.Record();
PerformanceDataManager.Increment(ProfilerConstants.RpcBatchesSent);
}
}
}
}
/// <summary>
/// ReceiveItems
/// Process the messageStream and call the callback with individual RPC messages
/// </summary>
/// <param name="messageBuffer"> the messageStream containing the batched RPC</param>
/// <param name="receiveCallback"> the callback to call has type int f(message, type, clientId, time) </param>
/// <param name="messageType"> the message type to pass back to callback</param>
/// <param name="clientId"> the clientId to pass back to callback</param>
/// <param name="receiveTime"> the packet receive time to pass back to callback</param>
public void ReceiveItems(in NetworkBuffer messageBuffer, ReceiveCallbackType receiveCallback, RpcQueueContainer.QueueItemType messageType, ulong clientId, float receiveTime)
{
using (var copy = PooledNetworkBuffer.Get())
{
do
{
// read the length of the next RPC
int rpcSize = PopLength(messageBuffer);
if (rpcSize < 0)
{
// abort if there's an error reading lengths
return;
}
// copy what comes after current stream position
long position = messageBuffer.Position;
copy.SetLength(rpcSize);
copy.Position = 0;
Buffer.BlockCopy(messageBuffer.GetBuffer(), (int)position, copy.GetBuffer(), 0, rpcSize);
receiveCallback(copy, messageType, clientId, receiveTime);
// seek over the RPC
// RPCReceiveQueueItem peeks at content, it doesn't advance
messageBuffer.Seek(rpcSize, SeekOrigin.Current);
} while (messageBuffer.Position < messageBuffer.Length);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51882ca216b244e40873fb93a17ee153
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
using System;
namespace MLAPI.Messaging
{
public struct ServerRpcSendParams
{
public NetworkUpdateStage UpdateStage;
}
public struct ServerRpcReceiveParams
{
public NetworkUpdateStage UpdateStage;
public ulong SenderClientId;
}
public struct ServerRpcParams
{
public ServerRpcSendParams Send;
public ServerRpcReceiveParams Receive;
}
public struct ClientRpcSendParams
{
public NetworkUpdateStage UpdateStage;
public ulong[] TargetClientIds;
}
public struct ClientRpcReceiveParams
{
public NetworkUpdateStage UpdateStage;
}
public struct ClientRpcParams
{
public ClientRpcSendParams Send;
public ClientRpcReceiveParams Receive;
}
#if UNITY_2020_2_OR_NEWER
// RuntimeAccessModifiersILPP will make this `public`
internal struct __RpcParams
#else
[Obsolete("Please do not use, will no longer be exposed in the future versions (framework internal)")]
public struct __RpcParams
#endif
{
public ServerRpcParams Server;
public ClientRpcParams Client;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: accabc5a65e8a45dda9207fac37d1b24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1bd9e643489acaa4eaaa62e3b10859e3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
using System;
using MLAPI.Transports;
using MLAPI.Serialization.Pooled;
namespace MLAPI.Messaging
{
/// <summary>
/// FrameQueueItem
/// Container structure for RPCs written to the Queue Frame
/// Used for both Inbound and Outbound RPCs
/// NOTE: This structure will change in the near future and is in a state of flux.
/// This will include removing specific properties or changing property types
/// </summary>
internal struct RpcFrameQueueItem
{
public NetworkUpdateStage UpdateStage;
public RpcQueueContainer.QueueItemType QueueItemType;
public ulong NetworkId; //Sender's network Identifier
public NetworkChannel NetworkChannel;
public ulong[] ClientNetworkIds; //Server invoked Client RPCs only
public long StreamSize;
public float Timestamp;
public PooledNetworkWriter NetworkWriter;
public PooledNetworkReader NetworkReader;
public PooledNetworkBuffer NetworkBuffer;
public ArraySegment<byte> MessageData;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f20d27472604314b9c3d9c2662fd97c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,753 @@
using System;
using System.Collections.Generic;
using MLAPI.Serialization;
using MLAPI.Serialization.Pooled;
using MLAPI.Profiling;
using MLAPI.Transports;
namespace MLAPI.Messaging
{
/// <summary>
/// RpcQueueContainer
/// Handles the management of an Rpc Queue
/// </summary>
internal class RpcQueueContainer : INetworkUpdateSystem
{
private const int k_MinQueueHistory = 2; //We need a minimum of 2 queue history buffers in order to properly handle looping back Rpcs when a host
public enum QueueItemType
{
ServerRpc,
ClientRpc,
CreateObject, //MLAPI Constant *** We need to determine if these belong here ***
DestroyObject, //MLAPI Constant
None //Indicates end of frame
}
public enum RpcQueueProcessingTypes
{
Send,
Receive,
}
// Inbound and Outbound QueueHistoryFrames
private readonly Dictionary<RpcQueueHistoryFrame.QueueFrameType, Dictionary<int, Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>>> QueueHistory =
new Dictionary<RpcQueueHistoryFrame.QueueFrameType, Dictionary<int, Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>>>();
private RpcQueueProcessor m_RpcQueueProcessor;
private uint m_OutboundFramesProcessed;
private uint m_InboundFramesProcessed;
private uint m_MaxFrameHistory;
private int m_InboundStreamBufferIndex;
private int m_OutBoundStreamBufferIndex;
private bool m_IsTestingEnabled;
private bool m_ProcessUpdateStagesExternally;
private bool m_IsNotUsingBatching;
public bool IsUsingBatching()
{
return !m_IsNotUsingBatching;
}
public void EnableBatchedRpcs(bool isbatchingEnabled)
{
m_IsNotUsingBatching = !isbatchingEnabled;
}
// INetworkUpdateSystem
public void NetworkUpdate(NetworkUpdateStage updateStage)
{
ProcessAndFlushRpcQueue(RpcQueueProcessingTypes.Receive, updateStage);
if (updateStage == NetworkUpdateStage.PostLateUpdate)
{
ProcessAndFlushRpcQueue(RpcQueueProcessingTypes.Send, 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>
/// AddToInternalMLAPISendQueue
/// NSS-TODO: This will need to be removed once we determine how we want to handle specific
/// internal MLAPI commands relative to RPCS.
/// Example: An network object is destroyed via server side (internal mlapi) command, but prior to this several RPCs are invoked for the to be destroyed object (Client RPC)
/// If both the DestroyObject internal mlapi command and the ClientRPCs are received in the same frame but the internal mlapi DestroyObject command is processed prior to the
/// RPCs being invoked then the object won't exist and additional warnings will be logged that the object no longer exists.
/// The vices versa scenario (create and then RPCs sent) is an unlikely/improbable scenario, but just in case added the CreateObject to this special case scenario.
///
/// To avoid the DestroyObject scenario, the internal MLAPI commands (DestroyObject and CreateObject) are always invoked after RPCs.
/// </summary>
/// <param name="queueItem">item to add to the internal MLAPI queue</param>
public void AddToInternalMLAPISendQueue(RpcFrameQueueItem queueItem)
{
m_RpcQueueProcessor.QueueInternalMLAPICommand(queueItem);
}
/// <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>
/// 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>
/// 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;
}
/// <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>
/// IncrementAndSetQueueHistoryFrame
/// Increments and sets frame count for this queue frame
/// </summary>
/// <param name="rpcQueueFrame">QueueHistoryFrame to be reset</param>
private void IncrementAndSetQueueHistoryFrame(RpcQueueHistoryFrame rpcQueueFrame)
{
if (rpcQueueFrame.GetQueueFrameType() == RpcQueueHistoryFrame.QueueFrameType.Inbound)
{
m_InboundFramesProcessed++;
}
else
{
m_OutboundFramesProcessed++;
}
}
/// <summary>
/// ResetQueueHistoryFrame
/// Resets the queue history frame passed to this method
/// </summary>
/// <param name="rpcQueueFrame">QueueHistoryFrame to be reset</param>
private static void ResetQueueHistoryFrame(RpcQueueHistoryFrame rpcQueueFrame)
{
//If we are dirt and have loopback data then don't clear this frame
if (rpcQueueFrame.IsDirty && !rpcQueueFrame.HasLoopbackData)
{
rpcQueueFrame.TotalSize = 0;
rpcQueueFrame.QueueItemOffsets.Clear();
rpcQueueFrame.QueueBuffer.Position = 0;
rpcQueueFrame.MarkCurrentStreamPosition();
rpcQueueFrame.IsDirty = false;
}
}
/// <summary>
/// AddQueueItemToInboundFrame
/// Adds an RPC queue item to the outbound frame
/// </summary>
/// <param name="qItemType">type of rpc (client or server)</param>
/// <param name="timeStamp">when it was received</param>
/// <param name="sourceNetworkId">who sent the rpc</param>
/// <param name="message">the message being received</param>
internal void AddQueueItemToInboundFrame(QueueItemType qItemType, float timeStamp, ulong sourceNetworkId, NetworkBuffer message)
{
long originalPosition = message.Position;
NetworkUpdateStage updateStage;
using (var reader = PooledNetworkReader.Get(message))
{
var longValue = reader.ReadUInt64Packed(); // NetworkObjectId (temporary, we reset position just below)
var shortValue = reader.ReadUInt16Packed(); // NetworkBehaviourId (temporary, we reset position just below)
updateStage = (NetworkUpdateStage)reader.ReadByteDirect();
}
message.Position = originalPosition;
var rpcQueueHistoryItem = GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, updateStage);
rpcQueueHistoryItem.IsDirty = true;
long StartPosition = rpcQueueHistoryItem.QueueBuffer.Position;
//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);
//Inbound we copy the entire packet and store the position offset
long streamSize = message.Length;
rpcQueueHistoryItem.QueueWriter.WriteInt64(streamSize);
rpcQueueHistoryItem.QueueWriter.WriteInt64(message.Position);
rpcQueueHistoryItem.QueueWriter.WriteBytes(message.GetBuffer(), streamSize);
//Add the packed size to the offsets for parsing over various entries
rpcQueueHistoryItem.QueueItemOffsets.Add((uint)rpcQueueHistoryItem.QueueBuffer.Position);
//Calculate the packed size based on stream progression
rpcQueueHistoryItem.TotalSize += (uint)(rpcQueueHistoryItem.QueueBuffer.Position - StartPosition);
}
/// <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!");
}
}
/// <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>
/// 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>
/// 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>
/// 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];
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
/// <summary>
/// LoopbackSendFrame
/// Will copy the contents of the current outbound QueueHistoryFrame to the current inbound QueueHistoryFrame
/// [NSS]: Leaving this here in the event a portion of this code is useful for doing Batch testing
/// </summary>
public void LoopbackSendFrame()
{
//If we do not have loop back or testing mode enabled then ignore the call
if (m_IsTestingEnabled)
{
var rpcQueueHistoryItemOutbound = GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (rpcQueueHistoryItemOutbound.QueueItemOffsets.Count > 0)
{
//Reset inbound queues based on update stage
foreach (NetworkUpdateStage netUpdateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
var rpcQueueHistoryItemInbound = GetQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, netUpdateStage);
ResetQueueHistoryFrame(rpcQueueHistoryItemInbound);
}
var pooledNetworkBuffer = PooledNetworkBuffer.Get();
var rpcFrameQueueItem = rpcQueueHistoryItemOutbound.GetFirstQueueItem();
while (rpcFrameQueueItem.QueueItemType != QueueItemType.None)
{
pooledNetworkBuffer.SetLength(rpcFrameQueueItem.StreamSize);
pooledNetworkBuffer.Position = 0;
byte[] pooledNetworkStreamArray = pooledNetworkBuffer.GetBuffer();
Buffer.BlockCopy(rpcFrameQueueItem.MessageData.Array ?? Array.Empty<byte>(), rpcFrameQueueItem.MessageData.Offset, pooledNetworkStreamArray, 0, (int)rpcFrameQueueItem.StreamSize);
if (!IsUsingBatching())
{
pooledNetworkBuffer.Position = 1;
}
AddQueueItemToInboundFrame(rpcFrameQueueItem.QueueItemType, UnityEngine.Time.realtimeSinceStartup, rpcFrameQueueItem.NetworkId, pooledNetworkBuffer);
rpcFrameQueueItem = rpcQueueHistoryItemOutbound.GetNextQueueItem();
}
}
}
}
#endif
/// <summary>
/// Initialize
/// This should be called during primary initialization period (typically during NetworkManager's Start method)
/// This will allocate [maxFrameHistory] + [1 currentFrame] number of PooledNetworkBuffers and keep them open until the session ends
/// Note: For zero frame history set maxFrameHistory to zero
/// </summary>
/// <param name="maxFrameHistory"></param>
public void Initialize(uint maxFrameHistory)
{
ClearParameters();
m_RpcQueueProcessor = new RpcQueueProcessor();
m_MaxFrameHistory = maxFrameHistory + k_MinQueueHistory;
if (!QueueHistory.ContainsKey(RpcQueueHistoryFrame.QueueFrameType.Inbound))
{
QueueHistory.Add(RpcQueueHistoryFrame.QueueFrameType.Inbound, new Dictionary<int, Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>>());
}
if (!QueueHistory.ContainsKey(RpcQueueHistoryFrame.QueueFrameType.Outbound))
{
QueueHistory.Add(RpcQueueHistoryFrame.QueueFrameType.Outbound, new Dictionary<int, Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>>());
}
for (int i = 0; i < m_MaxFrameHistory; i++)
{
if (!QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Outbound].ContainsKey(i))
{
QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Outbound].Add(i, new Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>());
var queueHistoryFrame = new RpcQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
queueHistoryFrame.QueueBuffer = PooledNetworkBuffer.Get();
queueHistoryFrame.QueueBuffer.Position = 0;
queueHistoryFrame.QueueWriter = PooledNetworkWriter.Get(queueHistoryFrame.QueueBuffer);
queueHistoryFrame.QueueReader = PooledNetworkReader.Get(queueHistoryFrame.QueueBuffer);
queueHistoryFrame.QueueItemOffsets = new List<uint>();
//For now all outbound, we will always have a single update in which they are processed (LATEUPDATE)
QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Outbound][i].Add(NetworkUpdateStage.PostLateUpdate, queueHistoryFrame);
}
if (!QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Inbound].ContainsKey(i))
{
QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Inbound].Add(i, new Dictionary<NetworkUpdateStage, RpcQueueHistoryFrame>());
//For inbound, we create a queue history frame per update stage
foreach (NetworkUpdateStage netUpdateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
{
var rpcQueueHistoryFrame = new RpcQueueHistoryFrame(RpcQueueHistoryFrame.QueueFrameType.Inbound, netUpdateStage);
rpcQueueHistoryFrame.QueueBuffer = PooledNetworkBuffer.Get();
rpcQueueHistoryFrame.QueueBuffer.Position = 0;
rpcQueueHistoryFrame.QueueWriter = PooledNetworkWriter.Get(rpcQueueHistoryFrame.QueueBuffer);
rpcQueueHistoryFrame.QueueReader = PooledNetworkReader.Get(rpcQueueHistoryFrame.QueueBuffer);
rpcQueueHistoryFrame.QueueItemOffsets = new List<uint>();
QueueHistory[RpcQueueHistoryFrame.QueueFrameType.Inbound][i].Add(netUpdateStage, rpcQueueHistoryFrame);
}
}
}
//As long as this instance is using the pre-defined update stages
if (!m_ProcessUpdateStagesExternally)
{
//Register with the network update loop system
this.RegisterAllNetworkUpdates();
}
}
public void SetTestingState(bool enabled)
{
m_IsTestingEnabled = enabled;
}
public bool IsTesting()
{
return m_IsTestingEnabled;
}
/// <summary>
/// Clears the stream indices and frames process properties
/// </summary>
private void ClearParameters()
{
m_InboundStreamBufferIndex = 0;
m_OutBoundStreamBufferIndex = 0;
m_OutboundFramesProcessed = 0;
m_InboundFramesProcessed = 0;
}
/// <summary>
/// Shutdown
/// Flushes the internal messages
/// Removes itself from the network update loop
/// Disposes readers, writers, clears the queue history, and resets any parameters
/// </summary>
public void Shutdown()
{
//As long as this instance is using the pre-defined update stages
if (!m_ProcessUpdateStagesExternally)
{
//Remove ourself from the network loop update system
this.UnregisterAllNetworkUpdates();
}
//We need to make sure all internal messages (i.e. object destroy) are sent
m_RpcQueueProcessor.InternalMessagesSendAndFlush();
//Dispose of any readers and writers
foreach (var queueHistorySection in QueueHistory)
{
foreach (var queueHistoryItemByStage in queueHistorySection.Value)
{
foreach (var queueHistoryItem in queueHistoryItemByStage.Value)
{
queueHistoryItem.Value.QueueWriter?.Dispose();
queueHistoryItem.Value.QueueReader?.Dispose();
queueHistoryItem.Value.QueueBuffer?.Dispose();
}
}
}
//Clear history and parameters
QueueHistory.Clear();
ClearParameters();
}
/// <summary>
/// RpcQueueContainer - Constructor
/// </summary>
/// <param name="processInternally">determines if it handles processing internally or if it will be done externally</param>
/// <param name="isLoopBackEnabled">turns loopback on or off (primarily debugging purposes)</param>
public RpcQueueContainer(bool processExternally)
{
m_ProcessUpdateStagesExternally = processExternally;
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
<<<<<<< HEAD:com.unity.multiplayer.mlapi/Runtime/Core/Experimental/NetworkRPCQueueManager.cs.meta
guid: 5191aef41bff11b479a8c81d51d2a08d
=======
guid: e2c328a4aae6b492f8999cd803e20bb2
>>>>>>> origin/experimental-stdrpcapi:com.unity.multiplayer.mlapi/Editor/CodeGen/MLAPIRuntimeILPP.cs.meta
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,251 @@
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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 179940fa53696f24a91ff34ce0439d47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Profiling;
using MLAPI.Configuration;
using MLAPI.Profiling;
namespace MLAPI.Messaging
{
/// <summary>
/// RpcQueueProcessing
/// Handles processing of RpcQueues
/// Inbound to invocation
/// Outbound to send
/// </summary>
internal class RpcQueueProcessor
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
private static ProfilerMarker s_ProcessReceiveQueue = new ProfilerMarker($"{nameof(RpcQueueProcessor)}.{nameof(ProcessReceiveQueue)}");
private static ProfilerMarker s_ProcessSendQueue = new ProfilerMarker($"{nameof(RpcQueueProcessor)}.{nameof(ProcessSendQueue)}");
#endif
// Batcher object used to manage the RPC batching on the send side
private readonly RpcBatcher m_RpcBatcher = new RpcBatcher();
private const int k_BatchThreshold = 512;
//NSS-TODO: Need to determine how we want to handle all other MLAPI send types
//Temporary place to keep internal MLAPI messages
private readonly List<RpcFrameQueueItem> m_InternalMLAPISendQueue = new List<RpcFrameQueueItem>();
/// <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.RpcQueueProcessed);
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>
/// ProcessSendQueue
/// Called to send both performance RPC and internal messages and then flush the outbound queue
/// </summary>
public void ProcessSendQueue()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_ProcessSendQueue.Begin();
#endif
RpcQueueSendAndFlush();
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_ProcessSendQueue.End();
#endif
InternalMessagesSendAndFlush();
}
/// <summary>
/// QueueInternalMLAPICommand
/// Added this as an example of how to add internal messages to the outbound send queue
/// </summary>
/// <param name="queueItem">message queue item to add<</param>
public void QueueInternalMLAPICommand(RpcFrameQueueItem queueItem)
{
m_InternalMLAPISendQueue.Add(queueItem);
}
/// <summary>
/// Generic Sending Method for Internal Messages
/// TODO: Will need to open this up for discussion, but we will want to determine if this is how we want internal MLAPI command
/// messages to be sent. We might want specific commands to occur during specific network update regions (see NetworkUpdate
/// </summary>
public void InternalMessagesSendAndFlush()
{
foreach (RpcFrameQueueItem queueItem in m_InternalMLAPISendQueue)
{
var PoolStream = queueItem.NetworkBuffer;
if (NetworkManager.Singleton.IsListening)
{
switch (queueItem.QueueItemType)
{
case RpcQueueContainer.QueueItemType.CreateObject:
{
foreach (ulong clientId in queueItem.ClientNetworkIds)
{
InternalMessageSender.Send(clientId, NetworkConstants.ADD_OBJECT, queueItem.NetworkChannel, PoolStream);
}
PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length);
ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length);
break;
}
case RpcQueueContainer.QueueItemType.DestroyObject:
{
foreach (ulong clientId in queueItem.ClientNetworkIds)
{
InternalMessageSender.Send(clientId, NetworkConstants.DESTROY_OBJECT, queueItem.NetworkChannel, PoolStream);
}
PerformanceDataManager.Increment(ProfilerConstants.RpcSent, queueItem.ClientNetworkIds.Length);
ProfilerStatManager.RpcsSent.Record(queueItem.ClientNetworkIds.Length);
break;
}
}
}
PoolStream.Dispose();
}
m_InternalMLAPISendQueue.Clear();
}
/// <summary>
/// RPCQueueSendAndFlush
/// Sends all RPC queue items in the current outbound frame
/// </summary>
private void RpcQueueSendAndFlush()
{
var advanceFrameHistory = false;
var rpcQueueContainer = NetworkManager.Singleton.RpcQueueContainer;
if (rpcQueueContainer != null)
{
var currentFrame = rpcQueueContainer.GetCurrentFrame(RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
if (currentFrame != null)
{
var currentQueueItem = currentFrame.GetFirstQueueItem();
while (currentQueueItem.QueueItemType != RpcQueueContainer.QueueItemType.None)
{
advanceFrameHistory = true;
if (rpcQueueContainer.IsUsingBatching())
{
m_RpcBatcher.QueueItem(currentQueueItem);
m_RpcBatcher.SendItems(k_BatchThreshold, SendCallback);
}
else
{
SendFrameQueueItem(currentQueueItem);
}
currentQueueItem = currentFrame.GetNextQueueItem();
}
//If the size is < m_BatchThreshold then just send the messages
if (advanceFrameHistory && rpcQueueContainer.IsUsingBatching())
{
m_RpcBatcher.SendItems(0, SendCallback);
}
}
//If we processed any RPCs, then advance to the next frame
if (advanceFrameHistory)
{
rpcQueueContainer.AdvanceFrameHistory(RpcQueueHistoryFrame.QueueFrameType.Outbound);
}
}
}
/// <summary>
/// SendCallback
/// This is the callback from the batcher when it need to send a batch
///
/// </summary>
/// <param name="clientId"> clientId to send to</param>
/// <param name="sendStream"> the stream to send</param>
private static void SendCallback(ulong clientId, RpcBatcher.SendStream sendStream)
{
var length = (int)sendStream.Buffer.Length;
var bytes = sendStream.Buffer.GetBuffer();
var sendBuffer = new ArraySegment<byte>(bytes, 0, length);
NetworkManager.Singleton.NetworkConfig.NetworkTransport.Send(clientId, sendBuffer, sendStream.NetworkChannel);
}
/// <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:
{
NetworkManager.Singleton.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)
{
NetworkManager.Singleton.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;
}
}
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
<<<<<<< HEAD:com.unity.multiplayer.mlapi/Runtime/Core/Experimental/NetworkProcessRPCQueue.cs.meta
guid: ce454f612d3d587498b214111dde1766
=======
guid: 363e6284d2a3f4d0eb19b11f82c399f9
>>>>>>> origin/experimental-stdrpcapi:com.unity.multiplayer.mlapi/Editor/CodeGen/ILPP.cs.meta
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: