com.unity.netcode.gameobjects@1.0.0-pre.10
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.0.0-pre.10] - 2022-06-21 ### Added - Added a new `OnTransportFailure` callback to `NetworkManager`. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the `NetworkManager` to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994) - Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to `NetworkManager` that an unrecoverable error was encountered. (#1994) - Added test to ensure a warning occurs when nesting NetworkObjects in a NetworkPrefab (#1969) - Added `NetworkManager.RemoveNetworkPrefab(...)` to remove a prefab from the prefabs list (#1950) ### Changed - Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025) - (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972) ### Removed ### Fixed - Fixed issue where dynamically spawned `NetworkObject`s could throw an exception if the scene of origin handle was zero (0) and the `NetworkObject` was already spawned. (#2017) - Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009) - Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003) - Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985) - Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984) - Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975) - Fixed issue where one or more clients disconnecting during a scene event would cause `LoadEventCompleted` or `UnloadEventCompleted` to wait until the `NetworkConfig.LoadSceneTimeOut` period before being triggered. (#1973) - Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972) - Fixed a regression in serialization support: `FixedString`, `Vector2Int`, and `Vector3Int` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper. (#1961) - Fixed generic types that inherit from NetworkBehaviour causing crashes at compile time. (#1976) - Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947) - Fixed `NetworkAnimator` issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946) - Fixed `NetworkAnimator` issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946) - Fixed `NetworkAnimator` issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946) - Fixed `NetworkAnimator` issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946) - Fixed `NetworkAnimator` issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946) - Fixed `NetworkAnimator` issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946) - Fixed issue where the `PacketLoss` metric for tools would return the packet loss over a connection lifetime instead of a single frame. (#2004)
This commit is contained in:
@@ -2,14 +2,11 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
||||
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||
|
||||
namespace Unity.Netcode.Editor.CodeGen
|
||||
{
|
||||
@@ -71,134 +68,26 @@ namespace Unity.Netcode.Editor.CodeGen
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ImportReferences(mainModule))
|
||||
var structTypes = mainModule.GetTypes()
|
||||
.Where(t => t.Resolve().HasInterface(CodeGenHelpers.INetworkSerializeByMemcpy_FullName) && !t.Resolve().IsAbstract && !t.Resolve().HasGenericParameters && t.Resolve().IsValueType)
|
||||
.ToList();
|
||||
|
||||
foreach (var type in structTypes)
|
||||
{
|
||||
// Initialize all the delegates for various NetworkVariable types to ensure they can be serailized
|
||||
|
||||
// Find all types we know we're going to want to serialize.
|
||||
// The list of these types includes:
|
||||
// - Non-generic INetworkSerializable types
|
||||
// - Non-Generic INetworkSerializeByMemcpy types
|
||||
// - Enums that are not declared within generic types
|
||||
// We can't process generic types because, to initialize a generic, we need a value
|
||||
// for `T` to initialize it with.
|
||||
var networkSerializableTypes = mainModule.GetTypes()
|
||||
.Where(t => t.Resolve().HasInterface(CodeGenHelpers.INetworkSerializable_FullName) && !t.Resolve().IsAbstract && !t.Resolve().HasGenericParameters && t.Resolve().IsValueType)
|
||||
.ToList();
|
||||
var structTypes = mainModule.GetTypes()
|
||||
.Where(t => t.Resolve().HasInterface(CodeGenHelpers.INetworkSerializeByMemcpy_FullName) && !t.Resolve().IsAbstract && !t.Resolve().HasGenericParameters && t.Resolve().IsValueType)
|
||||
.ToList();
|
||||
var enumTypes = mainModule.GetTypes()
|
||||
.Where(t => t.Resolve().IsEnum && !t.Resolve().IsAbstract && !t.Resolve().HasGenericParameters && t.Resolve().IsValueType)
|
||||
.ToList();
|
||||
|
||||
// Now, to support generics, we have to do an extra pass.
|
||||
// We look for any type that's a NetworkBehaviour type
|
||||
// Then we look through all the fields in that type, finding any field whose type is
|
||||
// descended from `NetworkVariableSerialization`. Then we check `NetworkVariableSerialization`'s
|
||||
// `T` value, and if it's a generic, then we know it was missed in the above sweep and needs
|
||||
// to be initialized. Now we have a full generic instance rather than a generic definition,
|
||||
// so we can validly generate an initializer for that particular instance of the generic type.
|
||||
var networkSerializableTypesSet = new HashSet<TypeReference>(networkSerializableTypes);
|
||||
var structTypesSet = new HashSet<TypeReference>(structTypes);
|
||||
var enumTypesSet = new HashSet<TypeReference>(enumTypes);
|
||||
var typeStack = new List<TypeReference>();
|
||||
foreach (var type in mainModule.GetTypes())
|
||||
// We'll avoid some confusion by ensuring users only choose one of the two
|
||||
// serialization schemes - by method OR by memcpy, not both. We'll also do a cursory
|
||||
// check that INetworkSerializeByMemcpy types are unmanaged.
|
||||
if (type.HasInterface(CodeGenHelpers.INetworkSerializeByMemcpy_FullName))
|
||||
{
|
||||
// Check if it's a NetworkBehaviour
|
||||
if (type.IsSubclassOf(CodeGenHelpers.NetworkBehaviour_FullName))
|
||||
if (type.HasInterface(CodeGenHelpers.INetworkSerializable_FullName))
|
||||
{
|
||||
// Iterate fields looking for NetworkVariableSerialization fields
|
||||
foreach (var field in type.Fields)
|
||||
{
|
||||
// Get the field type and its base type
|
||||
var fieldType = field.FieldType;
|
||||
var baseType = fieldType.Resolve().BaseType;
|
||||
if (baseType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// This type stack is used for resolving NetworkVariableSerialization's T value
|
||||
// When looking at base types, we get the type definition rather than the
|
||||
// type reference... which means that we get the generic definition with an
|
||||
// undefined T rather than the instance with the type filled in.
|
||||
// We then have to walk backward back down the type stack to resolve what T
|
||||
// is.
|
||||
typeStack.Clear();
|
||||
typeStack.Add(fieldType);
|
||||
// Iterate through the base types until we get to Object.
|
||||
// Object is the base for everything so we'll stop when we hit that.
|
||||
while (baseType.Name != mainModule.TypeSystem.Object.Name)
|
||||
{
|
||||
// If we've found a NetworkVariableSerialization type...
|
||||
if (baseType.IsGenericInstance && baseType.Resolve() == m_NetworkVariableSerializationType)
|
||||
{
|
||||
// Then we need to figure out what T is
|
||||
var genericType = (GenericInstanceType)baseType;
|
||||
var underlyingType = genericType.GenericArguments[0];
|
||||
if (underlyingType.Resolve() == null)
|
||||
{
|
||||
underlyingType = ResolveGenericType(underlyingType, typeStack);
|
||||
}
|
||||
|
||||
// If T is generic...
|
||||
if (underlyingType.IsGenericInstance)
|
||||
{
|
||||
// Then we pick the correct set to add it to and set it up
|
||||
// for initialization.
|
||||
if (underlyingType.HasInterface(CodeGenHelpers.INetworkSerializable_FullName))
|
||||
{
|
||||
networkSerializableTypesSet.Add(underlyingType);
|
||||
}
|
||||
|
||||
if (underlyingType.HasInterface(CodeGenHelpers.INetworkSerializeByMemcpy_FullName))
|
||||
{
|
||||
structTypesSet.Add(underlyingType);
|
||||
}
|
||||
|
||||
if (underlyingType.Resolve().IsEnum)
|
||||
{
|
||||
enumTypesSet.Add(underlyingType);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
typeStack.Add(baseType);
|
||||
baseType = baseType.Resolve().BaseType;
|
||||
}
|
||||
}
|
||||
m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types may not implement {nameof(INetworkSerializable)} - choose one or the other.");
|
||||
}
|
||||
// We'll also avoid some confusion by ensuring users only choose one of the two
|
||||
// serialization schemes - by method OR by memcpy, not both. We'll also do a cursory
|
||||
// check that INetworkSerializeByMemcpy types are unmanaged.
|
||||
else if (type.HasInterface(CodeGenHelpers.INetworkSerializeByMemcpy_FullName))
|
||||
if (!type.IsValueType)
|
||||
{
|
||||
if (type.HasInterface(CodeGenHelpers.INetworkSerializable_FullName))
|
||||
{
|
||||
m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types may not implement {nameof(INetworkSerializable)} - choose one or the other.");
|
||||
}
|
||||
if (!type.IsValueType)
|
||||
{
|
||||
m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types must be unmanaged types.");
|
||||
}
|
||||
m_Diagnostics.AddError($"{nameof(INetworkSerializeByMemcpy)} types must be unmanaged types.");
|
||||
}
|
||||
}
|
||||
|
||||
if (networkSerializableTypes.Count + structTypes.Count + enumTypes.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Finally we add to the module initializer some code to initialize the delegates in
|
||||
// NetworkVariableSerialization<T> for all necessary values of T, by calling initialization
|
||||
// methods in NetworkVariableHelpers.
|
||||
CreateModuleInitializer(assemblyDefinition, networkSerializableTypesSet.ToList(), structTypesSet.ToList(), enumTypesSet.ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Diagnostics.AddError($"Cannot import references into main module: {mainModule.Name}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -228,102 +117,5 @@ namespace Unity.Netcode.Editor.CodeGen
|
||||
|
||||
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
||||
}
|
||||
|
||||
private MethodReference m_InitializeDelegatesNetworkSerializable_MethodRef;
|
||||
private MethodReference m_InitializeDelegatesStruct_MethodRef;
|
||||
private MethodReference m_InitializeDelegatesEnum_MethodRef;
|
||||
|
||||
private TypeDefinition m_NetworkVariableSerializationType;
|
||||
|
||||
private const string k_InitializeNetworkSerializableMethodName = nameof(NetworkVariableHelper.InitializeDelegatesNetworkSerializable);
|
||||
private const string k_InitializeStructMethodName = nameof(NetworkVariableHelper.InitializeDelegatesStruct);
|
||||
private const string k_InitializeEnumMethodName = nameof(NetworkVariableHelper.InitializeDelegatesEnum);
|
||||
|
||||
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
|
||||
var helperType = typeof(NetworkVariableHelper);
|
||||
foreach (var methodInfo in helperType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
||||
{
|
||||
switch (methodInfo.Name)
|
||||
{
|
||||
case k_InitializeNetworkSerializableMethodName:
|
||||
m_InitializeDelegatesNetworkSerializable_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||
break;
|
||||
case k_InitializeStructMethodName:
|
||||
m_InitializeDelegatesStruct_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||
break;
|
||||
case k_InitializeEnumMethodName:
|
||||
m_InitializeDelegatesEnum_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_NetworkVariableSerializationType = moduleDefinition.ImportReference(typeof(NetworkVariableSerialization<>)).Resolve();
|
||||
return true;
|
||||
}
|
||||
|
||||
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
||||
{
|
||||
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
|
||||
if (staticCtorMethodDef == null)
|
||||
{
|
||||
staticCtorMethodDef = new MethodDefinition(
|
||||
".cctor", // Static Constructor (constant-constructor)
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.SpecialName |
|
||||
MethodAttributes.RTSpecialName |
|
||||
MethodAttributes.Static,
|
||||
typeDefinition.Module.TypeSystem.Void);
|
||||
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
typeDefinition.Methods.Add(staticCtorMethodDef);
|
||||
}
|
||||
|
||||
return staticCtorMethodDef;
|
||||
}
|
||||
|
||||
// Creates a static module constructor (which is executed when the module is loaded) that registers all the
|
||||
// message types in the assembly with MessagingSystem.
|
||||
// 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
|
||||
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeReference> networkSerializableTypes, List<TypeReference> structTypes, List<TypeReference> enumTypes)
|
||||
{
|
||||
foreach (var typeDefinition in assembly.MainModule.Types)
|
||||
{
|
||||
if (typeDefinition.FullName == "<Module>")
|
||||
{
|
||||
var staticCtorMethodDef = GetOrCreateStaticConstructor(typeDefinition);
|
||||
|
||||
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||
|
||||
var instructions = new List<Instruction>();
|
||||
|
||||
foreach (var type in structTypes)
|
||||
{
|
||||
var method = new GenericInstanceMethod(m_InitializeDelegatesStruct_MethodRef);
|
||||
method.GenericArguments.Add(type);
|
||||
instructions.Add(processor.Create(OpCodes.Call, method));
|
||||
}
|
||||
|
||||
foreach (var type in networkSerializableTypes)
|
||||
{
|
||||
var method = new GenericInstanceMethod(m_InitializeDelegatesNetworkSerializable_MethodRef);
|
||||
method.GenericArguments.Add(type);
|
||||
instructions.Add(processor.Create(OpCodes.Call, method));
|
||||
}
|
||||
|
||||
foreach (var type in enumTypes)
|
||||
{
|
||||
var method = new GenericInstanceMethod(m_InitializeDelegatesEnum_MethodRef);
|
||||
method.GenericArguments.Add(type);
|
||||
instructions.Add(processor.Create(OpCodes.Call, method));
|
||||
}
|
||||
|
||||
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user