diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs
index 22343fd8e..a13f0e209 100644
--- a/Dalamud/Game/Framework.cs
+++ b/Dalamud/Game/Framework.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -12,23 +11,21 @@ using Dalamud.Game.Gui.Toast;
using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
+using Dalamud.Logging.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
-using Serilog;
namespace Dalamud.Game;
///
/// This class represents the Framework of the native game client and grants access to various subsystems.
///
-[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
-#pragma warning disable SA1015
-[ResolveVia]
-#pragma warning restore SA1015
internal sealed class Framework : IDisposable, IServiceType, IFramework
{
+ private static readonly ModuleLog Log = new("Framework");
+
private static readonly Stopwatch StatsStopwatch = new();
private readonly GameLifecycle lifecycle;
@@ -70,19 +67,11 @@ internal sealed class Framework : IDisposable, IServiceType, IFramework
/// A value indicating if the call was successful.
public delegate bool OnRealDestroyDelegate(IntPtr framework);
- ///
- /// A delegate type used during the native Framework::free.
- ///
- /// The native Framework address.
- public delegate IntPtr OnDestroyDelegate();
-
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate bool OnUpdateDetour(IntPtr framework);
- private delegate IntPtr OnDestroyDetour(); // OnDestroyDelegate
-
///
- public event IFramework.OnUpdateDelegate Update;
+ public event IFramework.OnUpdateDelegate? Update;
///
/// Gets or sets a value indicating whether the collection of stats is enabled.
@@ -523,3 +512,86 @@ internal sealed class Framework : IDisposable, IServiceType, IFramework
}
}
}
+
+///
+/// Plugin-scoped version of a Framework service.
+///
+[PluginInterface]
+[InterfaceVersion("1.0")]
+[ServiceManager.ScopedService]
+#pragma warning disable SA1015
+[ResolveVia]
+#pragma warning restore SA1015
+internal class FrameworkPluginScoped : IDisposable, IServiceType, IFramework
+{
+ [ServiceManager.ServiceDependency]
+ private readonly Framework frameworkService = Service.Get();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal FrameworkPluginScoped()
+ {
+ this.frameworkService.Update += this.OnUpdateForward;
+ }
+
+ ///
+ public event IFramework.OnUpdateDelegate? Update;
+
+ ///
+ public DateTime LastUpdate => this.frameworkService.LastUpdate;
+
+ ///
+ public DateTime LastUpdateUTC => this.frameworkService.LastUpdateUTC;
+
+ ///
+ public TimeSpan UpdateDelta => this.frameworkService.UpdateDelta;
+
+ ///
+ public bool IsInFrameworkUpdateThread => this.frameworkService.IsInFrameworkUpdateThread;
+
+ ///
+ public bool IsFrameworkUnloading => this.frameworkService.IsFrameworkUnloading;
+
+ ///
+ public void Dispose()
+ {
+ this.frameworkService.Update -= this.OnUpdateForward;
+
+ this.Update = null;
+ }
+
+ ///
+ public Task RunOnFrameworkThread(Func func)
+ => this.frameworkService.RunOnFrameworkThread(func);
+
+ ///
+ public Task RunOnFrameworkThread(Action action)
+ => this.frameworkService.RunOnFrameworkThread(action);
+
+ ///
+ public Task RunOnFrameworkThread(Func> func)
+ => this.frameworkService.RunOnFrameworkThread(func);
+
+ ///
+ public Task RunOnFrameworkThread(Func func)
+ => this.frameworkService.RunOnFrameworkThread(func);
+
+ ///
+ public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default)
+ => this.frameworkService.RunOnTick(func, delay, delayTicks, cancellationToken);
+
+ ///
+ public Task RunOnTick(Action action, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default)
+ => this.frameworkService.RunOnTick(action, delay, delayTicks, cancellationToken);
+
+ ///
+ public Task RunOnTick(Func> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default)
+ => this.frameworkService.RunOnTick(func, delay, delayTicks, cancellationToken);
+
+ ///
+ public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default)
+ => this.frameworkService.RunOnTick(func, delay, delayTicks, cancellationToken);
+
+ private void OnUpdateForward(IFramework framework) => this.Update?.Invoke(framework);
+}
diff --git a/Dalamud/Plugin/Services/IFramework.cs b/Dalamud/Plugin/Services/IFramework.cs
index 334577b92..ca33c5867 100644
--- a/Dalamud/Plugin/Services/IFramework.cs
+++ b/Dalamud/Plugin/Services/IFramework.cs
@@ -1,9 +1,6 @@
-using System;
-using System.Threading;
+using System.Threading;
using System.Threading.Tasks;
-using Dalamud.Game;
-
namespace Dalamud.Plugin.Services;
///