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). ## [2.0.0-exp.2] - 2024-04-02 ### Added - Added updates to all internal messages to account for a distributed authority network session connection. (#2863) - Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) - For a customized `NetworkRigidbodyBase` class: - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes. - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned. - `NetworkRigidbodyBase.Initialize` is a protected method that, when invoked, will initialize the instance. This includes options to: - Set whether using a `RigidbodyTypes.Rigidbody` or `RigidbodyTypes.Rigidbody2D`. - Includes additional optional parameters to set the `NetworkTransform`, `Rigidbody`, and `Rigidbody2d` to use. - Provides additional public methods: - `NetworkRigidbodyBase.GetPosition` to return the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.GetRotation` to return the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MovePosition` to move to the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MoveRotation` to move to the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetPosition` to set the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetRotation` to set the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.ApplyCurrentTransform` to set the position and rotation of the `Rigidbody` or `Rigidbody2d` based on the associated `GameObject` transform (depending upon its initialized setting). - `NetworkRigidbodyBase.WakeIfSleeping` to wake up the rigid body if sleeping. - `NetworkRigidbodyBase.SleepRigidbody` to put the rigid body to sleep. - `NetworkRigidbodyBase.IsKinematic` to determine if the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) is currently kinematic. - `NetworkRigidbodyBase.SetIsKinematic` to set the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) current kinematic state. - `NetworkRigidbodyBase.ResetInterpolation` to reset the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) back to its original interpolation value when initialized. - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned `NetworkTransform` when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863) - Added `RigidbodyContactEventManager` that provides a more optimized way to process collision enter and collision stay events as opposed to the `Monobehaviour` approach. (#2863) - Can be used in client-server and distributed authority modes, but is particularly useful in distributed authority. - Added rigid body motion updates to `NetworkTransform` which allows users to set interolation on rigid bodies. (#2863) - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or `NetworkRigidbody` or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation. - Added distributed authority mode support to `NetworkAnimator`. (#2863) - Added session mode selection to `NetworkManager` inspector view. (#2863) - Added distributed authority permissions feature. (#2863) - Added distributed authority mode specific `NetworkObject` permissions flags (Distributable, Transferable, and RequestRequired). (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipStatus` method that applies one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.RemoveOwnershipStatus` method that removes one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.HasOwnershipStatus` method that will return (true or false) whether one or more ownership flags is set. (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipLock` method that locks ownership of a `NetworkObject` to prevent ownership from changing until the current owner releases the lock. (#2863) - Added distributed authority mode specific `NetworkObject.RequestOwnership` method that sends an ownership request to the current owner of a spawned `NetworkObject` instance. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequested` callback handler that is invoked on the owner/authoritative side when a non-owner requests ownership. Depending upon the boolean returned value depends upon whether the request is approved or denied. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequestResponse` callback handler that is invoked when a non-owner's request has been processed. This callback includes a `NetworkObjet.OwnershipRequestResponseStatus` response parameter that describes whether the request was approved or the reason why it was not approved. (#2863) - Added distributed authority mode specific `NetworkObject.DeferDespawn` method that defers the despawning of `NetworkObject` instances on non-authoritative clients based on the tick offset parameter. (#2863) - Added distributed authority mode specific `NetworkObject.OnDeferredDespawnComplete` callback handler that can be used to further control when deferring the despawning of a `NetworkObject` on non-authoritative instances. (#2863) - Added `NetworkClient.SessionModeType` as one way to determine the current session mode of the network session a client is connected to. (#2863) - Added distributed authority mode specific `NetworkClient.IsSessionOwner` property to determine if the current local client is the current session owner of a distributed authority session. (#2863) - Added distributed authority mode specific client side spawning capabilities. When running in distributed authority mode, clients can instantiate and spawn `NetworkObject` instances (the local client is authomatically the owner of the spawned object). (#2863) - This is useful to better visually synchronize owner authoritative motion models and newly spawned `NetworkObject` instances (i.e. projectiles for example). - Added distributed authority mode specific client side player spawning capabilities. Clients will automatically spawn their associated player object locally. (#2863) - Added distributed authority mode specific `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property (default is true) to provide control over the automatic spawning of player prefabs on the local client side. (#2863) - Added distributed authority mode specific `NetworkManager.OnFetchLocalPlayerPrefabToSpawn` callback that, when assigned, will allow the local client to provide the player prefab to be spawned for the local client. (#2863) - This is only invoked if the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property is set to true. - Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a `NetworkBehaviour` script much like that of `IsServer` or `IsClient`). (#2863) - Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a `NetworkBehaviour` script). (#2863) - Added support for distributed authority mode scene management where the currently assigned session owner can start scene events (i.e. scene loading and scene unloading). (#2863) ### Fixed - Fixed issue where the host was not invoking `OnClientDisconnectCallback` for its own local client when internally shutting down. (#2822) - Fixed issue where NetworkTransform could potentially attempt to "unregister" a named message prior to it being registered. (#2807) - Fixed issue where in-scene placed `NetworkObject`s with complex nested children `NetworkObject`s (more than one child in depth) would not synchronize properly if WorldPositionStays was set to true. (#2796) ### Changed - Changed client side awareness of other clients is now the same as a server or host. (#2863) - Changed `NetworkManager.ConnectedClients` can now be accessed by both server and clients. (#2863) - Changed `NetworkManager.ConnectedClientsList` can now be accessed by both server and clients. (#2863) - Changed `NetworkTransform` defaults to owner authoritative when connected to a distributed authority session. (#2863) - Changed `NetworkVariable` defaults to owner write and everyone read permissions when connected to a distributed authority session (even if declared with server read or write permissions). (#2863) - Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by `NetworkManager`. (#2863) - Changed `NetworkManager` inspector view layout where properties are now organized by category. (#2863) - Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) - Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807)
533 lines
19 KiB
C#
533 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using Mono.Cecil.Rocks;
|
|
using Unity.Collections;
|
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
|
using UnityEngine;
|
|
using Object = System.Object;
|
|
|
|
namespace Unity.Netcode.Editor.CodeGen
|
|
{
|
|
internal static class CodeGenHelpers
|
|
{
|
|
public const string DotnetModuleName = "netstandard.dll";
|
|
public const string UnityModuleName = "UnityEngine.CoreModule.dll";
|
|
public const string NetcodeModuleName = "Unity.Netcode.Runtime.dll";
|
|
|
|
public const string RuntimeAssemblyName = "Unity.Netcode.Runtime";
|
|
public const string ComponentsAssemblyName = "Unity.Netcode.Components";
|
|
|
|
public static readonly string NetworkBehaviour_FullName = typeof(NetworkBehaviour).FullName;
|
|
public static readonly string INetworkMessage_FullName = typeof(INetworkMessage).FullName;
|
|
public static readonly string ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName;
|
|
public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName;
|
|
public static readonly string RpcAttribute_FullName = typeof(RpcAttribute).FullName;
|
|
public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName;
|
|
public static readonly string ClientRpcParams_FullName = typeof(ClientRpcParams).FullName;
|
|
public static readonly string RpcParams_FullName = typeof(RpcParams).FullName;
|
|
public static readonly string ClientRpcSendParams_FullName = typeof(ClientRpcSendParams).FullName;
|
|
public static readonly string ClientRpcReceiveParams_FullName = typeof(ClientRpcReceiveParams).FullName;
|
|
public static readonly string ServerRpcSendParams_FullName = typeof(ServerRpcSendParams).FullName;
|
|
public static readonly string ServerRpcReceiveParams_FullName = typeof(ServerRpcReceiveParams).FullName;
|
|
public static readonly string INetworkSerializable_FullName = typeof(INetworkSerializable).FullName;
|
|
public static readonly string INetworkSerializeByMemcpy_FullName = typeof(INetworkSerializeByMemcpy).FullName;
|
|
public static readonly string IUTF8Bytes_FullName = typeof(IUTF8Bytes).FullName;
|
|
public static readonly string UnityColor_FullName = typeof(Color).FullName;
|
|
public static readonly string UnityColor32_FullName = typeof(Color32).FullName;
|
|
public static readonly string UnityVector2_FullName = typeof(Vector2).FullName;
|
|
public static readonly string UnityVector3_FullName = typeof(Vector3).FullName;
|
|
public static readonly string UnityVector4_FullName = typeof(Vector4).FullName;
|
|
public static readonly string UnityQuaternion_FullName = typeof(Quaternion).FullName;
|
|
public static readonly string UnityRay_FullName = typeof(Ray).FullName;
|
|
public static readonly string UnityRay2D_FullName = typeof(Ray2D).FullName;
|
|
|
|
public static uint Hash(this MethodDefinition methodDefinition)
|
|
{
|
|
var sigArr = Encoding.UTF8.GetBytes($"{methodDefinition.Module.Name} / {methodDefinition.FullName}");
|
|
var sigLen = sigArr.Length;
|
|
unsafe
|
|
{
|
|
fixed (byte* sigPtr = sigArr)
|
|
{
|
|
return XXHash.Hash32(sigPtr, sigLen);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string classTypeFullName)
|
|
{
|
|
if (typeDefinition == null || !typeDefinition.IsClass)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var baseTypeRef = typeDefinition.BaseType;
|
|
while (baseTypeRef != null)
|
|
{
|
|
if (baseTypeRef.FullName == classTypeFullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
try
|
|
{
|
|
baseTypeRef = baseTypeRef.Resolve().BaseType;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static string FullNameWithGenericParameters(this TypeReference typeReference, GenericParameter[] contextGenericParameters, TypeReference[] contextGenericParameterTypes)
|
|
{
|
|
var name = typeReference.FullName;
|
|
if (typeReference.HasGenericParameters)
|
|
{
|
|
name += "<";
|
|
for (var i = 0; i < typeReference.Resolve().GenericParameters.Count; ++i)
|
|
{
|
|
if (i != 0)
|
|
{
|
|
name += ", ";
|
|
}
|
|
|
|
for (var j = 0; j < contextGenericParameters.Length; ++j)
|
|
{
|
|
if (typeReference.GenericParameters[i].FullName == contextGenericParameters[i].FullName)
|
|
{
|
|
name += contextGenericParameterTypes[i].FullName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
name += ">";
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (typeReference == null)
|
|
{
|
|
return false;
|
|
}
|
|
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)
|
|
{
|
|
if (typeReference.IsArray)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
var typeDef = typeReference.Resolve();
|
|
// Note: this won't catch generics correctly.
|
|
//
|
|
// class Foo<T>: IInterface<T> {}
|
|
// class Bar: Foo<int> {}
|
|
//
|
|
// Bar.HasInterface(IInterface<int>) -> returns false even though it should be true.
|
|
//
|
|
// This can be fixed (see GetAllFieldsAndResolveGenerics() in NetworkBehaviourILPP to understand how)
|
|
// but right now we don't need that to work so it's left alone to reduce complexity
|
|
if (typeDef.BaseType.HasInterface(interfaceTypeFullName))
|
|
{
|
|
return true;
|
|
}
|
|
var typeFaces = typeDef.Interfaces;
|
|
return typeFaces.Any(iface => iface.InterfaceType.FullName == interfaceTypeFullName);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool IsSerializable(this TypeReference typeReference)
|
|
{
|
|
var typeSystem = typeReference.Module.TypeSystem;
|
|
|
|
// C# primitives
|
|
if (typeReference == typeSystem.Boolean)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Char)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.SByte)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Byte)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Int16)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.UInt16)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Int32)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.UInt32)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Int64)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.UInt64)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Single)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.Double)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference == typeSystem.String)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Unity primitives
|
|
if (typeReference.FullName == UnityColor_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityColor32_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityVector2_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityVector3_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityVector4_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityQuaternion_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityRay_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (typeReference.FullName == UnityRay2D_FullName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Enum
|
|
if (typeReference.GetEnumAsInt() != null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// INetworkSerializable
|
|
if (typeReference.HasInterface(INetworkSerializable_FullName))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Static array
|
|
if (typeReference.IsArray)
|
|
{
|
|
return typeReference.GetElementType().IsSerializable();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static TypeReference GetEnumAsInt(this TypeReference typeReference)
|
|
{
|
|
if (typeReference.IsArray)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
var typeDef = typeReference.Resolve();
|
|
return typeDef.IsEnum ? typeDef.GetEnumUnderlyingType() : null;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static void AddError(this List<DiagnosticMessage> diagnostics, string message)
|
|
{
|
|
diagnostics.AddError((SequencePoint)null, message);
|
|
}
|
|
|
|
public static void AddError(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDefinition, string message)
|
|
{
|
|
diagnostics.AddError(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
|
|
}
|
|
|
|
public static void AddError(this List<DiagnosticMessage> diagnostics, SequencePoint sequencePoint, string message)
|
|
{
|
|
diagnostics.Add(new DiagnosticMessage
|
|
{
|
|
DiagnosticType = DiagnosticType.Error,
|
|
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
|
|
Line = sequencePoint?.StartLine ?? 0,
|
|
Column = sequencePoint?.StartColumn ?? 0,
|
|
MessageData = $" - {message}"
|
|
});
|
|
}
|
|
|
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, string message)
|
|
{
|
|
diagnostics.AddWarning((SequencePoint)null, message);
|
|
}
|
|
|
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDefinition, string message)
|
|
{
|
|
diagnostics.AddWarning(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
|
|
}
|
|
|
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, SequencePoint sequencePoint, string message)
|
|
{
|
|
diagnostics.Add(new DiagnosticMessage
|
|
{
|
|
DiagnosticType = DiagnosticType.Warning,
|
|
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
|
|
Line = sequencePoint?.StartLine ?? 0,
|
|
Column = sequencePoint?.StartColumn ?? 0,
|
|
MessageData = $" - {message}"
|
|
});
|
|
}
|
|
|
|
public static void RemoveRecursiveReferences(this ModuleDefinition moduleDefinition)
|
|
{
|
|
// Weird behavior from Cecil: When importing a reference to a specific implementation of a generic
|
|
// method, it's importing the main module as a reference into itself. This causes Unity to have issues
|
|
// when attempting to iterate the assemblies to discover unit tests, as it goes into infinite recursion
|
|
// and eventually hits a stack overflow. I wasn't able to find any way to stop Cecil from importing the module
|
|
// into itself, so at the end of it all, we're just going to go back and remove it again.
|
|
var moduleName = moduleDefinition.Name;
|
|
if (moduleName.EndsWith(".dll") || moduleName.EndsWith(".exe"))
|
|
{
|
|
moduleName = moduleName.Substring(0, moduleName.Length - 4);
|
|
}
|
|
|
|
foreach (var reference in moduleDefinition.AssemblyReferences)
|
|
{
|
|
var referenceName = reference.Name.Split(',')[0];
|
|
if (referenceName.EndsWith(".dll") || referenceName.EndsWith(".exe"))
|
|
{
|
|
referenceName = referenceName.Substring(0, referenceName.Length - 4);
|
|
}
|
|
|
|
if (moduleName == referenceName)
|
|
{
|
|
try
|
|
{
|
|
moduleDefinition.AssemblyReferences.Remove(reference);
|
|
break;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compiledAssembly, out PostProcessorAssemblyResolver assemblyResolver)
|
|
{
|
|
assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly);
|
|
var readerParameters = new ReaderParameters
|
|
{
|
|
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
|
|
SymbolReaderProvider = new PortablePdbReaderProvider(),
|
|
AssemblyResolver = assemblyResolver,
|
|
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
|
|
ReadingMode = ReadingMode.Immediate
|
|
};
|
|
|
|
var assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
|
|
|
|
//apparently, it will happen that when we ask to resolve a type that lives inside Unity.Netcode.Runtime, and we
|
|
//are also postprocessing Unity.Netcode.Runtime, type resolving will fail, because we do not actually try to resolve
|
|
//inside the assembly we are processing. Let's make sure we do that, so that we can use postprocessor features inside
|
|
//Unity.Netcode.Runtime itself as well.
|
|
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
|
|
|
|
return assemblyDefinition;
|
|
}
|
|
|
|
private static void SearchForBaseModulesRecursive(AssemblyDefinition assemblyDefinition, PostProcessorAssemblyResolver assemblyResolver, ref ModuleDefinition unityModule, ref ModuleDefinition netcodeModule, HashSet<string> visited)
|
|
{
|
|
|
|
foreach (var module in assemblyDefinition.Modules)
|
|
{
|
|
if (module == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (unityModule != null && netcodeModule != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (unityModule == null && module.Name == UnityModuleName)
|
|
{
|
|
unityModule = module;
|
|
continue;
|
|
}
|
|
|
|
if (netcodeModule == null && module.Name == NetcodeModuleName)
|
|
{
|
|
netcodeModule = module;
|
|
continue;
|
|
}
|
|
}
|
|
if (unityModule != null && netcodeModule != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var assemblyNameReference in assemblyDefinition.MainModule.AssemblyReferences)
|
|
{
|
|
if (assemblyNameReference == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (visited.Contains(assemblyNameReference.Name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
visited.Add(assemblyNameReference.Name);
|
|
|
|
var assembly = assemblyResolver.Resolve(assemblyNameReference);
|
|
if (assembly == null)
|
|
{
|
|
continue;
|
|
}
|
|
SearchForBaseModulesRecursive(assembly, assemblyResolver, ref unityModule, ref netcodeModule, visited);
|
|
|
|
if (unityModule != null && netcodeModule != null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static (ModuleDefinition UnityModule, ModuleDefinition NetcodeModule) FindBaseModules(AssemblyDefinition assemblyDefinition, PostProcessorAssemblyResolver assemblyResolver)
|
|
{
|
|
ModuleDefinition unityModule = null;
|
|
ModuleDefinition netcodeModule = null;
|
|
var visited = new HashSet<string>();
|
|
SearchForBaseModulesRecursive(assemblyDefinition, assemblyResolver, ref unityModule, ref netcodeModule, visited);
|
|
|
|
return (unityModule, netcodeModule);
|
|
}
|
|
}
|
|
}
|