mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-29 20:03:41 +01:00
chore: convert Dalamud to file-scoped namespaces
This commit is contained in:
parent
b093323acc
commit
987ff8dc8f
368 changed files with 55081 additions and 55450 deletions
|
|
@ -23,412 +23,411 @@ using Dalamud.Plugin.Ipc.Exceptions;
|
|||
using Dalamud.Plugin.Ipc.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Plugin
|
||||
namespace Dalamud.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// This class acts as an interface to various objects needed to interact with Dalamud and the game.
|
||||
/// </summary>
|
||||
public sealed class DalamudPluginInterface : IDisposable
|
||||
{
|
||||
private readonly string pluginName;
|
||||
private readonly PluginConfigurations configs;
|
||||
|
||||
/// <summary>
|
||||
/// This class acts as an interface to various objects needed to interact with Dalamud and the game.
|
||||
/// Initializes a new instance of the <see cref="DalamudPluginInterface"/> class.
|
||||
/// Set up the interface and populate all fields needed.
|
||||
/// </summary>
|
||||
public sealed class DalamudPluginInterface : IDisposable
|
||||
/// <param name="pluginName">The internal name of the plugin.</param>
|
||||
/// <param name="assemblyLocation">Location of the assembly.</param>
|
||||
/// <param name="reason">The reason the plugin was loaded.</param>
|
||||
/// <param name="isDev">A value indicating whether this is a dev plugin.</param>
|
||||
internal DalamudPluginInterface(string pluginName, FileInfo assemblyLocation, PluginLoadReason reason, bool isDev)
|
||||
{
|
||||
private readonly string pluginName;
|
||||
private readonly PluginConfigurations configs;
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
var dataManager = Service<DataManager>.Get();
|
||||
var localization = Service<Localization>.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudPluginInterface"/> class.
|
||||
/// Set up the interface and populate all fields needed.
|
||||
/// </summary>
|
||||
/// <param name="pluginName">The internal name of the plugin.</param>
|
||||
/// <param name="assemblyLocation">Location of the assembly.</param>
|
||||
/// <param name="reason">The reason the plugin was loaded.</param>
|
||||
/// <param name="isDev">A value indicating whether this is a dev plugin.</param>
|
||||
internal DalamudPluginInterface(string pluginName, FileInfo assemblyLocation, PluginLoadReason reason, bool isDev)
|
||||
this.UiBuilder = new UiBuilder(pluginName);
|
||||
|
||||
this.pluginName = pluginName;
|
||||
this.AssemblyLocation = assemblyLocation;
|
||||
this.configs = Service<PluginManager>.Get().PluginConfigs;
|
||||
this.Reason = reason;
|
||||
this.IsDev = isDev;
|
||||
|
||||
this.LoadTime = DateTime.Now;
|
||||
this.LoadTimeUTC = DateTime.UtcNow;
|
||||
|
||||
this.GeneralChatType = configuration.GeneralChatType;
|
||||
this.Sanitizer = new Sanitizer(dataManager.Language);
|
||||
if (configuration.LanguageOverride != null)
|
||||
{
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
var dataManager = Service<DataManager>.Get();
|
||||
var localization = Service<Localization>.Get();
|
||||
|
||||
this.UiBuilder = new UiBuilder(pluginName);
|
||||
|
||||
this.pluginName = pluginName;
|
||||
this.AssemblyLocation = assemblyLocation;
|
||||
this.configs = Service<PluginManager>.Get().PluginConfigs;
|
||||
this.Reason = reason;
|
||||
this.IsDev = isDev;
|
||||
|
||||
this.LoadTime = DateTime.Now;
|
||||
this.LoadTimeUTC = DateTime.UtcNow;
|
||||
|
||||
this.GeneralChatType = configuration.GeneralChatType;
|
||||
this.Sanitizer = new Sanitizer(dataManager.Language);
|
||||
if (configuration.LanguageOverride != null)
|
||||
{
|
||||
this.UiLanguage = configuration.LanguageOverride;
|
||||
}
|
||||
this.UiLanguage = configuration.LanguageOverride;
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentUiLang = CultureInfo.CurrentUICulture;
|
||||
if (Localization.ApplicableLangCodes.Any(langCode => currentUiLang.TwoLetterISOLanguageName == langCode))
|
||||
this.UiLanguage = currentUiLang.TwoLetterISOLanguageName;
|
||||
else
|
||||
this.UiLanguage = "en";
|
||||
}
|
||||
|
||||
localization.LocalizationChanged += this.OnLocalizationChanged;
|
||||
configuration.DalamudConfigurationSaved += this.OnDalamudConfigurationSaved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for localization change with two-letter iso lang code.
|
||||
/// </summary>
|
||||
/// <param name="langCode">The new language code.</param>
|
||||
public delegate void LanguageChangedDelegate(string langCode);
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when loc is changed
|
||||
/// </summary>
|
||||
public event LanguageChangedDelegate LanguageChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason this plugin was loaded.
|
||||
/// </summary>
|
||||
public PluginLoadReason Reason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this is a dev plugin.
|
||||
/// </summary>
|
||||
public bool IsDev { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time that this plugin was loaded.
|
||||
/// </summary>
|
||||
public DateTime LoadTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UTC time that this plugin was loaded.
|
||||
/// </summary>
|
||||
public DateTime LoadTimeUTC { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timespan delta from when this plugin was loaded.
|
||||
/// </summary>
|
||||
public TimeSpan LoadTimeDelta => DateTime.Now - this.LoadTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory Dalamud assets are stored in.
|
||||
/// </summary>
|
||||
public DirectoryInfo DalamudAssetDirectory => Service<Dalamud>.Get().AssetDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of your plugin assembly.
|
||||
/// </summary>
|
||||
public FileInfo AssemblyLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory your plugin configurations are stored in.
|
||||
/// </summary>
|
||||
public DirectoryInfo ConfigDirectory => new(this.GetPluginConfigDirectory());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the config file of your plugin.
|
||||
/// </summary>
|
||||
public FileInfo ConfigFile => this.configs.GetConfigFile(this.pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UiBuilder"/> instance which allows you to draw UI into the game via ImGui draw calls.
|
||||
/// </summary>
|
||||
public UiBuilder UiBuilder { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Dalamud is running in Debug mode or the /xldev menu is open. This can occur on release builds.
|
||||
/// </summary>
|
||||
public bool IsDevMenuOpen => Service<DalamudInterface>.GetNullable() is { IsDevMenuOpen: true }; // Can be null during boot
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a debugger is attached.
|
||||
/// </summary>
|
||||
public bool IsDebugging => Debugger.IsAttached;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current UI language in two-letter iso format.
|
||||
/// </summary>
|
||||
public string UiLanguage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets serializer class with functions to remove special characters from strings.
|
||||
/// </summary>
|
||||
public ISanitizer Sanitizer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chat type used by default for plugin messages.
|
||||
/// </summary>
|
||||
public XivChatType GeneralChatType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of installed plugin names.
|
||||
/// </summary>
|
||||
public List<string> PluginNames => Service<PluginManager>.Get().InstalledPlugins.Select(p => p.Manifest.Name).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of installed plugin internal names.
|
||||
/// </summary>
|
||||
public List<string> PluginInternalNames => Service<PluginManager>.Get().InstalledPlugins.Select(p => p.Manifest.InternalName).ToList();
|
||||
|
||||
#region IPC
|
||||
|
||||
/// <inheritdoc cref="DataShare.GetOrCreateData{T}"/>
|
||||
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
||||
=> Service<DataShare>.Get().GetOrCreateData(tag, dataGenerator);
|
||||
|
||||
/// <inheritdoc cref="DataShare.RelinquishData"/>
|
||||
public void RelinquishData(string tag)
|
||||
=> Service<DataShare>.Get().RelinquishData(tag);
|
||||
|
||||
/// <inheritdoc cref="DataShare.TryGetData{T}"/>
|
||||
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
||||
=> Service<DataShare>.Get().TryGetData(tag, out data);
|
||||
|
||||
/// <inheritdoc cref="DataShare.GetData{T}"/>
|
||||
public T? GetData<T>(string tag) where T : class
|
||||
=> Service<DataShare>.Get().GetData<T>(tag);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IPC provider.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRet">The return type for funcs. Use object if this is unused.</typeparam>
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
/// <returns>An IPC provider.</returns>
|
||||
/// <exception cref="IpcTypeMismatchError">This is thrown when the requested types do not match the previously registered types are different.</exception>
|
||||
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
|
||||
=> new CallGatePubSub<TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IPC subscriber.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRet">The return type for funcs. Use object if this is unused.</typeparam>
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
/// <returns>An IPC subscriber.</returns>
|
||||
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
|
||||
=> new CallGatePubSub<TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Save a plugin configuration(inheriting IPluginConfiguration).
|
||||
/// </summary>
|
||||
/// <param name="currentConfig">The current configuration.</param>
|
||||
public void SavePluginConfig(IPluginConfiguration? currentConfig)
|
||||
{
|
||||
if (currentConfig == null)
|
||||
return;
|
||||
|
||||
this.configs.Save(currentConfig, this.pluginName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a previously saved plugin configuration or null if none was saved before.
|
||||
/// </summary>
|
||||
/// <returns>A previously saved config or null if none was saved before.</returns>
|
||||
public IPluginConfiguration? GetPluginConfig()
|
||||
{
|
||||
// This is done to support json deserialization of plugin configurations
|
||||
// even after running an in-game update of plugins, where the assembly version
|
||||
// changes.
|
||||
// Eventually it might make sense to have a separate method on this class
|
||||
// T GetPluginConfig<T>() where T : IPluginConfiguration
|
||||
// that can invoke LoadForType() directly instead of via reflection
|
||||
// This is here for now to support the current plugin API
|
||||
foreach (var type in Assembly.GetCallingAssembly().GetTypes())
|
||||
{
|
||||
if (type.IsAssignableTo(typeof(IPluginConfiguration)))
|
||||
{
|
||||
var currentUiLang = CultureInfo.CurrentUICulture;
|
||||
if (Localization.ApplicableLangCodes.Any(langCode => currentUiLang.TwoLetterISOLanguageName == langCode))
|
||||
this.UiLanguage = currentUiLang.TwoLetterISOLanguageName;
|
||||
else
|
||||
this.UiLanguage = "en";
|
||||
var mi = this.configs.GetType().GetMethod("LoadForType");
|
||||
var fn = mi.MakeGenericMethod(type);
|
||||
return (IPluginConfiguration)fn.Invoke(this.configs, new object[] { this.pluginName });
|
||||
}
|
||||
|
||||
localization.LocalizationChanged += this.OnLocalizationChanged;
|
||||
configuration.DalamudConfigurationSaved += this.OnDalamudConfigurationSaved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for localization change with two-letter iso lang code.
|
||||
/// </summary>
|
||||
/// <param name="langCode">The new language code.</param>
|
||||
public delegate void LanguageChangedDelegate(string langCode);
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when loc is changed
|
||||
/// </summary>
|
||||
public event LanguageChangedDelegate LanguageChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason this plugin was loaded.
|
||||
/// </summary>
|
||||
public PluginLoadReason Reason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this is a dev plugin.
|
||||
/// </summary>
|
||||
public bool IsDev { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time that this plugin was loaded.
|
||||
/// </summary>
|
||||
public DateTime LoadTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UTC time that this plugin was loaded.
|
||||
/// </summary>
|
||||
public DateTime LoadTimeUTC { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timespan delta from when this plugin was loaded.
|
||||
/// </summary>
|
||||
public TimeSpan LoadTimeDelta => DateTime.Now - this.LoadTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory Dalamud assets are stored in.
|
||||
/// </summary>
|
||||
public DirectoryInfo DalamudAssetDirectory => Service<Dalamud>.Get().AssetDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location of your plugin assembly.
|
||||
/// </summary>
|
||||
public FileInfo AssemblyLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory your plugin configurations are stored in.
|
||||
/// </summary>
|
||||
public DirectoryInfo ConfigDirectory => new(this.GetPluginConfigDirectory());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the config file of your plugin.
|
||||
/// </summary>
|
||||
public FileInfo ConfigFile => this.configs.GetConfigFile(this.pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UiBuilder"/> instance which allows you to draw UI into the game via ImGui draw calls.
|
||||
/// </summary>
|
||||
public UiBuilder UiBuilder { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Dalamud is running in Debug mode or the /xldev menu is open. This can occur on release builds.
|
||||
/// </summary>
|
||||
public bool IsDevMenuOpen => Service<DalamudInterface>.GetNullable() is { IsDevMenuOpen: true }; // Can be null during boot
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a debugger is attached.
|
||||
/// </summary>
|
||||
public bool IsDebugging => Debugger.IsAttached;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current UI language in two-letter iso format.
|
||||
/// </summary>
|
||||
public string UiLanguage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets serializer class with functions to remove special characters from strings.
|
||||
/// </summary>
|
||||
public ISanitizer Sanitizer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chat type used by default for plugin messages.
|
||||
/// </summary>
|
||||
public XivChatType GeneralChatType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of installed plugin names.
|
||||
/// </summary>
|
||||
public List<string> PluginNames => Service<PluginManager>.Get().InstalledPlugins.Select(p => p.Manifest.Name).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of installed plugin internal names.
|
||||
/// </summary>
|
||||
public List<string> PluginInternalNames => Service<PluginManager>.Get().InstalledPlugins.Select(p => p.Manifest.InternalName).ToList();
|
||||
|
||||
#region IPC
|
||||
|
||||
/// <inheritdoc cref="DataShare.GetOrCreateData{T}"/>
|
||||
public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class
|
||||
=> Service<DataShare>.Get().GetOrCreateData(tag, dataGenerator);
|
||||
|
||||
/// <inheritdoc cref="DataShare.RelinquishData"/>
|
||||
public void RelinquishData(string tag)
|
||||
=> Service<DataShare>.Get().RelinquishData(tag);
|
||||
|
||||
/// <inheritdoc cref="DataShare.TryGetData{T}"/>
|
||||
public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class
|
||||
=> Service<DataShare>.Get().TryGetData(tag, out data);
|
||||
|
||||
/// <inheritdoc cref="DataShare.GetData{T}"/>
|
||||
public T? GetData<T>(string tag) where T : class
|
||||
=> Service<DataShare>.Get().GetData<T>(tag);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IPC provider.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRet">The return type for funcs. Use object if this is unused.</typeparam>
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
/// <returns>An IPC provider.</returns>
|
||||
/// <exception cref="IpcTypeMismatchError">This is thrown when the requested types do not match the previously registered types are different.</exception>
|
||||
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
|
||||
=> new CallGatePubSub<TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateProvider{TRet}"/>
|
||||
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an IPC subscriber.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRet">The return type for funcs. Use object if this is unused.</typeparam>
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
/// <returns>An IPC subscriber.</returns>
|
||||
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
|
||||
=> new CallGatePubSub<TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
|
||||
|
||||
/// <inheritdoc cref="ICallGateSubscriber{TRet}"/>
|
||||
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
|
||||
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Save a plugin configuration(inheriting IPluginConfiguration).
|
||||
/// </summary>
|
||||
/// <param name="currentConfig">The current configuration.</param>
|
||||
public void SavePluginConfig(IPluginConfiguration? currentConfig)
|
||||
{
|
||||
if (currentConfig == null)
|
||||
return;
|
||||
|
||||
this.configs.Save(currentConfig, this.pluginName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a previously saved plugin configuration or null if none was saved before.
|
||||
/// </summary>
|
||||
/// <returns>A previously saved config or null if none was saved before.</returns>
|
||||
public IPluginConfiguration? GetPluginConfig()
|
||||
{
|
||||
// This is done to support json deserialization of plugin configurations
|
||||
// even after running an in-game update of plugins, where the assembly version
|
||||
// changes.
|
||||
// Eventually it might make sense to have a separate method on this class
|
||||
// T GetPluginConfig<T>() where T : IPluginConfiguration
|
||||
// that can invoke LoadForType() directly instead of via reflection
|
||||
// This is here for now to support the current plugin API
|
||||
foreach (var type in Assembly.GetCallingAssembly().GetTypes())
|
||||
{
|
||||
if (type.IsAssignableTo(typeof(IPluginConfiguration)))
|
||||
{
|
||||
var mi = this.configs.GetType().GetMethod("LoadForType");
|
||||
var fn = mi.MakeGenericMethod(type);
|
||||
return (IPluginConfiguration)fn.Invoke(this.configs, new object[] { this.pluginName });
|
||||
}
|
||||
}
|
||||
|
||||
// this shouldn't be a thing, I think, but just in case
|
||||
return this.configs.Load(this.pluginName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the config directory.
|
||||
/// </summary>
|
||||
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName.</returns>
|
||||
public string GetPluginConfigDirectory() => this.configs.GetDirectory(this.pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// Get the loc directory.
|
||||
/// </summary>
|
||||
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc.</returns>
|
||||
public string GetPluginLocDirectory() => this.configs.GetDirectory(Path.Combine(this.pluginName, "loc"));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Chat Links
|
||||
|
||||
/// <summary>
|
||||
/// Register a chat link handler.
|
||||
/// </summary>
|
||||
/// <param name="commandId">The ID of the command.</param>
|
||||
/// <param name="commandAction">The action to be executed.</param>
|
||||
/// <returns>Returns an SeString payload for the link.</returns>
|
||||
public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action<uint, SeString> commandAction)
|
||||
{
|
||||
return Service<ChatGui>.Get().AddChatLinkHandler(this.pluginName, commandId, commandAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a chat link handler.
|
||||
/// </summary>
|
||||
/// <param name="commandId">The ID of the command.</param>
|
||||
public void RemoveChatLinkHandler(uint commandId)
|
||||
{
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName, commandId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all chat link handlers registered by the plugin.
|
||||
/// </summary>
|
||||
public void RemoveChatLinkHandler()
|
||||
{
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Dependency Injection
|
||||
|
||||
/// <summary>
|
||||
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
||||
/// </summary>
|
||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||
/// <typeparam name="T">The type to create.</typeparam>
|
||||
/// <returns>The created and initialized type.</returns>
|
||||
public T? Create<T>(params object[] scopedObjects) where T : class
|
||||
{
|
||||
var svcContainer = Service<IoC.Internal.ServiceContainer>.Get();
|
||||
|
||||
var realScopedObjects = new object[scopedObjects.Length + 1];
|
||||
realScopedObjects[0] = this;
|
||||
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
||||
|
||||
return (T)svcContainer.CreateAsync(typeof(T), realScopedObjects).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inject services into properties on the provided object instance.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance to inject services into.</param>
|
||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||
/// <returns>Whether or not the injection succeeded.</returns>
|
||||
public bool Inject(object instance, params object[] scopedObjects)
|
||||
{
|
||||
var svcContainer = Service<IoC.Internal.ServiceContainer>.Get();
|
||||
|
||||
var realScopedObjects = new object[scopedObjects.Length + 1];
|
||||
realScopedObjects[0] = this;
|
||||
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
||||
|
||||
return svcContainer.InjectProperties(instance, realScopedObjects).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Unregister your plugin and dispose all references.
|
||||
/// </summary>
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
this.UiBuilder.ExplicitDispose();
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName);
|
||||
Service<Localization>.Get().LocalizationChanged -= this.OnLocalizationChanged;
|
||||
Service<DalamudConfiguration>.Get().DalamudConfigurationSaved -= this.OnDalamudConfigurationSaved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obsolete implicit dispose implementation. Should not be used.
|
||||
/// </summary>
|
||||
[Obsolete("Do not dispose \"DalamudPluginInterface\".", true)]
|
||||
public void Dispose()
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
private void OnLocalizationChanged(string langCode)
|
||||
{
|
||||
this.UiLanguage = langCode;
|
||||
this.LanguageChanged?.Invoke(langCode);
|
||||
}
|
||||
|
||||
private void OnDalamudConfigurationSaved(DalamudConfiguration dalamudConfiguration)
|
||||
{
|
||||
this.GeneralChatType = dalamudConfiguration.GeneralChatType;
|
||||
}
|
||||
// this shouldn't be a thing, I think, but just in case
|
||||
return this.configs.Load(this.pluginName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the config directory.
|
||||
/// </summary>
|
||||
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName.</returns>
|
||||
public string GetPluginConfigDirectory() => this.configs.GetDirectory(this.pluginName);
|
||||
|
||||
/// <summary>
|
||||
/// Get the loc directory.
|
||||
/// </summary>
|
||||
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc.</returns>
|
||||
public string GetPluginLocDirectory() => this.configs.GetDirectory(Path.Combine(this.pluginName, "loc"));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Chat Links
|
||||
|
||||
/// <summary>
|
||||
/// Register a chat link handler.
|
||||
/// </summary>
|
||||
/// <param name="commandId">The ID of the command.</param>
|
||||
/// <param name="commandAction">The action to be executed.</param>
|
||||
/// <returns>Returns an SeString payload for the link.</returns>
|
||||
public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action<uint, SeString> commandAction)
|
||||
{
|
||||
return Service<ChatGui>.Get().AddChatLinkHandler(this.pluginName, commandId, commandAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a chat link handler.
|
||||
/// </summary>
|
||||
/// <param name="commandId">The ID of the command.</param>
|
||||
public void RemoveChatLinkHandler(uint commandId)
|
||||
{
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName, commandId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all chat link handlers registered by the plugin.
|
||||
/// </summary>
|
||||
public void RemoveChatLinkHandler()
|
||||
{
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Dependency Injection
|
||||
|
||||
/// <summary>
|
||||
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
||||
/// </summary>
|
||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||
/// <typeparam name="T">The type to create.</typeparam>
|
||||
/// <returns>The created and initialized type.</returns>
|
||||
public T? Create<T>(params object[] scopedObjects) where T : class
|
||||
{
|
||||
var svcContainer = Service<IoC.Internal.ServiceContainer>.Get();
|
||||
|
||||
var realScopedObjects = new object[scopedObjects.Length + 1];
|
||||
realScopedObjects[0] = this;
|
||||
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
||||
|
||||
return (T)svcContainer.CreateAsync(typeof(T), realScopedObjects).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inject services into properties on the provided object instance.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance to inject services into.</param>
|
||||
/// <param name="scopedObjects">Objects to inject additionally.</param>
|
||||
/// <returns>Whether or not the injection succeeded.</returns>
|
||||
public bool Inject(object instance, params object[] scopedObjects)
|
||||
{
|
||||
var svcContainer = Service<IoC.Internal.ServiceContainer>.Get();
|
||||
|
||||
var realScopedObjects = new object[scopedObjects.Length + 1];
|
||||
realScopedObjects[0] = this;
|
||||
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
||||
|
||||
return svcContainer.InjectProperties(instance, realScopedObjects).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Unregister your plugin and dispose all references.
|
||||
/// </summary>
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
this.UiBuilder.ExplicitDispose();
|
||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.pluginName);
|
||||
Service<Localization>.Get().LocalizationChanged -= this.OnLocalizationChanged;
|
||||
Service<DalamudConfiguration>.Get().DalamudConfigurationSaved -= this.OnDalamudConfigurationSaved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obsolete implicit dispose implementation. Should not be used.
|
||||
/// </summary>
|
||||
[Obsolete("Do not dispose \"DalamudPluginInterface\".", true)]
|
||||
public void Dispose()
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
private void OnLocalizationChanged(string langCode)
|
||||
{
|
||||
this.UiLanguage = langCode;
|
||||
this.LanguageChanged?.Invoke(langCode);
|
||||
}
|
||||
|
||||
private void OnDalamudConfigurationSaved(DalamudConfiguration dalamudConfiguration)
|
||||
{
|
||||
this.GeneralChatType = dalamudConfiguration.GeneralChatType;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin
|
||||
namespace Dalamud.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// This interface represents a basic Dalamud plugin. All plugins have to implement this interface.
|
||||
/// </summary>
|
||||
public interface IDalamudPlugin : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface represents a basic Dalamud plugin. All plugins have to implement this interface.
|
||||
/// Gets the name of the plugin.
|
||||
/// </summary>
|
||||
public interface IDalamudPlugin : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
string Name { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
namespace Dalamud.Plugin.Internal.Exceptions
|
||||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This represents a banned plugin that attempted an operation.
|
||||
/// </summary>
|
||||
internal class BannedPluginException : PluginException
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents a banned plugin that attempted an operation.
|
||||
/// Initializes a new instance of the <see cref="BannedPluginException"/> class.
|
||||
/// </summary>
|
||||
internal class BannedPluginException : PluginException
|
||||
/// <param name="message">The message describing the invalid operation.</param>
|
||||
public BannedPluginException(string message)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BannedPluginException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message describing the invalid operation.</param>
|
||||
public BannedPluginException(string message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message describing the invalid operation.
|
||||
/// </summary>
|
||||
public override string Message { get; }
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message describing the invalid operation.
|
||||
/// </summary>
|
||||
public override string Message { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
namespace Dalamud.Plugin.Internal.Exceptions
|
||||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception that is thrown when a plugin is instructed to load while another plugin with the same
|
||||
/// assembly name is already present and loaded.
|
||||
/// </summary>
|
||||
internal class DuplicatePluginException : PluginException
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception that is thrown when a plugin is instructed to load while another plugin with the same
|
||||
/// assembly name is already present and loaded.
|
||||
/// Initializes a new instance of the <see cref="DuplicatePluginException"/> class.
|
||||
/// </summary>
|
||||
internal class DuplicatePluginException : PluginException
|
||||
/// <param name="assemblyName">Name of the conflicting assembly.</param>
|
||||
public DuplicatePluginException(string assemblyName)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuplicatePluginException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">Name of the conflicting assembly.</param>
|
||||
public DuplicatePluginException(string assemblyName)
|
||||
{
|
||||
this.AssemblyName = assemblyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the conflicting assembly.
|
||||
/// </summary>
|
||||
public string AssemblyName { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Message => $"A plugin with the same assembly name of {this.AssemblyName} is already loaded";
|
||||
this.AssemblyName = assemblyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the conflicting assembly.
|
||||
/// </summary>
|
||||
public string AssemblyName { get; init; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Message => $"A plugin with the same assembly name of {this.AssemblyName} is already loaded";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Exceptions
|
||||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception represents a file that does not implement IDalamudPlugin.
|
||||
/// </summary>
|
||||
internal class InvalidPluginException : PluginException
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception represents a file that does not implement IDalamudPlugin.
|
||||
/// Initializes a new instance of the <see cref="InvalidPluginException"/> class.
|
||||
/// </summary>
|
||||
internal class InvalidPluginException : PluginException
|
||||
/// <param name="dllFile">The invalid file.</param>
|
||||
public InvalidPluginException(FileInfo dllFile)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidPluginException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dllFile">The invalid file.</param>
|
||||
public InvalidPluginException(FileInfo dllFile)
|
||||
{
|
||||
this.DllFile = dllFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the invalid file.
|
||||
/// </summary>
|
||||
public FileInfo DllFile { get; init; }
|
||||
this.DllFile = dllFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the invalid file.
|
||||
/// </summary>
|
||||
public FileInfo DllFile { get; init; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
namespace Dalamud.Plugin.Internal.Exceptions
|
||||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This represents an invalid plugin operation.
|
||||
/// </summary>
|
||||
internal class InvalidPluginOperationException : PluginException
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents an invalid plugin operation.
|
||||
/// Initializes a new instance of the <see cref="InvalidPluginOperationException"/> class.
|
||||
/// </summary>
|
||||
internal class InvalidPluginOperationException : PluginException
|
||||
/// <param name="message">The message describing the invalid operation.</param>
|
||||
public InvalidPluginOperationException(string message)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidPluginOperationException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message describing the invalid operation.</param>
|
||||
public InvalidPluginOperationException(string message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message describing the invalid operation.
|
||||
/// </summary>
|
||||
public override string Message { get; }
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message describing the invalid operation.
|
||||
/// </summary>
|
||||
public override string Message { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Exceptions
|
||||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This represents the base Dalamud plugin exception.
|
||||
/// </summary>
|
||||
internal abstract class PluginException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents the base Dalamud plugin exception.
|
||||
/// </summary>
|
||||
internal abstract class PluginException : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,308 +9,307 @@ using System.Runtime.Loader;
|
|||
|
||||
using Dalamud.Plugin.Internal.Loader.LibraryModel;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader
|
||||
namespace Dalamud.Plugin.Internal.Loader;
|
||||
|
||||
/// <summary>
|
||||
/// A builder for creating an instance of <see cref="AssemblyLoadContext" />.
|
||||
/// </summary>
|
||||
internal class AssemblyLoadContextBuilder
|
||||
{
|
||||
private readonly List<string> additionalProbingPaths = new();
|
||||
private readonly List<string> resourceProbingPaths = new();
|
||||
private readonly List<string> resourceProbingSubpaths = new();
|
||||
private readonly Dictionary<string, ManagedLibrary> managedLibraries = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, NativeLibrary> nativeLibraries = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> privateAssemblies = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> defaultAssemblies = new(StringComparer.Ordinal);
|
||||
private AssemblyLoadContext defaultLoadContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? AssemblyLoadContext.Default;
|
||||
private string? mainAssemblyPath;
|
||||
private bool preferDefaultLoadContext;
|
||||
|
||||
private bool isCollectible;
|
||||
private bool loadInMemory;
|
||||
private bool shadowCopyNativeLibraries;
|
||||
|
||||
/// <summary>
|
||||
/// A builder for creating an instance of <see cref="AssemblyLoadContext" />.
|
||||
/// Creates an assembly load context using settings specified on the builder.
|
||||
/// </summary>
|
||||
internal class AssemblyLoadContextBuilder
|
||||
/// <returns>A new ManagedLoadContext.</returns>
|
||||
public AssemblyLoadContext Build()
|
||||
{
|
||||
private readonly List<string> additionalProbingPaths = new();
|
||||
private readonly List<string> resourceProbingPaths = new();
|
||||
private readonly List<string> resourceProbingSubpaths = new();
|
||||
private readonly Dictionary<string, ManagedLibrary> managedLibraries = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, NativeLibrary> nativeLibraries = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> privateAssemblies = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> defaultAssemblies = new(StringComparer.Ordinal);
|
||||
private AssemblyLoadContext defaultLoadContext = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? AssemblyLoadContext.Default;
|
||||
private string? mainAssemblyPath;
|
||||
private bool preferDefaultLoadContext;
|
||||
|
||||
private bool isCollectible;
|
||||
private bool loadInMemory;
|
||||
private bool shadowCopyNativeLibraries;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an assembly load context using settings specified on the builder.
|
||||
/// </summary>
|
||||
/// <returns>A new ManagedLoadContext.</returns>
|
||||
public AssemblyLoadContext Build()
|
||||
var resourceProbingPaths = new List<string>(this.resourceProbingPaths);
|
||||
foreach (var additionalPath in this.additionalProbingPaths)
|
||||
{
|
||||
var resourceProbingPaths = new List<string>(this.resourceProbingPaths);
|
||||
foreach (var additionalPath in this.additionalProbingPaths)
|
||||
foreach (var subPath in this.resourceProbingSubpaths)
|
||||
{
|
||||
foreach (var subPath in this.resourceProbingSubpaths)
|
||||
{
|
||||
resourceProbingPaths.Add(Path.Combine(additionalPath, subPath));
|
||||
}
|
||||
resourceProbingPaths.Add(Path.Combine(additionalPath, subPath));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mainAssemblyPath == null)
|
||||
throw new InvalidOperationException($"Missing required property. You must call '{nameof(this.SetMainAssemblyPath)}' to configure the default assembly.");
|
||||
|
||||
return new ManagedLoadContext(
|
||||
this.mainAssemblyPath,
|
||||
this.managedLibraries,
|
||||
this.nativeLibraries,
|
||||
this.privateAssemblies,
|
||||
this.defaultAssemblies,
|
||||
this.additionalProbingPaths,
|
||||
resourceProbingPaths,
|
||||
this.defaultLoadContext,
|
||||
this.preferDefaultLoadContext,
|
||||
this.isCollectible,
|
||||
this.loadInMemory,
|
||||
this.shadowCopyNativeLibraries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the file path to the main assembly for the context. This is used as the starting point for loading
|
||||
/// other assemblies. The directory that contains it is also known as the 'app local' directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must not be null or empty. Must be an absolute path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder SetMainAssemblyPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Argument must not be null or empty.", nameof(path));
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
this.mainAssemblyPath = path;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the default <see cref="AssemblyLoadContext"/> used by the <see cref="AssemblyLoadContextBuilder"/>.
|
||||
/// Use this feature if the <see cref="AssemblyLoadContext"/> of the <see cref="Assembly"/> is not the Runtime's default load context.
|
||||
/// i.e. (AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly) != <see cref="AssemblyLoadContext.Default"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The context to set.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder SetDefaultContext(AssemblyLoadContext context)
|
||||
{
|
||||
this.defaultLoadContext = context ?? throw new ArgumentException($"Bad Argument: AssemblyLoadContext in {nameof(AssemblyLoadContextBuilder)}.{nameof(this.SetDefaultContext)} is null.");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to prefer a private version of this assembly, even if that version is
|
||||
/// different from the version used by the host application.
|
||||
/// Use this when you do not need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// <para>
|
||||
/// This may mean the types loaded from
|
||||
/// this assembly will not match the types from an assembly with the same name, but different version,
|
||||
/// in the host application.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For example, if the host application has a type named <c>Foo</c> from assembly <c>Banana, Version=1.0.0.0</c>
|
||||
/// and the load context prefers a private version of <c>Banan, Version=2.0.0.0</c>, when comparing two objects,
|
||||
/// one created by the host (Foo1) and one created from within the load context (Foo2), they will not have the same
|
||||
/// type. <c>Foo1.GetType() != Foo2.GetType()</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferLoadContextAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
if (assemblyName.Name != null)
|
||||
this.privateAssemblies.Add(assemblyName.Name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to first attempt to load assemblies by this name from the default app context, even
|
||||
/// if other assemblies in this load context express a dependency on a higher or lower version.
|
||||
/// Use this when you need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContextAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
var names = new Queue<AssemblyName>(new[] { assemblyName });
|
||||
|
||||
while (names.TryDequeue(out var name))
|
||||
{
|
||||
if (name.Name == null || this.defaultAssemblies.Contains(name.Name))
|
||||
{
|
||||
// base cases
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.mainAssemblyPath == null)
|
||||
throw new InvalidOperationException($"Missing required property. You must call '{nameof(this.SetMainAssemblyPath)}' to configure the default assembly.");
|
||||
this.defaultAssemblies.Add(name.Name);
|
||||
|
||||
return new ManagedLoadContext(
|
||||
this.mainAssemblyPath,
|
||||
this.managedLibraries,
|
||||
this.nativeLibraries,
|
||||
this.privateAssemblies,
|
||||
this.defaultAssemblies,
|
||||
this.additionalProbingPaths,
|
||||
resourceProbingPaths,
|
||||
this.defaultLoadContext,
|
||||
this.preferDefaultLoadContext,
|
||||
this.isCollectible,
|
||||
this.loadInMemory,
|
||||
this.shadowCopyNativeLibraries);
|
||||
}
|
||||
// Load and find all dependencies of default assemblies.
|
||||
// This sacrifices some performance for determinism in how transitive
|
||||
// dependencies will be shared between host and plugin.
|
||||
var assembly = this.defaultLoadContext.LoadFromAssemblyName(name);
|
||||
|
||||
/// <summary>
|
||||
/// Set the file path to the main assembly for the context. This is used as the starting point for loading
|
||||
/// other assemblies. The directory that contains it is also known as the 'app local' directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must not be null or empty. Must be an absolute path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder SetMainAssemblyPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Argument must not be null or empty.", nameof(path));
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
this.mainAssemblyPath = path;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the default <see cref="AssemblyLoadContext"/> used by the <see cref="AssemblyLoadContextBuilder"/>.
|
||||
/// Use this feature if the <see cref="AssemblyLoadContext"/> of the <see cref="Assembly"/> is not the Runtime's default load context.
|
||||
/// i.e. (AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly) != <see cref="AssemblyLoadContext.Default"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The context to set.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder SetDefaultContext(AssemblyLoadContext context)
|
||||
{
|
||||
this.defaultLoadContext = context ?? throw new ArgumentException($"Bad Argument: AssemblyLoadContext in {nameof(AssemblyLoadContextBuilder)}.{nameof(this.SetDefaultContext)} is null.");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to prefer a private version of this assembly, even if that version is
|
||||
/// different from the version used by the host application.
|
||||
/// Use this when you do not need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// <para>
|
||||
/// This may mean the types loaded from
|
||||
/// this assembly will not match the types from an assembly with the same name, but different version,
|
||||
/// in the host application.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For example, if the host application has a type named <c>Foo</c> from assembly <c>Banana, Version=1.0.0.0</c>
|
||||
/// and the load context prefers a private version of <c>Banan, Version=2.0.0.0</c>, when comparing two objects,
|
||||
/// one created by the host (Foo1) and one created from within the load context (Foo2), they will not have the same
|
||||
/// type. <c>Foo1.GetType() != Foo2.GetType()</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferLoadContextAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
if (assemblyName.Name != null)
|
||||
this.privateAssemblies.Add(assemblyName.Name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to first attempt to load assemblies by this name from the default app context, even
|
||||
/// if other assemblies in this load context express a dependency on a higher or lower version.
|
||||
/// Use this when you need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContextAssembly(AssemblyName assemblyName)
|
||||
{
|
||||
var names = new Queue<AssemblyName>(new[] { assemblyName });
|
||||
|
||||
while (names.TryDequeue(out var name))
|
||||
foreach (var reference in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (name.Name == null || this.defaultAssemblies.Contains(name.Name))
|
||||
{
|
||||
// base cases
|
||||
continue;
|
||||
}
|
||||
|
||||
this.defaultAssemblies.Add(name.Name);
|
||||
|
||||
// Load and find all dependencies of default assemblies.
|
||||
// This sacrifices some performance for determinism in how transitive
|
||||
// dependencies will be shared between host and plugin.
|
||||
var assembly = this.defaultLoadContext.LoadFromAssemblyName(name);
|
||||
|
||||
foreach (var reference in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
names.Enqueue(reference);
|
||||
}
|
||||
names.Enqueue(reference);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to first search for binaries from the default app context, even
|
||||
/// if other assemblies in this load context express a dependency on a higher or lower version.
|
||||
/// Use this when you need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// <para>
|
||||
/// This may mean the types loaded from within the context are force-downgraded to the version provided
|
||||
/// by the host. <seealso cref="PreferLoadContextAssembly" /> can be used to selectively identify binaries
|
||||
/// which should not be loaded from the default load context.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="preferDefaultLoadContext">When true, first attemp to load binaries from the default load context.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContext(bool preferDefaultLoadContext)
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the load context to first search for binaries from the default app context, even
|
||||
/// if other assemblies in this load context express a dependency on a higher or lower version.
|
||||
/// Use this when you need to exchange types created from within the load context with other contexts
|
||||
/// or the default app context.
|
||||
/// <para>
|
||||
/// This may mean the types loaded from within the context are force-downgraded to the version provided
|
||||
/// by the host. <seealso cref="PreferLoadContextAssembly" /> can be used to selectively identify binaries
|
||||
/// which should not be loaded from the default load context.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="preferDefaultLoadContext">When true, first attemp to load binaries from the default load context.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContext(bool preferDefaultLoadContext)
|
||||
{
|
||||
this.preferDefaultLoadContext = preferDefaultLoadContext;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a managed library to the load context.
|
||||
/// </summary>
|
||||
/// <param name="library">The managed library.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddManagedLibrary(ManagedLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AdditionalProbingPath);
|
||||
|
||||
if (library.Name.Name != null)
|
||||
{
|
||||
this.preferDefaultLoadContext = preferDefaultLoadContext;
|
||||
|
||||
return this;
|
||||
this.managedLibraries.Add(library.Name.Name, library);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a managed library to the load context.
|
||||
/// </summary>
|
||||
/// <param name="library">The managed library.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddManagedLibrary(ManagedLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AdditionalProbingPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (library.Name.Name != null)
|
||||
{
|
||||
this.managedLibraries.Add(library.Name.Name, library);
|
||||
}
|
||||
/// <summary>
|
||||
/// Add a native library to the load context.
|
||||
/// </summary>
|
||||
/// <param name="library">A native library.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddNativeLibrary(NativeLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AppLocalPath);
|
||||
ValidateRelativePath(library.AdditionalProbingPath);
|
||||
|
||||
return this;
|
||||
}
|
||||
this.nativeLibraries.Add(library.Name, library);
|
||||
|
||||
/// <summary>
|
||||
/// Add a native library to the load context.
|
||||
/// </summary>
|
||||
/// <param name="library">A native library.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddNativeLibrary(NativeLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AppLocalPath);
|
||||
ValidateRelativePath(library.AdditionalProbingPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.nativeLibraries.Add(library.Name, library);
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be used to search for native and managed libraries.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must be a full file path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddProbingPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
|
||||
return this;
|
||||
}
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be used to search for native and managed libraries.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must be a full file path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddProbingPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
this.additionalProbingPaths.Add(path);
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
return this;
|
||||
}
|
||||
|
||||
this.additionalProbingPaths.Add(path);
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be use to search for resource assemblies (aka satellite assemblies).
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must be a full file path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddResourceProbingPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
|
||||
return this;
|
||||
}
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be use to search for resource assemblies (aka satellite assemblies).
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must be a full file path.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder AddResourceProbingPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
this.resourceProbingPaths.Add(path);
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
return this;
|
||||
}
|
||||
|
||||
this.resourceProbingPaths.Add(path);
|
||||
/// <summary>
|
||||
/// Enable unloading the assembly load context.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder EnableUnloading()
|
||||
{
|
||||
this.isCollectible = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable unloading the assembly load context.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder EnableUnloading()
|
||||
{
|
||||
this.isCollectible = true;
|
||||
/// <summary>
|
||||
/// Read .dll files into memory to avoid locking the files.
|
||||
/// This is not as efficient, so is not enabled by default, but is required for scenarios
|
||||
/// like hot reloading.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreloadAssembliesIntoMemory()
|
||||
{
|
||||
this.loadInMemory = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read .dll files into memory to avoid locking the files.
|
||||
/// This is not as efficient, so is not enabled by default, but is required for scenarios
|
||||
/// like hot reloading.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreloadAssembliesIntoMemory()
|
||||
{
|
||||
this.loadInMemory = true;
|
||||
/// <summary>
|
||||
/// Shadow copy native libraries (unmanaged DLLs) to avoid locking of these files.
|
||||
/// This is not as efficient, so is not enabled by default, but is required for scenarios
|
||||
/// like hot reloading of plugins dependent on native libraries.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder ShadowCopyNativeLibraries()
|
||||
{
|
||||
this.shadowCopyNativeLibraries = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shadow copy native libraries (unmanaged DLLs) to avoid locking of these files.
|
||||
/// This is not as efficient, so is not enabled by default, but is required for scenarios
|
||||
/// like hot reloading of plugins dependent on native libraries.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder ShadowCopyNativeLibraries()
|
||||
{
|
||||
this.shadowCopyNativeLibraries = true;
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be use to search for resource assemblies (aka satellite assemblies)
|
||||
/// relative to any paths specified as <see cref="AddProbingPath"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must not be a full file path since it will be appended to additional probing path roots.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
internal AssemblyLoadContextBuilder AddResourceProbingSubpath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
|
||||
return this;
|
||||
}
|
||||
if (Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be not a full path.", nameof(path));
|
||||
|
||||
/// <summary>
|
||||
/// Add a <paramref name="path"/> that should be use to search for resource assemblies (aka satellite assemblies)
|
||||
/// relative to any paths specified as <see cref="AddProbingPath"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path. Must not be a full file path since it will be appended to additional probing path roots.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
internal AssemblyLoadContextBuilder AddResourceProbingSubpath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(path));
|
||||
this.resourceProbingSubpaths.Add(path);
|
||||
|
||||
if (Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be not a full path.", nameof(path));
|
||||
return this;
|
||||
}
|
||||
|
||||
this.resourceProbingSubpaths.Add(path);
|
||||
private static void ValidateRelativePath(string probingPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(probingPath))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(probingPath));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void ValidateRelativePath(string probingPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(probingPath))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(probingPath));
|
||||
|
||||
if (Path.IsPathRooted(probingPath))
|
||||
throw new ArgumentException("Argument must be a relative path.", nameof(probingPath));
|
||||
}
|
||||
if (Path.IsPathRooted(probingPath))
|
||||
throw new ArgumentException("Argument must be a relative path.", nameof(probingPath));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,67 +6,66 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader.LibraryModel
|
||||
namespace Dalamud.Plugin.Internal.Loader.LibraryModel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a managed, .NET assembly.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name} = {AdditionalProbingPath}")]
|
||||
internal class ManagedLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a managed, .NET assembly.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name} = {AdditionalProbingPath}")]
|
||||
internal class ManagedLibrary
|
||||
private ManagedLibrary(AssemblyName name, string additionalProbingPath, string appLocalPath)
|
||||
{
|
||||
private ManagedLibrary(AssemblyName name, string additionalProbingPath, string appLocalPath)
|
||||
{
|
||||
this.Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.AdditionalProbingPath = additionalProbingPath ?? throw new ArgumentNullException(nameof(additionalProbingPath));
|
||||
this.AppLocalPath = appLocalPath ?? throw new ArgumentNullException(nameof(appLocalPath));
|
||||
}
|
||||
this.Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.AdditionalProbingPath = additionalProbingPath ?? throw new ArgumentNullException(nameof(additionalProbingPath));
|
||||
this.AppLocalPath = appLocalPath ?? throw new ArgumentNullException(nameof(appLocalPath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the managed library.
|
||||
/// </summary>
|
||||
public AssemblyName Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of the managed library.
|
||||
/// </summary>
|
||||
public AssemblyName Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file within an additional probing path root. This is typically a combination
|
||||
/// of the NuGet package ID (lowercased), version, and path within the package.
|
||||
/// <para>
|
||||
/// For example, <c>microsoft.data.sqlite/1.0.0/lib/netstandard1.3/Microsoft.Data.Sqlite.dll</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AdditionalProbingPath { get; }
|
||||
/// <summary>
|
||||
/// Gets the path to file within an additional probing path root. This is typically a combination
|
||||
/// of the NuGet package ID (lowercased), version, and path within the package.
|
||||
/// <para>
|
||||
/// For example, <c>microsoft.data.sqlite/1.0.0/lib/netstandard1.3/Microsoft.Data.Sqlite.dll</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AdditionalProbingPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file within a deployed, framework-dependent application.
|
||||
/// <para>
|
||||
/// For most managed libraries, this will be the file name.
|
||||
/// For example, <c>MyPlugin1.dll</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For runtime-specific managed implementations, this may include a sub folder path.
|
||||
/// For example, <c>runtimes/win/lib/netcoreapp2.0/System.Diagnostics.EventLog.dll</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AppLocalPath { get; }
|
||||
/// <summary>
|
||||
/// Gets the path to file within a deployed, framework-dependent application.
|
||||
/// <para>
|
||||
/// For most managed libraries, this will be the file name.
|
||||
/// For example, <c>MyPlugin1.dll</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For runtime-specific managed implementations, this may include a sub folder path.
|
||||
/// For example, <c>runtimes/win/lib/netcoreapp2.0/System.Diagnostics.EventLog.dll</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AppLocalPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="ManagedLibrary" /> from a NuGet package.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The name of the package.</param>
|
||||
/// <param name="packageVersion">The version of the package.</param>
|
||||
/// <param name="assetPath">The path within the NuGet package.</param>
|
||||
/// <returns>A managed library.</returns>
|
||||
public static ManagedLibrary CreateFromPackage(string packageId, string packageVersion, string assetPath)
|
||||
{
|
||||
// When the asset comes from "lib/$tfm/", Microsoft.NET.Sdk will flatten this during publish based on the most compatible TFM.
|
||||
// The SDK will not flatten managed libraries found under runtimes/
|
||||
var appLocalPath = assetPath.StartsWith("lib/")
|
||||
? Path.GetFileName(assetPath)
|
||||
: assetPath;
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="ManagedLibrary" /> from a NuGet package.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The name of the package.</param>
|
||||
/// <param name="packageVersion">The version of the package.</param>
|
||||
/// <param name="assetPath">The path within the NuGet package.</param>
|
||||
/// <returns>A managed library.</returns>
|
||||
public static ManagedLibrary CreateFromPackage(string packageId, string packageVersion, string assetPath)
|
||||
{
|
||||
// When the asset comes from "lib/$tfm/", Microsoft.NET.Sdk will flatten this during publish based on the most compatible TFM.
|
||||
// The SDK will not flatten managed libraries found under runtimes/
|
||||
var appLocalPath = assetPath.StartsWith("lib/")
|
||||
? Path.GetFileName(assetPath)
|
||||
: assetPath;
|
||||
|
||||
return new ManagedLibrary(
|
||||
new AssemblyName(Path.GetFileNameWithoutExtension(assetPath)),
|
||||
Path.Combine(packageId.ToLowerInvariant(), packageVersion, assetPath),
|
||||
appLocalPath);
|
||||
}
|
||||
return new ManagedLibrary(
|
||||
new AssemblyName(Path.GetFileNameWithoutExtension(assetPath)),
|
||||
Path.Combine(packageId.ToLowerInvariant(), packageVersion, assetPath),
|
||||
appLocalPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,64 +5,63 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader.LibraryModel
|
||||
namespace Dalamud.Plugin.Internal.Loader.LibraryModel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an unmanaged library, such as `libsqlite3`, which may need to be loaded
|
||||
/// for P/Invoke to work.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name} = {AdditionalProbingPath}")]
|
||||
internal class NativeLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an unmanaged library, such as `libsqlite3`, which may need to be loaded
|
||||
/// for P/Invoke to work.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name} = {AdditionalProbingPath}")]
|
||||
internal class NativeLibrary
|
||||
private NativeLibrary(string name, string appLocalPath, string additionalProbingPath)
|
||||
{
|
||||
private NativeLibrary(string name, string appLocalPath, string additionalProbingPath)
|
||||
{
|
||||
this.Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.AppLocalPath = appLocalPath ?? throw new ArgumentNullException(nameof(appLocalPath));
|
||||
this.AdditionalProbingPath = additionalProbingPath ?? throw new ArgumentNullException(nameof(additionalProbingPath));
|
||||
}
|
||||
this.Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
this.AppLocalPath = appLocalPath ?? throw new ArgumentNullException(nameof(appLocalPath));
|
||||
this.AdditionalProbingPath = additionalProbingPath ?? throw new ArgumentNullException(nameof(additionalProbingPath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the native library. This should match the name of the P/Invoke call.
|
||||
/// <para>
|
||||
/// For example, if specifying `[DllImport("sqlite3")]`, <see cref="Name" /> should be <c>sqlite3</c>.
|
||||
/// This may not match the exact file name as loading will attempt variations on the name according
|
||||
/// to OS convention. On Windows, P/Invoke will attempt to load `sqlite3.dll`. On macOS, it will
|
||||
/// attempt to find `sqlite3.dylib` and `libsqlite3.dylib`. On Linux, it will attempt to find
|
||||
/// `sqlite3.so` and `libsqlite3.so`.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of the native library. This should match the name of the P/Invoke call.
|
||||
/// <para>
|
||||
/// For example, if specifying `[DllImport("sqlite3")]`, <see cref="Name" /> should be <c>sqlite3</c>.
|
||||
/// This may not match the exact file name as loading will attempt variations on the name according
|
||||
/// to OS convention. On Windows, P/Invoke will attempt to load `sqlite3.dll`. On macOS, it will
|
||||
/// attempt to find `sqlite3.dylib` and `libsqlite3.dylib`. On Linux, it will attempt to find
|
||||
/// `sqlite3.so` and `libsqlite3.so`.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file within a deployed, framework-dependent application.
|
||||
/// <para>
|
||||
/// For example, <c>runtimes/linux-x64/native/libsqlite.so</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AppLocalPath { get; }
|
||||
/// <summary>
|
||||
/// Gets the path to file within a deployed, framework-dependent application.
|
||||
/// <para>
|
||||
/// For example, <c>runtimes/linux-x64/native/libsqlite.so</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AppLocalPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to file within an additional probing path root. This is typically a combination
|
||||
/// of the NuGet package ID (lowercased), version, and path within the package.
|
||||
/// <para>
|
||||
/// For example, <c>sqlite/3.13.3/runtimes/linux-x64/native/libsqlite.so</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AdditionalProbingPath { get; }
|
||||
/// <summary>
|
||||
/// Gets the path to file within an additional probing path root. This is typically a combination
|
||||
/// of the NuGet package ID (lowercased), version, and path within the package.
|
||||
/// <para>
|
||||
/// For example, <c>sqlite/3.13.3/runtimes/linux-x64/native/libsqlite.so</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string AdditionalProbingPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="NativeLibrary" /> from a NuGet package.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The name of the package.</param>
|
||||
/// <param name="packageVersion">The version of the package.</param>
|
||||
/// <param name="assetPath">The path within the NuGet package.</param>
|
||||
/// <returns>A native library.</returns>
|
||||
public static NativeLibrary CreateFromPackage(string packageId, string packageVersion, string assetPath)
|
||||
{
|
||||
return new NativeLibrary(
|
||||
Path.GetFileNameWithoutExtension(assetPath),
|
||||
assetPath,
|
||||
Path.Combine(packageId.ToLowerInvariant(), packageVersion, assetPath));
|
||||
}
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="NativeLibrary" /> from a NuGet package.
|
||||
/// </summary>
|
||||
/// <param name="packageId">The name of the package.</param>
|
||||
/// <param name="packageVersion">The version of the package.</param>
|
||||
/// <param name="assetPath">The path within the NuGet package.</param>
|
||||
/// <returns>A native library.</returns>
|
||||
public static NativeLibrary CreateFromPackage(string packageId, string packageVersion, string assetPath)
|
||||
{
|
||||
return new NativeLibrary(
|
||||
Path.GetFileNameWithoutExtension(assetPath),
|
||||
assetPath,
|
||||
Path.Combine(packageId.ToLowerInvariant(), packageVersion, assetPath));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,71 +7,70 @@ using System.IO;
|
|||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader
|
||||
namespace Dalamud.Plugin.Internal.Loader;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the configuration for a plugin loader.
|
||||
/// </summary>
|
||||
internal class LoaderConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the configuration for a plugin loader.
|
||||
/// Initializes a new instance of the <see cref="LoaderConfig"/> class.
|
||||
/// </summary>
|
||||
internal class LoaderConfig
|
||||
/// <param name="mainAssemblyPath">The full file path to the main assembly for the plugin.</param>
|
||||
public LoaderConfig(string mainAssemblyPath)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoaderConfig"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainAssemblyPath">The full file path to the main assembly for the plugin.</param>
|
||||
public LoaderConfig(string mainAssemblyPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must be null or not empty", nameof(mainAssemblyPath));
|
||||
if (string.IsNullOrEmpty(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must be null or not empty", nameof(mainAssemblyPath));
|
||||
|
||||
if (!Path.IsPathRooted(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must be an absolute file path", nameof(mainAssemblyPath));
|
||||
if (!Path.IsPathRooted(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must be an absolute file path", nameof(mainAssemblyPath));
|
||||
|
||||
if (!File.Exists(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must exist", nameof(mainAssemblyPath));
|
||||
if (!File.Exists(mainAssemblyPath))
|
||||
throw new ArgumentException("Value must exist", nameof(mainAssemblyPath));
|
||||
|
||||
this.MainAssemblyPath = mainAssemblyPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path to the main assembly.
|
||||
/// </summary>
|
||||
public string MainAssemblyPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of assemblies which should be treated as private.
|
||||
/// </summary>
|
||||
public ICollection<AssemblyName> PrivateAssemblies { get; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of assemblies which should be unified between the host and the plugin.
|
||||
/// </summary>
|
||||
/// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso>
|
||||
public ICollection<AssemblyName> SharedAssemblies { get; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether attempt to unify all types from a plugin with the host.
|
||||
/// <para>
|
||||
/// This does not guarantee types will unify.
|
||||
/// </para>
|
||||
/// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso>
|
||||
/// </summary>
|
||||
public bool PreferSharedTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default <see cref="AssemblyLoadContext"/> used by the <see cref="PluginLoader"/>.
|
||||
/// Use this feature if the <see cref="AssemblyLoadContext"/> of the <see cref="Assembly"/> is not the Runtime's default load context.
|
||||
/// i.e. (AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly) != <see cref="AssemblyLoadContext.Default"/>.
|
||||
/// </summary>
|
||||
public AssemblyLoadContext DefaultContext { get; set; } = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? AssemblyLoadContext.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the plugin can be unloaded from memory.
|
||||
/// </summary>
|
||||
public bool IsUnloadable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load assemblies into memory in order to not lock files.
|
||||
/// </summary>
|
||||
public bool LoadInMemory { get; set; }
|
||||
this.MainAssemblyPath = mainAssemblyPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file path to the main assembly.
|
||||
/// </summary>
|
||||
public string MainAssemblyPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of assemblies which should be treated as private.
|
||||
/// </summary>
|
||||
public ICollection<AssemblyName> PrivateAssemblies { get; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of assemblies which should be unified between the host and the plugin.
|
||||
/// </summary>
|
||||
/// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso>
|
||||
public ICollection<AssemblyName> SharedAssemblies { get; } = new List<AssemblyName>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether attempt to unify all types from a plugin with the host.
|
||||
/// <para>
|
||||
/// This does not guarantee types will unify.
|
||||
/// </para>
|
||||
/// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso>
|
||||
/// </summary>
|
||||
public bool PreferSharedTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default <see cref="AssemblyLoadContext"/> used by the <see cref="PluginLoader"/>.
|
||||
/// Use this feature if the <see cref="AssemblyLoadContext"/> of the <see cref="Assembly"/> is not the Runtime's default load context.
|
||||
/// i.e. (AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly) != <see cref="AssemblyLoadContext.Default"/>.
|
||||
/// </summary>
|
||||
public AssemblyLoadContext DefaultContext { get; set; } = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ?? AssemblyLoadContext.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the plugin can be unloaded from memory.
|
||||
/// </summary>
|
||||
public bool IsUnloadable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to load assemblies into memory in order to not lock files.
|
||||
/// </summary>
|
||||
public bool LoadInMemory { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,387 +11,386 @@ using System.Runtime.Loader;
|
|||
|
||||
using Dalamud.Plugin.Internal.Loader.LibraryModel;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader
|
||||
namespace Dalamud.Plugin.Internal.Loader;
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="AssemblyLoadContext" /> which attempts to load managed and native
|
||||
/// binaries at runtime immitating some of the behaviors of corehost.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("'{Name}' ({_mainAssemblyPath})")]
|
||||
internal class ManagedLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly string basePath;
|
||||
private readonly string mainAssemblyPath;
|
||||
private readonly IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies;
|
||||
private readonly IReadOnlyDictionary<string, NativeLibrary> nativeLibraries;
|
||||
private readonly IReadOnlyCollection<string> privateAssemblies;
|
||||
private readonly ICollection<string> defaultAssemblies;
|
||||
private readonly IReadOnlyCollection<string> additionalProbingPaths;
|
||||
private readonly bool preferDefaultLoadContext;
|
||||
private readonly string[] resourceRoots;
|
||||
private readonly bool loadInMemory;
|
||||
private readonly AssemblyLoadContext defaultLoadContext;
|
||||
private readonly AssemblyDependencyResolver dependencyResolver;
|
||||
private readonly bool shadowCopyNativeLibraries;
|
||||
private readonly string unmanagedDllShadowCopyDirectoryPath;
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="AssemblyLoadContext" /> which attempts to load managed and native
|
||||
/// binaries at runtime immitating some of the behaviors of corehost.
|
||||
/// Initializes a new instance of the <see cref="ManagedLoadContext"/> class.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("'{Name}' ({_mainAssemblyPath})")]
|
||||
internal class ManagedLoadContext : AssemblyLoadContext
|
||||
/// <param name="mainAssemblyPath">Main assembly path.</param>
|
||||
/// <param name="managedAssemblies">Managed assemblies.</param>
|
||||
/// <param name="nativeLibraries">Native assemblies.</param>
|
||||
/// <param name="privateAssemblies">Private assemblies.</param>
|
||||
/// <param name="defaultAssemblies">Default assemblies.</param>
|
||||
/// <param name="additionalProbingPaths">Additional probing paths.</param>
|
||||
/// <param name="resourceProbingPaths">Resource probing paths.</param>
|
||||
/// <param name="defaultLoadContext">Default load context.</param>
|
||||
/// <param name="preferDefaultLoadContext">If the default load context should be prefered.</param>
|
||||
/// <param name="isCollectible">If the dll is collectible.</param>
|
||||
/// <param name="loadInMemory">If the dll should be loaded in memory.</param>
|
||||
/// <param name="shadowCopyNativeLibraries">If native libraries should be shadow copied.</param>
|
||||
public ManagedLoadContext(
|
||||
string mainAssemblyPath,
|
||||
IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies,
|
||||
IReadOnlyDictionary<string, NativeLibrary> nativeLibraries,
|
||||
IReadOnlyCollection<string> privateAssemblies,
|
||||
IReadOnlyCollection<string> defaultAssemblies,
|
||||
IReadOnlyCollection<string> additionalProbingPaths,
|
||||
IReadOnlyCollection<string> resourceProbingPaths,
|
||||
AssemblyLoadContext defaultLoadContext,
|
||||
bool preferDefaultLoadContext,
|
||||
bool isCollectible,
|
||||
bool loadInMemory,
|
||||
bool shadowCopyNativeLibraries)
|
||||
: base(Path.GetFileNameWithoutExtension(mainAssemblyPath), isCollectible)
|
||||
{
|
||||
private readonly string basePath;
|
||||
private readonly string mainAssemblyPath;
|
||||
private readonly IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies;
|
||||
private readonly IReadOnlyDictionary<string, NativeLibrary> nativeLibraries;
|
||||
private readonly IReadOnlyCollection<string> privateAssemblies;
|
||||
private readonly ICollection<string> defaultAssemblies;
|
||||
private readonly IReadOnlyCollection<string> additionalProbingPaths;
|
||||
private readonly bool preferDefaultLoadContext;
|
||||
private readonly string[] resourceRoots;
|
||||
private readonly bool loadInMemory;
|
||||
private readonly AssemblyLoadContext defaultLoadContext;
|
||||
private readonly AssemblyDependencyResolver dependencyResolver;
|
||||
private readonly bool shadowCopyNativeLibraries;
|
||||
private readonly string unmanagedDllShadowCopyDirectoryPath;
|
||||
if (resourceProbingPaths == null)
|
||||
throw new ArgumentNullException(nameof(resourceProbingPaths));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManagedLoadContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="mainAssemblyPath">Main assembly path.</param>
|
||||
/// <param name="managedAssemblies">Managed assemblies.</param>
|
||||
/// <param name="nativeLibraries">Native assemblies.</param>
|
||||
/// <param name="privateAssemblies">Private assemblies.</param>
|
||||
/// <param name="defaultAssemblies">Default assemblies.</param>
|
||||
/// <param name="additionalProbingPaths">Additional probing paths.</param>
|
||||
/// <param name="resourceProbingPaths">Resource probing paths.</param>
|
||||
/// <param name="defaultLoadContext">Default load context.</param>
|
||||
/// <param name="preferDefaultLoadContext">If the default load context should be prefered.</param>
|
||||
/// <param name="isCollectible">If the dll is collectible.</param>
|
||||
/// <param name="loadInMemory">If the dll should be loaded in memory.</param>
|
||||
/// <param name="shadowCopyNativeLibraries">If native libraries should be shadow copied.</param>
|
||||
public ManagedLoadContext(
|
||||
string mainAssemblyPath,
|
||||
IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies,
|
||||
IReadOnlyDictionary<string, NativeLibrary> nativeLibraries,
|
||||
IReadOnlyCollection<string> privateAssemblies,
|
||||
IReadOnlyCollection<string> defaultAssemblies,
|
||||
IReadOnlyCollection<string> additionalProbingPaths,
|
||||
IReadOnlyCollection<string> resourceProbingPaths,
|
||||
AssemblyLoadContext defaultLoadContext,
|
||||
bool preferDefaultLoadContext,
|
||||
bool isCollectible,
|
||||
bool loadInMemory,
|
||||
bool shadowCopyNativeLibraries)
|
||||
: base(Path.GetFileNameWithoutExtension(mainAssemblyPath), isCollectible)
|
||||
this.mainAssemblyPath = mainAssemblyPath ?? throw new ArgumentNullException(nameof(mainAssemblyPath));
|
||||
this.dependencyResolver = new AssemblyDependencyResolver(mainAssemblyPath);
|
||||
this.basePath = Path.GetDirectoryName(mainAssemblyPath) ?? throw new ArgumentException("Invalid assembly path", nameof(mainAssemblyPath));
|
||||
this.managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
|
||||
this.privateAssemblies = privateAssemblies ?? throw new ArgumentNullException(nameof(privateAssemblies));
|
||||
this.defaultAssemblies = defaultAssemblies != null ? defaultAssemblies.ToList() : throw new ArgumentNullException(nameof(defaultAssemblies));
|
||||
this.nativeLibraries = nativeLibraries ?? throw new ArgumentNullException(nameof(nativeLibraries));
|
||||
this.additionalProbingPaths = additionalProbingPaths ?? throw new ArgumentNullException(nameof(additionalProbingPaths));
|
||||
this.defaultLoadContext = defaultLoadContext;
|
||||
this.preferDefaultLoadContext = preferDefaultLoadContext;
|
||||
this.loadInMemory = loadInMemory;
|
||||
|
||||
this.resourceRoots = new[] { this.basePath }
|
||||
.Concat(resourceProbingPaths)
|
||||
.ToArray();
|
||||
|
||||
this.shadowCopyNativeLibraries = shadowCopyNativeLibraries;
|
||||
this.unmanagedDllShadowCopyDirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
if (shadowCopyNativeLibraries)
|
||||
{
|
||||
if (resourceProbingPaths == null)
|
||||
throw new ArgumentNullException(nameof(resourceProbingPaths));
|
||||
this.Unloading += _ => this.OnUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
this.mainAssemblyPath = mainAssemblyPath ?? throw new ArgumentNullException(nameof(mainAssemblyPath));
|
||||
this.dependencyResolver = new AssemblyDependencyResolver(mainAssemblyPath);
|
||||
this.basePath = Path.GetDirectoryName(mainAssemblyPath) ?? throw new ArgumentException("Invalid assembly path", nameof(mainAssemblyPath));
|
||||
this.managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
|
||||
this.privateAssemblies = privateAssemblies ?? throw new ArgumentNullException(nameof(privateAssemblies));
|
||||
this.defaultAssemblies = defaultAssemblies != null ? defaultAssemblies.ToList() : throw new ArgumentNullException(nameof(defaultAssemblies));
|
||||
this.nativeLibraries = nativeLibraries ?? throw new ArgumentNullException(nameof(nativeLibraries));
|
||||
this.additionalProbingPaths = additionalProbingPaths ?? throw new ArgumentNullException(nameof(additionalProbingPaths));
|
||||
this.defaultLoadContext = defaultLoadContext;
|
||||
this.preferDefaultLoadContext = preferDefaultLoadContext;
|
||||
this.loadInMemory = loadInMemory;
|
||||
/// <summary>
|
||||
/// Load an assembly from a filepath.
|
||||
/// </summary>
|
||||
/// <param name="path">Assembly path.</param>
|
||||
/// <returns>A loaded assembly.</returns>
|
||||
public Assembly LoadAssemblyFromFilePath(string path)
|
||||
{
|
||||
if (!this.loadInMemory)
|
||||
return this.LoadFromAssemblyPath(path);
|
||||
|
||||
this.resourceRoots = new[] { this.basePath }
|
||||
.Concat(resourceProbingPaths)
|
||||
.ToArray();
|
||||
using var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
this.shadowCopyNativeLibraries = shadowCopyNativeLibraries;
|
||||
this.unmanagedDllShadowCopyDirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var pdbPath = Path.ChangeExtension(path, ".pdb");
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
return this.LoadFromStream(file, pdbFile);
|
||||
}
|
||||
|
||||
if (shadowCopyNativeLibraries)
|
||||
return this.LoadFromStream(file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">Name of the assembly.</param>
|
||||
/// <returns>Loaded assembly.</returns>
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
if (assemblyName.Name == null)
|
||||
{
|
||||
// not sure how to handle this case. It's technically possible.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((this.preferDefaultLoadContext || this.defaultAssemblies.Contains(assemblyName.Name)) && !this.privateAssemblies.Contains(assemblyName.Name))
|
||||
{
|
||||
// If default context is preferred, check first for types in the default context unless the dependency has been declared as private
|
||||
try
|
||||
{
|
||||
this.Unloading += _ => this.OnUnloaded();
|
||||
var defaultAssembly = this.defaultLoadContext.LoadFromAssemblyName(assemblyName);
|
||||
if (defaultAssembly != null)
|
||||
{
|
||||
// Older versions used to return null here such that returned assembly would be resolved from the default ALC.
|
||||
// However, with the addition of custom default ALCs, the Default ALC may not be the user's chosen ALC when
|
||||
// this context was built. As such, we simply return the Assembly from the user's chosen default load context.
|
||||
return defaultAssembly;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow errors in loading from the default context
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an assembly from a filepath.
|
||||
/// </summary>
|
||||
/// <param name="path">Assembly path.</param>
|
||||
/// <returns>A loaded assembly.</returns>
|
||||
public Assembly LoadAssemblyFromFilePath(string path)
|
||||
var resolvedPath = this.dependencyResolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||||
{
|
||||
if (!this.loadInMemory)
|
||||
return this.LoadFromAssemblyPath(path);
|
||||
|
||||
using var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
var pdbPath = Path.ChangeExtension(path, ".pdb");
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
return this.LoadFromStream(file, pdbFile);
|
||||
}
|
||||
|
||||
return this.LoadFromStream(file);
|
||||
return this.LoadAssemblyFromFilePath(resolvedPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">Name of the assembly.</param>
|
||||
/// <returns>Loaded assembly.</returns>
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
// Resource assembly binding does not use the TPA. Instead, it probes PLATFORM_RESOURCE_ROOTS (a list of folders)
|
||||
// for $folder/$culture/$assemblyName.dll
|
||||
// See https://github.com/dotnet/coreclr/blob/3fca50a36e62a7433d7601d805d38de6baee7951/src/binder/assemblybinder.cpp#L1232-L1290
|
||||
|
||||
if (!string.IsNullOrEmpty(assemblyName.CultureName) && !string.Equals(assemblyName.CultureName, "neutral"))
|
||||
{
|
||||
if (assemblyName.Name == null)
|
||||
foreach (var resourceRoot in this.resourceRoots)
|
||||
{
|
||||
// not sure how to handle this case. It's technically possible.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((this.preferDefaultLoadContext || this.defaultAssemblies.Contains(assemblyName.Name)) && !this.privateAssemblies.Contains(assemblyName.Name))
|
||||
{
|
||||
// If default context is preferred, check first for types in the default context unless the dependency has been declared as private
|
||||
try
|
||||
var resourcePath = Path.Combine(resourceRoot, assemblyName.CultureName, assemblyName.Name + ".dll");
|
||||
if (File.Exists(resourcePath))
|
||||
{
|
||||
var defaultAssembly = this.defaultLoadContext.LoadFromAssemblyName(assemblyName);
|
||||
if (defaultAssembly != null)
|
||||
{
|
||||
// Older versions used to return null here such that returned assembly would be resolved from the default ALC.
|
||||
// However, with the addition of custom default ALCs, the Default ALC may not be the user's chosen ALC when
|
||||
// this context was built. As such, we simply return the Assembly from the user's chosen default load context.
|
||||
return defaultAssembly;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow errors in loading from the default context
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedPath = this.dependencyResolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||||
{
|
||||
return this.LoadAssemblyFromFilePath(resolvedPath);
|
||||
}
|
||||
|
||||
// Resource assembly binding does not use the TPA. Instead, it probes PLATFORM_RESOURCE_ROOTS (a list of folders)
|
||||
// for $folder/$culture/$assemblyName.dll
|
||||
// See https://github.com/dotnet/coreclr/blob/3fca50a36e62a7433d7601d805d38de6baee7951/src/binder/assemblybinder.cpp#L1232-L1290
|
||||
|
||||
if (!string.IsNullOrEmpty(assemblyName.CultureName) && !string.Equals(assemblyName.CultureName, "neutral"))
|
||||
{
|
||||
foreach (var resourceRoot in this.resourceRoots)
|
||||
{
|
||||
var resourcePath = Path.Combine(resourceRoot, assemblyName.CultureName, assemblyName.Name + ".dll");
|
||||
if (File.Exists(resourcePath))
|
||||
{
|
||||
return this.LoadAssemblyFromFilePath(resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.managedAssemblies.TryGetValue(assemblyName.Name, out var library) && library != null)
|
||||
{
|
||||
if (this.SearchForLibrary(library, out var path) && path != null)
|
||||
{
|
||||
return this.LoadAssemblyFromFilePath(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if an assembly was not listed in the list of known assemblies,
|
||||
// fallback to the load context base directory
|
||||
var dllName = assemblyName.Name + ".dll";
|
||||
foreach (var probingPath in this.additionalProbingPaths.Prepend(this.basePath))
|
||||
{
|
||||
var localFile = Path.Combine(probingPath, dllName);
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
return this.LoadAssemblyFromFilePath(localFile);
|
||||
}
|
||||
return this.LoadAssemblyFromFilePath(resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the unmanaged binary using configured list of native libraries.
|
||||
/// </summary>
|
||||
/// <param name="unmanagedDllName">Unmanaged DLL name.</param>
|
||||
/// <returns>The unmanaged dll handle.</returns>
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
if (this.managedAssemblies.TryGetValue(assemblyName.Name, out var library) && library != null)
|
||||
{
|
||||
var resolvedPath = this.dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
||||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||||
if (this.SearchForLibrary(library, out var path) && path != null)
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(resolvedPath, normalizePath: false);
|
||||
return this.LoadAssemblyFromFilePath(path);
|
||||
}
|
||||
|
||||
foreach (var prefix in PlatformInformation.NativeLibraryPrefixes)
|
||||
}
|
||||
else
|
||||
{
|
||||
// if an assembly was not listed in the list of known assemblies,
|
||||
// fallback to the load context base directory
|
||||
var dllName = assemblyName.Name + ".dll";
|
||||
foreach (var probingPath in this.additionalProbingPaths.Prepend(this.basePath))
|
||||
{
|
||||
if (this.nativeLibraries.TryGetValue(prefix + unmanagedDllName, out var library))
|
||||
var localFile = Path.Combine(probingPath, dllName);
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
if (this.SearchForLibrary(library, prefix, out var path) && path != null)
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// coreclr allows code to use [DllImport("sni")] or [DllImport("sni.dll")]
|
||||
// This library treats the file name without the extension as the lookup name,
|
||||
// so this loop is necessary to check if the unmanaged name matches a library
|
||||
// when the file extension has been trimmed.
|
||||
foreach (var suffix in PlatformInformation.NativeLibraryExtensions)
|
||||
{
|
||||
if (!unmanagedDllName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check to see if there is a library entry for the library without the file extension
|
||||
var trimmedName = unmanagedDllName.Substring(0, unmanagedDllName.Length - suffix.Length);
|
||||
|
||||
if (this.nativeLibraries.TryGetValue(prefix + trimmedName, out library))
|
||||
{
|
||||
if (this.SearchForLibrary(library, prefix, out var path) && path != null)
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback to native assets which match the file name in the plugin base directory
|
||||
var prefixSuffixDllName = prefix + unmanagedDllName + suffix;
|
||||
var prefixDllName = prefix + unmanagedDllName;
|
||||
|
||||
foreach (var probingPath in this.additionalProbingPaths.Prepend(this.basePath))
|
||||
{
|
||||
var localFile = Path.Combine(probingPath, prefixSuffixDllName);
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(localFile);
|
||||
}
|
||||
|
||||
var localFileWithoutSuffix = Path.Combine(probingPath, prefixDllName);
|
||||
if (File.Exists(localFileWithoutSuffix))
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(localFileWithoutSuffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.LoadAssemblyFromFilePath(localFile);
|
||||
}
|
||||
}
|
||||
|
||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
||||
|
||||
private bool SearchForLibrary(ManagedLibrary library, out string? path)
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the unmanaged binary using configured list of native libraries.
|
||||
/// </summary>
|
||||
/// <param name="unmanagedDllName">Unmanaged DLL name.</param>
|
||||
/// <returns>The unmanaged dll handle.</returns>
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
var resolvedPath = this.dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
||||
if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath))
|
||||
{
|
||||
// 1. Check for in _basePath + app local path
|
||||
var localFile = Path.Combine(this.basePath, library.AppLocalPath);
|
||||
if (File.Exists(localFile))
|
||||
return this.LoadUnmanagedDllFromResolvedPath(resolvedPath, normalizePath: false);
|
||||
}
|
||||
|
||||
foreach (var prefix in PlatformInformation.NativeLibraryPrefixes)
|
||||
{
|
||||
if (this.nativeLibraries.TryGetValue(prefix + unmanagedDllName, out var library))
|
||||
{
|
||||
path = localFile;
|
||||
if (this.SearchForLibrary(library, prefix, out var path) && path != null)
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// coreclr allows code to use [DllImport("sni")] or [DllImport("sni.dll")]
|
||||
// This library treats the file name without the extension as the lookup name,
|
||||
// so this loop is necessary to check if the unmanaged name matches a library
|
||||
// when the file extension has been trimmed.
|
||||
foreach (var suffix in PlatformInformation.NativeLibraryExtensions)
|
||||
{
|
||||
if (!unmanagedDllName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check to see if there is a library entry for the library without the file extension
|
||||
var trimmedName = unmanagedDllName.Substring(0, unmanagedDllName.Length - suffix.Length);
|
||||
|
||||
if (this.nativeLibraries.TryGetValue(prefix + trimmedName, out library))
|
||||
{
|
||||
if (this.SearchForLibrary(library, prefix, out var path) && path != null)
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback to native assets which match the file name in the plugin base directory
|
||||
var prefixSuffixDllName = prefix + unmanagedDllName + suffix;
|
||||
var prefixDllName = prefix + unmanagedDllName;
|
||||
|
||||
foreach (var probingPath in this.additionalProbingPaths.Prepend(this.basePath))
|
||||
{
|
||||
var localFile = Path.Combine(probingPath, prefixSuffixDllName);
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(localFile);
|
||||
}
|
||||
|
||||
var localFileWithoutSuffix = Path.Combine(probingPath, prefixDllName);
|
||||
if (File.Exists(localFileWithoutSuffix))
|
||||
{
|
||||
return this.LoadUnmanagedDllFromResolvedPath(localFileWithoutSuffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
||||
|
||||
private bool SearchForLibrary(ManagedLibrary library, out string? path)
|
||||
{
|
||||
// 1. Check for in _basePath + app local path
|
||||
var localFile = Path.Combine(this.basePath, library.AppLocalPath);
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
path = localFile;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Search additional probing paths
|
||||
foreach (var searchPath in this.additionalProbingPaths)
|
||||
{
|
||||
var candidate = Path.Combine(searchPath, library.AdditionalProbingPath);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Search additional probing paths
|
||||
foreach (var searchPath in this.additionalProbingPaths)
|
||||
{
|
||||
var candidate = Path.Combine(searchPath, library.AdditionalProbingPath);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Search in base path
|
||||
foreach (var ext in PlatformInformation.ManagedAssemblyExtensions)
|
||||
{
|
||||
var local = Path.Combine(this.basePath, library.Name.Name + ext);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SearchForLibrary(NativeLibrary library, string prefix, out string? path)
|
||||
// 3. Search in base path
|
||||
foreach (var ext in PlatformInformation.ManagedAssemblyExtensions)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in PlatformInformation.NativeLibraryExtensions)
|
||||
{
|
||||
var candidate = Path.Combine(this.basePath, $"{prefix}{library.Name}{ext}");
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Search in base path + app local (for portable deployments of netcoreapp)
|
||||
var local = Path.Combine(this.basePath, library.AppLocalPath);
|
||||
var local = Path.Combine(this.basePath, library.Name.Name + ext);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Search additional probing paths
|
||||
foreach (var searchPath in this.additionalProbingPaths)
|
||||
{
|
||||
var candidate = Path.Combine(searchPath, library.AdditionalProbingPath);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private IntPtr LoadUnmanagedDllFromResolvedPath(string unmanagedDllPath, bool normalizePath = true)
|
||||
{
|
||||
if (normalizePath)
|
||||
{
|
||||
unmanagedDllPath = Path.GetFullPath(unmanagedDllPath);
|
||||
}
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.shadowCopyNativeLibraries
|
||||
? this.LoadUnmanagedDllFromShadowCopy(unmanagedDllPath)
|
||||
: this.LoadUnmanagedDllFromPath(unmanagedDllPath);
|
||||
private bool SearchForLibrary(NativeLibrary library, string prefix, out string? path)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in PlatformInformation.NativeLibraryExtensions)
|
||||
{
|
||||
var candidate = Path.Combine(this.basePath, $"{prefix}{library.Name}{ext}");
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr LoadUnmanagedDllFromShadowCopy(string unmanagedDllPath)
|
||||
// 2. Search in base path + app local (for portable deployments of netcoreapp)
|
||||
var local = Path.Combine(this.basePath, library.AppLocalPath);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
var shadowCopyDllPath = this.CreateShadowCopy(unmanagedDllPath);
|
||||
|
||||
return this.LoadUnmanagedDllFromPath(shadowCopyDllPath);
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
|
||||
private string CreateShadowCopy(string dllPath)
|
||||
// 3. Search additional probing paths
|
||||
foreach (var searchPath in this.additionalProbingPaths)
|
||||
{
|
||||
Directory.CreateDirectory(this.unmanagedDllShadowCopyDirectoryPath);
|
||||
|
||||
var dllFileName = Path.GetFileName(dllPath);
|
||||
var shadowCopyPath = Path.Combine(this.unmanagedDllShadowCopyDirectoryPath, dllFileName);
|
||||
|
||||
if (!File.Exists(shadowCopyPath))
|
||||
var candidate = Path.Combine(searchPath, library.AdditionalProbingPath);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
File.Copy(dllPath, shadowCopyPath);
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
return shadowCopyPath;
|
||||
}
|
||||
|
||||
private void OnUnloaded()
|
||||
{
|
||||
if (!this.shadowCopyNativeLibraries || !Directory.Exists(this.unmanagedDllShadowCopyDirectoryPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to delete shadow copies
|
||||
try
|
||||
{
|
||||
Directory.Delete(this.unmanagedDllShadowCopyDirectoryPath, recursive: true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Files might be locked by host process. Nothing we can do about it, I guess.
|
||||
}
|
||||
private IntPtr LoadUnmanagedDllFromResolvedPath(string unmanagedDllPath, bool normalizePath = true)
|
||||
{
|
||||
if (normalizePath)
|
||||
{
|
||||
unmanagedDllPath = Path.GetFullPath(unmanagedDllPath);
|
||||
}
|
||||
|
||||
return this.shadowCopyNativeLibraries
|
||||
? this.LoadUnmanagedDllFromShadowCopy(unmanagedDllPath)
|
||||
: this.LoadUnmanagedDllFromPath(unmanagedDllPath);
|
||||
}
|
||||
|
||||
private IntPtr LoadUnmanagedDllFromShadowCopy(string unmanagedDllPath)
|
||||
{
|
||||
var shadowCopyDllPath = this.CreateShadowCopy(unmanagedDllPath);
|
||||
|
||||
return this.LoadUnmanagedDllFromPath(shadowCopyDllPath);
|
||||
}
|
||||
|
||||
private string CreateShadowCopy(string dllPath)
|
||||
{
|
||||
Directory.CreateDirectory(this.unmanagedDllShadowCopyDirectoryPath);
|
||||
|
||||
var dllFileName = Path.GetFileName(dllPath);
|
||||
var shadowCopyPath = Path.Combine(this.unmanagedDllShadowCopyDirectoryPath, dllFileName);
|
||||
|
||||
if (!File.Exists(shadowCopyPath))
|
||||
{
|
||||
File.Copy(dllPath, shadowCopyPath);
|
||||
}
|
||||
|
||||
return shadowCopyPath;
|
||||
}
|
||||
|
||||
private void OnUnloaded()
|
||||
{
|
||||
if (!this.shadowCopyNativeLibraries || !Directory.Exists(this.unmanagedDllShadowCopyDirectoryPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to delete shadow copies
|
||||
try
|
||||
{
|
||||
Directory.Delete(this.unmanagedDllShadowCopyDirectoryPath, recursive: true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Files might be locked by host process. Nothing we can do about it, I guess.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,31 @@
|
|||
// Copyright (c) Nate McMaster, Dalamud team.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the Loader root for license information.
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader
|
||||
namespace Dalamud.Plugin.Internal.Loader;
|
||||
|
||||
/// <summary>
|
||||
/// Platform specific information.
|
||||
/// </summary>
|
||||
internal class PlatformInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Platform specific information.
|
||||
/// Gets a list of native OS specific library extensions.
|
||||
/// </summary>
|
||||
internal class PlatformInformation
|
||||
public static string[] NativeLibraryExtensions => new[] { ".dll" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of native OS specific library prefixes.
|
||||
/// </summary>
|
||||
public static string[] NativeLibraryPrefixes => new[] { string.Empty };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of native OS specific managed assembly extensions.
|
||||
/// </summary>
|
||||
public static string[] ManagedAssemblyExtensions => new[]
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of native OS specific library extensions.
|
||||
/// </summary>
|
||||
public static string[] NativeLibraryExtensions => new[] { ".dll" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of native OS specific library prefixes.
|
||||
/// </summary>
|
||||
public static string[] NativeLibraryPrefixes => new[] { string.Empty };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of native OS specific managed assembly extensions.
|
||||
/// </summary>
|
||||
public static string[] ManagedAssemblyExtensions => new[]
|
||||
{
|
||||
".dll",
|
||||
".ni.dll",
|
||||
".exe",
|
||||
".ni.exe",
|
||||
};
|
||||
}
|
||||
".dll",
|
||||
".ni.dll",
|
||||
".exe",
|
||||
".ni.exe",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,159 +5,158 @@ using System;
|
|||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Dalamud.Plugin.Internal.Loader
|
||||
namespace Dalamud.Plugin.Internal.Loader;
|
||||
|
||||
/// <summary>
|
||||
/// This loader attempts to load binaries for execution (both managed assemblies and native libraries)
|
||||
/// in the same way that .NET Core would if they were originally part of the .NET Core application.
|
||||
/// <para>
|
||||
/// This loader reads configuration files produced by .NET Core (.deps.json and runtimeconfig.json)
|
||||
/// as well as a custom file (*.config files). These files describe a list of .dlls and a set of dependencies.
|
||||
/// The loader searches the plugin path, as well as any additionally specified paths, for binaries
|
||||
/// which satisfy the plugin's requirements.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal class PluginLoader : IDisposable
|
||||
{
|
||||
private readonly LoaderConfig config;
|
||||
private readonly AssemblyLoadContextBuilder contextBuilder;
|
||||
private ManagedLoadContext context;
|
||||
private volatile bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// This loader attempts to load binaries for execution (both managed assemblies and native libraries)
|
||||
/// in the same way that .NET Core would if they were originally part of the .NET Core application.
|
||||
/// Initializes a new instance of the <see cref="PluginLoader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration for the plugin.</param>
|
||||
public PluginLoader(LoaderConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
this.contextBuilder = CreateLoadContextBuilder(config);
|
||||
this.context = (ManagedLoadContext)this.contextBuilder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this plugin is capable of being unloaded.
|
||||
/// </summary>
|
||||
public bool IsUnloadable
|
||||
=> this.context.IsCollectible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly load context.
|
||||
/// </summary>
|
||||
public AssemblyLoadContext LoadContext => this.context;
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="configure">A function which can be used to configure advanced options for the plugin loader.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Action<LoaderConfig> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
|
||||
var config = new LoaderConfig(assemblyFile);
|
||||
configure(config);
|
||||
return new PluginLoader(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unloads and reloads the plugin assemblies.
|
||||
/// This method throws if <see cref="IsUnloadable" /> is <c>false</c>.
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
{
|
||||
this.EnsureNotDisposed();
|
||||
|
||||
if (!this.IsUnloadable)
|
||||
{
|
||||
throw new InvalidOperationException("Reload cannot be used because IsUnloadable is false");
|
||||
}
|
||||
|
||||
this.context.Unload();
|
||||
this.context = (ManagedLoadContext)this.contextBuilder.Build();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the main assembly for the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The assembly.</returns>
|
||||
public Assembly LoadDefaultAssembly()
|
||||
{
|
||||
this.EnsureNotDisposed();
|
||||
return this.context.LoadAssemblyFromFilePath(this.config.MainAssemblyPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scope used by some System.Reflection APIs which might trigger assembly loading.
|
||||
/// <para>
|
||||
/// This loader reads configuration files produced by .NET Core (.deps.json and runtimeconfig.json)
|
||||
/// as well as a custom file (*.config files). These files describe a list of .dlls and a set of dependencies.
|
||||
/// The loader searches the plugin path, as well as any additionally specified paths, for binaries
|
||||
/// which satisfy the plugin's requirements.
|
||||
/// See https://github.com/dotnet/coreclr/blob/v3.0.0/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md for more details.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal class PluginLoader : IDisposable
|
||||
/// <returns>A contextual reflection scope.</returns>
|
||||
public AssemblyLoadContext.ContextualReflectionScope EnterContextualReflection()
|
||||
=> this.context.EnterContextualReflection();
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the plugin loader. This only does something if <see cref="IsUnloadable" /> is true.
|
||||
/// When true, this will unload assemblies which which were loaded during the lifetime
|
||||
/// of the plugin.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
private readonly LoaderConfig config;
|
||||
private readonly AssemblyLoadContextBuilder contextBuilder;
|
||||
private ManagedLoadContext context;
|
||||
private volatile bool disposed;
|
||||
if (this.disposed)
|
||||
return;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginLoader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration for the plugin.</param>
|
||||
public PluginLoader(LoaderConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
this.contextBuilder = CreateLoadContextBuilder(config);
|
||||
this.context = (ManagedLoadContext)this.contextBuilder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this plugin is capable of being unloaded.
|
||||
/// </summary>
|
||||
public bool IsUnloadable
|
||||
=> this.context.IsCollectible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly load context.
|
||||
/// </summary>
|
||||
public AssemblyLoadContext LoadContext => this.context;
|
||||
|
||||
/// <summary>
|
||||
/// Create a plugin loader for an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFile">The file path to the main assembly for the plugin.</param>
|
||||
/// <param name="configure">A function which can be used to configure advanced options for the plugin loader.</param>
|
||||
/// <returns>A loader.</returns>
|
||||
public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Action<LoaderConfig> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
|
||||
var config = new LoaderConfig(assemblyFile);
|
||||
configure(config);
|
||||
return new PluginLoader(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unloads and reloads the plugin assemblies.
|
||||
/// This method throws if <see cref="IsUnloadable" /> is <c>false</c>.
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
{
|
||||
this.EnsureNotDisposed();
|
||||
|
||||
if (!this.IsUnloadable)
|
||||
{
|
||||
throw new InvalidOperationException("Reload cannot be used because IsUnloadable is false");
|
||||
}
|
||||
this.disposed = true;
|
||||
|
||||
if (this.context.IsCollectible)
|
||||
this.context.Unload();
|
||||
this.context = (ManagedLoadContext)this.contextBuilder.Build();
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
private static AssemblyLoadContextBuilder CreateLoadContextBuilder(LoaderConfig config)
|
||||
{
|
||||
var builder = new AssemblyLoadContextBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// Load the main assembly for the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The assembly.</returns>
|
||||
public Assembly LoadDefaultAssembly()
|
||||
builder.SetMainAssemblyPath(config.MainAssemblyPath);
|
||||
builder.SetDefaultContext(config.DefaultContext);
|
||||
|
||||
foreach (var ext in config.PrivateAssemblies)
|
||||
{
|
||||
this.EnsureNotDisposed();
|
||||
return this.context.LoadAssemblyFromFilePath(this.config.MainAssemblyPath);
|
||||
builder.PreferLoadContextAssembly(ext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scope used by some System.Reflection APIs which might trigger assembly loading.
|
||||
/// <para>
|
||||
/// See https://github.com/dotnet/coreclr/blob/v3.0.0/Documentation/design-docs/AssemblyLoadContext.ContextualReflection.md for more details.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <returns>A contextual reflection scope.</returns>
|
||||
public AssemblyLoadContext.ContextualReflectionScope EnterContextualReflection()
|
||||
=> this.context.EnterContextualReflection();
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the plugin loader. This only does something if <see cref="IsUnloadable" /> is true.
|
||||
/// When true, this will unload assemblies which which were loaded during the lifetime
|
||||
/// of the plugin.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
if (config.PreferSharedTypes)
|
||||
{
|
||||
if (this.disposed)
|
||||
return;
|
||||
|
||||
this.disposed = true;
|
||||
|
||||
if (this.context.IsCollectible)
|
||||
this.context.Unload();
|
||||
builder.PreferDefaultLoadContext(true);
|
||||
}
|
||||
|
||||
private static AssemblyLoadContextBuilder CreateLoadContextBuilder(LoaderConfig config)
|
||||
if (config.IsUnloadable)
|
||||
{
|
||||
var builder = new AssemblyLoadContextBuilder();
|
||||
|
||||
builder.SetMainAssemblyPath(config.MainAssemblyPath);
|
||||
builder.SetDefaultContext(config.DefaultContext);
|
||||
|
||||
foreach (var ext in config.PrivateAssemblies)
|
||||
{
|
||||
builder.PreferLoadContextAssembly(ext);
|
||||
}
|
||||
|
||||
if (config.PreferSharedTypes)
|
||||
{
|
||||
builder.PreferDefaultLoadContext(true);
|
||||
}
|
||||
|
||||
if (config.IsUnloadable)
|
||||
{
|
||||
builder.EnableUnloading();
|
||||
}
|
||||
|
||||
if (config.LoadInMemory)
|
||||
{
|
||||
builder.PreloadAssembliesIntoMemory();
|
||||
builder.ShadowCopyNativeLibraries();
|
||||
}
|
||||
|
||||
foreach (var assemblyName in config.SharedAssemblies)
|
||||
{
|
||||
builder.PreferDefaultLoadContextAssembly(assemblyName);
|
||||
}
|
||||
|
||||
return builder;
|
||||
builder.EnableUnloading();
|
||||
}
|
||||
|
||||
private void EnsureNotDisposed()
|
||||
if (config.LoadInMemory)
|
||||
{
|
||||
if (this.disposed)
|
||||
throw new ObjectDisposedException(nameof(PluginLoader));
|
||||
builder.PreloadAssembliesIntoMemory();
|
||||
builder.ShadowCopyNativeLibraries();
|
||||
}
|
||||
|
||||
foreach (var assemblyName in config.SharedAssemblies)
|
||||
{
|
||||
builder.PreferDefaultLoadContextAssembly(assemblyName);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void EnsureNotDisposed()
|
||||
{
|
||||
if (this.disposed)
|
||||
throw new ObjectDisposedException(nameof(PluginLoader));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC errors are encountered.
|
||||
/// </summary>
|
||||
public abstract class IpcError : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC errors are encountered.
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
public abstract class IpcError : Exception
|
||||
public IpcError()
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
public IpcError()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public IpcError(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public IpcError(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="ex">The exception that is the cause of the current exception.</param>
|
||||
public IpcError(string message, Exception ex)
|
||||
: base(message, ex)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="ex">The exception that is the cause of the current exception.</param>
|
||||
public IpcError(string message, Exception ex)
|
||||
: base(message, ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
namespace Dalamud.Plugin.Ipc.Exceptions
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is invoked and the number of types does not match what was previously registered.
|
||||
/// </summary>
|
||||
public class IpcLengthMismatchError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is invoked and the number of types does not match what was previously registered.
|
||||
/// Initializes a new instance of the <see cref="IpcLengthMismatchError"/> class.
|
||||
/// </summary>
|
||||
public class IpcLengthMismatchError : IpcError
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
/// <param name="requestedLength">The amount of types requested when checking out the IPC.</param>
|
||||
/// <param name="actualLength">The amount of types registered by the IPC.</param>
|
||||
public IpcLengthMismatchError(string name, int requestedLength, int actualLength)
|
||||
: base($"IPC method {name} has a different number of types than was requested. {requestedLength} != {actualLength}")
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcLengthMismatchError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
/// <param name="requestedLength">The amount of types requested when checking out the IPC.</param>
|
||||
/// <param name="actualLength">The amount of types registered by the IPC.</param>
|
||||
public IpcLengthMismatchError(string name, int requestedLength, int actualLength)
|
||||
: base($"IPC method {name} has a different number of types than was requested. {requestedLength} != {actualLength}")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
namespace Dalamud.Plugin.Ipc.Exceptions
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is invoked, but no actions or funcs have been registered yet.
|
||||
/// </summary>
|
||||
public class IpcNotReadyError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is invoked, but no actions or funcs have been registered yet.
|
||||
/// Initializes a new instance of the <see cref="IpcNotReadyError"/> class.
|
||||
/// </summary>
|
||||
public class IpcNotReadyError : IpcError
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
public IpcNotReadyError(string name)
|
||||
: base($"IPC method {name} was not registered yet")
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcNotReadyError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
public IpcNotReadyError(string name)
|
||||
: base($"IPC method {name} was not registered yet")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions
|
||||
namespace Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered.
|
||||
/// </summary>
|
||||
public class IpcTypeMismatchError : IpcError
|
||||
{
|
||||
/// <summary>
|
||||
/// This exception is thrown when an IPC method is checked out, but the type does not match what was previously registered.
|
||||
/// Initializes a new instance of the <see cref="IpcTypeMismatchError"/> class.
|
||||
/// </summary>
|
||||
public class IpcTypeMismatchError : IpcError
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
/// <param name="requestedType">The before type.</param>
|
||||
/// <param name="actualType">The after type.</param>
|
||||
/// <param name="ex">The exception that is the cause of the current exception.</param>
|
||||
public IpcTypeMismatchError(string name, Type requestedType, Type actualType, Exception ex)
|
||||
: base($"IPC method {name} blew up when converting from {requestedType.Name} to {actualType}", ex)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IpcTypeMismatchError"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the IPC method.</param>
|
||||
/// <param name="requestedType">The before type.</param>
|
||||
/// <param name="actualType">The after type.</param>
|
||||
/// <param name="ex">The exception that is the cause of the current exception.</param>
|
||||
public IpcTypeMismatchError(string name, Type requestedType, Type actualType, Exception ex)
|
||||
: base($"IPC method {name} blew up when converting from {requestedType.Name} to {actualType}", ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,178 +4,177 @@ using Dalamud.Plugin.Ipc.Internal;
|
|||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
namespace Dalamud.Plugin.Ipc
|
||||
namespace Dalamud.Plugin.Ipc;
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage();
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, T8, TRet> func);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, T8, TRet> func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterAction"/>
|
||||
public void UnregisterAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
|
||||
public void UnregisterFunc();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
}
|
||||
|
||||
#pragma warning restore SA1402 // File may only contain a single type
|
||||
|
|
|
|||
|
|
@ -4,151 +4,150 @@ using Dalamud.Plugin.Ipc.Internal;
|
|||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
namespace Dalamud.Plugin.Ipc
|
||||
namespace Dalamud.Plugin.Ipc;
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction();
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc();
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
public interface ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
}
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
}
|
||||
|
||||
#pragma warning restore SA1402 // File may only contain a single type
|
||||
|
|
|
|||
|
|
@ -1,30 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// This class facilitates inter-plugin communication.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal class CallGate : IServiceType
|
||||
{
|
||||
/// <summary>
|
||||
/// This class facilitates inter-plugin communication.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal class CallGate : IServiceType
|
||||
private readonly Dictionary<string, CallGateChannel> gates = new();
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private CallGate()
|
||||
{
|
||||
private readonly Dictionary<string, CallGateChannel> gates = new();
|
||||
}
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private CallGate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider associated with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the IPC registration.</param>
|
||||
/// <returns>A CallGate registered under the given name.</returns>
|
||||
public CallGateChannel GetOrCreateChannel(string name)
|
||||
{
|
||||
if (!this.gates.TryGetValue(name, out var gate))
|
||||
gate = this.gates[name] = new CallGateChannel(name);
|
||||
return gate;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the provider associated with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the IPC registration.</param>
|
||||
/// <returns>A CallGate registered under the given name.</returns>
|
||||
public CallGateChannel GetOrCreateChannel(string name)
|
||||
{
|
||||
if (!this.gates.TryGetValue(name, out var gate))
|
||||
gate = this.gates[name] = new CallGateChannel(name);
|
||||
return gate;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,188 +7,187 @@ using Dalamud.Plugin.Ipc.Exceptions;
|
|||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// This class implements the channels and serialization needed for the typed gates to interact with each other.
|
||||
/// </summary>
|
||||
internal class CallGateChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements the channels and serialization needed for the typed gates to interact with each other.
|
||||
/// Initializes a new instance of the <see cref="CallGateChannel"/> class.
|
||||
/// </summary>
|
||||
internal class CallGateChannel
|
||||
/// <param name="name">The name of this IPC registration.</param>
|
||||
public CallGateChannel(string name)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CallGateChannel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of this IPC registration.</param>
|
||||
public CallGateChannel(string name)
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the IPC registration.
|
||||
/// </summary>
|
||||
public string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of delegate subscriptions for when SendMessage is called.
|
||||
/// </summary>
|
||||
public List<Delegate> Subscriptions { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action for when InvokeAction is called.
|
||||
/// </summary>
|
||||
public Delegate Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a func for when InvokeFunc is called.
|
||||
/// </summary>
|
||||
public Delegate Func { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoke all actions that have subscribed to this IPC.
|
||||
/// </summary>
|
||||
/// <param name="args">Message arguments.</param>
|
||||
internal void SendMessage(object?[]? args)
|
||||
{
|
||||
if (this.Subscriptions.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var subscription in this.Subscriptions)
|
||||
{
|
||||
this.Name = name;
|
||||
var methodInfo = subscription.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
|
||||
subscription.DynamicInvoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the IPC registration.
|
||||
/// </summary>
|
||||
public string Name { get; init; }
|
||||
/// <summary>
|
||||
/// Invoke an action registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Action arguments.</param>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
internal void InvokeAction(object?[]? args)
|
||||
{
|
||||
if (this.Action == null)
|
||||
throw new IpcNotReadyError(this.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of delegate subscriptions for when SendMessage is called.
|
||||
/// </summary>
|
||||
public List<Delegate> Subscriptions { get; } = new();
|
||||
var methodInfo = this.Action.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action for when InvokeAction is called.
|
||||
/// </summary>
|
||||
public Delegate Action { get; set; }
|
||||
this.Action.DynamicInvoke(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a func for when InvokeFunc is called.
|
||||
/// </summary>
|
||||
public Delegate Func { get; set; }
|
||||
/// <summary>
|
||||
/// Invoke a function registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Func arguments.</param>
|
||||
/// <returns>The return value.</returns>
|
||||
/// <typeparam name="TRet">The return type.</typeparam>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
internal TRet InvokeFunc<TRet>(object?[]? args)
|
||||
{
|
||||
if (this.Func == null)
|
||||
throw new IpcNotReadyError(this.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke all actions that have subscribed to this IPC.
|
||||
/// </summary>
|
||||
/// <param name="args">Message arguments.</param>
|
||||
internal void SendMessage(object?[]? args)
|
||||
var methodInfo = this.Func.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
|
||||
var result = this.Func.DynamicInvoke(args);
|
||||
|
||||
if (typeof(TRet) != methodInfo.ReturnType)
|
||||
result = this.ConvertObject(result, typeof(TRet));
|
||||
|
||||
return (TRet)result;
|
||||
}
|
||||
|
||||
private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo)
|
||||
{
|
||||
var paramTypes = methodInfo.GetParameters()
|
||||
.Select(pi => pi.ParameterType).ToArray();
|
||||
|
||||
if (args?.Length != paramTypes.Length)
|
||||
throw new IpcLengthMismatchError(this.Name, args.Length, paramTypes.Length);
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (this.Subscriptions.Count == 0)
|
||||
return;
|
||||
var arg = args[i];
|
||||
var paramType = paramTypes[i];
|
||||
|
||||
foreach (var subscription in this.Subscriptions)
|
||||
if (arg == null)
|
||||
{
|
||||
var methodInfo = subscription.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
if (paramType.IsValueType)
|
||||
throw new IpcValueNullError(this.Name, paramType, i);
|
||||
|
||||
subscription.DynamicInvoke(args);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke an action registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Action arguments.</param>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
internal void InvokeAction(object?[]? args)
|
||||
{
|
||||
if (this.Action == null)
|
||||
throw new IpcNotReadyError(this.Name);
|
||||
|
||||
var methodInfo = this.Action.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
|
||||
this.Action.DynamicInvoke(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a function registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Func arguments.</param>
|
||||
/// <returns>The return value.</returns>
|
||||
/// <typeparam name="TRet">The return type.</typeparam>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
internal TRet InvokeFunc<TRet>(object?[]? args)
|
||||
{
|
||||
if (this.Func == null)
|
||||
throw new IpcNotReadyError(this.Name);
|
||||
|
||||
var methodInfo = this.Func.GetMethodInfo();
|
||||
this.CheckAndConvertArgs(args, methodInfo);
|
||||
|
||||
var result = this.Func.DynamicInvoke(args);
|
||||
|
||||
if (typeof(TRet) != methodInfo.ReturnType)
|
||||
result = this.ConvertObject(result, typeof(TRet));
|
||||
|
||||
return (TRet)result;
|
||||
}
|
||||
|
||||
private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo)
|
||||
{
|
||||
var paramTypes = methodInfo.GetParameters()
|
||||
.Select(pi => pi.ParameterType).ToArray();
|
||||
|
||||
if (args?.Length != paramTypes.Length)
|
||||
throw new IpcLengthMismatchError(this.Name, args.Length, paramTypes.Length);
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
var argType = arg.GetType();
|
||||
if (argType != paramType)
|
||||
{
|
||||
var arg = args[i];
|
||||
var paramType = paramTypes[i];
|
||||
|
||||
if (arg == null)
|
||||
// check the inheritance tree
|
||||
var baseTypes = this.GenerateTypes(argType.BaseType);
|
||||
if (baseTypes.Any(t => t == paramType))
|
||||
{
|
||||
if (paramType.IsValueType)
|
||||
throw new IpcValueNullError(this.Name, paramType, i);
|
||||
|
||||
// The source type inherits from the destination type
|
||||
continue;
|
||||
}
|
||||
|
||||
var argType = arg.GetType();
|
||||
if (argType != paramType)
|
||||
{
|
||||
// check the inheritance tree
|
||||
var baseTypes = this.GenerateTypes(argType.BaseType);
|
||||
if (baseTypes.Any(t => t == paramType))
|
||||
{
|
||||
// The source type inherits from the destination type
|
||||
continue;
|
||||
}
|
||||
|
||||
args[i] = this.ConvertObject(arg, paramType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Type> GenerateTypes(Type type)
|
||||
{
|
||||
while (type != null && type != typeof(object))
|
||||
{
|
||||
yield return type;
|
||||
type = type.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
private object? ConvertObject(object? obj, Type type)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Verbose($"Could not convert {obj.GetType().Name} to {type.Name}, will look for compatible type instead");
|
||||
}
|
||||
|
||||
// If type -> type fails, try to find an object that matches.
|
||||
var sourceType = obj.GetType();
|
||||
var fieldNames = sourceType.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Select(f => f.Name);
|
||||
var propNames = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Select(p => p.Name);
|
||||
|
||||
var assignableTypes = type.Assembly.GetTypes()
|
||||
.Where(t => type.IsAssignableFrom(t) && type != t)
|
||||
.ToArray();
|
||||
|
||||
foreach (var assignableType in assignableTypes)
|
||||
{
|
||||
var matchesFields = assignableType.GetFields().All(f => fieldNames.Contains(f.Name));
|
||||
var matchesProps = assignableType.GetProperties().All(p => propNames.Contains(p.Name));
|
||||
if (matchesFields && matchesProps)
|
||||
{
|
||||
type = assignableType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new IpcTypeMismatchError(this.Name, obj.GetType(), type, ex);
|
||||
args[i] = this.ConvertObject(arg, paramType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Type> GenerateTypes(Type type)
|
||||
{
|
||||
while (type != null && type != typeof(object))
|
||||
{
|
||||
yield return type;
|
||||
type = type.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
private object? ConvertObject(object? obj, Type type)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Verbose($"Could not convert {obj.GetType().Name} to {type.Name}, will look for compatible type instead");
|
||||
}
|
||||
|
||||
// If type -> type fails, try to find an object that matches.
|
||||
var sourceType = obj.GetType();
|
||||
var fieldNames = sourceType.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Select(f => f.Name);
|
||||
var propNames = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Select(p => p.Name);
|
||||
|
||||
var assignableTypes = type.Assembly.GetTypes()
|
||||
.Where(t => type.IsAssignableFrom(t) && type != t)
|
||||
.ToArray();
|
||||
|
||||
foreach (var assignableType in assignableTypes)
|
||||
{
|
||||
var matchesFields = assignableType.GetFields().All(f => fieldNames.Contains(f.Name));
|
||||
var matchesProps = assignableType.GetProperties().All(p => propNames.Contains(p.Name));
|
||||
if (matchesFields && matchesProps)
|
||||
{
|
||||
type = assignableType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject(json, type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new IpcTypeMismatchError(this.Name, obj.GetType(), type, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,349 +2,348 @@ using System;
|
|||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage()
|
||||
=> base.SendMessage();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction()
|
||||
=> base.InvokeAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc()
|
||||
=> this.InvokeFunc<TRet>();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage()
|
||||
=> base.SendMessage();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction()
|
||||
=> base.InvokeAction();
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc()
|
||||
=> this.InvokeFunc<TRet>();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1)
|
||||
=> base.SendMessage(arg1);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1)
|
||||
=> base.InvokeAction(arg1);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1)
|
||||
=> this.InvokeFunc<TRet>(arg1);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1)
|
||||
=> base.SendMessage(arg1);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1)
|
||||
=> base.InvokeAction(arg1);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1)
|
||||
=> this.InvokeFunc<TRet>(arg1);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2)
|
||||
=> base.SendMessage(arg1, arg2);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2)
|
||||
=> base.InvokeAction(arg1, arg2);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2)
|
||||
=> base.SendMessage(arg1, arg2);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2)
|
||||
=> base.InvokeAction(arg1, arg2);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> base.SendMessage(arg1, arg2, arg3);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> base.InvokeAction(arg1, arg2, arg3);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> base.SendMessage(arg1, arg2, arg3);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> base.InvokeAction(arg1, arg2, arg3);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase"/>
|
||||
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
|
||||
public CallGatePubSub(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, T8, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterAction"/>
|
||||
public void RegisterAction(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.RegisterAction(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.RegisterFunc"/>
|
||||
public void RegisterFunc(Func<T1, T2, T3, T4, T5, T6, T7, T8, TRet> func)
|
||||
=> base.RegisterFunc(func);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.SendMessage"/>
|
||||
public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> base.SendMessage(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Subscribe"/>
|
||||
public void Subscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.Subscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.Unsubscribe"/>
|
||||
public void Unsubscribe(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
||||
=> base.Unsubscribe(action);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeAction"/>
|
||||
public void InvokeAction(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> base.InvokeAction(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
|
||||
/// <inheritdoc cref="CallGatePubSubBase.InvokeFunc"/>
|
||||
public TRet InvokeFunc(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
|
||||
=> this.InvokeFunc<TRet>(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
}
|
||||
|
||||
#pragma warning restore SA1402 // File may only contain a single type
|
||||
|
|
|
|||
|
|
@ -2,90 +2,89 @@ using System;
|
|||
|
||||
using Dalamud.Plugin.Ipc.Exceptions;
|
||||
|
||||
namespace Dalamud.Plugin.Ipc.Internal
|
||||
namespace Dalamud.Plugin.Ipc.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// This class facilitates inter-plugin communication.
|
||||
/// </summary>
|
||||
internal abstract class CallGatePubSubBase
|
||||
{
|
||||
/// <summary>
|
||||
/// This class facilitates inter-plugin communication.
|
||||
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
|
||||
/// </summary>
|
||||
internal abstract class CallGatePubSubBase
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
public CallGatePubSubBase(string name)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the IPC registration.</param>
|
||||
public CallGatePubSubBase(string name)
|
||||
{
|
||||
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying channel implementation.
|
||||
/// </summary>
|
||||
protected CallGateChannel Channel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a registered Action from inter-plugin communication.
|
||||
/// </summary>
|
||||
public void UnregisterAction()
|
||||
=> this.Channel.Action = null;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a registered Func from inter-plugin communication.
|
||||
/// </summary>
|
||||
public void UnregisterFunc()
|
||||
=> this.Channel.Func = null;
|
||||
|
||||
/// <summary>
|
||||
/// Registers an Action for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to register.</param>
|
||||
private protected void RegisterAction(Delegate action)
|
||||
=> this.Channel.Action = action;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a Func for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="func">Func to register.</param>
|
||||
private protected void RegisterFunc(Delegate func)
|
||||
=> this.Channel.Func = func;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe an expression to this registration.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to subscribe.</param>
|
||||
private protected void Subscribe(Delegate action)
|
||||
=> this.Channel.Subscriptions.Add(action);
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe an expression from this registration.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to unsubscribe.</param>
|
||||
private protected void Unsubscribe(Delegate action)
|
||||
=> this.Channel.Subscriptions.Remove(action);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke an action registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Action arguments.</param>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered an action for calling yet.</exception>
|
||||
private protected void InvokeAction(params object?[]? args)
|
||||
=> this.Channel.InvokeAction(args);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a function registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Parameter args.</param>
|
||||
/// <returns>The return value.</returns>
|
||||
/// <typeparam name="TRet">The return type.</typeparam>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
private protected TRet InvokeFunc<TRet>(params object?[]? args)
|
||||
=> this.Channel.InvokeFunc<TRet>(args);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke all actions that have subscribed to this IPC.
|
||||
/// </summary>
|
||||
/// <param name="args">Delegate arguments.</param>
|
||||
private protected void SendMessage(params object?[]? args)
|
||||
=> this.Channel.SendMessage(args);
|
||||
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying channel implementation.
|
||||
/// </summary>
|
||||
protected CallGateChannel Channel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes a registered Action from inter-plugin communication.
|
||||
/// </summary>
|
||||
public void UnregisterAction()
|
||||
=> this.Channel.Action = null;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a registered Func from inter-plugin communication.
|
||||
/// </summary>
|
||||
public void UnregisterFunc()
|
||||
=> this.Channel.Func = null;
|
||||
|
||||
/// <summary>
|
||||
/// Registers an Action for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to register.</param>
|
||||
private protected void RegisterAction(Delegate action)
|
||||
=> this.Channel.Action = action;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a Func for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="func">Func to register.</param>
|
||||
private protected void RegisterFunc(Delegate func)
|
||||
=> this.Channel.Func = func;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe an expression to this registration.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to subscribe.</param>
|
||||
private protected void Subscribe(Delegate action)
|
||||
=> this.Channel.Subscriptions.Add(action);
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe an expression from this registration.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to unsubscribe.</param>
|
||||
private protected void Unsubscribe(Delegate action)
|
||||
=> this.Channel.Subscriptions.Remove(action);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke an action registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Action arguments.</param>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered an action for calling yet.</exception>
|
||||
private protected void InvokeAction(params object?[]? args)
|
||||
=> this.Channel.InvokeAction(args);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a function registered for inter-plugin communication.
|
||||
/// </summary>
|
||||
/// <param name="args">Parameter args.</param>
|
||||
/// <returns>The return value.</returns>
|
||||
/// <typeparam name="TRet">The return type.</typeparam>
|
||||
/// <exception cref="IpcNotReadyError">This is thrown when the IPC publisher has not registered a func for calling yet.</exception>
|
||||
private protected TRet InvokeFunc<TRet>(params object?[]? args)
|
||||
=> this.Channel.InvokeFunc<TRet>(args);
|
||||
|
||||
/// <summary>
|
||||
/// Invoke all actions that have subscribed to this IPC.
|
||||
/// </summary>
|
||||
/// <param name="args">Delegate arguments.</param>
|
||||
private protected void SendMessage(params object?[]? args)
|
||||
=> this.Channel.SendMessage(args);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,32 @@
|
|||
namespace Dalamud.Plugin
|
||||
namespace Dalamud.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// This enum reflects reasons for loading a plugin.
|
||||
/// </summary>
|
||||
public enum PluginLoadReason
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum reflects reasons for loading a plugin.
|
||||
/// We don't know why this plugin was loaded.
|
||||
/// </summary>
|
||||
public enum PluginLoadReason
|
||||
{
|
||||
/// <summary>
|
||||
/// We don't know why this plugin was loaded.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was installed with the plugin installer.
|
||||
/// </summary>
|
||||
Installer,
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was installed with the plugin installer.
|
||||
/// </summary>
|
||||
Installer,
|
||||
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was just updated.
|
||||
/// </summary>
|
||||
Update,
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was just updated.
|
||||
/// </summary>
|
||||
Update,
|
||||
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was told to reload.
|
||||
/// </summary>
|
||||
Reload,
|
||||
/// <summary>
|
||||
/// This plugin was loaded because it was told to reload.
|
||||
/// </summary>
|
||||
Reload,
|
||||
|
||||
/// <summary>
|
||||
/// This plugin was loaded because the game was started or Dalamud was reinjected.
|
||||
/// </summary>
|
||||
Boot,
|
||||
}
|
||||
/// <summary>
|
||||
/// This plugin was loaded because the game was started or Dalamud was reinjected.
|
||||
/// </summary>
|
||||
Boot,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue