- 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:
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user