Skip to content

Commit

Permalink
perf: moving RPC collection to NetworkIdentity
Browse files Browse the repository at this point in the history
  • Loading branch information
James-Frowen committed Jun 11, 2023
1 parent 99aac33 commit 773910c
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 125 deletions.
6 changes: 2 additions & 4 deletions Assets/Mirage/Runtime/ClientObjectManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -708,9 +708,7 @@ internal void OnRpcMessage(RpcMessage msg)
return;
}

var behaviour = identity.NetworkBehaviours[msg.ComponentIndex];

var remoteCall = behaviour.RemoteCallCollection.Get(msg.FunctionIndex);
var remoteCall = identity.RemoteCallCollection.GetAbsolute(msg.FunctionIndex);

if (remoteCall.InvokeType != RpcInvokeType.ClientRpc)
{
Expand All @@ -719,7 +717,7 @@ internal void OnRpcMessage(RpcMessage msg)

using (var reader = NetworkReaderPool.GetReader(msg.Payload, _client.World))
{
remoteCall.Invoke(reader, behaviour, null, 0);
remoteCall.Invoke(reader, null, 0);
}
}

Expand Down
3 changes: 0 additions & 3 deletions Assets/Mirage/Runtime/Messages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public struct SceneReadyMessage { }
public struct ServerRpcMessage
{
public uint NetId;
public int ComponentIndex;
public int FunctionIndex;

// the parameters for the Cmd function
Expand All @@ -51,7 +50,6 @@ public struct ServerRpcMessage
public struct ServerRpcWithReplyMessage
{
public uint NetId;
public int ComponentIndex;
public int FunctionIndex;

// if the server Rpc can return values
Expand All @@ -72,7 +70,6 @@ public struct ServerRpcReply
public struct RpcMessage
{
public uint NetId;
public int ComponentIndex;
public int FunctionIndex;
public ArraySegment<byte> Payload;
}
Expand Down
19 changes: 3 additions & 16 deletions Assets/Mirage/Runtime/NetworkBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,23 +536,10 @@ internal void ResetSyncObjects()
}

#region RPC
// todo move this to NetworkIdentity to optimize (add a registermethod on NB that NI will call)

// overriden by weaver
// overridden by weaver
protected internal virtual int GetRpcCount() => 0;

/// <summary>
/// Collection that holds information about all RPC in this networkbehaviour (including derived classes)
/// <para>Can be used to get RPC name from its index</para>
/// <para>NOTE: Weaver uses this collection to add rpcs, If adding your own rpc do at your own risk</para>
/// </summary>
[NonSerialized]
public readonly RemoteCallCollection RemoteCallCollection;

protected NetworkBehaviour()
{
RemoteCallCollection = new RemoteCallCollection(this);
}
// overridden by weaver
protected internal virtual void RegisterRpc(RemoteCallCollection collection) { }
#endregion

public struct Id : IEquatable<Id>
Expand Down
28 changes: 27 additions & 1 deletion Assets/Mirage/Runtime/NetworkIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using Mirage.Events;
using Mirage.Logging;
using Mirage.RemoteCalls;
using Mirage.Serialization;
using UnityEngine;
using UnityEngine.Serialization;
Expand Down Expand Up @@ -247,7 +248,7 @@ internal void SetOwner(INetworkPlayer player)
// if authority changes, we need to check if we are still allowed to sync to/from this instance
foreach (var comp in NetworkBehaviours)
comp.UpdateSyncObjectShouldSync();

_onOwnerChanged.Invoke(_owner);

// only invoke again if new owner is not null
Expand Down Expand Up @@ -1196,5 +1197,30 @@ public override string ToString()
{
return $"Identity[{NetId}, {name}]";
}


// todo update comment
/// <summary>
/// Collection that holds information about all RPC in this networkbehaviour (including derived classes)
/// <para>Can be used to get RPC name from its index</para>
/// <para>NOTE: Weaver uses this collection to add rpcs, If adding your own rpc do at your own risk</para>
/// </summary>
[NonSerialized]
private RemoteCallCollection _remoteCallCollection;
internal RemoteCallCollection RemoteCallCollection
{
get
{
if (_remoteCallCollection == null)
{
// we shoulld be save to lazy init
// we only need to register RPCs when we receive them
// when sending the index is baked in by weaver
_remoteCallCollection = new RemoteCallCollection();
_remoteCallCollection.RegisterAll(NetworkBehaviours);
}
return _remoteCallCollection;
}
}
}
}
8 changes: 3 additions & 5 deletions Assets/Mirage/Runtime/RemoteCalls/ClientRpcSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,23 @@ public static void SendTarget(NetworkBehaviour behaviour, int index, NetworkWrit
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static RpcMessage CreateMessage(NetworkBehaviour behaviour, int index, NetworkWriter writer)
{
var rpc = behaviour.RemoteCallCollection.Get(index);

Validate(behaviour, rpc);
Validate(behaviour, index);

var message = new RpcMessage
{
NetId = behaviour.NetId,
ComponentIndex = behaviour.ComponentIndex,
FunctionIndex = index,
Payload = writer.ToArraySegment()
};
return message;
}

private static void Validate(NetworkBehaviour behaviour, RemoteCall rpc)
private static void Validate(NetworkBehaviour behaviour, int index)
{
var server = behaviour.Server;
if (server == null || !server.Active)
{
var rpc = behaviour.Identity.RemoteCallCollection.GetRelative(behaviour, index);
throw new InvalidOperationException($"RPC Function {rpc} called when server is not active.");
}
}
Expand Down
108 changes: 54 additions & 54 deletions Assets/Mirage/Runtime/RemoteCalls/RemoteCallHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,43 @@ public class RemoteCallCollection
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(RemoteCallCollection));

public RemoteCall[] remoteCalls;
/// <summary>
/// This is set by NetworkIdentity when we register each NetworkBehaviour so that they can pass their own idnex in
/// </summary>
public int[] IndexOffset;
public RemoteCall[] RemoteCalls;

public RemoteCallCollection(NetworkBehaviour behaviour)
public unsafe void RegisterAll(NetworkBehaviour[] behaviours)
{
remoteCalls = new RemoteCall[behaviour.GetRpcCount()];
var behaviourCount = behaviours.Length;
var totalCount = 0;
var counts = stackalloc int[behaviourCount];
IndexOffset = new int[behaviourCount];
for (var i = 0; i < behaviourCount; i++)
{
counts[i] = behaviours[i].GetRpcCount();
totalCount += counts[i];

if (i > 0)
IndexOffset[i] = IndexOffset[i - 1] + counts[i - 1];
}

RemoteCalls = new RemoteCall[totalCount];
for (var i = 0; i < behaviourCount; i++)
{
behaviours[i].RegisterRpc(this);
}
}

public void Register(int index, Type invokeClass, string name, RpcInvokeType invokerType, RpcDelegate func, bool cmdRequireAuthority)
public void Register(int index, string name, bool cmdRequireAuthority, RpcInvokeType invokerType, NetworkBehaviour behaviour, RpcDelegate func)
{
var indexOffset = GetIndexOffset(behaviour);
// weaver gives index, so should never give 2 indexes that are the same
if (remoteCalls[index] != null)
if (RemoteCalls[indexOffset + index] != null)
throw new InvalidOperationException("2 Rpc has same index");

var call = new RemoteCall(invokeClass, invokerType, func, cmdRequireAuthority, name);
remoteCalls[index] = call;
var call = new RemoteCall(behaviour, invokerType, func, cmdRequireAuthority, name);
RemoteCalls[indexOffset + index] = call;

if (logger.LogEnabled())
{
Expand All @@ -33,7 +55,7 @@ public void Register(int index, Type invokeClass, string name, RpcInvokeType inv
}
}

public void RegisterRequest<T>(int index, Type invokeClass, string name, RequestDelegate<T> func, bool cmdRequireAuthority)
public void RegisterRequest<T>(int index, string name, bool cmdRequireAuthority, NetworkBehaviour behaviour, RequestDelegate<T> func)
{
async UniTaskVoid Wrapper(NetworkBehaviour obj, NetworkReader reader, INetworkPlayer senderPlayer, int replyId)
{
Expand All @@ -58,12 +80,22 @@ void CmdWrapper(NetworkBehaviour obj, NetworkReader reader, INetworkPlayer sende
Wrapper(obj, reader, senderPlayer, replyId).Forget();
}

Register(index, invokeClass, name, RpcInvokeType.ServerRpc, CmdWrapper, cmdRequireAuthority);
Register(index, name, cmdRequireAuthority, RpcInvokeType.ServerRpc, behaviour, CmdWrapper);
}

public int GetIndexOffset(NetworkBehaviour behaviour)
{
return IndexOffset[behaviour.ComponentIndex];
}

public RemoteCall Get(int index)
public RemoteCall GetRelative(NetworkBehaviour behaviour, int index)
{
return remoteCalls[index];
return RemoteCalls[GetIndexOffset(behaviour) + index];
}

public RemoteCall GetAbsolute(int index)
{
return RemoteCalls[index];
}
}
/// <summary>
Expand Down Expand Up @@ -97,62 +129,30 @@ public class RemoteCall
/// <summary>
/// Function to be invoked when receiving message
/// </summary>
public readonly RpcDelegate function;
public readonly RpcDelegate Function;
/// <summary>
/// Used by ServerRpc
/// </summary>
public readonly bool RequireAuthority;
/// <summary>
/// User friendly name
/// </summary>
public readonly string name;
public readonly string Name;

public readonly NetworkBehaviour Behaviour;

public RemoteCall(Type declaringType, RpcInvokeType invokeType, RpcDelegate function, bool requireAuthority, string name)
public RemoteCall(NetworkBehaviour behaviour, RpcInvokeType invokeType, RpcDelegate function, bool requireAuthority, string name)
{
DeclaringType = declaringType;
Behaviour = behaviour;
InvokeType = invokeType;
this.function = function;
Function = function;
RequireAuthority = requireAuthority;
this.name = name;
}

public bool AreEqual(Type declaringType, RpcInvokeType invokeType, RpcDelegate function)
{
if (InvokeType != invokeType)
return false;

if (declaringType.IsGenericType)
return AreEqualIgnoringGeneric(declaringType, function);

return DeclaringType == declaringType
&& this.function == function;
}

private bool AreEqualIgnoringGeneric(Type declaringType, RpcDelegate function)
{
// if this.type not generic, then not equal
if (!DeclaringType.IsGenericType)
return false;

// types must be in same assembly to be equal
if (DeclaringType.Assembly != declaringType.Assembly)
return false;

Debug.Assert(declaringType == function.Method.DeclaringType);
Debug.Assert(DeclaringType == this.function.Method.DeclaringType);

// we check Assembly above, so we know these 2 functions must be in same assmebly here
// - we can check Namespace and Name to acount generic check
// - weaver check to make sure method in type have unique hash
// - weaver appends hash to names, so overloads will have different hash/names
return DeclaringType.Namespace == declaringType.Namespace
&& DeclaringType.Name == declaringType.Name
&& this.function.Method.Name == function.Method.Name;
Name = name;
}

internal void Invoke(NetworkReader reader, NetworkBehaviour invokingType, INetworkPlayer senderPlayer = null, int replyId = 0)
internal void Invoke(NetworkReader reader, INetworkPlayer senderPlayer = null, int replyId = 0)
{
function(invokingType, reader, senderPlayer, replyId);
Function(Behaviour, reader, senderPlayer, replyId);
}

/// <summary>
Expand All @@ -161,7 +161,7 @@ internal void Invoke(NetworkReader reader, NetworkBehaviour invokingType, INetwo
/// <returns></returns>
public override string ToString()
{
return name;
return Name;
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions Assets/Mirage/Runtime/RemoteCalls/ServerRpcSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public static void Send(NetworkBehaviour behaviour, int index, NetworkWriter wri
var message = new ServerRpcMessage
{
NetId = behaviour.NetId,
ComponentIndex = behaviour.ComponentIndex,
FunctionIndex = index,
Payload = writer.ToArraySegment()
};
Expand All @@ -30,7 +29,6 @@ public static UniTask<T> SendWithReturn<T>(NetworkBehaviour behaviour, int index
var message = new ServerRpcWithReplyMessage
{
NetId = behaviour.NetId,
ComponentIndex = behaviour.ComponentIndex,
FunctionIndex = index,
Payload = writer.ToArraySegment()
};
Expand All @@ -46,17 +44,18 @@ public static UniTask<T> SendWithReturn<T>(NetworkBehaviour behaviour, int index

private static void Validate(NetworkBehaviour behaviour, int index, bool requireAuthority)
{
var rpc = behaviour.RemoteCallCollection.Get(index);
var client = behaviour.Client;

if (client == null || !client.Active)
{
var rpc = behaviour.Identity.RemoteCallCollection.GetRelative(behaviour, index);
throw new InvalidOperationException($"ServerRpc Function {rpc} called on server without an active client.");
}

// if authority is required, then client must have authority to send
if (requireAuthority && !behaviour.HasAuthority)
{
var rpc = behaviour.Identity.RemoteCallCollection.GetRelative(behaviour, index);
throw new InvalidOperationException($"Trying to send ServerRpc for object without authority. {rpc}");
}

Expand Down
12 changes: 5 additions & 7 deletions Assets/Mirage/Runtime/ServerObjectManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,26 +322,24 @@ private static void ThrowIfNoCharacter(INetworkPlayer player)
/// <param name="msg"></param>
private void OnServerRpcWithReplyMessage(INetworkPlayer player, ServerRpcWithReplyMessage msg)
{
OnServerRpc(player, msg.NetId, msg.ComponentIndex, msg.FunctionIndex, msg.Payload, msg.ReplyId);
OnServerRpc(player, msg.NetId, msg.FunctionIndex, msg.Payload, msg.ReplyId);
}

private void OnServerRpcMessage(INetworkPlayer player, ServerRpcMessage msg)
{
OnServerRpc(player, msg.NetId, msg.ComponentIndex, msg.FunctionIndex, msg.Payload, default);
OnServerRpc(player, msg.NetId, msg.FunctionIndex, msg.Payload, default);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void OnServerRpc(INetworkPlayer player, uint netId, int componentIndex, int functionIndex, ArraySegment<byte> payload, int replyId)
private void OnServerRpc(INetworkPlayer player, uint netId, int functionIndex, ArraySegment<byte> payload, int replyId)
{
if (!_server.World.TryGetIdentity(netId, out var identity))
{
if (logger.WarnEnabled()) logger.LogWarning($"Spawned object not found when handling ServerRpc message [netId={netId}]");
return;
}

var behaviour = identity.NetworkBehaviours[componentIndex];

var remoteCall = behaviour.RemoteCallCollection.Get(functionIndex);
var remoteCall = identity.RemoteCallCollection.GetAbsolute(functionIndex);

if (remoteCall.InvokeType != RpcInvokeType.ServerRpc)
{
Expand All @@ -361,7 +359,7 @@ private void OnServerRpc(INetworkPlayer player, uint netId, int componentIndex,

using (var reader = NetworkReaderPool.GetReader(payload, _server.World))
{
remoteCall.Invoke(reader, behaviour, player, replyId);
remoteCall.Invoke(reader, player, replyId);
}
}

Expand Down
Loading

0 comments on commit 773910c

Please sign in to comment.