mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge pull request #1695 from goatcorp/net8-rollup
[net8] Rollup changes from master
This commit is contained in:
commit
27def97228
17 changed files with 1345 additions and 868 deletions
|
|
@ -111,10 +111,6 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Game\Addon\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="AddRuntimeDependenciesToContent" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles">
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
|
||||
|
|
|
|||
107
Dalamud/Game/Addon/AddonLifecyclePooledArgs.cs
Normal file
107
Dalamud/Game/Addon/AddonLifecyclePooledArgs.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
||||
namespace Dalamud.Game.Addon;
|
||||
|
||||
/// <summary>Argument pool for Addon Lifecycle services.</summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed class AddonLifecyclePooledArgs : IServiceType
|
||||
{
|
||||
private readonly AddonSetupArgs?[] addonSetupArgPool = new AddonSetupArgs?[64];
|
||||
private readonly AddonFinalizeArgs?[] addonFinalizeArgPool = new AddonFinalizeArgs?[64];
|
||||
private readonly AddonDrawArgs?[] addonDrawArgPool = new AddonDrawArgs?[64];
|
||||
private readonly AddonUpdateArgs?[] addonUpdateArgPool = new AddonUpdateArgs?[64];
|
||||
private readonly AddonRefreshArgs?[] addonRefreshArgPool = new AddonRefreshArgs?[64];
|
||||
private readonly AddonRequestedUpdateArgs?[] addonRequestedUpdateArgPool = new AddonRequestedUpdateArgs?[64];
|
||||
private readonly AddonReceiveEventArgs?[] addonReceiveEventArgPool = new AddonReceiveEventArgs?[64];
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private AddonLifecyclePooledArgs()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonSetupArgs> Rent(out AddonSetupArgs arg) => new(out arg, this.addonSetupArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonFinalizeArgs> Rent(out AddonFinalizeArgs arg) => new(out arg, this.addonFinalizeArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonDrawArgs> Rent(out AddonDrawArgs arg) => new(out arg, this.addonDrawArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonUpdateArgs> Rent(out AddonUpdateArgs arg) => new(out arg, this.addonUpdateArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonRefreshArgs> Rent(out AddonRefreshArgs arg) => new(out arg, this.addonRefreshArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonRequestedUpdateArgs> Rent(out AddonRequestedUpdateArgs arg) =>
|
||||
new(out arg, this.addonRequestedUpdateArgPool);
|
||||
|
||||
/// <summary>Rents an instance of an argument.</summary>
|
||||
/// <param name="arg">The rented instance.</param>
|
||||
/// <returns>The returner.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public PooledEntry<AddonReceiveEventArgs> Rent(out AddonReceiveEventArgs arg) =>
|
||||
new(out arg, this.addonReceiveEventArgPool);
|
||||
|
||||
/// <summary>Returns the object to the pool on dispose.</summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
public readonly ref struct PooledEntry<T>
|
||||
where T : AddonArgs, new()
|
||||
{
|
||||
private readonly Span<T> pool;
|
||||
private readonly T obj;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PooledEntry{T}"/> struct.</summary>
|
||||
/// <param name="arg">An instance of the argument.</param>
|
||||
/// <param name="pool">The pool to rent from and return to.</param>
|
||||
public PooledEntry(out T arg, Span<T> pool)
|
||||
{
|
||||
this.pool = pool;
|
||||
foreach (ref var item in pool)
|
||||
{
|
||||
if (Interlocked.Exchange(ref item, null) is { } v)
|
||||
{
|
||||
this.obj = arg = v;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.obj = arg = new();
|
||||
}
|
||||
|
||||
/// <summary>Returns the item to the pool.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
var tmp = this.obj;
|
||||
foreach (ref var item in this.pool)
|
||||
{
|
||||
if (Interlocked.Exchange(ref item, tmp) is not { } tmp2)
|
||||
return;
|
||||
tmp = tmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ namespace Dalamud.Game.Addon.Events;
|
|||
/// Service provider for addon event management.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal unsafe class AddonEventManager : IDisposable, IServiceType
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ public abstract unsafe class AddonArgs
|
|||
get => this.addon;
|
||||
set
|
||||
{
|
||||
if (this.addon == value)
|
||||
return;
|
||||
|
||||
this.addon = value;
|
||||
|
||||
// Note: always clear addonName on updating the addon being pointed.
|
||||
// Same address may point to a different addon.
|
||||
this.addonName = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
|
@ -19,7 +18,7 @@ namespace Dalamud.Game.Addon.Lifecycle;
|
|||
/// This class provides events for in-game addon lifecycles.
|
||||
/// </summary>
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||
{
|
||||
private static readonly ModuleLog Log = new("AddonLifecycle");
|
||||
|
|
@ -27,6 +26,9 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
[ServiceManager.ServiceDependency]
|
||||
private readonly Framework framework = Service<Framework>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly AddonLifecyclePooledArgs argsPool = Service<AddonLifecyclePooledArgs>.Get();
|
||||
|
||||
private readonly nint disallowedReceiveEventAddress;
|
||||
|
||||
private readonly AddonLifecycleAddressResolver address;
|
||||
|
|
@ -38,18 +40,6 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
private readonly Hook<AddonOnRefreshDelegate> onAddonRefreshHook;
|
||||
private readonly CallHook<AddonOnRequestedUpdateDelegate> onAddonRequestedUpdateHook;
|
||||
|
||||
// Note: these can be sourced from ObjectPool of appropriate types instead, but since we don't import that NuGet
|
||||
// package, and these events are always called from the main thread, this is fine.
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// TODO: turn constructors of these internal
|
||||
private readonly AddonSetupArgs recyclingSetupArgs = new();
|
||||
private readonly AddonFinalizeArgs recyclingFinalizeArgs = new();
|
||||
private readonly AddonDrawArgs recyclingDrawArgs = new();
|
||||
private readonly AddonUpdateArgs recyclingUpdateArgs = new();
|
||||
private readonly AddonRefreshArgs recyclingRefreshArgs = new();
|
||||
private readonly AddonRequestedUpdateArgs recyclingRequestedUpdateArgs = new();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private AddonLifecycle(TargetSigScanner sigScanner)
|
||||
{
|
||||
|
|
@ -253,12 +243,13 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Exception in OnAddonSetup ReceiveEvent Registration.");
|
||||
}
|
||||
|
||||
this.recyclingSetupArgs.AddonInternal = (nint)addon;
|
||||
this.recyclingSetupArgs.AtkValueCount = valueCount;
|
||||
this.recyclingSetupArgs.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreSetup, this.recyclingSetupArgs);
|
||||
valueCount = this.recyclingSetupArgs.AtkValueCount;
|
||||
values = (AtkValue*)this.recyclingSetupArgs.AtkValues;
|
||||
using var returner = this.argsPool.Rent(out AddonSetupArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.AtkValueCount = valueCount;
|
||||
arg.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreSetup, arg);
|
||||
valueCount = arg.AtkValueCount;
|
||||
values = (AtkValue*)arg.AtkValues;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -269,7 +260,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Caught exception when calling original AddonSetup. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.InvokeListenersSafely(AddonEvent.PostSetup, this.recyclingSetupArgs);
|
||||
this.InvokeListenersSafely(AddonEvent.PostSetup, arg);
|
||||
}
|
||||
|
||||
private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase)
|
||||
|
|
@ -284,8 +275,9 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Exception in OnAddonFinalize ReceiveEvent Removal.");
|
||||
}
|
||||
|
||||
this.recyclingFinalizeArgs.AddonInternal = (nint)atkUnitBase[0];
|
||||
this.InvokeListenersSafely(AddonEvent.PreFinalize, this.recyclingFinalizeArgs);
|
||||
using var returner = this.argsPool.Rent(out AddonFinalizeArgs arg);
|
||||
arg.AddonInternal = (nint)atkUnitBase[0];
|
||||
this.InvokeListenersSafely(AddonEvent.PreFinalize, arg);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -299,8 +291,9 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
|
||||
private void OnAddonDraw(AtkUnitBase* addon)
|
||||
{
|
||||
this.recyclingDrawArgs.AddonInternal = (nint)addon;
|
||||
this.InvokeListenersSafely(AddonEvent.PreDraw, this.recyclingDrawArgs);
|
||||
using var returner = this.argsPool.Rent(out AddonDrawArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
this.InvokeListenersSafely(AddonEvent.PreDraw, arg);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -311,14 +304,15 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Caught exception when calling original AddonDraw. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.InvokeListenersSafely(AddonEvent.PostDraw, this.recyclingDrawArgs);
|
||||
this.InvokeListenersSafely(AddonEvent.PostDraw, arg);
|
||||
}
|
||||
|
||||
private void OnAddonUpdate(AtkUnitBase* addon, float delta)
|
||||
{
|
||||
this.recyclingUpdateArgs.AddonInternal = (nint)addon;
|
||||
this.recyclingUpdateArgs.TimeDeltaInternal = delta;
|
||||
this.InvokeListenersSafely(AddonEvent.PreUpdate, this.recyclingUpdateArgs);
|
||||
using var returner = this.argsPool.Rent(out AddonUpdateArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.TimeDeltaInternal = delta;
|
||||
this.InvokeListenersSafely(AddonEvent.PreUpdate, arg);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -329,19 +323,20 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Caught exception when calling original AddonUpdate. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.InvokeListenersSafely(AddonEvent.PostUpdate, this.recyclingUpdateArgs);
|
||||
this.InvokeListenersSafely(AddonEvent.PostUpdate, arg);
|
||||
}
|
||||
|
||||
private byte OnAddonRefresh(AtkUnitManager* atkUnitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values)
|
||||
{
|
||||
byte result = 0;
|
||||
|
||||
this.recyclingRefreshArgs.AddonInternal = (nint)addon;
|
||||
this.recyclingRefreshArgs.AtkValueCount = valueCount;
|
||||
this.recyclingRefreshArgs.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRefresh, this.recyclingRefreshArgs);
|
||||
valueCount = this.recyclingRefreshArgs.AtkValueCount;
|
||||
values = (AtkValue*)this.recyclingRefreshArgs.AtkValues;
|
||||
using var returner = this.argsPool.Rent(out AddonRefreshArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.AtkValueCount = valueCount;
|
||||
arg.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRefresh, arg);
|
||||
valueCount = arg.AtkValueCount;
|
||||
values = (AtkValue*)arg.AtkValues;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -352,18 +347,19 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Caught exception when calling original AddonRefresh. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.InvokeListenersSafely(AddonEvent.PostRefresh, this.recyclingRefreshArgs);
|
||||
this.InvokeListenersSafely(AddonEvent.PostRefresh, arg);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
|
||||
{
|
||||
this.recyclingRequestedUpdateArgs.AddonInternal = (nint)addon;
|
||||
this.recyclingRequestedUpdateArgs.NumberArrayData = (nint)numberArrayData;
|
||||
this.recyclingRequestedUpdateArgs.StringArrayData = (nint)stringArrayData;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, this.recyclingRequestedUpdateArgs);
|
||||
numberArrayData = (NumberArrayData**)this.recyclingRequestedUpdateArgs.NumberArrayData;
|
||||
stringArrayData = (StringArrayData**)this.recyclingRequestedUpdateArgs.StringArrayData;
|
||||
using var returner = this.argsPool.Rent(out AddonRequestedUpdateArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.NumberArrayData = (nint)numberArrayData;
|
||||
arg.StringArrayData = (nint)stringArrayData;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, arg);
|
||||
numberArrayData = (NumberArrayData**)arg.NumberArrayData;
|
||||
stringArrayData = (StringArrayData**)arg.StringArrayData;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -374,7 +370,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
|||
Log.Error(e, "Caught exception when calling original AddonRequestedUpdate. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.InvokeListenersSafely(AddonEvent.PostRequestedUpdate, this.recyclingRequestedUpdateArgs);
|
||||
this.InvokeListenersSafely(AddonEvent.PostRequestedUpdate, arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,8 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
|
|||
{
|
||||
private static readonly ModuleLog Log = new("AddonLifecycle");
|
||||
|
||||
// Note: these can be sourced from ObjectPool of appropriate types instead, but since we don't import that NuGet
|
||||
// package, and these events are always called from the main thread, this is fine.
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// TODO: turn constructors of these internal
|
||||
private readonly AddonReceiveEventArgs recyclingReceiveEventArgs = new();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly AddonLifecyclePooledArgs argsPool = Service<AddonLifecyclePooledArgs>.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddonLifecycleReceiveEventListener"/> class.
|
||||
|
|
@ -82,16 +78,17 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
this.recyclingReceiveEventArgs.AddonInternal = (nint)addon;
|
||||
this.recyclingReceiveEventArgs.AtkEventType = (byte)eventType;
|
||||
this.recyclingReceiveEventArgs.EventParam = eventParam;
|
||||
this.recyclingReceiveEventArgs.AtkEvent = (IntPtr)atkEvent;
|
||||
this.recyclingReceiveEventArgs.Data = data;
|
||||
this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PreReceiveEvent, this.recyclingReceiveEventArgs);
|
||||
eventType = (AtkEventType)this.recyclingReceiveEventArgs.AtkEventType;
|
||||
eventParam = this.recyclingReceiveEventArgs.EventParam;
|
||||
atkEvent = (AtkEvent*)this.recyclingReceiveEventArgs.AtkEvent;
|
||||
data = this.recyclingReceiveEventArgs.Data;
|
||||
using var returner = this.argsPool.Rent(out AddonReceiveEventArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.AtkEventType = (byte)eventType;
|
||||
arg.EventParam = eventParam;
|
||||
arg.AtkEvent = (IntPtr)atkEvent;
|
||||
arg.Data = data;
|
||||
this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PreReceiveEvent, arg);
|
||||
eventType = (AtkEventType)arg.AtkEventType;
|
||||
eventParam = arg.EventParam;
|
||||
atkEvent = (AtkEvent*)arg.AtkEvent;
|
||||
data = arg.Data;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -102,6 +99,6 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
|
|||
Log.Error(e, "Caught exception when calling original AddonReceiveEvent. This may be a bug in the game or another plugin hooking this method.");
|
||||
}
|
||||
|
||||
this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PostReceiveEvent, this.recyclingReceiveEventArgs);
|
||||
this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PostReceiveEvent, arg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Game.ClientState.Statuses;
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Objects.Types;
|
||||
|
||||
|
|
@ -57,8 +58,22 @@ public unsafe class BattleChara : Character
|
|||
/// <summary>
|
||||
/// Gets the total casting time of the spell being cast by the chara.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can only be a portion of the total cast for some actions.
|
||||
/// Use AdjustedTotalCastTime if you always need the total cast time.
|
||||
/// </remarks>
|
||||
[Api10ToDo("Rename so it is not confused with AdjustedTotalCastTime")]
|
||||
public float TotalCastTime => this.Struct->GetCastInfo->TotalCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TotalCastTime"/> plus any adjustments from the game, such as Action offset 2B. Used for display purposes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the actual total cast time for all actions.
|
||||
/// </remarks>
|
||||
[Api10ToDo("Rename so it is not confused with TotalCastTime")]
|
||||
public float AdjustedTotalCastTime => this.Struct->GetCastInfo->AdjustedTotalCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying structure.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Dalamud.Game.Gui;
|
|||
using Dalamud.Game.Network.Internal.MarketBoardUploaders;
|
||||
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
|
||||
using Dalamud.Game.Network.Structures;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Networking.Http;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -268,8 +269,8 @@ internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
|||
return result;
|
||||
}
|
||||
|
||||
var cfcName = cfCondition.Name.ToString();
|
||||
if (cfcName.IsNullOrEmpty())
|
||||
var cfcName = cfCondition.Name.ToDalamudString();
|
||||
if (cfcName.Payloads.Count == 0)
|
||||
{
|
||||
cfcName = "Duty Roulette";
|
||||
cfCondition.Image = 112324;
|
||||
|
|
@ -279,7 +280,10 @@ internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
|||
{
|
||||
if (this.configuration.DutyFinderChatMessage)
|
||||
{
|
||||
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
|
||||
var b = new SeStringBuilder();
|
||||
b.Append("Duty pop: ");
|
||||
b.Append(cfcName);
|
||||
Service<ChatGui>.GetNullable()?.Print(b.Build());
|
||||
}
|
||||
|
||||
this.CfPop.InvokeSafely(cfCondition);
|
||||
|
|
|
|||
|
|
@ -96,12 +96,6 @@ internal class DalamudCommands : IServiceType
|
|||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xlime", new CommandInfo(this.OnDebugDrawIMEPanel)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudIMEPanelHelp", "Draw IME panel"),
|
||||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xllog", new CommandInfo(this.OnOpenLog)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open dev log DEBUG"),
|
||||
|
|
@ -308,11 +302,6 @@ internal class DalamudCommands : IServiceType
|
|||
dalamudInterface.ToggleDataWindow(arguments);
|
||||
}
|
||||
|
||||
private void OnDebugDrawIMEPanel(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenImeWindow();
|
||||
}
|
||||
|
||||
private void OnOpenLog(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().ToggleLogWindow();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -61,7 +61,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
private readonly ComponentDemoWindow componentDemoWindow;
|
||||
private readonly DataWindow dataWindow;
|
||||
private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow;
|
||||
private readonly DalamudImeWindow imeWindow;
|
||||
private readonly ConsoleWindow consoleWindow;
|
||||
private readonly PluginStatWindow pluginStatWindow;
|
||||
private readonly PluginInstallerWindow pluginWindow;
|
||||
|
|
@ -114,7 +113,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
|
||||
this.dataWindow = new DataWindow() { IsOpen = false };
|
||||
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow() { IsOpen = false };
|
||||
this.imeWindow = new DalamudImeWindow() { IsOpen = false };
|
||||
this.consoleWindow = new ConsoleWindow(configuration) { IsOpen = configuration.LogOpenAtStartup };
|
||||
this.pluginStatWindow = new PluginStatWindow() { IsOpen = false };
|
||||
this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false };
|
||||
|
|
@ -142,7 +140,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
this.WindowSystem.AddWindow(this.componentDemoWindow);
|
||||
this.WindowSystem.AddWindow(this.dataWindow);
|
||||
this.WindowSystem.AddWindow(this.gamepadModeNotifierWindow);
|
||||
this.WindowSystem.AddWindow(this.imeWindow);
|
||||
this.WindowSystem.AddWindow(this.consoleWindow);
|
||||
this.WindowSystem.AddWindow(this.pluginStatWindow);
|
||||
this.WindowSystem.AddWindow(this.pluginWindow);
|
||||
|
|
@ -265,11 +262,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
public void OpenGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.IsOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void OpenImeWindow() => this.imeWindow.IsOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="ConsoleWindow"/>.
|
||||
/// </summary>
|
||||
|
|
@ -365,11 +357,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
|
||||
#region Close
|
||||
|
||||
/// <summary>
|
||||
/// Closes the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void CloseImeWindow() => this.imeWindow.IsOpen = false;
|
||||
|
||||
/// <summary>
|
||||
/// Closes the <see cref="GamepadModeNotifierWindow"/>.
|
||||
/// </summary>
|
||||
|
|
@ -417,11 +404,6 @@ internal class DalamudInterface : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
public void ToggleGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="DalamudImeWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleImeWindow() => this.imeWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="ConsoleWindow"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -67,9 +67,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudIme dalamudIme = Service<DalamudIme>.Get();
|
||||
|
||||
private readonly SwapChainVtableResolver address = new();
|
||||
private readonly Hook<SetCursorDelegate> setCursorHook;
|
||||
|
|
@ -627,8 +624,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
var r = this.scene?.ProcessWndProcW(args.Hwnd, (User32.WindowMessage)args.Message, args.WParam, args.LParam);
|
||||
if (r is not null)
|
||||
args.SuppressWithValue(r.Value);
|
||||
|
||||
this.dalamudIme.ProcessImeMessage(args);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,266 +0,0 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Interface.Windowing;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// A window for displaying IME details.
|
||||
/// </summary>
|
||||
internal unsafe class DalamudImeWindow : Window
|
||||
{
|
||||
private const int ImePageSize = 9;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudImeWindow"/> class.
|
||||
/// </summary>
|
||||
public DalamudImeWindow()
|
||||
: base(
|
||||
"Dalamud IME",
|
||||
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoBackground)
|
||||
{
|
||||
this.Size = default(Vector2);
|
||||
|
||||
this.RespectCloseHotkey = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Draw()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void PostDraw()
|
||||
{
|
||||
if (Service<DalamudIme>.GetNullable() is not { } ime)
|
||||
return;
|
||||
|
||||
var viewport = ime.AssociatedViewport;
|
||||
if (viewport.NativePtr is null)
|
||||
return;
|
||||
|
||||
var drawCand = ime.ImmCand.Count != 0;
|
||||
var drawConv = drawCand || ime.ShowPartialConversion;
|
||||
var drawIme = ime.InputModeIcon != 0;
|
||||
var imeIconFont = InterfaceManager.DefaultFont;
|
||||
|
||||
var pad = ImGui.GetStyle().WindowPadding;
|
||||
var candTextSize = ImGui.CalcTextSize(ime.ImmComp == string.Empty ? " " : ime.ImmComp);
|
||||
|
||||
var native = ime.ImmCandNative;
|
||||
var totalIndex = native.dwSelection + 1;
|
||||
var totalSize = native.dwCount;
|
||||
|
||||
var pageStart = native.dwPageStart;
|
||||
var pageIndex = (pageStart / ImePageSize) + 1;
|
||||
var pageCount = (totalSize / ImePageSize) + 1;
|
||||
var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
|
||||
|
||||
// Calc the window size.
|
||||
var maxTextWidth = 0f;
|
||||
for (var i = 0; i < ime.ImmCand.Count; i++)
|
||||
{
|
||||
var textSize = ImGui.CalcTextSize($"{i + 1}. {ime.ImmCand[i]}");
|
||||
maxTextWidth = maxTextWidth > textSize.X ? maxTextWidth : textSize.X;
|
||||
}
|
||||
|
||||
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(pageInfo).X ? maxTextWidth : ImGui.CalcTextSize(pageInfo).X;
|
||||
maxTextWidth = maxTextWidth > ImGui.CalcTextSize(ime.ImmComp).X
|
||||
? maxTextWidth
|
||||
: ImGui.CalcTextSize(ime.ImmComp).X;
|
||||
|
||||
var numEntries = (drawCand ? ime.ImmCand.Count + 1 : 0) + 1 + (drawIme ? 1 : 0);
|
||||
var spaceY = ImGui.GetStyle().ItemSpacing.Y;
|
||||
var imeWindowHeight = (spaceY * (numEntries - 1)) + (candTextSize.Y * numEntries);
|
||||
var windowSize = new Vector2(maxTextWidth, imeWindowHeight) + (pad * 2);
|
||||
|
||||
// 1. Figure out the expanding direction.
|
||||
var expandUpward = ime.CursorPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y;
|
||||
var windowPos = ime.CursorPos - pad;
|
||||
if (expandUpward)
|
||||
{
|
||||
windowPos.Y -= windowSize.Y - candTextSize.Y - (pad.Y * 2);
|
||||
if (drawIme)
|
||||
windowPos.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (drawIme)
|
||||
windowPos.Y -= candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// 2. Contain within the viewport. Do not use clamp, as the target window might be too small.
|
||||
if (windowPos.X < viewport.WorkPos.X)
|
||||
windowPos.X = viewport.WorkPos.X;
|
||||
else if (windowPos.X + windowSize.X > viewport.WorkPos.X + viewport.WorkSize.X)
|
||||
windowPos.X = (viewport.WorkPos.X + viewport.WorkSize.X) - windowSize.X;
|
||||
if (windowPos.Y < viewport.WorkPos.Y)
|
||||
windowPos.Y = viewport.WorkPos.Y;
|
||||
else if (windowPos.Y + windowSize.Y > viewport.WorkPos.Y + viewport.WorkSize.Y)
|
||||
windowPos.Y = (viewport.WorkPos.Y + viewport.WorkSize.Y) - windowSize.Y;
|
||||
|
||||
var cursor = windowPos + pad;
|
||||
|
||||
// Draw the ime window.
|
||||
var drawList = ImGui.GetForegroundDrawList(viewport);
|
||||
|
||||
// Draw the background rect for candidates.
|
||||
if (drawCand)
|
||||
{
|
||||
Vector2 candRectLt, candRectRb;
|
||||
if (!expandUpward)
|
||||
{
|
||||
candRectLt = windowPos + candTextSize with { X = 0 } + pad with { X = 0 };
|
||||
candRectRb = windowPos + windowSize;
|
||||
if (drawIme)
|
||||
candRectLt.Y += spaceY + candTextSize.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
candRectLt = windowPos;
|
||||
candRectRb = windowPos + (windowSize - candTextSize with { X = 0 } - pad with { X = 0 });
|
||||
if (drawIme)
|
||||
candRectRb.Y -= spaceY + candTextSize.Y;
|
||||
}
|
||||
|
||||
drawList.AddRectFilled(
|
||||
candRectLt,
|
||||
candRectRb,
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ImGui.GetStyle().WindowRounding);
|
||||
}
|
||||
|
||||
if (!expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.InputModeIcon);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (!expandUpward && drawConv)
|
||||
{
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
}
|
||||
|
||||
if (drawCand)
|
||||
{
|
||||
// Add the candidate words.
|
||||
for (var i = 0; i < ime.ImmCand.Count; i++)
|
||||
{
|
||||
var selected = i == (native.dwSelection % ImePageSize);
|
||||
var color = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
if (selected)
|
||||
color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
|
||||
|
||||
drawList.AddText(cursor, color, $"{i + 1}. {ime.ImmCand[i]}");
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
// Add a separator
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
// Add the pages infomation.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawConv)
|
||||
{
|
||||
// Add a separator.
|
||||
drawList.AddLine(cursor, cursor + new Vector2(maxTextWidth, 0), ImGui.GetColorU32(ImGuiCol.Separator));
|
||||
|
||||
DrawTextBeingConverted();
|
||||
cursor.Y += candTextSize.Y + spaceY;
|
||||
}
|
||||
|
||||
if (expandUpward && drawIme)
|
||||
{
|
||||
for (var dx = -2; dx <= 2; dx++)
|
||||
{
|
||||
for (var dy = -2; dy <= 2; dy++)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor + new Vector2(dx, dy),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imeIconFont.RenderChar(
|
||||
drawList,
|
||||
imeIconFont.FontSize,
|
||||
cursor,
|
||||
ImGui.GetColorU32(ImGuiCol.Text),
|
||||
ime.InputModeIcon);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void DrawTextBeingConverted()
|
||||
{
|
||||
// Draw the text background.
|
||||
drawList.AddRectFilled(
|
||||
cursor - (pad / 2),
|
||||
cursor + candTextSize + (pad / 2),
|
||||
ImGui.GetColorU32(ImGuiCol.WindowBg));
|
||||
|
||||
// If only a part of the full text is marked for conversion, then draw background for the part being edited.
|
||||
if (ime.PartialConversionFrom != 0 || ime.PartialConversionTo != ime.ImmComp.Length)
|
||||
{
|
||||
var part1 = ime.ImmComp[..ime.PartialConversionFrom];
|
||||
var part2 = ime.ImmComp[..ime.PartialConversionTo];
|
||||
var size1 = ImGui.CalcTextSize(part1);
|
||||
var size2 = ImGui.CalcTextSize(part2);
|
||||
drawList.AddRectFilled(
|
||||
cursor + size1 with { Y = 0 },
|
||||
cursor + size2,
|
||||
ImGui.GetColorU32(ImGuiCol.TextSelectedBg));
|
||||
}
|
||||
|
||||
// Add the text being converted.
|
||||
drawList.AddText(cursor, ImGui.GetColorU32(ImGuiCol.Text), ime.ImmComp);
|
||||
|
||||
// Draw the caret inside the composition string.
|
||||
if (DalamudIme.ShowCursorInInputText)
|
||||
{
|
||||
var partBeforeCaret = ime.ImmComp[..ime.CompositionCursorOffset];
|
||||
var sizeBeforeCaret = ImGui.CalcTextSize(partBeforeCaret);
|
||||
drawList.AddLine(
|
||||
cursor + sizeBeforeCaret with { Y = 0 },
|
||||
cursor + sizeBeforeCaret,
|
||||
ImGui.GetColorU32(ImGuiCol.Text));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -165,6 +165,7 @@ internal static class ServiceManager
|
|||
|
||||
var earlyLoadingServices = new HashSet<Type>();
|
||||
var blockingEarlyLoadingServices = new HashSet<Type>();
|
||||
var providedServices = new HashSet<Type>();
|
||||
|
||||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
|
|
@ -197,7 +198,10 @@ internal static class ServiceManager
|
|||
|
||||
// We don't actually need to load provided services, something else does
|
||||
if (serviceKind.HasFlag(ServiceKind.ProvidedService))
|
||||
{
|
||||
providedServices.Add(serviceType);
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(
|
||||
serviceKind.HasFlag(ServiceKind.EarlyLoadedService) ||
|
||||
|
|
@ -340,7 +344,16 @@ internal static class ServiceManager
|
|||
}
|
||||
|
||||
if (!tasks.Any())
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
{
|
||||
// No more services we can start loading for now.
|
||||
// Either we're waiting for provided services, or there's a dependency cycle.
|
||||
providedServices.RemoveWhere(x => getAsyncTaskMap[x].IsCompleted);
|
||||
if (providedServices.Any())
|
||||
await Task.WhenAny(providedServices.Select(x => getAsyncTaskMap[x]));
|
||||
else
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (servicesToLoad.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ public static class ThreadSafety
|
|||
/// Throws an exception when the current thread is not the main thread.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the current thread is not the main thread.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertMainThread()
|
||||
{
|
||||
if (!threadStaticIsMainThread)
|
||||
|
|
@ -31,6 +33,7 @@ public static class ThreadSafety
|
|||
/// Throws an exception when the current thread is the main thread.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the current thread is the main thread.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void AssertNotMainThread()
|
||||
{
|
||||
if (threadStaticIsMainThread)
|
||||
|
|
@ -39,6 +42,15 @@ public static class ThreadSafety
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary><see cref="AssertMainThread"/>, but only on debug compilation mode.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void DebugAssertMainThread()
|
||||
{
|
||||
#if DEBUG
|
||||
AssertMainThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a thread as the main thread.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 722a2c512238ac4b5324e3d343b316d8c8633a02
|
||||
Subproject commit ac2ced26fc98153c65f5b8f0eaf0f464258ff683
|
||||
Loading…
Add table
Add a link
Reference in a new issue