com.unity.netcode.gameobjects@1.5.1

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.5.1] - 2023-06-07

### Added

- Added support for serializing `NativeArray<>` and `NativeList<>` in `FastBufferReader`/`FastBufferWriter`, `BufferSerializer`, `NetworkVariable`, and RPCs. (To use `NativeList<>`, add `UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT` to your Scripting Define Symbols in `Project Settings > Player`) (#2375)
- The location of the automatically-created default network prefab list can now be configured (#2544)
- Added: Message size limits (max single message and max fragmented message) can now be set using NetworkManager.MaximumTransmissionUnitSize and NetworkManager.MaximumFragmentedMessageSize for transports that don't work with the default values (#2530)
- Added `NetworkObject.SpawnWithObservers` property (default is true) that when set to false will spawn a `NetworkObject` with no observers and will not be spawned on any client until `NetworkObject.NetworkShow` is invoked. (#2568)

### Fixed

- Fixed: Fixed a null reference in codegen in some projects (#2581)
- Fixed issue where the `OnClientDisconnected` client identifier was incorrect after a pending client connection was denied. (#2569)
- Fixed warning "Runtime Network Prefabs was not empty at initialization time." being erroneously logged when no runtime network prefabs had been added (#2565)
- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)
- Fixed the "Generate Default Network Prefabs List" setting not loading correctly and always reverting to being checked. (#2545)
- Fixed issue where users could not use NetworkSceneManager.VerifySceneBeforeLoading to exclude runtime generated scenes from client synchronization. (#2550)
- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events.  (#2542,#2543)
- Fixed issue where parenting a NetworkTransform under a transform with a scale other than Vector3.one would result in incorrect values on non-authoritative instances. (#2538)
- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)
- Fixed the inspector throwing exceptions when attempting to render `NetworkVariable`s of enum types. (#2529)
- Making a `NetworkVariable` with an `INetworkSerializable` type that doesn't meet the `new()` constraint will now create a compile-time error instead of an editor crash (#2528)
- Fixed Multiplayer Tools package installation docs page link on the NetworkManager popup. (#2526)
- Fixed an exception and error logging when two different objects are shown and hidden on the same frame (#2524)
- Fixed a memory leak in `UnityTransport` that occurred if `StartClient` failed. (#2518)
- Fixed issue where a client could throw an exception if abruptly disconnected from a network session with one or more spawned `NetworkObject`(s). (#2510)
- Fixed issue where invalid endpoint addresses were not being detected and returning false from NGO UnityTransport. (#2496)
- Fixed some errors that could occur if a connection is lost and the loss is detected when attempting to write to the socket. (#2495)

## Changed

- Adding network prefabs before NetworkManager initialization is now supported. (#2565)
- Connecting clients being synchronized now switch to the server's active scene before spawning and synchronizing NetworkObjects. (#2532)
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.4. (#2533)
- Improved performance of NetworkBehaviour initialization by replacing reflection when initializing NetworkVariables with compile-time code generation, which should help reduce hitching during additive scene loads. (#2522)
This commit is contained in:
Unity Technologies
2023-06-07 00:00:00 +00:00
parent b5abc3ff7c
commit 4d70c198bd
119 changed files with 11328 additions and 3164 deletions

View File

@@ -10,6 +10,7 @@ using Unity.Collections;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
using UnityEngine;
using Object = System.Object;
namespace Unity.Netcode.Editor.CodeGen
{
@@ -112,6 +113,60 @@ namespace Unity.Netcode.Editor.CodeGen
return name;
}
public static TypeReference MakeGenericType(this TypeReference self, params TypeReference[] arguments)
{
if (self.GenericParameters.Count != arguments.Length)
{
throw new ArgumentException();
}
var instance = new GenericInstanceType(self);
foreach (var argument in arguments)
{
instance.GenericArguments.Add(argument);
}
return instance;
}
public static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
{
var reference = new MethodReference(self.Name, self.ReturnType)
{
DeclaringType = self.DeclaringType.MakeGenericType(arguments),
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis,
CallingConvention = self.CallingConvention,
};
foreach (var parameter in self.Parameters)
{
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
}
foreach (var generic_parameter in self.GenericParameters)
{
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
}
return reference;
}
public static bool IsSubclassOf(this TypeReference typeReference, TypeReference baseClass)
{
var type = typeReference.Resolve();
if (type?.BaseType == null || type.BaseType.Name == nameof(Object))
{
return false;
}
if (type.BaseType.Resolve() == baseClass.Resolve())
{
return true;
}
return type.BaseType.IsSubclassOf(baseClass);
}
public static bool HasInterface(this TypeReference typeReference, string interfaceTypeFullName)
{

View File

@@ -101,31 +101,28 @@ namespace Unity.Netcode.Editor.CodeGen
private ModuleDefinition m_NetcodeModule;
private PostProcessorAssemblyResolver m_AssemblyResolver;
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
private MethodReference m_MessagingSystem_CreateMessageAndGetVersion_MethodRef;
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
private MethodReference m_MessagingSystem_VersionGetter_Constructor_TypeRef;
private MethodReference m_MessageManager_ReceiveMessage_MethodRef;
private MethodReference m_MessageManager_CreateMessageAndGetVersion_MethodRef;
private TypeReference m_MessageManager_MessageWithHandler_TypeRef;
private MethodReference m_MessageManager_MessageHandler_Constructor_TypeRef;
private MethodReference m_MessageManager_VersionGetter_Constructor_TypeRef;
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef;
private FieldReference m_MessageManager_MessageWithHandler_MessageType_FieldRef;
private FieldReference m_MessageManager_MessageWithHandler_Handler_FieldRef;
private FieldReference m_MessageManager_MessageWithHandler_GetVersion_FieldRef;
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
private MethodReference m_List_Add_MethodRef;
private const string k_ReceiveMessageName = nameof(MessagingSystem.ReceiveMessage);
private const string k_CreateMessageAndGetVersionName = nameof(MessagingSystem.CreateMessageAndGetVersion);
private const string k_ReceiveMessageName = nameof(NetworkMessageManager.ReceiveMessage);
private const string k_CreateMessageAndGetVersionName = nameof(NetworkMessageManager.CreateMessageAndGetVersion);
private bool ImportReferences(ModuleDefinition moduleDefinition)
{
// Different environments seem to have different situations...
// Some have these definitions in netstandard.dll...
// some seem to have them elsewhere...
// Since they're standard .net classes they're not going to cause
// the same issues as referencing other assemblies, in theory, since
// the definitions should be standard and consistent across platforms
// (i.e., there's no #if UNITY_EDITOR in them that could create
// invalid IL code)
// Some have these definitions in netstandard.dll, some seem to have them elsewhere...
// Since they're standard .net classes they're not going to cause the same issues as referencing other assemblies,
// in theory, since the definitions should be standard and consistent across platforms
// (i.e., there's no #if UNITY_EDITOR in them that could create invalid IL code)
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
@@ -133,22 +130,22 @@ namespace Unity.Netcode.Editor.CodeGen
TypeDefinition versionGetterTypeDef = null;
TypeDefinition messageWithHandlerTypeDef = null;
TypeDefinition ilppMessageProviderTypeDef = null;
TypeDefinition messagingSystemTypeDef = null;
TypeDefinition messageManagerSystemTypeDef = null;
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
{
if (messageHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageHandler))
if (messageHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageHandler))
{
messageHandlerTypeDef = netcodeTypeDef;
continue;
}
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.VersionGetter))
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.VersionGetter))
{
versionGetterTypeDef = netcodeTypeDef;
continue;
}
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageWithHandler))
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageWithHandler))
{
messageWithHandlerTypeDef = netcodeTypeDef;
continue;
@@ -160,29 +157,29 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
if (messagingSystemTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem))
if (messageManagerSystemTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager))
{
messagingSystemTypeDef = netcodeTypeDef;
messageManagerSystemTypeDef = netcodeTypeDef;
continue;
}
}
m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
m_MessagingSystem_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());
m_MessageManager_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
m_MessageManager_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());
m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
m_MessageManager_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
{
switch (fieldDef.Name)
{
case nameof(MessagingSystem.MessageWithHandler.MessageType):
m_MessagingSystem_MessageWithHandler_MessageType_FieldRef = moduleDefinition.ImportReference(fieldDef);
case nameof(NetworkMessageManager.MessageWithHandler.MessageType):
m_MessageManager_MessageWithHandler_MessageType_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
case nameof(MessagingSystem.MessageWithHandler.Handler):
m_MessagingSystem_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
case nameof(NetworkMessageManager.MessageWithHandler.Handler):
m_MessageManager_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
case nameof(MessagingSystem.MessageWithHandler.GetVersion):
m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
case nameof(NetworkMessageManager.MessageWithHandler.GetVersion):
m_MessageManager_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
}
}
@@ -219,15 +216,15 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
foreach (var methodDef in messagingSystemTypeDef.Methods)
foreach (var methodDef in messageManagerSystemTypeDef.Methods)
{
switch (methodDef.Name)
{
case k_ReceiveMessageName:
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
m_MessageManager_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
case k_CreateMessageAndGetVersionName:
m_MessagingSystem_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
m_MessageManager_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
}
}
@@ -256,27 +253,27 @@ namespace Unity.Netcode.Editor.CodeGen
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
{
// MessagingSystem.__network_message_types.Add(new MessagingSystem.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
processor.Body.Variables.Add(new VariableDefinition(m_MessagingSystem_MessageWithHandler_TypeRef));
// NetworkMessageManager.__network_message_types.Add(new NetworkMessageManager.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
processor.Body.Variables.Add(new VariableDefinition(m_MessageManager_MessageWithHandler_TypeRef));
int messageWithHandlerLocIdx = processor.Body.Variables.Count - 1;
instructions.Add(processor.Create(OpCodes.Ldsfld, m_ILPPMessageProvider___network_message_types_FieldRef));
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Initobj, m_MessagingSystem_MessageWithHandler_TypeRef));
instructions.Add(processor.Create(OpCodes.Initobj, m_MessageManager_MessageWithHandler_TypeRef));
// tmp.MessageType = typeof(type);
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldtoken, type));
instructions.Add(processor.Create(OpCodes.Call, m_Type_GetTypeFromHandle_MethodRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_MessageType_FieldRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_MessageType_FieldRef));
// tmp.Handler = MessageHandler.ReceveMessage<type>
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldnull));
instructions.Add(processor.Create(OpCodes.Ldftn, receiveMethod));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_MessageHandler_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_Handler_FieldRef));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_MessageHandler_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_Handler_FieldRef));
// tmp.GetVersion = MessageHandler.CreateMessageAndGetVersion<type>
@@ -284,15 +281,15 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(processor.Create(OpCodes.Ldnull));
instructions.Add(processor.Create(OpCodes.Ldftn, versionMethod));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_VersionGetter_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_VersionGetter_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_GetVersion_FieldRef));
// ILPPMessageProvider.__network_message_types.Add(tmp);
instructions.Add(processor.Create(OpCodes.Ldloc, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Callvirt, m_List_Add_MethodRef));
}
// Creates a static module constructor (which is executed when the module is loaded) that registers all the message types in the assembly with MessagingSystem.
// Creates a static module constructor (which is executed when the module is loaded) that registers all the message types in the assembly with NetworkMessageManager.
// This is the same behavior as annotating a static method with [ModuleInitializer] in standardized C# (that attribute doesn't exist in Unity, but the static module constructor still works).
// https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-5.0
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
@@ -310,9 +307,9 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var type in networkMessageTypes)
{
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
var receiveMethod = new GenericInstanceMethod(m_MessageManager_ReceiveMessage_MethodRef);
receiveMethod.GenericArguments.Add(type);
var versionMethod = new GenericInstanceMethod(m_MessagingSystem_CreateMessageAndGetVersion_MethodRef);
var versionMethod = new GenericInstanceMethod(m_MessageManager_CreateMessageAndGetVersion_MethodRef);
versionMethod.GenericArguments.Add(type);
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod, versionMethod);
}

