// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Doozy.Runtime.Common.Attributes; using Doozy.Runtime.Common.Utils; using UnityEditor; using UnityEngine; namespace Doozy.Editor.Common.Utils { public static class DomainReloadHandler { private static Assembly doozyEditorAssembly => ReflectionUtils.doozyEditorAssembly; private static Assembly doozyRuntimeAssembly => ReflectionUtils.doozyRuntimeAssembly; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void OnRuntimeLoad() { if(!EditorSettings.enterPlayModeOptionsEnabled) return; // ReSharper disable NotAccessedVariable int clearedValues = 0; int executedMethods = 0; // ReSharper restore NotAccessedVariable foreach (MemberInfo member in GetMembers(true)) { //Fields { var field = member as FieldInfo; if (field != null && !field.FieldType.IsGenericParameter && field.IsStatic) { Type fieldType = field.FieldType; ClearOnReloadAttribute reloadAttribute = field.GetCustomAttribute(); object valueOnReload = reloadAttribute?.ValueOnReload; bool createNewInstance = reloadAttribute != null && reloadAttribute.CreateNewInstance; dynamic value = valueOnReload != null ? Convert.ChangeType(valueOnReload, fieldType) : null; if (createNewInstance) value = Activator.CreateInstance(fieldType); try { field.SetValue(null, value); } catch { // ignored } clearedValues++; } } //Properties { var property = member as PropertyInfo; if (property != null && !property.PropertyType.IsGenericParameter && property.GetAccessors(true).Any(x => x.IsStatic)) { Type fieldType = property.PropertyType; ClearOnReloadAttribute reloadAttribute = property.GetCustomAttribute(); object valueOnReload = reloadAttribute?.ValueOnReload; bool createNewInstance = reloadAttribute != null && reloadAttribute.CreateNewInstance; dynamic value = valueOnReload != null ? Convert.ChangeType(valueOnReload, fieldType) : null; if (createNewInstance) value = Activator.CreateInstance(fieldType); try { property.SetValue(null, value); } catch { // ignored } clearedValues++; } } } foreach (MemberInfo member in GetMethodMembers(true)) { var method = member as MethodInfo; if (method == null || method.IsGenericMethod || !method.IsStatic) continue; method.Invoke(null, new object[] {}); executedMethods++; } // Debug.Log($"Cleared {clearedValues} members, executed {executedMethods} methods"); } private static IEnumerable GetMethodMembers(bool inherit) where TAttribute : System.Attribute { const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public; var members = new List(); //EDITOR try { //Methods members.AddRange(from t in doozyEditorAssembly.GetTypes() where t.IsClass where !t.IsGenericParameter from m in t.GetMethods(flags) where !m.ContainsGenericParameters where m.IsDefined(typeof(TAttribute), inherit) select m); } catch (ReflectionTypeLoadException) { //ignored } //RUNTIME try { //Methods members.AddRange(from t in doozyRuntimeAssembly.GetTypes() where t.IsClass where !t.IsGenericParameter from m in t.GetMethods(flags) where !m.ContainsGenericParameters where m.IsDefined(typeof(TAttribute), inherit) select m); } catch (ReflectionTypeLoadException) { //ignored } // foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) // { // try // { // //Methods // members.AddRange(from t in assembly.GetTypes() // where t.IsClass // where !t.IsGenericParameter // from m in t.GetMethods(flags) // where !m.ContainsGenericParameters // where m.IsDefined(typeof(TAttribute), inherit) // select m); // } // catch (ReflectionTypeLoadException) // { // //ignored // } // } return members; } private static IEnumerable GetMembers(bool inherit) where TAttribute : System.Attribute { const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy; var members = new List(); //EDITOR try { foreach (Type type in doozyEditorAssembly.GetTypes()) { if (!type.IsClass) continue; //Fields members.AddRange(type.GetFields(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); //Properties members.AddRange(type.GetProperties(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); //Events members.AddRange((from eventInfo in type.GetEvents(flags) where eventInfo.IsDefined(typeof(TAttribute), inherit) select GetEventField(type, eventInfo.Name)).Cast()); } } catch (ReflectionTypeLoadException) { //ignored } //RUNTIME try { foreach (Type type in doozyRuntimeAssembly.GetTypes()) { if (!type.IsClass) continue; //Fields members.AddRange(type.GetFields(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); //Properties members.AddRange(type.GetProperties(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); //Events members.AddRange((from eventInfo in type.GetEvents(flags) where eventInfo.IsDefined(typeof(TAttribute), inherit) select GetEventField(type, eventInfo.Name)).Cast()); } } catch (ReflectionTypeLoadException) { //ignored } // foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) // { // try // { // foreach (Type type in assembly.GetTypes()) // { // if (!type.IsClass) continue; // // //Fields // members.AddRange(type.GetFields(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); // // //Properties // members.AddRange(type.GetProperties(flags).Cast().Where(member => member.IsDefined(typeof(TAttribute), inherit))); // // //Events // members.AddRange((from eventInfo in type.GetEvents(flags) where eventInfo.IsDefined(typeof(TAttribute), inherit) select GetEventField(type, eventInfo.Name)).Cast()); // } // // } // catch (ReflectionTypeLoadException) // { // //ignored // } // } return members; } private static FieldInfo GetEventField(Type type, string eventName) { const BindingFlags flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic; FieldInfo field = null; while (type != null) { //Events defined as field field = type.GetField(eventName, flags); if (field != null && (field.FieldType == typeof(MulticastDelegate) || field.FieldType.IsSubclassOf(typeof(MulticastDelegate)))) break; //Events defined as property { add; remove; } field = type.GetField(EventName(eventName), flags); if (field != null) break; type = type.BaseType; } return field; } private static string EventName(string eventName) => $"EVENT_{eventName.ToUpper()}"; } }