[Milestone] AssemblyLoader completed.

Details:
- Assembly Mgmt Service for loading now a separate interface, not intended for normal use.
- Assembly Loader work; implemented custom dictionary key and table.
- Assembly loading work.
- EventService completed.
- Moved assembly extensions to ModUtils.cs
- Work to event service.
NetworkService work
- Added ImpromptuInterfaces package.
- Networking Service work to support NetVars
- Event Service
- Added assemblies references package for script compilation. Updated Roslyn version for compatibility.
- Package Loading work.
Swap Harmony to HarmonyX
- More refactor conversion to FluentResults.
- Updated StylesService to return Results.
- Refactor of PackageService partially complete.
- Made IService.Reset() required to return a Result.
- Moved plugin/assembly related code to their own folder (same namespace).
- Updated interfaces to reflect the use of Result<T>.
- Partial refactor, incomplete.
- Added 'FluentResults' so we can stop using cursed Exception-based flow control in loading code.
- Added 'OneOf' nuget package: https://github.com/mcintyre321/OneOf
for the implementation of the Optional<T> pattern and complex discrete return types instead of cursed enums (see current AssemblyManager.cs).
- Reapplied old branch changes.
This commit is contained in:
MapleWheels
2024-11-04 02:33:31 -05:00
committed by Maplewheels
parent 01cc1d331b
commit 6880e5e9ee
97 changed files with 4100 additions and 1512 deletions
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
// ReSharper disable InconsistentNaming
namespace Barotrauma.LuaCs.Services;
public interface IAssemblyManagementService : IReusableService
{
/// <summary>
/// Searches for an assembly given it's fully qualified name, while excluding the contexts with the given Guids, if supplied.
/// </summary>
/// <param name="assemblyName">The fully-qualified assembly name.</param>
/// <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(string assemblyName, in Guid[] excludedContexts);
/// <summary>
/// Searches for an assembly given it's fully qualified name, while excluding the contexts with the given Guids, if supplied.
/// </summary>
/// <param name="assemblyName">The assembly info.</param>
/// <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(AssemblyName assemblyName, in Guid[] excludedContexts);
/// <summary>
/// Gets the assembly <see cref="MetadataReference"/> collection for the BCL and base game assemblies.
/// </summary>
/// <returns><see cref="MetadataReference"/> collection, if any are found. Returns an empty collection otherwise.</returns>
ImmutableArray<MetadataReference> GetDefaultMetadataReferences();
/// <summary>
/// Gets the assembly <see cref="MetadataReference"/> collection for all add-in assemblies loaded.
/// </summary>
/// <returns><see cref="MetadataReference"/> collection, if any are found. Returns an empty collection otherwise.</returns>
ImmutableArray<MetadataReference> GetAddInContextsMetadataReferences();
/// <summary>
///
/// </summary>
ImmutableArray<IAssemblyLoaderService> AssemblyLoaderServices { get; }
}
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Barotrauma.LuaCs.Configuration;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services.Safe;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Services;
public partial interface IConfigService : IReusableService, ILuaConfigService
{
/*
* Resource Files.
*/
FluentResults.Result AddConfigs(ImmutableArray<IConfigResourceInfo> configResources);
FluentResults.Result AddConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
FluentResults.Result RemoveConfigs(ImmutableArray<IConfigResourceInfo> configResources);
FluentResults.Result RemoveConfigsProfiles(ImmutableArray<IConfigProfileResourceInfo> configProfilesResources);
/*
* From resources
*/
FluentResults.Result AddConfigs(ImmutableArray<IConfigInfo> configs);
FluentResults.Result AddConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
FluentResults.Result RemoveConfigs(ImmutableArray<IConfigInfo> configs);
FluentResults.Result RemoveConfigsProfiles(ImmutableArray<IConfigProfileInfo> configProfiles);
/*
* Immediate mode
*/
FluentResults.Result<IConfigEntry<T>> AddConfigEntry<T>(ContentPackage package, string name,
T defaultValue,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<T, bool> valueChangePredicate = null,
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
FluentResults.Result<IConfigList> AddConfigList(ContentPackage package, string name,
int defaultIndex, IReadOnlyList<string> values,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<IConfigList, int, bool> valueChangePredicate = null,
Action<IConfigList, int> onValueChanged = null);
FluentResults.Result<IConfigRangeEntry<T>> AddConfigRangeEntry<T>(ContentPackage package, string name,
T defaultValue, T minValue, T maxValue,
Func<IConfigRangeEntry<T>, int> getStepCount,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<T, bool> valueChangePredicate = null,
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
FluentResults.Result<IConfigEntry<T>> AddConfigEntry<T>(string packageName, string name,
T defaultValue,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<T, bool> valueChangePredicate = null,
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
FluentResults.Result<IConfigList> AddConfigList(string packageName, string name,
int defaultIndex, IReadOnlyList<string> values,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<IConfigList, int, bool> valueChangePredicate = null,
Action<IConfigList, int> onValueChanged = null);
FluentResults.Result<IConfigRangeEntry<T>> AddConfigRangeEntry<T>(string packageName, string name,
T defaultValue, T minValue, T maxValue,
Func<IConfigRangeEntry<T>, int> getStepCount,
NetSync syncMode = NetSync.None,
ClientPermissions permissions = ClientPermissions.None,
Func<T, bool> valueChangePredicate = null,
Action<IConfigEntry<T>> onValueChanged = null) where T : IConvertible, IEquatable<T>;
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(ContentPackage package);
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(string packageName);
IReadOnlyDictionary<(ContentPackage, string), IConfigBase> GetAllConfigs();
FluentResults.Result<IConfigBase> GetConfig(ContentPackage package, string name);
FluentResults.Result<IConfigBase> GetConfig(string packageName, string name);
FluentResults.Result<T> GetConfig<T>(ContentPackage package, string name) where T : IConfigBase;
FluentResults.Result<T> GetConfig<T>(string packageName, string name) where T : IConfigBase;
}
@@ -0,0 +1,45 @@
using System;
using System.Reflection;
using Barotrauma.LuaCs.Events;
using Barotrauma.LuaCs.Services.Compatibility;
using Barotrauma.LuaCs.Services.Safe;
namespace Barotrauma.LuaCs.Services;
public interface IEventService : IReusableService, ILuaEventService
{
FluentResults.Result SetLegacyLuaRunnerFactory<T>(Func<LuaCsFunc, T> runnerFactory) where T : IEvent<T>;
void RemoveLegacyLuaRunnerFactory<T>() where T : IEvent<T>;
void SetAliasToEvent<T>(string alias) where T : IEvent<T>;
void RemoveEventAlias(string alias);
void RemoveAllEventAliases<T>() where T : IEvent<T>;
/// <summary>
///
/// </summary>
/// <param name="subscriber"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
FluentResults.Result Subscribe<T>(T subscriber) where T : IEvent<T>;
/// <summary>
///
/// </summary>
/// <param name="subscriber"></param>
/// <typeparam name="T"></typeparam>
void Unsubscribe<T>(T subscriber) where T : IEvent;
/// <summary>
/// Clears all subscribers for a given event type and removes any registration to the type.
/// </summary>
/// <typeparam name="T">The event type.</typeparam>
void ClearAllEventSubscribers<T>() where T : IEvent;
/// <summary>
/// Clears all subscribers lists.
/// </summary>
void ClearAllSubscribers();
/// <summary>
/// Invokes all alive subscribers of the given event using the provided invocation factory.
/// </summary>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
FluentResults.Result PublishEvent<T>(Action<T> action) where T : IEvent<T>;
}
@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Services;
public interface ILocalizationService : IReusableService
{
IReadOnlyCollection<CultureInfo> GetLoadedLocales();
void Remove(ImmutableArray<ILocalizationResourceInfo> localizations);
FluentResults.Result SetCurrentCulture(CultureInfo culture);
FluentResults.Result SetCurrentCulture(string cultureName);
FluentResults.Result LoadLocalizations(ImmutableArray<ILocalizationResourceInfo> localizationResources);
/// <summary>
/// Tries to get a localized string without a fallback. Returns success/failure and associated data.
/// </summary>
/// <param name="key">Neutral localization key.</param>
/// <returns></returns>
FluentResults.Result<string> GetLocalizedString(string key);
FluentResults.Result<string> GetLocalizedString(string key, CultureInfo targetCulture);
string GetLocalizedString(string key, string fallback);
string GetLocalizedString(string key, string fallback, CultureInfo targetCulture);
FluentResults.Result<string> GetLocalizedStringForPackage(ContentPackage package, string key);
FluentResults.Result<string> GetLocalizedStringForPackage(ContentPackage package, string key, CultureInfo targetCulture);
string GetLocalizedStringForPackage(ContentPackage package, string key, string fallback);
string GetLocalizedStringForPackage(ContentPackage package, string key, string fallback, CultureInfo targetCulture);
FluentResults.Result RegisterLocalizationResolver(CultureInfo targetCulture, Func<string, CultureInfo, string> factoryResolver);
bool IsCurrentCultureSupported(IResourceCultureInfo culturesInfo);
}
@@ -0,0 +1,27 @@
using System;
using Barotrauma.Networking;
using FluentResults;
using Microsoft.Xna.Framework;
namespace Barotrauma.LuaCs.Services;
/// <summary>
/// Provides console and debug logging services
/// </summary>
public interface ILoggerService : IReusableService
{
void HandleException(Exception exception, string prefix = null);
void LogError(string message);
void LogWarning(string message);
void LogMessage(string message, Color? serverColor = null, Color? clientColor = null);
void Log(string message, Color? color = null, ServerLog.MessageType messageType = ServerLog.MessageType.ServerMessage);
void LogResults(FluentResults.Result result);
#region DebugBuilds
void LogDebug(string message, Color? color = null);
void LogDebugWarning(string message);
void LogDebugError(string message);
#endregion
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
using Barotrauma.LuaCs.Data;
using MoonSharp.Interpreter;
using MoonSharp.Interpreter.Interop;
namespace Barotrauma.LuaCs.Services;
public interface ILuaScriptService : IReusableService
{
#region Script_File_Collector
/// <summary>
/// Adds the script files to the runner but does not execute them.
/// </summary>
/// <param name="luaResource"></param>
/// <returns></returns>
FluentResults.Result AddScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource);
/// <summary>
/// Removes the specific resources from the script runner. Important: Does not stop the
/// execution of any code related to the files nor guarantee cleanup of resources!
/// </summary>
/// <param name="luaResource"></param>
void RemoveScriptFiles(ImmutableArray<ILuaResourceInfo> luaResource);
/// <summary>
/// Executes loaded script files on the management service.
/// </summary>
/// <param name="pauseExecutionOnScriptError"></param>
/// <param name="verboseLogging"></param>
/// <returns></returns>
FluentResults.Result ExecuteScripts(bool pauseExecutionOnScriptError = false, bool verboseLogging = false);
ImmutableArray<ILuaResourceInfo> GetScriptResources();
#endregion
}
public interface ILuaScriptManagementService : IReusableService
{
#region Script_File_Execution
FluentResults.Result ExecuteLoadedScripts(ContentPackage package, bool pauseExecutionOnError = false, bool verboseLogging = false);
FluentResults.Result ExecuteLoadedScripts(ImmutableArray<ILuaResourceInfo> scripts, bool pauseExecutionOnError = false, bool verboseLogging = false);
FluentResults.Result ExecuteLoadedScripts(bool pauseExecutionOnError = false, bool verboseLogging = false);
#endregion
#region Type_Registration
IUserDataDescriptor RegisterType(Type type);
IUserDataDescriptor RegisterType(string typeName);
IUserDataDescriptor RegisterGenericType(Type type);
IUserDataDescriptor RegisterGenericType(string typeName, params string[] typeNameArgs);
void UnregisterType(Type type);
void UnregisterType(string typeName);
void UnregisterAllTypes();
#endregion
#region Type_Checks_&Utilities
bool IsRegistered(Type type);
bool IsTargetType(object obj, string typeName);
string TypeOf(object obj);
object CreateStatic(string typeName);
object CreateEnumTable(string typeName);
FieldInfo FindFieldRecursively(Type type, string fieldName);
void MakeFieldAccessible(IUserDataDescriptor descriptor, string fieldName);
MethodInfo FindMethodRecursively(Type type, string methodName, Type[] types = null);
void MakeMethodAccessible(IUserDataDescriptor descriptor, string methodName, string[] parameters = null);
PropertyInfo FindPropertyRecursively(Type type, string propertyName);
void MakePropertyAccessible(IUserDataDescriptor descriptor, string propertyName);
void AddMethod(IUserDataDescriptor descriptor, string methodName, object function);
void AddField(IUserDataDescriptor descriptor, string fieldName, DynValue value);
void RemoveMember(IUserDataDescriptor descriptor, string memberName);
bool HasMember(object obj, string memberName);
DynValue CreateUserDataFromDescriptor(DynValue scriptObject, IUserDataDescriptor descriptor);
DynValue CreateUserDataFromType(DynValue scriptObject, Type desiredType);
#endregion
}
@@ -0,0 +1,25 @@
using System;
using Barotrauma.LuaCs.Data;
using Barotrauma.LuaCs.Networking;
using Barotrauma.LuaCs.Services.Compatibility;
using Barotrauma.Networking;
namespace Barotrauma.LuaCs.Services;
internal delegate void NetMessageReceived(IReadMessage netMessage);
internal interface INetworkingService : IReusableService, ILuaCsNetworking
{
bool IsActive { get; }
bool IsSynchronized { get; }
public INetWriteMessage Start(Guid netId);
public void Receive(Guid netId, NetMessageReceived action);
#if SERVER
public void Send(IWriteMessage netMessage, NetworkConnection connection = null, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable);
#elif CLIENT
public void Send(IWriteMessage netMessage, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable);
#endif
public void RegisterNetVar(INetVar netVar);
public void SendNetVar(INetVar netVar);
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Barotrauma.Extensions;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Services;
public interface IPackageManagementService : IReusableService
{
/// <summary>
/// Adds packages to the queue of loadable packages without initializing them.
/// </summary>
/// <param name="packages"></param>
void QueuePackages(ImmutableArray<LoadablePackage> packages);
/// <summary>
/// Generates the ModConfigInfo for all queued packages and adds them to the store.
/// </summary>
/// <param name="loadParallel">Use multithreaded loading.</param>
/// <param name="reportFailOnDuplicates">Whether duplicate packages should be reported as errors.</param>
/// <returns>Failure/Success records for each package.</returns>
FluentResults.Result ParseQueuedPackages(bool loadParallel = true, bool reportFailOnDuplicates = false);
/// <summary>
/// Loads only the localizations, configs, and config profiles for stored packages.
/// </summary>
/// <param name="loadParallel"></param>
/// <returns></returns>
FluentResults.Result LoadPackageConfigsResourcesGroup(bool loadParallel = true);
/// <summary>
/// Loads all resources for stored packages.
/// </summary>
/// <param name="loadParallel">Use multithreaded loading.</param>
/// <param name="safeResourcesOnly">Only load safe scripting resources, such as Lua. C# plugins disabled.</param>
/// <returns></returns>
FluentResults.Result LoadAllPackageResources(bool loadParallel = true, bool safeResourcesOnly = true);
FluentResults.Result UnloadPackages();
bool IsPackageLoaded(ContentPackage package);
bool CheckDependencyLoaded(IPackageDependencyInfo info);
bool CheckDependenciesLoaded([NotNull]IEnumerable<IPackageDependencyInfo> infos, out ImmutableArray<IPackageDependencyInfo> missingPackages);
bool CheckEnvironmentSupported(IPlatformInfo platform);
/// <summary>
/// Tries to get the package dependency record to refer to that specific package if it exists, optionally create it.
/// </summary>
/// <param name="package">ContentPackage reference</param>
/// <param name="addIfMissing">Register a new IPackageDependencyInfo reference.</param>
/// <returns></returns>
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ContentPackage package,
bool addIfMissing = false);
/// <summary>
/// Tries to get the package dependency record to refer to that specific package if it exists, optionally create it.
/// </summary>
/// <param name="steamWorkshopId">The Steam Workshop ID, if available, if not enter zero ('0').</param>
/// <param name="packageName">The name of the package.</param>
/// <param name="folderPath">The folder path, as formatted in [ContentPackage.Path].</param>
/// <param name="addIfMissing">Register a new IPackageDependencyInfo reference.</param>
/// <returns></returns>
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(ulong steamWorkshopId,
string packageName, string folderPath = null, bool addIfMissing = false);
/// <summary>
/// Tries to get the package dependency record to refer to that specific package if it exists.
/// Note: This overload does not allow the registration of a new dependency.
/// </summary>
/// <param name="folderPath">The folder path, as formatted in [ContentPackage.Path].</param>
/// <returns></returns>
FluentResults.Result<IPackageDependencyInfo> GetPackageDependencyInfoRecord(string folderPath);
IPackageDependencyInfo CreateOrphanPackageDependencyInfoRecord(string packageName,
string packagePath, ulong steamWorkshopId);
}
public readonly record struct LoadablePackage
{
public ContentPackage Package { get; }
public bool IsEnabled { get; }
public LoadablePackage(ContentPackage package, bool isEnabled)
{
Package = package;
IsEnabled = isEnabled;
}
public static ImmutableArray<LoadablePackage> FromEnumerable(IEnumerable<ContentPackage> packages, bool isEnabled)
{
var builder = ImmutableArray.CreateBuilder<LoadablePackage>();
packages.ForEach(p => builder.Add(new LoadablePackage(p, isEnabled)));
return builder.ToImmutable();
}
}
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Barotrauma.LuaCs.Data;
using FluentResults;
namespace Barotrauma.LuaCs.Services;
public interface IPackageService : IReusableService,
// These allow us the pass the IContentPackageService to anything that needs the data without having to directly reference the member
IResourceCultureInfo, IAssembliesResourcesInfo, ILocalizationsResourcesInfo, ILuaScriptsResourcesInfo
{
ContentPackage Package { get; }
IModConfigInfo ModConfigInfo { get; }
bool IsEnabledInModList { get; }
/// <summary>
/// Try to load the XML config and resources information from the given package.
/// </summary>
/// <param name="package"></param>
/// <returns>Whether the package was parsed without errors.</returns>
FluentResults.Result LoadResourcesInfo([NotNull]LoadablePackage package);
/// <summary>
/// Tries to load all assemblies and instance plugins for the given resources list, regardless whether they're marked as optional and/or lazy load.
/// Will sort by load priority unless overriden/bypassed.
/// </summary>
/// <param name="assembliesInfo"></param>
/// <param name="ignoreDependencySorting"></param>
/// <returns>Whether loading is successful. Returns true on an empty list.</returns>
FluentResults.Result LoadPlugins([NotNull]IAssembliesResourcesInfo assembliesInfo, bool ignoreDependencySorting = false);
FluentResults.Result LoadLocalizations([NotNull]ILocalizationsResourcesInfo localizationsInfo);
FluentResults.Result AddLuaScripts([NotNull]ILuaScriptsResourcesInfo luaScriptsInfo);
#if CLIENT
FluentResults.Result LoadStyles([NotNull]IStylesResourcesInfo stylesInfo);
#endif
FluentResults.Result LoadConfig([NotNull]IConfigsResourcesInfo configsResourcesInfo, [NotNull]IConfigProfilesResourcesInfo configProfilesResourcesInfo);
}
@@ -0,0 +1,53 @@
using System;
using System.Collections.Immutable;
using System.Reflection;
using Barotrauma.LuaCs.Data;
using Microsoft.CodeAnalysis;
namespace Barotrauma.LuaCs.Services;
public interface IPluginManagementService : IReusableService
{
/// <summary>
/// Checks if an assembly with either the fully-qualified name globally or a 'friendly name' within loaded plugins
/// with the given name is loaded.
/// </summary>
/// <param name="friendlyName"></param>
/// <returns></returns>
bool IsAssemblyLoadedGlobal(string friendlyName);
// TODO: Documentation.
FluentResults.Result<ImmutableArray<T>> GetTypes<T>(
ContentPackage package = null,
string namespacePrefix = null,
bool includeInterfaces = false,
bool includeAbstractTypes = false,
bool includeDefaultContext = true,
bool includeExplicitAssembliesOnly = false);
/// <summary>
/// Gets the assembly <c>MetadataReference</c> collection for the BCL and base game assemblies.
/// </summary>
/// <returns></returns>
ImmutableArray<MetadataReference> GetStandardMetadataReferences();
/// <summary>
///
/// </summary>
/// <returns></returns>
ImmutableArray<MetadataReference> GetPluginMetadataReferences();
/// <summary>
///
/// </summary>
/// <param name="package"></param>
/// <returns></returns>
FluentResults.Result<ImmutableArray<IAssemblyResourceInfo>> GetCachedAssembliesForPackage(ContentPackage package);
/// <summary>
///
/// </summary>
/// <param name="resource"></param>
/// <returns>Success/Failure and list of failed resources, if any.</returns>
FluentResults.Result<ImmutableArray<IAssemblyResourceInfo>> LoadAssemblyResources(ImmutableArray<IAssemblyResourceInfo> resource);
}
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
using Barotrauma.LuaCs.Data;
namespace Barotrauma.LuaCs.Services;
public interface IPluginService : IReusableService
{
bool IsAssemblyLoaded(string friendlyName);
/// <summary>
/// Loads the assemblies for the given information
/// </summary>
/// <param name="assemblyResourcesInfo"></param>
/// <param name="injectServices"></param>
/// <param name="typeInstances"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
FluentResults.Result LoadAndInstanceTypes<T>(IEnumerable<IAssemblyResourceInfo> assemblyResourcesInfo, bool injectServices, out ImmutableArray<T> typeInstances) where T : class, IAssemblyPlugin;
FluentResults.Result<ImmutableArray<T>> GetLoadedPluginTypesInPackage<T>() where T : class, IAssemblyPlugin;
/// <summary>
/// Advances the loading/execution state of the plugin. IMPORTANT: You cannot set the execution state of plugins
/// to 'Disposed'. You must instead call the 'DisposePlugins' method.
/// </summary>
/// <param name="newState"></param>
/// <returns></returns>
FluentResults.Result AdvancePluginStates(PluginRunState newState);
/// <summary>
/// Disposes of all running plugins hosted by the service and releases their references to allow unloading.
/// </summary>
/// <returns>Success of the operation. Returns false if any plugin threw errors during disposal.</returns>
FluentResults.Result DisposePlugins();
/// <summary>
/// Gets the current plugin execution state.
/// </summary>
/// <returns></returns>
PluginRunState GetPluginRunState();
}
public enum PluginRunState
{
Instanced=0,
PreInitialization=1,
Initialized=2,
LoadingCompleted=3,
Disposed=4
}
@@ -0,0 +1,29 @@
using System;
namespace Barotrauma.LuaCs.Services;
/// <summary>
/// Defines a service that can be reset to it's post-constructor state and reused without needing to be disposed.
/// Intended for persistent services.
/// </summary>
public interface IReusableService : IService
{
/// <summary>
/// Returns the service to its original state (post-instantiation).
/// Allows a service instance to be reused without disposing of the instance.
/// </summary>
FluentResults.Result Reset();
}
/// <summary>
/// Base interface inherited by all services.
/// </summary>
public interface IService : IDisposable
{
bool IsDisposed { get; }
public void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException($"Tried to call method on disposed object '{this.GetType().Name}'!");
}
}
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using LightInject;
namespace Barotrauma.LuaCs.Services;
/// <summary>
/// Provides instancing and management of IServices.
/// </summary>
public interface IServicesProvider
{
#region Type_Registration
/// <summary>
/// Registers a type as a service for a given interface.
/// </summary>
/// <param name="lifetime"></param>
/// <param name="lifetimeInstance"></param>
/// <typeparam name="TSvcInterface"></typeparam>
/// <typeparam name="TService"></typeparam>
void RegisterServiceType<TSvcInterface, TService>(ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IReusableService where TService : class, IReusableService, TSvcInterface;
/// <summary>
/// Registers a type as a service for a given interface that can be requested by name.
/// </summary>
/// <param name="name"></param>
/// <param name="lifetime"></param>
/// <param name="lifetimeInstance"></param>
/// <typeparam name="TSvcInterface"></typeparam>
/// <typeparam name="TService"></typeparam>
void RegisterServiceType<TSvcInterface, TService>(string name, ServiceLifetime lifetime, ILifetime lifetimeInstance = null) where TSvcInterface : class, IReusableService where TService : class, IReusableService, TSvcInterface;
/// <summary>
/// Called whenever a new service type for a given interface is implemented.
/// Args[0]: Interface type
/// Args[1]: Implementing type
/// </summary>
event System.Action<Type, Type> OnServiceRegistered;
/// <summary>
/// Runs compilation of registered services.
/// </summary>
public void Compile();
#endregion
#region Services_Instancing_Injection
/// <summary>
/// Injects services into the properties of already instanced objects.
/// </summary>
/// <param name="inst"></param>
/// <typeparam name="T"></typeparam>
void InjectServices<T>(T inst) where T : class;
/// <summary>
/// Tries to get a service for the given interface, returns success/failure.
/// </summary>
/// <param name="service"></param>
/// <param name="lifetime"></param>
/// <typeparam name="TSvcInterface"></typeparam>
/// <returns></returns>
bool TryGetService<TSvcInterface>(out TSvcInterface service) where TSvcInterface : class, IReusableService;
/// <summary>
/// Tries to get a service for the given name and interface, returns success/failure.
/// </summary>
/// <param name="name"></param>
/// <param name="service"></param>
/// <param name="lifetime"></param>
/// <typeparam name="TSvcInterface"></typeparam>
/// <returns></returns>
bool TryGetService<TSvcInterface>(string name, out TSvcInterface service) where TSvcInterface : class, IReusableService;
/// <summary>
/// Called whenever a new service is created/instanced.
/// Args[0]: The interface type of the service.
/// Args[1]: The instance of the service.
/// </summary>
event System.Action<Type, IReusableService> OnServiceInstanced;
#endregion
#region ActiveServices
/// <summary>
/// Returns all services for the given interface.
/// </summary>
/// <typeparam name="TSvc"></typeparam>
/// <returns></returns>
ImmutableArray<TSvc> GetAllServices<TSvc>() where TSvc : class, IReusableService;
#endregion
// Notes: Left public due to the common use of Publicizers
#region Internal_Use
/// <summary>
/// Notes: Internal use only if hosted by LuaCsForBarotrauma. Disposes of all services and resets DI container. Warning: unable to dispose of services held by other objects.
/// </summary>
void DisposeAndReset();
#endregion
}
public enum ServiceLifetime
{
Transient, Singleton, PerThread, Invalid, Custom
}
@@ -0,0 +1,42 @@
using System.Collections.Immutable;
using System.Xml.Linq;
namespace Barotrauma.LuaCs.Services;
public interface IStorageService : IReusableService
{
#region LocalGameData
FluentResults.Result<XDocument> LoadLocalXml(ContentPackage package, string localFilePath);
FluentResults.Result<byte[]> LoadLocalBinary(ContentPackage package, string localFilePath);
FluentResults.Result<string> LoadLocalText(ContentPackage package, string localFilePath);
FluentResults.Result<bool> FileExistsInLocalData(ContentPackage package, string localFilePath);
#endregion
#region ContentPackageData
FluentResults.Result<XDocument> LoadPackageXml(ContentPackage package, string localFilePath, out XDocument document);
FluentResults.Result<byte[]> LoadPackageBinary(ContentPackage package, string localFilePath, out byte[] bytes);
FluentResults.Result<string> LoadPackageText(ContentPackage package, string localFilePath, out string text);
FluentResults.Result<ImmutableArray<XDocument>> LoadPackageXmlFiles(ContentPackage package, ImmutableArray<string> localFilePath);
FluentResults.Result<ImmutableArray<byte[]>> TryLoadPackageBinaryFiles(ContentPackage package, ImmutableArray<string> localFilePath);
FluentResults.Result<ImmutableArray<string>> TryLoadPackageTextFiles(ContentPackage package, ImmutableArray<string> localFilePath);
FluentResults.Result<ImmutableArray<string>> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively);
FluentResults.Result<bool> FileExistsInPackage(ContentPackage package, string localFilePath);
#endregion
#region AbsolutePaths
FluentResults.Result<XDocument> TryLoadXml(string filePatht);
FluentResults.Result TrySaveXml(string filePath, in XDocument document);
FluentResults.Result<byte[]> TryLoadBinary(string filePath);
FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes);
FluentResults.Result<string> TryLoadText(string filePath);
FluentResults.Result TrySaveText(string filePath, string text);
FluentResults.Result<bool> FileExists(string filePath);
#endregion
}