View File

@@ -18,6 +18,8 @@ namespace Unity.Netcode.Editor.CodeGen
internal sealed class NetworkBehaviourILPP : ILPPInterface
{
private const string k_ReadValueMethodName = nameof(FastBufferReader.ReadValueSafe);
private const string k_ReadValueInPlaceMethodName = nameof(FastBufferReader.ReadValueSafeInPlace);
private const string k_ReadValueTempMethodName = nameof(FastBufferReader.ReadValueSafeTemp);
private const string k_WriteValueMethodName = nameof(FastBufferWriter.WriteValueSafe);
public override ILPPInterface GetInstance() => this;
@@ -166,6 +168,11 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var type in m_WrappedNetworkVariableTypes)
{
if (type.Resolve() == null)
{
continue;
}
if (IsSpecialCaseType(type))
{
continue;
@@ -177,7 +184,72 @@ namespace Unity.Netcode.Editor.CodeGen
GenericInstanceMethod serializeMethod = null;
GenericInstanceMethod equalityMethod;
if (type.IsValueType)
if (type.Resolve().FullName == "Unity.Collections.NativeArray`1")
{
var wrappedType = ((GenericInstanceType)type).GenericArguments[0];
if (IsSpecialCaseType(wrappedType) || wrappedType.HasInterface(typeof(INetworkSerializeByMemcpy).FullName) || wrappedType.Resolve().IsEnum || IsMemcpyableType(wrappedType))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyArray_MethodRef);
}
else if (wrappedType.HasInterface(typeof(INetworkSerializable).FullName))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableArray_MethodRef);
}
else if (wrappedType.HasInterface(CodeGenHelpers.IUTF8Bytes_FullName) && wrappedType.HasInterface(k_INativeListBool_FullName))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringArray_MethodRef);
}
if (wrappedType.HasInterface(typeof(IEquatable<>).FullName + "<" + wrappedType.FullName + ">"))
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableArray_MethodRef);
}
else
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsArray_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(wrappedType);
}
equalityMethod.GenericArguments.Add(wrappedType);
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
else if (type.Resolve().FullName == "Unity.Collections.NativeList`1")
{
var wrappedType = ((GenericInstanceType)type).GenericArguments[0];
if (IsSpecialCaseType(wrappedType) || wrappedType.HasInterface(typeof(INetworkSerializeByMemcpy).FullName) || wrappedType.Resolve().IsEnum || IsMemcpyableType(wrappedType))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyList_MethodRef);
}
else if (wrappedType.HasInterface(typeof(INetworkSerializable).FullName))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableList_MethodRef);
}
else if (wrappedType.HasInterface(CodeGenHelpers.IUTF8Bytes_FullName) && wrappedType.HasInterface(k_INativeListBool_FullName))
{
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringList_MethodRef);
}
if (wrappedType.HasInterface(typeof(IEquatable<>).FullName + "<" + wrappedType.FullName + ">"))
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableList_MethodRef);
}
else
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsList_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(wrappedType);
}
equalityMethod.GenericArguments.Add(wrappedType);
}
#endif
else if (type.IsValueType)
{
if (type.HasInterface(typeof(INetworkSerializeByMemcpy).FullName) || type.Resolve().IsEnum || IsMemcpyableType(type))
{
@@ -200,11 +272,32 @@ namespace Unity.Netcode.Editor.CodeGen
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(type);
}
equalityMethod.GenericArguments.Add(type);
}
else
{
if (type.HasInterface(typeof(INetworkSerializable).FullName))
{
var constructors = type.Resolve().GetConstructors();
var hasEmptyConstructor = false;
foreach (var constructor in constructors)
{
if (constructor.Parameters.Count == 0)
{
hasEmptyConstructor = true;
}
}
if (!hasEmptyConstructor)
{
m_Diagnostics.AddError($"{type} cannot be used in a network variable - Managed {nameof(INetworkSerializable)} instances must meet the `new()` (default empty constructor) constraint.");
continue;
}
serializeMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeSerializer_ManagedINetworkSerializable_MethodRef);
}
@@ -216,14 +309,18 @@ namespace Unity.Netcode.Editor.CodeGen
{
equalityMethod = new GenericInstanceMethod(m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(type);
}
equalityMethod.GenericArguments.Add(type);
}
if (serializeMethod != null)
{
serializeMethod.GenericArguments.Add(type);
instructions.Add(processor.Create(OpCodes.Call, m_MainModule.ImportReference(serializeMethod)));
}
equalityMethod.GenericArguments.Add(type);
instructions.Add(processor.Create(OpCodes.Call, m_MainModule.ImportReference(equalityMethod)));
}
@@ -251,11 +348,15 @@ namespace Unity.Netcode.Editor.CodeGen
private FieldReference m_NetworkManager_rpc_name_table_FieldRef;
private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef;
private TypeReference m_NetworkBehaviour_TypeRef;
private TypeReference m_NetworkVariableBase_TypeRef;
private MethodReference m_NetworkVariableBase_Initialize_MethodRef;
private MethodReference m_NetworkBehaviour___nameNetworkVariable_MethodRef;
private MethodReference m_NetworkBehaviour_beginSendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_endSendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_beginSendClientRpc_MethodRef;
private MethodReference m_NetworkBehaviour_endSendClientRpc_MethodRef;
private FieldReference m_NetworkBehaviour_rpc_exec_stage_FieldRef;
private FieldReference m_NetworkBehaviour_NetworkVariableFields_FieldRef;
private MethodReference m_NetworkBehaviour_getNetworkManager_MethodRef;
private MethodReference m_NetworkBehaviour_getOwnerClientId_MethodRef;
private MethodReference m_NetworkHandlerDelegateCtor_MethodRef;
@@ -267,14 +368,37 @@ namespace Unity.Netcode.Editor.CodeGen
private FieldReference m_ServerRpcParams_Receive_SenderClientId_FieldRef;
private TypeReference m_ClientRpcParams_TypeRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpy_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyArray_MethodRef;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyList_MethodRef;
#endif
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializable_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableArray_MethodRef;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableList_MethodRef;
#endif
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_ManagedINetworkSerializable_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_FixedString_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringArray_MethodRef;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
private MethodReference m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringList_MethodRef;
#endif
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedIEquatable_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatable_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableArray_MethodRef;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableList_MethodRef;
#endif
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef;
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsArray_MethodRef;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsList_MethodRef;
#endif
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
private MethodReference m_ExceptionCtorMethodReference;
private MethodReference m_List_NetworkVariableBase_Add;
private MethodReference m_BytePacker_WriteValueBitPacked_Short_MethodRef;
private MethodReference m_BytePacker_WriteValueBitPacked_UShort_MethodRef;
private MethodReference m_BytePacker_WriteValueBitPacked_Int_MethodRef;
@@ -289,6 +413,8 @@ namespace Unity.Netcode.Editor.CodeGen
private MethodReference m_ByteUnpacker_ReadValueBitPacked_Long_MethodRef;
private MethodReference m_ByteUnpacker_ReadValueBitPacked_ULong_MethodRef;
private MethodReference m_NetworkBehaviour_createNativeList_MethodRef;
private TypeReference m_FastBufferWriter_TypeRef;
private readonly Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
private readonly List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
@@ -348,12 +474,18 @@ namespace Unity.Netcode.Editor.CodeGen
private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__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);
private const string k_NetworkBehaviour_endSendServerRpc = nameof(NetworkBehaviour.__endSendServerRpc);
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_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_NetworkVariableBase_Initialize = nameof(NetworkVariableBase.Initialize);
private const string k_RpcAttribute_Delivery = nameof(RpcAttribute.Delivery);
private const string k_ServerRpcAttribute_RequireOwnership = nameof(ServerRpcAttribute.RequireOwnership);
@@ -379,6 +511,7 @@ namespace Unity.Netcode.Editor.CodeGen
TypeDefinition networkManagerTypeDef = null;
TypeDefinition networkBehaviourTypeDef = null;
TypeDefinition networkVariableBaseTypeDef = null;
TypeDefinition networkHandlerDelegateTypeDef = null;
TypeDefinition rpcParamsTypeDef = null;
TypeDefinition serverRpcParamsTypeDef = null;
@@ -402,6 +535,12 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
if (networkVariableBaseTypeDef == null && netcodeTypeDef.Name == nameof(NetworkVariableBase))
{
networkVariableBaseTypeDef = netcodeTypeDef;
continue;
}
if (networkHandlerDelegateTypeDef == null && netcodeTypeDef.Name == nameof(NetworkManager.RpcReceiveHandler))
{
networkHandlerDelegateTypeDef = netcodeTypeDef;
@@ -548,6 +687,12 @@ namespace Unity.Netcode.Editor.CodeGen
case k_NetworkBehaviour_endSendClientRpc:
m_NetworkBehaviour_endSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
case k_NetworkBehaviour_createNativeList:
m_NetworkBehaviour_createNativeList_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
case k_NetworkBehaviour___nameNetworkVariable:
m_NetworkBehaviour___nameNetworkVariable_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
}
}
@@ -558,6 +703,21 @@ namespace Unity.Netcode.Editor.CodeGen
case k_NetworkBehaviour_rpc_exec_stage:
m_NetworkBehaviour_rpc_exec_stage_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
case k_NetworkBehaviour_NetworkVariableFields:
m_NetworkBehaviour_NetworkVariableFields_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
}
}
m_NetworkVariableBase_TypeRef = moduleDefinition.ImportReference(networkVariableBaseTypeDef);
foreach (var methodDef in networkVariableBaseTypeDef.Methods)
{
switch (methodDef.Name)
{
case k_NetworkVariableBase_Initialize:
m_NetworkVariableBase_Initialize_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
}
}
@@ -685,24 +845,69 @@ namespace Unity.Netcode.Editor.CodeGen
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpy):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpy_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpyArray):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyArray_MethodRef = method;
break;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedByMemcpyList):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedByMemcpyList_MethodRef = method;
break;
#endif
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedINetworkSerializable):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializable_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedINetworkSerializableArray):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableArray_MethodRef = method;
break;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_UnmanagedINetworkSerializableList):
m_NetworkVariableSerializationTypes_InitializeSerializer_UnmanagedINetworkSerializableList_MethodRef = method;
break;
#endif
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_ManagedINetworkSerializable):
m_NetworkVariableSerializationTypes_InitializeSerializer_ManagedINetworkSerializable_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_FixedString):
m_NetworkVariableSerializationTypes_InitializeSerializer_FixedString_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_FixedStringArray):
m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringArray_MethodRef = method;
break;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
case nameof(NetworkVariableSerializationTypes.InitializeSerializer_FixedStringList):
m_NetworkVariableSerializationTypes_InitializeSerializer_FixedStringList_MethodRef = method;
break;
#endif
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_ManagedIEquatable):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedIEquatable_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatable):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatable_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatableArray):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableArray_MethodRef = method;
break;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedIEquatableList):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedIEquatableList_MethodRef = method;
break;
#endif
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedValueEquals):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef = method;
break;
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedValueEqualsArray):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsArray_MethodRef = method;
break;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_UnmanagedValueEqualsList):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEqualsList_MethodRef = method;
break;
#endif
case nameof(NetworkVariableSerializationTypes.InitializeEqualityChecker_ManagedClassEquals):
m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef = method;
break;
@@ -785,6 +990,16 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
// Standard types are really hard to reliably find using the Mono Cecil way, they resolve differently in Mono vs .NET Core
// Importing with typeof() is less dangerous for standard framework types though, so we can just do it
var exceptionType = typeof(Exception);
var exceptionCtor = exceptionType.GetConstructor(new[] { typeof(string) });
m_ExceptionCtorMethodReference = m_MainModule.ImportReference(exceptionCtor);
var listType = typeof(List<NetworkVariableBase>);
var addMethod = listType.GetMethod(nameof(List<NetworkVariableBase>.Add), new[] { typeof(NetworkVariableBase) });
m_List_NetworkVariableBase_Add = moduleDefinition.ImportReference(addMethod);
return true;
}
@@ -931,6 +1146,8 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
GenerateVariableInitialization(typeDefinition);
if (!typeDefinition.HasGenericParameters && !typeDefinition.IsGenericInstance)
{
var fieldTypes = new List<TypeReference>();
@@ -1147,28 +1364,48 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
var checkType = paramType.Resolve();
var checkType = paramType;
if (paramType.IsArray)
{
checkType = ((ArrayType)paramType).ElementType.Resolve();
}
if ((parameters[0].ParameterType.Resolve() == checkType ||
(parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
if (!method.HasGenericParameters)
{
return method;
}
if (!paramType.IsGenericInstance && (parameters[0].ParameterType.Resolve() == checkType ||
(parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
{
return method;
}
if (parameters[0].ParameterType == paramType ||
(parameters[0].ParameterType == paramType.MakeByReferenceType() && parameters[0].IsIn))
{
return method;
if (parameters[0].ParameterType == paramType || parameters[0].ParameterType.FullName == paramType.FullName ||
(parameters[0].ParameterType == paramType.MakeByReferenceType() && parameters[0].IsIn))
{
return method;
}
}
if (method.HasGenericParameters && method.GenericParameters.Count == 1)
else if (method.GenericParameters.Count == 1)
{
var resolved = method.Parameters[0].ParameterType.Resolve();
if (resolved != null && resolved != paramType.Resolve())
{
continue;
}
if (method.GenericParameters[0].HasConstraints)
{
if (paramType.IsGenericInstance && (
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
paramType.Resolve().FullName == "Unity.Collections.NativeList`1" ||
#endif
paramType.Resolve().FullName == "Unity.Collections.NativeArray`1"))
{
if (method.Parameters[0].ParameterType.Resolve() != paramType.Resolve())
{
continue;
}
var instanceType = (GenericInstanceType)paramType;
checkType = instanceType.GenericArguments[0];
}
var meetsConstraints = true;
foreach (var constraint in method.GenericParameters[0].Constraints)
{
@@ -1201,7 +1438,17 @@ namespace Unity.Netcode.Editor.CodeGen
if (meetsConstraints)
{
var instanceMethod = new GenericInstanceMethod(method);
if (paramType.IsArray)
if (paramType.IsGenericInstance && (
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
paramType.Resolve().FullName == "Unity.Collections.NativeList`1" ||
#endif
paramType.Resolve().FullName == "Unity.Collections.NativeArray`1"))
{
var wrappedType = ((GenericInstanceType)paramType).GenericArguments[0];
instanceMethod.GenericArguments.Add(wrappedType);
}
else if (paramType.IsArray)
{
instanceMethod.GenericArguments.Add(((ArrayType)paramType).ElementType);
}
@@ -1310,9 +1557,9 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
if (!parameters[0].IsOut)
if (!parameters[0].IsOut && !parameters[0].ParameterType.IsByReference)
{
return null;
continue;
}
var methodParam = ((ByReferenceType)parameters[0].ParameterType).ElementType;
@@ -1322,24 +1569,56 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
var checkType = paramType.Resolve();
var checkType = (TypeReference)paramType.Resolve();
if (paramType.IsArray)
{
checkType = ((ArrayType)paramType).ElementType.Resolve();
}
if (methodParam.Resolve() == checkType.Resolve() || methodParam.Resolve() == checkType.MakeByReferenceType().Resolve())
if (!method.HasGenericParameters)
{
return method;
}
if (!paramType.IsGenericInstance && (methodParam.Resolve() == checkType.Resolve() || methodParam.Resolve() == checkType.MakeByReferenceType().Resolve()))
{
return method;
}
if (methodParam.Resolve() == paramType || methodParam.Resolve() == paramType.MakeByReferenceType().Resolve())
{
return method;
if (methodParam.Resolve() == paramType || methodParam.FullName == paramType.FullName)
{
return method;
}
}
if (method.HasGenericParameters && method.GenericParameters.Count == 1)
else if (method.GenericParameters.Count == 1)
{
var resolved = method.Parameters[0].ParameterType.Resolve();
if (resolved != null && resolved != paramType.Resolve())
{
continue;
}
if (paramType.IsGenericInstance && (
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
paramType.Resolve().FullName == "Unity.Collections.NativeList`1" ||
#endif
paramType.Resolve().FullName == "Unity.Collections.NativeArray`1"))
{
if (method.Name == "OnSendGlobalCounterClientRpc")
{
m_Diagnostics.AddWarning(
$"{method}: {method.Parameters[0].ParameterType} | {paramType}"
);
}
if (method.Parameters[0].ParameterType.Resolve() != paramType.Resolve())
{
if (method.Name == "OnSendGlobalCounterClientRpc")
{
m_Diagnostics.AddWarning(
$"{method}: Not suitable"
);
}
continue;
}
var instanceType = (GenericInstanceType)paramType;
checkType = instanceType.GenericArguments[0];
}
if (method.GenericParameters[0].HasConstraints)
{
var meetsConstraints = true;
@@ -1376,7 +1655,16 @@ namespace Unity.Netcode.Editor.CodeGen
if (meetsConstraints)
{
var instanceMethod = new GenericInstanceMethod(method);
if (paramType.IsArray)
if (paramType.IsGenericInstance && (
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
paramType.Resolve().FullName == "Unity.Collections.NativeList`1" ||
#endif
paramType.Resolve().FullName == "Unity.Collections.NativeArray`1"))
{
var wrappedType = ((GenericInstanceType)paramType).GenericArguments[0];
instanceMethod.GenericArguments.Add(wrappedType);
}
else if (paramType.IsArray)
{
instanceMethod.GenericArguments.Add(((ArrayType)paramType).ElementType);
}
@@ -1384,7 +1672,6 @@ namespace Unity.Netcode.Editor.CodeGen
{
instanceMethod.GenericArguments.Add(paramType);
}
return instanceMethod;
}
}
@@ -1446,7 +1733,22 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
var typeMethod = GetFastBufferReaderReadMethod(k_ReadValueMethodName, paramType);
MethodReference typeMethod;
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
if (paramType.Resolve().FullName == "Unity.Collections.NativeList`1")
{
typeMethod = GetFastBufferReaderReadMethod(k_ReadValueInPlaceMethodName, paramType);
}
else
#endif
if (paramType.Resolve().FullName == "Unity.Collections.NativeArray`1")
{
typeMethod = GetFastBufferReaderReadMethod(k_ReadValueTempMethodName, paramType);
}
else
{
typeMethod = GetFastBufferReaderReadMethod(k_ReadValueMethodName, paramType);
}
if (typeMethod != null)
{
methodRef = m_MainModule.ImportReference(typeMethod);
@@ -1786,7 +2088,7 @@ namespace Unity.Netcode.Editor.CodeGen
}
else
{
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} - Don't know how to serialize {paramType.Name}. RPC parameter types must either implement {nameof(INetworkSerializeByMemcpy)} or {nameof(INetworkSerializable)}. If this type is external and you are sure its memory layout makes it serializable by memcpy, you can replace {paramType} with {typeof(ForceNetworkSerializeByMemcpy<>).Name}<{paramType}>, or you can create extension methods for {nameof(FastBufferReader)}.{nameof(FastBufferReader.ReadValueSafe)}(this {nameof(FastBufferReader)}, out {paramType}) and {nameof(FastBufferWriter)}.{nameof(FastBufferWriter.WriteValueSafe)}(this {nameof(FastBufferWriter)}, in {paramType}) to define serialization for this type.");
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} - Don't know how to serialize {paramType}. RPC parameter types must either implement {nameof(INetworkSerializeByMemcpy)} or {nameof(INetworkSerializable)}. If this type is external and you are sure its memory layout makes it serializable by memcpy, you can replace {paramType} with {typeof(ForceNetworkSerializeByMemcpy<>).Name}<{paramType}>, or you can create extension methods for {nameof(FastBufferReader)}.{nameof(FastBufferReader.ReadValueSafe)}(this {nameof(FastBufferReader)}, out {paramType}) and {nameof(FastBufferWriter)}.{nameof(FastBufferWriter.WriteValueSafe)}(this {nameof(FastBufferWriter)}, in {paramType}) to define serialization for this type.");
continue;
}
@@ -1889,6 +2191,132 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction));
}
private void GenerateVariableInitialization(TypeDefinition type)
{
foreach (var methodDefinition in type.Methods)
{
if (methodDefinition.Name == k_NetworkBehaviour___initializeVariables)
{
// If this hits, we've already generated the method for this class because a child class got processed first.
return;
}
}
var method = new MethodDefinition(
k_NetworkBehaviour___initializeVariables,
MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig,
m_MainModule.TypeSystem.Void);
var processor = method.Body.GetILProcessor();
method.Body.Variables.Add(new VariableDefinition(m_MainModule.TypeSystem.Boolean));
processor.Emit(OpCodes.Nop);
foreach (var fieldDefinition in type.Fields)
{
FieldReference field = fieldDefinition;
if (type.HasGenericParameters)
{
var genericType = new GenericInstanceType(fieldDefinition.DeclaringType);
foreach (var parameter in fieldDefinition.DeclaringType.GenericParameters)
{
genericType.GenericArguments.Add(parameter);
}
field = new FieldReference(fieldDefinition.Name, fieldDefinition.FieldType, genericType);
}
if (!field.FieldType.IsArray && !field.FieldType.Resolve().IsArray && field.FieldType.IsSubclassOf(m_NetworkVariableBase_TypeRef))
{
// if({variable} == null) {
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, field);
processor.Emit(OpCodes.Ldnull);
processor.Emit(OpCodes.Ceq);
processor.Emit(OpCodes.Stloc_0);
processor.Emit(OpCodes.Ldloc_0);
var afterThrowInstruction = processor.Create(OpCodes.Nop);
processor.Emit(OpCodes.Brfalse, afterThrowInstruction);
// throw new Exception("...");
processor.Emit(OpCodes.Nop);
processor.Emit(OpCodes.Ldstr, $"{type.Name}.{field.Name} cannot be null. All {nameof(NetworkVariableBase)} instances must be initialized.");
processor.Emit(OpCodes.Newobj, m_ExceptionCtorMethodReference);
processor.Emit(OpCodes.Throw);
// }
processor.Append(afterThrowInstruction);
// {variable}.Initialize(this);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, field);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Callvirt, m_NetworkVariableBase_Initialize_MethodRef);
// __nameNetworkVariable({variable}, "{variable}");
processor.Emit(OpCodes.Nop);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, field);
processor.Emit(OpCodes.Ldstr, field.Name.Replace("<", string.Empty).Replace(">k__BackingField", string.Empty));
processor.Emit(OpCodes.Call, m_NetworkBehaviour___nameNetworkVariable_MethodRef);
// NetworkVariableFields.Add({variable});
processor.Emit(OpCodes.Nop);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, m_NetworkBehaviour_NetworkVariableFields_FieldRef);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldfld, field);
processor.Emit(OpCodes.Callvirt, m_List_NetworkVariableBase_Add);
}
}
// Find the base method...
MethodReference initializeVariablesBaseReference = null;
foreach (var methodDefinition in type.BaseType.Resolve().Methods)
{
if (methodDefinition.Name == k_NetworkBehaviour___initializeVariables)
{
initializeVariablesBaseReference = m_MainModule.ImportReference(methodDefinition);
break;
}
}
if (initializeVariablesBaseReference == 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.
GenerateVariableInitialization(type.BaseType.Resolve());
foreach (var methodDefinition in type.BaseType.Resolve().Methods)
{
if (methodDefinition.Name == k_NetworkBehaviour___initializeVariables)
{
initializeVariablesBaseReference = m_MainModule.ImportReference(methodDefinition);
break;
}
}
}
if (type.BaseType.Resolve().HasGenericParameters)
{
var baseTypeInstance = (GenericInstanceType)type.BaseType;
initializeVariablesBaseReference = initializeVariablesBaseReference.MakeGeneric(baseTypeInstance.GenericArguments.ToArray());
}
// base.__initializeVariables();
processor.Emit(OpCodes.Nop);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Call, initializeVariablesBaseReference);
processor.Emit(OpCodes.Nop);
processor.Emit(OpCodes.Ret);
type.Methods.Add(method);
}
private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute, uint rpcMethodId)
{
var typeSystem = methodDefinition.Module.TypeSystem;
@@ -2048,6 +2476,28 @@ namespace Unity.Netcode.Editor.CodeGen
processor.Emit(OpCodes.Brfalse, jumpInstruction);
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
if (paramType.IsGenericInstance && paramType.Resolve().FullName == "Unity.Collections.NativeList`1")
{
// var list = NetworkBehaviour.__createNativeList<T>();
// This simplifies things - easier to call __createNativeList() and have the implementation in C#
// than to try to actually construct a NativeList in IL. This is also more future-proof.
// Unlike other types, NativeList<> calls ReadValueSafeInPlace instead of ReadValueSafe.
// FastBufferReader doesn't support a non-in-place deserializer for NativeList in order to
// avoid users using it without realizing the allocation overhead that would cost. In-place
// is more efficient when an existing value exists, and when it doesn't, it's easy to create one,
// which is what we do here.
var method = new GenericInstanceMethod(m_NetworkBehaviour_createNativeList_MethodRef);
var genericParam = (GenericInstanceType)paramType;
method.GenericArguments.Add(genericParam.GenericArguments[0]);
processor.Emit(OpCodes.Call, method);
processor.Emit(OpCodes.Stloc, localIndex);
}
#endif
var foundMethodRef = GetReadMethodForParameter(paramType, out var methodRef);
if (foundMethodRef)
{
@@ -2100,7 +2550,7 @@ namespace Unity.Netcode.Editor.CodeGen
}
else
{
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} - Don't know how to serialize {paramType.Name}. RPC parameter types must either implement {nameof(INetworkSerializeByMemcpy)} or {nameof(INetworkSerializable)}. If this type is external and you are sure its memory layout makes it serializable by memcpy, you can replace {paramType} with {typeof(ForceNetworkSerializeByMemcpy<>).Name}<{paramType}>, or you can create extension methods for {nameof(FastBufferReader)}.{nameof(FastBufferReader.ReadValueSafe)}(this {nameof(FastBufferReader)}, out {paramType}) and {nameof(FastBufferWriter)}.{nameof(FastBufferWriter.WriteValueSafe)}(this {nameof(FastBufferWriter)}, in {paramType}) to define serialization for this type.");
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} - Don't know how to deserialize {paramType}. RPC parameter types must either implement {nameof(INetworkSerializeByMemcpy)} or {nameof(INetworkSerializable)}. If this type is external and you are sure its memory layout makes it serializable by memcpy, you can replace {paramType} with {typeof(ForceNetworkSerializeByMemcpy<>).Name}<{paramType}>, or you can create extension methods for {nameof(FastBufferReader)}.{nameof(FastBufferReader.ReadValueSafe)}(this {nameof(FastBufferReader)}, out {paramType}) and {nameof(FastBufferWriter)}.{nameof(FastBufferWriter.WriteValueSafe)}(this {nameof(FastBufferWriter)}, in {paramType}) to define serialization for this type.");
continue;
}

View File

@@ -112,7 +112,7 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var fieldDefinition in typeDefinition.Fields)
{
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage))
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields))
{
fieldDefinition.IsFamily = true;
}
@@ -123,7 +123,10 @@ namespace Unity.Netcode.Editor.CodeGen
if (methodDefinition.Name == nameof(NetworkBehaviour.__beginSendServerRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendServerRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc))
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__initializeVariables) ||
methodDefinition.Name == nameof(NetworkBehaviour.__nameNetworkVariable) ||
methodDefinition.Name == nameof(NetworkBehaviour.__createNativeList))
{
methodDefinition.IsFamily = true;
}