From 523ee8d6413a53480f287e2dad8261ad1dcb2802 Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Thu, 5 Oct 2023 15:32:08 -0400 Subject: [PATCH] - Added friendly name to ACLs for identification during unloading. - Added debug messages showing mods that have not unloaded from a previous session. --- .../LuaCs/Plugins/AssemblyManager.cs | 28 +++++++++++++++---- .../LuaCs/Plugins/CsPackageManager.cs | 24 ++++++++++++++-- .../MemoryFileAssemblyContextLoader.cs | 1 + 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs index 45468968f..370f6cff8 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/AssemblyManager.cs @@ -340,10 +340,23 @@ public partial class AssemblyManager /// 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 partial class AssemblyManager 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 partial class AssemblyManager /// 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 partial class AssemblyManager 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 partial class AssemblyManager /// [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 @@ public partial class AssemblyManager 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 partial class AssemblyManager 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 28179429c..fb87c1769 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Plugins/CsPackageManager.cs @@ -284,6 +284,24 @@ public sealed class CsPackageManager : IDisposable _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 sealed class CsPackageManager : IDisposable } // 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 sealed class CsPackageManager : IDisposable } #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 sealed class CsPackageManager : IDisposable 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 8c29f07e4..b4bbd5a6a 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;