mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge 95e7299017 into 4ac4505d72
This commit is contained in:
commit
54db39446e
13 changed files with 194 additions and 33 deletions
102
Dalamud.Test/Compliance/PublicApiTests.cs
Normal file
102
Dalamud.Test/Compliance/PublicApiTests.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Xunit;
|
||||
|
||||
|
||||
namespace Dalamud.Test.Compliance;
|
||||
|
||||
public class PublicApiTests
|
||||
{
|
||||
private static List<Type> IgnoredTypes { get; } =
|
||||
[
|
||||
typeof(Utility.CStringExtensions),
|
||||
];
|
||||
|
||||
private static List<Assembly> PermittedAssemblies { get; } =
|
||||
[
|
||||
typeof(object).Assembly,
|
||||
typeof(Dalamud).Assembly,
|
||||
|
||||
// Imgui and friends
|
||||
typeof(SharpDX.Color).Assembly,
|
||||
typeof(Bindings.ImGui.ImGui).Assembly,
|
||||
typeof(Bindings.ImGuizmo.ImGuizmo).Assembly,
|
||||
typeof(Bindings.ImPlot.ImPlot).Assembly,
|
||||
|
||||
// exposed to plugins via API
|
||||
typeof(Lumina.GameData).Assembly,
|
||||
typeof(Lumina.Excel.Sheets.Action).Assembly,
|
||||
];
|
||||
|
||||
private static List<Type> PermittedTypes { get; } = [
|
||||
// Used for IPluginLog, limited serilog exposure is OK.
|
||||
typeof(Serilog.ILogger),
|
||||
typeof(Serilog.Core.LoggingLevelSwitch),
|
||||
typeof(Serilog.Events.LogEventLevel),
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void NoRestrictedTypes()
|
||||
{
|
||||
foreach (var type in typeof(Dalamud).Assembly.GetTypes().Where(t => t.IsPublic).Except(IgnoredTypes))
|
||||
{
|
||||
if (type.GetCustomAttribute<ObsoleteAttribute>() != null) continue;
|
||||
|
||||
foreach (var m in type.GetMethods().Where(m => m.IsPublic && !m.IsSpecialName))
|
||||
{
|
||||
if (m.GetCustomAttribute<ObsoleteAttribute>() != null) continue;
|
||||
|
||||
if (!this.IsPermittedType(m.ReturnType))
|
||||
{
|
||||
Assert.Fail($"Method {type.FullName}.{m.Name} returns unapproved type: {m.ReturnType.FullName}");
|
||||
}
|
||||
|
||||
foreach (var param in m.GetParameters())
|
||||
{
|
||||
if (!this.IsPermittedType(param.ParameterType))
|
||||
{
|
||||
Assert.Fail($"Method {type.FullName}.{m.Name} uses unapproved type: {param.ParameterType.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var p in type.GetProperties())
|
||||
{
|
||||
if (p.GetCustomAttribute<ObsoleteAttribute>() != null) continue;
|
||||
if (p.GetMethod?.IsPrivate == true && p.SetMethod?.IsPrivate == true) continue;
|
||||
|
||||
if (!this.IsPermittedType(p.PropertyType))
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Property {type.FullName}.{p.Name} is unapproved type: {p.PropertyType.FullName}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var f in type.GetFields().Where(f => f.IsPublic && !f.IsSpecialName))
|
||||
{
|
||||
if (f.GetCustomAttribute<ObsoleteAttribute>() != null) continue;
|
||||
|
||||
if (!this.IsPermittedType(f.FieldType))
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Field {type.FullName}.{f.Name} is unapproved type: {f.FieldType.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPermittedType(Type subject)
|
||||
{
|
||||
if (subject.IsGenericType && !subject.GetGenericArguments().All(this.IsPermittedType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return subject.Namespace?.StartsWith("System") == true ||
|
||||
PermittedTypes.Any(t => t == subject) ||
|
||||
PermittedAssemblies.Any(a => a == subject.Assembly);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,14 +43,14 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.1">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.0.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
|
@ -31,6 +33,8 @@ public class AddonRefreshArgs : AddonArgs, ICloneable
|
|||
/// <summary>
|
||||
/// Gets the AtkValues in the form of a span.
|
||||
/// </summary>
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
[Obsolete($"Exposed ClientStructs type; use {nameof(AtkValues)}/{nameof(AtkValueCount)} instead.", false)]
|
||||
public unsafe Span<AtkValue> AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
|
||||
|
||||
/// <inheritdoc cref="ICloneable.Clone"/>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
|
@ -31,6 +33,8 @@ public class AddonSetupArgs : AddonArgs, ICloneable
|
|||
/// <summary>
|
||||
/// Gets the AtkValues in the form of a span.
|
||||
/// </summary>
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
[Obsolete($"Exposed ClientStructs type; use {nameof(AtkValues)}/{nameof(AtkValueCount)} instead.", false)]
|
||||
public unsafe Span<AtkValue> AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
|
||||
|
||||
/// <inheritdoc cref="ICloneable.Clone"/>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ public enum AddonEvent
|
|||
/// <summary>
|
||||
/// An event that is fired prior to an addon being setup with its implementation of
|
||||
/// <see cref="AtkUnitBase.OnSetup"/>. This event is useful for modifying the initial data contained within
|
||||
/// <see cref="AddonSetupArgs.AtkValueSpan"/> prior to the addon being created.
|
||||
/// <see cref="AddonSetupArgs.AtkValues"/> prior to the addon being created.
|
||||
/// </summary>
|
||||
/// <seealso cref="AddonSetupArgs"/>
|
||||
PreSetup,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired after an addon has finished its initial setup. This event is particularly useful for
|
||||
/// developers seeking to add custom elements to now-initialized and populated node lists, as well as reading data
|
||||
|
|
@ -64,7 +64,7 @@ public enum AddonEvent
|
|||
/// </remarks>
|
||||
/// <seealso cref="AddonFinalizeArgs"/>
|
||||
PreFinalize,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired before a call to <see cref="AtkUnitBase.OnRequestedUpdate"/> is made in response to a
|
||||
/// change in the subscribed <see cref="AddonRequestedUpdateArgs.NumberArrayData"/> or
|
||||
|
|
@ -81,13 +81,13 @@ public enum AddonEvent
|
|||
/// to the Free Company's overview.
|
||||
/// </example>
|
||||
PreRequestedUpdate,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired after an addon has finished processing an <c>ArrayData</c> update.
|
||||
/// See <see cref="PreRequestedUpdate"/> for more information.
|
||||
/// </summary>
|
||||
PostRequestedUpdate,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired before an addon calls its <see cref="AtkUnitManager.RefreshAddon"/> method. Refreshes are
|
||||
/// generally triggered in response to certain user interactions such as changing tabs, and are primarily used to
|
||||
|
|
@ -96,13 +96,13 @@ public enum AddonEvent
|
|||
/// <seealso cref="AddonRefreshArgs"/>
|
||||
/// <seealso cref="PostRefresh"/>
|
||||
PreRefresh,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired after an addon has finished its refresh.
|
||||
/// See <see cref="PreRefresh"/> for more information.
|
||||
/// </summary>
|
||||
PostRefresh,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired before an addon begins processing a user-driven event via
|
||||
/// <see cref="AtkEventListener.ReceiveEvent"/>, such as mousing over an element or clicking a button. This event
|
||||
|
|
@ -112,7 +112,7 @@ public enum AddonEvent
|
|||
/// <seealso cref="AddonReceiveEventArgs"/>
|
||||
/// <seealso cref="PostReceiveEvent"/>
|
||||
PreReceiveEvent,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired after an addon finishes calling its <see cref="AtkEventListener.ReceiveEvent"/> method.
|
||||
/// See <see cref="PreReceiveEvent"/> for more information.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Dalamud.Game.ClientState.JobGauge.Enums;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Gauge;
|
||||
|
||||
namespace Dalamud.Game.ClientState.JobGauge.Types;
|
||||
|
|
@ -69,37 +71,39 @@ public unsafe class SMNGauge : JobGaugeBase<SummonerGauge>
|
|||
/// Gets the current aether flags.
|
||||
/// Use the summon accessors instead.
|
||||
/// </summary>
|
||||
[Api14ToDo("Declare our own enum for this to avoid CS type.")]
|
||||
[Obsolete("Use specific accessors instead until API14.")]
|
||||
public AetherFlags AetherFlags => this.Struct->AetherFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Bahamut is ready to be summoned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> or <c>false</c>.</returns>
|
||||
public bool IsBahamutReady => !this.AetherFlags.HasFlag(AetherFlags.PhoenixReady);
|
||||
public bool IsBahamutReady => !this.Struct->AetherFlags.HasFlag(AetherFlags.PhoenixReady);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether if Phoenix is ready to be summoned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> or <c>false</c>.</returns>
|
||||
public bool IsPhoenixReady => this.AetherFlags.HasFlag(AetherFlags.PhoenixReady);
|
||||
public bool IsPhoenixReady => this.Struct->AetherFlags.HasFlag(AetherFlags.PhoenixReady);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether if Ifrit is ready to be summoned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> or <c>false</c>.</returns>
|
||||
public bool IsIfritReady => this.AetherFlags.HasFlag(AetherFlags.IfritReady);
|
||||
public bool IsIfritReady => this.Struct->AetherFlags.HasFlag(AetherFlags.IfritReady);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether if Titan is ready to be summoned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> or <c>false</c>.</returns>
|
||||
public bool IsTitanReady => this.AetherFlags.HasFlag(AetherFlags.TitanReady);
|
||||
public bool IsTitanReady => this.Struct->AetherFlags.HasFlag(AetherFlags.TitanReady);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether if Garuda is ready to be summoned.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> or <c>false</c>.</returns>
|
||||
public bool IsGarudaReady => this.AetherFlags.HasFlag(AetherFlags.GarudaReady);
|
||||
public bool IsGarudaReady => this.Struct->AetherFlags.HasFlag(AetherFlags.GarudaReady);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether if Ifrit is currently attuned.
|
||||
|
|
@ -128,5 +132,5 @@ public unsafe class SMNGauge : JobGaugeBase<SummonerGauge>
|
|||
/// <summary>
|
||||
/// Gets the amount of Aetherflow available.
|
||||
/// </summary>
|
||||
public byte AetherflowStacks => (byte)(this.AetherFlags & AetherFlags.Aetherflow);
|
||||
public byte AetherflowStacks => (byte)(this.Struct->AetherFlags & AetherFlags.Aetherflow);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api13ToDo("Maybe make this config scoped to internal name?")]
|
||||
[Api14ToDo("Maybe make this config scoped to internal name?")]
|
||||
public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public abstract class Easing
|
|||
/// Gets the current value of the animation, following unclamped logic.
|
||||
/// </summary>
|
||||
[Obsolete($"This field has been deprecated. Use either {nameof(ValueClamped)} or {nameof(ValueUnclamped)} instead.", true)]
|
||||
[Api13ToDo("Map this field to ValueClamped, probably.")]
|
||||
[Api14ToDo("Map this field to ValueClamped, probably.")]
|
||||
public double Value => this.ValueUnclamped;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete. To be fixed with API14.
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -21,14 +25,10 @@ public static partial class ImGuiComponents
|
|||
/// <param name="helpText">The text to display on hover.</param>
|
||||
/// <param name="icon">The icon to use.</param>
|
||||
/// <param name="color">The color of the icon.</param>
|
||||
public static void HelpMarker(string helpText, FontAwesomeIcon icon, Vector4? color = null)
|
||||
public static void HelpMarker(string helpText, FontAwesomeIcon icon, System.Numerics.Vector4 color)
|
||||
{
|
||||
using var col = new ImRaii.Color();
|
||||
|
||||
if (color.HasValue)
|
||||
{
|
||||
col.Push(ImGuiCol.TextDisabled, color.Value);
|
||||
}
|
||||
col.Push(ImGuiCol.TextDisabled, color);
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
|
|
@ -48,4 +48,40 @@ public static partial class ImGuiComponents
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HelpMarker component to add a custom icon with text on hover.
|
||||
/// </summary>
|
||||
/// <param name="helpText">The text to display on hover.</param>
|
||||
/// <param name="icon">The icon to use.</param>
|
||||
/// <param name="color">The color of the icon.</param>
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
[Obsolete("CS type is deprecated. Use System.Numerics.Vector4 instead.")]
|
||||
public static void HelpMarker(string helpText, FontAwesomeIcon icon, Vector4? color = null)
|
||||
{
|
||||
if (color.HasValue)
|
||||
{
|
||||
HelpMarker(helpText, icon, color.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Code duplication is easier than splitting up the Nullable in a way that doesn't break the API.
|
||||
ImGui.SameLine();
|
||||
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
{
|
||||
ImGui.TextDisabled(icon.ToIconString());
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using (ImRaii.Tooltip())
|
||||
{
|
||||
using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f))
|
||||
{
|
||||
ImGui.Text(helpText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -382,6 +382,8 @@ public static unsafe class MemoryHelper
|
|||
/// <param name="utf8String">The memory address to read from.</param>
|
||||
/// <returns>The read in string.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Obsolete("CS types in Dalamud are deprecated.")]
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
public static SeString ReadSeString(Utf8String* utf8String) =>
|
||||
utf8String == null ? string.Empty : SeString.Parse(utf8String->AsSpan());
|
||||
|
||||
|
|
@ -613,6 +615,8 @@ public static unsafe class MemoryHelper
|
|||
/// <param name="utf8String">The memory address to read from.</param>
|
||||
/// <param name="value">The read in string.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Obsolete("CS types in Dalamud are deprecated.")]
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
public static unsafe void ReadSeString(Utf8String* utf8String, out SeString value)
|
||||
=> value = ReadSeString(utf8String);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace Dalamud.Utility;
|
|||
/// Utility class for marking something to be changed for API 13, for ease of lookup.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.All, Inherited = false)]
|
||||
internal sealed class Api13ToDoAttribute : Attribute
|
||||
internal sealed class Api14ToDoAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks that this should be made internal.
|
||||
|
|
@ -12,11 +12,16 @@ internal sealed class Api13ToDoAttribute : Attribute
|
|||
public const string MakeInternal = "Make internal.";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Api13ToDoAttribute"/> class.
|
||||
/// Marks that this should be removed entirely.
|
||||
/// </summary>
|
||||
public const string Remove = "Remove.";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Api14ToDoAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="what">The explanation.</param>
|
||||
/// <param name="what2">The explanation 2.</param>
|
||||
public Api13ToDoAttribute(string what, string what2 = "")
|
||||
public Api14ToDoAttribute(string what, string what2 = "")
|
||||
{
|
||||
_ = what;
|
||||
_ = what2;
|
||||
|
|
@ -56,6 +56,8 @@ public static class VectorExtensions
|
|||
return new Vector2(v.X, y);
|
||||
}
|
||||
|
||||
[Obsolete("CS type is deprecated. Use ByteColor instead.")]
|
||||
[Api14ToDo(Api14ToDoAttribute.Remove)]
|
||||
public static ByteColor ToByteColor(this Vector4 v)
|
||||
{
|
||||
return new ByteColor { A = (byte)(v.W * 255), R = (byte)(v.X * 255), G = (byte)(v.Y * 255), B = (byte)(v.Z * 255) };
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public static partial class Util
|
|||
/// <summary>
|
||||
/// Gets the Dalamud version.
|
||||
/// </summary>
|
||||
[Api13ToDo("Remove. Make both versions here internal. Add an API somewhere.")]
|
||||
[Api14ToDo("Remove. Make both versions here internal. Add an API somewhere.")]
|
||||
public static string AssemblyVersion { get; } =
|
||||
Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!.ToString();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue