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.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
#pragma warning disable SA1015
|
||||
[InherentDependency<ReliableFileStorage>] // We must still have this when unloading
|
||||
#pragma warning restore SA1015
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Dalamud;
|
|||
/// <summary>
|
||||
/// The main Dalamud class containing all subsystems.
|
||||
/// </summary>
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
internal sealed class Dalamud : IServiceType
|
||||
{
|
||||
#region Internals
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Game.Addon.Events;
|
|||
/// Service provider for addon event management.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal unsafe class AddonEventManager : IDisposable, IServiceType
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Game.Addon.Lifecycle;
|
|||
/// This class provides events for in-game addon lifecycles.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||
{
|
||||
private static readonly ModuleLog Log = new("AddonLifecycle");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Dalamud.Game.Config;
|
|||
/// This class represents the game's configuration.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
|
||||
{
|
||||
private readonly GameConfigAddressResolver address = new();
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Dalamud.Game.DutyState;
|
|||
/// This class represents the state of the currently occupied duty.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
|
||||
{
|
||||
private readonly DutyStateAddressResolver address;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
|
@ -22,14 +21,14 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader
|
|||
|
||||
private const string ApiKey = "GGD6RdSfGyRiHM5WDnAo0Nj9Nv7aC5NDhMj3BebT";
|
||||
|
||||
private readonly HttpClient httpClient = Service<HappyHttpClient>.Get().SharedHttpClient;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UniversalisMarketBoardUploader"/> class.
|
||||
/// </summary>
|
||||
public UniversalisMarketBoardUploader()
|
||||
{
|
||||
}
|
||||
/// <param name="happyHttpClient">An instance of <see cref="HappyHttpClient"/>.</param>
|
||||
public UniversalisMarketBoardUploader(HappyHttpClient happyHttpClient) =>
|
||||
this.httpClient = happyHttpClient.SharedHttpClient;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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.Structures;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Networking.Http;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
|
@ -23,7 +24,7 @@ namespace Dalamud.Game.Network.Internal;
|
|||
/// <summary>
|
||||
/// This class handles network notifications and uploading market board data.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
||||
{
|
||||
private readonly IMarketBoardUploader uploader;
|
||||
|
|
@ -55,9 +56,12 @@ internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
|||
private bool disposing;
|
||||
|
||||
[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.Setup(sigScanner);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Dalamud.Game;
|
|||
/// </summary>
|
||||
[PluginInterface]
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
#pragma warning disable SA1015
|
||||
[ResolveVia<ISigScanner>]
|
||||
#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.
|
||||
/// </summary>
|
||||
[PluginInterface]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
#pragma warning disable SA1015
|
||||
[ResolveVia<IDragDropManager>]
|
||||
#pragma warning restore SA1015
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Dalamud.Interface.GameFonts;
|
|||
/// <summary>
|
||||
/// Loads game font for use in ImGui.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal class GameFontManager : IServiceType
|
||||
{
|
||||
private static readonly string?[] FontNames =
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
|
|
@ -9,6 +7,7 @@ using System.Runtime.InteropServices;
|
|||
|
||||
using CheapLoc;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Internal;
|
||||
|
|
@ -25,14 +24,14 @@ using Dalamud.Interface.Style;
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
|
||||
using ImPlotNET;
|
||||
using PInvoke;
|
||||
using Serilog.Events;
|
||||
|
|
@ -49,7 +48,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
|
||||
private static readonly ModuleLog Log = new("DUI");
|
||||
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly DalamudConfiguration configuration;
|
||||
private readonly InterfaceManager interfaceManager;
|
||||
|
||||
private readonly ChangelogWindow changelogWindow;
|
||||
private readonly ColorDemoWindow colorDemoWindow;
|
||||
|
|
@ -92,11 +93,16 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
DalamudConfiguration configuration,
|
||||
InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene,
|
||||
PluginImageCache pluginImageCache,
|
||||
Branding branding)
|
||||
Branding branding,
|
||||
Game.Framework framework,
|
||||
ClientState clientState,
|
||||
TitleScreenMenu titleScreenMenu,
|
||||
GameGui gameGui)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
this.configuration = configuration;
|
||||
this.interfaceManager = interfaceManagerWithScene.Manager;
|
||||
|
||||
var interfaceManager = interfaceManagerWithScene.Manager;
|
||||
this.WindowSystem = new WindowSystem("DalamudCore");
|
||||
|
||||
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
|
||||
|
|
@ -104,13 +110,20 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
this.dataWindow = new DataWindow() { IsOpen = false };
|
||||
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { 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.pluginWindow = new PluginInstallerWindow(pluginImageCache) { IsOpen = false };
|
||||
this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false };
|
||||
this.settingsWindow = new SettingsWindow() { IsOpen = false };
|
||||
this.selfTestWindow = new SelfTestWindow() { 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.profilerWindow = new ProfilerWindow() { IsOpen = false };
|
||||
this.branchSwitcherWindow = new BranchSwitcherWindow() { IsOpen = false };
|
||||
|
|
@ -136,7 +149,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
ImGuiManagedAsserts.AssertsEnabled = configuration.AssertsEnabledAtStartup;
|
||||
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
|
||||
|
||||
interfaceManager.Draw += this.OnDraw;
|
||||
this.interfaceManager.Draw += this.OnDraw;
|
||||
|
||||
var tsm = Service<TitleScreenMenu>.Get();
|
||||
tsm.AddEntryCore(Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), branding.LogoSmall, () => this.OpenPluginInstaller());
|
||||
|
|
@ -173,7 +186,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
Service<InterfaceManager>.Get().Draw -= this.OnDraw;
|
||||
this.interfaceManager.Draw -= this.OnDraw;
|
||||
|
||||
this.WindowSystem.RemoveAllWindows();
|
||||
|
||||
|
|
@ -356,7 +369,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// Toggles the <see cref="DataWindow"/>.
|
||||
/// </summary>
|
||||
/// <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();
|
||||
if (dataKind != null && this.dataWindow.IsOpen)
|
||||
|
|
@ -378,7 +391,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// <summary>
|
||||
/// Toggles the <see cref="ImeWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleIMEWindow() => this.imeWindow.Toggle();
|
||||
public void ToggleImeWindow() => this.imeWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="ConsoleWindow"/>.
|
||||
|
|
@ -504,7 +517,8 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
|
||||
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));
|
||||
|
||||
ImGui.SetNextWindowPos(new Vector2(0, 0));
|
||||
|
|
@ -579,18 +593,16 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
{
|
||||
if (ImGui.BeginMainMenuBar())
|
||||
{
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
|
||||
if (ImGui.BeginMenu("Dalamud"))
|
||||
{
|
||||
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))
|
||||
{
|
||||
configuration.DevBarOpenAtStartup ^= true;
|
||||
configuration.QueueSave();
|
||||
this.configuration.DevBarOpenAtStartup ^= true;
|
||||
this.configuration.QueueSave();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
|
@ -607,25 +619,25 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
if (ImGui.MenuItem(logLevel + "##logLevelSwitch", string.Empty, EntryPoint.LogLevelSwitch.MinimumLevel == logLevel))
|
||||
{
|
||||
EntryPoint.LogLevelSwitch.MinimumLevel = logLevel;
|
||||
configuration.LogLevel = logLevel;
|
||||
configuration.QueueSave();
|
||||
this.configuration.LogLevel = logLevel;
|
||||
this.configuration.QueueSave();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
var logSynchronously = configuration.LogSynchronously;
|
||||
var logSynchronously = this.configuration.LogSynchronously;
|
||||
if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously))
|
||||
{
|
||||
configuration.LogSynchronously = logSynchronously;
|
||||
configuration.QueueSave();
|
||||
this.configuration.LogSynchronously = logSynchronously;
|
||||
this.configuration.QueueSave();
|
||||
|
||||
EntryPoint.InitLogging(
|
||||
dalamud.StartInfo.LogPath!,
|
||||
dalamud.StartInfo.BootShowConsole,
|
||||
configuration.LogSynchronously,
|
||||
dalamud.StartInfo.LogName);
|
||||
this.dalamud.StartInfo.LogPath!,
|
||||
this.dalamud.StartInfo.BootShowConsole,
|
||||
this.configuration.LogSynchronously,
|
||||
this.dalamud.StartInfo.LogName);
|
||||
}
|
||||
|
||||
var antiDebug = Service<AntiDebug>.Get();
|
||||
|
|
@ -637,8 +649,8 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
else
|
||||
antiDebug.Disable();
|
||||
|
||||
configuration.IsAntiAntiDebugEnabled = newEnabled;
|
||||
configuration.QueueSave();
|
||||
this.configuration.IsAntiAntiDebugEnabled = newEnabled;
|
||||
this.configuration.QueueSave();
|
||||
}
|
||||
|
||||
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;
|
||||
configuration.QueueSave();
|
||||
this.configuration.ReportShutdownCrashes = !this.configuration.ReportShutdownCrashes;
|
||||
this.configuration.QueueSave();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
|
@ -744,7 +756,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
}
|
||||
|
||||
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($"CLR: {Environment.Version}", false);
|
||||
|
||||
|
|
@ -766,10 +778,10 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
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;
|
||||
configuration.QueueSave();
|
||||
this.configuration.AssertsEnabledAtStartup = !this.configuration.AssertsEnabledAtStartup;
|
||||
this.configuration.QueueSave();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Clear focus"))
|
||||
|
|
@ -779,7 +791,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
|
||||
if (ImGui.MenuItem("Clear stacks"))
|
||||
{
|
||||
Service<InterfaceManager>.Get().ClearStacks();
|
||||
this.interfaceManager.ClearStacks();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Dump style"))
|
||||
|
|
@ -792,7 +804,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
{
|
||||
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";
|
||||
}
|
||||
else
|
||||
|
|
@ -815,9 +827,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
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();
|
||||
|
|
@ -827,7 +839,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
{
|
||||
if (ImGui.MenuItem("Replace ExceptionHandler"))
|
||||
{
|
||||
dalamud.ReplaceExceptionHandler();
|
||||
this.dalamud.ReplaceExceptionHandler();
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
|
|
@ -922,7 +934,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
if (Service<GameGui>.Get().GameUiHidden)
|
||||
ImGui.BeginMenu("UI is hidden...", false);
|
||||
|
||||
if (configuration.ShowDevBarInfo)
|
||||
if (this.configuration.ShowDevBarInfo)
|
||||
{
|
||||
ImGui.PushFont(InterfaceManager.MonoFont);
|
||||
|
||||
|
|
@ -931,9 +943,9 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
ImGui.BeginMenu(ImGui.GetIO().Framerate.ToString("000"), false);
|
||||
ImGui.BeginMenu($"W:{Util.FormatBytes(GC.GetTotalMemory(false))}", false);
|
||||
|
||||
var videoMem = Service<InterfaceManager>.Get().GetD3dMemoryInfo();
|
||||
var videoMem = this.interfaceManager.GetD3dMemoryInfo();
|
||||
ImGui.BeginMenu(
|
||||
!videoMem.HasValue ? $"V:???" : $"V:{Util.FormatBytes(videoMem.Value.Used)}",
|
||||
!videoMem.HasValue ? "V:???" : $"V:{Util.FormatBytes(videoMem.Value.Used)}",
|
||||
false);
|
||||
|
||||
ImGui.PopFont();
|
||||
|
|
|
|||
|
|
@ -1285,7 +1285,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
/// <summary>
|
||||
/// Represents an instance of InstanceManager with scene ready for use.
|
||||
/// </summary>
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
public class InterfaceManagerWithScene : IServiceType
|
||||
{
|
||||
/// <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 (WarrantsChangelog())
|
||||
this.MakeFont();
|
||||
Service<GameFontManager>.GetAsync().ContinueWith(t => this.MakeFont(t.Result));
|
||||
}
|
||||
|
||||
private enum State
|
||||
|
|
@ -98,7 +98,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
|
|||
Service<DalamudInterface>.Get().SetCreditsDarkeningAnimation(true);
|
||||
this.tsmWindow.AllowDrawing = false;
|
||||
|
||||
this.MakeFont();
|
||||
this.MakeFont(Service<GameFontManager>.Get());
|
||||
|
||||
this.state = State.WindowFadeIn;
|
||||
this.windowFade.Reset();
|
||||
|
|
@ -379,12 +379,6 @@ internal sealed class ChangelogWindow : Window, IDisposable
|
|||
this.logoTexture.Dispose();
|
||||
}
|
||||
|
||||
private void MakeFont()
|
||||
{
|
||||
if (this.bannerFont == null)
|
||||
{
|
||||
var gfm = Service<GameFontManager>.Get();
|
||||
this.bannerFont = gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.MiedingerMid18));
|
||||
}
|
||||
}
|
||||
private void MakeFont(GameFontManager gfm) =>
|
||||
this.bannerFont ??= gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.MiedingerMid18));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,11 +56,10 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsoleWindow"/> class.
|
||||
/// </summary>
|
||||
public ConsoleWindow()
|
||||
/// <param name="configuration">An instance of <see cref="DalamudConfiguration"/>.</param>
|
||||
public ConsoleWindow(DalamudConfiguration configuration)
|
||||
: base("Dalamud Console", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
|
||||
{
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
this.autoScroll = configuration.LogAutoScroll;
|
||||
this.autoOpen = configuration.LogOpenAtStartup;
|
||||
SerilogEventSink.Instance.LogLine += this.OnLogLine;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
|||
private string[] testerImagePaths = new string[5];
|
||||
private string testerIconPath = string.Empty;
|
||||
|
||||
private IDalamudTextureWrap?[] testerImages;
|
||||
private IDalamudTextureWrap?[]? testerImages;
|
||||
private IDalamudTextureWrap? testerIcon;
|
||||
|
||||
private bool testerError = false;
|
||||
|
|
@ -132,9 +132,10 @@ internal class PluginInstallerWindow : Window, IDisposable
|
|||
/// Initializes a new instance of the <see cref="PluginInstallerWindow"/> class.
|
||||
/// </summary>
|
||||
/// <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(
|
||||
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)
|
||||
{
|
||||
this.IsOpen = true;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ using Dalamud.Interface.Utility;
|
|||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows;
|
||||
|
||||
|
|
@ -25,6 +25,12 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
private const float TargetFontSizePt = 18f;
|
||||
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 Dictionary<Guid, InOutCubic> shadeEasings = new();
|
||||
|
|
@ -39,12 +45,32 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TitleScreenMenuWindow"/> class.
|
||||
/// </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(
|
||||
"TitleScreenMenuOverlay",
|
||||
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar |
|
||||
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.DisableWindowSounds = true;
|
||||
this.ForceMainWindow = true;
|
||||
|
|
@ -53,14 +79,10 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
this.PositionCondition = ImGuiCond.Always;
|
||||
this.RespectCloseHotkey = false;
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
var interfaceManager = Service<InterfaceManager>.Get();
|
||||
|
||||
var shadeTex =
|
||||
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "tsmShade.png"));
|
||||
this.shadeTexture = shadeTex ?? throw new Exception("Could not load TSM background texture.");
|
||||
|
||||
var framework = Service<Framework>.Get();
|
||||
framework.Update += this.FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +117,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
public void Dispose()
|
||||
{
|
||||
this.shadeTexture.Dispose();
|
||||
var framework = Service<Framework>.Get();
|
||||
framework.Update -= this.FrameworkOnUpdate;
|
||||
this.framework.Update -= this.FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -106,9 +127,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
return;
|
||||
|
||||
var scale = ImGui.GetIO().FontGlobalScale;
|
||||
var entries = Service<TitleScreenMenu>.Get().Entries
|
||||
.OrderByDescending(x => x.IsInternal)
|
||||
.ToList();
|
||||
var entries = this.titleScreenMenu.Entries.OrderByDescending(x => x.IsInternal).ToList();
|
||||
|
||||
switch (this.state)
|
||||
{
|
||||
|
|
@ -369,17 +388,14 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
|||
|
||||
private void FrameworkOnUpdate(IFramework framework)
|
||||
{
|
||||
var clientState = Service<ClientState>.Get();
|
||||
this.IsOpen = !clientState.IsLoggedIn;
|
||||
this.IsOpen = !this.clientState.IsLoggedIn;
|
||||
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
if (!configuration.ShowTsm)
|
||||
if (!this.configuration.ShowTsm)
|
||||
this.IsOpen = false;
|
||||
|
||||
var gameGui = Service<GameGui>.Get();
|
||||
var charaSelect = gameGui.GetAddonByName("CharaSelect", 1);
|
||||
var charaMake = gameGui.GetAddonByName("CharaMake", 1);
|
||||
var titleDcWorldMap = gameGui.GetAddonByName("TitleDCWorldMap", 1);
|
||||
var charaSelect = this.gameGui.GetAddonByName("CharaSelect", 1);
|
||||
var charaMake = this.gameGui.GetAddonByName("CharaMake", 1);
|
||||
var titleDcWorldMap = this.gameGui.GetAddonByName("TitleDCWorldMap", 1);
|
||||
if (charaMake != IntPtr.Zero || charaSelect != IntPtr.Zero || titleDcWorldMap != IntPtr.Zero)
|
||||
this.IsOpen = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
|
@ -16,7 +15,7 @@ namespace Dalamud.IoC.Internal;
|
|||
/// This is only used to resolve dependencies for plugins.
|
||||
/// Dalamud services are constructed via Service{T}.ConstructObject at the moment.
|
||||
/// </summary>
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
internal class ServiceContainer : IServiceProvider, IServiceType
|
||||
{
|
||||
private static readonly ModuleLog Log = new("SERVICECONTAINER");
|
||||
|
|
@ -228,7 +227,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||
serviceType = implementingType;
|
||||
|
||||
if (serviceType.GetCustomAttribute<ServiceManager.ScopedService>() != null)
|
||||
if (serviceType.GetCustomAttribute<ServiceManager.ScopedServiceAttribute>() != null)
|
||||
{
|
||||
if (scope == null)
|
||||
{
|
||||
|
|
@ -299,7 +298,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
var contains = types.Any(x => x.IsAssignableTo(type));
|
||||
|
||||
// Scoped services are created on-demand
|
||||
return contains || type.GetCustomAttribute<ServiceManager.ScopedService>() != null;
|
||||
return contains || type.GetCustomAttribute<ServiceManager.ScopedServiceAttribute>() != null;
|
||||
}
|
||||
|
||||
var parameters = ctor.GetParameters();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
|
@ -40,7 +39,7 @@ namespace Dalamud.Plugin.Internal;
|
|||
/// Class responsible for loading and unloading plugins.
|
||||
/// NOTE: ALL plugin exposed services are marked as dependencies for PluginManager in Service{T}.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
#pragma warning disable SA1015
|
||||
|
||||
// DalamudTextureWrap registers textures to dispose with IM
|
||||
|
|
@ -85,6 +84,9 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
[ServiceManager.ServiceDependency]
|
||||
private readonly HappyHttpClient happyHttpClient = Service<HappyHttpClient>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly ChatGui chatGui = Service<ChatGui>.Get();
|
||||
|
||||
static PluginManager()
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
this.openInstallerWindowPluginChangelogsLink = Service<ChatGui>.Get().AddChatLinkHandler("Dalamud", 1003, (_, _) =>
|
||||
this.openInstallerWindowPluginChangelogsLink = this.chatGui.AddChatLinkHandler("Dalamud", 1003, (_, _) =>
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
|
@ -199,6 +202,11 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main repository.
|
||||
/// </summary>
|
||||
public PluginRepository MainRepo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all plugin repositories. The main repo should always be first.
|
||||
/// </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>
|
||||
public void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
|
||||
{
|
||||
var chatGui = Service<ChatGui>.Get();
|
||||
|
||||
if (updateMetadata is { Count: > 0 })
|
||||
{
|
||||
chatGui.Print(new XivChatEntry
|
||||
this.chatGui.Print(new XivChatEntry
|
||||
{
|
||||
Message = new SeString(new List<Payload>()
|
||||
{
|
||||
|
|
@ -307,11 +313,11 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
{
|
||||
if (metadata.Status == PluginUpdateStatus.StatusKind.Success)
|
||||
{
|
||||
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
|
||||
this.chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
|
||||
}
|
||||
else
|
||||
{
|
||||
chatGui.Print(new XivChatEntry
|
||||
this.chatGui.Print(new XivChatEntry
|
||||
{
|
||||
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version, PluginUpdateStatus.LocalizeUpdateStatusKind(metadata.Status)),
|
||||
Type = XivChatType.Urgent,
|
||||
|
|
@ -407,10 +413,10 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
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
|
||||
.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;
|
||||
await this.ReloadPluginMastersAsync(notify);
|
||||
|
|
|
|||
|
|
@ -267,10 +267,6 @@ internal class LocalPlugin : IDisposable
|
|||
var pluginManager = await Service<PluginManager>.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)
|
||||
_ = await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,20 @@ internal class PluginRepository
|
|||
|
||||
private static readonly ModuleLog Log = new("PLUGINR");
|
||||
|
||||
private static readonly HttpClient HttpClient = new(new SocketsHttpHandler
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="happyHttpClient">An instance of <see cref="HappyHttpClient"/>.</param>
|
||||
/// <param name="pluginMasterUrl">The plugin master URL.</param>
|
||||
/// <param name="isEnabled">Whether the plugin repo is enabled.</param>
|
||||
public PluginRepository(HappyHttpClient happyHttpClient, string pluginMasterUrl, bool isEnabled)
|
||||
{
|
||||
this.httpClient = new(new SocketsHttpHandler
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.All,
|
||||
ConnectCallback = Service<HappyHttpClient>.Get().SharedHappyEyeballsCallback.ConnectCallback,
|
||||
ConnectCallback = happyHttpClient.SharedHappyEyeballsCallback.ConnectCallback,
|
||||
})
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
|
|
@ -51,24 +61,11 @@ internal class PluginRepository
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pluginMasterUrl">The plugin master URL.</param>
|
||||
/// <param name="isEnabled">Whether the plugin repo is enabled.</param>
|
||||
public PluginRepository(string pluginMasterUrl, bool isEnabled)
|
||||
{
|
||||
this.PluginMasterUrl = pluginMasterUrl;
|
||||
this.IsThirdParty = pluginMasterUrl != MainRepoUrl;
|
||||
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>
|
||||
/// Gets the pluginmaster.json URL.
|
||||
/// </summary>
|
||||
|
|
@ -94,6 +91,14 @@ internal class PluginRepository
|
|||
/// </summary>
|
||||
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>
|
||||
/// Reload the plugin master asynchronously in a task.
|
||||
/// </summary>
|
||||
|
|
@ -107,7 +112,7 @@ internal class PluginRepository
|
|||
{
|
||||
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();
|
||||
|
||||
var data = await response.Content.ReadAsStringAsync();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Dalamud.Plugin.Ipc.Internal;
|
|||
/// <summary>
|
||||
/// This class facilitates sharing data-references of standard types between plugins without using more expensive IPC.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal class DataShare : IServiceType
|
||||
{
|
||||
private readonly Dictionary<string, DataCache> caches = new();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
|
@ -29,9 +30,17 @@ internal static class ServiceManager
|
|||
/// </summary>
|
||||
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();
|
||||
#endif
|
||||
|
||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||
|
||||
private static ManualResetEvent unloadResetEvent = new(false);
|
||||
|
||||
|
|
@ -86,21 +95,34 @@ internal static class ServiceManager
|
|||
/// <param name="scanner">Instance of <see cref="TargetSigScanner"/>.</param>
|
||||
public static void InitializeProvidedServices(Dalamud dalamud, ReliableFileStorage fs, DalamudConfiguration configuration, TargetSigScanner scanner)
|
||||
{
|
||||
#if DEBUG
|
||||
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(fs);
|
||||
ProvideService(configuration);
|
||||
ProvideService(new ServiceContainer());
|
||||
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>
|
||||
|
|
@ -171,7 +193,22 @@ internal static class ServiceManager
|
|||
{
|
||||
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();
|
||||
Timings.Event("BlockingServices Initialized");
|
||||
}
|
||||
|
|
@ -215,13 +252,14 @@ internal static class ServiceManager
|
|||
tasks.Add((Task)typeof(Service<>)
|
||||
.MakeGenericType(serviceType)
|
||||
.InvokeMember(
|
||||
"StartLoader",
|
||||
nameof(Service<IServiceType>.StartLoader),
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
servicesToLoad.Remove(serviceType);
|
||||
|
||||
#if DEBUG
|
||||
tasks.Add(tasks.Last().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
|
|
@ -231,6 +269,7 @@ internal static class ServiceManager
|
|||
LoadedServices.Add(serviceType);
|
||||
}
|
||||
}));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!tasks.Any())
|
||||
|
|
@ -350,10 +389,12 @@ internal static class ServiceManager
|
|||
null);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
lock (LoadedServices)
|
||||
{
|
||||
LoadedServices.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
unloadResetEvent.Set();
|
||||
}
|
||||
|
|
@ -373,7 +414,7 @@ internal static class ServiceManager
|
|||
/// <returns>The type of service this type is.</returns>
|
||||
public static ServiceKind GetServiceKind(this Type type)
|
||||
{
|
||||
var attr = type.GetCustomAttribute<Service>(true)?.GetType();
|
||||
var attr = type.GetCustomAttribute<ServiceAttribute>(true)?.GetType();
|
||||
if (attr == null)
|
||||
return ServiceKind.None;
|
||||
|
||||
|
|
@ -381,13 +422,13 @@ internal static class ServiceManager
|
|||
type.IsAssignableTo(typeof(IServiceType)),
|
||||
"Service did not inherit from IServiceType");
|
||||
|
||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedServiceAttribute)))
|
||||
return ServiceKind.BlockingEarlyLoadedService;
|
||||
|
||||
if (attr.IsAssignableTo(typeof(EarlyLoadedService)))
|
||||
if (attr.IsAssignableTo(typeof(EarlyLoadedServiceAttribute)))
|
||||
return ServiceKind.EarlyLoadedService;
|
||||
|
||||
if (attr.IsAssignableTo(typeof(ScopedService)))
|
||||
if (attr.IsAssignableTo(typeof(ScopedServiceAttribute)))
|
||||
return ServiceKind.ScopedService;
|
||||
|
||||
return ServiceKind.ProvidedService;
|
||||
|
|
@ -414,16 +455,57 @@ internal static class ServiceManager
|
|||
/// Indicates that the class is a service.
|
||||
/// </summary>
|
||||
[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>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
||||
/// </summary>
|
||||
[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>
|
||||
|
|
@ -431,8 +513,15 @@ internal static class ServiceManager
|
|||
/// blocking game main thread until it completes.
|
||||
/// </summary>
|
||||
[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>
|
||||
|
|
@ -440,8 +529,15 @@ internal static class ServiceManager
|
|||
/// service scope, and that it cannot be created outside of a scope.
|
||||
/// </summary>
|
||||
[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>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
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.
|
||||
/// </remarks>
|
||||
/// <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
|
||||
{
|
||||
private static readonly ServiceManager.ServiceAttribute ServiceAttribute;
|
||||
private static TaskCompletionSource<T> instanceTcs = new();
|
||||
private static List<Type>? dependencyServices;
|
||||
|
||||
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)
|
||||
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
|
||||
ServiceManager.Log.Debug("Service<{0}>: Static ctor called", typeof(T).Name);
|
||||
ServiceManager.Log.Debug("Service<{0}>: Static ctor called", type.Name);
|
||||
|
||||
if (exposeToPlugins)
|
||||
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>
|
||||
public static void Provide(T obj)
|
||||
{
|
||||
instanceTcs.SetResult(obj);
|
||||
ServiceManager.Log.Debug("Service<{0}>: Provided", typeof(T).Name);
|
||||
instanceTcs.SetResult(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -83,6 +91,21 @@ internal static class Service<T> where T : IServiceType
|
|||
/// <returns>The object.</returns>
|
||||
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)
|
||||
instanceTcs.Task.Wait();
|
||||
return instanceTcs.Task.Result;
|
||||
|
|
@ -116,12 +139,16 @@ internal static class Service<T> where T : IServiceType
|
|||
}
|
||||
|
||||
/// <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>
|
||||
/// <returns>List of dependency services.</returns>
|
||||
[UsedImplicitly]
|
||||
public static List<Type> GetDependencyServices()
|
||||
{
|
||||
if (dependencyServices is not null)
|
||||
return dependencyServices;
|
||||
|
||||
var res = new List<Type>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return res
|
||||
var deps = res
|
||||
.Distinct()
|
||||
.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}")));
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private static Task<T> StartLoader()
|
||||
return dependencyServices = deps;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
throw new InvalidOperationException($"{typeof(T).Name} is already loaded or disposed.");
|
||||
|
||||
var attr = typeof(T).GetCustomAttribute<ServiceManager.Service>(true)?.GetType();
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.EarlyLoadedService)) != true)
|
||||
var attr = ServiceAttribute.GetType();
|
||||
if (attr.IsAssignableTo(typeof(ServiceManager.EarlyLoadedServiceAttribute)) != true)
|
||||
throw new InvalidOperationException($"{typeof(T).Name} is not an EarlyLoadedService");
|
||||
|
||||
return Task.Run(Timings.AttachTimingHandle(async () =>
|
||||
|
|
@ -212,6 +262,7 @@ internal static class Service<T> where T : IServiceType
|
|||
var instance = await ConstructObject();
|
||||
instanceTcs.SetResult(instance);
|
||||
|
||||
List<Task>? tasks = null;
|
||||
foreach (var method in typeof(T).GetMethods(
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
{
|
||||
|
|
@ -221,8 +272,23 @@ internal static class Service<T> where T : IServiceType
|
|||
ServiceManager.Log.Debug("Service<{0}>: Calling {1}", typeof(T).Name, method.Name);
|
||||
var args = await Task.WhenAll(method.GetParameters().Select(
|
||||
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);
|
||||
return instance;
|
||||
|
|
@ -303,8 +369,20 @@ internal static class Service<T> where T : IServiceType
|
|||
ctor.GetParameters().Select(x => ResolveServiceFromTypeAsync(x.ParameterType)));
|
||||
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)!;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -328,15 +406,24 @@ internal static class Service<T> where T : IServiceType
|
|||
internal static class ServiceHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a list of dependencies for a service. Only accepts Service<T> types.
|
||||
/// These are returned as Service<T> types.
|
||||
/// Get a list of dependencies for a service. Only accepts <see cref="Service{T}"/> types.
|
||||
/// These are returned as <see cref="Service{T}"/> types.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">The dependencies for this service.</param>
|
||||
/// <returns>A list of dependencies.</returns>
|
||||
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(
|
||||
"GetDependencyServices",
|
||||
nameof(Service<IServiceType>.GetDependencyServices),
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
|
|
@ -344,14 +431,18 @@ internal static class ServiceHelpers
|
|||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to obtain a Service<T> for.</param>
|
||||
/// <returns>The Service<T>.</returns>
|
||||
/// <param name="type">The type to obtain a <see cref="Service{T}"/> for.</param>
|
||||
/// <returns>The <see cref="Service{T}"/>.</returns>
|
||||
public static Type GetAsService(Type type)
|
||||
{
|
||||
return typeof(Service<>)
|
||||
.MakeGenericType(type);
|
||||
#if DEBUG
|
||||
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>
|
||||
/// This is not an early-loaded service, as it is needed before they are initialized.
|
||||
/// </remarks>
|
||||
[ServiceManager.Service]
|
||||
[ServiceManager.ProvidedService]
|
||||
public class ReliableFileStorage : IServiceType, IDisposable
|
||||
{
|
||||
private static readonly ModuleLog Log = new("VFS");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue