- Some work on PluginManagementService, IAssemblyLoaderService and IAssemblyManagementService refactors.

- Fixed mod list sync not checking for zero package diff length.
- Fixed the services provider not being able to inject itself as a dependcency.
- Added UseInternalName data spec to ModConfig.xml
- Changed Basic.Reference.Assemblies to Net80.
This commit is contained in:
MapleWheels
2026-01-29 18:01:45 -05:00
committed by Maplewheels
parent 37e3a195dc
commit 13a9bc443e
13 changed files with 324 additions and 180 deletions

View File

@@ -7,7 +7,7 @@
<PackageReference Include="LightInject" Version="6.6.4" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="FluentResults" Version="3.16.0" />
<PackageReference Include="Basic.Reference.Assemblies.Net60" Version="1.7.9" />
<PackageReference Include="Basic.Reference.Assemblies.Net80" Version="1.8.4" />
<PackageReference Include="ImpromptuInterface " Version="8.0.4" />
<PackageReference Include="Microsoft.Toolkit.Diagnostics" Version="7.1.2"/>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Libraries\moonsharp\MoonSharp.Interpreter\MoonSharp.Interpreter.csproj" />

View File

@@ -43,6 +43,7 @@ public record AssemblyResourceInfo : BaseResourceInfo, IAssemblyResourceInfo
{
public string FriendlyName { get; init; }
public bool IsScript { get; init; }
public bool UseInternalAccessName { get; init; }
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace Barotrauma.LuaCs.Data;
@@ -31,6 +32,12 @@ public interface IAssemblyResourceInfo : IBaseResourceInfo
/// Is this entry referring to a script file collection.
/// </summary>
public bool IsScript { get; }
/// <summary>
/// <b>[Required(IsScript: true)] Whether the internal compiled assembly name should be named to enabled use of the
/// <see cref="InternalsVisibleToAttribute"/> attribute.</b>
/// </summary>
public bool UseInternalAccessName { get; }
}

View File

@@ -22,6 +22,7 @@ using FluentResults;
using ImpromptuInterface;
using LightInject;
using Microsoft.Toolkit.Diagnostics;
using AssemblyLoader = Barotrauma.LuaCs.Services.AssemblyLoader;
namespace Barotrauma
{
@@ -185,6 +186,7 @@ namespace Barotrauma
servicesProvider.RegisterServiceResolver<ILuaCsHook>(factory => factory.GetInstance<IEventService>() as ILuaCsHook);
servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IAssemblyManagementService, PluginManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<IAssemblyLoaderService.IFactory, AssemblyLoader.Factory>(ServiceLifetime.Transient);
servicesProvider.RegisterServiceResolver<IPluginManagementService>(factory => factory.GetInstance<IAssemblyManagementService>());
servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
servicesProvider.RegisterServiceType<ILuaScriptLoader, LuaScriptLoader>(ServiceLifetime.Transient);

View File

@@ -182,7 +182,7 @@ public partial class LoggerService : ILoggerService
return;
}
if (result.Errors.Any())
if (result.IsFailed)
{
foreach (var error in result.Errors)
{
@@ -192,7 +192,7 @@ public partial class LoggerService : ILoggerService
}
else
{
LogError(error.Message);
LogError($"FluentResults::IError: {error.Message}");
}

View File

@@ -261,7 +261,9 @@ public sealed class PackageManagementService : IPackageManagementService
return result;
}
private static ImmutableArray<T> SelectCompatible<T>(ImmutableArray<T> resources, ImmutableHashSet<Identifier> enabledPackagesIdents, ImmutableArray<ContentPackage> loadingOrder)
private static ImmutableArray<T> SelectCompatible<T>(ImmutableArray<T> resources,
ImmutableHashSet<Identifier> enabledPackagesIdents,
ImmutableArray<ContentPackage> loadingOrder)
where T : IBaseResourceInfo
{
return resources
@@ -289,14 +291,22 @@ public sealed class PackageManagementService : IPackageManagementService
var result = new FluentResults.Result();
result.WithReasons(UnloadPackages(toRemove).Reasons);
if (!toRemove.IsDefaultOrEmpty)
{
result.WithReasons(UnloadPackages(toRemove).Reasons);
}
if (result.IsFailed)
{
return result;
}
return result.WithReasons(LoadPackagesInfo(toAdd).Reasons);
if (!toAdd.IsDefaultOrEmpty)
{
result.WithReasons(LoadPackagesInfo(toAdd).Reasons);
}
return result;
}
public FluentResults.Result StopRunningPackages()

