mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 20:24:16 +01:00
Merge branch 'master' of github.com:goatcorp/Dalamud into profiles
This commit is contained in:
commit
faedf16d47
32 changed files with 225 additions and 131 deletions
|
|
@ -6,12 +6,12 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
// General
|
// General
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ namespace Dalamud.CorePlugin
|
||||||
/// Initializes a new instance of the <see cref="PluginImpl"/> class.
|
/// Initializes a new instance of the <see cref="PluginImpl"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginInterface">Dalamud plugin interface.</param>
|
/// <param name="pluginInterface">Dalamud plugin interface.</param>
|
||||||
|
/// <param name="log">Logging service.</param>
|
||||||
public PluginImpl(DalamudPluginInterface pluginInterface, PluginLog log)
|
public PluginImpl(DalamudPluginInterface pluginInterface, PluginLog log)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -739,10 +739,12 @@ namespace Dalamud.Injector
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
{
|
{
|
||||||
[System.Runtime.InteropServices.DllImport("c")]
|
[System.Runtime.InteropServices.DllImport("c")]
|
||||||
static extern ulong clock_gettime_nsec_np(int clock_id);
|
#pragma warning disable SA1300
|
||||||
|
static extern ulong clock_gettime_nsec_np(int clockId);
|
||||||
|
#pragma warning restore SA1300
|
||||||
|
|
||||||
const int CLOCK_MONOTONIC_RAW = 4;
|
const int CLOCK_MONOTONIC_RAW = 4;
|
||||||
var rawTickCountFixed = (clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) / 1000000);
|
var rawTickCountFixed = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) / 1000000;
|
||||||
Log.Information("ArgumentBuilder::DeriveKey() fixing up rawTickCount from {0} to {1} on macOS", rawTickCount, rawTickCountFixed);
|
Log.Information("ArgumentBuilder::DeriveKey() fixing up rawTickCount from {0} to {1} on macOS", rawTickCount, rawTickCountFixed);
|
||||||
rawTickCount = (uint)rawTickCountFixed;
|
rawTickCount = (uint)rawTickCountFixed;
|
||||||
}
|
}
|
||||||
|
|
@ -764,21 +766,27 @@ namespace Dalamud.Injector
|
||||||
gameArgumentString = string.Join(" ", gameArguments.Select(x => EncodeParameterArgument(x)));
|
gameArgumentString = string.Join(" ", gameArguments.Select(x => EncodeParameterArgument(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var process = GameStart.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, noFixAcl, (Process p) =>
|
var process = GameStart.LaunchGame(
|
||||||
{
|
Path.GetDirectoryName(gamePath),
|
||||||
if (!withoutDalamud && mode == "entrypoint")
|
gamePath,
|
||||||
|
gameArgumentString,
|
||||||
|
noFixAcl,
|
||||||
|
p =>
|
||||||
{
|
{
|
||||||
var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
|
if (!withoutDalamud && mode == "entrypoint")
|
||||||
Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
|
|
||||||
if (RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)) != 0)
|
|
||||||
{
|
{
|
||||||
Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
|
var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
|
||||||
throw new Exception("RewriteRemoteEntryPointW failed");
|
Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
|
||||||
}
|
if (RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)) != 0)
|
||||||
|
{
|
||||||
|
Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
|
||||||
|
throw new Exception("RewriteRemoteEntryPointW failed");
|
||||||
|
}
|
||||||
|
|
||||||
Log.Verbose("RewriteRemoteEntryPointW called!");
|
Log.Verbose("RewriteRemoteEntryPointW called!");
|
||||||
}
|
}
|
||||||
}, waitForGameWindow);
|
},
|
||||||
|
waitForGameWindow);
|
||||||
|
|
||||||
Log.Verbose("Game process started with PID {0}", process.Id);
|
Log.Verbose("Game process started with PID {0}", process.Id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,9 @@ namespace Dalamud.Injector
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Claim a SE Debug Privilege.
|
||||||
|
/// </summary>
|
||||||
public static void ClaimSeDebug()
|
public static void ClaimSeDebug()
|
||||||
{
|
{
|
||||||
var hToken = PInvoke.INVALID_HANDLE_VALUE;
|
var hToken = PInvoke.INVALID_HANDLE_VALUE;
|
||||||
|
|
@ -345,8 +348,6 @@ namespace Dalamud.Injector
|
||||||
private static class PInvoke
|
private static class PInvoke
|
||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
|
|
||||||
|
|
||||||
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
||||||
|
|
||||||
public const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000;
|
public const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000;
|
||||||
|
|
@ -369,6 +370,8 @@ namespace Dalamud.Injector
|
||||||
|
|
||||||
public const UInt32 ERROR_NO_TOKEN = 0x000003F0;
|
public const UInt32 ERROR_NO_TOKEN = 0x000003F0;
|
||||||
|
|
||||||
|
public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
|
||||||
|
|
||||||
public enum MULTIPLE_TRUSTEE_OPERATION
|
public enum MULTIPLE_TRUSTEE_OPERATION
|
||||||
{
|
{
|
||||||
NO_MULTIPLE_TRUSTEE,
|
NO_MULTIPLE_TRUSTEE,
|
||||||
|
|
@ -431,7 +434,7 @@ namespace Dalamud.Injector
|
||||||
SecurityAnonymous,
|
SecurityAnonymous,
|
||||||
SecurityIdentification,
|
SecurityIdentification,
|
||||||
SecurityImpersonation,
|
SecurityImpersonation,
|
||||||
SecurityDelegation
|
SecurityDelegation,
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
@ -485,8 +488,7 @@ namespace Dalamud.Injector
|
||||||
|
|
||||||
[DllImport("advapi32.dll", SetLastError = true)]
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
public static extern bool ImpersonateSelf(
|
public static extern bool ImpersonateSelf(
|
||||||
SECURITY_IMPERSONATION_LEVEL impersonationLevel
|
SECURITY_IMPERSONATION_LEVEL impersonationLevel);
|
||||||
);
|
|
||||||
|
|
||||||
[DllImport("advapi32.dll", SetLastError = true)]
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
public static extern bool OpenProcessToken(
|
public static extern bool OpenProcessToken(
|
||||||
|
|
@ -496,10 +498,10 @@ namespace Dalamud.Injector
|
||||||
|
|
||||||
[DllImport("advapi32.dll", SetLastError = true)]
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
public static extern bool OpenThreadToken(
|
public static extern bool OpenThreadToken(
|
||||||
IntPtr ThreadHandle,
|
IntPtr threadHandle,
|
||||||
uint DesiredAccess,
|
uint desiredAccess,
|
||||||
bool OpenAsSelf,
|
bool openAsSelf,
|
||||||
out IntPtr TokenHandle);
|
out IntPtr tokenHandle);
|
||||||
|
|
||||||
[DllImport("advapi32.dll", SetLastError = true)]
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
|
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
// General
|
// General
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static")]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Dalamud.Injector
|
namespace Dalamud.Injector
|
||||||
{
|
{
|
||||||
|
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1413:Use trailing comma in multi-line initializers", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1519:Braces should not be omitted from multi-line child statement", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1414:Tuple types in signatures should have element names", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Legacy code")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "Legacy code")]
|
||||||
internal class LegacyBlowfish
|
internal class LegacyBlowfish
|
||||||
{
|
{
|
||||||
#region P-Array and S-Boxes
|
#region P-Array and S-Boxes
|
||||||
|
|
@ -203,10 +211,9 @@ namespace Dalamud.Injector
|
||||||
private static readonly int Rounds = 16;
|
private static readonly int Rounds = 16;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize a new blowfish.
|
/// Initializes a new instance of the <see cref="LegacyBlowfish"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key to use.</param>
|
/// <param name="key">The key to use.</param>
|
||||||
/// <param name="fucked">Whether or not a sign confusion should be introduced during key init. This is needed for SE's implementation of blowfish.</param>
|
|
||||||
public LegacyBlowfish(byte[] key)
|
public LegacyBlowfish(byte[] key)
|
||||||
{
|
{
|
||||||
foreach (var (i, keyFragment) in WrappingUInt32(key, this.p.Length))
|
foreach (var (i, keyFragment) in WrappingUInt32(key, this.p.Length))
|
||||||
|
|
@ -306,7 +313,9 @@ namespace Dalamud.Injector
|
||||||
|
|
||||||
for (var j = 0; j < 4 && enumerator.MoveNext(); j++)
|
for (var j = 0; j < 4 && enumerator.MoveNext(); j++)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CS0675
|
||||||
n = (uint)((n << 8) | (sbyte)enumerator.Current); // NOTE(goat): THIS IS A BUG! SE's implementation wrongly uses signed numbers for this, so we need to as well.
|
n = (uint)((n << 8) | (sbyte)enumerator.Current); // NOTE(goat): THIS IS A BUG! SE's implementation wrongly uses signed numbers for this, so we need to as well.
|
||||||
|
#pragma warning restore CS0675
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return (i, n);
|
yield return (i, n);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -97,6 +99,19 @@ internal sealed class Dalamud : IServiceType
|
||||||
/// Gets location of stored assets.
|
/// Gets location of stored assets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DirectoryInfo AssetDirectory => new(Service<DalamudStartInfo>.Get().AssetDirectory!);
|
internal DirectoryInfo AssetDirectory => new(Service<DalamudStartInfo>.Get().AssetDirectory!);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signal to the crash handler process that we should restart the game.
|
||||||
|
/// </summary>
|
||||||
|
public static void RestartGame()
|
||||||
|
{
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
static extern void RaiseException(uint dwExceptionCode, uint dwExceptionFlags, uint nNumberOfArguments, IntPtr lpArguments);
|
||||||
|
|
||||||
|
RaiseException(0x12345678, 0, 0, IntPtr.Zero);
|
||||||
|
Process.GetCurrentProcess().Kill();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue an unload of Dalamud when it gets the chance.
|
/// Queue an unload of Dalamud when it gets the chance.
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,8 @@ public class ChatHandlers : IServiceType
|
||||||
|
|
||||||
var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
|
var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
|
||||||
|
|
||||||
if (this.configuration.PrintDalamudWelcomeMsg) {
|
if (this.configuration.PrintDalamudWelcomeMsg)
|
||||||
|
{
|
||||||
chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud vD{0} loaded."), assemblyVersion)
|
chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud vD{0} loaded."), assemblyVersion)
|
||||||
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), pluginManager.InstalledPlugins.Count(x => x.IsLoaded)));
|
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), pluginManager.InstalledPlugins.Count(x => x.IsLoaded)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ public enum BattleNpcSubKind : byte
|
||||||
/// </summary>
|
/// </summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
///<summary>
|
/// <summary>
|
||||||
/// Weak Spots / Battle NPC parts
|
/// Weak Spots / Battle NPC parts
|
||||||
/// Eg: Titan's Heart (Naval), Tioman's left and right wing (Sohm Al), Golem Soulstone (The Sunken Temple of Qarn)
|
/// Eg: Titan's Heart (Naval), Tioman's left and right wing (Sohm Al), Golem Soulstone (The Sunken Temple of Qarn).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BattleNpcPart = 1,
|
BattleNpcPart = 1,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,42 +22,42 @@ public unsafe class BattleChara : Character
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current status effects.
|
/// Gets the current status effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StatusList StatusList => new(&this.Struct->StatusManager);
|
public StatusList StatusList => new(this.Struct->GetStatusManager);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the chara is currently casting.
|
/// Gets a value indicating whether the chara is currently casting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCasting => this.Struct->SpellCastInfo.IsCasting > 0;
|
public bool IsCasting => this.Struct->GetCastInfo->IsCasting > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the cast is interruptible.
|
/// Gets a value indicating whether the cast is interruptible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCastInterruptible => this.Struct->SpellCastInfo.Interruptible > 0;
|
public bool IsCastInterruptible => this.Struct->GetCastInfo->Interruptible > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the spell action type of the spell being cast by the actor.
|
/// Gets the spell action type of the spell being cast by the actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte CastActionType => (byte)this.Struct->SpellCastInfo.ActionType;
|
public byte CastActionType => (byte)this.Struct->GetCastInfo->ActionType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the spell action ID of the spell being cast by the actor.
|
/// Gets the spell action ID of the spell being cast by the actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CastActionId => this.Struct->SpellCastInfo.ActionID;
|
public uint CastActionId => this.Struct->GetCastInfo->ActionID;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the object ID of the target currently being cast at by the chara.
|
/// Gets the object ID of the target currently being cast at by the chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint CastTargetObjectId => this.Struct->SpellCastInfo.CastTargetID;
|
public uint CastTargetObjectId => this.Struct->GetCastInfo->CastTargetID;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current casting time of the spell being cast by the chara.
|
/// Gets the current casting time of the spell being cast by the chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float CurrentCastTime => this.Struct->SpellCastInfo.CurrentCastTime;
|
public float CurrentCastTime => this.Struct->GetCastInfo->CurrentCastTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the total casting time of the spell being cast by the chara.
|
/// Gets the total casting time of the spell being cast by the chara.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float TotalCastTime => this.Struct->SpellCastInfo.TotalCastTime;
|
public float TotalCastTime => this.Struct->GetCastInfo->TotalCastTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the underlying structure.
|
/// Gets the underlying structure.
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ public unsafe class Character : GameObject
|
||||||
/// Gets a byte array describing the visual appearance of this Chara.
|
/// Gets a byte array describing the visual appearance of this Chara.
|
||||||
/// Indexed by <see cref="CustomizeIndex"/>.
|
/// Indexed by <see cref="CustomizeIndex"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Customize => MemoryHelper.Read<byte>((IntPtr)this.Struct->CustomizeData, 28);
|
public byte[] Customize => MemoryHelper.Read<byte>((IntPtr)this.Struct->DrawData.CustomizeData.Data, 28);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Free Company tag of this chara.
|
/// Gets the Free Company tag of this chara.
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <param name="value">The returned value of the config option.</param>
|
/// <param name="value">The returned value of the config option.</param>
|
||||||
/// <returns>A value representing the success.</returns>
|
/// <returns>A value representing the success.</returns>
|
||||||
public unsafe bool TryGetBool(string name, out bool value) {
|
public unsafe bool TryGetBool(string name, out bool value)
|
||||||
|
{
|
||||||
value = false;
|
value = false;
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
|
|
@ -97,7 +98,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <returns>Value of the config option.</returns>
|
/// <returns>Value of the config option.</returns>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
||||||
public bool GetBool(string name) {
|
public bool GetBool(string name)
|
||||||
|
{
|
||||||
if (!this.TryGetBool(name, out var value))
|
if (!this.TryGetBool(name, out var value))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -114,7 +116,8 @@ public class GameConfigSection
|
||||||
/// <param name="value">New value of the config option.</param>
|
/// <param name="value">New value of the config option.</param>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
||||||
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
||||||
public unsafe void Set(string name, bool value) {
|
public unsafe void Set(string name, bool value)
|
||||||
|
{
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -139,7 +142,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <param name="value">The returned value of the config option.</param>
|
/// <param name="value">The returned value of the config option.</param>
|
||||||
/// <returns>A value representing the success.</returns>
|
/// <returns>A value representing the success.</returns>
|
||||||
public unsafe bool TryGetUInt(string name, out uint value) {
|
public unsafe bool TryGetUInt(string name, out uint value)
|
||||||
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
|
|
@ -169,7 +173,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <returns>Value of the config option.</returns>
|
/// <returns>Value of the config option.</returns>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
||||||
public uint GetUInt(string name) {
|
public uint GetUInt(string name)
|
||||||
|
{
|
||||||
if (!this.TryGetUInt(name, out var value))
|
if (!this.TryGetUInt(name, out var value))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -186,8 +191,10 @@ public class GameConfigSection
|
||||||
/// <param name="value">New value of the config option.</param>
|
/// <param name="value">New value of the config option.</param>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
||||||
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
||||||
public unsafe void Set(string name, uint value) {
|
public unsafe void Set(string name, uint value)
|
||||||
this.framework.RunOnFrameworkThread(() => {
|
{
|
||||||
|
this.framework.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -213,7 +220,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <param name="value">The returned value of the config option.</param>
|
/// <param name="value">The returned value of the config option.</param>
|
||||||
/// <returns>A value representing the success.</returns>
|
/// <returns>A value representing the success.</returns>
|
||||||
public unsafe bool TryGetFloat(string name, out float value) {
|
public unsafe bool TryGetFloat(string name, out float value)
|
||||||
|
{
|
||||||
value = 0;
|
value = 0;
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
|
|
@ -243,7 +251,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <returns>Value of the config option.</returns>
|
/// <returns>Value of the config option.</returns>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
||||||
public float GetFloat(string name) {
|
public float GetFloat(string name)
|
||||||
|
{
|
||||||
if (!this.TryGetFloat(name, out var value))
|
if (!this.TryGetFloat(name, out var value))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -260,8 +269,10 @@ public class GameConfigSection
|
||||||
/// <param name="value">New value of the config option.</param>
|
/// <param name="value">New value of the config option.</param>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
||||||
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
||||||
public unsafe void Set(string name, float value) {
|
public unsafe void Set(string name, float value)
|
||||||
this.framework.RunOnFrameworkThread(() => {
|
{
|
||||||
|
this.framework.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -287,7 +298,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <param name="value">The returned value of the config option.</param>
|
/// <param name="value">The returned value of the config option.</param>
|
||||||
/// <returns>A value representing the success.</returns>
|
/// <returns>A value representing the success.</returns>
|
||||||
public unsafe bool TryGetString(string name, out string value) {
|
public unsafe bool TryGetString(string name, out string value)
|
||||||
|
{
|
||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
|
|
@ -327,7 +339,8 @@ public class GameConfigSection
|
||||||
/// <param name="name">Name of the config option.</param>
|
/// <param name="name">Name of the config option.</param>
|
||||||
/// <returns>Value of the config option.</returns>
|
/// <returns>Value of the config option.</returns>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Thrown if the config option is not found.</exception>
|
||||||
public string GetString(string name) {
|
public string GetString(string name)
|
||||||
|
{
|
||||||
if (!this.TryGetString(name, out var value))
|
if (!this.TryGetString(name, out var value))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -344,8 +357,10 @@ public class GameConfigSection
|
||||||
/// <param name="value">New value of the config option.</param>
|
/// <param name="value">New value of the config option.</param>
|
||||||
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
/// <exception cref="ConfigOptionNotFoundException">Throw if the config option is not found.</exception>
|
||||||
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
/// <exception cref="UnreachableException">Thrown if the name of the config option is found, but the struct was not.</exception>
|
||||||
public unsafe void Set(string name, string value) {
|
public unsafe void Set(string name, string value)
|
||||||
this.framework.RunOnFrameworkThread(() => {
|
{
|
||||||
|
this.framework.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
if (!this.TryGetIndex(name, out var index))
|
if (!this.TryGetIndex(name, out var index))
|
||||||
{
|
{
|
||||||
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
throw new ConfigOptionNotFoundException(this.SectionName, name);
|
||||||
|
|
@ -365,7 +380,8 @@ public class GameConfigSection
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool TryGetIndex(string name, out uint index) {
|
private unsafe bool TryGetIndex(string name, out uint index)
|
||||||
|
{
|
||||||
if (this.indexMap.TryGetValue(name, out index))
|
if (this.indexMap.TryGetValue(name, out index))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -373,14 +389,16 @@ public class GameConfigSection
|
||||||
|
|
||||||
var configBase = this.GetConfigBase();
|
var configBase = this.GetConfigBase();
|
||||||
var e = configBase->ConfigEntry;
|
var e = configBase->ConfigEntry;
|
||||||
for (var i = 0U; i < configBase->ConfigCount; i++, e++) {
|
for (var i = 0U; i < configBase->ConfigCount; i++, e++)
|
||||||
|
{
|
||||||
if (e->Name == null)
|
if (e->Name == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eName = MemoryHelper.ReadStringNullTerminated(new IntPtr(e->Name));
|
var eName = MemoryHelper.ReadStringNullTerminated(new IntPtr(e->Name));
|
||||||
if (eName.Equals(name)) {
|
if (eName.Equals(name))
|
||||||
|
{
|
||||||
this.indexMap.TryAdd(name, i);
|
this.indexMap.TryAdd(name, i);
|
||||||
this.nameMap.TryAdd(i, name);
|
this.nameMap.TryAdd(i, name);
|
||||||
index = i;
|
index = i;
|
||||||
|
|
@ -392,7 +410,8 @@ public class GameConfigSection
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool TryGetEntry(uint index, out ConfigEntry* entry) {
|
private unsafe bool TryGetEntry(uint index, out ConfigEntry* entry)
|
||||||
|
{
|
||||||
entry = null;
|
entry = null;
|
||||||
var configBase = this.GetConfigBase();
|
var configBase = this.GetConfigBase();
|
||||||
if (configBase->ConfigEntry == null || index >= configBase->ConfigCount)
|
if (configBase->ConfigEntry == null || index >= configBase->ConfigCount)
|
||||||
|
|
|
||||||
|
|
@ -25,25 +25,25 @@ namespace Dalamud.Game;
|
||||||
[ServiceManager.BlockingEarlyLoadedService]
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class Framework : IDisposable, IServiceType
|
public sealed class Framework : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
|
private static readonly Stopwatch StatsStopwatch = new();
|
||||||
|
|
||||||
private readonly GameLifecycle lifecycle;
|
private readonly GameLifecycle lifecycle;
|
||||||
|
|
||||||
private static Stopwatch statsStopwatch = new();
|
|
||||||
|
|
||||||
private readonly Stopwatch updateStopwatch = new();
|
private readonly Stopwatch updateStopwatch = new();
|
||||||
private readonly HitchDetector hitchDetector;
|
private readonly HitchDetector hitchDetector;
|
||||||
|
|
||||||
private readonly Hook<OnUpdateDetour> updateHook;
|
private readonly Hook<OnUpdateDetour> updateHook;
|
||||||
private readonly Hook<OnRealDestroyDelegate> destroyHook;
|
private readonly Hook<OnRealDestroyDelegate> destroyHook;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
private readonly object runOnNextTickTaskListSync = new();
|
private readonly object runOnNextTickTaskListSync = new();
|
||||||
private List<RunOnNextTickTaskBase> runOnNextTickTaskList = new();
|
private List<RunOnNextTickTaskBase> runOnNextTickTaskList = new();
|
||||||
private List<RunOnNextTickTaskBase> runOnNextTickTaskList2 = new();
|
private List<RunOnNextTickTaskBase> runOnNextTickTaskList2 = new();
|
||||||
|
|
||||||
private Thread? frameworkUpdateThread;
|
private Thread? frameworkUpdateThread;
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
|
||||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private Framework(SigScanner sigScanner, GameLifecycle lifecycle)
|
private Framework(SigScanner sigScanner, GameLifecycle lifecycle)
|
||||||
{
|
{
|
||||||
|
|
@ -346,7 +346,7 @@ public sealed class Framework : IDisposable, IServiceType
|
||||||
this.destroyHook.Dispose();
|
this.destroyHook.Dispose();
|
||||||
|
|
||||||
this.updateStopwatch.Reset();
|
this.updateStopwatch.Reset();
|
||||||
statsStopwatch.Reset();
|
StatsStopwatch.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
[ServiceManager.CallWhenServicesReady]
|
[ServiceManager.CallWhenServicesReady]
|
||||||
|
|
@ -420,11 +420,11 @@ public sealed class Framework : IDisposable, IServiceType
|
||||||
|
|
||||||
if (StatsEnabled)
|
if (StatsEnabled)
|
||||||
{
|
{
|
||||||
statsStopwatch.Restart();
|
StatsStopwatch.Restart();
|
||||||
this.RunPendingTickTasks();
|
this.RunPendingTickTasks();
|
||||||
statsStopwatch.Stop();
|
StatsStopwatch.Stop();
|
||||||
|
|
||||||
AddToStats(nameof(this.RunPendingTickTasks), statsStopwatch.Elapsed.TotalMilliseconds);
|
AddToStats(nameof(this.RunPendingTickTasks), StatsStopwatch.Elapsed.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -440,7 +440,7 @@ public sealed class Framework : IDisposable, IServiceType
|
||||||
// Individually invoke OnUpdate handlers and time them.
|
// Individually invoke OnUpdate handlers and time them.
|
||||||
foreach (var d in invokeList)
|
foreach (var d in invokeList)
|
||||||
{
|
{
|
||||||
statsStopwatch.Restart();
|
StatsStopwatch.Restart();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
d.Method.Invoke(d.Target, new object[] { this });
|
d.Method.Invoke(d.Target, new object[] { this });
|
||||||
|
|
@ -450,13 +450,13 @@ public sealed class Framework : IDisposable, IServiceType
|
||||||
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
||||||
}
|
}
|
||||||
|
|
||||||
statsStopwatch.Stop();
|
StatsStopwatch.Stop();
|
||||||
|
|
||||||
var key = $"{d.Target}::{d.Method.Name}";
|
var key = $"{d.Target}::{d.Method.Name}";
|
||||||
if (notUpdated.Contains(key))
|
if (notUpdated.Contains(key))
|
||||||
notUpdated.Remove(key);
|
notUpdated.Remove(key);
|
||||||
|
|
||||||
AddToStats(key, statsStopwatch.Elapsed.TotalMilliseconds);
|
AddToStats(key, StatsStopwatch.Elapsed.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup handlers that are no longer being called
|
// Cleanup handlers that are no longer being called
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Core;
|
|
||||||
|
|
||||||
namespace Dalamud.Game.Network;
|
namespace Dalamud.Game.Network;
|
||||||
|
|
||||||
|
|
@ -26,11 +25,11 @@ public sealed class GameNetwork : IDisposable, IServiceType
|
||||||
private readonly HitchDetector hitchDetectorUp;
|
private readonly HitchDetector hitchDetectorUp;
|
||||||
private readonly HitchDetector hitchDetectorDown;
|
private readonly HitchDetector hitchDetectorDown;
|
||||||
|
|
||||||
private IntPtr baseAddress;
|
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
|
private IntPtr baseAddress;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private GameNetwork(SigScanner sigScanner)
|
private GameNetwork(SigScanner sigScanner)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
// General
|
// General
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "I don't care anymore", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "I don't care anymore")]
|
||||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Dalamud.Hooking.Internal;
|
||||||
internal class HookManager : IDisposable, IServiceType
|
internal class HookManager : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logger shared with <see cref="Unhooker"/>
|
/// Logger shared with <see cref="Unhooker"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static readonly ModuleLog Log = new("HM");
|
internal static readonly ModuleLog Log = new("HM");
|
||||||
|
|
||||||
|
|
@ -41,6 +41,11 @@ internal class HookManager : IDisposable, IServiceType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ConcurrentDictionary<IntPtr, Unhooker> Unhookers { get; } = new();
|
internal static ConcurrentDictionary<IntPtr, Unhooker> Unhookers { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a static dictionary of the number of hooks on a given address.
|
||||||
|
/// </summary>
|
||||||
|
internal static ConcurrentDictionary<IntPtr, List<IDalamudHook?>> MultiHookTracker { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
|
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
|
||||||
/// an existing instance if the address was registered previously. By default, the unhooker will restore between 0
|
/// an existing instance if the address was registered previously. By default, the unhooker will restore between 0
|
||||||
|
|
@ -68,11 +73,6 @@ internal class HookManager : IDisposable, IServiceType
|
||||||
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
|
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a static dictionary of the number of hooks on a given address.
|
|
||||||
/// </summary>
|
|
||||||
internal static ConcurrentDictionary<IntPtr, List<IDalamudHook?>> MultiHookTracker { get; } = new();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,19 @@ internal class DalamudCommands : IServiceType
|
||||||
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
|
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
|
||||||
ShowInHelp = false,
|
ShowInHelp = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
commandManager.AddHandler("/xlkill", new CommandInfo(this.OnKillCommand)
|
commandManager.AddHandler("/xlkill", new CommandInfo(this.OnKillCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Kill the game.",
|
HelpMessage = "Kill the game.",
|
||||||
ShowInHelp = false,
|
ShowInHelp = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
commandManager.AddHandler("/xlrestart", new CommandInfo(this.OnRestartCommand)
|
||||||
|
{
|
||||||
|
HelpMessage = "Restart the game.",
|
||||||
|
ShowInHelp = false,
|
||||||
|
});
|
||||||
|
|
||||||
commandManager.AddHandler("/xlhelp", new CommandInfo(this.OnHelpCommand)
|
commandManager.AddHandler("/xlhelp", new CommandInfo(this.OnHelpCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = Loc.Localize("DalamudCmdInfoHelp", "Shows list of commands available. If an argument is provided, shows help for that command."),
|
HelpMessage = Loc.Localize("DalamudCmdInfoHelp", "Shows list of commands available. If an argument is provided, shows help for that command."),
|
||||||
|
|
@ -147,12 +153,17 @@ internal class DalamudCommands : IServiceType
|
||||||
Service<ChatGui>.Get().Print("Unloading...");
|
Service<ChatGui>.Get().Print("Unloading...");
|
||||||
Service<Dalamud>.Get().Unload();
|
Service<Dalamud>.Get().Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnKillCommand(string command, string arguments)
|
private void OnKillCommand(string command, string arguments)
|
||||||
{
|
{
|
||||||
Process.GetCurrentProcess().Kill();
|
Process.GetCurrentProcess().Kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRestartCommand(string command, string arguments)
|
||||||
|
{
|
||||||
|
Dalamud.RestartGame();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnHelpCommand(string command, string arguments)
|
private void OnHelpCommand(string command, string arguments)
|
||||||
{
|
{
|
||||||
var chatGui = Service<ChatGui>.Get();
|
var chatGui = Service<ChatGui>.Get();
|
||||||
|
|
|
||||||
|
|
@ -717,12 +717,7 @@ internal class DalamudInterface : IDisposable, IServiceType
|
||||||
|
|
||||||
if (ImGui.MenuItem("Restart game"))
|
if (ImGui.MenuItem("Restart game"))
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll")]
|
Dalamud.RestartGame();
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
static extern void RaiseException(uint dwExceptionCode, uint dwExceptionFlags, uint nNumberOfArguments, IntPtr lpArguments);
|
|
||||||
|
|
||||||
RaiseException(0x12345678, 0, 0, IntPtr.Zero);
|
|
||||||
Process.GetCurrentProcess().Kill();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.MenuItem("Kill game"))
|
if (ImGui.MenuItem("Kill game"))
|
||||||
|
|
|
||||||
|
|
@ -1792,7 +1792,8 @@ internal class DataWindow : Window
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(string.Join(", ", share.Users));
|
ImGui.TextUnformatted(string.Join(", ", share.Users));
|
||||||
}
|
}
|
||||||
} finally
|
}
|
||||||
|
finally
|
||||||
{
|
{
|
||||||
ImGui.EndTable();
|
ImGui.EndTable();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Networking.Http;
|
using Dalamud.Networking.Http;
|
||||||
using Dalamud.Plugin.Internal;
|
using Dalamud.Plugin.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ namespace Dalamud.Interface.Internal.Windows;
|
||||||
internal class PluginStatWindow : Window
|
internal class PluginStatWindow : Window
|
||||||
{
|
{
|
||||||
private bool showDalamudHooks;
|
private bool showDalamudHooks;
|
||||||
|
private string drawSearchText = string.Empty;
|
||||||
|
private string frameworkSearchText = string.Empty;
|
||||||
private string hookSearchText = string.Empty;
|
private string hookSearchText = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -79,6 +81,12 @@ internal class PluginStatWindow : Window
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGuiComponents.TextWithLabel("Collective Average", $"{(loadedPlugins.Any() ? totalAverage / loadedPlugins.Count() / 10000f : 0):F4}ms", "Average of all average draw times");
|
ImGuiComponents.TextWithLabel("Collective Average", $"{(loadedPlugins.Any() ? totalAverage / loadedPlugins.Count() / 10000f : 0):F4}ms", "Average of all average draw times");
|
||||||
|
|
||||||
|
ImGui.InputTextWithHint(
|
||||||
|
"###PluginStatWindow_DrawSearch",
|
||||||
|
"Search",
|
||||||
|
ref this.drawSearchText,
|
||||||
|
500);
|
||||||
|
|
||||||
if (ImGui.BeginTable(
|
if (ImGui.BeginTable(
|
||||||
"##PluginStatsDrawTimes",
|
"##PluginStatsDrawTimes",
|
||||||
4,
|
4,
|
||||||
|
|
@ -114,6 +122,12 @@ internal class PluginStatWindow : Window
|
||||||
|
|
||||||
foreach (var plugin in loadedPlugins)
|
foreach (var plugin in loadedPlugins)
|
||||||
{
|
{
|
||||||
|
if (!this.drawSearchText.IsNullOrEmpty()
|
||||||
|
&& !plugin.Manifest.Name.Contains(this.drawSearchText, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
@ -168,6 +182,12 @@ internal class PluginStatWindow : Window
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGuiComponents.TextWithLabel("Collective Average", $"{(statsHistory.Any() ? totalAverage / statsHistory.Length : 0):F4}ms", "Average of all average update times");
|
ImGuiComponents.TextWithLabel("Collective Average", $"{(statsHistory.Any() ? totalAverage / statsHistory.Length : 0):F4}ms", "Average of all average update times");
|
||||||
|
|
||||||
|
ImGui.InputTextWithHint(
|
||||||
|
"###PluginStatWindow_FrameworkSearch",
|
||||||
|
"Search",
|
||||||
|
ref this.frameworkSearchText,
|
||||||
|
500);
|
||||||
|
|
||||||
if (ImGui.BeginTable(
|
if (ImGui.BeginTable(
|
||||||
"##PluginStatsFrameworkTimes",
|
"##PluginStatsFrameworkTimes",
|
||||||
4,
|
4,
|
||||||
|
|
@ -208,6 +228,13 @@ internal class PluginStatWindow : Window
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.frameworkSearchText.IsNullOrEmpty()
|
||||||
|
&& handlerHistory.Key != null
|
||||||
|
&& !handlerHistory.Key.Contains(this.frameworkSearchText, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,16 @@ internal sealed class SettingsEntry<T> : SettingsEntry
|
||||||
private object? valueBacking;
|
private object? valueBacking;
|
||||||
private object? fallbackValue;
|
private object? fallbackValue;
|
||||||
|
|
||||||
public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action<T?>? change = null, Func<T?, string?>? warning = null, Func<T?, string?>? validity = null, Func<bool>? visibility = null,
|
public SettingsEntry(
|
||||||
object? fallbackValue = null)
|
string name,
|
||||||
|
string description,
|
||||||
|
LoadSettingDelegate load,
|
||||||
|
SaveSettingDelegate save,
|
||||||
|
Action<T?>? change = null,
|
||||||
|
Func<T?, string?>? warning = null,
|
||||||
|
Func<T?, string?>? validity = null,
|
||||||
|
Func<bool>? visibility = null,
|
||||||
|
object? fallbackValue = null)
|
||||||
{
|
{
|
||||||
this.load = load;
|
this.load = load;
|
||||||
this.save = save;
|
this.save = save;
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
|
|
||||||
var initialCursor = ImGui.GetCursorPos();
|
var initialCursor = ImGui.GetCursorPos();
|
||||||
|
|
||||||
|
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, (float)shadeEasing.Value))
|
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, (float)shadeEasing.Value))
|
||||||
{
|
{
|
||||||
ImGui.Image(this.shadeTexture.ImGuiHandle, new Vector2(this.shadeTexture.Width * scale, this.shadeTexture.Height * scale));
|
ImGui.Image(this.shadeTexture.ImGuiHandle, new Vector2(this.shadeTexture.Width * scale, this.shadeTexture.Height * scale));
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,12 @@ public sealed class UiBuilder : IDisposable
|
||||||
private readonly InterfaceManager interfaceManager = Service<InterfaceManager>.Get();
|
private readonly InterfaceManager interfaceManager = Service<InterfaceManager>.Get();
|
||||||
private readonly GameFontManager gameFontManager = Service<GameFontManager>.Get();
|
private readonly GameFontManager gameFontManager = Service<GameFontManager>.Get();
|
||||||
|
|
||||||
private bool hasErrorWindow = false;
|
|
||||||
private bool lastFrameUiHideState = false;
|
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
|
private bool hasErrorWindow = false;
|
||||||
|
private bool lastFrameUiHideState = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
|
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
|
||||||
/// You do not have to call this manually.
|
/// You do not have to call this manually.
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,12 @@ public class PluginLog : IServiceType, IDisposable
|
||||||
public static void LogRaw(LogEventLevel level, Exception? exception, string messageTemplate, params object[] values)
|
public static void LogRaw(LogEventLevel level, Exception? exception, string messageTemplate, params object[] values)
|
||||||
=> WriteLog(Assembly.GetCallingAssembly().GetName().Name, level, messageTemplate, exception, values);
|
=> WriteLog(Assembly.GetCallingAssembly().GetName().Name, level, messageTemplate, exception, values);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
#region New instanced methods
|
#region New instanced methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -290,12 +296,6 @@ public class PluginLog : IServiceType, IDisposable
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ILogger GetPluginLogger(string? pluginName)
|
private static ILogger GetPluginLogger(string? pluginName)
|
||||||
{
|
{
|
||||||
return Serilog.Log.ForContext("SourceContext", pluginName ?? string.Empty);
|
return Serilog.Log.ForContext("SourceContext", pluginName ?? string.Empty);
|
||||||
|
|
|
||||||
|
|
@ -1132,6 +1132,7 @@ internal static partial class NativeFunctions
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Native kernel32 functions.
|
/// Native kernel32 functions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Stupid rule")]
|
||||||
internal static partial class NativeFunctions
|
internal static partial class NativeFunctions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1394,7 +1395,7 @@ internal static partial class NativeFunctions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HEAP_* from heapapi
|
/// HEAP_* from heapapi.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum HeapOptions
|
public enum HeapOptions
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ internal partial class PluginManager : IDisposable, IServiceType
|
||||||
|
|
||||||
private readonly object pluginListLock = new();
|
private readonly object pluginListLock = new();
|
||||||
private readonly DirectoryInfo pluginDirectory;
|
private readonly DirectoryInfo pluginDirectory;
|
||||||
private readonly DirectoryInfo devPluginDirectory;
|
|
||||||
private readonly BannedPlugin[]? bannedPlugins;
|
private readonly BannedPlugin[]? bannedPlugins;
|
||||||
|
|
||||||
private readonly DalamudLinkPayload openInstallerWindowPluginChangelogsLink;
|
private readonly DalamudLinkPayload openInstallerWindowPluginChangelogsLink;
|
||||||
|
|
|
||||||
|
|
@ -16,18 +16,15 @@ using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Dalamud;
|
namespace Dalamud;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// - Unify dependency walking code(load/unload
|
||||||
|
// - Visualize/output .dot or imgui thing
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class to initialize Service<T>s.
|
/// Class to initialize Service<T>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class ServiceManager
|
internal static class ServiceManager
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* TODO:
|
|
||||||
* - Unify dependency walking code(load/unload
|
|
||||||
* - Visualize/output .dot or imgui thing
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
|
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ internal static class Service<T> where T : IServiceType
|
||||||
.OfType<InherentDependencyAttribute>()
|
.OfType<InherentDependencyAttribute>()
|
||||||
.Select(x => x.GetType().GetGenericArguments().First()));
|
.Select(x => x.GetType().GetGenericArguments().First()));
|
||||||
|
|
||||||
|
|
||||||
// HACK: PluginManager needs to depend on ALL plugin exposed services
|
// HACK: PluginManager needs to depend on ALL plugin exposed services
|
||||||
if (typeof(T) == typeof(PluginManager))
|
if (typeof(T) == typeof(PluginManager))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Networking.Http;
|
using Dalamud.Networking.Http;
|
||||||
using Dalamud.Plugin.Internal.Types;
|
using Dalamud.Plugin.Internal.Types;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ public static class AsyncUtils
|
||||||
/// <param name="tasks">A list of tasks to race.</param>
|
/// <param name="tasks">A list of tasks to race.</param>
|
||||||
/// <typeparam name="T">The return type of all raced tasks.</typeparam>
|
/// <typeparam name="T">The return type of all raced tasks.</typeparam>
|
||||||
/// <exception cref="AggregateException">Thrown when all tasks given to this method fail.</exception>
|
/// <exception cref="AggregateException">Thrown when all tasks given to this method fail.</exception>
|
||||||
/// <returns>Returns the first task that completes, according to <see cref="Task{TResult}.IsCompletedSuccessfully"/>.</returns>
|
/// <returns>Returns the first task that completes, according to <see cref="Task.IsCompletedSuccessfully"/>.</returns>
|
||||||
public static Task<T> FirstSuccessfulTask<T>(ICollection<Task<T>> tasks)
|
public static Task<T> FirstSuccessfulTask<T>(ICollection<Task<T>> tasks)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics;
|
using FFXIVClientStructs.FFXIV.Client.Graphics;
|
||||||
|
|
||||||
namespace Dalamud.Utility.Numerics;
|
namespace Dalamud.Utility.Numerics;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue