Add IInternal/PublicDisposableService (#1696)

* Add IInternal/PublicDisposableService

Plugins are exposed interfaces that are not inherited from
`IDisposable`, but services implementing plugin interfaces often
implement `IDisposable`. Some plugins may try to call
`IDisposable.Dispose` on everything provided, and it also is possible to
use `using` clause too eagerly while working on Dalamud itself, such as
writing `using var smth = await Service<SomeService>.GetAsync();`. Such
behaviors often lead to a difficult-to-debug errors, and making those
services either not an `IDisposable` or making `IDisposable.Dispose` do
nothing if the object has been loaded would prevent such errors. As
`ServiceManager` must be the only class dealing with construction and
disposal of services, `IInternalDisposableService` has been added to
limit who can dispose the object. `IPublicDisposableService` also has
been added to classes that can be constructed and accessed directly by
plugins; for those, `Dispose` will be ignored if the instance is a
service instance, and only `DisposeService` will respond.

In addition, `DalamudPluginInterface` and `UiBuilder` also have been
changed so that their `IDisposable.Dispose` no longer respond, and
instead, internal functions have been added to only allow disposal from
Dalamud.

* Cleanup

* Postmerge fixes

* More explanation on RunOnFrameworkThread(ClearHooks)

* Mark ReliableFileStorage public ctor obsolete

---------

Co-authored-by: goat <16760685+goaaats@users.noreply.github.com>
This commit is contained in:
srkizer 2024-03-17 00:58:05 +09:00 committed by GitHub
parent dcec076ca7
commit 87b9edb448
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 441 additions and 381 deletions

View file

@ -19,7 +19,7 @@ namespace Dalamud.Game.Addon.Events;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.EarlyLoadedService]
internal unsafe class AddonEventManager : IDisposable, IServiceType
internal unsafe class AddonEventManager : IInternalDisposableService
{
/// <summary>
/// PluginName for Dalamud Internal use.
@ -62,7 +62,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
private delegate nint UpdateCursorDelegate(RaptureAtkModule* module);
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.onUpdateCursor.Dispose();
@ -204,7 +204,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
#pragma warning disable SA1015
[ResolveVia<IAddonEventManager>]
#pragma warning restore SA1015
internal class AddonEventManagerPluginScoped : IDisposable, IServiceType, IAddonEventManager
internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddonEventManager
{
[ServiceManager.ServiceDependency]
private readonly AddonEventManager eventManagerService = Service<AddonEventManager>.Get();
@ -225,7 +225,7 @@ internal class AddonEventManagerPluginScoped : IDisposable, IServiceType, IAddon
}
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
// if multiple plugins force cursors and dispose without un-forcing them then all forces will be cleared.
if (this.isForcingCursor)

View file

@ -19,7 +19,7 @@ namespace Dalamud.Game.Addon.Lifecycle;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.EarlyLoadedService]
internal unsafe class AddonLifecycle : IDisposable, IServiceType
internal unsafe class AddonLifecycle : IInternalDisposableService
{
private static readonly ModuleLog Log = new("AddonLifecycle");
@ -89,7 +89,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
internal List<AddonLifecycleEventListener> EventListeners { get; } = new();
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.onAddonSetupHook.Dispose();
this.onAddonSetup2Hook.Dispose();
@ -383,7 +383,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
#pragma warning disable SA1015
[ResolveVia<IAddonLifecycle>]
#pragma warning restore SA1015
internal class AddonLifecyclePluginScoped : IDisposable, IServiceType, IAddonLifecycle
internal class AddonLifecyclePluginScoped : IInternalDisposableService, IAddonLifecycle
{
[ServiceManager.ServiceDependency]
private readonly AddonLifecycle addonLifecycleService = Service<AddonLifecycle>.Get();
@ -391,7 +391,7 @@ internal class AddonLifecyclePluginScoped : IDisposable, IServiceType, IAddonLif
private readonly List<AddonLifecycleEventListener> eventListeners = new();
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
foreach (var listener in this.eventListeners)
{

View file

@ -23,7 +23,7 @@ namespace Dalamud.Game.ClientState;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class ClientState : IDisposable, IServiceType, IClientState
internal sealed class ClientState : IInternalDisposableService, IClientState
{
private static readonly ModuleLog Log = new("ClientState");
@ -115,7 +115,7 @@ internal sealed class ClientState : IDisposable, IServiceType, IClientState
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.setupTerritoryTypeHook.Dispose();
this.framework.Update -= this.FrameworkOnOnUpdateEvent;
@ -196,7 +196,7 @@ internal sealed class ClientState : IDisposable, IServiceType, IClientState
#pragma warning disable SA1015
[ResolveVia<IClientState>]
#pragma warning restore SA1015
internal class ClientStatePluginScoped : IDisposable, IServiceType, IClientState
internal class ClientStatePluginScoped : IInternalDisposableService, IClientState
{
[ServiceManager.ServiceDependency]
private readonly ClientState clientStateService = Service<ClientState>.Get();
@ -257,7 +257,7 @@ internal class ClientStatePluginScoped : IDisposable, IServiceType, IClientState
public bool IsGPosing => this.clientStateService.IsGPosing;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.clientStateService.TerritoryChanged -= this.TerritoryChangedForward;
this.clientStateService.Login -= this.LoginForward;

View file

@ -10,7 +10,7 @@ namespace Dalamud.Game.ClientState.Conditions;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed partial class Condition : IServiceType, ICondition
internal sealed partial class Condition : IInternalDisposableService, ICondition
{
/// <summary>
/// Gets the current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has.
@ -22,6 +22,8 @@ internal sealed partial class Condition : IServiceType, ICondition
private readonly bool[] cache = new bool[MaxConditionEntries];
private bool isDisposed;
[ServiceManager.ServiceConstructor]
private Condition(ClientState clientState)
{
@ -35,6 +37,9 @@ internal sealed partial class Condition : IServiceType, ICondition
this.framework.Update += this.FrameworkUpdate;
}
/// <summary>Finalizes an instance of the <see cref="Condition" /> class.</summary>
~Condition() => this.Dispose(false);
/// <inheritdoc/>
public event ICondition.ConditionChangeDelegate? ConditionChange;
@ -60,6 +65,9 @@ internal sealed partial class Condition : IServiceType, ICondition
public bool this[ConditionFlag flag]
=> this[(int)flag];
/// <inheritdoc/>
void IInternalDisposableService.DisposeService() => this.Dispose(true);
/// <inheritdoc/>
public bool Any()
{
@ -89,6 +97,19 @@ internal sealed partial class Condition : IServiceType, ICondition
return false;
}
private void Dispose(bool disposing)
{
if (this.isDisposed)
return;
if (disposing)
{
this.framework.Update -= this.FrameworkUpdate;
}
this.isDisposed = true;
}
private void FrameworkUpdate(IFramework unused)
{
for (var i = 0; i < MaxConditionEntries; i++)
@ -112,44 +133,6 @@ internal sealed partial class Condition : IServiceType, ICondition
}
}
/// <summary>
/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc.
/// </summary>
internal sealed partial class Condition : IDisposable
{
private bool isDisposed;
/// <summary>
/// Finalizes an instance of the <see cref="Condition" /> class.
/// </summary>
~Condition()
{
this.Dispose(false);
}
/// <summary>
/// Disposes this instance, alongside its hooks.
/// </summary>
void IDisposable.Dispose()
{
GC.SuppressFinalize(this);
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (this.isDisposed)
return;
if (disposing)
{
this.framework.Update -= this.FrameworkUpdate;
}
this.isDisposed = true;
}
}
/// <summary>
/// Plugin-scoped version of a Condition service.
/// </summary>
@ -159,7 +142,7 @@ internal sealed partial class Condition : IDisposable
#pragma warning disable SA1015
[ResolveVia<ICondition>]
#pragma warning restore SA1015
internal class ConditionPluginScoped : IDisposable, IServiceType, ICondition
internal class ConditionPluginScoped : IInternalDisposableService, ICondition
{
[ServiceManager.ServiceDependency]
private readonly Condition conditionService = Service<Condition>.Get();
@ -185,7 +168,7 @@ internal class ConditionPluginScoped : IDisposable, IServiceType, ICondition
public bool this[int flag] => this.conditionService[flag];
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.conditionService.ConditionChange -= this.ConditionChangedForward;

View file

@ -21,7 +21,7 @@ namespace Dalamud.Game.ClientState.GamePad;
#pragma warning disable SA1015
[ResolveVia<IGamepadState>]
#pragma warning restore SA1015
internal unsafe class GamepadState : IDisposable, IServiceType, IGamepadState
internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
{
private readonly Hook<ControllerPoll>? gamepadPoll;
@ -109,7 +109,7 @@ internal unsafe class GamepadState : IDisposable, IServiceType, IGamepadState
/// <summary>
/// Disposes this instance, alongside its hooks.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.Dispose(true);
GC.SuppressFinalize(this);

View file

@ -19,7 +19,7 @@ namespace Dalamud.Game.Command;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class CommandManager : IServiceType, IDisposable, ICommandManager
internal sealed class CommandManager : IInternalDisposableService, ICommandManager
{
private static readonly ModuleLog Log = new("Command");
@ -130,7 +130,7 @@ internal sealed class CommandManager : IServiceType, IDisposable, ICommandManage
}
/// <inheritdoc/>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.chatGui.CheckMessageHandled -= this.OnCheckMessageHandled;
}
@ -170,7 +170,7 @@ internal sealed class CommandManager : IServiceType, IDisposable, ICommandManage
#pragma warning disable SA1015
[ResolveVia<ICommandManager>]
#pragma warning restore SA1015
internal class CommandManagerPluginScoped : IDisposable, IServiceType, ICommandManager
internal class CommandManagerPluginScoped : IInternalDisposableService, ICommandManager
{
private static readonly ModuleLog Log = new("Command");
@ -193,7 +193,7 @@ internal class CommandManagerPluginScoped : IDisposable, IServiceType, ICommandM
public ReadOnlyDictionary<string, CommandInfo> Commands => this.commandManagerService.Commands;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
foreach (var command in this.pluginRegisteredCommands)
{

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game.Config;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
internal sealed class GameConfig : IInternalDisposableService, IGameConfig
{
private readonly TaskCompletionSource tcsInitialization = new();
private readonly TaskCompletionSource<GameConfigSection> tcsSystem = new();
@ -195,7 +195,7 @@ internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
public void Set(UiControlOption option, string value) => this.UiControl.Set(option.GetName(), value);
/// <inheritdoc/>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
var ode = new ObjectDisposedException(nameof(GameConfig));
this.tcsInitialization.SetExceptionIfIncomplete(ode);
@ -248,7 +248,7 @@ internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable
#pragma warning disable SA1015
[ResolveVia<IGameConfig>]
#pragma warning restore SA1015
internal class GameConfigPluginScoped : IDisposable, IServiceType, IGameConfig
internal class GameConfigPluginScoped : IInternalDisposableService, IGameConfig
{
[ServiceManager.ServiceDependency]
private readonly GameConfig gameConfigService = Service<GameConfig>.Get();
@ -295,7 +295,7 @@ internal class GameConfigPluginScoped : IDisposable, IServiceType, IGameConfig
public GameConfigSection UiControl => this.gameConfigService.UiControl;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.gameConfigService.Changed -= this.ConfigChangedForward;
this.initializationTask.ContinueWith(

View file

@ -13,7 +13,7 @@ namespace Dalamud.Game.DutyState;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
internal unsafe class DutyState : IInternalDisposableService, IDutyState
{
private readonly DutyStateAddressResolver address;
private readonly Hook<SetupContentDirectNetworkMessageDelegate> contentDirectorNetworkMessageHook;
@ -62,7 +62,7 @@ internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
private bool CompletedThisTerritory { get; set; }
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.contentDirectorNetworkMessageHook.Dispose();
this.framework.Update -= this.FrameworkOnUpdateEvent;
@ -168,7 +168,7 @@ internal unsafe class DutyState : IDisposable, IServiceType, IDutyState
#pragma warning disable SA1015
[ResolveVia<IDutyState>]
#pragma warning restore SA1015
internal class DutyStatePluginScoped : IDisposable, IServiceType, IDutyState
internal class DutyStatePluginScoped : IInternalDisposableService, IDutyState
{
[ServiceManager.ServiceDependency]
private readonly DutyState dutyStateService = Service<DutyState>.Get();
@ -200,7 +200,7 @@ internal class DutyStatePluginScoped : IDisposable, IServiceType, IDutyState
public bool IsDutyStarted => this.dutyStateService.IsDutyStarted;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.dutyStateService.DutyStarted -= this.DutyStartedForward;
this.dutyStateService.DutyWiped -= this.DutyWipedForward;

View file

@ -23,7 +23,7 @@ namespace Dalamud.Game;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class Framework : IDisposable, IServiceType, IFramework
internal sealed class Framework : IInternalDisposableService, IFramework
{
private static readonly ModuleLog Log = new("Framework");
@ -274,7 +274,7 @@ internal sealed class Framework : IDisposable, IServiceType, IFramework
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.RunOnFrameworkThread(() =>
{
@ -469,7 +469,7 @@ internal sealed class Framework : IDisposable, IServiceType, IFramework
#pragma warning disable SA1015
[ResolveVia<IFramework>]
#pragma warning restore SA1015
internal class FrameworkPluginScoped : IDisposable, IServiceType, IFramework
internal class FrameworkPluginScoped : IInternalDisposableService, IFramework
{
[ServiceManager.ServiceDependency]
private readonly Framework frameworkService = Service<Framework>.Get();
@ -504,7 +504,7 @@ internal class FrameworkPluginScoped : IDisposable, IServiceType, IFramework
public bool IsFrameworkUnloading => this.frameworkService.IsFrameworkUnloading;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.frameworkService.Update -= this.OnUpdateForward;

View file

@ -29,7 +29,7 @@ namespace Dalamud.Game.Gui;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed unsafe class ChatGui : IDisposable, IServiceType, IChatGui
internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
{
private static readonly ModuleLog Log = new("ChatGui");
@ -109,7 +109,7 @@ internal sealed unsafe class ChatGui : IDisposable, IServiceType, IChatGui
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.printMessageHook.Dispose();
this.populateItemLinkHook.Dispose();
@ -409,7 +409,7 @@ internal sealed unsafe class ChatGui : IDisposable, IServiceType, IChatGui
#pragma warning disable SA1015
[ResolveVia<IChatGui>]
#pragma warning restore SA1015
internal class ChatGuiPluginScoped : IDisposable, IServiceType, IChatGui
internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
{
[ServiceManager.ServiceDependency]
private readonly ChatGui chatGuiService = Service<ChatGui>.Get();
@ -447,7 +447,7 @@ internal class ChatGuiPluginScoped : IDisposable, IServiceType, IChatGui
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.chatGuiService.ChatMessage -= this.OnMessageForward;
this.chatGuiService.CheckMessageHandled -= this.OnCheckMessageForward;

View file

@ -28,7 +28,7 @@ namespace Dalamud.Game.Gui.ContextMenu;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.EarlyLoadedService]
internal sealed unsafe class ContextMenu : IDisposable, IServiceType, IContextMenu
internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextMenu
{
private static readonly ModuleLog Log = new("ContextMenu");
@ -77,7 +77,7 @@ internal sealed unsafe class ContextMenu : IDisposable, IServiceType, IContextMe
private IReadOnlyList<MenuItem>? SubmenuItems { get; set; }
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
var manager = RaptureAtkUnitManager.Instance();
var menu = manager->GetAddonByName("ContextMenu");
@ -496,7 +496,7 @@ original:
#pragma warning disable SA1015
[ResolveVia<IContextMenu>]
#pragma warning restore SA1015
internal class ContextMenuPluginScoped : IDisposable, IServiceType, IContextMenu
internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMenu
{
[ServiceManager.ServiceDependency]
private readonly ContextMenu parentService = Service<ContextMenu>.Get();
@ -514,7 +514,7 @@ internal class ContextMenuPluginScoped : IDisposable, IServiceType, IContextMenu
private object MenuItemsLock { get; } = new();
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.parentService.OnMenuOpened -= this.OnMenuOpenedForward;

View file

@ -22,7 +22,7 @@ namespace Dalamud.Game.Gui.Dtr;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
{
private const uint BaseNodeId = 1000;
@ -101,7 +101,7 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
}
/// <inheritdoc/>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.addonLifecycle.UnregisterListener(this.dtrPostDrawListener);
this.addonLifecycle.UnregisterListener(this.dtrPostRequestedUpdateListener);
@ -493,7 +493,7 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
#pragma warning disable SA1015
[ResolveVia<IDtrBar>]
#pragma warning restore SA1015
internal class DtrBarPluginScoped : IDisposable, IServiceType, IDtrBar
internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar
{
[ServiceManager.ServiceDependency]
private readonly DtrBar dtrBarService = Service<DtrBar>.Get();
@ -501,7 +501,7 @@ internal class DtrBarPluginScoped : IDisposable, IServiceType, IDtrBar
private readonly Dictionary<string, DtrBarEntry> pluginEntries = new();
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
foreach (var entry in this.pluginEntries)
{

View file

@ -16,7 +16,7 @@ namespace Dalamud.Game.Gui.FlyText;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class FlyTextGui : IDisposable, IServiceType, IFlyTextGui
internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui
{
/// <summary>
/// The native function responsible for adding fly text to the UI. See <see cref="FlyTextGuiAddressResolver.AddFlyText"/>.
@ -78,7 +78,7 @@ internal sealed class FlyTextGui : IDisposable, IServiceType, IFlyTextGui
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.createFlyTextHook.Dispose();
}
@ -277,7 +277,7 @@ internal sealed class FlyTextGui : IDisposable, IServiceType, IFlyTextGui
#pragma warning disable SA1015
[ResolveVia<IFlyTextGui>]
#pragma warning restore SA1015
internal class FlyTextGuiPluginScoped : IDisposable, IServiceType, IFlyTextGui
internal class FlyTextGuiPluginScoped : IInternalDisposableService, IFlyTextGui
{
[ServiceManager.ServiceDependency]
private readonly FlyTextGui flyTextGuiService = Service<FlyTextGui>.Get();
@ -294,7 +294,7 @@ internal class FlyTextGuiPluginScoped : IDisposable, IServiceType, IFlyTextGui
public event IFlyTextGui.OnFlyTextCreatedDelegate? FlyTextCreated;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.flyTextGuiService.FlyTextCreated -= this.FlyTextCreatedForward;

View file

@ -27,7 +27,7 @@ namespace Dalamud.Game.Gui;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui
internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui
{
private static readonly ModuleLog Log = new("GameGui");
@ -344,7 +344,7 @@ internal sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui
/// <summary>
/// Disables the hooks and submodules of this module.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.setGlobalBgmHook.Dispose();
this.handleItemHoverHook.Dispose();
@ -520,7 +520,7 @@ internal sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui
#pragma warning disable SA1015
[ResolveVia<IGameGui>]
#pragma warning restore SA1015
internal class GameGuiPluginScoped : IDisposable, IServiceType, IGameGui
internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui
{
[ServiceManager.ServiceDependency]
private readonly GameGui gameGuiService = Service<GameGui>.Get();
@ -558,7 +558,7 @@ internal class GameGuiPluginScoped : IDisposable, IServiceType, IGameGui
public HoveredAction HoveredAction => this.gameGuiService.HoveredAction;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.gameGuiService.UiHideToggled -= this.UiHideToggledForward;
this.gameGuiService.HoveredItemChanged -= this.HoveredItemForward;

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game.Gui.PartyFinder;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class PartyFinderGui : IDisposable, IServiceType, IPartyFinderGui
internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderGui
{
private readonly PartyFinderAddressResolver address;
private readonly IntPtr memory;
@ -47,7 +47,7 @@ internal sealed class PartyFinderGui : IDisposable, IServiceType, IPartyFinderGu
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.receiveListingHook.Dispose();
@ -131,7 +131,7 @@ internal sealed class PartyFinderGui : IDisposable, IServiceType, IPartyFinderGu
#pragma warning disable SA1015
[ResolveVia<IPartyFinderGui>]
#pragma warning restore SA1015
internal class PartyFinderGuiPluginScoped : IDisposable, IServiceType, IPartyFinderGui
internal class PartyFinderGuiPluginScoped : IInternalDisposableService, IPartyFinderGui
{
[ServiceManager.ServiceDependency]
private readonly PartyFinderGui partyFinderGuiService = Service<PartyFinderGui>.Get();
@ -148,7 +148,7 @@ internal class PartyFinderGuiPluginScoped : IDisposable, IServiceType, IPartyFin
public event IPartyFinderGui.PartyFinderListingEventDelegate? ReceiveListing;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.partyFinderGuiService.ReceiveListing -= this.ReceiveListingForward;

View file

@ -14,7 +14,7 @@ namespace Dalamud.Game.Gui.Toast;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed partial class ToastGui : IDisposable, IServiceType, IToastGui
internal sealed partial class ToastGui : IInternalDisposableService, IToastGui
{
private const uint QuestToastCheckmarkMagic = 60081;
@ -73,7 +73,7 @@ internal sealed partial class ToastGui : IDisposable, IServiceType, IToastGui
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.showNormalToastHook.Dispose();
this.showQuestToastHook.Dispose();
@ -383,7 +383,7 @@ internal sealed partial class ToastGui
#pragma warning disable SA1015
[ResolveVia<IToastGui>]
#pragma warning restore SA1015
internal class ToastGuiPluginScoped : IDisposable, IServiceType, IToastGui
internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui
{
[ServiceManager.ServiceDependency]
private readonly ToastGui toastGuiService = Service<ToastGui>.Get();
@ -408,7 +408,7 @@ internal class ToastGuiPluginScoped : IDisposable, IServiceType, IToastGui
public event IToastGui.OnErrorToastDelegate? ErrorToast;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.toastGuiService.Toast -= this.ToastForward;
this.toastGuiService.QuestToast -= this.QuestToastForward;

View file

@ -12,7 +12,7 @@ namespace Dalamud.Game.Internal;
/// This class disables anti-debug functionality in the game client.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal sealed partial class AntiDebug : IServiceType
internal sealed class AntiDebug : IInternalDisposableService
{
private readonly byte[] nop = new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 };
private byte[] original;
@ -43,16 +43,25 @@ internal sealed partial class AntiDebug : IServiceType
}
}
/// <summary>Finalizes an instance of the <see cref="AntiDebug"/> class.</summary>
~AntiDebug() => this.Disable();
/// <summary>
/// Gets a value indicating whether the anti-debugging is enabled.
/// </summary>
public bool IsEnabled { get; private set; } = false;
/// <inheritdoc />
void IInternalDisposableService.DisposeService() => this.Disable();
/// <summary>
/// Enables the anti-debugging by overwriting code in memory.
/// </summary>
public void Enable()
{
if (this.IsEnabled)
return;
this.original = new byte[this.nop.Length];
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled)
{
@ -73,6 +82,9 @@ internal sealed partial class AntiDebug : IServiceType
/// </summary>
public void Disable()
{
if (!this.IsEnabled)
return;
if (this.debugCheckAddress != IntPtr.Zero && this.original != null)
{
Log.Information($"Reverting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
@ -86,45 +98,3 @@ internal sealed partial class AntiDebug : IServiceType
this.IsEnabled = false;
}
}
/// <summary>
/// Implementing IDisposable.
/// </summary>
internal sealed partial class AntiDebug : IDisposable
{
private bool disposed = false;
/// <summary>
/// Finalizes an instance of the <see cref="AntiDebug"/> class.
/// </summary>
~AntiDebug() => this.Dispose(false);
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
/// <param name="disposing">If this was disposed through calling Dispose() or from being finalized.</param>
private void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
// If anti-debug is enabled and is being disposed, odds are either the game is exiting, or Dalamud is being reloaded.
// If it is the latter, there's half a chance a debugger is currently attached. There's no real need to disable the
// check in either situation anyways. However if Dalamud is being reloaded, the sig may fail so may as well undo it.
this.Disable();
}
this.disposed = true;
}
}

View file

@ -20,7 +20,7 @@ namespace Dalamud.Game.Internal;
/// This class implements in-game Dalamud options in the in-game System menu.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService
{
private readonly AtkValueChangeType atkValueChangeType;
private readonly AtkValueSetString atkValueSetString;
@ -40,6 +40,8 @@ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
private readonly string locDalamudPlugins;
private readonly string locDalamudSettings;
private bool disposed = false;
[ServiceManager.ServiceConstructor]
private DalamudAtkTweaks(TargetSigScanner sigScanner)
{
@ -69,6 +71,9 @@ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
this.hookAtkUnitBaseReceiveGlobalEvent.Enable();
}
/// <summary>Finalizes an instance of the <see cref="DalamudAtkTweaks"/> class.</summary>
~DalamudAtkTweaks() => this.Dispose(false);
private delegate void AgentHudOpenSystemMenuPrototype(void* thisPtr, AtkValue* atkValueArgs, uint menuSize);
private delegate void AtkValueChangeType(AtkValue* thisPtr, ValueType type);
@ -79,6 +84,26 @@ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
private delegate IntPtr AtkUnitBaseReceiveGlobalEvent(AtkUnitBase* thisPtr, ushort cmd, uint a3, IntPtr a4, uint* a5);
/// <inheritdoc/>
void IInternalDisposableService.DisposeService() => this.Dispose(true);
private void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
this.hookAgentHudOpenSystemMenu.Dispose();
this.hookUiModuleRequestMainCommand.Dispose();
this.hookAtkUnitBaseReceiveGlobalEvent.Dispose();
// this.contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened;
}
this.disposed = true;
}
/*
private void ContextMenuOnContextMenuOpened(ContextMenuOpenedArgs args)
{
@ -229,45 +254,3 @@ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType
}
}
}
/// <summary>
/// Implements IDisposable.
/// </summary>
internal sealed partial class DalamudAtkTweaks : IDisposable
{
private bool disposed = false;
/// <summary>
/// Finalizes an instance of the <see cref="DalamudAtkTweaks"/> class.
/// </summary>
~DalamudAtkTweaks() => this.Dispose(false);
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
private void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
this.hookAgentHudOpenSystemMenu.Dispose();
this.hookUiModuleRequestMainCommand.Dispose();
this.hookAtkUnitBaseReceiveGlobalEvent.Dispose();
// this.contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened;
}
this.disposed = true;
}
}

View file

@ -19,7 +19,7 @@ namespace Dalamud.Game.Inventory;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal class GameInventory : IDisposable, IServiceType
internal class GameInventory : IInternalDisposableService
{
private readonly List<GameInventoryPluginScoped> subscribersPendingChange = new();
private readonly List<GameInventoryPluginScoped> subscribers = new();
@ -61,7 +61,7 @@ internal class GameInventory : IDisposable, IServiceType
private unsafe delegate void RaptureAtkModuleUpdateDelegate(RaptureAtkModule* ram, float f1);
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
lock (this.subscribersPendingChange)
{
@ -351,7 +351,7 @@ internal class GameInventory : IDisposable, IServiceType
#pragma warning disable SA1015
[ResolveVia<IGameInventory>]
#pragma warning restore SA1015
internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInventory
internal class GameInventoryPluginScoped : IInternalDisposableService, IGameInventory
{
private static readonly ModuleLog Log = new(nameof(GameInventoryPluginScoped));
@ -406,7 +406,7 @@ internal class GameInventoryPluginScoped : IDisposable, IServiceType, IGameInven
public event IGameInventory.InventoryChangedDelegate<InventoryItemMergedArgs>? ItemMergedExplicit;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.gameInventoryService.Unsubscribe(this);

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game.Network;
/// </summary>
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
internal sealed class GameNetwork : IDisposable, IServiceType, IGameNetwork
internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork
{
private readonly GameNetworkAddressResolver address;
private readonly Hook<ProcessZonePacketDownDelegate> processZonePacketDownHook;
@ -59,7 +59,7 @@ internal sealed class GameNetwork : IDisposable, IServiceType, IGameNetwork
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
/// <inheritdoc/>
void IDisposable.Dispose()
void IInternalDisposableService.DisposeService()
{
this.processZonePacketDownHook.Dispose();
this.processZonePacketUpHook.Dispose();
@ -145,7 +145,7 @@ internal sealed class GameNetwork : IDisposable, IServiceType, IGameNetwork
#pragma warning disable SA1015
[ResolveVia<IGameNetwork>]
#pragma warning restore SA1015
internal class GameNetworkPluginScoped : IDisposable, IServiceType, IGameNetwork
internal class GameNetworkPluginScoped : IInternalDisposableService, IGameNetwork
{
[ServiceManager.ServiceDependency]
private readonly GameNetwork gameNetworkService = Service<GameNetwork>.Get();
@ -162,7 +162,7 @@ internal class GameNetworkPluginScoped : IDisposable, IServiceType, IGameNetwork
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
/// <inheritdoc/>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.gameNetworkService.NetworkMessage -= this.NetworkMessageForward;

View file

@ -26,7 +26,7 @@ namespace Dalamud.Game.Network.Internal;
/// This class handles network notifications and uploading market board data.
/// </summary>
[ServiceManager.BlockingEarlyLoadedService]
internal unsafe class NetworkHandlers : IDisposable, IServiceType
internal unsafe class NetworkHandlers : IInternalDisposableService
{
private readonly IMarketBoardUploader uploader;
@ -213,7 +213,7 @@ internal unsafe class NetworkHandlers : IDisposable, IServiceType
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.disposing = true;
this.Dispose(this.disposing);

View file

@ -10,7 +10,7 @@ namespace Dalamud.Game.Network.Internal;
/// This class enables TCP optimizations in the game socket for better performance.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal sealed class WinSockHandlers : IDisposable, IServiceType
internal sealed class WinSockHandlers : IInternalDisposableService
{
private Hook<SocketDelegate> ws2SocketHook;
@ -27,7 +27,7 @@ internal sealed class WinSockHandlers : IDisposable, IServiceType
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public void Dispose()
void IInternalDisposableService.DisposeService()
{
this.ws2SocketHook?.Dispose();
}

View file

@ -104,6 +104,10 @@ public class SigScanner : IDisposable, ISigScanner
/// <inheritdoc/>
public ProcessModule Module { get; }
/// <summary>Gets or sets a value indicating whether this instance of <see cref="SigScanner"/> is meant to be a
/// Dalamud service.</summary>
private protected bool IsService { get; set; }
private IntPtr TextSectionTop => this.TextSectionBase + this.TextSectionSize;
/// <summary>
@ -309,13 +313,11 @@ public class SigScanner : IDisposable, ISigScanner
}
}
/// <summary>
/// Free the memory of the copied module search area on object disposal, if applicable.
/// </summary>
/// <inheritdoc/>
public void Dispose()
{
this.Save();
Marshal.FreeHGlobal(this.moduleCopyPtr);
if (!this.IsService)
this.DisposeCore();
}
/// <summary>
@ -337,6 +339,15 @@ public class SigScanner : IDisposable, ISigScanner
}
}
/// <summary>
/// Free the memory of the copied module search area on object disposal, if applicable.
/// </summary>
private protected void DisposeCore()
{
this.Save();
Marshal.FreeHGlobal(this.moduleCopyPtr);
}
/// <summary>
/// Helper for ScanText to get the correct address for IDA sigs that mark the first JMP or CALL location.
/// </summary>

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game;
#pragma warning disable SA1015
[ResolveVia<ISigScanner>]
#pragma warning restore SA1015
internal class TargetSigScanner : SigScanner, IServiceType
internal class TargetSigScanner : SigScanner, IPublicDisposableService
{
/// <summary>
/// Initializes a new instance of the <see cref="TargetSigScanner"/> class.
@ -26,4 +26,14 @@ internal class TargetSigScanner : SigScanner, IServiceType
: base(Process.GetCurrentProcess().MainModule!, doCopy, cacheFile)
{
}
/// <inheritdoc/>
void IInternalDisposableService.DisposeService()
{
if (this.IsService)
this.DisposeCore();
}
/// <inheritdoc/>
void IPublicDisposableService.MarkDisposeOnlyFromService() => this.IsService = true;
}