View File

@@ -7,114 +7,124 @@ using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Text;
using System.Threading;
using Barotrauma.Extensions;
using Barotrauma.IO;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Events;
using FluentResults;
using FluentResults.LuaCs;
using ImpromptuInterface.Build;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Toolkit.Diagnostics;
using OneOf;
namespace Barotrauma.LuaCs.Services;
public class PluginManagementService : IPluginManagementService, IAssemblyManagementService
public class PluginManagementService : IAssemblyManagementService
{
private readonly Func<IAssemblyLoaderService.LoaderInitData, IAssemblyLoaderService> _assemblyLoaderServiceFactory;
private readonly ConcurrentDictionary<ContentPackage, (List<IAssemblyResourceInfo> ResourceInfos, IAssemblyLoaderService Loader)> _packageAssemblyResources = new();
private readonly ConcurrentDictionary<ContentPackage, List<IDisposable>> _pluginInstances = new();
private readonly Lazy<IEventService> _eventService;
private readonly ConditionalWeakTable<IAssemblyLoaderService, ContentPackage> _unloadingAssemblyLoaders = new();
private readonly ConditionalWeakTable<Assembly, ConcurrentDictionary<string, Type>> _assemblyTypesCache = new();
#region CSHARP_COMPILATION_OPTIONS
public PluginManagementService(
Func<IAssemblyLoaderService.LoaderInitData,
IAssemblyLoaderService> assemblyLoaderServiceFactory,
Lazy<IEventService> eventService)
{
_assemblyLoaderServiceFactory = assemblyLoaderServiceFactory;
_eventService = eventService;
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoadedGlobal;
}
private void OnAssemblyLoadedGlobal(object sender, AssemblyLoadEventArgs args)
{
// cache types by name
try
private static readonly CSharpParseOptions ScriptParseOptions = CSharpParseOptions.Default
.WithPreprocessorSymbols(new[]
{
var context = AssemblyLoadContext.GetLoadContext(args.LoadedAssembly);
if (context is not IAssemblyLoaderService loaderService)
return;
_eventService.Value.PublishEvent<IEventAssemblyLoaded>(sub => sub.OnAssemblyLoaded(args.LoadedAssembly));
var lookupDict = new ConcurrentDictionary<string, Type>();
foreach (var type in args.LoadedAssembly.GetSafeTypes())
{
lookupDict[type.FullName ?? type.Name] = type;
}
_assemblyTypesCache.AddOrUpdate(args.LoadedAssembly, lookupDict);
}
catch (Exception e)
{
// ignored
return;
}
}
#if SERVER
"SERVER"
#elif CLIENT
"CLIENT"
#else
"UNDEFINED"
#endif
#if DEBUG
,"DEBUG"
#endif
});
private int _isDisposed = 0;
public bool IsDisposed
{
get => ModUtils.Threading.GetBool(ref _isDisposed);
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
#if WINDOWS
private const string PLATFORM_TARGET = "Windows";
#elif OSX
private const string PLATFORM_TARGET = "OSX";
#elif LINUX
private const string PLATFORM_TARGET = "Linux";
#endif
#if CLIENT
private const string ARCHITECTURE_TARGET = "Client";
#elif SERVER
private const string ARCHITECTURE_TARGET = "Server";
#endif
private static readonly CSharpCompilationOptions CompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithMetadataImportOptions(MetadataImportOptions.All)
#if DEBUG
.WithOptimizationLevel(OptimizationLevel.Debug)
#else
.WithOptimizationLevel(OptimizationLevel.Release)
#endif
.WithAllowUnsafe(true);
private static readonly SyntaxTree BaseAssemblyImports = CSharpSyntaxTree.ParseText(
new StringBuilder()
.AppendLine("using System.Reflection;")
.AppendLine("using Barotrauma;")
.AppendLine("using System.Runtime.CompilerServices;")
.AppendLine("[assembly: IgnoresAccessChecksTo(\"BarotraumaCore\")]")
#if CLIENT
.AppendLine("[assembly: IgnoresAccessChecksTo(\"Barotrauma\")]")
#elif SERVER
.AppendLine("[assembly: IgnoresAccessChecksTo(\"DedicatedServer\")]")
#endif
.ToString(),
ScriptParseOptions);
#endregion
#region Disposal
public void Dispose()
{
throw new NotImplementedException();
}
public bool IsDisposed { get; }
public FluentResults.Result Reset()
{
if (IsDisposed)
return FluentResults.Result.Fail($"{nameof(PluginManagementService)} is disposed!");
return FluentResults.Result.Fail("not implemented");
throw new NotImplementedException();
}
public Result<ImmutableArray<Type>> GetImplementingTypes<T>(bool includeInterfaces = false,
bool includeAbstractTypes = false, bool includeDefaultContext = true)
#endregion
private IServicesProvider _serviceProvider;
private IAssemblyLoaderService.IFactory _assemblyLoaderFactory;
private IStorageService _storageService;
private ILoggerService _logger;
private readonly ConcurrentDictionary<ContentPackage, IAssemblyLoaderService> _assemblyLoaders = new();
private readonly AsyncReaderWriterLock _operationsLock = new();
public PluginManagementService(
IServicesProvider serviceProvider,
IAssemblyLoaderService.IFactory assemblyLoaderFactory,
IStorageService storageService,
ILoggerService logger)
{
var builder = ImmutableArray.CreateBuilder<Type>();
if (this._packageAssemblyResources.Any())
{
foreach (var resource in this._packageAssemblyResources
.Where(res => !res.Value.Loader.IsReferenceOnlyMode))
{
builder.AddRange(resource.Value.Loader.Assemblies
.SelectMany(assembly => assembly.GetSafeTypes())
.Where(type => type.IsAssignableTo(typeof(T)))
.Where(type => includeInterfaces || !type.IsInterface)
.Where(type => includeAbstractTypes || !type.IsAbstract));
}
}
if (includeDefaultContext)
{
builder.AddRange(AssemblyLoadContext.Default.Assemblies
.SelectMany(assembly => assembly.GetSafeTypes())
.Where(type => type.IsAssignableTo(typeof(T)))
.Where(type => includeInterfaces || !type.IsInterface)
.Where(type => includeAbstractTypes || !type.IsAbstract));
}
return builder.Count == 0
? FluentResults.Result.Fail($"Failed to find any types that implement {typeof(T).Name})")
: FluentResults.Result.Ok(builder.ToImmutable());
Guard.IsNotNull(serviceProvider, nameof(serviceProvider));
_serviceProvider = serviceProvider;
_assemblyLoaderFactory = assemblyLoaderFactory;
_storageService = storageService;
_logger = logger;
}
public Type GetType(string typeName, bool isByRefType = false, bool includeInterfaces = true,
public Result<ImmutableArray<Type>> GetImplementingTypes<T>(bool includeInterfaces = false, bool includeAbstractTypes = false,
bool includeDefaultContext = true)
{
throw new NotImplementedException();
}
public Type GetType(string typeName, bool isByRefType = false, bool includeInterfaces = false,
bool includeDefaultContext = true)
{
if (includeDefaultContext)
@@ -144,91 +154,197 @@ public class PluginManagementService : IPluginManagementService, IAssemblyManage
return null;
}
public FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resources)
{
IService.CheckDisposed(this);
if (resources.IsDefaultOrEmpty)
{
ThrowHelper.ThrowArgumentNullException($"{nameof(LoadAssemblyResources)}: The resources list is empty!");
}
return FluentResults.Result.Fail("not implemented");
}
public ImmutableArray<Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
bool hostInstanceReference = false) where T : IDisposable
{
throw new NotImplementedException();
}
public FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resources)
{
if (resources.IsDefaultOrEmpty)
{
ThrowHelper.ThrowArgumentNullException($"{nameof(LoadAssemblyResources)} The resource list is empty.)");
}
using var lck = _operationsLock.AcquireReaderLock().ConfigureAwait(false).GetAwaiter().GetResult();
IService.CheckDisposed(this);
var orderedContentPacks = resources.GroupBy(res => res.OwnerPackage)
.OrderBy(res => resources.FindIndex(r2 => r2.OwnerPackage == res.Key))
.ToImmutableArray();
var result = new FluentResults.Result();
return FluentResults.Result.Fail($"{nameof(LoadAssemblyResources)}: Not Implemented!");
throw new NotImplementedException();
foreach (var contentPack in orderedContentPacks)
{
LoadBinaries(contentPack);
LoadAndCompileScriptAssemblies(contentPack);
}
return result;
// helper methods
void LoadBinaries(IGrouping<ContentPackage,IAssemblyResourceInfo> contentPackRes)
{
var binaries = contentPackRes.Where(cRes => !cRes.IsScript)
.OrderBy(bin => bin.LoadPriority)
.SelectMany(bin => bin.FilePaths)
.ToImmutableArray();
if (binaries.IsDefaultOrEmpty)
{
return;
}
var assemblyLoader = _assemblyLoaders.GetOrAdd(contentPackRes.Key, (cp) => _assemblyLoaderFactory.CreateInstance(
new IAssemblyLoaderService.LoaderInitData(
InstanceId: Guid.NewGuid(),
contentPackRes.Key.Name,
IsReferenceMode: false,
OwnerPackage: contentPackRes.Key,
OnUnload: OnAssemblyLoaderUnloading,
OnResolvingManaged: OnAssemblyLoaderResolvingManaged,
OnResolvingUnmanagedDll: OnAssemblyLoaderResolvingUnmanaged
)));
var dependencyPaths = binaries
.Select(bin => System.IO.Path.GetDirectoryName(bin.FullPath))
.Distinct()
.ToImmutableArray();
foreach (var binResource in binaries)
{
var res = assemblyLoader.LoadAssemblyFromFile(binResource.FullPath, dependencyPaths);
result.WithReasons(res.Reasons);
#if DEBUG
return ImmutableArray<Result<(Type, T)>>.Empty;
_logger.LogResults(res.ToResult());
#endif
if (res.IsFailed)
{
_logger.LogResults(res.ToResult());
}
}
}
void LoadAndCompileScriptAssemblies(IGrouping<ContentPackage, IAssemblyResourceInfo> contentPackRes)
{
var scripts = contentPackRes.Where(cRes => cRes.IsScript)
.OrderBy(scr => scr.LoadPriority)
.Select(scr => (scr.FriendlyName, scr.FilePaths, scr.UseInternalAccessName))
.GroupBy(scr => scr.FriendlyName)
.ToImmutableArray();
if (scripts.IsDefaultOrEmpty)
{
return;
}
var metadataReferences = GetMetadataReferences();
var assemblyLoader = _assemblyLoaders.GetOrAdd(contentPackRes.Key, (cp) => _assemblyLoaderFactory.CreateInstance(
new IAssemblyLoaderService.LoaderInitData(
InstanceId: Guid.NewGuid(),
contentPackRes.Key.Name,
IsReferenceMode: false,
OwnerPackage: contentPackRes.Key,
OnUnload: OnAssemblyLoaderUnloading,
OnResolvingManaged: OnAssemblyLoaderResolvingManaged,
OnResolvingUnmanagedDll: OnAssemblyLoaderResolvingUnmanaged
)));
// create syntax trees
var syntaxTreesBuilder = ImmutableArray.CreateBuilder<SyntaxTree>();
foreach (var resourceInfo in contentPackRes)
{
if (resourceInfo.FilePaths.IsDefaultOrEmpty)
{
ThrowHelper.ThrowArgumentNullException($"{nameof(LoadAndCompileScriptAssemblies)} The resource list is empty for package {resourceInfo.OwnerPackage}.");
}
var loadRes = GetSourceFilesText(resourceInfo.FilePaths);
if (loadRes.IsFailed)
{
_logger.LogResults(loadRes.ToResult());
continue;
}
CancellationToken token = CancellationToken.None;
syntaxTreesBuilder.Add(SyntaxFactory.ParseSyntaxTree(
text: loadRes.Value,
options: ScriptParseOptions,
path: null,
encoding: Encoding.Default,
cancellationToken: token
));
}
throw new NotImplementedException();
}
Result<string> GetSourceFilesText(ImmutableArray<ContentPath> resourceInfoFilePaths)
{
if (_storageService.LoadPackageTextFiles(resourceInfoFilePaths) is not { IsDefaultOrEmpty: false } res)
{
_logger.LogError($"{nameof(GetSourceFilesText)}: Failed to load source files for ContentPackage {resourceInfoFilePaths.First().ContentPackage?.Name}.");
return FluentResults.Result.Fail($"{nameof(GetSourceFilesText)}: Failed to load source files for ContentPackage {resourceInfoFilePaths.First().ContentPackage?.Name}.");
}
var loadRes = new FluentResults.Result();
StringBuilder sb = new StringBuilder();
foreach ((ContentPath Path, Result<string> FileResult) loadResult in res)
{
if (loadResult.FileResult.IsFailed)
{
loadRes.WithErrors(loadResult.FileResult.Errors);
continue;
}
sb.AppendLine(loadResult.FileResult.Value);
}
if (loadRes.IsFailed)
{
return loadRes;
}
return sb.ToString();
}
IEnumerable<MetadataReference> GetMetadataReferences()
{
return Basic.Reference.Assemblies.Net80.References.All;
}
}
private IntPtr OnAssemblyLoaderResolvingUnmanaged(Assembly arg1, string arg2)
{
throw new NotImplementedException();
}
private Assembly OnAssemblyLoaderResolvingManaged(IAssemblyLoaderService arg1, AssemblyName arg2)
{
throw new NotImplementedException();
}
private void OnAssemblyLoaderUnloading(IAssemblyLoaderService loader)
{
throw new NotImplementedException();
}
public FluentResults.Result UnloadManagedAssemblies()
{
var res = new FluentResults.Result();
// cleanup managed plugins
if (_pluginInstances.Any())
{
foreach (var packageInstances in _pluginInstances)
{
if (!packageInstances.Value.Any())
continue;
foreach (var disposable in packageInstances.Value)
{
try
{
disposable.Dispose();
}
catch (Exception e)
{
res = res.WithError(new ExceptionalError(e)
.WithMetadata(MetadataType.ExceptionObject, this));
}
}
}
_pluginInstances.Clear();
}
_assemblyTypesCache.Clear();
// cleanup running assembly contexts
if (_packageAssemblyResources.Any())
{
foreach (var resource in _packageAssemblyResources.ToImmutableDictionary())
{
if (resource.Value.Loader is not null)
{
try
{
resource.Value.Loader.Dispose();
_unloadingAssemblyLoaders.AddOrUpdate(resource.Value.Loader, resource.Key);
_packageAssemblyResources.TryRemove(resource);
_packageAssemblyResources.TryAdd(resource.Key, (resource.Value.ResourceInfos, null));
}
catch (Exception e)
{
res = res.WithError(new ExceptionalError(e)
.WithMetadata(MetadataType.ExceptionObject, this));
}
}
}
}
return res.WithSuccess($"Unloading of managed assemblies started successfully,");
return FluentResults.Result.Fail($"{nameof(UnloadManagedAssemblies)}: Not Implemented.");
throw new NotImplementedException();
}
public Result<Assembly> GetLoadedAssembly(OneOf<AssemblyName, string> assemblyName, in Guid[] excludedContexts)
{
throw new NotImplementedException();
}
public ImmutableArray<MetadataReference> GetDefaultMetadataReferences(bool includeDefaultContext = true)
{
throw new NotImplementedException();
}
public ImmutableArray<IAssemblyLoaderService> AssemblyLoaderServices { get; }
}

View File

@@ -79,6 +79,7 @@ public sealed class ModConfigFileParserService :
// Type Specific
FriendlyName = src.Element.GetAttributeString("FriendlyName", string.Empty),
IsScript = src.Element.GetAttributeBool("IsScript", false),
UseInternalAccessName = src.Element.GetAttributeBool("UseInternalAccessName", false)
};
}

View File

@@ -21,6 +21,8 @@ public class ServicesProvider : IServicesProvider
{
EnablePropertyInjection = false
});
_serviceContainerInst.Register<IServicesProvider>((f) => this);
}
public void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface

View File

@@ -12,7 +12,7 @@ using OneOf;
namespace Barotrauma.LuaCs.Services;
public interface IAssemblyManagementService : IReusableService
public interface IAssemblyManagementService : IPluginManagementService
{
/// <summary>
@@ -22,17 +22,4 @@ public interface IAssemblyManagementService : IReusableService
/// <param name="excludedContexts">Guids of excluded contexts.</param>
/// <returns><b>On Success:</b> The assembly. <br/><b>On Failure:</b> nothing.</returns>
FluentResults.Result<Assembly> GetLoadedAssembly(OneOf<AssemblyName, string> assemblyName, in Guid[] excludedContexts);
/// <summary>
/// Gets all <see cref="MetadataReference"/> for all service-managed assemblies.
/// </summary>
/// <returns><see cref="MetadataReference"/> collection for all service-managed, and default if selected, assemblies, if any are found. Returns an empty collection otherwise.</returns>
ImmutableArray<MetadataReference> GetDefaultMetadataReferences(bool includeDefaultContext = true);
/// <summary>
/// Returns all active, managed assembly loaders.
/// </summary>
ImmutableArray<IAssemblyLoaderService> AssemblyLoaderServices { get; }
}

