- 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="LightInject" Version="6.6.4" />
|
||||||
<PackageReference Include="OneOf" Version="3.0.271" />
|
<PackageReference Include="OneOf" Version="3.0.271" />
|
||||||
<PackageReference Include="FluentResults" Version="3.16.0" />
|
<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="ImpromptuInterface " Version="8.0.4" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.Diagnostics" Version="7.1.2"/>
|
<PackageReference Include="Microsoft.Toolkit.Diagnostics" Version="7.1.2"/>
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\Libraries\moonsharp\MoonSharp.Interpreter\MoonSharp.Interpreter.csproj" />
|
<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 string FriendlyName { get; init; }
|
||||||
public bool IsScript { get; init; }
|
public bool IsScript { get; init; }
|
||||||
|
public bool UseInternalAccessName { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Data;
|
namespace Barotrauma.LuaCs.Data;
|
||||||
|
|
||||||
@@ -31,6 +32,12 @@ public interface IAssemblyResourceInfo : IBaseResourceInfo
|
|||||||
/// Is this entry referring to a script file collection.
|
/// Is this entry referring to a script file collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsScript { get; }
|
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 ImpromptuInterface;
|
||||||
using LightInject;
|
using LightInject;
|
||||||
using Microsoft.Toolkit.Diagnostics;
|
using Microsoft.Toolkit.Diagnostics;
|
||||||
|
using AssemblyLoader = Barotrauma.LuaCs.Services.AssemblyLoader;
|
||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
@@ -185,6 +186,7 @@ namespace Barotrauma
|
|||||||
servicesProvider.RegisterServiceResolver<ILuaCsHook>(factory => factory.GetInstance<IEventService>() as ILuaCsHook);
|
servicesProvider.RegisterServiceResolver<ILuaCsHook>(factory => factory.GetInstance<IEventService>() as ILuaCsHook);
|
||||||
servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
|
servicesProvider.RegisterServiceType<IPackageManagementService, PackageManagementService>(ServiceLifetime.Singleton);
|
||||||
servicesProvider.RegisterServiceType<IAssemblyManagementService, PluginManagementService>(ServiceLifetime.Singleton);
|
servicesProvider.RegisterServiceType<IAssemblyManagementService, PluginManagementService>(ServiceLifetime.Singleton);
|
||||||
|
servicesProvider.RegisterServiceType<IAssemblyLoaderService.IFactory, AssemblyLoader.Factory>(ServiceLifetime.Transient);
|
||||||
servicesProvider.RegisterServiceResolver<IPluginManagementService>(factory => factory.GetInstance<IAssemblyManagementService>());
|
servicesProvider.RegisterServiceResolver<IPluginManagementService>(factory => factory.GetInstance<IAssemblyManagementService>());
|
||||||
servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
servicesProvider.RegisterServiceType<ILuaScriptManagementService, LuaScriptManagementService>(ServiceLifetime.Singleton);
|
||||||
servicesProvider.RegisterServiceType<ILuaScriptLoader, LuaScriptLoader>(ServiceLifetime.Transient);
|
servicesProvider.RegisterServiceType<ILuaScriptLoader, LuaScriptLoader>(ServiceLifetime.Transient);
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public partial class LoggerService : ILoggerService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.Errors.Any())
|
if (result.IsFailed)
|
||||||
{
|
{
|
||||||
foreach (var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
@@ -192,7 +192,7 @@ public partial class LoggerService : ILoggerService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogError(error.Message);
|
LogError($"FluentResults::IError: {error.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+13
-3
@@ -261,7 +261,9 @@ public sealed class PackageManagementService : IPackageManagementService
|
|||||||
return result;
|
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
|
where T : IBaseResourceInfo
|
||||||
{
|
{
|
||||||
return resources
|
return resources
|
||||||
@@ -289,14 +291,22 @@ public sealed class PackageManagementService : IPackageManagementService
|
|||||||
|
|
||||||
var result = new FluentResults.Result();
|
var result = new FluentResults.Result();
|
||||||
|
|
||||||
result.WithReasons(UnloadPackages(toRemove).Reasons);
|
if (!toRemove.IsDefaultOrEmpty)
|
||||||
|
{
|
||||||
|
result.WithReasons(UnloadPackages(toRemove).Reasons);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.IsFailed)
|
if (result.IsFailed)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.WithReasons(LoadPackagesInfo(toAdd).Reasons);
|
if (!toAdd.IsDefaultOrEmpty)
|
||||||
|
{
|
||||||
|
result.WithReasons(LoadPackagesInfo(toAdd).Reasons);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result StopRunningPackages()
|
public FluentResults.Result StopRunningPackages()
|
||||||
|
|||||||
+265
-149
@@ -7,114 +7,124 @@ using System.Linq.Expressions;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Barotrauma.Extensions;
|
using Barotrauma.Extensions;
|
||||||
|
using Barotrauma.IO;
|
||||||
using Barotrauma.LuaCs.Data;
|
using Barotrauma.LuaCs.Data;
|
||||||
using Barotrauma.LuaCs.Events;
|
using Barotrauma.LuaCs.Events;
|
||||||
using FluentResults;
|
using FluentResults;
|
||||||
using FluentResults.LuaCs;
|
using FluentResults.LuaCs;
|
||||||
using ImpromptuInterface.Build;
|
using ImpromptuInterface.Build;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Toolkit.Diagnostics;
|
using Microsoft.Toolkit.Diagnostics;
|
||||||
using OneOf;
|
using OneOf;
|
||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public class PluginManagementService : IPluginManagementService, IAssemblyManagementService
|
public class PluginManagementService : IAssemblyManagementService
|
||||||
{
|
{
|
||||||
private readonly Func<IAssemblyLoaderService.LoaderInitData, IAssemblyLoaderService> _assemblyLoaderServiceFactory;
|
#region CSHARP_COMPILATION_OPTIONS
|
||||||
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();
|
|
||||||
|
|
||||||
public PluginManagementService(
|
private static readonly CSharpParseOptions ScriptParseOptions = CSharpParseOptions.Default
|
||||||
Func<IAssemblyLoaderService.LoaderInitData,
|
.WithPreprocessorSymbols(new[]
|
||||||
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
|
|
||||||
{
|
{
|
||||||
var context = AssemblyLoadContext.GetLoadContext(args.LoadedAssembly);
|
#if SERVER
|
||||||
if (context is not IAssemblyLoaderService loaderService)
|
"SERVER"
|
||||||
return;
|
#elif CLIENT
|
||||||
_eventService.Value.PublishEvent<IEventAssemblyLoaded>(sub => sub.OnAssemblyLoaded(args.LoadedAssembly));
|
"CLIENT"
|
||||||
var lookupDict = new ConcurrentDictionary<string, Type>();
|
#else
|
||||||
foreach (var type in args.LoadedAssembly.GetSafeTypes())
|
"UNDEFINED"
|
||||||
{
|
#endif
|
||||||
lookupDict[type.FullName ?? type.Name] = type;
|
#if DEBUG
|
||||||
}
|
,"DEBUG"
|
||||||
_assemblyTypesCache.AddOrUpdate(args.LoadedAssembly, lookupDict);
|
#endif
|
||||||
}
|
});
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _isDisposed = 0;
|
#if WINDOWS
|
||||||
public bool IsDisposed
|
private const string PLATFORM_TARGET = "Windows";
|
||||||
{
|
#elif OSX
|
||||||
get => ModUtils.Threading.GetBool(ref _isDisposed);
|
private const string PLATFORM_TARGET = "OSX";
|
||||||
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
|
#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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsDisposed { get; }
|
||||||
public FluentResults.Result Reset()
|
public FluentResults.Result Reset()
|
||||||
{
|
{
|
||||||
if (IsDisposed)
|
throw new NotImplementedException();
|
||||||
return FluentResults.Result.Fail($"{nameof(PluginManagementService)} is disposed!");
|
|
||||||
|
|
||||||
return FluentResults.Result.Fail("not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<ImmutableArray<Type>> GetImplementingTypes<T>(bool includeInterfaces = false,
|
#endregion
|
||||||
bool includeAbstractTypes = false, bool includeDefaultContext = true)
|
|
||||||
|
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>();
|
Guard.IsNotNull(serviceProvider, nameof(serviceProvider));
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
if (this._packageAssemblyResources.Any())
|
_assemblyLoaderFactory = assemblyLoaderFactory;
|
||||||
{
|
_storageService = storageService;
|
||||||
foreach (var resource in this._packageAssemblyResources
|
_logger = logger;
|
||||||
.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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
bool includeDefaultContext = true)
|
||||||
{
|
{
|
||||||
if (includeDefaultContext)
|
if (includeDefaultContext)
|
||||||
@@ -144,91 +154,197 @@ public class PluginManagementService : IPluginManagementService, IAssemblyManage
|
|||||||
return null;
|
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,
|
public ImmutableArray<Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
|
||||||
bool hostInstanceReference = false) where T : IDisposable
|
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
|
#if DEBUG
|
||||||
return ImmutableArray<Result<(Type, T)>>.Empty;
|
_logger.LogResults(res.ToResult());
|
||||||
#endif
|
#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();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentResults.Result UnloadManagedAssemblies()
|
public FluentResults.Result UnloadManagedAssemblies()
|
||||||
{
|
{
|
||||||
var res = new FluentResults.Result();
|
return FluentResults.Result.Fail($"{nameof(UnloadManagedAssemblies)}: Not Implemented.");
|
||||||
|
throw new NotImplementedException();
|
||||||
// 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,");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Assembly> GetLoadedAssembly(OneOf<AssemblyName, string> assemblyName, in Guid[] excludedContexts)
|
public Result<Assembly> GetLoadedAssembly(OneOf<AssemblyName, string> assemblyName, in Guid[] excludedContexts)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableArray<MetadataReference> GetDefaultMetadataReferences(bool includeDefaultContext = true)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableArray<IAssemblyLoaderService> AssemblyLoaderServices { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -79,6 +79,7 @@ public sealed class ModConfigFileParserService :
|
|||||||
// Type Specific
|
// Type Specific
|
||||||
FriendlyName = src.Element.GetAttributeString("FriendlyName", string.Empty),
|
FriendlyName = src.Element.GetAttributeString("FriendlyName", string.Empty),
|
||||||
IsScript = src.Element.GetAttributeBool("IsScript", false),
|
IsScript = src.Element.GetAttributeBool("IsScript", false),
|
||||||
|
UseInternalAccessName = src.Element.GetAttributeBool("UseInternalAccessName", false)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public class ServicesProvider : IServicesProvider
|
|||||||
{
|
{
|
||||||
EnablePropertyInjection = false
|
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
|
public void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IService where TService : class, IService, TSvcInterface
|
||||||
|
|||||||
+1
-14
@@ -12,7 +12,7 @@ using OneOf;
|
|||||||
|
|
||||||
namespace Barotrauma.LuaCs.Services;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
|
|
||||||
public interface IAssemblyManagementService : IReusableService
|
public interface IAssemblyManagementService : IPluginManagementService
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -22,17 +22,4 @@ public interface IAssemblyManagementService : IReusableService
|
|||||||
/// <param name="excludedContexts">Guids of excluded contexts.</param>
|
/// <param name="excludedContexts">Guids of excluded contexts.</param>
|
||||||
/// <returns><b>On Success:</b> The assembly. <br/><b>On Failure:</b> nothing.</returns>
|
/// <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);
|
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; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -32,13 +32,6 @@ public interface IPluginManagementService : IReusableService
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Type GetType(string typeName, bool isByRefType = false, bool includeInterfaces = false, bool includeDefaultContext = true);
|
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>
|
/// <summary>
|
||||||
/// Creates instances of the given type and provides Property Injection and instance reference caching. Disposes of
|
/// Creates instances of the given type and provides Property Injection and instance reference caching. Disposes of
|
||||||
/// all references that throw errors on
|
/// 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,
|
ImmutableArray<FluentResults.Result<(Type, T)>> ActivateTypeInstances<T>(ImmutableArray<Type> types, bool serviceInjection = true,
|
||||||
bool hostInstanceReference = false) where T : IDisposable;
|
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>
|
/// <summary>
|
||||||
/// Unloads all managed <see cref="IAssemblyPlugin"/>, <see cref="Assembly"/>, and <see cref="IAssemblyLoaderService"/>s.
|
/// 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;
|
namespace Barotrauma.LuaCs.Services;
|
||||||
public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
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 Guid Id { get; init; }
|
||||||
public ContentPackage OwnerPackage { get; private set; }
|
public ContentPackage OwnerPackage { get; private set; }
|
||||||
public bool IsReferenceOnlyMode { get; init; }
|
public bool IsReferenceOnlyMode { get; init; }
|
||||||
@@ -55,7 +69,6 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
private int _operationsRunning;
|
private int _operationsRunning;
|
||||||
|
|
||||||
//internal
|
//internal
|
||||||
private readonly IAssemblyManagementService _assemblyManagementService;
|
|
||||||
private readonly Action<IAssemblyLoaderService> _onUnload;
|
private readonly Action<IAssemblyLoaderService> _onUnload;
|
||||||
private readonly Func<IAssemblyLoaderService, AssemblyName, Assembly> _onResolvingManaged;
|
private readonly Func<IAssemblyLoaderService, AssemblyName, Assembly> _onResolvingManaged;
|
||||||
private readonly Func<Assembly, string, IntPtr> _onResolvingUnmanagedDll;
|
private readonly Func<Assembly, string, IntPtr> _onResolvingUnmanagedDll;
|
||||||
@@ -68,7 +81,6 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
public AssemblyLoader(IAssemblyLoaderService.LoaderInitData initData)
|
public AssemblyLoader(IAssemblyLoaderService.LoaderInitData initData)
|
||||||
: base(isCollectible: true, name: initData.Name)
|
: base(isCollectible: true, name: initData.Name)
|
||||||
{
|
{
|
||||||
_assemblyManagementService = initData.AssemblyManagementService;
|
|
||||||
Id = initData.InstanceId;
|
Id = initData.InstanceId;
|
||||||
IsReferenceOnlyMode = initData.IsReferenceMode;
|
IsReferenceOnlyMode = initData.IsReferenceMode;
|
||||||
this._onUnload = initData.OnUnload;
|
this._onUnload = initData.OnUnload;
|
||||||
@@ -217,7 +229,10 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var p = Path.GetFullPath(path.CleanUpPath());
|
var p = Path.GetFullPath(path.CleanUpPath());
|
||||||
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
|
if (!_dependencyResolvers.ContainsKey(p))
|
||||||
|
{
|
||||||
|
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ namespace Barotrauma.LuaCs;
|
|||||||
|
|
||||||
public interface IAssemblyLoaderService : IService
|
public interface IAssemblyLoaderService : IService
|
||||||
{
|
{
|
||||||
|
public interface IFactory : IService
|
||||||
|
{
|
||||||
|
IAssemblyLoaderService CreateInstance(LoaderInitData initData);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor record for instancing.
|
/// Constructor record for instancing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -26,7 +31,6 @@ public interface IAssemblyLoaderService : IService
|
|||||||
/// <param name="OnResolvingManaged"></param>
|
/// <param name="OnResolvingManaged"></param>
|
||||||
/// <param name="OnResolvingUnmanagedDll"></param>
|
/// <param name="OnResolvingUnmanagedDll"></param>
|
||||||
public record LoaderInitData(
|
public record LoaderInitData(
|
||||||
[Required][NotNull] IAssemblyManagementService AssemblyManagementService,
|
|
||||||
[Required] Guid InstanceId,
|
[Required] Guid InstanceId,
|
||||||
[Required][NotNull] string Name,
|
[Required][NotNull] string Name,
|
||||||
[Required] bool IsReferenceMode,
|
[Required] bool IsReferenceMode,
|
||||||
|
|||||||
Reference in New Issue
Block a user