mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 05:04:15 +01:00
Better Service dependency handling (#1535)
This commit is contained in:
parent
fcebd15077
commit
b66be84b93
25 changed files with 415 additions and 197 deletions
|
|
@ -21,7 +21,7 @@ namespace Dalamud.Configuration.Internal;
|
||||||
/// Class containing Dalamud settings.
|
/// Class containing Dalamud settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
#pragma warning disable SA1015
|
#pragma warning disable SA1015
|
||||||
[InherentDependency<ReliableFileStorage>] // We must still have this when unloading
|
[InherentDependency<ReliableFileStorage>] // We must still have this when unloading
|
||||||
#pragma warning restore SA1015
|
#pragma warning restore SA1015
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Dalamud;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main Dalamud class containing all subsystems.
|
/// The main Dalamud class containing all subsystems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
internal sealed class Dalamud : IServiceType
|
internal sealed class Dalamud : IServiceType
|
||||||
{
|
{
|
||||||
#region Internals
|
#region Internals
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Game.Addon.Events;
|
||||||
/// Service provider for addon event management.
|
/// Service provider for addon event management.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal unsafe class AddonEventManager : IDisposable, IServiceType
|
internal unsafe class AddonEventManager : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Game.Addon.Lifecycle;
|
||||||
/// This class provides events for in-game addon lifecycles.
|
/// This class provides events for in-game addon lifecycles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("AddonLifecycle");
|
private static readonly ModuleLog Log = new("AddonLifecycle");
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Dalamud.Game.Config;
|
||||||
/// This class represents the game's configuration.
|
/// This class represents the game's configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
|
internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
|
||||||
{
|
{
|
||||||
private readonly GameConfigAddressResolver address = new();
|
private readonly GameConfigAddressResolver address = new();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Dalamud.Game.DutyState;
|
||||||
/// This class represents the state of the currently occupied duty.
|
/// This class represents the state of the currently occupied duty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
|
internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
|
||||||
{
|
{
|
||||||
private readonly DutyStateAddressResolver address;
|
private readonly DutyStateAddressResolver address;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -22,14 +21,14 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader
|
||||||
|
|
||||||
private const string ApiKey = "GGD6RdSfGyRiHM5WDnAo0Nj9Nv7aC5NDhMj3BebT";
|
private const string ApiKey = "GGD6RdSfGyRiHM5WDnAo0Nj9Nv7aC5NDhMj3BebT";
|
||||||
|
|
||||||
private readonly HttpClient httpClient = Service<HappyHttpClient>.Get().SharedHttpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UniversalisMarketBoardUploader"/> class.
|
/// Initializes a new instance of the <see cref="UniversalisMarketBoardUploader"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UniversalisMarketBoardUploader()
|
/// <param name="happyHttpClient">An instance of <see cref="HappyHttpClient"/>.</param>
|
||||||
{
|
public UniversalisMarketBoardUploader(HappyHttpClient happyHttpClient) =>
|
||||||
}
|
this.httpClient = happyHttpClient.SharedHttpClient;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task Upload(MarketBoardItemRequest request)
|
public async Task Upload(MarketBoardItemRequest request)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Dalamud.Game.Network.Internal.MarketBoardUploaders;
|
||||||
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
|
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
|
||||||
using Dalamud.Game.Network.Structures;
|
using Dalamud.Game.Network.Structures;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.Networking.Http;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
@ -23,7 +24,7 @@ namespace Dalamud.Game.Network.Internal;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class handles network notifications and uploading market board data.
|
/// This class handles network notifications and uploading market board data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
private readonly IMarketBoardUploader uploader;
|
private readonly IMarketBoardUploader uploader;
|
||||||
|
|
@ -55,9 +56,12 @@ internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
||||||
private bool disposing;
|
private bool disposing;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private NetworkHandlers(GameNetwork gameNetwork, TargetSigScanner sigScanner)
|
private NetworkHandlers(
|
||||||
|
GameNetwork gameNetwork,
|
||||||
|
TargetSigScanner sigScanner,
|
||||||
|
HappyHttpClient happyHttpClient)
|
||||||
{
|
{
|
||||||
this.uploader = new UniversalisMarketBoardUploader();
|
this.uploader = new UniversalisMarketBoardUploader(happyHttpClient);
|
||||||
|
|
||||||
this.addressResolver = new NetworkHandlersAddressResolver();
|
this.addressResolver = new NetworkHandlersAddressResolver();
|
||||||
this.addressResolver.Setup(sigScanner);
|
this.addressResolver.Setup(sigScanner);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Dalamud.Game;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
#pragma warning disable SA1015
|
#pragma warning disable SA1015
|
||||||
[ResolveVia<ISigScanner>]
|
[ResolveVia<ISigScanner>]
|
||||||
#pragma warning restore SA1015
|
#pragma warning restore SA1015
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Dalamud.Interface.DragDrop;
|
||||||
/// and can be used to create ImGui drag and drop sources and targets for those external events.
|
/// and can be used to create ImGui drag and drop sources and targets for those external events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
#pragma warning disable SA1015
|
#pragma warning disable SA1015
|
||||||
[ResolveVia<IDragDropManager>]
|
[ResolveVia<IDragDropManager>]
|
||||||
#pragma warning restore SA1015
|
#pragma warning restore SA1015
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Dalamud.Interface.GameFonts;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads game font for use in ImGui.
|
/// Loads game font for use in ImGui.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal class GameFontManager : IServiceType
|
internal class GameFontManager : IServiceType
|
||||||
{
|
{
|
||||||
private static readonly string?[] FontNames =
|
private static readonly string?[] FontNames =
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -9,6 +7,7 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Game.Internal;
|
using Dalamud.Game.Internal;
|
||||||
|
|
@ -25,14 +24,14 @@ using Dalamud.Interface.Style;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Logging;
|
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Plugin.Internal;
|
using Dalamud.Plugin.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using ImGuiScene;
|
|
||||||
using ImPlotNET;
|
using ImPlotNET;
|
||||||
using PInvoke;
|
using PInvoke;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
@ -49,7 +48,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
|
|
||||||
private static readonly ModuleLog Log = new("DUI");
|
private static readonly ModuleLog Log = new("DUI");
|
||||||
|
|
||||||
|
private readonly Dalamud dalamud;
|
||||||
private readonly DalamudConfiguration configuration;
|
private readonly DalamudConfiguration configuration;
|
||||||
|
private readonly InterfaceManager interfaceManager;
|
||||||
|
|
||||||
private readonly ChangelogWindow changelogWindow;
|
private readonly ChangelogWindow changelogWindow;
|
||||||
private readonly ColorDemoWindow colorDemoWindow;
|
private readonly ColorDemoWindow colorDemoWindow;
|
||||||
|
|
@ -92,11 +93,16 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
DalamudConfiguration configuration,
|
DalamudConfiguration configuration,
|
||||||
InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene,
|
InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene,
|
||||||
PluginImageCache pluginImageCache,
|
PluginImageCache pluginImageCache,
|
||||||
Branding branding)
|
Branding branding,
|
||||||
|
Game.Framework framework,
|
||||||
|
ClientState clientState,
|
||||||
|
TitleScreenMenu titleScreenMenu,
|
||||||
|
GameGui gameGui)
|
||||||
{
|
{
|
||||||
|
this.dalamud = dalamud;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
|
this.interfaceManager = interfaceManagerWithScene.Manager;
|
||||||
|
|
||||||
var interfaceManager = interfaceManagerWithScene.Manager;
|
|
||||||
this.WindowSystem = new WindowSystem("DalamudCore");
|
this.WindowSystem = new WindowSystem("DalamudCore");
|
||||||
|
|
||||||
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
|
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
|
||||||
|
|
@ -104,13 +110,20 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
this.dataWindow = new DataWindow() { IsOpen = false };
|
this.dataWindow = new DataWindow() { IsOpen = false };
|
||||||
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { IsOpen = false };
|
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { IsOpen = false };
|
||||||
this.imeWindow = new ImeWindow() { IsOpen = false };
|
this.imeWindow = new ImeWindow() { IsOpen = false };
|
||||||
this.consoleWindow = new ConsoleWindow() { IsOpen = configuration.LogOpenAtStartup };
|
this.consoleWindow = new ConsoleWindow(configuration) { IsOpen = configuration.LogOpenAtStartup };
|
||||||
this.pluginStatWindow = new PluginStatWindow() { IsOpen = false };
|
this.pluginStatWindow = new PluginStatWindow() { IsOpen = false };
|
||||||
this.pluginWindow = new PluginInstallerWindow(pluginImageCache) { IsOpen = false };
|
this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false };
|
||||||
this.settingsWindow = new SettingsWindow() { IsOpen = false };
|
this.settingsWindow = new SettingsWindow() { IsOpen = false };
|
||||||
this.selfTestWindow = new SelfTestWindow() { IsOpen = false };
|
this.selfTestWindow = new SelfTestWindow() { IsOpen = false };
|
||||||
this.styleEditorWindow = new StyleEditorWindow() { IsOpen = false };
|
this.styleEditorWindow = new StyleEditorWindow() { IsOpen = false };
|
||||||
this.titleScreenMenuWindow = new TitleScreenMenuWindow() { IsOpen = false };
|
this.titleScreenMenuWindow = new TitleScreenMenuWindow(
|
||||||
|
clientState,
|
||||||
|
dalamud,
|
||||||
|
configuration,
|
||||||
|
framework,
|
||||||
|
gameGui,
|
||||||
|
this.interfaceManager,
|
||||||
|
titleScreenMenu) { IsOpen = false };
|
||||||
this.changelogWindow = new ChangelogWindow(this.titleScreenMenuWindow) { IsOpen = false };
|
this.changelogWindow = new ChangelogWindow(this.titleScreenMenuWindow) { IsOpen = false };
|
||||||
this.profilerWindow = new ProfilerWindow() { IsOpen = false };
|
this.profilerWindow = new ProfilerWindow() { IsOpen = false };
|
||||||
this.branchSwitcherWindow = new BranchSwitcherWindow() { IsOpen = false };
|
this.branchSwitcherWindow = new BranchSwitcherWindow() { IsOpen = false };
|
||||||
|
|
@ -136,7 +149,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
ImGuiManagedAsserts.AssertsEnabled = configuration.AssertsEnabledAtStartup;
|
ImGuiManagedAsserts.AssertsEnabled = configuration.AssertsEnabledAtStartup;
|
||||||
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
|
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
|
||||||
|
|
||||||
interfaceManager.Draw += this.OnDraw;
|
this.interfaceManager.Draw += this.OnDraw;
|
||||||
|
|
||||||
var tsm = Service<TitleScreenMenu>.Get();
|
var tsm = Service<TitleScreenMenu>.Get();
|
||||||
tsm.AddEntryCore(Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), branding.LogoSmall, () => this.OpenPluginInstaller());
|
tsm.AddEntryCore(Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), branding.LogoSmall, () => this.OpenPluginInstaller());
|
||||||
|
|
@ -173,7 +186,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Service<InterfaceManager>.Get().Draw -= this.OnDraw;
|
this.interfaceManager.Draw -= this.OnDraw;
|
||||||
|
|
||||||
this.WindowSystem.RemoveAllWindows();
|
this.WindowSystem.RemoveAllWindows();
|
||||||
|
|
||||||
|
|
@ -356,7 +369,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
/// Toggles the <see cref="DataWindow"/>.
|
/// Toggles the <see cref="DataWindow"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataKind">The data kind to switch to after opening.</param>
|
/// <param name="dataKind">The data kind to switch to after opening.</param>
|
||||||
public void ToggleDataWindow(string dataKind = null)
|
public void ToggleDataWindow(string? dataKind = null)
|
||||||
{
|
{
|
||||||
this.dataWindow.Toggle();
|
this.dataWindow.Toggle();
|
||||||
if (dataKind != null && this.dataWindow.IsOpen)
|
if (dataKind != null && this.dataWindow.IsOpen)
|
||||||
|
|
@ -378,7 +391,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Toggles the <see cref="ImeWindow"/>.
|
/// Toggles the <see cref="ImeWindow"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ToggleIMEWindow() => this.imeWindow.Toggle();
|
public void ToggleImeWindow() => this.imeWindow.Toggle();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Toggles the <see cref="ConsoleWindow"/>.
|
/// Toggles the <see cref="ConsoleWindow"/>.
|
||||||
|
|
@ -504,7 +517,8 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
|
|
||||||
private void DrawCreditsDarkeningAnimation()
|
private void DrawCreditsDarkeningAnimation()
|
||||||
{
|
{
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.WindowRounding | ImGuiStyleVar.WindowBorderSize, 0f);
|
using var style1 = ImRaii.PushStyle(ImGuiStyleVar.WindowRounding, 0f);
|
||||||
|
using var style2 = ImRaii.PushStyle(ImGuiStyleVar.WindowBorderSize, 0f);
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0, 0, 0, 0));
|
using var color = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0, 0, 0, 0));
|
||||||
|
|
||||||
ImGui.SetNextWindowPos(new Vector2(0, 0));
|
ImGui.SetNextWindowPos(new Vector2(0, 0));
|
||||||
|
|
@ -579,18 +593,16 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (ImGui.BeginMainMenuBar())
|
if (ImGui.BeginMainMenuBar())
|
||||||
{
|
{
|
||||||
var dalamud = Service<Dalamud>.Get();
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
var pluginManager = Service<PluginManager>.Get();
|
var pluginManager = Service<PluginManager>.Get();
|
||||||
|
|
||||||
if (ImGui.BeginMenu("Dalamud"))
|
if (ImGui.BeginMenu("Dalamud"))
|
||||||
{
|
{
|
||||||
ImGui.MenuItem("Draw dev menu", string.Empty, ref this.isImGuiDrawDevMenu);
|
ImGui.MenuItem("Draw dev menu", string.Empty, ref this.isImGuiDrawDevMenu);
|
||||||
var devBarAtStartup = configuration.DevBarOpenAtStartup;
|
var devBarAtStartup = this.configuration.DevBarOpenAtStartup;
|
||||||
if (ImGui.MenuItem("Draw dev menu at startup", string.Empty, ref devBarAtStartup))
|
if (ImGui.MenuItem("Draw dev menu at startup", string.Empty, ref devBarAtStartup))
|
||||||
{
|
{
|
||||||
configuration.DevBarOpenAtStartup ^= true;
|
this.configuration.DevBarOpenAtStartup ^= true;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
@ -607,25 +619,25 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
if (ImGui.MenuItem(logLevel + "##logLevelSwitch", string.Empty, EntryPoint.LogLevelSwitch.MinimumLevel == logLevel))
|
if (ImGui.MenuItem(logLevel + "##logLevelSwitch", string.Empty, EntryPoint.LogLevelSwitch.MinimumLevel == logLevel))
|
||||||
{
|
{
|
||||||
EntryPoint.LogLevelSwitch.MinimumLevel = logLevel;
|
EntryPoint.LogLevelSwitch.MinimumLevel = logLevel;
|
||||||
configuration.LogLevel = logLevel;
|
this.configuration.LogLevel = logLevel;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
var logSynchronously = configuration.LogSynchronously;
|
var logSynchronously = this.configuration.LogSynchronously;
|
||||||
if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously))
|
if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously))
|
||||||
{
|
{
|
||||||
configuration.LogSynchronously = logSynchronously;
|
this.configuration.LogSynchronously = logSynchronously;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
|
|
||||||
EntryPoint.InitLogging(
|
EntryPoint.InitLogging(
|
||||||
dalamud.StartInfo.LogPath!,
|
this.dalamud.StartInfo.LogPath!,
|
||||||
dalamud.StartInfo.BootShowConsole,
|
this.dalamud.StartInfo.BootShowConsole,
|
||||||
configuration.LogSynchronously,
|
this.configuration.LogSynchronously,
|
||||||
dalamud.StartInfo.LogName);
|
this.dalamud.StartInfo.LogName);
|
||||||
}
|
}
|
||||||
|
|
||||||
var antiDebug = Service<AntiDebug>.Get();
|
var antiDebug = Service<AntiDebug>.Get();
|
||||||
|
|
@ -637,8 +649,8 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
else
|
else
|
||||||
antiDebug.Disable();
|
antiDebug.Disable();
|
||||||
|
|
||||||
configuration.IsAntiAntiDebugEnabled = newEnabled;
|
this.configuration.IsAntiAntiDebugEnabled = newEnabled;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
@ -730,10 +742,10 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Report crashes at shutdown", null, configuration.ReportShutdownCrashes))
|
if (ImGui.MenuItem("Report crashes at shutdown", null, this.configuration.ReportShutdownCrashes))
|
||||||
{
|
{
|
||||||
configuration.ReportShutdownCrashes = !configuration.ReportShutdownCrashes;
|
this.configuration.ReportShutdownCrashes = !this.configuration.ReportShutdownCrashes;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
@ -744,7 +756,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.MenuItem(Util.AssemblyVersion, false);
|
ImGui.MenuItem(Util.AssemblyVersion, false);
|
||||||
ImGui.MenuItem(dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false);
|
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false);
|
||||||
ImGui.MenuItem($"D: {Util.GetGitHash()}[{Util.GetGitCommitCount()}] CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.Interop.Resolver.Version}]", false);
|
ImGui.MenuItem($"D: {Util.GetGitHash()}[{Util.GetGitCommitCount()}] CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.Interop.Resolver.Version}]", false);
|
||||||
ImGui.MenuItem($"CLR: {Environment.Version}", false);
|
ImGui.MenuItem($"CLR: {Environment.Version}", false);
|
||||||
|
|
||||||
|
|
@ -766,10 +778,10 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
ImGuiManagedAsserts.AssertsEnabled = val;
|
ImGuiManagedAsserts.AssertsEnabled = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Enable asserts at startup", null, configuration.AssertsEnabledAtStartup))
|
if (ImGui.MenuItem("Enable asserts at startup", null, this.configuration.AssertsEnabledAtStartup))
|
||||||
{
|
{
|
||||||
configuration.AssertsEnabledAtStartup = !configuration.AssertsEnabledAtStartup;
|
this.configuration.AssertsEnabledAtStartup = !this.configuration.AssertsEnabledAtStartup;
|
||||||
configuration.QueueSave();
|
this.configuration.QueueSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Clear focus"))
|
if (ImGui.MenuItem("Clear focus"))
|
||||||
|
|
@ -779,7 +791,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
|
|
||||||
if (ImGui.MenuItem("Clear stacks"))
|
if (ImGui.MenuItem("Clear stacks"))
|
||||||
{
|
{
|
||||||
Service<InterfaceManager>.Get().ClearStacks();
|
this.interfaceManager.ClearStacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Dump style"))
|
if (ImGui.MenuItem("Dump style"))
|
||||||
|
|
@ -792,7 +804,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (propertyInfo.PropertyType == typeof(Vector2))
|
if (propertyInfo.PropertyType == typeof(Vector2))
|
||||||
{
|
{
|
||||||
var vec2 = (Vector2)propertyInfo.GetValue(style);
|
var vec2 = (Vector2)propertyInfo.GetValue(style)!;
|
||||||
info += $"{propertyInfo.Name} = new Vector2({vec2.X.ToString(enCulture)}f, {vec2.Y.ToString(enCulture)}f),\n";
|
info += $"{propertyInfo.Name} = new Vector2({vec2.X.ToString(enCulture)}f, {vec2.Y.ToString(enCulture)}f),\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -815,9 +827,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
Log.Information(info);
|
Log.Information(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Show dev bar info", null, configuration.ShowDevBarInfo))
|
if (ImGui.MenuItem("Show dev bar info", null, this.configuration.ShowDevBarInfo))
|
||||||
{
|
{
|
||||||
configuration.ShowDevBarInfo = !configuration.ShowDevBarInfo;
|
this.configuration.ShowDevBarInfo = !this.configuration.ShowDevBarInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
|
|
@ -827,7 +839,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (ImGui.MenuItem("Replace ExceptionHandler"))
|
if (ImGui.MenuItem("Replace ExceptionHandler"))
|
||||||
{
|
{
|
||||||
dalamud.ReplaceExceptionHandler();
|
this.dalamud.ReplaceExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
|
|
@ -922,7 +934,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
if (Service<GameGui>.Get().GameUiHidden)
|
if (Service<GameGui>.Get().GameUiHidden)
|
||||||
ImGui.BeginMenu("UI is hidden...", false);
|
ImGui.BeginMenu("UI is hidden...", false);
|
||||||
|
|
||||||
if (configuration.ShowDevBarInfo)
|
if (this.configuration.ShowDevBarInfo)
|
||||||
{
|
{
|
||||||
ImGui.PushFont(InterfaceManager.MonoFont);
|
ImGui.PushFont(InterfaceManager.MonoFont);
|
||||||
|
|
||||||
|
|
@ -931,9 +943,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
ImGui.BeginMenu(ImGui.GetIO().Framerate.ToString("000"), false);
|
ImGui.BeginMenu(ImGui.GetIO().Framerate.ToString("000"), false);
|
||||||
ImGui.BeginMenu($"W:{Util.FormatBytes(GC.GetTotalMemory(false))}", false);
|
ImGui.BeginMenu($"W:{Util.FormatBytes(GC.GetTotalMemory(false))}", false);
|
||||||
|
|
||||||
var videoMem = Service<InterfaceManager>.Get().GetD3dMemoryInfo();
|
var videoMem = this.interfaceManager.GetD3dMemoryInfo();
|
||||||
ImGui.BeginMenu(
|
ImGui.BeginMenu(
|
||||||
!videoMem.HasValue ? $"V:???" : $"V:{Util.FormatBytes(videoMem.Value.Used)}",
|
!videoMem.HasValue ? "V:???" : $"V:{Util.FormatBytes(videoMem.Value.Used)}",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
|
||||||
|
|
@ -1285,7 +1285,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an instance of InstanceManager with scene ready for use.
|
/// Represents an instance of InstanceManager with scene ready for use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
public class InterfaceManagerWithScene : IServiceType
|
public class InterfaceManagerWithScene : IServiceType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
|
||||||
|
|
||||||
// If we are going to show a changelog, make sure we have the font ready, otherwise it will hitch
|
// If we are going to show a changelog, make sure we have the font ready, otherwise it will hitch
|
||||||
if (WarrantsChangelog())
|
if (WarrantsChangelog())
|
||||||
this.MakeFont();
|
Service<GameFontManager>.GetAsync().ContinueWith(t => this.MakeFont(t.Result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum State
|
private enum State
|
||||||
|
|
@ -98,7 +98,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
|
||||||
Service<DalamudInterface>.Get().SetCreditsDarkeningAnimation(true);
|
Service<DalamudInterface>.Get().SetCreditsDarkeningAnimation(true);
|
||||||
this.tsmWindow.AllowDrawing = false;
|
this.tsmWindow.AllowDrawing = false;
|
||||||
|
|
||||||
this.MakeFont();
|
this.MakeFont(Service<GameFontManager>.Get());
|
||||||
|
|
||||||
this.state = State.WindowFadeIn;
|
this.state = State.WindowFadeIn;
|
||||||
this.windowFade.Reset();
|
this.windowFade.Reset();
|
||||||
|
|
@ -379,12 +379,6 @@ internal sealed class ChangelogWindow : Window, IDisposable
|
||||||
this.logoTexture.Dispose();
|
this.logoTexture.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MakeFont()
|
private void MakeFont(GameFontManager gfm) =>
|
||||||
{
|
this.bannerFont ??= gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.MiedingerMid18));
|
||||||
if (this.bannerFont == null)
|
|
||||||
{
|
|
||||||
var gfm = Service<GameFontManager>.Get();
|
|
||||||
this.bannerFont = gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.MiedingerMid18));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,10 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ConsoleWindow"/> class.
|
/// Initializes a new instance of the <see cref="ConsoleWindow"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConsoleWindow()
|
/// <param name="configuration">An instance of <see cref="DalamudConfiguration"/>.</param>
|
||||||
|
public ConsoleWindow(DalamudConfiguration configuration)
|
||||||
: base("Dalamud Console", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
|
: base("Dalamud Console", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
this.autoScroll = configuration.LogAutoScroll;
|
this.autoScroll = configuration.LogAutoScroll;
|
||||||
this.autoOpen = configuration.LogOpenAtStartup;
|
this.autoOpen = configuration.LogOpenAtStartup;
|
||||||
SerilogEventSink.Instance.LogLine += this.OnLogLine;
|
SerilogEventSink.Instance.LogLine += this.OnLogLine;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
private string[] testerImagePaths = new string[5];
|
private string[] testerImagePaths = new string[5];
|
||||||
private string testerIconPath = string.Empty;
|
private string testerIconPath = string.Empty;
|
||||||
|
|
||||||
private IDalamudTextureWrap?[] testerImages;
|
private IDalamudTextureWrap?[]? testerImages;
|
||||||
private IDalamudTextureWrap? testerIcon;
|
private IDalamudTextureWrap? testerIcon;
|
||||||
|
|
||||||
private bool testerError = false;
|
private bool testerError = false;
|
||||||
|
|
@ -132,9 +132,10 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
/// Initializes a new instance of the <see cref="PluginInstallerWindow"/> class.
|
/// Initializes a new instance of the <see cref="PluginInstallerWindow"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="imageCache">An instance of <see cref="PluginImageCache"/> class.</param>
|
/// <param name="imageCache">An instance of <see cref="PluginImageCache"/> class.</param>
|
||||||
public PluginInstallerWindow(PluginImageCache imageCache)
|
/// <param name="configuration">An instance of <see cref="DalamudConfiguration"/>.</param>
|
||||||
|
public PluginInstallerWindow(PluginImageCache imageCache, DalamudConfiguration configuration)
|
||||||
: base(
|
: base(
|
||||||
Locs.WindowTitle + (Service<DalamudConfiguration>.Get().DoPluginTest ? Locs.WindowTitleMod_Testing : string.Empty) + "###XlPluginInstaller",
|
Locs.WindowTitle + (configuration.DoPluginTest ? Locs.WindowTitleMod_Testing : string.Empty) + "###XlPluginInstaller",
|
||||||
ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar)
|
ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar)
|
||||||
{
|
{
|
||||||
this.IsOpen = true;
|
this.IsOpen = true;
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using ImGuiScene;
|
|
||||||
|
|
||||||
namespace Dalamud.Interface.Internal.Windows;
|
namespace Dalamud.Interface.Internal.Windows;
|
||||||
|
|
||||||
|
|
@ -25,6 +25,12 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
private const float TargetFontSizePt = 18f;
|
private const float TargetFontSizePt = 18f;
|
||||||
private const float TargetFontSizePx = TargetFontSizePt * 4 / 3;
|
private const float TargetFontSizePx = TargetFontSizePt * 4 / 3;
|
||||||
|
|
||||||
|
private readonly ClientState clientState;
|
||||||
|
private readonly DalamudConfiguration configuration;
|
||||||
|
private readonly Framework framework;
|
||||||
|
private readonly GameGui gameGui;
|
||||||
|
private readonly TitleScreenMenu titleScreenMenu;
|
||||||
|
|
||||||
private readonly IDalamudTextureWrap shadeTexture;
|
private readonly IDalamudTextureWrap shadeTexture;
|
||||||
|
|
||||||
private readonly Dictionary<Guid, InOutCubic> shadeEasings = new();
|
private readonly Dictionary<Guid, InOutCubic> shadeEasings = new();
|
||||||
|
|
@ -39,12 +45,32 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TitleScreenMenuWindow"/> class.
|
/// Initializes a new instance of the <see cref="TitleScreenMenuWindow"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TitleScreenMenuWindow()
|
/// <param name="clientState">An instance of <see cref="ClientState"/>.</param>
|
||||||
|
/// <param name="dalamud">An instance of <see cref="Dalamud"/>.</param>
|
||||||
|
/// <param name="configuration">An instance of <see cref="DalamudConfiguration"/>.</param>
|
||||||
|
/// <param name="framework">An instance of <see cref="Framework"/>.</param>
|
||||||
|
/// <param name="interfaceManager">An instance of <see cref="InterfaceManager"/>.</param>
|
||||||
|
/// <param name="titleScreenMenu">An instance of <see cref="TitleScreenMenu"/>.</param>
|
||||||
|
/// <param name="gameGui">An instance of <see cref="gameGui"/>.</param>
|
||||||
|
public TitleScreenMenuWindow(
|
||||||
|
ClientState clientState,
|
||||||
|
Dalamud dalamud,
|
||||||
|
DalamudConfiguration configuration,
|
||||||
|
Framework framework,
|
||||||
|
GameGui gameGui,
|
||||||
|
InterfaceManager interfaceManager,
|
||||||
|
TitleScreenMenu titleScreenMenu)
|
||||||
: base(
|
: base(
|
||||||
"TitleScreenMenuOverlay",
|
"TitleScreenMenuOverlay",
|
||||||
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar |
|
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar |
|
||||||
ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus)
|
ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus)
|
||||||
{
|
{
|
||||||
|
this.clientState = clientState;
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.framework = framework;
|
||||||
|
this.gameGui = gameGui;
|
||||||
|
this.titleScreenMenu = titleScreenMenu;
|
||||||
|
|
||||||
this.IsOpen = true;
|
this.IsOpen = true;
|
||||||
this.DisableWindowSounds = true;
|
this.DisableWindowSounds = true;
|
||||||
this.ForceMainWindow = true;
|
this.ForceMainWindow = true;
|
||||||
|
|
@ -53,14 +79,10 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
this.PositionCondition = ImGuiCond.Always;
|
this.PositionCondition = ImGuiCond.Always;
|
||||||
this.RespectCloseHotkey = false;
|
this.RespectCloseHotkey = false;
|
||||||
|
|
||||||
var dalamud = Service<Dalamud>.Get();
|
|
||||||
var interfaceManager = Service<InterfaceManager>.Get();
|
|
||||||
|
|
||||||
var shadeTex =
|
var shadeTex =
|
||||||
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "tsmShade.png"));
|
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "tsmShade.png"));
|
||||||
this.shadeTexture = shadeTex ?? throw new Exception("Could not load TSM background texture.");
|
this.shadeTexture = shadeTex ?? throw new Exception("Could not load TSM background texture.");
|
||||||
|
|
||||||
var framework = Service<Framework>.Get();
|
|
||||||
framework.Update += this.FrameworkOnUpdate;
|
framework.Update += this.FrameworkOnUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,8 +117,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
this.shadeTexture.Dispose();
|
this.shadeTexture.Dispose();
|
||||||
var framework = Service<Framework>.Get();
|
this.framework.Update -= this.FrameworkOnUpdate;
|
||||||
framework.Update -= this.FrameworkOnUpdate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -106,9 +127,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var scale = ImGui.GetIO().FontGlobalScale;
|
var scale = ImGui.GetIO().FontGlobalScale;
|
||||||
var entries = Service<TitleScreenMenu>.Get().Entries
|
var entries = this.titleScreenMenu.Entries.OrderByDescending(x => x.IsInternal).ToList();
|
||||||
.OrderByDescending(x => x.IsInternal)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
switch (this.state)
|
switch (this.state)
|
||||||
{
|
{
|
||||||
|
|
@ -369,17 +388,14 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
|
|
||||||
private void FrameworkOnUpdate(IFramework framework)
|
private void FrameworkOnUpdate(IFramework framework)
|
||||||
{
|
{
|
||||||
var clientState = Service<ClientState>.Get();
|
this.IsOpen = !this.clientState.IsLoggedIn;
|
||||||
this.IsOpen = !clientState.IsLoggedIn;
|
|
||||||
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
if (!this.configuration.ShowTsm)
|
||||||
if (!configuration.ShowTsm)
|
|
||||||
this.IsOpen = false;
|
this.IsOpen = false;
|
||||||
|
|
||||||
var gameGui = Service<GameGui>.Get();
|
var charaSelect = this.gameGui.GetAddonByName("CharaSelect", 1);
|
||||||
var charaSelect = gameGui.GetAddonByName("CharaSelect", 1);
|
var charaMake = this.gameGui.GetAddonByName("CharaMake", 1);
|
||||||
var charaMake = gameGui.GetAddonByName("CharaMake", 1);
|
var titleDcWorldMap = this.gameGui.GetAddonByName("TitleDCWorldMap", 1);
|
||||||
var titleDcWorldMap = gameGui.GetAddonByName("TitleDCWorldMap", 1);
|
|
||||||
if (charaMake != IntPtr.Zero || charaSelect != IntPtr.Zero || titleDcWorldMap != IntPtr.Zero)
|
if (charaMake != IntPtr.Zero || charaSelect != IntPtr.Zero || titleDcWorldMap != IntPtr.Zero)
|
||||||
this.IsOpen = false;
|
this.IsOpen = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -16,7 +15,7 @@ namespace Dalamud.IoC.Internal;
|
||||||
/// This is only used to resolve dependencies for plugins.
|
/// This is only used to resolve dependencies for plugins.
|
||||||
/// Dalamud services are constructed via Service{T}.ConstructObject at the moment.
|
/// Dalamud services are constructed via Service{T}.ConstructObject at the moment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
internal class ServiceContainer : IServiceProvider, IServiceType
|
internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("SERVICECONTAINER");
|
private static readonly ModuleLog Log = new("SERVICECONTAINER");
|
||||||
|
|
@ -228,7 +227,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||||
serviceType = implementingType;
|
serviceType = implementingType;
|
||||||
|
|
||||||
if (serviceType.GetCustomAttribute<ServiceManager.ScopedService>() != null)
|
if (serviceType.GetCustomAttribute<ServiceManager.ScopedServiceAttribute>() != null)
|
||||||
{
|
{
|
||||||
if (scope == null)
|
if (scope == null)
|
||||||
{
|
{
|
||||||
|
|
@ -299,7 +298,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
var contains = types.Any(x => x.IsAssignableTo(type));
|
var contains = types.Any(x => x.IsAssignableTo(type));
|
||||||
|
|
||||||
// Scoped services are created on-demand
|
// Scoped services are created on-demand
|
||||||
return contains || type.GetCustomAttribute<ServiceManager.ScopedService>() != null;
|
return contains || type.GetCustomAttribute<ServiceManager.ScopedServiceAttribute>() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameters = ctor.GetParameters();
|
var parameters = ctor.GetParameters();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
@ -40,7 +39,7 @@ namespace Dalamud.Plugin.Internal;
|
||||||
/// Class responsible for loading and unloading plugins.
|
/// Class responsible for loading and unloading plugins.
|
||||||
/// NOTE: ALL plugin exposed services are marked as dependencies for PluginManager in Service{T}.
|
/// NOTE: ALL plugin exposed services are marked as dependencies for PluginManager in Service{T}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
#pragma warning disable SA1015
|
#pragma warning disable SA1015
|
||||||
|
|
||||||
// DalamudTextureWrap registers textures to dispose with IM
|
// DalamudTextureWrap registers textures to dispose with IM
|
||||||
|
|
@ -85,6 +84,9 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly HappyHttpClient happyHttpClient = Service<HappyHttpClient>.Get();
|
private readonly HappyHttpClient happyHttpClient = Service<HappyHttpClient>.Get();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly ChatGui chatGui = Service<ChatGui>.Get();
|
||||||
|
|
||||||
static PluginManager()
|
static PluginManager()
|
||||||
{
|
{
|
||||||
DalamudApiLevel = typeof(PluginManager).Assembly.GetName().Version!.Major;
|
DalamudApiLevel = typeof(PluginManager).Assembly.GetName().Version!.Major;
|
||||||
|
|
@ -131,12 +133,13 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
throw new InvalidDataException("Couldn't deserialize banned plugins manifest.");
|
throw new InvalidDataException("Couldn't deserialize banned plugins manifest.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openInstallerWindowPluginChangelogsLink = Service<ChatGui>.Get().AddChatLinkHandler("Dalamud", 1003, (_, _) =>
|
this.openInstallerWindowPluginChangelogsLink = this.chatGui.AddChatLinkHandler("Dalamud", 1003, (_, _) =>
|
||||||
{
|
{
|
||||||
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerWindow.PluginInstallerOpenKind.Changelogs);
|
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerWindow.PluginInstallerOpenKind.Changelogs);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.configuration.PluginTestingOptIns ??= new List<PluginTestingOptIn>();
|
this.configuration.PluginTestingOptIns ??= new();
|
||||||
|
this.MainRepo = PluginRepository.CreateMainRepo(this.happyHttpClient);
|
||||||
|
|
||||||
this.ApplyPatches();
|
this.ApplyPatches();
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +202,11 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the main repository.
|
||||||
|
/// </summary>
|
||||||
|
public PluginRepository MainRepo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of all plugin repositories. The main repo should always be first.
|
/// Gets a list of all plugin repositories. The main repo should always be first.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -284,11 +292,9 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
/// <param name="header">The header text to send to chat prior to any update info.</param>
|
/// <param name="header">The header text to send to chat prior to any update info.</param>
|
||||||
public void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
|
public void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
|
||||||
{
|
{
|
||||||
var chatGui = Service<ChatGui>.Get();
|
|
||||||
|
|
||||||
if (updateMetadata is { Count: > 0 })
|
if (updateMetadata is { Count: > 0 })
|
||||||
{
|
{
|
||||||
chatGui.Print(new XivChatEntry
|
this.chatGui.Print(new XivChatEntry
|
||||||
{
|
{
|
||||||
Message = new SeString(new List<Payload>()
|
Message = new SeString(new List<Payload>()
|
||||||
{
|
{
|
||||||
|
|
@ -307,11 +313,11 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (metadata.Status == PluginUpdateStatus.StatusKind.Success)
|
if (metadata.Status == PluginUpdateStatus.StatusKind.Success)
|
||||||
{
|
{
|
||||||
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
|
this.chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
chatGui.Print(new XivChatEntry
|
this.chatGui.Print(new XivChatEntry
|
||||||
{
|
{
|
||||||
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version, PluginUpdateStatus.LocalizeUpdateStatusKind(metadata.Status)),
|
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version, PluginUpdateStatus.LocalizeUpdateStatusKind(metadata.Status)),
|
||||||
Type = XivChatType.Urgent,
|
Type = XivChatType.Urgent,
|
||||||
|
|
@ -407,10 +413,10 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public async Task SetPluginReposFromConfigAsync(bool notify)
|
public async Task SetPluginReposFromConfigAsync(bool notify)
|
||||||
{
|
{
|
||||||
var repos = new List<PluginRepository>() { PluginRepository.MainRepo };
|
var repos = new List<PluginRepository> { this.MainRepo };
|
||||||
repos.AddRange(this.configuration.ThirdRepoList
|
repos.AddRange(this.configuration.ThirdRepoList
|
||||||
.Where(repo => repo.IsEnabled)
|
.Where(repo => repo.IsEnabled)
|
||||||
.Select(repo => new PluginRepository(repo.Url, repo.IsEnabled)));
|
.Select(repo => new PluginRepository(this.happyHttpClient, repo.Url, repo.IsEnabled)));
|
||||||
|
|
||||||
this.Repos = repos;
|
this.Repos = repos;
|
||||||
await this.ReloadPluginMastersAsync(notify);
|
await this.ReloadPluginMastersAsync(notify);
|
||||||
|
|
|
||||||
|
|
@ -267,10 +267,6 @@ internal class LocalPlugin : IDisposable
|
||||||
var pluginManager = await Service<PluginManager>.GetAsync();
|
var pluginManager = await Service<PluginManager>.GetAsync();
|
||||||
var dalamud = await Service<Dalamud>.GetAsync();
|
var dalamud = await Service<Dalamud>.GetAsync();
|
||||||
|
|
||||||
// UiBuilder constructor requires the following two.
|
|
||||||
await Service<InterfaceManager>.GetAsync();
|
|
||||||
await Service<GameFontManager>.GetAsync();
|
|
||||||
|
|
||||||
if (this.manifest.LoadRequiredState == 0)
|
if (this.manifest.LoadRequiredState == 0)
|
||||||
_ = await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync();
|
_ = await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,47 +28,44 @@ internal class PluginRepository
|
||||||
|
|
||||||
private static readonly ModuleLog Log = new("PLUGINR");
|
private static readonly ModuleLog Log = new("PLUGINR");
|
||||||
|
|
||||||
private static readonly HttpClient HttpClient = new(new SocketsHttpHandler
|
private readonly HttpClient httpClient;
|
||||||
{
|
|
||||||
AutomaticDecompression = DecompressionMethods.All,
|
|
||||||
ConnectCallback = Service<HappyHttpClient>.Get().SharedHappyEyeballsCallback.ConnectCallback,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
Timeout = TimeSpan.FromSeconds(20),
|
|
||||||
DefaultRequestHeaders =
|
|
||||||
{
|
|
||||||
Accept =
|
|
||||||
{
|
|
||||||
new MediaTypeWithQualityHeaderValue("application/json"),
|
|
||||||
},
|
|
||||||
CacheControl = new CacheControlHeaderValue
|
|
||||||
{
|
|
||||||
NoCache = true,
|
|
||||||
},
|
|
||||||
UserAgent =
|
|
||||||
{
|
|
||||||
new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PluginRepository"/> class.
|
/// Initializes a new instance of the <see cref="PluginRepository"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="happyHttpClient">An instance of <see cref="HappyHttpClient"/>.</param>
|
||||||
/// <param name="pluginMasterUrl">The plugin master URL.</param>
|
/// <param name="pluginMasterUrl">The plugin master URL.</param>
|
||||||
/// <param name="isEnabled">Whether the plugin repo is enabled.</param>
|
/// <param name="isEnabled">Whether the plugin repo is enabled.</param>
|
||||||
public PluginRepository(string pluginMasterUrl, bool isEnabled)
|
public PluginRepository(HappyHttpClient happyHttpClient, string pluginMasterUrl, bool isEnabled)
|
||||||
{
|
{
|
||||||
|
this.httpClient = new(new SocketsHttpHandler
|
||||||
|
{
|
||||||
|
AutomaticDecompression = DecompressionMethods.All,
|
||||||
|
ConnectCallback = happyHttpClient.SharedHappyEyeballsCallback.ConnectCallback,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(20),
|
||||||
|
DefaultRequestHeaders =
|
||||||
|
{
|
||||||
|
Accept =
|
||||||
|
{
|
||||||
|
new MediaTypeWithQualityHeaderValue("application/json"),
|
||||||
|
},
|
||||||
|
CacheControl = new CacheControlHeaderValue
|
||||||
|
{
|
||||||
|
NoCache = true,
|
||||||
|
},
|
||||||
|
UserAgent =
|
||||||
|
{
|
||||||
|
new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
this.PluginMasterUrl = pluginMasterUrl;
|
this.PluginMasterUrl = pluginMasterUrl;
|
||||||
this.IsThirdParty = pluginMasterUrl != MainRepoUrl;
|
this.IsThirdParty = pluginMasterUrl != MainRepoUrl;
|
||||||
this.IsEnabled = isEnabled;
|
this.IsEnabled = isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a new instance of the <see cref="PluginRepository"/> class for the main repo.
|
|
||||||
/// </summary>
|
|
||||||
public static PluginRepository MainRepo => new(MainRepoUrl, true);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the pluginmaster.json URL.
|
/// Gets the pluginmaster.json URL.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -94,6 +91,14 @@ internal class PluginRepository
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PluginRepositoryState State { get; private set; }
|
public PluginRepositoryState State { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a new instance of the <see cref="PluginRepository"/> class for the main repo.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="happyHttpClient">An instance of <see cref="HappyHttpClient"/>.</param>
|
||||||
|
/// <returns>The new instance of main repository.</returns>
|
||||||
|
public static PluginRepository CreateMainRepo(HappyHttpClient happyHttpClient) =>
|
||||||
|
new(happyHttpClient, MainRepoUrl, true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reload the plugin master asynchronously in a task.
|
/// Reload the plugin master asynchronously in a task.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -107,7 +112,7 @@ internal class PluginRepository
|
||||||
{
|
{
|
||||||
Log.Information($"Fetching repo: {this.PluginMasterUrl}");
|
Log.Information($"Fetching repo: {this.PluginMasterUrl}");
|
||||||
|
|
||||||
using var response = await HttpClient.GetAsync(this.PluginMasterUrl);
|
using var response = await this.httpClient.GetAsync(this.PluginMasterUrl);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var data = await response.Content.ReadAsStringAsync();
|
var data = await response.Content.ReadAsStringAsync();
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Dalamud.Plugin.Ipc.Internal;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class facilitates sharing data-references of standard types between plugins without using more expensive IPC.
|
/// This class facilitates sharing data-references of standard types between plugins without using more expensive IPC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal class DataShare : IServiceType
|
internal class DataShare : IServiceType
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, DataCache> caches = new();
|
private readonly Dictionary<string, DataCache> caches = new();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
@ -29,9 +30,17 @@ internal static class ServiceManager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ModuleLog Log = new("SVC");
|
public static readonly ModuleLog Log = new("SVC");
|
||||||
|
|
||||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
#if DEBUG
|
||||||
|
/// <summary>
|
||||||
|
/// Marks which service constructor the current thread's in. For use from <see cref="Service{T}"/> only.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly ThreadLocal<Type?> CurrentConstructorServiceType = new();
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "CollectionNeverQueried.Local", Justification = "Debugging purposes")]
|
||||||
private static readonly List<Type> LoadedServices = new();
|
private static readonly List<Type> LoadedServices = new();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||||
|
|
||||||
private static ManualResetEvent unloadResetEvent = new(false);
|
private static ManualResetEvent unloadResetEvent = new(false);
|
||||||
|
|
||||||
|
|
@ -86,21 +95,34 @@ internal static class ServiceManager
|
||||||
/// <param name="scanner">Instance of <see cref="TargetSigScanner"/>.</param>
|
/// <param name="scanner">Instance of <see cref="TargetSigScanner"/>.</param>
|
||||||
public static void InitializeProvidedServices(Dalamud dalamud, ReliableFileStorage fs, DalamudConfiguration configuration, TargetSigScanner scanner)
|
public static void InitializeProvidedServices(Dalamud dalamud, ReliableFileStorage fs, DalamudConfiguration configuration, TargetSigScanner scanner)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
lock (LoadedServices)
|
lock (LoadedServices)
|
||||||
{
|
{
|
||||||
void ProvideService<T>(T service) where T : IServiceType
|
|
||||||
{
|
|
||||||
Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ProvidedService), "Provided service must have Service attribute");
|
|
||||||
Service<T>.Provide(service);
|
|
||||||
LoadedServices.Add(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
ProvideService(dalamud);
|
ProvideService(dalamud);
|
||||||
ProvideService(fs);
|
ProvideService(fs);
|
||||||
ProvideService(configuration);
|
ProvideService(configuration);
|
||||||
ProvideService(new ServiceContainer());
|
ProvideService(new ServiceContainer());
|
||||||
ProvideService(scanner);
|
ProvideService(scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
void ProvideService<T>(T service) where T : IServiceType
|
||||||
|
{
|
||||||
|
Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ProvidedService), "Provided service must have Service attribute");
|
||||||
|
Service<T>.Provide(service);
|
||||||
|
LoadedServices.Add(typeof(T));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ProvideService(dalamud);
|
||||||
|
ProvideService(fs);
|
||||||
|
ProvideService(configuration);
|
||||||
|
ProvideService(new ServiceContainer());
|
||||||
|
ProvideService(scanner);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void ProvideService<T>(T service) where T : IServiceType => Service<T>.Provide(service);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -171,7 +193,22 @@ internal static class ServiceManager
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.WhenAll(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]));
|
var whenBlockingComplete = Task.WhenAll(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]));
|
||||||
|
while (await Task.WhenAny(whenBlockingComplete, Task.Delay(30000)) != whenBlockingComplete)
|
||||||
|
{
|
||||||
|
if (NativeFunctions.MessageBoxW(
|
||||||
|
IntPtr.Zero,
|
||||||
|
"Dalamud is taking a long time to load. Would you like to continue without Dalamud?\n" +
|
||||||
|
"This can be caused by a faulty plugin, or a bug in Dalamud.",
|
||||||
|
"Dalamud",
|
||||||
|
NativeFunctions.MessageBoxType.IconWarning | NativeFunctions.MessageBoxType.YesNo) == 6)
|
||||||
|
{
|
||||||
|
throw new TimeoutException(
|
||||||
|
"Failed to load services in the given time limit, " +
|
||||||
|
"and the user chose to continue without Dalamud.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||||
Timings.Event("BlockingServices Initialized");
|
Timings.Event("BlockingServices Initialized");
|
||||||
}
|
}
|
||||||
|
|
@ -215,13 +252,14 @@ internal static class ServiceManager
|
||||||
tasks.Add((Task)typeof(Service<>)
|
tasks.Add((Task)typeof(Service<>)
|
||||||
.MakeGenericType(serviceType)
|
.MakeGenericType(serviceType)
|
||||||
.InvokeMember(
|
.InvokeMember(
|
||||||
"StartLoader",
|
nameof(Service<IServiceType>.StartLoader),
|
||||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null));
|
null));
|
||||||
servicesToLoad.Remove(serviceType);
|
servicesToLoad.Remove(serviceType);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
tasks.Add(tasks.Last().ContinueWith(task =>
|
tasks.Add(tasks.Last().ContinueWith(task =>
|
||||||
{
|
{
|
||||||
if (task.IsFaulted)
|
if (task.IsFaulted)
|
||||||
|
|
@ -231,6 +269,7 @@ internal static class ServiceManager
|
||||||
LoadedServices.Add(serviceType);
|
LoadedServices.Add(serviceType);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tasks.Any())
|
if (!tasks.Any())
|
||||||
|
|
@ -350,10 +389,12 @@ internal static class ServiceManager
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
lock (LoadedServices)
|
lock (LoadedServices)
|
||||||
{
|
{
|
||||||
LoadedServices.Clear();
|
LoadedServices.Clear();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
unloadResetEvent.Set();
|
unloadResetEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +414,7 @@ internal static class ServiceManager
|
||||||
/// <returns>The type of service this type is.</returns>
|
/// <returns>The type of service this type is.</returns>
|
||||||
public static ServiceKind GetServiceKind(this Type type)
|
public static ServiceKind GetServiceKind(this Type type)
|
||||||
{
|
{
|
||||||
var attr = type.GetCustomAttribute<Service>(true)?.GetType();
|
var attr = type.GetCustomAttribute<ServiceAttribute>(true)?.GetType();
|
||||||
if (attr == null)
|
if (attr == null)
|
||||||
return ServiceKind.None;
|
return ServiceKind.None;
|
||||||
|
|
||||||
|
|
@ -381,13 +422,13 @@ internal static class ServiceManager
|
||||||
type.IsAssignableTo(typeof(IServiceType)),
|
type.IsAssignableTo(typeof(IServiceType)),
|
||||||
"Service did not inherit from IServiceType");
|
"Service did not inherit from IServiceType");
|
||||||
|
|
||||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedServiceAttribute)))
|
||||||
return ServiceKind.BlockingEarlyLoadedService;
|
return ServiceKind.BlockingEarlyLoadedService;
|
||||||
|
|
||||||
if (attr.IsAssignableTo(typeof(EarlyLoadedService)))
|
if (attr.IsAssignableTo(typeof(EarlyLoadedServiceAttribute)))
|
||||||
return ServiceKind.EarlyLoadedService;
|
return ServiceKind.EarlyLoadedService;
|
||||||
|
|
||||||
if (attr.IsAssignableTo(typeof(ScopedService)))
|
if (attr.IsAssignableTo(typeof(ScopedServiceAttribute)))
|
||||||
return ServiceKind.ScopedService;
|
return ServiceKind.ScopedService;
|
||||||
|
|
||||||
return ServiceKind.ProvidedService;
|
return ServiceKind.ProvidedService;
|
||||||
|
|
@ -414,16 +455,57 @@ internal static class ServiceManager
|
||||||
/// Indicates that the class is a service.
|
/// Indicates that the class is a service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class Service : Attribute
|
public abstract class ServiceAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The kind of the service.</param>
|
||||||
|
protected ServiceAttribute(ServiceKind kind) => this.Kind = kind;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the kind of the service.
|
||||||
|
/// </summary>
|
||||||
|
public ServiceKind Kind { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the class is a service, that is provided by some other source.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class ProvidedServiceAttribute : ServiceAttribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ProvidedServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ProvidedServiceAttribute()
|
||||||
|
: base(ServiceKind.ProvidedService)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class EarlyLoadedService : Service
|
public class EarlyLoadedServiceAttribute : ServiceAttribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EarlyLoadedServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public EarlyLoadedServiceAttribute()
|
||||||
|
: this(ServiceKind.EarlyLoadedService)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EarlyLoadedServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The service kind.</param>
|
||||||
|
protected EarlyLoadedServiceAttribute(ServiceKind kind)
|
||||||
|
: base(kind)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -431,8 +513,15 @@ internal static class ServiceManager
|
||||||
/// blocking game main thread until it completes.
|
/// blocking game main thread until it completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class BlockingEarlyLoadedService : EarlyLoadedService
|
public class BlockingEarlyLoadedServiceAttribute : EarlyLoadedServiceAttribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BlockingEarlyLoadedServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public BlockingEarlyLoadedServiceAttribute()
|
||||||
|
: base(ServiceKind.BlockingEarlyLoadedService)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -440,8 +529,15 @@ internal static class ServiceManager
|
||||||
/// service scope, and that it cannot be created outside of a scope.
|
/// service scope, and that it cannot be created outside of a scope.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class ScopedService : Service
|
public class ScopedServiceAttribute : ServiceAttribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ScopedServiceAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ScopedServiceAttribute()
|
||||||
|
: base(ServiceKind.ScopedService)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -20,17 +19,26 @@ namespace Dalamud;
|
||||||
/// Only used internally within Dalamud, if plugins need access to things it should be _only_ via DI.
|
/// Only used internally within Dalamud, if plugins need access to things it should be _only_ via DI.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <typeparam name="T">The class you want to store in the service locator.</typeparam>
|
/// <typeparam name="T">The class you want to store in the service locator.</typeparam>
|
||||||
|
[SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "Service container static type")]
|
||||||
internal static class Service<T> where T : IServiceType
|
internal static class Service<T> where T : IServiceType
|
||||||
{
|
{
|
||||||
|
private static readonly ServiceManager.ServiceAttribute ServiceAttribute;
|
||||||
private static TaskCompletionSource<T> instanceTcs = new();
|
private static TaskCompletionSource<T> instanceTcs = new();
|
||||||
|
private static List<Type>? dependencyServices;
|
||||||
|
|
||||||
static Service()
|
static Service()
|
||||||
{
|
{
|
||||||
var exposeToPlugins = typeof(T).GetCustomAttribute<PluginInterfaceAttribute>() != null;
|
var type = typeof(T);
|
||||||
|
ServiceAttribute =
|
||||||
|
type.GetCustomAttribute<ServiceManager.ServiceAttribute>(true)
|
||||||
|
?? throw new InvalidOperationException(
|
||||||
|
$"{nameof(T)} is missing {nameof(ServiceManager.ServiceAttribute)} annotations.");
|
||||||
|
|
||||||
|
var exposeToPlugins = type.GetCustomAttribute<PluginInterfaceAttribute>() != null;
|
||||||
if (exposeToPlugins)
|
if (exposeToPlugins)
|
||||||
ServiceManager.Log.Debug("Service<{0}>: Static ctor called; will be exposed to plugins", typeof(T).Name);
|
ServiceManager.Log.Debug("Service<{0}>: Static ctor called; will be exposed to plugins", type.Name);
|
||||||
else
|
else
|
||||||
ServiceManager.Log.Debug("Service<{0}>: Static ctor called", typeof(T).Name);
|
ServiceManager.Log.Debug("Service<{0}>: Static ctor called", type.Name);
|
||||||
|
|
||||||
if (exposeToPlugins)
|
if (exposeToPlugins)
|
||||||
Service<ServiceContainer>.Get().RegisterSingleton(instanceTcs.Task);
|
Service<ServiceContainer>.Get().RegisterSingleton(instanceTcs.Task);
|
||||||
|
|
@ -63,8 +71,8 @@ internal static class Service<T> where T : IServiceType
|
||||||
/// <param name="obj">Object to set.</param>
|
/// <param name="obj">Object to set.</param>
|
||||||
public static void Provide(T obj)
|
public static void Provide(T obj)
|
||||||
{
|
{
|
||||||
instanceTcs.SetResult(obj);
|
|
||||||
ServiceManager.Log.Debug("Service<{0}>: Provided", typeof(T).Name);
|
ServiceManager.Log.Debug("Service<{0}>: Provided", typeof(T).Name);
|
||||||
|
instanceTcs.SetResult(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -83,6 +91,21 @@ internal static class Service<T> where T : IServiceType
|
||||||
/// <returns>The object.</returns>
|
/// <returns>The object.</returns>
|
||||||
public static T Get()
|
public static T Get()
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (ServiceAttribute.Kind != ServiceManager.ServiceKind.ProvidedService
|
||||||
|
&& ServiceManager.CurrentConstructorServiceType.Value is { } currentServiceType)
|
||||||
|
{
|
||||||
|
var deps = ServiceHelpers.GetDependencies(currentServiceType);
|
||||||
|
if (!deps.Contains(typeof(T)))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Calling {nameof(Service<IServiceType>)}<{typeof(T)}>.{nameof(Get)} which is not one of the" +
|
||||||
|
$" dependency services is forbidden from the service constructor of {currentServiceType}." +
|
||||||
|
$" This has a high chance of introducing hard-to-debug hangs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!instanceTcs.Task.IsCompleted)
|
if (!instanceTcs.Task.IsCompleted)
|
||||||
instanceTcs.Task.Wait();
|
instanceTcs.Task.Wait();
|
||||||
return instanceTcs.Task.Result;
|
return instanceTcs.Task.Result;
|
||||||
|
|
@ -116,12 +139,16 @@ internal static class Service<T> where T : IServiceType
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an enumerable containing Service<T>s that are required for this Service to initialize without blocking.
|
/// Gets an enumerable containing <see cref="Service{T}"/>s that are required for this Service to initialize
|
||||||
|
/// without blocking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>List of dependency services.</returns>
|
/// <returns>List of dependency services.</returns>
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public static List<Type> GetDependencyServices()
|
public static List<Type> GetDependencyServices()
|
||||||
{
|
{
|
||||||
|
if (dependencyServices is not null)
|
||||||
|
return dependencyServices;
|
||||||
|
|
||||||
var res = new List<Type>();
|
var res = new List<Type>();
|
||||||
|
|
||||||
ServiceManager.Log.Verbose("Service<{0}>: Getting dependencies", typeof(T).Name);
|
ServiceManager.Log.Verbose("Service<{0}>: Getting dependencies", typeof(T).Name);
|
||||||
|
|
@ -189,19 +216,42 @@ internal static class Service<T> where T : IServiceType
|
||||||
ServiceManager.Log.Verbose("Service<{0}>: => Dependency: {1}", typeof(T).Name, type.Name);
|
ServiceManager.Log.Verbose("Service<{0}>: => Dependency: {1}", typeof(T).Name, type.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
var deps = res
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
if (typeof(T).GetCustomAttribute<ServiceManager.BlockingEarlyLoadedServiceAttribute>() is not null)
|
||||||
|
{
|
||||||
|
var offenders = deps.Where(
|
||||||
|
x => x.GetCustomAttribute<ServiceManager.ServiceAttribute>(true)!.Kind
|
||||||
|
is not ServiceManager.ServiceKind.BlockingEarlyLoadedService
|
||||||
|
and not ServiceManager.ServiceKind.ProvidedService)
|
||||||
|
.ToArray();
|
||||||
|
if (offenders.Any())
|
||||||
|
{
|
||||||
|
ServiceManager.Log.Error(
|
||||||
|
"{me} is a {bels}; it can only depend on {bels} and {ps}.\nOffending dependencies:\n{offenders}",
|
||||||
|
typeof(T),
|
||||||
|
nameof(ServiceManager.BlockingEarlyLoadedServiceAttribute),
|
||||||
|
nameof(ServiceManager.BlockingEarlyLoadedServiceAttribute),
|
||||||
|
nameof(ServiceManager.ProvidedServiceAttribute),
|
||||||
|
string.Join("\n", offenders.Select(x => $"\t* {x.Name}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencyServices = deps;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
/// <summary>
|
||||||
private static Task<T> StartLoader()
|
/// Starts the service loader. Only to be called from <see cref="ServiceManager"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The loader task.</returns>
|
||||||
|
internal static Task<T> StartLoader()
|
||||||
{
|
{
|
||||||
if (instanceTcs.Task.IsCompleted)
|
if (instanceTcs.Task.IsCompleted)
|
||||||
throw new InvalidOperationException($"{typeof(T).Name} is already loaded or disposed.");
|
throw new InvalidOperationException($"{typeof(T).Name} is already loaded or disposed.");
|
||||||
|
|
||||||
var attr = typeof(T).GetCustomAttribute<ServiceManager.Service>(true)?.GetType();
|
var attr = ServiceAttribute.GetType();
|
||||||
if (attr?.IsAssignableTo(typeof(ServiceManager.EarlyLoadedService)) != true)
|
if (attr.IsAssignableTo(typeof(ServiceManager.EarlyLoadedServiceAttribute)) != true)
|
||||||
throw new InvalidOperationException($"{typeof(T).Name} is not an EarlyLoadedService");
|
throw new InvalidOperationException($"{typeof(T).Name} is not an EarlyLoadedService");
|
||||||
|
|
||||||
return Task.Run(Timings.AttachTimingHandle(async () =>
|
return Task.Run(Timings.AttachTimingHandle(async () =>
|
||||||
|
|
@ -212,6 +262,7 @@ internal static class Service<T> where T : IServiceType
|
||||||
var instance = await ConstructObject();
|
var instance = await ConstructObject();
|
||||||
instanceTcs.SetResult(instance);
|
instanceTcs.SetResult(instance);
|
||||||
|
|
||||||
|
List<Task>? tasks = null;
|
||||||
foreach (var method in typeof(T).GetMethods(
|
foreach (var method in typeof(T).GetMethods(
|
||||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
{
|
{
|
||||||
|
|
@ -221,9 +272,24 @@ internal static class Service<T> where T : IServiceType
|
||||||
ServiceManager.Log.Debug("Service<{0}>: Calling {1}", typeof(T).Name, method.Name);
|
ServiceManager.Log.Debug("Service<{0}>: Calling {1}", typeof(T).Name, method.Name);
|
||||||
var args = await Task.WhenAll(method.GetParameters().Select(
|
var args = await Task.WhenAll(method.GetParameters().Select(
|
||||||
x => ResolveServiceFromTypeAsync(x.ParameterType)));
|
x => ResolveServiceFromTypeAsync(x.ParameterType)));
|
||||||
method.Invoke(instance, args);
|
try
|
||||||
|
{
|
||||||
|
if (method.Invoke(instance, args) is Task task)
|
||||||
|
{
|
||||||
|
tasks ??= new();
|
||||||
|
tasks.Add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
tasks ??= new();
|
||||||
|
tasks.Add(Task.FromException(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tasks is not null)
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
|
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +369,19 @@ internal static class Service<T> where T : IServiceType
|
||||||
ctor.GetParameters().Select(x => ResolveServiceFromTypeAsync(x.ParameterType)));
|
ctor.GetParameters().Select(x => ResolveServiceFromTypeAsync(x.ParameterType)));
|
||||||
using (Timings.Start($"{typeof(T).Name} Construct"))
|
using (Timings.Start($"{typeof(T).Name} Construct"))
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
ServiceManager.CurrentConstructorServiceType.Value = typeof(Service<T>);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (T)ctor.Invoke(args)!;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ServiceManager.CurrentConstructorServiceType.Value = null;
|
||||||
|
}
|
||||||
|
#else
|
||||||
return (T)ctor.Invoke(args)!;
|
return (T)ctor.Invoke(args)!;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,30 +406,43 @@ internal static class Service<T> where T : IServiceType
|
||||||
internal static class ServiceHelpers
|
internal static class ServiceHelpers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a list of dependencies for a service. Only accepts Service<T> types.
|
/// Get a list of dependencies for a service. Only accepts <see cref="Service{T}"/> types.
|
||||||
/// These are returned as Service<T> types.
|
/// These are returned as <see cref="Service{T}"/> types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serviceType">The dependencies for this service.</param>
|
/// <param name="serviceType">The dependencies for this service.</param>
|
||||||
/// <returns>A list of dependencies.</returns>
|
/// <returns>A list of dependencies.</returns>
|
||||||
public static List<Type> GetDependencies(Type serviceType)
|
public static List<Type> GetDependencies(Type serviceType)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (!serviceType.IsGenericType || serviceType.GetGenericTypeDefinition() != typeof(Service<>))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Expected an instance of {nameof(Service<IServiceType>)}<>",
|
||||||
|
nameof(serviceType));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return (List<Type>)serviceType.InvokeMember(
|
return (List<Type>)serviceType.InvokeMember(
|
||||||
"GetDependencyServices",
|
nameof(Service<IServiceType>.GetDependencyServices),
|
||||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null) ?? new List<Type>();
|
null) ?? new List<Type>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the Service<T> type for a given service type.
|
/// Get the <see cref="Service{T}"/> type for a given service type.
|
||||||
/// This will throw if the service type is not a valid service.
|
/// This will throw if the service type is not a valid service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to obtain a Service<T> for.</param>
|
/// <param name="type">The type to obtain a <see cref="Service{T}"/> for.</param>
|
||||||
/// <returns>The Service<T>.</returns>
|
/// <returns>The <see cref="Service{T}"/>.</returns>
|
||||||
public static Type GetAsService(Type type)
|
public static Type GetAsService(Type type)
|
||||||
{
|
{
|
||||||
return typeof(Service<>)
|
#if DEBUG
|
||||||
.MakeGenericType(type);
|
if (!type.IsAssignableTo(typeof(IServiceType)))
|
||||||
|
throw new ArgumentException($"Expected an instance of {nameof(IServiceType)}", nameof(type));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return typeof(Service<>).MakeGenericType(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Dalamud.Storage;
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is not an early-loaded service, as it is needed before they are initialized.
|
/// This is not an early-loaded service, as it is needed before they are initialized.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[ServiceManager.Service]
|
[ServiceManager.ProvidedService]
|
||||||
public class ReliableFileStorage : IServiceType, IDisposable
|
public class ReliableFileStorage : IServiceType, IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("VFS");
|
private static readonly ModuleLog Log = new("VFS");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue