using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; using HG.Reflection; using JetBrains.Annotations; using UnityEngine; namespace RoR2; [MeansImplicitUse] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class SystemInitializerAttribute : HG.Reflection.SearchableAttribute { private class SystemInitializationLogHandler : ILogHandler { public ILogHandler underlyingLogHandler; private SystemInitializerAttribute _currentInitializer; private string logPrefix = string.Empty; public SystemInitializerAttribute currentInitializer { get { return _currentInitializer; } set { _currentInitializer = value; logPrefix = "[" + currentInitializer.associatedType.FullName + "] "; } } public void LogException(Exception exception, UnityEngine.Object context) { LogFormat(LogType.Exception, context, exception.Message, null); } public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args) { underlyingLogHandler.LogFormat(logType, context, logPrefix + format, args); } } private class SystemTiming : IComparable { public string systemName; public long systemInitDuration; public long longestSingleIteration; public long externalTimeDuration; public long totalTimeSpent => externalTimeDuration + systemInitDuration; public int CompareTo(object obj) { SystemTiming systemTiming = obj as SystemTiming; return -totalTimeSpent.CompareTo(systemTiming.totalTimeSpent); } } public static bool hasExecuted = false; public Type[] dependencies = Array.Empty(); private MethodInfo methodInfo; private Type associatedType; private static List preInitializedTypes = new List(5); private static Thread initThread; private static Queue initializerAttributes; private static long FRAME_TIME_THRESHOLD_MS = 22L; public SystemInitializerAttribute(params Type[] dependencies) { if (dependencies != null) { this.dependencies = dependencies; } } public static void RegisterPreInitializedType(Type _type) { preInitializedTypes.Add(_type); } public static void Execute() { Queue queue = new Queue(); foreach (SystemInitializerAttribute instance in HG.Reflection.SearchableAttribute.GetInstances()) { MethodInfo methodInfo = instance.target as MethodInfo; if (methodInfo != null && methodInfo.IsStatic) { queue.Enqueue(instance); instance.methodInfo = methodInfo; instance.associatedType = methodInfo.DeclaringType; } } HashSet initializedTypes = new HashSet(); int num = 0; while (queue.Count > 0) { SystemInitializerAttribute systemInitializerAttribute2 = queue.Dequeue(); if (!InitializerDependenciesMet(systemInitializerAttribute2)) { queue.Enqueue(systemInitializerAttribute2); num++; if (num >= queue.Count) { break; } } else { systemInitializerAttribute2.methodInfo.Invoke(null, Array.Empty()); initializedTypes.Add(systemInitializerAttribute2.associatedType); num = 0; } } hasExecuted = true; bool InitializerDependenciesMet(SystemInitializerAttribute initializerAttribute) { Type[] array = initializerAttribute.dependencies; foreach (Type item in array) { if (!initializedTypes.Contains(item)) { return false; } } return true; } } public static void ExecuteStatic() { int num = 0; Type[] assemblyTypes = RoR2Application.AssemblyTypes; foreach (Type type in assemblyTypes) { num++; MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo element in methods) { SystemInitializerAttribute customAttribute = CustomAttributeExtensions.GetCustomAttribute(element); if (customAttribute != null) { initializerAttributes.Enqueue(customAttribute); customAttribute.methodInfo = element; customAttribute.associatedType = type; } } } } public static IEnumerator ExecuteCoroutine() { initializerAttributes = new Queue(); initThread = new Thread(ExecuteStatic); initThread.Start(); yield return null; while (initThread.IsAlive) { yield return null; } initThread = null; HashSet initializedTypes = new HashSet(); foreach (Type preInitializedType in preInitializedTypes) { initializedTypes.Add(preInitializedType); } preInitializedTypes.Clear(); new Stopwatch().Start(); Stopwatch multiLoopTimer = new Stopwatch(); multiLoopTimer.Start(); int yields = 0; int num = 0; while (initializerAttributes.Count > 0) { SystemInitializerAttribute initializerAttribute2 = initializerAttributes.Dequeue(); if (!InitializerDependenciesMet(initializerAttribute2)) { initializerAttributes.Enqueue(initializerAttribute2); num++; if (num >= initializerAttributes.Count) { UnityEngine.Debug.LogFormat("SystemInitializerAttribute infinite loop detected. currentMethod={0}", initializerAttribute2.associatedType.FullName + initializerAttribute2.methodInfo.Name); break; } continue; } IEnumerator thisStep = initializerAttribute2.methodInfo.Invoke(null, Array.Empty()) as IEnumerator; List coroutineStack = new List { thisStep }; multiLoopTimer.Stop(); long accumulatedTime = multiLoopTimer.ElapsedMilliseconds; multiLoopTimer.Start(); if (accumulatedTime > FRAME_TIME_THRESHOLD_MS) { accumulatedTime = 0L; multiLoopTimer.Stop(); multiLoopTimer.Reset(); multiLoopTimer.Start(); yields++; yield return null; } while (thisStep != null) { while (accumulatedTime < FRAME_TIME_THRESHOLD_MS && thisStep != null) { if (!thisStep.MoveNext()) { coroutineStack.RemoveAt(coroutineStack.Count - 1); thisStep = ((coroutineStack.Count <= 0) ? null : coroutineStack[coroutineStack.Count - 1]); } else if (thisStep.Current != null) { coroutineStack.Add(thisStep.Current as IEnumerator); thisStep = coroutineStack[coroutineStack.Count - 1]; } multiLoopTimer.Stop(); accumulatedTime = multiLoopTimer.ElapsedMilliseconds; multiLoopTimer.Start(); } if (accumulatedTime > FRAME_TIME_THRESHOLD_MS) { accumulatedTime = 0L; multiLoopTimer.Stop(); multiLoopTimer.Reset(); multiLoopTimer.Start(); } if (thisStep != null) { yields++; accumulatedTime = 0L; multiLoopTimer.Stop(); multiLoopTimer.Reset(); multiLoopTimer.Start(); yield return null; } } initializedTypes.Add(initializerAttribute2.associatedType); num = 0; } initializerAttributes = null; hasExecuted = true; bool InitializerDependenciesMet(SystemInitializerAttribute initializerAttribute) { Type[] array = initializerAttribute.dependencies; foreach (Type item in array) { initializedTypes.Contains(item); if (!initializedTypes.Contains(item)) { return false; } } return true; } } }