fishnet installed

This commit is contained in:
2023-05-31 11:32:21 -04:00
parent 47b25269f1
commit a001fe1b04
1291 changed files with 126631 additions and 1 deletions

View File

@ -0,0 +1,505 @@
using FishNet.Broadcast;
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.CodeGenerating.Processing;
using FishNet.CodeGenerating.Processing.Rpc;
using FishNet.Configuring;
using FishNet.Serializing.Helping;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating.ILCore
{
public class FishNetILPP : ILPostProcessor
{
#region Const.
internal const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime";
#endregion
public override bool WillProcess(ICompiledAssembly compiledAssembly)
{
if (compiledAssembly.Name.StartsWith("Unity."))
return false;
if (compiledAssembly.Name.StartsWith("UnityEngine."))
return false;
if (compiledAssembly.Name.StartsWith("UnityEditor."))
return false;
if (compiledAssembly.Name.Contains("Editor"))
return false;
/* This line contradicts the one below where referencesFishNet
* becomes true if the assembly is FishNetAssembly. This is here
* intentionally to stop codegen from running on the runtime
* fishnet assembly, but the option below is for debugging. I would
* comment out this check if I wanted to compile fishnet runtime. */
//if (CODEGEN_THIS_NAMESPACE.Length == 0)
//{
// if (compiledAssembly.Name == RUNTIME_ASSEMBLY_NAME)
// return false;
//}
bool referencesFishNet = FishNetILPP.IsFishNetAssembly(compiledAssembly) || compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == RUNTIME_ASSEMBLY_NAME);
return referencesFishNet;
}
public override ILPostProcessor GetInstance() => this;
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
{
AssemblyDefinition assemblyDef = ILCoreHelper.GetAssemblyDefinition(compiledAssembly);
if (assemblyDef == null)
return null;
//Check WillProcess again; somehow certain editor scripts skip the WillProcess check.
if (!WillProcess(compiledAssembly))
return null;
CodegenSession session = new CodegenSession();
if (!session.Initialize(assemblyDef.MainModule))
return null;
bool modified = false;
bool fnAssembly = IsFishNetAssembly(compiledAssembly);
if (fnAssembly)
modified |= ModifyMakePublicMethods(session);
/* If one or more scripts use RPCs but don't inherit NetworkBehaviours
* then don't bother processing the rest. */
if (session.GetClass<NetworkBehaviourProcessor>().NonNetworkBehaviourHasInvalidAttributes(session.Module.Types))
return new ILPostProcessResult(null, session.Diagnostics);
modified |= session.GetClass<WriterProcessor>().Process();
modified |= session.GetClass<ReaderProcessor>().Process();
modified |= CreateDeclaredSerializerDelegates(session);
modified |= CreateDeclaredSerializers(session);
modified |= CreateDeclaredComparerDelegates(session);
modified |= CreateIBroadcast(session);
modified |= CreateQOLAttributes(session);
modified |= CreateNetworkBehaviours(session);
modified |= CreateGenericReadWriteDelegates(session);
if (fnAssembly)
{
AssemblyNameReference anr = session.Module.AssemblyReferences.FirstOrDefault<AssemblyNameReference>(x => x.FullName == session.Module.Assembly.FullName);
if (anr != null)
session.Module.AssemblyReferences.Remove(anr);
}
/* If there are warnings about SyncVars being in different assemblies.
* This is awful ... codegen would need to be reworked to save
* syncvars across all assemblies so that scripts referencing them from
* another assembly can have it's instructions changed. This however is an immense
* amount of work so it will have to be put on hold, for... a long.. long while. */
if (session.DifferentAssemblySyncVars.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:");
foreach (FieldDefinition item in session.DifferentAssemblySyncVars)
sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}.");
session.LogWarning("v------- IMPORTANT -------v");
session.LogWarning(sb.ToString());
session.DifferentAssemblySyncVars.Clear();
}
//session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}.");
if (!modified)
{
return null;
}
else
{
MemoryStream pe = new MemoryStream();
MemoryStream pdb = new MemoryStream();
WriterParameters writerParameters = new WriterParameters
{
SymbolWriterProvider = new PortablePdbWriterProvider(),
SymbolStream = pdb,
WriteSymbols = true
};
assemblyDef.Write(pe, writerParameters);
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), session.Diagnostics);
}
}
/// <summary>
/// Makees methods public scope which use CodegenMakePublic attribute.
/// </summary>
/// <returns></returns>
private bool ModifyMakePublicMethods(CodegenSession session)
{
string makePublicTypeFullName = typeof(CodegenMakePublicAttribute).FullName;
foreach (TypeDefinition td in session.Module.Types)
{
foreach (MethodDefinition md in td.Methods)
{
foreach (CustomAttribute ca in md.CustomAttributes)
{
if (ca.AttributeType.FullName == makePublicTypeFullName)
{
md.Attributes &= ~MethodAttributes.Assembly;
md.Attributes |= MethodAttributes.Public;
}
}
}
}
//There is always at least one modified.
return true;
}
/// <summary>
/// Creates delegates for user declared serializers.
/// </summary>
internal bool CreateDeclaredSerializerDelegates(CodegenSession session)
{
bool modified = false;
TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract);
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
continue;
if (td.Attributes.HasFlag(readWriteExtensionTypeAttr))
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializerDelegates(td, true);
}
return modified;
}
/// <summary>
/// Creates serializers for custom types within user declared serializers.
/// </summary>
private bool CreateDeclaredSerializers(CodegenSession session)
{
bool modified = false;
TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract);
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
continue;
if (td.Attributes.HasFlag(readWriteExtensionTypeAttr))
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializers(td);
}
return modified;
}
/// <summary>
/// Creates delegates for user declared comparers.
/// </summary>
internal bool CreateDeclaredComparerDelegates(CodegenSession session)
{
bool modified = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
continue;
modified |= session.GetClass<CustomSerializerProcessor>().CreateComparerDelegates(td);
}
return modified;
}
/// <summary>
/// Creaters serializers and calls for IBroadcast.
/// </summary>
/// <param name="moduleDef"></param>
/// <param name="diagnostics"></param>
private bool CreateIBroadcast(CodegenSession session)
{
bool modified = false;
string networkBehaviourFullName = session.GetClass<NetworkBehaviourHelper>().FullName;
HashSet<TypeDefinition> typeDefs = new HashSet<TypeDefinition>();
foreach (TypeDefinition td in session.Module.Types)
{
TypeDefinition climbTd = td;
do
{
//Reached NetworkBehaviour class.
if (climbTd.FullName == networkBehaviourFullName)
break;
///* Check initial class as well all types within
// * the class. Then check all of it's base classes. */
if (climbTd.ImplementsInterface<IBroadcast>())
typeDefs.Add(climbTd);
//7ms
//Add nested. Only going to go a single layer deep.
foreach (TypeDefinition nestedTypeDef in td.NestedTypes)
{
if (nestedTypeDef.ImplementsInterface<IBroadcast>())
typeDefs.Add(nestedTypeDef);
}
//0ms
climbTd = climbTd.GetNextBaseTypeDefinition(session);
//this + name check 40ms
} while (climbTd != null);
}
//Create reader/writers for found typeDefs.
foreach (TypeDefinition td in typeDefs)
{
TypeReference typeRef = session.ImportReference(td);
bool canSerialize = session.GetClass<GeneralHelper>().HasSerializerAndDeserializer(typeRef, true);
if (!canSerialize)
session.LogError($"Broadcast {td.Name} does not support serialization. Use a supported type or create a custom serializer.");
else
modified = true;
}
return modified;
}
/// <summary>
/// Handles QOLAttributes such as [Server].
/// </summary>
/// <returns></returns>
private bool CreateQOLAttributes(CodegenSession session)
{
bool modified = false;
bool codeStripping = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
/* First pass, potentially only pass.
* If code stripping them this will be run again. The first iteration
* is to ensure things are removed in the proper order. */
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
continue;
modified |= session.GetClass<QolAttributeProcessor>().Process(td, codeStripping);
}
return modified;
}
/// <summary>
/// Creates NetworkBehaviour changes.
/// </summary>
/// <param name="moduleDef"></param>
/// <param name="diagnostics"></param>
private bool CreateNetworkBehaviours(CodegenSession session)
{
bool modified = false;
//Get all network behaviours to process.
List<TypeDefinition> networkBehaviourTypeDefs = session.Module.Types
.Where(td => td.IsSubclassOf(session, session.GetClass<NetworkBehaviourHelper>().FullName))
.ToList();
//Moment a NetworkBehaviour exist the assembly is considered modified.
if (networkBehaviourTypeDefs.Count > 0)
modified = true;
/* Remove types which are inherited. This gets the child most networkbehaviours.
* Since processing iterates all parent classes there's no reason to include them */
RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs);
//Set how many rpcs are in children classes for each typedef.
Dictionary<TypeDefinition, uint> inheritedRpcCounts = new Dictionary<TypeDefinition, uint>();
SetChildRpcCounts(inheritedRpcCounts, networkBehaviourTypeDefs);
//Set how many synctypes are in children classes for each typedef.
Dictionary<TypeDefinition, uint> inheritedSyncTypeCounts = new Dictionary<TypeDefinition, uint>();
SetChildSyncTypeCounts(inheritedSyncTypeCounts, networkBehaviourTypeDefs);
/* This holds all sync types created, synclist, dictionary, var
* and so on. This data is used after all syncvars are made so
* other methods can look for references to created synctypes and
* replace accessors accordingly. */
List<(SyncType, ProcessedSync)> allProcessedSyncs = new List<(SyncType, ProcessedSync)>();
HashSet<string> allProcessedCallbacks = new HashSet<string>();
List<TypeDefinition> processedClasses = new List<TypeDefinition>();
foreach (TypeDefinition typeDef in networkBehaviourTypeDefs)
{
session.ImportReference(typeDef);
//Synctypes processed for this nb and it's inherited classes.
List<(SyncType, ProcessedSync)> processedSyncs = new List<(SyncType, ProcessedSync)>();
session.GetClass<NetworkBehaviourProcessor>().Process(typeDef, processedSyncs,
inheritedSyncTypeCounts, inheritedRpcCounts);
//Add to all processed.
allProcessedSyncs.AddRange(processedSyncs);
}
/* Must run through all scripts should user change syncvar
* from outside the networkbehaviour. */
if (allProcessedSyncs.Count > 0)
{
foreach (TypeDefinition td in session.Module.Types)
{
session.GetClass<NetworkBehaviourSyncProcessor>().ReplaceGetSets(td, allProcessedSyncs);
session.GetClass<RpcProcessor>().RedirectBaseCalls();
}
}
/* Removes typedefinitions which are inherited by
* another within tds. For example, if the collection
* td contains A, B, C and our structure is
* A : B : C then B and C will be removed from the collection
* Since they are both inherited by A. */
void RemoveInheritedTypeDefinitions(List<TypeDefinition> tds)
{
HashSet<TypeDefinition> inheritedTds = new HashSet<TypeDefinition>();
/* Remove any networkbehaviour typedefs which are inherited by
* another networkbehaviour typedef. When a networkbehaviour typedef
* is processed so are all of the inherited types. */
for (int i = 0; i < tds.Count; i++)
{
/* Iterates all base types and
* adds them to inheritedTds so long
* as the base type is not a NetworkBehaviour. */
TypeDefinition copyTd = tds[i].GetNextBaseTypeDefinition(session);
while (copyTd != null)
{
//Class is NB.
if (copyTd.FullName == session.GetClass<NetworkBehaviourHelper>().FullName)
break;
inheritedTds.Add(copyTd);
copyTd = copyTd.GetNextBaseTypeDefinition(session);
}
}
//Remove all inherited types.
foreach (TypeDefinition item in inheritedTds)
tds.Remove(item);
}
/* Sets how many Rpcs are within the children
* of each typedefinition. EG: if our structure is
* A : B : C, with the following RPC counts...
* A 3
* B 1
* C 2
* then B child rpc counts will be 3, and C will be 4. */
void SetChildRpcCounts(Dictionary<TypeDefinition, uint> typeDefCounts, List<TypeDefinition> tds)
{
foreach (TypeDefinition typeDef in tds)
{
//Number of RPCs found while climbing typeDef.
uint childCount = 0;
TypeDefinition copyTd = typeDef;
do
{
//How many RPCs are in copyTd.
uint copyCount = session.GetClass<RpcProcessor>().GetRpcCount(copyTd);
/* If not found it this is the first time being
* processed. When this occurs set the value
* to 0. It will be overwritten below if baseCount
* is higher. */
uint previousCopyChildCount = 0;
if (!typeDefCounts.TryGetValue(copyTd, out previousCopyChildCount))
typeDefCounts[copyTd] = 0;
/* If baseCount is higher then replace count for copyTd.
* This can occur when a class is inherited by several types
* and the first processed type might only have 1 rpc, while
* the next has 2. This could be better optimized but to keep
* the code easier to read, it will stay like this. */
if (childCount > previousCopyChildCount)
typeDefCounts[copyTd] = childCount;
//Increase baseCount with RPCs found here.
childCount += copyCount;
copyTd = copyTd.GetNextBaseClassToProcess(session);
} while (copyTd != null);
}
}
/* This performs the same functionality as SetChildRpcCounts
* but for SyncTypes. */
void SetChildSyncTypeCounts(Dictionary<TypeDefinition, uint> typeDefCounts, List<TypeDefinition> tds)
{
foreach (TypeDefinition typeDef in tds)
{
//Number of RPCs found while climbing typeDef.
uint childCount = 0;
TypeDefinition copyTd = typeDef;
/* Iterate up to the parent script and then reverse
* the order. This is so that the topmost is 0
* and each inerhiting script adds onto that.
* Setting child types this way makes it so parent
* types don't need to have their synctype/rpc counts
* rebuilt when scripts are later to be found
* inheriting from them. */
List<TypeDefinition> reversedTypeDefs = new List<TypeDefinition>();
do
{
reversedTypeDefs.Add(copyTd);
copyTd = copyTd.GetNextBaseClassToProcess(session);
} while (copyTd != null);
reversedTypeDefs.Reverse();
foreach (TypeDefinition td in reversedTypeDefs)
{
//How many RPCs are in copyTd.
uint copyCount = session.GetClass<NetworkBehaviourSyncProcessor>().GetSyncTypeCount(td);
/* If not found it this is the first time being
* processed. When this occurs set the value
* to 0. It will be overwritten below if baseCount
* is higher. */
uint previousCopyChildCount = 0;
if (!typeDefCounts.TryGetValue(td, out previousCopyChildCount))
typeDefCounts[td] = 0;
/* If baseCount is higher then replace count for copyTd.
* This can occur when a class is inherited by several types
* and the first processed type might only have 1 rpc, while
* the next has 2. This could be better optimized but to keep
* the code easier to read, it will stay like this. */
if (childCount > previousCopyChildCount)
typeDefCounts[td] = childCount;
//Increase baseCount with RPCs found here.
childCount += copyCount;
}
}
}
return modified;
}
/// <summary>
/// Creates generic delegates for all read and write methods.
/// </summary>
/// <param name="moduleDef"></param>
/// <param name="diagnostics"></param>
private bool CreateGenericReadWriteDelegates(CodegenSession session)
{
session.GetClass<WriterProcessor>().CreateStaticMethodDelegates();
session.GetClass<ReaderProcessor>().CreateStaticMethodDelegates();
return true;
}
internal static bool IsFishNetAssembly(ICompiledAssembly assembly) => (assembly.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
internal static bool IsFishNetAssembly(CodegenSession session) => (session.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
internal static bool IsFishNetAssembly(ModuleDefinition moduleDef) => (moduleDef.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f03d76b376c1d5b4591039af6fd4c9e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.IO;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating.ILCore
{
internal static class ILCoreHelper
{
/// <summary>
/// Returns AssembleDefinition for compiledAssembly.
/// </summary>
/// <param name="compiledAssembly"></param>
/// <returns></returns>
internal static AssemblyDefinition GetAssemblyDefinition(ICompiledAssembly compiledAssembly)
{
PostProcessorAssemblyResolver assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly);
ReaderParameters readerParameters = new ReaderParameters
{
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
SymbolReaderProvider = new PortablePdbReaderProvider(),
AssemblyResolver = assemblyResolver,
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
ReadingMode = ReadingMode.Immediate
};
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
//Allows us to resolve inside FishNet assembly, such as for components.
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
return assemblyDefinition;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dfcfb917dd9268744962ae61aa0115b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,139 @@
using MonoFN.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating
{
internal class PostProcessorAssemblyResolver : IAssemblyResolver
{
private readonly string[] m_AssemblyReferences;
private readonly Dictionary<string, AssemblyDefinition> m_AssemblyCache = new Dictionary<string, AssemblyDefinition>();
private readonly ICompiledAssembly m_CompiledAssembly;
private AssemblyDefinition m_SelfAssembly;
public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly)
{
m_CompiledAssembly = compiledAssembly;
m_AssemblyReferences = compiledAssembly.References;
}
public void Dispose() { }
public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters(ReadingMode.Deferred));
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
lock (m_AssemblyCache)
{
if (name.Name == m_CompiledAssembly.Name)
{
return m_SelfAssembly;
}
var fileName = FindFile(name);
if (fileName == null)
{
return null;
}
var lastWriteTime = File.GetLastWriteTime(fileName);
var cacheKey = $"{fileName}{lastWriteTime}";
if (m_AssemblyCache.TryGetValue(cacheKey, out var result))
{
return result;
}
parameters.AssemblyResolver = this;
var ms = MemoryStreamFor(fileName);
var pdb = $"{fileName}.pdb";
if (File.Exists(pdb))
{
parameters.SymbolStream = MemoryStreamFor(pdb);
}
var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters);
m_AssemblyCache.Add(cacheKey, assemblyDefinition);
return assemblyDefinition;
}
}
private string FindFile(AssemblyNameReference name)
{
var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll");
if (fileName != null)
{
return fileName;
}
// perhaps the type comes from an exe instead
fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe");
if (fileName != null)
{
return fileName;
}
//Unfortunately the current ICompiledAssembly API only provides direct references.
//It is very much possible that a postprocessor ends up investigating a type in a directly
//referenced assembly, that contains a field that is not in a directly referenced assembly.
//if we don't do anything special for that situation, it will fail to resolve. We should fix this
//in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references
//are always located next to direct references, so we search in all directories of direct references we
//got passed, and if we find the file in there, we resolve to it.
return m_AssemblyReferences
.Select(Path.GetDirectoryName)
.Distinct()
.Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll"))
.FirstOrDefault(File.Exists);
}
private static MemoryStream MemoryStreamFor(string fileName)
{
return Retry(10, TimeSpan.FromSeconds(1), () =>
{
byte[] byteArray;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byteArray = new byte[fs.Length];
var readLength = fs.Read(byteArray, 0, (int)fs.Length);
if (readLength != fs.Length)
{
throw new InvalidOperationException("File read length is not full length of file.");
}
}
return new MemoryStream(byteArray);
});
}
private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func<MemoryStream> func)
{
try
{
return func();
}
catch (IOException)
{
if (retryCount == 0)
{
throw;
}
Console.WriteLine($"Caught IO Exception, trying {retryCount} more times");
Thread.Sleep(waitTime);
return Retry(retryCount - 1, waitTime, func);
}
}
public void AddAssemblyDefinitionBeingOperatedOn(AssemblyDefinition assemblyDefinition)
{
m_SelfAssembly = assemblyDefinition;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c247f4266b2864eb96e6a9ae6557d31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,22 @@
using MonoFN.Cecil;
using System.Linq;
using System.Reflection;
namespace FishNet.CodeGenerating.ILCore
{
internal class PostProcessorReflectionImporter : DefaultReflectionImporter
{
private const string k_SystemPrivateCoreLib = "System.Private.CoreLib";
private readonly AssemblyNameReference m_CorrectCorlib;
public PostProcessorReflectionImporter(ModuleDefinition module) : base(module)
{
m_CorrectCorlib = module.AssemblyReferences.FirstOrDefault(a => a.Name == "mscorlib" || a.Name == "netstandard" || a.Name == k_SystemPrivateCoreLib);
}
public override AssemblyNameReference ImportReference(AssemblyName reference)
{
return m_CorrectCorlib != null && reference.Name == k_SystemPrivateCoreLib ? m_CorrectCorlib : base.ImportReference(reference);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 484e8ad8c4dde382ea67036b32935ef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using MonoFN.Cecil;
namespace FishNet.CodeGenerating.ILCore
{
internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider
{
public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDef)
{
return new PostProcessorReflectionImporter(moduleDef);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9273a5dad109ab0783891e36c983080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: