From 10e3f60d401750b614a55e3c942a1bb160969d4a Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 14:43:44 +0200 Subject: [PATCH 1/9] Fix systemic assembly load when using GetAssemblies() --- .../Per Project/ProjectAssemblyDefinition.cs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs index a033051..8b9a7a5 100644 --- a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs +++ b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs @@ -27,31 +27,18 @@ namespace CarterGames.Shared.SaveManager { public static class ProjectAssemblyDefinition { - public static Assembly[] ProjectEditorAssemblies + public static Assembly[] ProjectEditorAssemblies { get; } = new Assembly[] { - get - { - return new Assembly[] - { - Assembly.Load("CarterGames.SaveManager.Editor"), - Assembly.Load("CarterGames.SaveManager.Runtime"), - Assembly.Load("CarterGames.Shared.SaveManager.Editor"), - Assembly.Load("CarterGames.Shared.SaveManager") - }; - } - } - - - public static Assembly[] ProjectRuntimeAssemblies + Assembly.Load("CarterGames.SaveManager.Editor"), + Assembly.Load("CarterGames.SaveManager.Runtime"), + Assembly.Load("CarterGames.Shared.SaveManager.Editor"), + Assembly.Load("CarterGames.Shared.SaveManager") + }; + + public static Assembly[] ProjectRuntimeAssemblies { get; } = new Assembly[] { - get - { - return new Assembly[] - { - Assembly.Load("CarterGames.SaveManager.Runtime"), - Assembly.Load("CarterGames.Shared.SaveManager") - }; - } - } + Assembly.Load("CarterGames.SaveManager.Runtime"), + Assembly.Load("CarterGames.Shared.SaveManager") + }; } } \ No newline at end of file From cb8a16b5acca67dcdadb0b5828496dc53dda9b09 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 15:02:11 +0200 Subject: [PATCH 2/9] ProjectAssemblyDefinition's scope is now internal to avoid external assembly modifications --- .../Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs index 8b9a7a5..48e4942 100644 --- a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs +++ b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs @@ -25,7 +25,7 @@ namespace CarterGames.Shared.SaveManager { - public static class ProjectAssemblyDefinition + internal static class ProjectAssemblyDefinition { public static Assembly[] ProjectEditorAssemblies { get; } = new Assembly[] { From 7c309924e3b2bfa817067c42fce04954928f0129 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 15:06:53 +0200 Subject: [PATCH 3/9] Rename a variable to be more explicit in AssemblyHelper --- Shared Systems/Runtime/Helpers/AssemblyHelper.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs index c23e3d9..686a493 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs @@ -37,7 +37,7 @@ public static class AssemblyHelper | Fields ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */ - private static Assembly[] audioManagerAssemblies; + private static Assembly[] _cachedAssemblies; /* ───────────────────────────────────────────────────────────────────────────────────────────────────────────── | Properties @@ -46,13 +46,13 @@ public static class AssemblyHelper /// /// Gets all the cart assemblies to use when checking in internally only. /// - private static IEnumerable AudioManagerAssemblies + private static IEnumerable CachedAssemblies { get { - if (audioManagerAssemblies != null) return audioManagerAssemblies; - audioManagerAssemblies = GetAssemblies(); - return audioManagerAssemblies; + if (_cachedAssemblies != null) return _cachedAssemblies; + _cachedAssemblies = GetAssemblies(); + return _cachedAssemblies; } } @@ -82,7 +82,7 @@ private static Assembly[] GetAssemblies() /// The total in the project. public static int CountClassesOfType(bool internalCheckOnly = true) { - var assemblies = internalCheckOnly ? AudioManagerAssemblies : AppDomain.CurrentDomain.GetAssemblies(); + var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); return assemblies.SelectMany(x => x.GetTypes()) .Count(x => x.IsClass && typeof(T).IsAssignableFrom(x)); @@ -110,7 +110,7 @@ public static int CountClassesOfType(params Assembly[] assemblies) /// All the implementations of the entered class. public static IEnumerable GetClassesOfType(bool internalCheckOnly = true) { - var assemblies = internalCheckOnly ? AudioManagerAssemblies : AppDomain.CurrentDomain.GetAssemblies(); + var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); return assemblies.SelectMany(x => x.GetTypes()) .Where(x => x.IsClass && typeof(T).IsAssignableFrom(x) && !x.IsAbstract && x.FullName != typeof(T).FullName) @@ -126,7 +126,7 @@ public static IEnumerable GetClassesOfType(bool internalCheckOnly = true) /// All the implementations of the entered class. public static IEnumerable GetClassesNamesOfType(bool internalCheckOnly = true) { - var assemblies = internalCheckOnly ? AudioManagerAssemblies : AppDomain.CurrentDomain.GetAssemblies(); + var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); return assemblies.SelectMany(x => x.GetTypes()) .Where(x => x.IsClass && typeof(T).IsAssignableFrom(x) && x.FullName != typeof(T).FullName); From f7cbb6ee08de21dc5d438320496761e24d053dd3 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 15:34:42 +0200 Subject: [PATCH 4/9] Improve type searching speed with cache --- .../Runtime/Helpers/AssemblyHelper.cs | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs index 686a493..0bf8198 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs @@ -36,13 +36,19 @@ public static class AssemblyHelper /* ───────────────────────────────────────────────────────────────────────────────────────────────────────────── | Fields ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */ - + + /// + /// It is intended to use this cache to avoid the use + /// of in each call. + /// + private static readonly Dictionary> TypeCache = new(); + private static Assembly[] _cachedAssemblies; - + /* ───────────────────────────────────────────────────────────────────────────────────────────────────────────── | Properties ───────────────────────────────────────────────────────────────────────────────────────────────────────────── */ - + /// /// Gets all the cart assemblies to use when checking in internally only. /// @@ -82,10 +88,7 @@ private static Assembly[] GetAssemblies() /// The total in the project. public static int CountClassesOfType(bool internalCheckOnly = true) { - var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); - - return assemblies.SelectMany(x => x.GetTypes()) - .Count(x => x.IsClass && typeof(T).IsAssignableFrom(x)); + return GetClassesNamesOfType(internalCheckOnly).Count(); } @@ -110,14 +113,11 @@ public static int CountClassesOfType(params Assembly[] assemblies) /// All the implementations of the entered class. public static IEnumerable GetClassesOfType(bool internalCheckOnly = true) { - var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); - - return assemblies.SelectMany(x => x.GetTypes()) - .Where(x => x.IsClass && typeof(T).IsAssignableFrom(x) && !x.IsAbstract && x.FullName != typeof(T).FullName) + return GetClassesNamesOfType(internalCheckOnly) .Select(type => (T)Activator.CreateInstance(type)); } - - + + /// /// Gets all the classes of the entered type in the project. /// @@ -126,13 +126,27 @@ public static IEnumerable GetClassesOfType(bool internalCheckOnly = true) /// All the implementations of the entered class. public static IEnumerable GetClassesNamesOfType(bool internalCheckOnly = true) { + Type targetType = typeof(T); + + // We don't need to search in the project / assembly if we already know the type. + if (TypeCache.TryGetValue(targetType, out List cachedTypes)) + return cachedTypes; + var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); - return assemblies.SelectMany(x => x.GetTypes()) - .Where(x => x.IsClass && typeof(T).IsAssignableFrom(x) && x.FullName != typeof(T).FullName); + // Searching all the implementation of the class + var foundTypes = assemblies + .SelectMany(x => x.GetTypes()) + .Where(x => x.IsClass && targetType.IsAssignableFrom(x) && x != targetType) + .ToList(); + + // Caching for later + TypeCache[targetType] = foundTypes; + + return foundTypes; } - - + + /// /// Gets all the classes of the entered type in the project. /// From dc70835c5fc10a5f689b88edc7affcea441fbd79 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 15:39:00 +0200 Subject: [PATCH 5/9] Fix potential errors or leaks when using loaded assemblies --- Shared Systems/Runtime/Helpers/AssemblyHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs index 0bf8198..efd22b8 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs @@ -132,7 +132,8 @@ public static IEnumerable GetClassesNamesOfType(bool internalCheckOnly if (TypeCache.TryGetValue(targetType, out List cachedTypes)) return cachedTypes; - var assemblies = internalCheckOnly ? CachedAssemblies : AppDomain.CurrentDomain.GetAssemblies(); + var assemblies = internalCheckOnly ? CachedAssemblies + : UnityEngine.Assemblies.CurrentAssemblies.GetLoadedAssemblies(); // Searching all the implementation of the class var foundTypes = assemblies From c1caac3ba07e989e6bfeee7733831ea43a78bc39 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 16:04:51 +0200 Subject: [PATCH 6/9] Fix mass instanciation in AssemblyClassDef --- .../Runtime/Helpers/AssemblyClassDef.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Shared Systems/Runtime/Helpers/AssemblyClassDef.cs b/Shared Systems/Runtime/Helpers/AssemblyClassDef.cs index d027b30..fb0ee78 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyClassDef.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyClassDef.cs @@ -118,9 +118,25 @@ public T GetDefinedType() try { - return AssemblyHelper.GetClassesOfType(false).FirstOrDefault(t => - t.GetType().Assembly.FullName == Assembly && t.GetType().FullName == Type); - } + // We try first to resolve the type with his name : + // https://stackoverflow.com/questions/1825147/type-gettypenamespace-a-b-classname-returns-null + string assemblyQualifiedName = $"{Type}, {Assembly}"; + Type targetType = System.Type.GetType(assemblyQualifiedName); + + // If it failed, we use the fallback (without instantiation.) + if (targetType == null) + { + targetType = AssemblyHelper.GetClassesNamesOfType(false) + .FirstOrDefault(t => t.Assembly.FullName == Assembly && t.FullName == Type); + } + + if (targetType != null) + { + return (T)Activator.CreateInstance(targetType); + } + + return default; + } #pragma warning disable catch (Exception e) { From 7063d5e17f0cd4e74d5917fa62a165ebf548eed7 Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 16:23:02 +0200 Subject: [PATCH 7/9] Fix Json crash when instanced --- Shared Systems/Runtime/Helpers/AssemblyHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs index efd22b8..c9d62a7 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs @@ -138,7 +138,8 @@ public static IEnumerable GetClassesNamesOfType(bool internalCheckOnly // Searching all the implementation of the class var foundTypes = assemblies .SelectMany(x => x.GetTypes()) - .Where(x => x.IsClass && targetType.IsAssignableFrom(x) && x != targetType) + .Where(x => x.IsClass && !x.IsAbstract && !x.ContainsGenericParameters + && targetType.IsAssignableFrom(x) && x != targetType) .ToList(); // Caching for later From 0cf1380cccf18ea9e9c9566e328ff85753fdb59d Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 17:07:04 +0200 Subject: [PATCH 8/9] Fix TypeInitializationException on initialization --- .../Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs index 48e4942..8074f79 100644 --- a/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs +++ b/Shared Systems/Runtime/Helpers/Per Project/ProjectAssemblyDefinition.cs @@ -27,6 +27,7 @@ namespace CarterGames.Shared.SaveManager { internal static class ProjectAssemblyDefinition { +#if UNITY_EDITOR public static Assembly[] ProjectEditorAssemblies { get; } = new Assembly[] { Assembly.Load("CarterGames.SaveManager.Editor"), @@ -34,11 +35,12 @@ internal static class ProjectAssemblyDefinition Assembly.Load("CarterGames.Shared.SaveManager.Editor"), Assembly.Load("CarterGames.Shared.SaveManager") }; - +#else public static Assembly[] ProjectRuntimeAssemblies { get; } = new Assembly[] { Assembly.Load("CarterGames.SaveManager.Runtime"), Assembly.Load("CarterGames.Shared.SaveManager") }; +#endif } } \ No newline at end of file From 244797e00821c90e6c8d557401dfeeb3730d2efa Mon Sep 17 00:00:00 2001 From: NoixDeXydre Date: Fri, 15 May 2026 23:34:35 +0200 Subject: [PATCH 9/9] Make GetLoadedAssemblies only compatible for Unity 6.X --- Code/Runtime/Save Data/Objects/SaveObjectController.cs | 9 +++++++-- Shared Systems/Runtime/Helpers/AssemblyHelper.cs | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Code/Runtime/Save Data/Objects/SaveObjectController.cs b/Code/Runtime/Save Data/Objects/SaveObjectController.cs index 77a4bcc..b315cae 100644 --- a/Code/Runtime/Save Data/Objects/SaveObjectController.cs +++ b/Code/Runtime/Save Data/Objects/SaveObjectController.cs @@ -148,8 +148,13 @@ public static void Initialize() AllGlobalSaveObjects = new List(); AllGlobalSaveValues = new Dictionary>(); - var objTypes = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(x => x.GetTypes()) +#if UNITY_6000_0_OR_NEWER + var assemblies = UnityEngine.Assemblies.CurrentAssemblies.GetLoadedAssemblies(); +#else + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); +#endif + + var objTypes = assemblies.SelectMany(x => x.GetTypes()) .Where(x => x.IsClass && typeof(SaveObject).IsAssignableFrom(x) && !x.IsAbstract && x.FullName != typeof(SaveObject).FullName) .Select(type => (SaveObject)ScriptableObject.CreateInstance(type)) .Select(t => t.GetType()) diff --git a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs index c9d62a7..8cbc23e 100644 --- a/Shared Systems/Runtime/Helpers/AssemblyHelper.cs +++ b/Shared Systems/Runtime/Helpers/AssemblyHelper.cs @@ -132,8 +132,13 @@ public static IEnumerable GetClassesNamesOfType(bool internalCheckOnly if (TypeCache.TryGetValue(targetType, out List cachedTypes)) return cachedTypes; - var assemblies = internalCheckOnly ? CachedAssemblies +#if UNITY_6000_0_OR_NEWER + var assemblies = internalCheckOnly ? CachedAssemblies : UnityEngine.Assemblies.CurrentAssemblies.GetLoadedAssemblies(); +#else + var assemblies = internalCheckOnly ? CachedAssemblies + : AppDomain.CurrentDomain.GetAssemblies(); +#endif // Searching all the implementation of the class var foundTypes = assemblies