diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs index 45468968f4..370f6cff8b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs @@ -340,10 +340,23 @@ public IEnumerable GetAllLoadedACLs() /// public event System.Func IsReadyToUnloadACL; + /// + /// Compiles an assembly from supplied references and syntax trees into the specified AssemblyContextLoader. + /// A new ACL will be created if the Guid supplied is Guid.Empty. + /// + /// + /// + /// + /// + /// A non-unique name for later reference. Optional, set to null if unused. + /// The guid of the assembly + /// + /// public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName, [NotNull] IEnumerable syntaxTree, IEnumerable externalMetadataReferences, [NotNull] CSharpCompilationOptions compilationOptions, + string friendlyName, ref Guid id, IEnumerable externFileAssemblyRefs = null) { @@ -351,7 +364,7 @@ public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compi if (compiledAssemblyName.IsNullOrWhiteSpace()) return AssemblyLoadingSuccessState.BadName; - if (!GetOrCreateACL(id, out var acl)) + if (!GetOrCreateACL(id, friendlyName, out var acl)) return AssemblyLoadingSuccessState.ACLLoadFailure; id = acl.Id; // pass on true id returned @@ -399,11 +412,12 @@ public bool SetACLToTemplateMode(Guid guid) /// If the supplied Guid is Empty, then a new ACl will be created and the Guid will be assigned to it. /// /// List of assemblies to try and load. + /// A non-unique name for later reference. Optional. /// Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var. /// Operation success messages. /// public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable filePaths, - ref Guid id) + string friendlyName, ref Guid id) { if (filePaths is null) @@ -419,7 +433,7 @@ public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumer return AssemblyLoadingSuccessState.NoAssemblyFound; } - if (GetOrCreateACL(id, out var loadedAcl)) + if (GetOrCreateACL(id, friendlyName, out var loadedAcl)) { var state = loadedAcl.Acl.LoadFromFiles(assemblyFilePaths); // if failure, we dispose of the acl @@ -567,10 +581,11 @@ public bool TryGetACL(Guid id, out LoadedACL acl) /// [IMPORTANT] After calling this method, the id you use should be taken from the acl container (acl.Id). /// /// + /// A non-unique name for later reference. Optional. /// /// Should only return false if an error occurs. [MethodImpl(MethodImplOptions.NoInlining)] - private bool GetOrCreateACL(Guid id, out LoadedACL acl) + private bool GetOrCreateACL(Guid id, string friendlyName, out LoadedACL acl) { OpsLockLoaded.EnterUpgradeableReadLock(); try @@ -581,7 +596,7 @@ private bool GetOrCreateACL(Guid id, out LoadedACL acl) try { id = Guid.NewGuid(); - acl = new LoadedACL(id, this); + acl = new LoadedACL(id, this, friendlyName); LoadedACLs[id] = acl; return true; } @@ -719,11 +734,12 @@ public sealed class LoadedACL public readonly MemoryFileAssemblyContextLoader Acl; private readonly AssemblyManager _manager; - internal LoadedACL(Guid id, AssemblyManager manager) + internal LoadedACL(Guid id, AssemblyManager manager, string friendlyName) { this.Id = id; this.Acl = new(manager); this._manager = manager; + this.Acl.FriendlyName = friendlyName; } public ImmutableDictionary AssembliesTypes => _assembliesTypes; diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs index 28179429c4..fb87c17699 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs @@ -284,6 +284,24 @@ public AssemblyLoadingSuccessState LoadAssemblyPackages() _assemblyManager.OnAssemblyLoaded += AssemblyManagerOnAssemblyLoaded; _assemblyManager.OnAssemblyUnloading += AssemblyManagerOnAssemblyUnloading; + // log error if some ACLs are still unloading (some assembly is still in use) + if (_assemblyManager.IsCurrentlyUnloading) + { + ModUtils.Logging.PrintError($"WARNING: Some mods from a previous session (lobby) are still loaded! This may result in undefined behaviour! Please restart your game. \nIf you wish to avoid this issue in the future, please disable the below mods and report the error to the mod author."); + foreach (var wkref in _assemblyManager.StillUnloadingACLs) + { + ModUtils.Logging.PrintError($"The below ACL is still unloading:"); + if (wkref.TryGetTarget(out var tgt)) + { + ModUtils.Logging.PrintError($"ACL Name: {tgt.Name}"); + foreach (Assembly assembly in tgt.Assemblies) + { + ModUtils.Logging.PrintError($"-- Assembly: {assembly.GetName()}"); + } + } + } + } + // load publicized assemblies var publicizedDir = Path.Combine(Environment.CurrentDirectory, "Publicized"); @@ -343,7 +361,7 @@ public AssemblyLoadingSuccessState LoadAssemblyPackages() } // try load them into an acl - var loadState = _assemblyManager.LoadAssembliesFromLocations(list, ref _publicizedAssemblyLoader); + var loadState = _assemblyManager.LoadAssembliesFromLocations(list, "luacs_publicized_assemblies", ref _publicizedAssemblyLoader); // loaded if (loadState is AssemblyLoadingSuccessState.Success) @@ -485,7 +503,7 @@ public AssemblyLoadingSuccessState LoadAssemblyPackages() } #endif - successState = _assemblyManager.LoadAssembliesFromLocations(pair.Value.AssembliesFilePaths, ref id); + successState = _assemblyManager.LoadAssembliesFromLocations(pair.Value.AssembliesFilePaths, pair.Key.Name, ref id); // error handling if (successState is not AssemblyLoadingSuccessState.Success) @@ -550,7 +568,7 @@ public AssemblyLoadingSuccessState LoadAssemblyPackages() syntaxTrees, null, CompilationOptions, - ref id, publicizedAssemblies); + pair.Key.Name, ref id, publicizedAssemblies); if (successState is not AssemblyLoadingSuccessState.Success) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs index 8c29f07e44..b4bbd5a6a4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/MemoryFileAssemblyContextLoader.cs @@ -23,6 +23,7 @@ namespace Barotrauma; public class MemoryFileAssemblyContextLoader : AssemblyLoadContext { // public + public string FriendlyName { get; set; } = null; // ReSharper disable MemberCanBePrivate.Global public Assembly CompiledAssembly { get; private set; } = null; public byte[] CompiledAssemblyImage { get; private set; } = null;