505 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			505 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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);
 | |
| 
 | |
|     }
 | |
| } |