From d86c21d7fc2327c4525669ef20fb023c580ed043 Mon Sep 17 00:00:00 2001 From: Kitty Draper Date: Thu, 14 Sep 2023 16:46:24 -0500 Subject: [PATCH] Support for generic NetworkBehaviour types --- .../Editor/CodeGen/NetworkBehaviourILPP.cs | 193 +++++++++++++----- .../CodeGen/RuntimeAccessModifiersILPP.cs | 49 ++--- .../Runtime/Core/NetworkBehaviour.cs | 44 +++- .../Runtime/Core/NetworkManager.cs | 15 -- .../Runtime/Messaging/Messages/RpcMessages.cs | 8 +- .../NetworkVariableSerialization.cs | 16 ++ .../Tests/Runtime/RpcTests.cs | 43 +++- 7 files changed, 256 insertions(+), 112 deletions(-) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index 03999db128..5313bbb454 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -328,10 +328,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly) private MethodReference m_NetworkManager_getIsServer_MethodRef; private MethodReference m_NetworkManager_getIsClient_MethodRef; private FieldReference m_NetworkManager_LogLevel_FieldRef; - private FieldReference m_NetworkManager_rpc_func_table_FieldRef; - private MethodReference m_NetworkManager_rpc_func_table_Add_MethodRef; - private FieldReference m_NetworkManager_rpc_name_table_FieldRef; - private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef; + private MethodReference m_NetworkBehaviour___registerRpc_MethodRef; private TypeReference m_NetworkBehaviour_TypeRef; private TypeReference m_NetworkVariableBase_TypeRef; private MethodReference m_NetworkVariableBase_Initialize_MethodRef; @@ -457,9 +454,9 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly) private const string k_NetworkManager_IsServer = nameof(NetworkManager.IsServer); private const string k_NetworkManager_IsClient = nameof(NetworkManager.IsClient); private const string k_NetworkManager_LogLevel = nameof(NetworkManager.LogLevel); - private const string k_NetworkManager_rpc_func_table = nameof(NetworkManager.__rpc_func_table); - private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table); + private const string k_NetworkBehaviour_rpc_func_table = nameof(NetworkBehaviour.__rpc_func_table); + private const string k_NetworkBehaviour_rpc_name_table = nameof(NetworkBehaviour.__rpc_name_table); private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage); private const string k_NetworkBehaviour_NetworkVariableFields = nameof(NetworkBehaviour.NetworkVariableFields); private const string k_NetworkBehaviour_beginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc); @@ -467,10 +464,12 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly) private const string k_NetworkBehaviour_beginSendClientRpc = nameof(NetworkBehaviour.__beginSendClientRpc); private const string k_NetworkBehaviour_endSendClientRpc = nameof(NetworkBehaviour.__endSendClientRpc); private const string k_NetworkBehaviour___initializeVariables = nameof(NetworkBehaviour.__initializeVariables); + private const string k_NetworkBehaviour___initializeRpcs = nameof(NetworkBehaviour.__initializeRpcs); private const string k_NetworkBehaviour_createNativeList = nameof(NetworkBehaviour.__createNativeList); private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager); private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId); private const string k_NetworkBehaviour___nameNetworkVariable = nameof(NetworkBehaviour.__nameNetworkVariable); + private const string k_NetworkBehaviour___registerRpc = nameof(NetworkBehaviour.__registerRpc); private const string k_NetworkVariableBase_Initialize = nameof(NetworkVariableBase.Initialize); @@ -530,7 +529,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) continue; } - if (networkHandlerDelegateTypeDef == null && netcodeTypeDef.Name == nameof(NetworkManager.RpcReceiveHandler)) + if (networkHandlerDelegateTypeDef == null && netcodeTypeDef.Name == nameof(NetworkBehaviour.RpcReceiveHandler)) { networkHandlerDelegateTypeDef = netcodeTypeDef; continue; @@ -629,20 +628,6 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) case k_NetworkManager_LogLevel: m_NetworkManager_LogLevel_FieldRef = moduleDefinition.ImportReference(fieldDef); break; - case k_NetworkManager_rpc_func_table: - m_NetworkManager_rpc_func_table_FieldRef = moduleDefinition.ImportReference(fieldDef); - - m_NetworkManager_rpc_func_table_Add_MethodRef = fieldDef.FieldType.Resolve().Methods.First(m => m.Name == "Add"); - m_NetworkManager_rpc_func_table_Add_MethodRef.DeclaringType = fieldDef.FieldType; - m_NetworkManager_rpc_func_table_Add_MethodRef = moduleDefinition.ImportReference(m_NetworkManager_rpc_func_table_Add_MethodRef); - break; - case k_NetworkManager_rpc_name_table: - m_NetworkManager_rpc_name_table_FieldRef = moduleDefinition.ImportReference(fieldDef); - - m_NetworkManager_rpc_name_table_Add_MethodRef = fieldDef.FieldType.Resolve().Methods.First(m => m.Name == "Add"); - m_NetworkManager_rpc_name_table_Add_MethodRef.DeclaringType = fieldDef.FieldType; - m_NetworkManager_rpc_name_table_Add_MethodRef = moduleDefinition.ImportReference(m_NetworkManager_rpc_name_table_Add_MethodRef); - break; } } @@ -682,6 +667,10 @@ private bool ImportReferences(ModuleDefinition moduleDefinition) case k_NetworkBehaviour___nameNetworkVariable: m_NetworkBehaviour___nameNetworkVariable_MethodRef = moduleDefinition.ImportReference(methodDef); break; + case k_NetworkBehaviour___registerRpc: + m_NetworkBehaviour___registerRpc_MethodRef = moduleDefinition.ImportReference(methodDef); + break; + } } @@ -1095,8 +1084,15 @@ private void GetAllBaseTypesAndResolveGenerics(TypeDefinition type, ref List(); - var rpcNames = new List<(uint RpcMethodId, string RpcMethodName)>(); + foreach (var methodDefinition in typeDefinition.Methods) + { + if (methodDefinition.Name == k_NetworkBehaviour___initializeRpcs) + { + // If this hits, we've already generated the method for this class because a child class got processed first. + return; + } + } + var rpcHandlers = new List<(uint RpcMethodId, MethodDefinition RpcHandler, string RpcMethodName)>(); bool isEditorOrDevelopment = assemblyDefines.Contains("UNITY_EDITOR") || assemblyDefines.Contains("DEVELOPMENT_BUILD"); @@ -1127,12 +1123,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, rpcMethodId); - rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId))); - - if (isEditorOrDevelopment) - { - rpcNames.Add((rpcMethodId, methodDefinition.Name)); - } + rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId), methodDefinition.Name)); } GenerateVariableInitialization(typeDefinition); @@ -1187,42 +1178,84 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass } } - if (rpcHandlers.Count > 0 || rpcNames.Count > 0) + if (rpcHandlers.Count > 0) { - var staticCtorMethodDef = new MethodDefinition( - $"InitializeRPCS_{typeDefinition.Name}", - MethodAttributes.Assembly | - MethodAttributes.Static, + var initializeRpcsMethodDef = new MethodDefinition( + k_NetworkBehaviour___initializeRpcs, + MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig, typeDefinition.Module.TypeSystem.Void); - staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor)); - typeDefinition.Methods.Add(staticCtorMethodDef); + initializeRpcsMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + + typeDefinition.Methods.Add(initializeRpcsMethodDef); var instructions = new List(); - var processor = staticCtorMethodDef.Body.GetILProcessor(); + var processor = initializeRpcsMethodDef.Body.GetILProcessor(); - foreach (var (rpcMethodId, rpcHandler) in rpcHandlers) + foreach (var (rpcMethodId, rpcHandler, rpcMethodName) in rpcHandlers) { typeDefinition.Methods.Add(rpcHandler); - // NetworkManager.__rpc_func_table.Add(RpcMethodId, HandleFunc); - instructions.Add(processor.Create(OpCodes.Ldsfld, m_NetworkManager_rpc_func_table_FieldRef)); + MethodReference callMethod = rpcHandler; + if (typeDefinition.HasGenericParameters) + { + var genericTypes = new List(); + foreach (var parameter in typeDefinition.GenericParameters) + { + genericTypes.Add(parameter); + } + callMethod = callMethod.MakeGeneric(genericTypes.ToArray()); + } + + // __registerRpc(RpcMethodId, HandleFunc, methodName); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); instructions.Add(processor.Create(OpCodes.Ldnull)); - instructions.Add(processor.Create(OpCodes.Ldftn, rpcHandler)); + instructions.Add(processor.Create(OpCodes.Ldftn, callMethod)); instructions.Add(processor.Create(OpCodes.Newobj, m_NetworkHandlerDelegateCtor_MethodRef)); - instructions.Add(processor.Create(OpCodes.Call, m_NetworkManager_rpc_func_table_Add_MethodRef)); + instructions.Add(processor.Create(OpCodes.Ldstr, rpcMethodName)); + instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour___registerRpc_MethodRef)); } - foreach (var (rpcMethodId, rpcMethodName) in rpcNames) + // Find the base method... + MethodReference initializeRpcsBaseReference = null; + foreach (var methodDefinition in typeDefinition.BaseType.Resolve().Methods) { - // NetworkManager.__rpc_name_table.Add(RpcMethodId, RpcMethodName); - instructions.Add(processor.Create(OpCodes.Ldsfld, m_NetworkManager_rpc_name_table_FieldRef)); - instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId))); - instructions.Add(processor.Create(OpCodes.Ldstr, rpcMethodName)); - instructions.Add(processor.Create(OpCodes.Call, m_NetworkManager_rpc_name_table_Add_MethodRef)); + if (methodDefinition.Name == k_NetworkBehaviour___initializeRpcs) + { + initializeRpcsBaseReference = m_MainModule.ImportReference(methodDefinition); + break; + } } + if (initializeRpcsBaseReference == null) + { + // If we couldn't find it, we have to go ahead and add it. + // The base class could be in another assembly... that's ok, this won't + // actually save but it'll generate the same method the same way later, + // so this at least allows us to reference it. + ProcessNetworkBehaviour(typeDefinition.BaseType.Resolve(), assemblyDefines); + foreach (var methodDefinition in typeDefinition.BaseType.Resolve().Methods) + { + if (methodDefinition.Name == k_NetworkBehaviour___initializeRpcs) + { + initializeRpcsBaseReference = m_MainModule.ImportReference(methodDefinition); + break; + } + } + } + + if (typeDefinition.BaseType.Resolve().HasGenericParameters) + { + var baseTypeInstance = (GenericInstanceType)typeDefinition.BaseType; + initializeRpcsBaseReference = initializeRpcsBaseReference.MakeGeneric(baseTypeInstance.GenericArguments.ToArray()); + } + + // base.__initializeRpcs(); + instructions.Add(processor.Create(OpCodes.Nop)); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); + instructions.Add(processor.Create(OpCodes.Call, initializeRpcsBaseReference)); + instructions.Add(processor.Create(OpCodes.Nop)); + instructions.Reverse(); instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction)); } @@ -1453,6 +1486,27 @@ private MethodReference GetFastBufferWriterWriteMethod(string name, TypeReferenc private bool GetWriteMethodForParameter(TypeReference paramType, out MethodReference methodRef) { + if (paramType.Resolve() == null) + { + // Handle generic types by passing them to RpcFallbackSerialization + // This just passes directly to NetworkVariableSerialization, but I could not figure out how to + // get ILPP to generate valid code for calling a method of the format + // `GenericClass.StaticMethod(ref T value)` - it would either complain about T being + // defined in another module, or it would end up generating a completely invalid call to a + // random method on another random class. + var serializationHelperType = m_MainModule.ImportReference(typeof(RpcFallbackSerialization)); + + foreach (var method in serializationHelperType.Resolve().Methods) + { + if (method.Name == nameof(NetworkVariableSerialization.Write)) + { + var reference = new GenericInstanceMethod(m_MainModule.ImportReference(method)); + reference.GenericArguments.Add(paramType); + methodRef = reference; + return true; + } + } + } if (paramType.FullName == typeof(short).FullName) { methodRef = m_BytePacker_WriteValueBitPacked_Short_MethodRef; @@ -1669,6 +1723,27 @@ private MethodReference GetFastBufferReaderReadMethod(string name, TypeReference private bool GetReadMethodForParameter(TypeReference paramType, out MethodReference methodRef) { + if (paramType.Resolve() == null) + { + // Handle generic types by passing them to RpcFallbackSerialization + // This just passes directly to NetworkVariableSerialization, but I could not figure out how to + // get ILPP to generate valid code for calling a method of the format + // `GenericClass.StaticMethod(ref T value)` - it would either complain about T being + // defined in another module, or it would end up generating a completely invalid call to a + // random method on another random class. + var serializationHelperType = m_MainModule.ImportReference(typeof(RpcFallbackSerialization)); + + foreach (var method in serializationHelperType.Resolve().Methods) + { + if (method.Name == nameof(NetworkVariableSerialization.Read)) + { + var reference = new GenericInstanceMethod(m_MainModule.ImportReference(method)); + reference.GenericArguments.Add(paramType); + methodRef = reference; + return true; + } + } + } if (paramType.FullName == typeof(short).FullName) { methodRef = m_ByteUnpacker_ReadValueBitPacked_Short_MethodRef; @@ -1959,7 +2034,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA Instruction jumpInstruction = null; - if (!paramType.IsValueType) + if (!paramType.IsValueType && paramType.Resolve() != null) { if (!GetWriteMethodForParameter(typeSystem.Boolean, out var boolMethodRef)) { @@ -2432,7 +2507,7 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition Instruction jumpInstruction = null; - if (!paramType.IsValueType) + if (!paramType.IsValueType && paramType.Resolve() != null) { if (!GetReadMethodForParameter(typeSystem.Boolean, out var boolMethodRef)) { @@ -2559,9 +2634,21 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition // NetworkBehaviour.XXXRpc(...); processor.Emit(OpCodes.Ldarg_0); - processor.Emit(OpCodes.Castclass, methodDefinition.DeclaringType); + var castType = (TypeReference)methodDefinition.DeclaringType; + var callMethod = (MethodReference)methodDefinition; + if (castType.HasGenericParameters) + { + var genericTypes = new List(); + foreach (var parameter in castType.GenericParameters) + { + genericTypes.Add(parameter); + } + castType = castType.MakeGenericInstanceType(genericTypes.ToArray()); + callMethod = callMethod.MakeGeneric(genericTypes.ToArray()); + } + processor.Emit(OpCodes.Castclass, castType); Enumerable.Range(0, paramCount).ToList().ForEach(paramIndex => processor.Emit(OpCodes.Ldloc, paramLocalMap[paramIndex])); - processor.Emit(OpCodes.Callvirt, methodDefinition); + processor.Emit(OpCodes.Callvirt, callMethod); // NetworkBehaviour.__rpc_exec_stage = __RpcExecStage.None; processor.Emit(OpCodes.Ldarg_0); diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs index 51cf65adb5..7023e8f0ee 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/RuntimeAccessModifiersILPP.cs @@ -46,13 +46,11 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) switch (typeDefinition.Name) { - case nameof(NetworkManager): - ProcessNetworkManager(typeDefinition, compiledAssembly.Defines); - break; case nameof(NetworkBehaviour): ProcessNetworkBehaviour(typeDefinition); break; case nameof(__RpcParams): + case nameof(RpcFallbackSerialization): typeDefinition.IsPublic = true; break; } @@ -79,48 +77,37 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics); } - private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assemblyDefines) + private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) { - foreach (var fieldDefinition in typeDefinition.Fields) + foreach (var nestedType in typeDefinition.NestedTypes) { - if (fieldDefinition.Name == nameof(NetworkManager.__rpc_func_table)) - { - fieldDefinition.IsPublic = true; - } - - if (fieldDefinition.Name == nameof(NetworkManager.RpcReceiveHandler)) + if (nestedType.Name == nameof(NetworkBehaviour.__RpcExecStage)) { - fieldDefinition.IsPublic = true; + nestedType.IsNestedFamily = true; } - - if (fieldDefinition.Name == nameof(NetworkManager.__rpc_name_table)) + if (nestedType.Name == nameof(NetworkBehaviour.RpcReceiveHandler)) { - fieldDefinition.IsPublic = true; + nestedType.IsNestedPublic = true; } } - foreach (var nestedTypeDefinition in typeDefinition.NestedTypes) + foreach (var fieldDefinition in typeDefinition.Fields) { - if (nestedTypeDefinition.Name == nameof(NetworkManager.RpcReceiveHandler)) + if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields)) { - nestedTypeDefinition.IsNestedPublic = true; + fieldDefinition.IsFamilyOrAssembly = true; + } + if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_func_table)) + { + fieldDefinition.IsFamilyOrAssembly = true; } - } - } - private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) - { - foreach (var nestedType in typeDefinition.NestedTypes) - { - if (nestedType.Name == nameof(NetworkBehaviour.__RpcExecStage)) + if (fieldDefinition.Name == nameof(NetworkBehaviour.RpcReceiveHandler)) { - nestedType.IsNestedFamily = true; + fieldDefinition.IsFamilyOrAssembly = true; } - } - foreach (var fieldDefinition in typeDefinition.Fields) - { - if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields)) + if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_name_table)) { fieldDefinition.IsFamilyOrAssembly = true; } @@ -133,6 +120,8 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition) methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) || methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc) || methodDefinition.Name == nameof(NetworkBehaviour.__initializeVariables) || + methodDefinition.Name == nameof(NetworkBehaviour.__initializeRpcs) || + methodDefinition.Name == nameof(NetworkBehaviour.__registerRpc) || methodDefinition.Name == nameof(NetworkBehaviour.__nameNetworkVariable) || methodDefinition.Name == nameof(NetworkBehaviour.__createNativeList)) { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 7a79babb67..d20c7228b7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -3,6 +3,7 @@ using Unity.Collections; using UnityEngine; + namespace Unity.Netcode { /// @@ -11,6 +12,18 @@ namespace Unity.Netcode public abstract class NetworkBehaviour : MonoBehaviour { #pragma warning disable IDE1006 // disable naming rule violation check + + // RuntimeAccessModifiersILPP will make this `public` + internal delegate void RpcReceiveHandler(NetworkBehaviour behaviour, FastBufferReader reader, __RpcParams parameters); + + // RuntimeAccessModifiersILPP will make this `public` + internal static readonly Dictionary> __rpc_func_table = new Dictionary>(); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + // RuntimeAccessModifiersILPP will make this `public` + internal static readonly Dictionary> __rpc_name_table = new Dictionary>(); +#endif + // RuntimeAccessModifiersILPP will make this `protected` internal enum __RpcExecStage { @@ -97,7 +110,7 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth bufferWriter.Dispose(); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName)) + if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName)) { NetworkManager.NetworkMetrics.TrackRpcSent( NetworkManager.ServerClientId, @@ -228,7 +241,7 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth bufferWriter.Dispose(); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName)) + if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName)) { if (clientRpcParams.Send.TargetClientIds != null) { @@ -565,6 +578,25 @@ internal virtual void __initializeVariables() // ILPP generates code for all NetworkBehaviour subtypes to initialize each type's network variables. } +#pragma warning disable IDE1006 // disable naming rule violation check + // RuntimeAccessModifiersILPP will make this `protected` + internal virtual void __initializeRpcs() +#pragma warning restore IDE1006 // restore naming rule violation check + { + // ILPP generates code for all NetworkBehaviour subtypes to initialize each type's RPCs. + } + +#pragma warning disable IDE1006 // disable naming rule violation check + // RuntimeAccessModifiersILPP will make this `protected` + internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMethodName) +#pragma warning restore IDE1006 // restore naming rule violation check + { + __rpc_func_table[GetType()][hash] = handler; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + __rpc_name_table[GetType()][hash] = rpcMethodName; +#endif + } + #pragma warning disable IDE1006 // disable naming rule violation check // RuntimeAccessModifiersILPP will make this `protected` // Using this method here because ILPP doesn't seem to let us do visibility modification on properties. @@ -583,6 +615,14 @@ internal void InitializeVariables() m_VarInit = true; + if (!__rpc_func_table.ContainsKey(GetType())) + { + __rpc_func_table[GetType()] = new Dictionary(); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + __rpc_name_table[GetType()] = new Dictionary(); +#endif + __initializeRpcs(); + } __initializeVariables(); { diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 04e6dd4c0b..4d4ddb7351 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -15,21 +15,6 @@ namespace Unity.Netcode [AddComponentMenu("Netcode/Network Manager", -100)] public class NetworkManager : MonoBehaviour, INetworkUpdateSystem { -#pragma warning disable IDE1006 // disable naming rule violation check - - // RuntimeAccessModifiersILPP will make this `public` - internal delegate void RpcReceiveHandler(NetworkBehaviour behaviour, FastBufferReader reader, __RpcParams parameters); - - // RuntimeAccessModifiersILPP will make this `public` - internal static readonly Dictionary __rpc_func_table = new Dictionary(); - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - // RuntimeAccessModifiersILPP will make this `public` - internal static readonly Dictionary __rpc_name_table = new Dictionary(); -#endif - -#pragma warning restore IDE1006 // restore naming rule violation check - public void NetworkUpdate(NetworkUpdateStage updateStage) { switch (updateStage) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs index 59ecb9f531..1798c5c0c5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs @@ -34,7 +34,7 @@ public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkCo return false; } - if (!NetworkManager.__rpc_func_table.ContainsKey(metadata.NetworkRpcMethodId)) + if (!NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()].ContainsKey(metadata.NetworkRpcMethodId)) { return false; } @@ -42,7 +42,7 @@ public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkCo payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (NetworkManager.__rpc_name_table.TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName)) + if (NetworkBehaviour.__rpc_name_table[networkBehaviour.GetType()].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName)) { networkManager.NetworkMetrics.TrackRpcReceived( context.SenderId, @@ -67,7 +67,7 @@ public static void Handle(ref NetworkContext context, ref RpcMetadata metadata, try { - NetworkManager.__rpc_func_table[metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams); + NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams); } catch (Exception ex) { @@ -75,7 +75,7 @@ public static void Handle(ref NetworkContext context, ref RpcMetadata metadata, if (networkManager.LogLevel == LogLevel.Developer) { Debug.Log($"RPC Table Contents"); - foreach (var entry in NetworkManager.__rpc_func_table) + foreach (var entry in NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()]) { Debug.Log($"{entry.Key} | {entry.Value.Method.Name}"); } diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableSerialization.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableSerialization.cs index 9db6031fb7..fb4da8291f 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableSerialization.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableSerialization.cs @@ -1012,4 +1012,20 @@ internal static void Read(FastBufferReader reader, ref T value) Serializer.Read(reader, ref value); } } + + // RuntimeAccessModifiersILPP will make this `public` + // This is just pass-through to NetworkVariableSerialization but is here becaues I could not get ILPP + // to generate code that would successfully call Type.Method(T), but it has no problem calling Type.Method(T) + internal class RpcFallbackSerialization + { + public static void Write(FastBufferWriter writer, ref T value) + { + NetworkVariableSerialization.Write(writer, ref value); + } + + public static void Read(FastBufferReader reader, ref T value) + { + NetworkVariableSerialization.Read(reader, ref value); + } + } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/RpcTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/RpcTests.cs index 19f3bb0dbd..4533194062 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/RpcTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/RpcTests.cs @@ -12,9 +12,23 @@ namespace Unity.Netcode.RuntimeTests { public class RpcTests : NetcodeIntegrationTest { - public class RpcTestNB : NetworkBehaviour + public class GenericRpcTestNB : NetworkBehaviour where T: unmanaged + { + public event Action OnServer_Rpc; + + [ServerRpc] + public void MyServerRpc(T clientId, ServerRpcParams param = default) + { + OnServer_Rpc(clientId, param); + } + } + + public class RpcTestNBFloat : GenericRpcTestNB + { + } + + public class RpcTestNB : GenericRpcTestNB { - public event Action OnServer_Rpc; #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT public event Action, ServerRpcParams> OnNativeListServer_Rpc; #endif @@ -26,12 +40,6 @@ public class RpcTestNB : NetworkBehaviour public event Action OnClient_Rpc; - [ServerRpc] - public void MyServerRpc(ulong clientId, ServerRpcParams param = default) - { - OnServer_Rpc(clientId, param); - } - #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT [ServerRpc] public void MyNativeListServerRpc(NativeList clientId, ServerRpcParams param = default) @@ -67,6 +75,7 @@ public void MyTypedServerRpc(Vector3 param1, Vector3[] param2, protected override void OnCreatePlayerPrefab() { m_PlayerPrefab.AddComponent(); + m_PlayerPrefab.AddComponent(); } [UnityTest] @@ -74,12 +83,15 @@ public IEnumerator TestRpcs() { // This is the *SERVER VERSION* of the *CLIENT PLAYER* RpcTestNB component var serverClientRpcTestNB = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); + var serverClientRpcTestNBFloat = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); // This is the *CLIENT VERSION* of the *CLIENT PLAYER* RpcTestNB component var localClienRpcTestNB = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); + var localClienRpcTestNBFloat = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); // Setup state bool hasReceivedServerRpc = false; + bool hasReceivedFloatServerRpc = false; bool hasReceivedTypedServerRpc = false; bool hasReceivedClientRpcRemotely = false; bool hasReceivedClientRpcLocally = false; @@ -106,6 +118,12 @@ public IEnumerator TestRpcs() Assert.Fail("ServerRpc invoked locally. Weaver failure?"); }; + localClienRpcTestNBFloat.OnServer_Rpc += (clientId, param) => + { + // The RPC invoked locally. (Weaver failure?) + Assert.Fail("ServerRpc (float) invoked locally. Weaver failure?"); + }; + serverClientRpcTestNB.OnServer_Rpc += (clientId, param) => { Debug.Log("ServerRpc received on server object"); @@ -113,6 +131,13 @@ public IEnumerator TestRpcs() hasReceivedServerRpc = true; }; + serverClientRpcTestNBFloat.OnServer_Rpc += (clientId, param) => + { + Debug.Log("ServerRpc (float) received on server object"); + Assert.True(param.Receive.SenderClientId == clientId); + hasReceivedFloatServerRpc = true; + }; + serverClientRpcTestNB.OnClient_Rpc += () => { // The RPC invoked locally. (Weaver failure?) @@ -145,6 +170,7 @@ public IEnumerator TestRpcs() // Send ServerRpc localClienRpcTestNB.MyServerRpc(m_ClientNetworkManagers[0].LocalClientId); + localClienRpcTestNBFloat.MyServerRpc(m_ClientNetworkManagers[0].LocalClientId); // Send TypedServerRpc localClienRpcTestNB.MyTypedServerRpc(vector3, vector3s, @@ -181,6 +207,7 @@ public IEnumerator TestRpcs() yield return WaitForConditionOrTimeOut(() => hasReceivedServerRpc && hasReceivedClientRpcLocally && hasReceivedClientRpcRemotely && hasReceivedTypedServerRpc); Assert.True(hasReceivedServerRpc, "ServerRpc was not received"); + Assert.True(hasReceivedFloatServerRpc, "ServerRpc was not received"); Assert.True(hasReceivedTypedServerRpc, "TypedServerRpc was not received"); Assert.True(hasReceivedClientRpcLocally, "ClientRpc was not locally received on the server"); Assert.True(hasReceivedClientRpcRemotely, "ClientRpc was not remotely received on the client");