Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions Dalamud.Test/Compliance/PublicApiTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
14 changes: 7 additions & 7 deletions Dalamud.Test/Dalamud.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Dalamud.Utility;

using FFXIVClientStructs.FFXIV.Component.GUI;

namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
Expand Down Expand Up @@ -31,6 +33,8 @@ public AddonRefreshArgs()
/// <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"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Dalamud.Utility;

using FFXIVClientStructs.FFXIV.Component.GUI;

namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
Expand Down Expand Up @@ -31,6 +33,8 @@ public AddonSetupArgs()
/// <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"/>
Expand Down
16 changes: 8 additions & 8 deletions Dalamud/Game/Addon/Lifecycle/AddonEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down
16 changes: 10 additions & 6 deletions Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -69,37 +71,39 @@ internal SMNGauge(IntPtr address)
/// 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.
Expand Down Expand Up @@ -128,5 +132,5 @@ internal SMNGauge(IntPtr address)
/// <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);
}
2 changes: 1 addition & 1 deletion Dalamud/Game/Gui/Dtr/DtrBarEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public bool Shown
}

/// <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>
Expand Down
2 changes: 1 addition & 1 deletion Dalamud/Interface/Animation/Easing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected Easing(TimeSpan duration)
/// 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>
Expand Down
40 changes: 38 additions & 2 deletions Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -21,15 +25,47 @@ 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();
col.Push(ImGuiCol.TextDisabled, color);

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);
}
}
}
}

/// <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)
{
col.Push(ImGuiCol.TextDisabled, color.Value);
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))
Expand Down
4 changes: 4 additions & 0 deletions Dalamud/Memory/MemoryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ public static SeString ReadSeString(nint memoryAddress, int maxLength) =>
/// <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());

Expand Down Expand Up @@ -613,6 +615,8 @@ public static void ReadSeString(nint memoryAddress, int maxLength, out SeString
/// <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);

Expand Down
Loading
Loading