com.unity.netcode.gameobjects@1.7.0

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.7.0] - 2023-10-11

### Added

- exposed NetworkObject.GetNetworkBehaviourAtOrderIndex as a public API (#2724)
- Added context menu tool that provides users with the ability to quickly update the GlobalObjectIdHash value for all in-scene placed prefab instances that were created prior to adding a NetworkObject component to it. (#2707)
- Added methods NetworkManager.SetPeerMTU and NetworkManager.GetPeerMTU to be able to set MTU sizes per-peer (#2676)
- Added `GenerateSerializationForGenericParameterAttribute`, which can be applied to user-created Network Variable types to ensure the codegen generates serialization for the generic types they wrap. (#2694)
- Added `GenerateSerializationForTypeAttribute`, which can be applied to any class or method to ensure the codegen generates serialization for the specific provided type. (#2694)
- Exposed `NetworkVariableSerialization<T>.Read`, `NetworkVariableSerialization<T>.Write`, `NetworkVariableSerialization<T>.AreEqual`, and `NetworkVariableSerialization<T>.Duplicate` to further support the creation of user-created network variables by allowing users to access the generated serialization methods and serialize generic types efficiently without boxing. (#2694)
- Added `NetworkVariableBase.MarkNetworkBehaviourDirty` so that user-created network variable types can mark their containing `NetworkBehaviour` to be processed by the update loop. (#2694)

### Fixed

- Fixed issue where the server side `NetworkSceneManager` instance was not adding the currently active scene to its list of scenes loaded. (#2723)
- Generic NetworkBehaviour types no longer result in compile errors or runtime errors (#2720)
- Rpcs within Generic NetworkBehaviour types can now serialize parameters of the class's generic types (but may not have generic types of their own) (#2720)
- Errors are no longer thrown when entering play mode with domain reload disabled (#2720)
- NetworkSpawn is now correctly called each time when entering play mode with scene reload disabled (#2720)
- NetworkVariables of non-integer types will no longer break the inspector (#2714)
- NetworkVariables with NonSerializedAttribute will not appear in the inspector (#2714)
- Fixed issue where `UnityTransport` would attempt to establish WebSocket connections even if using UDP/DTLS Relay allocations when the build target was WebGL. This only applied to working in the editor since UDP/DTLS can't work in the browser. (#2695)
- Fixed issue where a `NetworkBehaviour` component's `OnNetworkDespawn` was not being invoked on the host-server side for an in-scene placed `NetworkObject` when a scene was unloaded (during a scene transition) and the `NetworkBehaviour` component was positioned/ordered before the `NetworkObject` component. (#2685)
- Fixed issue where `SpawnWithObservers` was not being honored when `NetworkConfig.EnableSceneManagement` was disabled. (#2682)
- Fixed issue where `NetworkAnimator` was not internally tracking changes to layer weights which prevented proper layer weight synchronization back to the original layer weight value. (#2674)
- Fixed "writing past the end of the buffer" error when calling ResetDirty() on managed network variables that are larger than 256 bytes when serialized. (#2670)
- Fixed issue where generation of the `DefaultNetworkPrefabs` asset was not enabled by default. (#2662)
- Fixed issue where the `GlobalObjectIdHash` value could be updated but the asset not marked as dirty. (#2662)
- Fixed issue where the `GlobalObjectIdHash` value of a (network) prefab asset could be assigned an incorrect value when editing the prefab in a temporary scene. (#2662)
- Fixed issue where the `GlobalObjectIdHash` value generated after creating a (network) prefab from an object constructed within the scene would not be the correct final value in a stand alone build. (#2662)

### Changed

- Updated dependency on `com.unity.transport` to version 1.4.0. (#2716)
This commit is contained in:
Unity Technologies
2023-10-11 00:00:00 +00:00
parent b3bd4727ab
commit ffef45b50f
43 changed files with 1783 additions and 322 deletions

View File

@@ -8,6 +8,9 @@ using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
using MethodAttributes = Mono.Cecil.MethodAttributes;
@@ -66,7 +69,7 @@ namespace Unity.Netcode.Editor.CodeGen
{
m_MainModule = mainModule;
if (ImportReferences(mainModule))
if (ImportReferences(mainModule, compiledAssembly.Defines))
{
// process `NetworkBehaviour` types
try
@@ -76,7 +79,38 @@ namespace Unity.Netcode.Editor.CodeGen
.ToList()
.ForEach(b => ProcessNetworkBehaviour(b, compiledAssembly.Defines));
CreateNetworkVariableTypeInitializers(assemblyDefinition);
foreach (var type in mainModule.GetTypes())
{
var resolved = type.Resolve();
foreach (var attribute in resolved.CustomAttributes)
{
if (attribute.AttributeType.Name == nameof(GenerateSerializationForTypeAttribute))
{
var wrappedType = mainModule.ImportReference((TypeReference)attribute.ConstructorArguments[0].Value);
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
}
}
}
foreach (var method in resolved.Methods)
{
foreach (var attribute in method.CustomAttributes)
{
if (attribute.AttributeType.Name == nameof(GenerateSerializationForTypeAttribute))
{
var wrappedType = mainModule.ImportReference((TypeReference)attribute.ConstructorArguments[0].Value);
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
}
}
}
}
}
CreateNetworkVariableTypeInitializers(assemblyDefinition, compiledAssembly.Defines);
}
catch (Exception e)
{
@@ -135,7 +169,7 @@ namespace Unity.Netcode.Editor.CodeGen
return false;
}
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly)
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly, string[] assemblyDefines)
{
var typeDefinition = new TypeDefinition("__GEN", "NetworkVariableSerializationHelper", TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, assembly.MainModule.TypeSystem.Object);
@@ -145,7 +179,15 @@ namespace Unity.Netcode.Editor.CodeGen
MethodAttributes.Static,
assembly.MainModule.TypeSystem.Void);
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
bool isEditor = assemblyDefines.Contains("UNITY_EDITOR");
if (isEditor)
{
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_InitializeOnLoadAttribute_Ctor));
}
else
{
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
}
typeDefinition.Methods.Add(staticCtorMethodDef);
@@ -196,10 +238,7 @@ namespace Unity.Netcode.Editor.CodeGen
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsArray_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(wrappedType);
}
serializeMethod?.GenericArguments.Add(wrappedType);
equalityMethod.GenericArguments.Add(wrappedType);
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
@@ -259,10 +298,7 @@ namespace Unity.Netcode.Editor.CodeGen
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(type);
}
serializeMethod?.GenericArguments.Add(type);
equalityMethod.GenericArguments.Add(type);
}
else
@@ -296,10 +332,7 @@ namespace Unity.Netcode.Editor.CodeGen
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(type);
}
serializeMethod?.GenericArguments.Add(type);
equalityMethod.GenericArguments.Add(type);
}
@@ -328,10 +361,7 @@ namespace Unity.Netcode.Editor.CodeGen
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;
@@ -382,6 +412,7 @@ namespace Unity.Netcode.Editor.CodeGen
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
private MethodReference m_RuntimeInitializeOnLoadAttribute_Ctor;
private MethodReference m_InitializeOnLoadAttribute_Ctor;
private MethodReference m_ExceptionCtorMethodReference;
private MethodReference m_List_NetworkVariableBase_Add;
@@ -457,9 +488,9 @@ namespace Unity.Netcode.Editor.CodeGen
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 +498,12 @@ namespace Unity.Netcode.Editor.CodeGen
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);
@@ -484,7 +517,7 @@ namespace Unity.Netcode.Editor.CodeGen
// CodeGen cannot reference the collections assembly to do a typeof() on it due to a bug that causes that to crash.
private const string k_INativeListBool_FullName = "Unity.Collections.INativeList`1<System.Byte>";
private bool ImportReferences(ModuleDefinition moduleDefinition)
private bool ImportReferences(ModuleDefinition moduleDefinition, string[] assemblyDefines)
{
TypeDefinition debugTypeDef = null;
foreach (var unityTypeDef in m_UnityModule.GetAllTypes())
@@ -496,6 +529,13 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
bool isEditor = assemblyDefines.Contains("UNITY_EDITOR");
if (isEditor)
{
m_InitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(InitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
}
m_RuntimeInitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
TypeDefinition networkManagerTypeDef = null;
@@ -530,7 +570,7 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
if (networkHandlerDelegateTypeDef == null && netcodeTypeDef.Name == nameof(NetworkManager.RpcReceiveHandler))
if (networkHandlerDelegateTypeDef == null && netcodeTypeDef.Name == nameof(NetworkBehaviour.RpcReceiveHandler))
{
networkHandlerDelegateTypeDef = netcodeTypeDef;
continue;
@@ -629,20 +669,6 @@ namespace Unity.Netcode.Editor.CodeGen
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 +708,10 @@ namespace Unity.Netcode.Editor.CodeGen
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 +1125,15 @@ namespace Unity.Netcode.Editor.CodeGen
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] assemblyDefines)
{
var rpcHandlers = new List<(uint RpcMethodId, MethodDefinition RpcHandler)>();
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 +1164,7 @@ namespace Unity.Netcode.Editor.CodeGen
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);
@@ -1146,13 +1178,22 @@ namespace Unity.Netcode.Editor.CodeGen
//var type = field.FieldType;
if (type.IsGenericInstance)
{
if (type.Resolve().Name == typeof(NetworkVariable<>).Name || type.Resolve().Name == typeof(NetworkList<>).Name)
foreach (var attribute in type.Resolve().CustomAttributes)
{
var genericInstanceType = (GenericInstanceType)type;
var wrappedType = genericInstanceType.GenericArguments[0];
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
if (attribute.AttributeType.Name == nameof(GenerateSerializationForGenericParameterAttribute))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
var idx = (int)attribute.ConstructorArguments[0].Value;
var genericInstanceType = (GenericInstanceType)type;
if (idx < 0 || idx >= genericInstanceType.GenericArguments.Count)
{
m_Diagnostics.AddError($"{type} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
continue;
}
var wrappedType = genericInstanceType.GenericArguments[idx];
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
}
}
}
}
@@ -1173,13 +1214,22 @@ namespace Unity.Netcode.Editor.CodeGen
GetAllBaseTypesAndResolveGenerics(type.Resolve(), ref baseTypes, genericParams);
foreach (var baseType in baseTypes)
{
if (baseType.Resolve().Name == typeof(NetworkVariable<>).Name || baseType.Resolve().Name == typeof(NetworkList<>).Name)
foreach (var attribute in baseType.Resolve().CustomAttributes)
{
var genericInstanceType = (GenericInstanceType)baseType;
var wrappedType = genericInstanceType.GenericArguments[0];
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
if (attribute.AttributeType.Name == nameof(GenerateSerializationForGenericParameterAttribute))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
var idx = (int)attribute.ConstructorArguments[0].Value;
var genericInstanceType = (GenericInstanceType)baseType;
if (idx < 0 || idx >= genericInstanceType.GenericArguments.Count)
{
m_Diagnostics.AddError($"{baseType} has a {nameof(GenerateSerializationForGenericParameterAttribute)} referencing a parameter index outside the valid range (0-{genericInstanceType.GenericArguments.Count - 1}");
continue;
}
var wrappedType = genericInstanceType.GenericArguments[idx];
if (!m_WrappedNetworkVariableTypes.Contains(wrappedType))
{
m_WrappedNetworkVariableTypes.Add(wrappedType);
}
}
}
}
@@ -1187,42 +1237,84 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
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<Instruction>();
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<TypeReference>();
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 +1545,27 @@ namespace Unity.Netcode.Editor.CodeGen
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<T>.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<bool>.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 +1782,27 @@ namespace Unity.Netcode.Editor.CodeGen
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<T>.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<bool>.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 +2093,7 @@ namespace Unity.Netcode.Editor.CodeGen
Instruction jumpInstruction = null;
if (!paramType.IsValueType)
if (!paramType.IsValueType && paramType.Resolve() != null)
{
if (!GetWriteMethodForParameter(typeSystem.Boolean, out var boolMethodRef))
{
@@ -2432,7 +2566,7 @@ namespace Unity.Netcode.Editor.CodeGen
Instruction jumpInstruction = null;
if (!paramType.IsValueType)
if (!paramType.IsValueType && paramType.Resolve() != null)
{
if (!GetReadMethodForParameter(typeSystem.Boolean, out var boolMethodRef))
{
@@ -2559,9 +2693,21 @@ namespace Unity.Netcode.Editor.CodeGen
// 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<TypeReference>();
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);

View File

@@ -53,6 +53,7 @@ namespace Unity.Netcode.Editor.CodeGen
ProcessNetworkBehaviour(typeDefinition);
break;
case nameof(__RpcParams):
case nameof(RpcFallbackSerialization):
typeDefinition.IsPublic = true;
break;
}
@@ -79,6 +80,9 @@ namespace Unity.Netcode.Editor.CodeGen
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
}
// TODO: Deprecate...
// This is changing accessibility for values that are no longer used, but since our validator runs
// after ILPP and sees those values as public, they cannot be removed until a major version change.
private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assemblyDefines)
{
foreach (var fieldDefinition in typeDefinition.Fields)
@@ -116,6 +120,10 @@ namespace Unity.Netcode.Editor.CodeGen
{
nestedType.IsNestedFamily = true;
}
if (nestedType.Name == nameof(NetworkBehaviour.RpcReceiveHandler))
{
nestedType.IsNestedPublic = true;
}
}
foreach (var fieldDefinition in typeDefinition.Fields)
@@ -124,6 +132,20 @@ namespace Unity.Netcode.Editor.CodeGen
{
fieldDefinition.IsFamilyOrAssembly = true;
}
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_func_table))
{
fieldDefinition.IsFamilyOrAssembly = true;
}
if (fieldDefinition.Name == nameof(NetworkBehaviour.RpcReceiveHandler))
{
fieldDefinition.IsFamilyOrAssembly = true;
}
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_name_table))
{
fieldDefinition.IsFamilyOrAssembly = true;
}
}
foreach (var methodDefinition in typeDefinition.Methods)
@@ -133,6 +155,8 @@ namespace Unity.Netcode.Editor.CodeGen
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))
{