- Fixed NRE in TryBeginDispose
- Made `OnException` event useful. - Added some null checks where expected. - Fixed overridden Unload not being called. - Removed partial from AssemblyManager.cs - Made ClearTypesList() actually work. - Made exception details show in console on release builds. - Made content package name show on plugin load. - Made execution standard instead of none for autogenerated and erroneous RunConfigs.
This commit is contained in:
committed by
Evil Factory
parent
ac068aa3f9
commit
e984633ca5
@@ -25,7 +25,7 @@ namespace Barotrauma;
|
||||
/// Provides functionality for the loading, unloading and management of plugins implementing IAssemblyPlugin.
|
||||
/// All plugins are loaded into their own AssemblyLoadContext along with their dependencies.
|
||||
/// </summary>
|
||||
public partial class AssemblyManager
|
||||
public class AssemblyManager
|
||||
{
|
||||
#region ExternalAPI
|
||||
|
||||
@@ -143,16 +143,23 @@ public partial class AssemblyManager
|
||||
{
|
||||
if (!_subTypesLookupCache.TryAdd(typeName, list1))
|
||||
{
|
||||
ModUtils.Logging.PrintError($"{nameof(AssemblyManager)}: Unable to add subtypes to cache of type {typeName}!");
|
||||
ModUtils.Logging.PrintError(
|
||||
$"{nameof(AssemblyManager)}: Unable to add subtypes to cache of type {typeName}!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModUtils.Logging.PrintMessage($"{nameof(AssemblyManager)}: Warning: No types found during search for subtypes of {typeName}");
|
||||
ModUtils.Logging.PrintMessage(
|
||||
$"{nameof(AssemblyManager)}: Warning: No types found during search for subtypes of {typeName}");
|
||||
}
|
||||
|
||||
return list1;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.OnException?.Invoke($"{nameof(AssemblyManager)}::{nameof(GetSubTypesInLoadedAssemblies)}() | Error: {e.Message}", e);
|
||||
return ImmutableList<Type>.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
OpsLockLoaded.ExitReadLock();
|
||||
@@ -187,7 +194,6 @@ public partial class AssemblyManager
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="types"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSubTypesFromACL(Guid id, out IEnumerable<Type> types)
|
||||
{
|
||||
@@ -206,7 +212,7 @@ public partial class AssemblyManager
|
||||
/// Allows iteration over all types, including interfaces, in all loaded assemblies in the AsmMgr who's names match the string.
|
||||
/// Note: Will return the by-reference equivalent type if the type name is prefixed with "out " or "ref ".
|
||||
/// </summary>
|
||||
/// <param name="name">The string name of the type to search for.</param>
|
||||
/// <param name="typeName">The string name of the type to search for.</param>
|
||||
/// <returns>An Enumerator for matching types. List will be empty if bad params are supplied.</returns>
|
||||
public IEnumerable<Type> GetTypesByName(string typeName)
|
||||
{
|
||||
@@ -243,12 +249,19 @@ public partial class AssemblyManager
|
||||
types.Add(byRef ? t.MakeByRefType() : t);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
t = assembly.GetType(typeName, false, false);
|
||||
if (t is not null)
|
||||
types.Add(byRef ? t.MakeByRefType() : t);
|
||||
try
|
||||
{
|
||||
t = assembly.GetType(typeName, false, false);
|
||||
if (t is not null)
|
||||
types.Add(byRef ? t.MakeByRefType() : t);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.OnException?.Invoke($"{nameof(AssemblyManager)}::{nameof(GetTypesByName)}() | Error: {e.Message}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -316,9 +329,9 @@ public partial class AssemblyManager
|
||||
/// <returns></returns>
|
||||
public IEnumerable<LoadedACL> GetAllLoadedACLs()
|
||||
{
|
||||
OpsLockLoaded.EnterReadLock();
|
||||
try
|
||||
{
|
||||
OpsLockLoaded.EnterReadLock();
|
||||
return LoadedACLs.Select(kvp => kvp.Value).ToImmutableList();
|
||||
}
|
||||
finally
|
||||
@@ -360,6 +373,9 @@ public partial class AssemblyManager
|
||||
// validation
|
||||
if (compiledAssemblyName.IsNullOrWhiteSpace())
|
||||
return AssemblyLoadingSuccessState.BadName;
|
||||
|
||||
if (syntaxTree is null)
|
||||
return AssemblyLoadingSuccessState.InvalidAssembly;
|
||||
|
||||
if (!GetOrCreateACL(id, friendlyName, out var acl))
|
||||
return AssemblyLoadingSuccessState.ACLLoadFailure;
|
||||
@@ -419,8 +435,10 @@ public partial class AssemblyManager
|
||||
|
||||
if (filePaths is null)
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
var exception = new ArgumentNullException(
|
||||
$"{nameof(AssemblyManager)}::{nameof(LoadAssembliesFromLocations)}() | file paths supplied is null!");
|
||||
this.OnException?.Invoke($"Error: {exception.Message}", exception);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
ImmutableList<string> assemblyFilePaths = filePaths.ToImmutableList(); // copy the list before loading
|
||||
@@ -468,12 +486,15 @@ public partial class AssemblyManager
|
||||
{
|
||||
if (loadedAcl.Value.Acl is not null)
|
||||
{
|
||||
foreach (Delegate del in IsReadyToUnloadACL.GetInvocationList())
|
||||
if (IsReadyToUnloadACL is not null)
|
||||
{
|
||||
if (del is System.Func<LoadedACL, bool> { } func)
|
||||
foreach (Delegate del in IsReadyToUnloadACL.GetInvocationList())
|
||||
{
|
||||
if (!func.Invoke(loadedAcl.Value))
|
||||
return false; // Not ready, exit
|
||||
if (del is System.Func<LoadedACL, bool> { } func)
|
||||
{
|
||||
if (!func.Invoke(loadedAcl.Value))
|
||||
return false; // Not ready, exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,9 +513,10 @@ public partial class AssemblyManager
|
||||
LoadedACLs.Clear();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
catch(Exception e)
|
||||
{
|
||||
// should never happen
|
||||
this.OnException?.Invoke($"{nameof(TryBeginDispose)}() | Error: {e.Message}", e);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
@@ -609,9 +631,9 @@ public partial class AssemblyManager
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
catch(Exception e)
|
||||
{
|
||||
// should never happen but in-case
|
||||
this.OnException?.Invoke($"{nameof(GetOrCreateACL)}Error: {e.Message}", e);
|
||||
acl = null;
|
||||
return false;
|
||||
}
|
||||
@@ -648,9 +670,9 @@ public partial class AssemblyManager
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
// should never happen
|
||||
this.OnException?.Invoke($"{nameof(DisposeACL)}() | Error: {e.Message}", e);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
@@ -677,8 +699,9 @@ public partial class AssemblyManager
|
||||
.ToImmutableDictionary(t => t.FullName ?? t.Name, t => t);
|
||||
_subTypesLookupCache.Clear();
|
||||
}
|
||||
catch(ArgumentException _)
|
||||
catch(ArgumentException ae)
|
||||
{
|
||||
this.OnException?.Invoke($"{nameof(RebuildTypesList)}() | Error: {ae.Message}", ae);
|
||||
try
|
||||
{
|
||||
// some types must've had duplicate type names, build the list while filtering
|
||||
@@ -699,6 +722,7 @@ public partial class AssemblyManager
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.OnException?.Invoke($"{nameof(RebuildTypesList)}() | Error: {e.Message}", e);
|
||||
ModUtils.Logging.PrintError($"{nameof(AssemblyManager)}: Unable to create list of default assembly types! Default AssemblyLoadContext types searching not available.");
|
||||
#if DEBUG
|
||||
ModUtils.Logging.PrintError($"{nameof(AssemblyManager)}: Exception Details :{e.Message} | {e.InnerException}");
|
||||
@@ -729,14 +753,14 @@ public partial class AssemblyManager
|
||||
public readonly Guid Id;
|
||||
private ImmutableDictionary<string, Type> _assembliesTypes = ImmutableDictionary<string, Type>.Empty;
|
||||
public readonly MemoryFileAssemblyContextLoader Acl;
|
||||
private readonly AssemblyManager _manager;
|
||||
|
||||
internal LoadedACL(Guid id, AssemblyManager manager, string friendlyName)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Acl = new(manager);
|
||||
this._manager = manager;
|
||||
this.Acl.FriendlyName = friendlyName;
|
||||
this.Acl = new(manager)
|
||||
{
|
||||
FriendlyName = friendlyName
|
||||
};
|
||||
}
|
||||
public ImmutableDictionary<string, Type> AssembliesTypes => _assembliesTypes;
|
||||
|
||||
@@ -752,7 +776,7 @@ public partial class AssemblyManager
|
||||
.SelectMany(a => a.GetSafeTypes())
|
||||
.ToImmutableDictionary(t => t.FullName ?? t.Name, t => t);
|
||||
}
|
||||
catch(ArgumentException _)
|
||||
catch(ArgumentException)
|
||||
{
|
||||
// some types must've had duplicate type names, build the list while filtering
|
||||
Dictionary<string, Type> types = new();
|
||||
@@ -774,7 +798,7 @@ public partial class AssemblyManager
|
||||
|
||||
internal void ClearTypesList()
|
||||
{
|
||||
_assembliesTypes.Clear();
|
||||
_assembliesTypes = ImmutableDictionary<string, Type>.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,12 +826,12 @@ public static class AssemblyExtensions
|
||||
{
|
||||
return re.Types.Where(x => x != null)!;
|
||||
}
|
||||
catch (InvalidOperationException ioe)
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return new List<Type>();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
return new List<Type>();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using Barotrauma.Steam;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using MonoMod.Utils;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
@@ -147,12 +148,12 @@ public sealed class CsPackageManager : IDisposable
|
||||
/// <summary>
|
||||
/// Whether or not plugins' types have been instantiated.
|
||||
/// </summary>
|
||||
public bool PluginsInitialized { get; private set; } = false;
|
||||
public bool PluginsInitialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not plugins are fully loaded.
|
||||
/// </summary>
|
||||
public bool PluginsLoaded { get; private set; } = false;
|
||||
public bool PluginsLoaded { get; private set; }
|
||||
|
||||
public IEnumerable<ContentPackage> GetCurrentPackagesByLoadOrder() => _currentPackagesByLoadOrder;
|
||||
|
||||
@@ -333,7 +334,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
throw new DirectoryNotFoundException("No publicized assemblies found.");
|
||||
}
|
||||
// no directory found, use the other one
|
||||
catch (DirectoryNotFoundException dne)
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
if (_luaCsSetup.Config.PreferToUseWorkshopLuaSetup)
|
||||
{
|
||||
@@ -454,8 +455,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
if (reliableMap && OrderAndFilterPackagesByDependencies(
|
||||
_packagesDependencies,
|
||||
out var readyToLoad,
|
||||
out var cannotLoadPackages,
|
||||
null))
|
||||
out var cannotLoadPackages))
|
||||
{
|
||||
packagesToLoadInOrder.AddRange(readyToLoad);
|
||||
if (cannotLoadPackages is not null)
|
||||
@@ -611,21 +611,19 @@ public sealed class CsPackageManager : IDisposable
|
||||
|
||||
bool ShouldRunPackage(ContentPackage package, RunConfig config)
|
||||
{
|
||||
if (config.AutoGenerated)
|
||||
return false;
|
||||
return (!_luaCsSetup.Config.TreatForcedModsAsNormal && config.IsForced())
|
||||
|| (ContentPackageManager.EnabledPackages.All.Contains(package) && config.IsForcedOrStandard());
|
||||
}
|
||||
|
||||
void UpdatePackagesToDisable(ref HashSet<ContentPackage> list,
|
||||
void UpdatePackagesToDisable(ref HashSet<ContentPackage> set,
|
||||
ContentPackage newDisabledPackage,
|
||||
IEnumerable<KeyValuePair<ContentPackage, ImmutableList<ContentPackage>>> dependenciesMap)
|
||||
{
|
||||
list.Add(newDisabledPackage);
|
||||
set.Add(newDisabledPackage);
|
||||
foreach (var package in dependenciesMap)
|
||||
{
|
||||
if (package.Value.Contains(newDisabledPackage))
|
||||
list.Add(newDisabledPackage);
|
||||
set.Add(newDisabledPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,7 +653,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
// init
|
||||
foreach (var plugin in contentPlugins.Value)
|
||||
{
|
||||
TryRun(() => plugin.Initialize(), $"{nameof(IAssemblyPlugin.Initialize)}", plugin.GetType().Name);
|
||||
TryRun(() => plugin.Initialize(), $"{nameof(IAssemblyPlugin.Initialize)}", $"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +662,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
// load complete
|
||||
foreach (var plugin in contentPlugins.Value)
|
||||
{
|
||||
TryRun(() => plugin.OnLoadCompleted(), $"{nameof(IAssemblyPlugin.OnLoadCompleted)}", plugin.GetType().Name);
|
||||
TryRun(() => plugin.OnLoadCompleted(), $"{nameof(IAssemblyPlugin.OnLoadCompleted)}", $"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,7 +696,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
// init
|
||||
foreach (var plugin in contentPlugins.Value)
|
||||
{
|
||||
TryRun(() => plugin.PreInitPatching(), $"{nameof(IAssemblyPlugin.PreInitPatching)}", plugin.GetType().Name);
|
||||
TryRun(() => plugin.PreInitPatching(), $"{nameof(IAssemblyPlugin.PreInitPatching)}", $"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,21 +739,20 @@ public sealed class CsPackageManager : IDisposable
|
||||
try
|
||||
{
|
||||
plugin = (IAssemblyPlugin)Activator.CreateInstance(type);
|
||||
_loadedPlugins[pair.Key].Add(plugin);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ModUtils.Logging.PrintError($"{nameof(CsPackageManager)}: Error while instantiating plugin of type {type}. Now disposing...");
|
||||
#if DEBUG
|
||||
ModUtils.Logging.PrintError($"{nameof(CsPackageManager)}: Details: {e.Message} | {e.InnerException}");
|
||||
#endif
|
||||
TryRun(() => plugin?.Dispose(), "Dispose", type.FullName ?? type.Name);
|
||||
|
||||
plugin = null;
|
||||
if (plugin is not null)
|
||||
{
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
TryRun(() => plugin?.Dispose(), nameof(IAssemblyPlugin.Dispose), type.FullName ?? type.Name);
|
||||
plugin = null;
|
||||
}
|
||||
}
|
||||
if (plugin is not null)
|
||||
_loadedPlugins[pair.Key].Add(plugin);
|
||||
else
|
||||
ModUtils.Logging.PrintError($"{nameof(CsPackageManager)}: Error while instantiating plugin of type {type}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,7 +769,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
{
|
||||
foreach (var plugin in contentPlugins.Value)
|
||||
{
|
||||
TryRun(() => plugin.Dispose(), $"{nameof(IAssemblyPlugin.Dispose)}", plugin.GetType().Name);
|
||||
TryRun(() => plugin.Dispose(), $"{nameof(IAssemblyPlugin.Dispose)}", $"CP: {_reverseLookupGuidList[contentPlugins.Key].Name} Plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
contentPlugins.Value.Clear();
|
||||
}
|
||||
@@ -816,9 +813,7 @@ public sealed class CsPackageManager : IDisposable
|
||||
catch (Exception e)
|
||||
{
|
||||
ModUtils.Logging.PrintError($"{nameof(CsPackageManager)}: Error while running {messageMethodName}() on plugin of type {messageTypeName}");
|
||||
#if DEBUG
|
||||
ModUtils.Logging.PrintError($"{nameof(CsPackageManager)}: Details: {e.Message} | {e.InnerException}");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,10 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using Barotrauma;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
|
||||
|
||||
namespace Barotrauma;
|
||||
|
||||
@@ -23,16 +20,16 @@ namespace Barotrauma;
|
||||
public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
|
||||
{
|
||||
// public
|
||||
public string FriendlyName { get; set; } = null;
|
||||
public string FriendlyName { get; set; }
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
public Assembly CompiledAssembly { get; private set; } = null;
|
||||
public byte[] CompiledAssemblyImage { get; private set; } = null;
|
||||
public Assembly CompiledAssembly { get; private set; }
|
||||
public byte[] CompiledAssemblyImage { get; private set; }
|
||||
// ReSharper restore MemberCanBePrivate.Global
|
||||
// internal
|
||||
private readonly Dictionary<string, AssemblyDependencyResolver> _dependencyResolvers = new(); // path-folder, resolver
|
||||
protected bool IsResolving; //this is to avoid circular dependency lookup.
|
||||
private AssemblyManager _assemblyManager;
|
||||
public bool IsTemplateMode { get; set; } = false;
|
||||
public bool IsTemplateMode { get; set; }
|
||||
|
||||
public MemoryFileAssemblyContextLoader(AssemblyManager assemblyManager) : base(isCollectible: true)
|
||||
{
|
||||
@@ -73,23 +70,23 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
|
||||
LoadFromAssemblyPath(sanitizedFilePath);
|
||||
}
|
||||
// on fail of any we're done because we assume that loaded files are related. This ACL needs to be unloaded and collected.
|
||||
catch (ArgumentNullException ane)
|
||||
catch (ArgumentNullException)
|
||||
{
|
||||
return AssemblyLoadingSuccessState.BadFilePath;
|
||||
}
|
||||
catch (ArgumentException ae)
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return AssemblyLoadingSuccessState.BadFilePath;
|
||||
}
|
||||
catch (FileLoadException fle)
|
||||
catch (FileLoadException)
|
||||
{
|
||||
return AssemblyLoadingSuccessState.CannotLoadFile;
|
||||
}
|
||||
catch (FileNotFoundException fne)
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return AssemblyLoadingSuccessState.NoAssemblyFound;
|
||||
}
|
||||
catch (BadImageFormatException bfe)
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
return AssemblyLoadingSuccessState.InvalidAssembly;
|
||||
}
|
||||
@@ -281,7 +278,7 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
|
||||
}
|
||||
|
||||
|
||||
private new void Unload()
|
||||
public new void Unload()
|
||||
{
|
||||
CompiledAssembly = null;
|
||||
CompiledAssemblyImage = null;
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class RunConfig
|
||||
this.AutoGenerated = autoGenerated;
|
||||
if (autoGenerated)
|
||||
{
|
||||
(Client, Server) = ("None", "None");
|
||||
(Client, Server) = ("Standard", "Standard");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,18 +60,18 @@ public sealed class RunConfig
|
||||
{
|
||||
Client = SanitizeRunSetting(Client);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
Client = "None";
|
||||
Client = "Standard";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Server = SanitizeRunSetting(Server);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
Server = "None";
|
||||
Server = "Standard";
|
||||
}
|
||||
|
||||
Dependencies ??= new RunConfig.Dependency[] { };
|
||||
@@ -79,9 +79,9 @@ public sealed class RunConfig
|
||||
static string SanitizeRunSetting(string str) =>
|
||||
str switch
|
||||
{
|
||||
null => "None",
|
||||
"" => "None",
|
||||
" " => "None",
|
||||
null => "Standard",
|
||||
"" => "Standard",
|
||||
" " => "Standard",
|
||||
_ => str[0].ToString().ToUpper() + str.Substring(1).ToLower()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user