Skip to content

feat: Add RPC Name Lookup Table Provided by NetworkBehaviourILPP #875

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
mainModule.GetTypes()
.Where(t => t.IsSubclassOf(CodeGenHelpers.NetworkBehaviour_FullName))
.ToList()
.ForEach(ProcessNetworkBehaviour);
.ForEach(b => ProcessNetworkBehaviour(b, compiledAssembly.Defines));
}
else
{
Expand Down Expand Up @@ -95,6 +95,8 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
private FieldReference m_NetworkManager_LogLevel_FieldRef;
private FieldReference m_NetworkManager_ntable_FieldRef;
private MethodReference m_NetworkManager_ntable_Add_MethodRef;
private FieldReference m_NetworkManager_rpc_name_table_FieldRef;
private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef;
private TypeReference m_NetworkBehaviour_TypeRef;
private MethodReference m_NetworkBehaviour_BeginSendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_EndSendServerRpc_MethodRef;
Expand Down Expand Up @@ -164,6 +166,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
private const string k_NetworkManager_LogLevel = nameof(NetworkManager.LogLevel);
#pragma warning disable 618
private const string k_NetworkManager_ntable = nameof(NetworkManager.__ntable);
private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table);

private const string k_NetworkBehaviour_BeginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc);
private const string k_NetworkBehaviour_EndSendServerRpc = nameof(NetworkBehaviour.__endSendServerRpc);
Expand Down Expand Up @@ -235,6 +238,10 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
m_NetworkManager_ntable_FieldRef = moduleDefinition.ImportReference(fieldInfo);
m_NetworkManager_ntable_Add_MethodRef = moduleDefinition.ImportReference(fieldInfo.FieldType.GetMethod("Add"));
break;
case k_NetworkManager_rpc_name_table:
m_NetworkManager_rpc_name_table_FieldRef = moduleDefinition.ImportReference(fieldInfo);
m_NetworkManager_rpc_name_table_Add_MethodRef = moduleDefinition.ImportReference(fieldInfo.FieldType.GetMethod("Add"));
break;
}
}

Expand Down Expand Up @@ -523,9 +530,13 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
return true;
}

private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] assemblyDefines)
{
var staticHandlers = new List<(uint Hash, MethodDefinition Method)>();
var rpcHandlers = new List<(uint RpcHash, MethodDefinition RpcHandler)>();
var rpcNames = new List<(uint RpcHash, string RpcName)>();

bool isEditorOrDevelopment = assemblyDefines.Contains("UNITY_EDITOR") || assemblyDefines.Contains("DEVELOPMENT_BUILD");

foreach (var methodDefinition in typeDefinition.Methods)
{
var rpcAttribute = CheckAndGetRPCAttribute(methodDefinition);
Expand All @@ -541,10 +552,16 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
}

InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, methodDefHash);
staticHandlers.Add((methodDefHash, GenerateStaticHandler(methodDefinition, rpcAttribute)));

rpcHandlers.Add((methodDefHash, GenerateStaticHandler(methodDefinition, rpcAttribute)));

if (isEditorOrDevelopment)
{
rpcNames.Add((methodDefHash, methodDefinition.Name));
}
}

if (staticHandlers.Count > 0)
if (rpcHandlers.Count > 0 || rpcNames.Count > 0)
{
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
if (staticCtorMethodDef == null)
Expand All @@ -562,24 +579,29 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)

var instructions = new List<Instruction>();
var processor = staticCtorMethodDef.Body.GetILProcessor();
foreach (var (hash, method) in staticHandlers)
{
if (hash == 0 || method == null)
{
continue;
}

typeDefinition.Methods.Add(method);
foreach (var (rpcHash, rpcHandler) in rpcHandlers)
{
typeDefinition.Methods.Add(rpcHandler);

// NetworkManager.__ntable.Add(HandlerHash, HandlerMethod);
// NetworkManager.__ntable.Add(RpcHash, HandleFunc);
instructions.Add(processor.Create(OpCodes.Ldsfld, m_NetworkManager_ntable_FieldRef));
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)hash)));
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcHash)));
instructions.Add(processor.Create(OpCodes.Ldnull));
instructions.Add(processor.Create(OpCodes.Ldftn, method));
instructions.Add(processor.Create(OpCodes.Ldftn, rpcHandler));
instructions.Add(processor.Create(OpCodes.Newobj, m_NetworkHandlerDelegateCtor_MethodRef));
instructions.Add(processor.Create(OpCodes.Call, m_NetworkManager_ntable_Add_MethodRef));
}

foreach (var (rpcHash, rpcName) in rpcNames)
{
// NetworkManager.__rpc_name_table.Add(RpcHash, RpcName);
instructions.Add(processor.Create(OpCodes.Ldsfld, m_NetworkManager_rpc_name_table_FieldRef));
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcHash)));
instructions.Add(processor.Create(OpCodes.Ldstr, rpcName));
instructions.Add(processor.Create(OpCodes.Call, m_NetworkManager_rpc_name_table_Add_MethodRef));
}

instructions.Reverse();
instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if UNITY_2020_2_OR_NEWER
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Unity.CompilationPipeline.Common.Diagnostics;
Expand Down Expand Up @@ -49,7 +50,7 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
switch (typeDefinition.Name)
{
case nameof(NetworkManager):
ProcessNetworkManager(typeDefinition);
ProcessNetworkManager(typeDefinition, compiledAssembly.Defines);
break;
case nameof(NetworkBehaviour):
ProcessNetworkBehaviour(typeDefinition);
Expand Down Expand Up @@ -81,14 +82,22 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
}

private void ProcessNetworkManager(TypeDefinition typeDefinition)
private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assemblyDefines)
{
foreach (var fieldDefinition in typeDefinition.Fields)
{
if (fieldDefinition.Name == nameof(NetworkManager.__ntable))
{
fieldDefinition.IsPublic = true;
}

if (assemblyDefines.Contains("UNITY_EDITOR") || assemblyDefines.Contains("DEVELOPMENT_BUILD"))
{
if (fieldDefinition.Name == nameof(NetworkManager.__rpc_name_table))
{
fieldDefinition.IsPublic = true;
}
}
}
}

Expand Down
15 changes: 13 additions & 2 deletions com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,21 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem, IProfilableTr
#if UNITY_2020_2_OR_NEWER
// RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<uint, Action<NetworkBehaviour, NetworkSerializer, __RpcParams>> __ntable = new Dictionary<uint, Action<NetworkBehaviour, NetworkSerializer, __RpcParams>>();
#else

#if UNITY_EDITOR || DEVELOPMENT_BUILD
// RuntimeAccessModifiersILPP will make this `public`
internal static readonly Dictionary<uint, string> __rpc_name_table = new Dictionary<uint, string>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to figure out the name of the RPC as it happens, rather than keeping a map around?

Asking mostly out of curiosity, because I figure if this the solution we ended up with, it's probably the only way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no other way really. We will use this to retrieve the name when invoking RPC.

Copy link
Contributor

@0xFA11 0xFA11 Jun 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized it'd be nicer to call __ntable as __rpc_func_table too :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want me to do that as part of this PR? I don't think it's a good idea to do it in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah, but I will do that soon.

#else // !(UNITY_EDITOR || DEVELOPMENT_BUILD)
// RuntimeAccessModifiersILPP and NetworkBehaviourILPP requires a variable with this name to exist when outside of Debug build or Editor.
// If the name doesn't exist, generation and compilation errors occurs.
// For the sake of consistency, we are using the same type.
// Final note: this variable will not be used. Since it's never initialized, it will not be in the memory footprint.
internal static readonly Dictionary<uint, string> __rpc_name_table = null;
#endif // UNITY_EDITOR || DEVELOPMENT_BUILD
#else // !UNITY_2020_2_OR_NEWER
[Obsolete("Please do not use, will no longer be exposed in the future versions (framework internal)")]
public static readonly Dictionary<uint, Action<NetworkBehaviour, NetworkSerializer, __RpcParams>> __ntable = new Dictionary<uint, Action<NetworkBehaviour, NetworkSerializer, __RpcParams>>();
#endif
#endif // UNITY_2020_2_OR_NEWER
#pragma warning restore IDE1006 // restore naming rule violation check

#if DEVELOPMENT_BUILD || UNITY_EDITOR
Expand Down