2 using System.Collections.Generic;
3 using System.Collections.Immutable;
4 using System.Diagnostics.CodeAnalysis;
7 using System.Reflection;
8 using System.Runtime.CompilerServices;
10 using System.Threading;
12 using Microsoft.CodeAnalysis;
13 using Microsoft.CodeAnalysis.CSharp;
22 #region PRIVATE_FUNCDATA
24 private static readonly CSharpParseOptions ScriptParseOptions = CSharpParseOptions.Default
25 .WithPreprocessorSymbols(
new[]
40 private const string PLATFORM_TARGET =
"Windows";
42 private const string PLATFORM_TARGET =
"OSX";
44 private const string PLATFORM_TARGET =
"Linux";
48 private const string ARCHITECTURE_TARGET =
"Client";
50 private const string ARCHITECTURE_TARGET =
"Server";
53 private static readonly CSharpCompilationOptions CompilationOptions =
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
54 .WithMetadataImportOptions(MetadataImportOptions.All)
56 .WithOptimizationLevel(OptimizationLevel.Debug)
58 .WithOptimizationLevel(OptimizationLevel.Release)
60 .WithAllowUnsafe(
true);
62 private static readonly SyntaxTree BaseAssemblyImports = CSharpSyntaxTree.ParseText(
64 .AppendLine(
"using System.Reflection;")
65 .AppendLine(
"using Barotrauma;")
66 .AppendLine(
"using System.Runtime.CompilerServices;")
67 .AppendLine(
"[assembly: IgnoresAccessChecksTo(\"BarotraumaCore\")]")
69 .AppendLine(
"[assembly: IgnoresAccessChecksTo(\"Barotrauma\")]")
71 .AppendLine(
"[assembly: IgnoresAccessChecksTo(\"DedicatedServer\")]")
76 private readonly
string[] _publicizedAssembliesToLoad =
87 private const string SCRIPT_FILE_REGEX =
"*.cs";
88 private const string ASSEMBLY_FILE_REGEX =
"*.dll";
90 private readonly
float _assemblyUnloadTimeoutSeconds = 6f;
91 private Guid _publicizedAssemblyLoader;
92 private readonly List<ContentPackage> _currentPackagesByLoadOrder =
new();
93 private readonly Dictionary<ContentPackage, ImmutableList<ContentPackage>> _packagesDependencies =
new();
94 private readonly Dictionary<ContentPackage, Guid> _loadedCompiledPackageAssemblies =
new();
95 private readonly Dictionary<Guid, ContentPackage> _reverseLookupGuidList =
new();
96 private readonly Dictionary<Guid, HashSet<IAssemblyPlugin>> _loadedPlugins =
new ();
97 private readonly Dictionary<Guid, ImmutableHashSet<Type>> _pluginTypes =
new();
98 private readonly Dictionary<ContentPackage, RunConfig> _packageRunConfigs =
new();
99 private readonly Dictionary<Guid, ImmutableList<Type>> _luaRegisteredTypes =
new();
101 private readonly LuaCsSetup _luaCsSetup;
102 private DateTime _assemblyUnloadStartTime;
109 #region LUA_EXTENSIONS
121 var matchingPacks = _loadedCompiledPackageAssemblies
122 .Where(kvp => kvp.Key.Name.ToLowerInvariant().Contains(name.ToLowerInvariant()))
123 .Select(kvp => kvp.Value)
125 if (!matchingPacks.Any())
127 var types = matchingPacks
128 .Where(guid => !_luaRegisteredTypes.ContainsKey(guid))
129 .Select(guid =>
new KeyValuePair<Guid, ImmutableList<Type>>(
131 _assemblyManager.TryGetSubTypesFromACL(guid, out var types)
132 ? types.ToImmutableList()
133 : ImmutableList<Type>.Empty))
137 foreach (var kvp
in types)
139 _luaRegisteredTypes[kvp.Key] = kvp.Value;
140 foreach (Type type
in kvp.Value)
142 MoonSharp.Interpreter.UserData.RegisterType(type);
185 var guid = _pluginTypes
186 .Where(kvp => kvp.Value.Contains(t))
187 .Select(kvp => kvp.Key)
188 .FirstOrDefault(Guid.Empty);
190 if (guid.Equals(Guid.Empty) || !_reverseLookupGuidList.ContainsKey(guid) || _reverseLookupGuidList[guid] is
null)
192 package = _reverseLookupGuidList[guid];
205 loadedPlugins =
null;
206 if (package is
null || !_loadedCompiledPackageAssemblies.ContainsKey(package))
208 var guid = _loadedCompiledPackageAssemblies[package];
209 if (guid.Equals(Guid.Empty) || !_loadedPlugins.ContainsKey(guid))
211 loadedPlugins = _loadedPlugins[guid];
220 [MethodImpl(MethodImplOptions.Synchronized)]
230 ModUtils.Logging.PrintError($
"Error while executing Dispose event: {e.Message}");
236 foreach (Delegate del
in OnDispose.GetInvocationList())
243 ReflectionUtils.ResetCache();
246 _pluginTypes.Clear();
247 _loadedPlugins.Clear();
248 _publicizedAssemblyLoader = Guid.Empty;
249 _packagesDependencies.Clear();
250 _loadedCompiledPackageAssemblies.Clear();
251 _reverseLookupGuidList.Clear();
252 _packageRunConfigs.Clear();
253 _currentPackagesByLoadOrder.Clear();
256 foreach (var kvp
in _luaRegisteredTypes)
258 foreach (Type type
in kvp.Value)
260 MoonSharp.Interpreter.UserData.UnregisterType(type);
263 _luaRegisteredTypes.Clear();
265 _assemblyUnloadStartTime = DateTime.Now;
266 _publicizedAssemblyLoader = Guid.Empty;
269 while (!_assemblyManager.TryBeginDispose())
272 if (_assemblyUnloadStartTime.AddSeconds(_assemblyUnloadTimeoutSeconds) > DateTime.Now)
278 _assemblyUnloadStartTime = DateTime.Now;
280 while (!_assemblyManager.FinalizeDispose())
283 if (_assemblyUnloadStartTime.AddSeconds(_assemblyUnloadTimeoutSeconds) > DateTime.Now)
289 _assemblyManager.OnAssemblyLoaded -= AssemblyManagerOnAssemblyLoaded;
290 _assemblyManager.OnAssemblyUnloading -= AssemblyManagerOnAssemblyUnloading;
293 GC.SuppressFinalize(
this);
304 return AssemblyLoadingSuccessState.AlreadyLoaded;
307 _assemblyManager.OnAssemblyLoaded += AssemblyManagerOnAssemblyLoaded;
308 _assemblyManager.OnAssemblyUnloading += AssemblyManagerOnAssemblyUnloading;
311 _assemblyManager.FinalizeDispose();
312 if (_assemblyManager.IsCurrentlyUnloading)
314 ModUtils.Logging.PrintMessage($
"The below ACLs are still unloading:");
315 foreach (var wkref
in _assemblyManager.StillUnloadingACLs)
317 if (wkref.TryGetTarget(out var tgt))
319 ModUtils.Logging.PrintMessage($
"ACL Name: {tgt.FriendlyName}");
320 foreach (Assembly assembly
in tgt.Assemblies)
322 ModUtils.Logging.PrintMessage($
"-- Assembly: {assembly.GetName()}");
328 ImmutableList<Assembly> publicizedAssemblies = ImmutableList<Assembly>.Empty;
329 List<string> publicizedAssembliesLocList =
new();
331 foreach (
string dllName
in _publicizedAssembliesToLoad)
333 GetFiles(publicizedAssembliesLocList, dllName);
336 void GetFiles(List<string> list,
string searchQuery)
338 bool workshopFirst = _luaCsSetup.Config.PreferToUseWorkshopLuaSetup || LuaCsSetup.IsRunningInsideWorkshop;
340 var publicizedDir = Path.Combine(Environment.CurrentDirectory,
"Publicized");
345 var pck = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
348 publicizedDir = Path.Combine(pck.Dir,
"Binary",
"Publicized");
354 list.AddRange(Directory.GetFiles(publicizedDir, searchQuery));
357 catch (DirectoryNotFoundException)
361 ModUtils.Logging.PrintError($
"Unable to find <LuaCsPackage>/Binary/Publicized/ . Using Game folder instead.");
362 publicizedDir = Path.Combine(Environment.CurrentDirectory,
"Publicized");
366 ModUtils.Logging.PrintError($
"Unable to find <GameFolder>/Publicized/ . Using LuaCsPackage folder instead.");
367 var pck = LuaCsSetup.GetPackage(LuaCsSetup.LuaForBarotraumaId);
370 publicizedDir = Path.Combine(pck.Dir,
"Binary",
"Publicized");
375 list.AddRange(Directory.GetFiles(publicizedDir, searchQuery));
380 var loadState = _assemblyManager.LoadAssembliesFromLocations(publicizedAssembliesLocList,
"luacs_publicized_assemblies", ref _publicizedAssemblyLoader);
383 if (loadState is AssemblyLoadingSuccessState.Success)
385 if (_assemblyManager.TryGetACL(_publicizedAssemblyLoader, out var acl))
387 publicizedAssemblies = acl.Acl.Assemblies.ToImmutableList();
388 _assemblyManager.SetACLToTemplateMode(_publicizedAssemblyLoader);
394 IEnumerable<ContentPackage> packages = BuildPackagesList();
397 _packageRunConfigs.AddRange(packages
398 .Select(p =>
new KeyValuePair<ContentPackage, RunConfig>(p, GetRunConfigForPackage(p)))
399 .ToDictionary(p => p.Key, p=> p.Value));
402 var cpToRunA = _packageRunConfigs
403 .Where(kvp => ShouldRunPackage(kvp.Key, kvp.Value))
404 .Select(kvp => kvp.Key)
408 HashSet<string> cpNames =
new();
409 HashSet<string> duplicateNames =
new();
412 foreach (ContentPackage package
in cpToRunA)
414 if (cpNames.Contains(package.Name))
416 if (!duplicateNames.Contains(package.Name))
418 duplicateNames.Add(package.Name);
423 cpNames.Add(package.Name);
428 foreach (
string name
in duplicateNames)
430 var duplCpList = cpToRunA
431 .Where(p => p.Name.Equals(name))
434 if (duplCpList.Count < 2)
437 ContentPackage toKeep =
null;
438 foreach (ContentPackage package
in duplCpList)
440 if (package.Dir.Contains(
"LocalMods"))
447 toKeep ??= duplCpList.First();
449 duplCpList.Remove(toKeep);
450 cpToRunA.RemoveWhere(p => duplCpList.Contains(p));
453 var cpToRun = cpToRunA.ToImmutableList();
456 bool reliableMap = TryBuildDependenciesMap(cpToRun, out var packDeps);
459 ModUtils.Logging.PrintMessage($
"{nameof(CsPackageManager)}: Unable to create reliable dependencies map.");
462 _packagesDependencies.AddRange(packDeps.ToDictionary(
464 kvp => kvp.Value.ToImmutableList())
467 List<ContentPackage> packagesToLoadInOrder =
new();
470 if (reliableMap && OrderAndFilterPackagesByDependencies(
471 _packagesDependencies,
473 out var cannotLoadPackages))
475 packagesToLoadInOrder.AddRange(readyToLoad);
476 if (cannotLoadPackages is not
null)
478 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to load the following mods due to dependency errors:");
479 foreach (var pair
in cannotLoadPackages)
481 ModUtils.Logging.PrintError($
"Package: {pair.Key.Name} | Reason: {pair.Value}");
488 packagesToLoadInOrder.AddRange(_packagesDependencies.Select( p=> p.Key));
489 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to create a reliable load order. Defaulting to unordered loading!");
493 var toLoad = packagesToLoadInOrder
494 .Select(cp =>
new KeyValuePair<ContentPackage, LoadableData>(
497 TryScanPackagesForAssemblies(cp, out var list1) ? list1 :
null,
498 TryScanPackageForScripts(cp, out var list2) ? list2 :
null,
499 GetRunConfigForPackage(cp))))
500 .ToImmutableDictionary();
502 HashSet<ContentPackage> badPackages =
new();
503 foreach (var pair
in toLoad)
506 if (badPackages.Contains(pair.Key))
511 AssemblyLoadingSuccessState successState;
512 if (pair.Value.AssembliesFilePaths is not
null && pair.Value.AssembliesFilePaths.Any())
514 ModUtils.Logging.PrintMessage($
"Loading assemblies for CPackage {pair.Key.Name}");
516 foreach (
string assembliesFilePath
in pair.Value.AssembliesFilePaths)
518 ModUtils.Logging.PrintMessage($
"Found assemblies located at {Path.GetFullPath(ModUtils.IO.SanitizePath(assembliesFilePath))}");
522 successState = _assemblyManager.LoadAssembliesFromLocations(pair.Value.AssembliesFilePaths, pair.Key.Name, ref
id);
525 if (successState is not AssemblyLoadingSuccessState.Success)
527 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to load the binary assemblies for package {pair.Key.Name}. Error: {successState.ToString()}");
528 UpdatePackagesToDisable(ref badPackages, pair.Key, _packagesDependencies);
534 if (pair.Value.ScriptsFilePaths is not
null && pair.Value.ScriptsFilePaths.Any())
536 ModUtils.Logging.PrintMessage($
"Loading scripts for CPackage {pair.Key.Name}");
537 List<SyntaxTree> syntaxTrees =
new();
539 syntaxTrees.Add(GetPackageScriptImports());
540 bool abortPackage =
false;
542 foreach (
string scriptPath
in pair.Value.ScriptsFilePaths)
544 var state = ModUtils.IO.GetOrCreateFileText(scriptPath, out
string fileText,
null,
false);
546 if (state is not ModUtils.IO.IOActionResultState.Success)
548 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to load the script files for package {pair.Key.Name}. Error: {state.ToString()}");
549 UpdatePackagesToDisable(ref badPackages, pair.Key, _packagesDependencies);
556 CancellationToken token =
new();
557 syntaxTrees.Add(SyntaxFactory.ParseSyntaxTree(fileText, ScriptParseOptions, scriptPath, Encoding.Default, token));
559 if (token.IsCancellationRequested)
561 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to load the script files for package {pair.Key.Name}. Error: Syntax Parse Error.");
562 UpdatePackagesToDisable(ref badPackages, pair.Key, _packagesDependencies);
570 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to load the script files for package {pair.Key.Name}. Error: {e.Message}");
571 UpdatePackagesToDisable(ref badPackages, pair.Key, _packagesDependencies);
582 successState = _assemblyManager.LoadAssemblyFromMemory(
583 pair.Value.config.UseInternalAssemblyName ?
"CompiledAssembly" : pair.Key.Name.Replace(
" ",
""),
589 pair.Value.config.UseNonPublicizedAssemblies ?
null : publicizedAssemblies);
591 if (successState is not AssemblyLoadingSuccessState.Success)
593 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Unable to compile script assembly for package {pair.Key.Name}. Error: {successState.ToString()}");
594 UpdatePackagesToDisable(ref badPackages, pair.Key, _packagesDependencies);
600 if (
id != Guid.Empty)
602 ModUtils.Logging.PrintMessage($
"Assemblies from CPackage {pair.Key.Name} loaded with Guid {id}.");
603 _loadedCompiledPackageAssemblies.Add(pair.Key,
id);
604 _reverseLookupGuidList.Add(
id, pair.Key);
609 _currentPackagesByLoadOrder.AddRange(toLoad
610 .Where(p => !badPackages.Contains(p.Key))
611 .Select(p => p.Key));
614 foreach (var pair
in _loadedCompiledPackageAssemblies)
616 if (_assemblyManager.TryGetSubTypesFromACL<
IAssemblyPlugin>(pair.Value, out var types))
618 _pluginTypes[pair.Value] = types.ToImmutableHashSet();
619 foreach (var type
in _pluginTypes[pair.Value])
621 ModUtils.Logging.PrintMessage($
"Loading type: {type.Name}");
627 return AssemblyLoadingSuccessState.Success;
630 bool ShouldRunPackage(ContentPackage package,
RunConfig config)
632 return (!_luaCsSetup.Config.TreatForcedModsAsNormal && config.
IsForced())
633 || (ContentPackageManager.EnabledPackages.All.Contains(package) && config.
IsForcedOrStandard());
636 void UpdatePackagesToDisable(ref HashSet<ContentPackage>
set,
637 ContentPackage newDisabledPackage,
638 IEnumerable<KeyValuePair<ContentPackage, ImmutableList<ContentPackage>>> dependenciesMap)
640 set.Add(newDisabledPackage);
641 foreach (var package
in dependenciesMap)
643 if (package.Value.Contains(newDisabledPackage))
644 set.Add(newDisabledPackage);
656 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to call plugins' Initialize() without any loaded assemblies!");
662 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to call plugins' Initialize() without type instantiation!");
669 foreach (var contentPlugins
in _loadedPlugins)
672 foreach (var plugin
in contentPlugins.Value)
674 TryRun(() => plugin.Initialize(), $
"{nameof(IAssemblyPlugin.Initialize)}", $
"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
678 foreach (var contentPlugins
in _loadedPlugins)
681 foreach (var plugin
in contentPlugins.Value)
683 TryRun(() => plugin.OnLoadCompleted(), $
"{nameof(IAssemblyPlugin.OnLoadCompleted)}", $
"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
697 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to call plugins' PreInitPatching() without any loaded assemblies!");
703 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to call plugins' PreInitPatching() without type initialization!");
712 foreach (var contentPlugins
in _loadedPlugins)
715 foreach (var plugin
in contentPlugins.Value)
717 TryRun(() => plugin.PreInitPatching(), $
"{nameof(IAssemblyPlugin.PreInitPatching)}", $
"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
732 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to instantiate plugins without any loaded assemblies!");
742 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Attempted to load plugins when they were already loaded!");
747 foreach (var pair
in _pluginTypes)
750 foreach (Type type
in pair.Value)
752 if (!_loadedPlugins.ContainsKey(pair.Key))
753 _loadedPlugins.Add(pair.Key,
new());
754 else if (_loadedPlugins[pair.Key] is
null)
755 _loadedPlugins[pair.Key] =
new();
760 _loadedPlugins[pair.Key].Add(plugin);
764 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Error while instantiating plugin of type {type}. Now disposing...");
765 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Details: {e.Message} | {e.InnerException}");
767 if (plugin is not
null)
786 foreach (var contentPlugins
in _loadedPlugins)
788 foreach (var plugin
in contentPlugins.Value)
790 TryRun(() => plugin.Dispose(), $
"{nameof(IAssemblyPlugin.Dispose)}", $
"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
792 contentPlugins.Value.Clear();
795 _loadedPlugins.Clear();
812 var path = System.IO.Path.Combine(Path.GetFullPath(package.Dir),
"CSharp",
"RunConfig.xml");
813 if (!File.Exists(path))
818 return ModUtils.IO.LoadOrCreateTypeXml(out config, path, () =>
new RunConfig(
true).Sanitize(),
false);
825 private void TryRun(Action action,
string messageMethodName,
string messageTypeName)
833 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Error while running {messageMethodName}() on plugin of type {messageTypeName}");
834 ModUtils.Logging.PrintError($
"{nameof(CsPackageManager)}: Details: {e.Message} | {e.InnerException}");
838 private void AssemblyManagerOnAssemblyUnloading(Assembly assembly)
840 ReflectionUtils.RemoveAssemblyFromCache(assembly);
843 private void AssemblyManagerOnAssemblyLoaded(Assembly assembly)
848 if (assembly.FullName is not
null && assembly.FullName.StartsWith(
"System."))
850 ReflectionUtils.AddNonAbstractAssemblyTypes(assembly,
true);
855 this._assemblyManager = assemblyManager;
856 this._luaCsSetup = luaCsSetup;
864 private static bool TryScanPackageForScripts(ContentPackage package, out ImmutableList<string> scriptFilePaths)
866 string pathShared = Path.Combine(ModUtils.IO.GetContentPackageDir(package),
"CSharp",
"Shared");
867 string pathArch = Path.Combine(ModUtils.IO.GetContentPackageDir(package),
"CSharp", ARCHITECTURE_TARGET);
869 List<string> files =
new();
871 if (Directory.Exists(pathShared))
872 files.AddRange(Directory.GetFiles(pathShared, SCRIPT_FILE_REGEX, SearchOption.AllDirectories));
873 if (Directory.Exists(pathArch))
874 files.AddRange(Directory.GetFiles(pathArch, SCRIPT_FILE_REGEX, SearchOption.AllDirectories));
878 scriptFilePaths = files.ToImmutableList();
881 scriptFilePaths = ImmutableList<string>.Empty;
885 private static bool TryScanPackagesForAssemblies(ContentPackage package, out ImmutableList<string> assemblyFilePaths)
887 string path = Path.Combine(ModUtils.IO.GetContentPackageDir(package),
"bin", ARCHITECTURE_TARGET, PLATFORM_TARGET);
889 if (!Directory.Exists(path))
891 assemblyFilePaths = ImmutableList<string>.Empty;
895 assemblyFilePaths = System.IO.Directory.GetFiles(path, ASSEMBLY_FILE_REGEX, SearchOption.AllDirectories)
897 return assemblyFilePaths.Count > 0;
900 private static RunConfig GetRunConfigForPackage(ContentPackage package)
907 private IEnumerable<ContentPackage> BuildPackagesList()
912 return ContentPackageManager.AllPackages.Union(ContentPackageManager.EnabledPackages.All).Where(pack => !pack.Name.ToLowerInvariant().Equals(
"vanilla"));
916 private static SyntaxTree GetPackageScriptImports() => BaseAssemblyImports;
925 private static bool TryBuildDependenciesMap(ImmutableList<ContentPackage> packages, out Dictionary<ContentPackage, List<ContentPackage>> dependenciesMap)
927 bool reliableMap =
true;
928 dependenciesMap =
new();
929 foreach (var package
in packages)
931 dependenciesMap.Add(package,
new());
934 if (config.Dependencies is
null || !config.Dependencies.Any())
939 ContentPackage dep = packages.FirstOrDefault(p =>
940 (dependency.SteamWorkshopId != 0 && p.TryExtractSteamWorkshopId(out var steamWorkshopId)
941 && steamWorkshopId.Value == dependency.SteamWorkshopId)
942 || (!dependency.PackageName.IsNullOrWhiteSpace() && p.Name.ToLowerInvariant().Contains(dependency.PackageName.ToLowerInvariant())),
null);
946 dependenciesMap[package].Add(dep);
950 ModUtils.Logging.PrintWarning($
"Warning: The ContentPackage {package.Name} lists a dependency of (STEAMID: {dependency.SteamWorkshopId}, PackageName: {dependency.PackageName}) but it could not be found in the to-be-loaded CSharp packages list!");
970 private static bool OrderAndFilterPackagesByDependencies(
971 Dictionary<ContentPackage, ImmutableList<ContentPackage>> packages,
972 out IEnumerable<ContentPackage> readyToLoad,
973 out IEnumerable<KeyValuePair<ContentPackage, string>> cannotLoadPackages,
974 Func<ContentPackage, bool> packageChecksPredicate =
null)
976 HashSet<ContentPackage> completedPackages =
new();
977 List<ContentPackage> readyPackages =
new();
978 Dictionary<ContentPackage, string> unableToLoad =
new();
979 HashSet<ContentPackage> currentNodeChain =
new();
981 readyToLoad = readyPackages;
985 foreach (var toProcessPack
in packages)
987 ProcessPackage(toProcessPack.Key, toProcessPack.Value);
990 PackageProcRet ProcessPackage(ContentPackage packageToProcess, IEnumerable<ContentPackage> dependencies)
993 if (unableToLoad.ContainsKey(packageToProcess))
995 return PackageProcRet.BadPackage;
999 if (completedPackages.Contains(packageToProcess))
1001 return PackageProcRet.AlreadyCompleted;
1005 if (currentNodeChain.Contains(packageToProcess))
1007 StringBuilder sb =
new();
1008 sb.AppendLine(
"Error: Cyclic Dependency. ")
1010 "The following ContentPackages rely on eachother in a way that makes it impossible to know which to load first! ")
1012 "Note: the package listed twice shows where the cycle starts/ends and is not necessarily the problematic package.");
1014 foreach (var package
in currentNodeChain)
1017 sb.AppendLine($
"{i}. {package.Name}");
1020 sb.AppendLine($
"{i}. {packageToProcess.Name}");
1021 unableToLoad.Add(packageToProcess, sb.ToString());
1022 completedPackages.Add(packageToProcess);
1023 return PackageProcRet.BadPackage;
1026 if (packageChecksPredicate is not
null && !packageChecksPredicate.Invoke(packageToProcess))
1028 unableToLoad.Add(packageToProcess, $
"Unable to load package {packageToProcess.Name} due to failing checks.");
1029 completedPackages.Add(packageToProcess);
1030 return PackageProcRet.BadPackage;
1033 currentNodeChain.Add(packageToProcess);
1035 foreach (ContentPackage dependency
in dependencies)
1038 if (!packages.ContainsKey(dependency))
1041 if (!ContentPackageManager.EnabledPackages.All.Contains(dependency))
1044 ModUtils.Logging.PrintWarning(
1045 $
"Warning: the ContentPackage of {packageToProcess.Name} requires the Dependency {dependency.Name} but this package wasn't found in the enabled mods list!");
1051 var ret = ProcessPackage(dependency, packages[dependency]);
1053 if (ret is PackageProcRet.BadPackage)
1055 if (!unableToLoad.ContainsKey(packageToProcess))
1057 unableToLoad.Add(packageToProcess, $
"Error: Dependency failure. Failed to load {dependency.Name}");
1059 currentNodeChain.Remove(packageToProcess);
1060 if (!completedPackages.Contains(packageToProcess))
1062 completedPackages.Add(packageToProcess);
1064 return PackageProcRet.BadPackage;
1068 currentNodeChain.Remove(packageToProcess);
1069 completedPackages.Add(packageToProcess);
1070 readyPackages.Add(packageToProcess);
1071 return PackageProcRet.Completed;
1076 ModUtils.Logging.PrintError($
"Error while generating dependency loading order! Exception: {e.Message}");
1078 ModUtils.Logging.PrintError($
"Stack Trace: {e.StackTrace}");
1080 cannotLoadPackages = unableToLoad.Any() ? unableToLoad :
null;
1083 cannotLoadPackages = unableToLoad.Any() ? unableToLoad :
null;
1087 private enum PackageProcRet :
byte
1094 private record LoadableData(ImmutableList<string> AssembliesFilePaths, ImmutableList<string> ScriptsFilePaths,
RunConfig config);
Provides functionality for the loading, unloading and management of plugins implementing IAssemblyPlu...
void RunPluginsInit()
Executes instantiated plugins' Initialize() and OnLoadCompleted() methods.
void RunPluginsPreInit()
Executes instantiated plugins' PreInitPatching() method.
bool TryGetLoadedPluginsForPackage(ContentPackage package, out IEnumerable< IAssemblyPlugin > loadedPlugins)
Tries to get the loaded plugins for a given package.
Action OnDispose
Called when clean up is being performed. Use when relying on or making use of references from this ma...
bool AssembliesLoaded
Whether or not assemblies have been loaded.
void InstantiatePlugins(bool force=false)
Initializes plugin types that are registered.
AssemblyLoadingSuccessState LoadAssemblyPackages()
Begins the loading process of scanning packages for scripts and binary assemblies,...
bool TryGetPackageForPlugin< T >(out ContentPackage package)
Tries to find the content package that a given plugin belongs to.
static bool GetOrCreateRunConfig(ContentPackage package, out RunConfig config)
Gets the RunConfig.xml for the given package located at [cp_root]/CSharp/RunConfig....
bool PluginsPreInit
Whether or not loaded plugins had their preloader run.
bool LuaTryRegisterPackageTypes(string name, bool caseSensitive=false)
Searches for all types in all loaded assemblies from content packages who's names contain the name st...
bool PluginsInitialized
Whether or not plugins' types have been instantiated.
bool PluginsLoaded
Whether or not plugins are fully loaded.
IEnumerable< ContentPackage > GetCurrentPackagesByLoadOrder()
bool IsForcedOrStandard()