View File

@@ -32,13 +32,6 @@ public interface IPluginManagementService : IReusableService
/// <returns></returns>
Type GetType(string typeName, bool isByRefType = false, bool includeInterfaces = false, bool includeDefaultContext = true);
/// <summary>
/// Loads the provided assembly resources in the order of their dependencies and intra-mod priority load order.
/// </summary>
/// <param name="resources"></param>
/// <returns>Success/Failure and list of failed resources, if any.</returns>
FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resources);
/// <summary>
/// Creates instances of the given type and provides Property Injection and instance reference caching. Disposes of
/// all references that throw errors on
@@ -50,6 +43,12 @@ public interface IPluginManagementService : IReusableService
ImmutableArray<FluentResults.Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
bool hostInstanceReference = false) where T : IDisposable;
/// <summary>
/// Loads the provided assembly resources in the order of their dependencies and intra-mod priority load order.
/// </summary>
/// <param name="resources"></param>
/// <returns>Success/Failure and list of failed resources, if any.</returns>
FluentResults.Result LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resources);
/// <summary>
/// Unloads all managed <see cref="IAssemblyPlugin"/>, <see cref="Assembly"/>, and <see cref="IAssemblyLoaderService"/>s.

View File

@@ -24,6 +24,20 @@ using Path = System.IO.Path;
namespace Barotrauma.LuaCs.Services;
public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
{
public class Factory : IAssemblyLoaderService.IFactory
{
public IAssemblyLoaderService CreateInstance(IAssemblyLoaderService.LoaderInitData initData)
{
return new AssemblyLoader(initData);
}
public void Dispose()
{
//stateless service
}
public bool IsDisposed => false;
}
public Guid Id { get; init; }
public ContentPackage OwnerPackage { get; private set; }
public bool IsReferenceOnlyMode { get; init; }
@@ -55,7 +69,6 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
private int _operationsRunning;
//internal
private readonly IAssemblyManagementService _assemblyManagementService;
private readonly Action<IAssemblyLoaderService> _onUnload;
private readonly Func<IAssemblyLoaderService, AssemblyName, Assembly> _onResolvingManaged;
private readonly Func<Assembly, string, IntPtr> _onResolvingUnmanagedDll;
@@ -68,7 +81,6 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
public AssemblyLoader(IAssemblyLoaderService.LoaderInitData initData)
: base(isCollectible: true, name: initData.Name)
{
_assemblyManagementService = initData.AssemblyManagementService;
Id = initData.InstanceId;
IsReferenceOnlyMode = initData.IsReferenceMode;
this._onUnload = initData.OnUnload;
@@ -217,7 +229,10 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
try
{
var p = Path.GetFullPath(path.CleanUpPath());
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
if (!_dependencyResolvers.ContainsKey(p))
{
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
}
}
catch (Exception ex)
{

View File

@@ -13,6 +13,11 @@ namespace Barotrauma.LuaCs;
public interface IAssemblyLoaderService : IService
{
public interface IFactory : IService
{
IAssemblyLoaderService CreateInstance(LoaderInitData initData);
}
/// <summary>
/// Constructor record for instancing.
/// </summary>
@@ -26,7 +31,6 @@ public interface IAssemblyLoaderService : IService
/// <param name="OnResolvingManaged"></param>
/// <param name="OnResolvingUnmanagedDll"></param>
public record LoaderInitData(
[Required][NotNull] IAssemblyManagementService AssemblyManagementService,
[Required] Guid InstanceId,
[Required][NotNull] string Name,
[Required] bool IsReferenceMode,