forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			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);
 | 
						|
 | 
						|
    }
 | 
						|
} |