Skip to content

Commit

Permalink
- Added friendly name to ACLs for identification during unloading.
Browse files Browse the repository at this point in the history
- Added debug messages showing mods that have not unloaded from a previous session.
  • Loading branch information
TBN-MapleWheels authored and evilfactory committed Oct 6, 2023
1 parent 40c8f3e commit 523ee8d
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -340,18 +340,31 @@ public IEnumerable<LoadedACL> GetAllLoadedACLs()
/// </summary>
public event System.Func<LoadedACL, bool> IsReadyToUnloadACL;

/// <summary>
/// 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.
/// </summary>
/// <param name="compiledAssemblyName"></param>
/// <param name="syntaxTree"></param>
/// <param name="externalMetadataReferences"></param>
/// <param name="compilationOptions"></param>
/// <param name="friendlyName">A non-unique name for later reference. Optional, set to null if unused.</param>
/// <param name="id">The guid of the assembly </param>
/// <param name="externFileAssemblyRefs"></param>
/// <returns></returns>
public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compiledAssemblyName,
[NotNull] IEnumerable<SyntaxTree> syntaxTree,
IEnumerable<MetadataReference> externalMetadataReferences,
[NotNull] CSharpCompilationOptions compilationOptions,
string friendlyName,
ref Guid id,
IEnumerable<Assembly> externFileAssemblyRefs = null)
{
// validation
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
Expand Down Expand Up @@ -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.
/// </summary>
/// <param name="filePaths">List of assemblies to try and load.</param>
/// <param name="friendlyName">A non-unique name for later reference. Optional.</param>
/// <param name="id">Guid of the ACL or Empty if none specified. Guid of ACL will be assigned to this var.</param>
/// <returns>Operation success messages.</returns>
/// <exception cref="ArgumentNullException"></exception>
public AssemblyLoadingSuccessState LoadAssembliesFromLocations([NotNull] IEnumerable<string> filePaths,
ref Guid id)
string friendlyName, ref Guid id)
{

if (filePaths is null)
Expand All @@ -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
Expand Down Expand Up @@ -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).
/// </summary>
/// <param name="id"></param>
/// <param name="friendlyName">A non-unique name for later reference. Optional.</param>
/// <param name="acl"></param>
/// <returns>Should only return false if an error occurs.</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private bool GetOrCreateACL(Guid id, out LoadedACL acl)
private bool GetOrCreateACL(Guid id, string friendlyName, out LoadedACL acl)
{
OpsLockLoaded.EnterUpgradeableReadLock();
try
Expand All @@ -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;
}
Expand Down Expand Up @@ -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<string, Type> AssembliesTypes => _assembliesTypes;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 523ee8d

Please sign in to comment.