diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj
index b6d3749bc..9f780b080 100644
--- a/Dalamud.Injector/Dalamud.Injector.csproj
+++ b/Dalamud.Injector/Dalamud.Injector.csproj
@@ -84,6 +84,7 @@
+
diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 9b2db0674..acbe1e621 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -16,7 +16,7 @@ namespace Dalamud.Configuration.Internal
/// Class containing Dalamud settings.
///
[Serializable]
- internal sealed class DalamudConfiguration
+ internal sealed class DalamudConfiguration : IServiceType
{
///
/// Currently used beta key for Dalamud staging builds.
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index dc1ff8da1..747e78e25 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -33,7 +33,7 @@ namespace Dalamud
///
/// The main Dalamud class containing all subsystems.
///
- internal sealed class Dalamud : IDisposable
+ internal sealed class Dalamud : IDisposable, IServiceType
{
#region Internals
diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud/DalamudStartInfo.cs
index 2fd47e759..904b245fd 100644
--- a/Dalamud/DalamudStartInfo.cs
+++ b/Dalamud/DalamudStartInfo.cs
@@ -10,7 +10,7 @@ namespace Dalamud
/// Struct containing information needed to initialize Dalamud.
///
[Serializable]
- public record DalamudStartInfo
+ public record DalamudStartInfo : IServiceType
{
///
/// Initializes a new instance of the class.
diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs
index b3d90a3a3..48cd0d325 100644
--- a/Dalamud/Data/DataManager.cs
+++ b/Dalamud/Data/DataManager.cs
@@ -27,7 +27,7 @@ namespace Dalamud.Data
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class DataManager : IDisposable
+ public sealed class DataManager : IDisposable, IServiceType
{
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs
index 1468dbafb..53636a54e 100644
--- a/Dalamud/Game/ChatHandlers.cs
+++ b/Dalamud/Game/ChatHandlers.cs
@@ -29,7 +29,7 @@ namespace Dalamud.Game
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public class ChatHandlers
+ public class ChatHandlers : IServiceType
{
// private static readonly Dictionary UnicodeToDiscordEmojiDict = new()
// {
diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
index e8f4e9da3..dd735bd42 100644
--- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
+++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
@@ -15,7 +15,7 @@ namespace Dalamud.Game.ClientState.Aetherytes
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class AetheryteList
+ public sealed partial class AetheryteList : IServiceType
{
[ServiceManager.ServiceDependency]
private readonly ClientState clientState = Service.Get();
diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
index 85706de6d..e2f34204f 100644
--- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs
+++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
@@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.Buddy
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class BuddyList
+ public sealed partial class BuddyList : IServiceType
{
private const uint InvalidObjectID = 0xE0000000;
diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs
index bafb4cdf8..5bdd9c694 100644
--- a/Dalamud/Game/ClientState/ClientState.cs
+++ b/Dalamud/Game/ClientState/ClientState.cs
@@ -28,7 +28,7 @@ namespace Dalamud.Game.ClientState
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class ClientState : IDisposable
+ public sealed class ClientState : IDisposable, IServiceType
{
private readonly ClientStateAddressResolver address;
private readonly Hook setupTerritoryTypeHook;
@@ -128,16 +128,6 @@ namespace Dalamud.Game.ClientState
///
public bool IsPvPExcludingDen { get; private set; }
- ///
- /// Enable this module.
- ///
- public void Enable()
- {
- Service.Get().Enable();
- Service.Get().Enable();
- this.setupTerritoryTypeHook.Enable();
- }
-
///
/// Dispose of managed and unmanaged resources.
///
@@ -150,6 +140,12 @@ namespace Dalamud.Game.ClientState
Service.Get().CfPop -= this.NetworkHandlersOnCfPop;
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction()
+ {
+ this.setupTerritoryTypeHook.Enable();
+ }
+
private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType)
{
this.TerritoryType = terriType;
diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs
index 0bfe44145..6cf0bbdd5 100644
--- a/Dalamud/Game/ClientState/Conditions/Condition.cs
+++ b/Dalamud/Game/ClientState/Conditions/Condition.cs
@@ -12,7 +12,7 @@ namespace Dalamud.Game.ClientState.Conditions
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class Condition
+ public sealed partial class Condition : IServiceType
{
///
/// The current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has.
@@ -82,16 +82,14 @@ namespace Dalamud.Game.ClientState.Conditions
return false;
}
- ///
- /// Enables the hooks of the Condition class function.
- ///
- public void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(Framework framework)
{
// Initialization
for (var i = 0; i < MaxConditionEntries; i++)
this.cache[i] = this[i];
- Service.Get().Update += this.FrameworkUpdate;
+ framework.Update += this.FrameworkUpdate;
}
private void FrameworkUpdate(Framework framework)
diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs
index b1a2d9c22..93a0e60b9 100644
--- a/Dalamud/Game/ClientState/Fates/FateTable.cs
+++ b/Dalamud/Game/ClientState/Fates/FateTable.cs
@@ -14,7 +14,7 @@ namespace Dalamud.Game.ClientState.Fates
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class FateTable
+ public sealed partial class FateTable : IServiceType
{
private readonly ClientStateAddressResolver address;
diff --git a/Dalamud/Game/ClientState/GamePad/GamepadState.cs b/Dalamud/Game/ClientState/GamePad/GamepadState.cs
index ce452bcfa..05462c6f7 100644
--- a/Dalamud/Game/ClientState/GamePad/GamepadState.cs
+++ b/Dalamud/Game/ClientState/GamePad/GamepadState.cs
@@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.GamePad
[PluginInterface]
[InterfaceVersion("1.0.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public unsafe class GamepadState : IDisposable
+ public unsafe class GamepadState : IDisposable, IServiceType
{
private readonly Hook gamepadPoll;
@@ -168,10 +168,8 @@ namespace Dalamud.Game.ClientState.GamePad
GC.SuppressFinalize(this);
}
- ///
- /// Enables the hook of the GamepadPoll function.
- ///
- internal void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction()
{
this.gamepadPoll.Enable();
}
diff --git a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
index dba74ab67..aab5d4991 100644
--- a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
+++ b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
@@ -15,7 +15,7 @@ namespace Dalamud.Game.ClientState.JobGauge
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public class JobGauges
+ public class JobGauges : IServiceType
{
private Dictionary cache = new();
diff --git a/Dalamud/Game/ClientState/Keys/KeyState.cs b/Dalamud/Game/ClientState/Keys/KeyState.cs
index 90c3ad872..953f01f75 100644
--- a/Dalamud/Game/ClientState/Keys/KeyState.cs
+++ b/Dalamud/Game/ClientState/Keys/KeyState.cs
@@ -23,7 +23,7 @@ namespace Dalamud.Game.ClientState.Keys
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public class KeyState
+ public class KeyState : IServiceType
{
// The array is accessed in a way that this limit doesn't appear to exist
// but there is other state data past this point, and keys beyond here aren't
diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
index 5500aa533..a5ae12aed 100644
--- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs
+++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
@@ -17,7 +17,7 @@ namespace Dalamud.Game.ClientState.Objects
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class ObjectTable
+ public sealed partial class ObjectTable : IServiceType
{
private const int ObjectTableLength = 424;
diff --git a/Dalamud/Game/ClientState/Objects/TargetManager.cs b/Dalamud/Game/ClientState/Objects/TargetManager.cs
index 881f77c54..54ed83546 100644
--- a/Dalamud/Game/ClientState/Objects/TargetManager.cs
+++ b/Dalamud/Game/ClientState/Objects/TargetManager.cs
@@ -12,7 +12,7 @@ namespace Dalamud.Game.ClientState.Objects
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed unsafe class TargetManager
+ public sealed unsafe class TargetManager : IServiceType
{
private readonly ClientStateAddressResolver address;
diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs
index aee27160c..8f2f96eee 100644
--- a/Dalamud/Game/ClientState/Party/PartyList.cs
+++ b/Dalamud/Game/ClientState/Party/PartyList.cs
@@ -15,7 +15,7 @@ namespace Dalamud.Game.ClientState.Party
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed unsafe partial class PartyList
+ public sealed unsafe partial class PartyList : IServiceType
{
private const int GroupLength = 8;
private const int AllianceLength = 20;
diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs
index 1b83cd3b1..0af2aede2 100644
--- a/Dalamud/Game/Command/CommandManager.cs
+++ b/Dalamud/Game/Command/CommandManager.cs
@@ -18,7 +18,7 @@ namespace Dalamud.Game.Command
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class CommandManager
+ public sealed class CommandManager : IServiceType
{
private readonly Dictionary commandMap = new();
private readonly Regex commandRegexEn = new(@"^The command (?.+) does not exist\.$", RegexOptions.Compiled);
diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs
index e9cdc18ac..70b4fd7e8 100644
--- a/Dalamud/Game/Framework.cs
+++ b/Dalamud/Game/Framework.cs
@@ -25,7 +25,7 @@ namespace Dalamud.Game
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class Framework : IDisposable
+ public sealed class Framework : IDisposable, IServiceType
{
private static Stopwatch statsStopwatch = new();
@@ -47,13 +47,6 @@ namespace Dalamud.Game
this.updateHook = new Hook(this.Address.TickAddress, this.HandleFrameworkUpdate);
this.freeHook = new Hook(this.Address.FreeAddress, this.HandleFrameworkFree);
this.destroyHook = new Hook(this.Address.DestroyAddress, this.HandleFrameworkDestroy);
-
- gameGui.Enable();
- gameNetwork.Enable();
-
- this.updateHook.Enable();
- this.freeHook.Enable();
- this.destroyHook.Enable();
}
///
@@ -224,6 +217,14 @@ namespace Dalamud.Game
statsStopwatch.Reset();
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction()
+ {
+ this.updateHook.Enable();
+ this.freeHook.Enable();
+ this.destroyHook.Enable();
+ }
+
private bool HandleFrameworkUpdate(IntPtr framework)
{
this.frameworkUpdateThread ??= Thread.CurrentThread;
diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs
index 1b798a6e5..96e7b9eaf 100644
--- a/Dalamud/Game/Gui/ChatGui.cs
+++ b/Dalamud/Game/Gui/ChatGui.cs
@@ -22,7 +22,7 @@ namespace Dalamud.Game.Gui
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class ChatGui : IDisposable
+ public sealed class ChatGui : IDisposable, IServiceType
{
private readonly ChatGuiAddressResolver address;
@@ -123,16 +123,6 @@ namespace Dalamud.Game.Gui
///
public byte LastLinkedItemFlags { get; private set; }
- ///
- /// Enables this module.
- ///
- public void Enable()
- {
- this.printMessageHook.Enable();
- this.populateItemLinkHook.Enable();
- this.interactableLinkClickedHook.Enable();
- }
-
///
/// Dispose of managed and unmanaged resources.
///
@@ -280,6 +270,14 @@ namespace Dalamud.Game.Gui
}
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(GameGui gameGui, LibcFunction libcFunction)
+ {
+ this.printMessageHook.Enable();
+ this.populateItemLinkHook.Enable();
+ this.interactableLinkClickedHook.Enable();
+ }
+
private void HandlePopulateItemLinkDetour(IntPtr linkObjectPtr, IntPtr itemInfoPtr)
{
try
diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs
index c8a8a8756..2e14e6402 100644
--- a/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs
+++ b/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
-
+using Dalamud.Configuration.Internal;
using Dalamud.Game.Gui.ContextMenus.OldStructs;
using Dalamud.Hooking;
using Dalamud.IoC;
@@ -25,7 +25,7 @@ namespace Dalamud.Game.Gui.ContextMenus
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class ContextMenu : IDisposable
+ public sealed class ContextMenu : IDisposable, IServiceType
{
private const int MaxContextMenuItemsPerContextMenu = 32;
@@ -97,18 +97,6 @@ namespace Dalamud.Game.Gui.ContextMenus
this.contextMenuOpeningHook.Disable();
}
- ///
- /// Enable this subsystem.
- ///
- internal void Enable()
- {
- this.contextMenuOpeningHook.Enable();
- this.contextMenuOpenedHook.Enable();
- this.contextMenuItemSelectedHook.Enable();
- this.subContextMenuOpeningHook.Enable();
- this.subContextMenuOpenedHook.Enable();
- }
-
private static unsafe bool IsInventoryContext(OldAgentContextInterface* agentContextInterface)
{
return agentContextInterface == AgentInventoryContext.Instance();
@@ -122,6 +110,19 @@ namespace Dalamud.Game.Gui.ContextMenus
}
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(GameGui gameGui)
+ {
+ if (!EnvironmentConfiguration.DalamudDoContextMenu)
+ return;
+
+ this.contextMenuOpeningHook.Enable();
+ this.contextMenuOpenedHook.Enable();
+ this.contextMenuItemSelectedHook.Enable();
+ this.subContextMenuOpeningHook.Enable();
+ this.subContextMenuOpenedHook.Enable();
+ }
+
private unsafe IntPtr ContextMenuOpeningDetour(IntPtr a1, IntPtr a2, IntPtr a3, uint a4, IntPtr a5, OldAgentContextInterface* agentContextInterface, IntPtr a7, ushort a8)
{
this.currentAgentContextInterface = agentContextInterface;
diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs
index e4a75071c..6ff9dc83f 100644
--- a/Dalamud/Game/Gui/Dtr/DtrBar.cs
+++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs
@@ -18,7 +18,7 @@ namespace Dalamud.Game.Gui.Dtr
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed unsafe class DtrBar : IDisposable
+ public sealed unsafe class DtrBar : IDisposable, IServiceType
{
private const uint BaseNodeId = 1000;
diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs
index ba70e891e..736ad85db 100644
--- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs
+++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs
@@ -17,7 +17,7 @@ namespace Dalamud.Game.Gui.FlyText
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class FlyTextGui : IDisposable
+ public sealed class FlyTextGui : IDisposable, IServiceType
{
///
/// The native function responsible for adding fly text to the UI. See .
@@ -129,7 +129,10 @@ namespace Dalamud.Game.Gui.FlyText
var strOffset = 28u;
// Get the UI module and flytext addon pointers
- var gameGui = Service.Get();
+ var gameGui = Service.GetNullable();
+ if (gameGui == null)
+ return;
+
var ui = (FFXIVClientStructs.FFXIV.Client.UI.UIModule*)gameGui.GetUIModule();
var flytext = gameGui.GetAddonByName("_FlyText", 1);
@@ -174,10 +177,8 @@ namespace Dalamud.Game.Gui.FlyText
}
}
- ///
- /// Enables this module.
- ///
- internal void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(GameGui gameGui)
{
this.createFlyTextHook.Enable();
}
diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs
index 53ab50255..b9f6f8e3b 100644
--- a/Dalamud/Game/Gui/GameGui.cs
+++ b/Dalamud/Game/Gui/GameGui.cs
@@ -28,7 +28,7 @@ namespace Dalamud.Game.Gui
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed unsafe class GameGui : IDisposable
+ public sealed unsafe class GameGui : IDisposable, IServiceType
{
private readonly GameGuiAddressResolver address;
@@ -418,19 +418,9 @@ namespace Dalamud.Game.Gui
/// The background music key.
public void SetBgm(ushort bgmKey) => this.setGlobalBgmHook.Original(bgmKey, 0, 0, 0, 0, 0);
- ///
- /// Enables the hooks and submodules of this module.
- ///
- public void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction()
{
- Service.GetAsync().ContinueWith(x => x.Result.Enable());
- Service.GetAsync().ContinueWith(x => x.Result.Enable());
- Service.GetAsync().ContinueWith(x => x.Result.Enable());
- Service.GetAsync().ContinueWith(x => x.Result.Enable());
-
- if (EnvironmentConfiguration.DalamudDoContextMenu)
- Service.GetAsync().ContinueWith(x => x.Result.Enable());
-
this.setGlobalBgmHook.Enable();
this.handleItemHoverHook.Enable();
this.handleItemOutHook.Enable();
diff --git a/Dalamud/Game/Gui/Internal/DalamudIME.cs b/Dalamud/Game/Gui/Internal/DalamudIME.cs
index e0301b8b8..e16692d43 100644
--- a/Dalamud/Game/Gui/Internal/DalamudIME.cs
+++ b/Dalamud/Game/Gui/Internal/DalamudIME.cs
@@ -19,7 +19,7 @@ namespace Dalamud.Game.Gui.Internal
/// This class handles IME for non-English users.
///
[ServiceManager.EarlyLoadedService]
- internal unsafe class DalamudIME : IDisposable
+ internal unsafe class DalamudIME : IDisposable, IServiceType
{
private static readonly ModuleLog Log = new("IME");
@@ -83,15 +83,13 @@ namespace Dalamud.Game.Gui.Internal
return new Vector2(this.cursorPos->X, this.cursorPos->Y);
}
- ///
- /// Enables the IME module.
- ///
- internal void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene)
{
try
{
this.wndProcDelegate = this.WndProcDetour;
- this.interfaceHandle = Service.Get().WindowHandlePtr;
+ this.interfaceHandle = interfaceManagerWithScene.Manager.WindowHandlePtr;
this.wndProcPtr = Marshal.GetFunctionPointerForDelegate(this.wndProcDelegate);
this.oldWndProcPtr = SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.wndProcPtr);
diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs
index 5b85588cc..406051a99 100644
--- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs
+++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs
@@ -16,7 +16,7 @@ namespace Dalamud.Game.Gui.PartyFinder
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class PartyFinderGui : IDisposable
+ public sealed class PartyFinderGui : IDisposable, IServiceType
{
private readonly PartyFinderAddressResolver address;
private readonly IntPtr memory;
@@ -55,14 +55,6 @@ namespace Dalamud.Game.Gui.PartyFinder
///
public event PartyFinderListingEventDelegate ReceiveListing;
- ///
- /// Enables this module.
- ///
- public void Enable()
- {
- this.receiveListingHook.Enable();
- }
-
///
/// Dispose of managed and unmanaged resources.
///
@@ -80,6 +72,12 @@ namespace Dalamud.Game.Gui.PartyFinder
}
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(GameGui gameGui)
+ {
+ this.receiveListingHook.Enable();
+ }
+
private void HandleReceiveListingDetour(IntPtr managerPtr, IntPtr data)
{
try
diff --git a/Dalamud/Game/Gui/Toast/ToastGui.cs b/Dalamud/Game/Gui/Toast/ToastGui.cs
index 3cafffe32..8602be735 100644
--- a/Dalamud/Game/Gui/Toast/ToastGui.cs
+++ b/Dalamud/Game/Gui/Toast/ToastGui.cs
@@ -15,7 +15,7 @@ namespace Dalamud.Game.Gui.Toast
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed partial class ToastGui : IDisposable
+ public sealed partial class ToastGui : IDisposable, IServiceType
{
private const uint QuestToastCheckmarkMagic = 60081;
@@ -100,16 +100,6 @@ namespace Dalamud.Game.Gui.Toast
#endregion
- ///
- /// Enables this module.
- ///
- public void Enable()
- {
- this.showNormalToastHook.Enable();
- this.showQuestToastHook.Enable();
- this.showErrorToastHook.Enable();
- }
-
///
/// Disposes of managed and unmanaged resources.
///
@@ -153,6 +143,14 @@ namespace Dalamud.Game.Gui.Toast
return terminated;
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(GameGui gameGui)
+ {
+ this.showNormalToastHook.Enable();
+ this.showQuestToastHook.Enable();
+ this.showErrorToastHook.Enable();
+ }
+
private SeString ParseString(IntPtr text)
{
var bytes = new List();
@@ -202,7 +200,9 @@ namespace Dalamud.Game.Gui.Toast
{
options ??= new ToastOptions();
- var manager = Service.Get().GetUIModule();
+ var manager = Service.GetNullable()?.GetUIModule();
+ if (manager == null)
+ return;
// terminate the string
var terminated = Terminate(bytes);
@@ -211,7 +211,7 @@ namespace Dalamud.Game.Gui.Toast
{
fixed (byte* ptr = terminated)
{
- this.HandleNormalToastDetour(manager, (IntPtr)ptr, 5, (byte)options.Position, (byte)options.Speed, 0);
+ this.HandleNormalToastDetour(manager!.Value, (IntPtr)ptr, 5, (byte)options.Position, (byte)options.Speed, 0);
}
}
}
@@ -283,7 +283,9 @@ namespace Dalamud.Game.Gui.Toast
{
options ??= new QuestToastOptions();
- var manager = Service.Get().GetUIModule();
+ var manager = Service.GetNullable()?.GetUIModule();
+ if (manager == null)
+ return;
// terminate the string
var terminated = Terminate(bytes);
@@ -295,7 +297,7 @@ namespace Dalamud.Game.Gui.Toast
fixed (byte* ptr = terminated)
{
this.HandleQuestToastDetour(
- manager,
+ manager!.Value,
(int)options.Position,
(IntPtr)ptr,
ioc1,
@@ -385,7 +387,9 @@ namespace Dalamud.Game.Gui.Toast
private void ShowError(byte[] bytes)
{
- var manager = Service.Get().GetUIModule();
+ var manager = Service.GetNullable()?.GetUIModule();
+ if (manager == null)
+ return;
// terminate the string
var terminated = Terminate(bytes);
@@ -394,7 +398,7 @@ namespace Dalamud.Game.Gui.Toast
{
fixed (byte* ptr = terminated)
{
- this.HandleErrorToastDetour(manager, (IntPtr)ptr, 0);
+ this.HandleErrorToastDetour(manager!.Value, (IntPtr)ptr, 0);
}
}
}
diff --git a/Dalamud/Game/Internal/AntiDebug.cs b/Dalamud/Game/Internal/AntiDebug.cs
index 284732a26..99797f4df 100644
--- a/Dalamud/Game/Internal/AntiDebug.cs
+++ b/Dalamud/Game/Internal/AntiDebug.cs
@@ -9,7 +9,7 @@ namespace Dalamud.Game.Internal
/// This class disables anti-debug functionality in the game client.
///
[ServiceManager.EarlyLoadedService]
- internal sealed partial class AntiDebug
+ internal sealed partial class AntiDebug : IServiceType
{
private readonly byte[] nop = new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 };
private byte[] original;
diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs
index 33413ee96..575801b33 100644
--- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs
+++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs
@@ -24,7 +24,7 @@ namespace Dalamud.Game.Internal
/// This class implements in-game Dalamud options in the in-game System menu.
///
[ServiceManager.EarlyLoadedService]
- internal sealed unsafe partial class DalamudAtkTweaks
+ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
{
private readonly AtkValueChangeType atkValueChangeType;
private readonly AtkValueSetString atkValueSetString;
@@ -39,7 +39,7 @@ namespace Dalamud.Game.Internal
private readonly string locDalamudSettings;
[ServiceManager.ServiceConstructor]
- private DalamudAtkTweaks(SigScanner sigScanner)
+ private DalamudAtkTweaks(SigScanner sigScanner, ContextMenu contextMenu)
{
var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 32 C0 4C 8B AC 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ??");
@@ -60,10 +60,7 @@ namespace Dalamud.Game.Internal
this.locDalamudPlugins = Loc.Localize("SystemMenuPlugins", "Dalamud Plugins");
this.locDalamudSettings = Loc.Localize("SystemMenuSettings", "Dalamud Settings");
- var contextMenu = Service.Get();
contextMenu.ContextMenuOpened += this.ContextMenuOnContextMenuOpened;
-
- this.Enable();
}
private delegate void AgentHudOpenSystemMenuPrototype(void* thisPtr, AtkValue* atkValueArgs, uint menuSize);
@@ -76,10 +73,8 @@ namespace Dalamud.Game.Internal
private delegate IntPtr AtkUnitBaseReceiveGlobalEvent(AtkUnitBase* thisPtr, ushort cmd, uint a3, IntPtr a4, uint* a5);
- ///
- /// Enables the .
- ///
- public void Enable()
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(DalamudInterface dalamudInterface)
{
this.hookAgentHudOpenSystemMenu.Enable();
this.hookUiModuleRequestMainCommand.Enable();
diff --git a/Dalamud/Game/Libc/LibcFunction.cs b/Dalamud/Game/Libc/LibcFunction.cs
index b51277232..aa149a3ce 100644
--- a/Dalamud/Game/Libc/LibcFunction.cs
+++ b/Dalamud/Game/Libc/LibcFunction.cs
@@ -13,7 +13,7 @@ namespace Dalamud.Game.Libc
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class LibcFunction
+ public sealed class LibcFunction : IServiceType
{
private readonly LibcFunctionAddressResolver address;
private readonly StdStringFromCStringDelegate stdStringCtorCString;
diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs
index 3c22d3077..2f2903a8b 100644
--- a/Dalamud/Game/Network/GameNetwork.cs
+++ b/Dalamud/Game/Network/GameNetwork.cs
@@ -15,7 +15,7 @@ namespace Dalamud.Game.Network
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public sealed class GameNetwork : IDisposable
+ public sealed class GameNetwork : IDisposable, IServiceType
{
private readonly GameNetworkAddressResolver address;
private readonly Hook processZonePacketDownHook;
@@ -59,15 +59,6 @@ namespace Dalamud.Game.Network
///
public event OnNetworkMessageDelegate NetworkMessage;
- ///
- /// Enable this module.
- ///
- public void Enable()
- {
- this.processZonePacketDownHook.Enable();
- this.processZonePacketUpHook.Enable();
- }
-
///
/// Dispose of managed and unmanaged resources.
///
@@ -98,6 +89,13 @@ namespace Dalamud.Game.Network
}
}
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction()
+ {
+ this.processZonePacketDownHook.Enable();
+ this.processZonePacketUpHook.Enable();
+ }
+
private void ProcessZonePacketDownDetour(IntPtr a, uint targetId, IntPtr dataPtr)
{
this.baseAddress = a;
diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs
index e39c3b9af..e8380459f 100644
--- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs
+++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs
@@ -22,7 +22,7 @@ namespace Dalamud.Game.Network.Internal
/// This class handles network notifications and uploading market board data.
///
[ServiceManager.EarlyLoadedService]
- internal class NetworkHandlers
+ internal class NetworkHandlers : IServiceType
{
private readonly List marketBoardRequests = new();
diff --git a/Dalamud/Game/Network/Internal/WinSockHandlers.cs b/Dalamud/Game/Network/Internal/WinSockHandlers.cs
index e2ab0c9bb..ed9d635a3 100644
--- a/Dalamud/Game/Network/Internal/WinSockHandlers.cs
+++ b/Dalamud/Game/Network/Internal/WinSockHandlers.cs
@@ -10,7 +10,7 @@ namespace Dalamud.Game.Network.Internal
/// This class enables TCP optimizations in the game socket for better performance.
///
[ServiceManager.EarlyLoadedService]
- internal sealed class WinSockHandlers : IDisposable
+ internal sealed class WinSockHandlers : IDisposable, IServiceType
{
private Hook ws2SocketHook;
diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs
index 832066ee5..bab5c4e6d 100644
--- a/Dalamud/Game/SigScanner.cs
+++ b/Dalamud/Game/SigScanner.cs
@@ -19,7 +19,7 @@ namespace Dalamud.Game
///
[PluginInterface]
[InterfaceVersion("1.0")]
- public class SigScanner : IDisposable
+ public class SigScanner : IDisposable, IServiceType
{
private readonly FileInfo? cacheFile;
diff --git a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs b/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs
index 85431e06c..02c187bad 100644
--- a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs
+++ b/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs
@@ -14,7 +14,7 @@ namespace Dalamud.Game.Text.SeStringHandling
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
[Obsolete("This class is obsolete. Please use the static methods on SeString instead.")]
- public sealed class SeStringManager
+ public sealed class SeStringManager : IServiceType
{
[ServiceManager.ServiceConstructor]
private SeStringManager()
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index b9c0d270c..0a8ee331a 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -14,7 +14,7 @@ namespace Dalamud.Hooking.Internal
/// This class manages the final disposition of hooks, cleaning up any that have not reverted their changes.
///
[ServiceManager.EarlyLoadedService]
- internal class HookManager : IDisposable
+ internal class HookManager : IDisposable, IServiceType
{
private static readonly ModuleLog Log = new("HM");
diff --git a/Dalamud/IServiceType.cs b/Dalamud/IServiceType.cs
new file mode 100644
index 000000000..973795faf
--- /dev/null
+++ b/Dalamud/IServiceType.cs
@@ -0,0 +1,8 @@
+namespace Dalamud;
+
+///
+/// Marker class for service types.
+///
+public interface IServiceType
+{
+}
diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs
index ce4460257..28214d887 100644
--- a/Dalamud/Interface/GameFonts/GameFontManager.cs
+++ b/Dalamud/Interface/GameFonts/GameFontManager.cs
@@ -18,7 +18,7 @@ namespace Dalamud.Interface.GameFonts
/// Loads game font for use in ImGui.
///
[ServiceManager.EarlyLoadedService]
- internal class GameFontManager : IDisposable
+ internal class GameFontManager : IDisposable, IServiceType
{
private static readonly string?[] FontNames =
{
diff --git a/Dalamud/Interface/Internal/DalamudCommands.cs b/Dalamud/Interface/Internal/DalamudCommands.cs
index a9fc47995..11c8cb7f0 100644
--- a/Dalamud/Interface/Internal/DalamudCommands.cs
+++ b/Dalamud/Interface/Internal/DalamudCommands.cs
@@ -19,7 +19,7 @@ namespace Dalamud.Interface.Internal
/// Class handling Dalamud core commands.
///
[ServiceManager.EarlyLoadedService]
- internal class DalamudCommands
+ internal class DalamudCommands : IServiceType
{
[ServiceManager.ServiceConstructor]
private DalamudCommands(CommandManager commandManager)
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index d5a34d34e..e5d16289e 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -35,8 +35,8 @@ namespace Dalamud.Interface.Internal
///
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
///
- [ServiceManager.AfterDrawingEarlyLoadedService]
- internal class DalamudInterface : IDisposable
+ [ServiceManager.EarlyLoadedService]
+ internal class DalamudInterface : IDisposable, IServiceType
{
private static readonly ModuleLog Log = new("DUI");
@@ -77,8 +77,12 @@ namespace Dalamud.Interface.Internal
private bool isImGuiDrawMetricsWindow = false;
[ServiceManager.ServiceConstructor]
- private DalamudInterface(Dalamud dalamud, DalamudConfiguration configuration, InterfaceManager interfaceManager)
+ private DalamudInterface(
+ Dalamud dalamud,
+ DalamudConfiguration configuration,
+ InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene)
{
+ var interfaceManager = interfaceManagerWithScene.Manager;
this.WindowSystem = new WindowSystem("DalamudCore");
this.changelogWindow = new ChangelogWindow() { IsOpen = false };
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index da7575990..78faf595c 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -7,12 +7,10 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
-using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.ClientState.Keys;
-using Dalamud.Game.Gui.Internal;
using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
using Dalamud.Interface.GameFonts;
@@ -45,7 +43,7 @@ namespace Dalamud.Interface.Internal
/// This class manages interaction with the ImGui interface.
///
[ServiceManager.BlockingEarlyLoadedService]
- internal class InterfaceManager : IDisposable
+ internal class InterfaceManager : IDisposable, IServiceType
{
private const float MinimumFallbackFontSizePt = 9.6f; // Game's minimum AXIS font size
private const float MinimumFallbackFontSizePx = MinimumFallbackFontSizePt * 4.0f / 3.0f;
@@ -61,7 +59,6 @@ namespace Dalamud.Interface.Internal
private readonly ManualResetEvent fontBuildSignal;
private readonly SwapChainVtableResolver address;
- private readonly TaskCompletionSource sceneInitializeTaskCompletionSource = new();
private RawDX11Scene? scene;
private Hook? presentHook;
@@ -71,7 +68,7 @@ namespace Dalamud.Interface.Internal
// can't access imgui IO before first present call
private bool lastWantCapture = false;
private bool isRebuildingFonts = false;
-
+ private bool isOverrideGameCursor = false;
private bool isFallbackFontMode = false;
[ServiceManager.ServiceConstructor]
@@ -100,13 +97,6 @@ namespace Dalamud.Interface.Internal
{
Log.Error(e, "RTSS Free failed");
}
-
- Task.Run(async () =>
- {
- var framework = await Service.GetAsync();
- var sigScanner = await Service.GetAsync();
- await framework.RunOnFrameworkThread(() => this.Enable(sigScanner));
- });
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@@ -160,11 +150,6 @@ namespace Dalamud.Interface.Internal
///
public static ImFontPtr MonoFont { get; private set; }
- ///
- /// Gets a task that gets completed when scene gets initialized.
- ///
- public Task SceneInitializeTask => this.sceneInitializeTaskCompletionSource.Task;
-
///
/// Gets or sets the pointer to ImGui.IO(), when it was last used.
///
@@ -178,15 +163,20 @@ namespace Dalamud.Interface.Internal
///
/// Gets the address handle to the main process window.
///
- public IntPtr WindowHandlePtr => this.scene.WindowHandlePtr;
+ public IntPtr WindowHandlePtr => this.scene?.WindowHandlePtr ?? IntPtr.Zero;
///
/// Gets or sets a value indicating whether or not the game's cursor should be overridden with the ImGui cursor.
///
public bool OverrideGameCursor
{
- get => this.scene.UpdateCursor;
- set => this.scene.UpdateCursor = value;
+ get => this.scene?.UpdateCursor ?? this.isOverrideGameCursor;
+ set
+ {
+ this.isOverrideGameCursor = value;
+ if (this.scene != null)
+ this.scene.UpdateCursor = value;
+ }
}
///
@@ -344,6 +334,12 @@ namespace Dalamud.Interface.Internal
///
public void RebuildFonts()
{
+ if (this.scene == null)
+ {
+ Log.Verbose("[FONT] RebuildFonts(): scene not ready, doing nothing");
+ return;
+ }
+
Log.Verbose("[FONT] RebuildFonts() called");
// don't invoke this multiple times per frame, in case multiple plugins call it
@@ -439,7 +435,7 @@ namespace Dalamud.Interface.Internal
private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags)
{
if (this.scene != null && swapChain != this.scene.SwapChain.NativePointer)
- return this.presentHook.Original(swapChain, syncInterval, presentFlags);
+ return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
if (this.scene == null)
{
@@ -451,7 +447,7 @@ namespace Dalamud.Interface.Internal
}
catch (DllNotFoundException ex)
{
- this.sceneInitializeTaskCompletionSource.SetException(ex);
+ Service.ProvideException(ex);
Log.Error(ex, "Could not load ImGui dependencies.");
var res = PInvoke.User32.MessageBox(
@@ -492,6 +488,7 @@ namespace Dalamud.Interface.Internal
Log.Error(ex, "Could not delete dalamudUI.ini");
}
+ this.scene.UpdateCursor = this.isOverrideGameCursor;
this.scene.ImGuiIniPath = iniFileInfo.FullName;
this.scene.OnBuildUI += this.Display;
this.scene.OnNewInputFrame += this.OnNewInputFrame;
@@ -557,7 +554,7 @@ namespace Dalamud.Interface.Internal
Log.Information("[IM] Scene & ImGui setup OK!");
- Service.Get().Enable();
+ Service.Get().RunOnFrameworkThread(() => Service.Provide(new(this)));
}
}
@@ -567,17 +564,11 @@ namespace Dalamud.Interface.Internal
this.RenderImGui();
- if (!this.SceneInitializeTask.IsCompleted)
- this.sceneInitializeTaskCompletionSource.SetResult();
-
return pRes;
}
this.RenderImGui();
- if (!this.SceneInitializeTask.IsCompleted)
- this.sceneInitializeTaskCompletionSource.SetResult();
-
return this.presentHook.Original(swapChain, syncInterval, presentFlags);
}
@@ -978,21 +969,20 @@ namespace Dalamud.Interface.Internal
}
}
- private void Enable(SigScanner sigScanner)
+ [ServiceManager.CallWhenServicesReady]
+ private void ContinueConstruction(SigScanner sigScanner)
{
this.address.Setup(sigScanner);
- this.setCursorHook = Hook.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true);
+ this.setCursorHook = Hook.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true)!;
this.presentHook = new Hook(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = new Hook(this.address.ResizeBuffers, this.ResizeBuffersDetour);
- var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
-
Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
- Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
+ Log.Verbose($"SetCursor address 0x{this.setCursorHook!.Address.ToInt64():X}");
+ Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
+ Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
- this.setCursorHook?.Enable();
+ this.setCursorHook.Enable();
this.presentHook.Enable();
this.resizeBuffersHook.Enable();
@@ -1018,8 +1008,8 @@ namespace Dalamud.Interface.Internal
private void Disable()
{
this.setCursorHook?.Disable();
- this.presentHook.Disable();
- this.resizeBuffersHook.Disable();
+ this.presentHook?.Disable();
+ this.resizeBuffersHook?.Disable();
}
// This is intended to only be called as a handler attached to scene.OnNewRenderFrame
@@ -1029,7 +1019,7 @@ namespace Dalamud.Interface.Internal
this.SetupFonts();
Log.Verbose("[FONT] RebuildFontsInternal() detaching");
- this.scene.OnNewRenderFrame -= this.RebuildFontsInternal;
+ this.scene!.OnNewRenderFrame -= this.RebuildFontsInternal;
Log.Verbose("[FONT] Calling InvalidateFonts");
try
@@ -1077,11 +1067,11 @@ namespace Dalamud.Interface.Internal
// We have to ensure we're working with the main swapchain,
// as viewports might be resizing as well
if (this.scene == null || swapChain != this.scene.SwapChain.NativePointer)
- return this.resizeBuffersHook.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
+ return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
this.scene?.OnPreResize();
- var ret = this.resizeBuffersHook.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
+ var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
if (ret.ToInt64() == 0x887A0001)
{
Log.Error("invalid call to resizeBuffers");
@@ -1097,7 +1087,7 @@ namespace Dalamud.Interface.Internal
if (this.lastWantCapture == true && (!this.scene?.IsImGuiCursor(hCursor) ?? false) && this.OverrideGameCursor)
return IntPtr.Zero;
- return this.setCursorHook.Original(hCursor);
+ return this.setCursorHook!.Original(hCursor);
}
private void OnNewInputFrame()
@@ -1185,6 +1175,26 @@ namespace Dalamud.Interface.Internal
Service.Get().Draw();
}
+ ///
+ /// Represents an instance of InstanceManager with scene ready for use.
+ ///
+ public class InterfaceManagerWithScene : IServiceType
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An instance of .
+ internal InterfaceManagerWithScene(InterfaceManager interfaceManager)
+ {
+ this.Manager = interfaceManager;
+ }
+
+ ///
+ /// Associated InterfaceManager.
+ ///
+ public InterfaceManager Manager { get; init; }
+ }
+
///
/// Represents a glyph request.
///
diff --git a/Dalamud/Interface/Internal/Notifications/NotificationManager.cs b/Dalamud/Interface/Internal/Notifications/NotificationManager.cs
index c68017caf..f1318a7fe 100644
--- a/Dalamud/Interface/Internal/Notifications/NotificationManager.cs
+++ b/Dalamud/Interface/Internal/Notifications/NotificationManager.cs
@@ -14,7 +14,7 @@ namespace Dalamud.Interface.Internal.Notifications
/// Ported from https://github.com/patrickcjk/imgui-notify.
///
[ServiceManager.EarlyLoadedService]
- internal class NotificationManager
+ internal class NotificationManager : IServiceType
{
///
/// Value indicating the bottom-left X padding.
diff --git a/Dalamud/Interface/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu.cs
index 8e00cea3b..f44b112d7 100644
--- a/Dalamud/Interface/TitleScreenMenu.cs
+++ b/Dalamud/Interface/TitleScreenMenu.cs
@@ -13,7 +13,7 @@ namespace Dalamud.Interface
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
- public class TitleScreenMenu
+ public class TitleScreenMenu : IServiceType
{
///
/// Gets the texture size needed for title screen menu logos.
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index 753e81b4e..0119d1a78 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -109,12 +109,12 @@ namespace Dalamud.Interface
///
/// Gets the game's active Direct3D device.
///
- public Device Device => Service.Get().Device;
+ public Device Device => Service.Get().Manager.Device!;
///
/// Gets the game's main window handle.
///
- public IntPtr WindowHandlePtr => Service.Get().WindowHandlePtr;
+ public IntPtr WindowHandlePtr => Service.Get().Manager.WindowHandlePtr;
///
/// Gets or sets a value indicating whether this plugin should hide its UI automatically when the game's UI is hidden.
@@ -215,7 +215,7 @@ namespace Dalamud.Interface
/// The full filepath to the image.
/// A object wrapping the created image. Use inside ImGui.Image().
public TextureWrap LoadImage(string filePath)
- => Service.Get().LoadImage(filePath);
+ => Service.Get().Manager.LoadImage(filePath);
///
/// Loads an image from a byte stream, such as a png downloaded into memory.
@@ -223,7 +223,7 @@ namespace Dalamud.Interface
/// A byte array containing the raw image data.
/// A object wrapping the created image. Use inside ImGui.Image().
public TextureWrap LoadImage(byte[] imageData)
- => Service.Get().LoadImage(imageData);
+ => Service.Get().Manager.LoadImage(imageData);
///
/// Loads an image from raw unformatted pixel data, with no type or header information. To load formatted data, use .
@@ -234,7 +234,7 @@ namespace Dalamud.Interface
/// The number of channels (bytes per pixel) of the image contained in . This should usually be 4.
/// A object wrapping the created image. Use inside ImGui.Image().
public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels)
- => Service.Get().LoadImageRaw(imageData, width, height, numChannels);
+ => Service.Get().Manager.LoadImageRaw(imageData, width, height, numChannels);
///
/// Gets a game font.
diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs
index 5369f3c67..9edd1479c 100644
--- a/Dalamud/IoC/Internal/ServiceContainer.cs
+++ b/Dalamud/IoC/Internal/ServiceContainer.cs
@@ -12,7 +12,7 @@ namespace Dalamud.IoC.Internal
///
/// A simple singleton-only IOC container that provides (optional) version-based dependency resolution.
///
- internal class ServiceContainer : IServiceProvider
+ internal class ServiceContainer : IServiceProvider, IServiceType
{
private static readonly ModuleLog Log = new("SERVICECONTAINER");
diff --git a/Dalamud/Localization.cs b/Dalamud/Localization.cs
index acd9d5dd2..94e020d5b 100644
--- a/Dalamud/Localization.cs
+++ b/Dalamud/Localization.cs
@@ -14,7 +14,7 @@ namespace Dalamud
/// Class handling localization.
///
[ServiceManager.EarlyLoadedService]
- public class Localization
+ public class Localization : IServiceType
{
///
/// Array of language codes which have a valid translation in Dalamud.
diff --git a/Dalamud/Logging/Internal/TaskTracker.cs b/Dalamud/Logging/Internal/TaskTracker.cs
index 354decc55..ca7576398 100644
--- a/Dalamud/Logging/Internal/TaskTracker.cs
+++ b/Dalamud/Logging/Internal/TaskTracker.cs
@@ -13,7 +13,7 @@ namespace Dalamud.Logging.Internal
/// Class responsible for tracking asynchronous tasks.
///
[ServiceManager.EarlyLoadedService]
- internal class TaskTracker : IDisposable
+ internal class TaskTracker : IDisposable, IServiceType
{
private static readonly ModuleLog Log = new("TT");
private static readonly List TrackedTasksInternal = new();
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index d88e82236..9f803f79a 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -31,7 +31,7 @@ namespace Dalamud.Plugin.Internal;
/// Class responsible for loading and unloading plugins.
///
[ServiceManager.EarlyLoadedService]
-internal partial class PluginManager : IDisposable
+internal partial class PluginManager : IDisposable, IServiceType
{
///
/// The current Dalamud API level, used to handle breaking changes. Only plugins with this level will be loaded.
@@ -386,12 +386,8 @@ internal partial class PluginManager : IDisposable
// Load plugins that want to be loaded during Framework.Tick, when drawing facilities are available
loadTasks.Add(
- Service
+ Service
.GetAsync()
- .ContinueWith(
- x => x.Result.SceneInitializeTask,
- TaskContinuationOptions.RunContinuationsAsynchronously)
- .Unwrap()
.ContinueWith(
_ => Service.Get().RunOnTick(
() => LoadPluginsSync(
diff --git a/Dalamud/Plugin/Internal/StartupPluginLoader.cs b/Dalamud/Plugin/Internal/StartupPluginLoader.cs
index 8a9063747..a95e25ace 100644
--- a/Dalamud/Plugin/Internal/StartupPluginLoader.cs
+++ b/Dalamud/Plugin/Internal/StartupPluginLoader.cs
@@ -10,7 +10,7 @@ namespace Dalamud.Plugin.Internal;
/// Class responsible for loading plugins on startup.
///
[ServiceManager.BlockingEarlyLoadedService]
-public class StartupPluginLoader
+public class StartupPluginLoader : IServiceType
{
private static readonly ModuleLog Log = new("SPL");
diff --git a/Dalamud/Plugin/Ipc/Internal/CallGate.cs b/Dalamud/Plugin/Ipc/Internal/CallGate.cs
index 5d283672c..2a857b2d0 100644
--- a/Dalamud/Plugin/Ipc/Internal/CallGate.cs
+++ b/Dalamud/Plugin/Ipc/Internal/CallGate.cs
@@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Ipc.Internal
/// This class facilitates inter-plugin communication.
///
[ServiceManager.EarlyLoadedService]
- internal class CallGate
+ internal class CallGate : IServiceType
{
private readonly Dictionary gates = new();
diff --git a/Dalamud/ServiceManager.cs b/Dalamud/ServiceManager.cs
index 5dc6d08f1..80dd01a90 100644
--- a/Dalamud/ServiceManager.cs
+++ b/Dalamud/ServiceManager.cs
@@ -6,7 +6,6 @@ using System.Reflection;
using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
-using Dalamud.Interface.Internal;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Utility.Timing;
@@ -67,7 +66,6 @@ namespace Dalamud
var earlyLoadingServices = new HashSet();
var blockingEarlyLoadingServices = new HashSet();
- var afterDrawingEarlyLoadedServices = new HashSet();
var dependencyServicesMap = new Dictionary>();
var getAsyncTaskMap = new Dictionary();
@@ -90,10 +88,6 @@ namespace Dalamud
getAsyncTaskMap[serviceType] = getTask;
blockingEarlyLoadingServices.Add(serviceType);
}
- else if (attr.IsAssignableTo(typeof(AfterDrawingEarlyLoadedService)))
- {
- afterDrawingEarlyLoadedServices.Add(serviceType);
- }
else
{
earlyLoadingServices.Add(serviceType);
@@ -126,55 +120,44 @@ namespace Dalamud
try
{
- for (var i = 0; i < 2; i++)
+ var tasks = new List();
+ var servicesToLoad = new HashSet();
+ servicesToLoad.UnionWith(earlyLoadingServices);
+ servicesToLoad.UnionWith(blockingEarlyLoadingServices);
+
+ while (servicesToLoad.Any())
{
- var tasks = new List();
- var servicesToLoad = new HashSet();
- if (i == 0)
+ foreach (var serviceType in servicesToLoad)
{
- servicesToLoad.UnionWith(earlyLoadingServices);
- servicesToLoad.UnionWith(blockingEarlyLoadingServices);
+ if (dependencyServicesMap[serviceType].Any(
+ x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
+ continue;
+
+ tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
+ "StartLoader",
+ BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
+ null,
+ null,
+ null));
+ servicesToLoad.Remove(serviceType);
+ }
+
+ if (!tasks.Any())
+ throw new InvalidOperationException("Unresolvable dependency cycle detected");
+
+ if (servicesToLoad.Any())
+ {
+ await Task.WhenAny(tasks);
+ var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
+ if (faultedTasks.Any())
+ throw new AggregateException(faultedTasks);
}
else
{
- servicesToLoad.UnionWith(afterDrawingEarlyLoadedServices);
- await (await Service.GetAsync()).SceneInitializeTask;
+ await Task.WhenAll(tasks);
}
- while (servicesToLoad.Any())
- {
- foreach (var serviceType in servicesToLoad)
- {
- if (dependencyServicesMap[serviceType].Any(
- x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
- continue;
-
- tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
- "StartLoader",
- BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
- null,
- null,
- null));
- servicesToLoad.Remove(serviceType);
- }
-
- if (!tasks.Any())
- throw new InvalidOperationException("Unresolvable dependency cycle detected");
-
- if (servicesToLoad.Any())
- {
- await Task.WhenAny(tasks);
- var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
- if (faultedTasks.Any())
- throw new AggregateException(faultedTasks);
- }
- else
- {
- await Task.WhenAll(tasks);
- }
-
- tasks.RemoveAll(x => x.IsCompleted);
- }
+ tasks.RemoveAll(x => x.IsCompleted);
}
}
catch (Exception e)
@@ -236,11 +219,11 @@ namespace Dalamud
}
///
- /// Indicates that the class is a service, and will be instantiated automatically on startup,
- /// when drawing becomes available.
+ /// Indicates that the method should be called when the services given in the constructor are ready.
///
- [AttributeUsage(AttributeTargets.Class)]
- public class AfterDrawingEarlyLoadedService : EarlyLoadedService
+ [AttributeUsage(AttributeTargets.Method)]
+ [MeansImplicitUse]
+ public class CallWhenServicesReady : Attribute
{
}
}
diff --git a/Dalamud/Service{T}.cs b/Dalamud/Service{T}.cs
index e8fd0f32f..f813ef8dd 100644
--- a/Dalamud/Service{T}.cs
+++ b/Dalamud/Service{T}.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-using System.Threading;
using System.Threading.Tasks;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
@@ -18,7 +17,7 @@ namespace Dalamud
/// Only used internally within Dalamud, if plugins need access to things it should be _only_ via DI.
///
/// The class you want to store in the service locator.
- internal static class Service
+ internal static class Service where T : IServiceType
{
// ReSharper disable once StaticMemberInGenericType
private static readonly TaskCompletionSource InstanceTcs = new();
@@ -51,10 +50,23 @@ namespace Dalamud
ServiceManager.Log.Debug("Service<{0}>: Begin construction", typeof(T).Name);
try
{
- var x = await ConstructObject();
+ var instance = await ConstructObject();
+ InstanceTcs.SetResult(instance);
+
+ foreach (var method in typeof(T).GetMethods(
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+ {
+ if (method.GetCustomAttribute(true) == null)
+ continue;
+
+ 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);
+ }
+
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
- InstanceTcs.SetResult(x);
- return x;
+ return instance;
}
catch (Exception e)
{
@@ -71,10 +83,20 @@ namespace Dalamud
/// Object to set.
public static void Provide(T obj)
{
- InstanceTcs!.SetResult(obj);
+ InstanceTcs.SetResult(obj);
ServiceManager.Log.Debug("Service<{0}>: Provided", typeof(T).Name);
}
+ ///
+ /// Sets the service load state to failure.
+ ///
+ /// The exception.
+ public static void ProvideException(Exception exception)
+ {
+ ServiceManager.Log.Error(exception, "Service<{0}>: Error", typeof(T).Name);
+ InstanceTcs.SetException(exception);
+ }
+
///
/// Pull the instance out of the service locator, waiting if necessary.
///
@@ -103,6 +125,7 @@ namespace Dalamud
/// Gets an enumerable containing Service<T>s that are required for this Service to initialize without blocking.
///
/// List of dependency services.
+ [UsedImplicitly]
public static List GetDependencyServices()
{
var res = new List();
@@ -119,7 +142,7 @@ namespace Dalamud
.ToList();
}
- private static async Task