diff --git a/.editorconfig b/.editorconfig index 829de8f05..0ae30cf95 100644 --- a/.editorconfig +++ b/.editorconfig @@ -35,7 +35,7 @@ dotnet_naming_rule.private_instance_fields_rule.severity = warning dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols dotnet_naming_rule.private_static_fields_rule.severity = warning -dotnet_naming_rule.private_static_fields_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols dotnet_naming_rule.private_static_readonly_rule.severity = warning dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style @@ -57,12 +57,12 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly -dotnet_style_parentheses_in_arithmetic_binary_operators =always_for_clarity:suggestion -dotnet_style_parentheses_in_other_binary_operators =always_for_clarity:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion -dotnet_style_parentheses_in_other_operators=always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = always_for_clarity:silent dotnet_style_object_initializer = false dotnet_style_qualification_for_event = true:suggestion dotnet_style_qualification_for_field = true:suggestion @@ -78,7 +78,7 @@ csharp_space_before_comma = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_comma = true csharp_space_after_cast = false -csharp_space_around_binary_operators = before_and_after +csharp_space_around_binary_operators = before_and_after csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = none @@ -101,7 +101,7 @@ resharper_braces_for_ifelse = required_for_multiline resharper_can_use_global_alias = false resharper_csharp_align_multiline_parameter = true resharper_csharp_align_multiple_declaration = true -resharper_csharp_empty_block_style = together_same_line +resharper_csharp_empty_block_style = multiline resharper_csharp_int_align_comments = true resharper_csharp_new_line_before_while = true resharper_csharp_wrap_after_declaration_lpar = true @@ -133,13 +133,13 @@ resharper_suggest_var_or_type_built_in_types_highlighting = hint resharper_suggest_var_or_type_elsewhere_highlighting = hint resharper_suggest_var_or_type_simple_types_highlighting = hint resharper_unused_auto_property_accessor_global_highlighting = none -csharp_style_deconstructed_variable_declaration=true:silent +csharp_style_deconstructed_variable_declaration = true:silent [*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] indent_style = space indent_size = 4 tab_width = 4 -dotnet_style_parentheses_in_other_operators=always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = always_for_clarity:silent [*.{yaml,yml}] indent_style = space diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index 9a741a47f..94f1c7d0f 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -133,8 +133,8 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { // ============================== VEH ======================================== // logging::I("Initializing VEH..."); - if (utils::is_running_on_linux()) { - logging::I("=> VEH was disabled, running on linux"); + if (utils::is_running_on_wine()) { + logging::I("=> VEH was disabled, running on wine"); } else if (g_startInfo.BootVehEnabled) { if (veh::add_handler(g_startInfo.BootVehFull, g_startInfo.WorkingDirectory)) logging::I("=> Done!"); diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index 79205eb8d..b45795045 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -578,7 +578,7 @@ std::vector utils::get_env_list(const wchar_t* pcszName) { return res; } -bool utils::is_running_on_linux() { +bool utils::is_running_on_wine() { if (get_env(L"XL_WINEONLINUX")) return true; HMODULE hntdll = GetModuleHandleW(L"ntdll.dll"); @@ -588,6 +588,10 @@ bool utils::is_running_on_linux() { return true; if (GetProcAddress(hntdll, "wine_get_host_version")) return true; + if (GetProcAddress(hntdll, "wine_server_call")) + return true; + if (GetProcAddress(hntdll, "wine_unix_to_nt_file_name")) + return true; return false; } diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index 5d5c90dde..5e3caa4d6 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -264,7 +264,7 @@ namespace utils { return get_env_list(unicode::convert(pcszName).c_str()); } - bool is_running_on_linux(); + bool is_running_on_wine(); std::filesystem::path get_module_path(HMODULE hModule); diff --git a/Dalamud.Common/ClientLanguage.cs b/Dalamud.Common/ClientLanguage.cs new file mode 100644 index 000000000..1fd58dce1 --- /dev/null +++ b/Dalamud.Common/ClientLanguage.cs @@ -0,0 +1,27 @@ +namespace Dalamud.Common; + +/// +/// Enum describing the language the game loads in. +/// +public enum ClientLanguage +{ + /// + /// Indicating a Japanese game client. + /// + Japanese, + + /// + /// Indicating an English game client. + /// + English, + + /// + /// Indicating a German game client. + /// + German, + + /// + /// Indicating a French game client. + /// + French, +} diff --git a/Dalamud.Common/Dalamud.Common.csproj b/Dalamud.Common/Dalamud.Common.csproj new file mode 100644 index 000000000..ac5d3fdba --- /dev/null +++ b/Dalamud.Common/Dalamud.Common.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud.Common/DalamudStartInfo.cs similarity index 96% rename from Dalamud/DalamudStartInfo.cs rename to Dalamud.Common/DalamudStartInfo.cs index 63a61c97e..069a0ef9f 100644 --- a/Dalamud/DalamudStartInfo.cs +++ b/Dalamud.Common/DalamudStartInfo.cs @@ -1,16 +1,13 @@ -using System; -using System.Collections.Generic; - -using Dalamud.Game; +using Dalamud.Common.Game; using Newtonsoft.Json; -namespace Dalamud; +namespace Dalamud.Common; /// /// Struct containing information needed to initialize Dalamud. /// [Serializable] -public record DalamudStartInfo : IServiceType +public record DalamudStartInfo { /// /// Initializes a new instance of the class. @@ -96,7 +93,7 @@ public record DalamudStartInfo : IServiceType /// /// Gets or sets troubleshooting information to attach when generating a tspack file. /// - public string TroubleshootingPackData { get; set; } + public string? TroubleshootingPackData { get; set; } /// /// Gets or sets a value that specifies how much to wait before a new Dalamud session. diff --git a/Dalamud/Game/GameVersion.cs b/Dalamud.Common/Game/GameVersion.cs similarity index 97% rename from Dalamud/Game/GameVersion.cs rename to Dalamud.Common/Game/GameVersion.cs index 2b2021e60..26ff0e48f 100644 --- a/Dalamud/Game/GameVersion.cs +++ b/Dalamud.Common/Game/GameVersion.cs @@ -1,11 +1,9 @@ -using System; using System.Globalization; -using System.Linq; using System.Text; using Newtonsoft.Json; -namespace Dalamud.Game; +namespace Dalamud.Common.Game; /// /// A GameVersion object contains give hierarchical numeric components: year, month, @@ -168,14 +166,14 @@ public sealed class GameVersion : ICloneable, IComparable, IComparable new GameVersion(this.Year, this.Month, this.Day, this.Major, this.Minor); /// - public int CompareTo(object obj) + public int CompareTo(object? obj) { if (obj == null) return 1; @@ -315,7 +313,7 @@ public sealed class GameVersion : ICloneable, IComparable, IComparable - public int CompareTo(GameVersion value) + public int CompareTo(GameVersion? value) { if (value == null) return 1; @@ -348,7 +346,7 @@ public sealed class GameVersion : ICloneable, IComparable, IComparable - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is not GameVersion value) return false; @@ -357,7 +355,7 @@ public sealed class GameVersion : ICloneable, IComparable, IComparable - public bool Equals(GameVersion value) + public bool Equals(GameVersion? value) { if (value == null) { diff --git a/Dalamud/Game/GameVersionConverter.cs b/Dalamud.Common/Game/GameVersionConverter.cs similarity index 98% rename from Dalamud/Game/GameVersionConverter.cs rename to Dalamud.Common/Game/GameVersionConverter.cs index f307b6fb9..a1876869a 100644 --- a/Dalamud/Game/GameVersionConverter.cs +++ b/Dalamud.Common/Game/GameVersionConverter.cs @@ -1,8 +1,6 @@ -using System; - using Newtonsoft.Json; -namespace Dalamud.Game; +namespace Dalamud.Common.Game; /// /// Converts a to and from a string (e.g. "2010.01.01.1234.5678"). diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index 25919af07..3938d0c80 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -27,7 +27,7 @@ - + diff --git a/Dalamud.CorePlugin/PluginImpl.cs b/Dalamud.CorePlugin/PluginImpl.cs index 2f76a1087..03f11e989 100644 --- a/Dalamud.CorePlugin/PluginImpl.cs +++ b/Dalamud.CorePlugin/PluginImpl.cs @@ -2,9 +2,9 @@ using System; using System.IO; using Dalamud.Configuration.Internal; +using Dalamud.Game; using Dalamud.Game.Command; using Dalamud.Interface.Windowing; -using Dalamud.Logging; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Dalamud.Utility; @@ -39,9 +39,6 @@ namespace Dalamud.CorePlugin { } - /// - public string Name => "Dalamud.CorePlugin"; - /// public void Dispose() { @@ -52,37 +49,39 @@ namespace Dalamud.CorePlugin private readonly WindowSystem windowSystem = new("Dalamud.CorePlugin"); private Localization localization; + private IPluginLog pluginLog; + /// /// Initializes a new instance of the class. /// /// Dalamud plugin interface. /// Logging service. - public PluginImpl(DalamudPluginInterface pluginInterface, IPluginLog log) + public PluginImpl(DalamudPluginInterface pluginInterface, IPluginLog log, ISigScanner scanner) { try { // this.InitLoc(); this.Interface = pluginInterface; + this.pluginLog = log; this.windowSystem.AddWindow(new PluginWindow()); + this.pluginLog.Information(scanner.ToString()); + this.Interface.UiBuilder.Draw += this.OnDraw; this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi; this.Interface.UiBuilder.OpenMainUi += this.OnOpenMainUi; - Service.Get().AddHandler("/coreplug", new(this.OnCommand) { HelpMessage = $"Access the {this.Name} plugin." }); + Service.Get().AddHandler("/coreplug", new(this.OnCommand) { HelpMessage = "Access the plugin." }); log.Information("CorePlugin ctor!"); } catch (Exception ex) { - PluginLog.Error(ex, "kaboom"); + log.Error(ex, "kaboom"); } } - /// - public string Name => "Dalamud.CorePlugin"; - /// /// Gets the plugin interface. /// @@ -130,13 +129,13 @@ namespace Dalamud.CorePlugin } catch (Exception ex) { - PluginLog.Error(ex, "Boom"); + this.pluginLog.Error(ex, "Boom"); } } private void OnCommand(string command, string args) { - PluginLog.Information("Command called!"); + this.pluginLog.Information("Command called!"); // this.window.IsOpen = true; } diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj index ea9e4f0a3..d8a74e58d 100644 --- a/Dalamud.Injector/Dalamud.Injector.csproj +++ b/Dalamud.Injector/Dalamud.Injector.csproj @@ -81,12 +81,6 @@ - - - - - - - + diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs index a35248062..bd9fa87f8 100644 --- a/Dalamud.Injector/EntryPoint.cs +++ b/Dalamud.Injector/EntryPoint.cs @@ -9,7 +9,8 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; -using Dalamud.Game; +using Dalamud.Common; +using Dalamud.Common.Game; using Newtonsoft.Json; using Reloaded.Memory.Buffers; using Serilog; diff --git a/Dalamud.Interface/Dalamud.Interface.csproj b/Dalamud.Interface/Dalamud.Interface.csproj deleted file mode 100644 index 1dd8468be..000000000 --- a/Dalamud.Interface/Dalamud.Interface.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net7.0-windows - x64 - x64;AnyCPU - enable - enable - true - Dalamud.Interface - - - - - - - diff --git a/Dalamud.Interface/ImGuiTable.cs b/Dalamud.Interface/ImGuiTable.cs deleted file mode 100644 index 5ea6a2c9a..000000000 --- a/Dalamud.Interface/ImGuiTable.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Dalamud.Interface.Raii; -using ImGuiNET; - -namespace Dalamud.Interface; - -public static class ImGuiTable -{ - // Draw a simple table with the given data using the drawRow action. - // Headers and thus columns and column count are defined by columnTitles. - public static void DrawTable(string label, IEnumerable data, Action drawRow, ImGuiTableFlags flags = ImGuiTableFlags.None, - params string[] columnTitles) - { - if (columnTitles.Length == 0) - return; - - using var table = ImRaii.Table(label, columnTitles.Length, flags); - if (!table) - return; - - foreach (var title in columnTitles) - { - ImGui.TableNextColumn(); - ImGui.TableHeader(title); - } - - foreach (var datum in data) - { - ImGui.TableNextRow(); - drawRow(datum); - } - } - - // Draw a simple table with the given data using the drawRow action inside a collapsing header. - // Headers and thus columns and column count are defined by columnTitles. - public static void DrawTabbedTable(string label, IEnumerable data, Action drawRow, ImGuiTableFlags flags = ImGuiTableFlags.None, - params string[] columnTitles) - { - if (ImGui.CollapsingHeader(label)) - DrawTable($"{label}##Table", data, drawRow, flags, columnTitles); - } -} diff --git a/Dalamud.Interface/InterfaceHelpers.cs b/Dalamud.Interface/InterfaceHelpers.cs deleted file mode 100644 index 26f09bedb..000000000 --- a/Dalamud.Interface/InterfaceHelpers.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Dalamud.Interface; - -public static class InterfaceHelpers -{ - public static float GlobalScale = 1.0f; -} diff --git a/Dalamud.Test/Game/GameVersionTests.cs b/Dalamud.Test/Game/GameVersionTests.cs index 44a5813c8..dcace4279 100644 --- a/Dalamud.Test/Game/GameVersionTests.cs +++ b/Dalamud.Test/Game/GameVersionTests.cs @@ -1,4 +1,4 @@ -using Dalamud.Game; +using Dalamud.Common.Game; using Xunit; namespace Dalamud.Test.Game diff --git a/Dalamud.sln b/Dalamud.sln index 20442e52d..200238a83 100644 --- a/Dalamud.sln +++ b/Dalamud.sln @@ -38,7 +38,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.InteropS EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DalamudCrashHandler", "DalamudCrashHandler\DalamudCrashHandler.vcxproj", "{317A264C-920B-44A1-8A34-F3A6827B0705}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Interface", "Dalamud.Interface\Dalamud.Interface.csproj", "{757C997D-AA58-4241-8299-243C56514917}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Common", "Dalamud.Common\Dalamud.Common.csproj", "{F21B13D2-D7D0-4456-B70F-3F8D695064E2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -204,18 +204,18 @@ Global {317A264C-920B-44A1-8A34-F3A6827B0705}.Release|x64.Build.0 = Release|x64 {317A264C-920B-44A1-8A34-F3A6827B0705}.Release|x86.ActiveCfg = Release|x64 {317A264C-920B-44A1-8A34-F3A6827B0705}.Release|x86.Build.0 = Release|x64 - {757C997D-AA58-4241-8299-243C56514917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Debug|Any CPU.Build.0 = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Debug|x64.ActiveCfg = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Debug|x64.Build.0 = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Debug|x86.ActiveCfg = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Debug|x86.Build.0 = Debug|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|Any CPU.ActiveCfg = Release|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|Any CPU.Build.0 = Release|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|x64.ActiveCfg = Release|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|x64.Build.0 = Release|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|x86.ActiveCfg = Release|Any CPU - {757C997D-AA58-4241-8299-243C56514917}.Release|x86.Build.0 = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|x64.Build.0 = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Debug|x86.Build.0 = Debug|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|Any CPU.Build.0 = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|x64.ActiveCfg = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|x64.Build.0 = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|x86.ActiveCfg = Release|Any CPU + {F21B13D2-D7D0-4456-B70F-3F8D695064E2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Dalamud/ClientLanguage.cs b/Dalamud/ClientLanguage.cs index 4e04d4a54..8f2c52456 100644 --- a/Dalamud/ClientLanguage.cs +++ b/Dalamud/ClientLanguage.cs @@ -1,5 +1,7 @@ namespace Dalamud; +// TODO(v10): Delete this, and use Dalamud.Common.ClientLanguage instead for everything. + /// /// Enum describing the language the game loads in. /// diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 2d0a08942..65f10c4ba 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -6,7 +5,9 @@ using System.Linq; using Dalamud.Game.Text; using Dalamud.Interface.Style; +using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal.Profiles; +using Dalamud.Storage; using Dalamud.Utility; using Newtonsoft.Json; using Serilog; @@ -18,7 +19,11 @@ namespace Dalamud.Configuration.Internal; /// Class containing Dalamud settings. /// [Serializable] -internal sealed class DalamudConfiguration : IServiceType +[ServiceManager.Service] +#pragma warning disable SA1015 +[InherentDependency] // We must still have this when unloading +#pragma warning restore SA1015 +internal sealed class DalamudConfiguration : IServiceType, IDisposable { private static readonly JsonSerializerSettings SerializerSettings = new() { @@ -422,23 +427,37 @@ internal sealed class DalamudConfiguration : IServiceType /// /// Load a configuration from the provided path. /// - /// The path to load the configuration file from. + /// Path to read from. + /// File storage. /// The deserialized configuration file. - public static DalamudConfiguration Load(string path) + public static DalamudConfiguration Load(string path, ReliableFileStorage fs) { DalamudConfiguration deserialized = null; + try { - deserialized = JsonConvert.DeserializeObject(File.ReadAllText(path), SerializerSettings); + fs.ReadAllText(path, text => + { + deserialized = + JsonConvert.DeserializeObject(text, SerializerSettings); + + // If this reads as null, the file was empty, that's no good + if (deserialized == null) + throw new Exception("Read config was null."); + }); } - catch (Exception ex) + catch (FileNotFoundException) { - Log.Warning(ex, "Failed to load DalamudConfiguration at {0}", path); + // ignored + } + catch (Exception e) + { + Log.Error(e, "Could not load DalamudConfiguration at {Path}, creating new", path); } deserialized ??= new DalamudConfiguration(); deserialized.configPath = path; - + return deserialized; } @@ -457,6 +476,13 @@ internal sealed class DalamudConfiguration : IServiceType { this.Save(); } + + /// + public void Dispose() + { + // Make sure that we save, if a save is queued while we are shutting down + this.Update(); + } /// /// Save the file, if needed. Only needs to be done once a frame. @@ -476,7 +502,8 @@ internal sealed class DalamudConfiguration : IServiceType { ThreadSafety.AssertMainThread(); - Util.WriteAllTextSafe(this.configPath, JsonConvert.SerializeObject(this, SerializerSettings)); + Service.Get().WriteAllText( + this.configPath, JsonConvert.SerializeObject(this, SerializerSettings)); this.DalamudConfigurationSaved?.Invoke(this); } } diff --git a/Dalamud/Configuration/PluginConfigurations.cs b/Dalamud/Configuration/PluginConfigurations.cs index 957a7c99e..de5e071c1 100644 --- a/Dalamud/Configuration/PluginConfigurations.cs +++ b/Dalamud/Configuration/PluginConfigurations.cs @@ -1,6 +1,6 @@ using System.IO; -using Dalamud.Utility; +using Dalamud.Storage; using Newtonsoft.Json; namespace Dalamud.Configuration; @@ -31,24 +31,39 @@ public sealed class PluginConfigurations /// /// Plugin configuration. /// Plugin name. - public void Save(IPluginConfiguration config, string pluginName) + /// WorkingPluginId of the plugin. + public void Save(IPluginConfiguration config, string pluginName, Guid workingPluginId) { - Util.WriteAllTextSafe(this.GetConfigFile(pluginName).FullName, SerializeConfig(config)); + Service.Get() + .WriteAllText(this.GetConfigFile(pluginName).FullName, SerializeConfig(config), workingPluginId); } /// /// Load plugin configuration. /// /// Plugin name. + /// WorkingPluginID of the plugin. /// Plugin configuration. - public IPluginConfiguration? Load(string pluginName) + public IPluginConfiguration? Load(string pluginName, Guid workingPluginId) { var path = this.GetConfigFile(pluginName); - if (!path.Exists) - return null; + IPluginConfiguration? config = null; + try + { + Service.Get().ReadAllText(path.FullName, text => + { + config = DeserializeConfig(text); + if (config == null) + throw new Exception("Read config was null."); + }, workingPluginId); + } + catch (FileNotFoundException) + { + // ignored + } - return DeserializeConfig(File.ReadAllText(path.FullName)); + return config; } /// diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index c38594771..f50a39aa3 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.IO; using System.Linq; @@ -7,12 +6,15 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Dalamud.Common; using Dalamud.Configuration.Internal; using Dalamud.Game; using Dalamud.Game.Gui.Internal; using Dalamud.Interface.Internal; using Dalamud.Plugin.Internal; +using Dalamud.Storage; using Dalamud.Utility; +using Dalamud.Utility.Timing; using PInvoke; using Serilog; @@ -28,6 +30,7 @@ namespace Dalamud; /// /// The main Dalamud class containing all subsystems. /// +[ServiceManager.Service] internal sealed class Dalamud : IServiceType { #region Internals @@ -40,26 +43,48 @@ internal sealed class Dalamud : IServiceType /// Initializes a new instance of the class. /// /// DalamudStartInfo instance. + /// ReliableFileStorage instance. /// The Dalamud configuration. /// Event used to signal the main thread to continue. - public Dalamud(DalamudStartInfo info, DalamudConfiguration configuration, IntPtr mainThreadContinueEvent) + public Dalamud(DalamudStartInfo info, ReliableFileStorage fs, DalamudConfiguration configuration, IntPtr mainThreadContinueEvent) { + this.StartInfo = info; + this.unloadSignal = new ManualResetEvent(false); this.unloadSignal.Reset(); + + // Directory resolved signatures(CS, our own) will be cached in + var cacheDir = new DirectoryInfo(Path.Combine(this.StartInfo.WorkingDirectory!, "cachedSigs")); + if (!cacheDir.Exists) + cacheDir.Create(); + + // Set up the SigScanner for our target module + TargetSigScanner scanner; + using (Timings.Start("SigScanner Init")) + { + scanner = new TargetSigScanner( + true, new FileInfo(Path.Combine(cacheDir.FullName, $"{this.StartInfo.GameVersion}.json"))); + } - ServiceManager.InitializeProvidedServicesAndClientStructs(this, info, configuration); + ServiceManager.InitializeProvidedServices(this, fs, configuration, scanner); + + // Set up FFXIVClientStructs + this.SetupClientStructsResolver(cacheDir); if (!configuration.IsResumeGameAfterPluginLoad) { NativeFunctions.SetEvent(mainThreadContinueEvent); - try - { - _ = ServiceManager.InitializeEarlyLoadableServices(); - } - catch (Exception e) - { - Log.Error(e, "Service initialization failure"); - } + ServiceManager.InitializeEarlyLoadableServices() + .ContinueWith(t => + { + if (t.IsCompletedSuccessfully) + return; + + Log.Error(t.Exception!, "Service initialization failure"); + Util.Fatal( + "Dalamud failed to load all necessary services.\n\nThe game will continue, but you may not be able to use plugins.", + "Dalamud", false); + }); } else { @@ -94,11 +119,16 @@ internal sealed class Dalamud : IServiceType }); } } + + /// + /// Gets the start information for this Dalamud instance. + /// + internal DalamudStartInfo StartInfo { get; private set; } /// /// Gets location of stored assets. /// - internal DirectoryInfo AssetDirectory => new(Service.Get().AssetDirectory!); + internal DirectoryInfo AssetDirectory => new(this.StartInfo.AssetDirectory!); /// /// Signal to the crash handler process that we should restart the game. @@ -167,10 +197,19 @@ internal sealed class Dalamud : IServiceType internal void ReplaceExceptionHandler() { var releaseSig = "40 55 53 56 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 48 83 3D ?? ?? ?? ?? ??"; - var releaseFilter = Service.Get().ScanText(releaseSig); + var releaseFilter = Service.Get().ScanText(releaseSig); Log.Debug($"SE debug filter at {releaseFilter.ToInt64():X}"); var oldFilter = NativeFunctions.SetUnhandledExceptionFilter(releaseFilter); Log.Debug("Reset ExceptionFilter, old: {0}", oldFilter); } + + private void SetupClientStructsResolver(DirectoryInfo cacheDir) + { + using (Timings.Start("CS Resolver Init")) + { + FFXIVClientStructs.Interop.Resolver.GetInstance.SetupSearchSpace(Service.Get().SearchBase, new FileInfo(Path.Combine(cacheDir.FullName, $"{this.StartInfo.GameVersion}_cs.json"))); + FFXIVClientStructs.Interop.Resolver.GetInstance.Resolve(); + } + } } diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index bcc06d435..69d08f517 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -8,7 +8,7 @@ - 7.11.0.0 + 9.0.0.0 XIV Launcher addon framework $(DalamudVersion) $(DalamudVersion) @@ -68,7 +68,7 @@ - + @@ -77,6 +77,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -88,7 +89,7 @@ - + @@ -106,6 +107,10 @@ + + + + diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index a4c81c7a7..6195532ab 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -1,27 +1,20 @@ -using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; -using Dalamud.Interface.Internal; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using Dalamud.Utility.Timing; -using ImGuiScene; using JetBrains.Annotations; using Lumina; using Lumina.Data; -using Lumina.Data.Files; -using Lumina.Data.Parsing.Tex.Buffers; using Lumina.Excel; using Newtonsoft.Json; using Serilog; -using SharpDX.DXGI; namespace Dalamud.Data; @@ -34,18 +27,15 @@ namespace Dalamud.Data; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed class DataManager : IDisposable, IServiceType, IDataManager +internal sealed class DataManager : IDisposable, IServiceType, IDataManager { - private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex"; - private const string HighResolutionIconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}_hr1.tex"; - private readonly Thread luminaResourceThread; private readonly CancellationTokenSource luminaCancellationTokenSource; [ServiceManager.ServiceConstructor] - private DataManager(DalamudStartInfo dalamudStartInfo, Dalamud dalamud) + private DataManager(Dalamud dalamud) { - this.Language = dalamudStartInfo.Language; + this.Language = (ClientLanguage)dalamud.StartInfo.Language; // Set up default values so plugins do not null-reference when data is being loaded. this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary(new Dictionary()); @@ -93,17 +83,20 @@ public sealed class DataManager : IDisposable, IServiceType, IDataManager Log.Information("Lumina is ready: {0}", this.GameData.DataPath); - try + if (!dalamud.StartInfo.TroubleshootingPackData.IsNullOrEmpty()) { - var tsInfo = - JsonConvert.DeserializeObject( - dalamudStartInfo.TroubleshootingPackData); - this.HasModifiedGameDataFiles = - tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed or LauncherTroubleshootingInfo.IndexIntegrityResult.Exception; - } - catch - { - // ignored + try + { + var tsInfo = + JsonConvert.DeserializeObject( + dalamud.StartInfo.TroubleshootingPackData); + this.HasModifiedGameDataFiles = + tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed or LauncherTroubleshootingInfo.IndexIntegrityResult.Exception; + } + catch + { + // ignored + } } } @@ -137,10 +130,14 @@ public sealed class DataManager : IDisposable, IServiceType, IDataManager /// public ClientLanguage Language { get; private set; } - /// + /// + /// Gets a list of server opcodes. + /// public ReadOnlyDictionary ServerOpCodes { get; private set; } - - /// + + /// + /// Gets a list of client opcodes. + /// [UsedImplicitly] public ReadOnlyDictionary ClientOpCodes { get; private set; } @@ -150,12 +147,14 @@ public sealed class DataManager : IDisposable, IServiceType, IDataManager /// public ExcelModule Excel => this.GameData.Excel; - /// - public bool IsDataReady { get; private set; } - /// public bool HasModifiedGameDataFiles { get; private set; } + /// + /// Gets a value indicating whether Game Data is ready to be read. + /// + internal bool IsDataReady { get; private set; } + #region Lumina Wrappers /// @@ -183,158 +182,6 @@ public sealed class DataManager : IDisposable, IServiceType, IDataManager public bool FileExists(string path) => this.GameData.FileExists(path); - /// - /// Get a containing the icon with the given ID. - /// - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(uint iconId) - => this.GetIcon(this.Language, iconId, false); - - /// - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(uint iconId, bool highResolution) - => this.GetIcon(this.Language, iconId, highResolution); - - /// - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(bool isHq, uint iconId) - { - var type = isHq ? "hq/" : string.Empty; - return this.GetIcon(type, iconId); - } - - /// - /// Get a containing the icon with the given ID, of the given language. - /// - /// The requested language. - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(ClientLanguage iconLanguage, uint iconId) - => this.GetIcon(iconLanguage, iconId, false); - - /// - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(ClientLanguage iconLanguage, uint iconId, bool highResolution) - { - var type = iconLanguage switch - { - ClientLanguage.Japanese => "ja/", - ClientLanguage.English => "en/", - ClientLanguage.German => "de/", - ClientLanguage.French => "fr/", - _ => throw new ArgumentOutOfRangeException(nameof(iconLanguage), $"Unknown Language: {iconLanguage}"), - }; - - return this.GetIcon(type, iconId, highResolution); - } - - /// - /// Get a containing the icon with the given ID, of the given type. - /// - /// The type of the icon (e.g. 'hq' to get the HQ variant of an item icon). - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(string? type, uint iconId) - => this.GetIcon(type, iconId, false); - - /// - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(string? type, uint iconId, bool highResolution) - { - var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat; - - type ??= string.Empty; - if (type.Length > 0 && !type.EndsWith("/")) - type += "/"; - - var filePath = string.Format(format, iconId / 1000, type, iconId); - var file = this.GetFile(filePath); - - if (type == string.Empty || file != default) - return file; - - // Couldn't get specific type, try for generic version. - filePath = string.Format(format, iconId / 1000, string.Empty, iconId); - file = this.GetFile(filePath); - return file; - } - - /// - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetHqIcon(uint iconId) - => this.GetIcon(true, iconId); - - /// - [Obsolete("Use ITextureProvider instead")] - [return: NotNullIfNotNull(nameof(tex))] - public TextureWrap? GetImGuiTexture(TexFile? tex) - { - if (tex is null) - return null; - - var im = Service.Get(); - var buffer = tex.TextureBuffer; - var bpp = 1 << (((int)tex.Header.Format & (int)TexFile.TextureFormat.BppMask) >> - (int)TexFile.TextureFormat.BppShift); - - var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(tex.Header.Format, false); - if (conversion != TexFile.DxgiFormatConversion.NoConversion || !im.SupportsDxgiFormat((Format)dxgiFormat)) - { - dxgiFormat = (int)Format.B8G8R8A8_UNorm; - buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8); - bpp = 32; - } - - var pitch = buffer is BlockCompressionTextureBuffer - ? Math.Max(1, (buffer.Width + 3) / 4) * 2 * bpp - : ((buffer.Width * bpp) + 7) / 8; - return im.LoadImageFromDxgiFormat(buffer.RawData, pitch, buffer.Width, buffer.Height, (Format)dxgiFormat); - } - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTexture(string path) - => this.GetImGuiTexture(this.GetFile(path)); - - /// - /// Get a containing the icon with the given ID. - /// - /// The icon ID. - /// The containing the icon. - /// TODO(v9): remove in api9 in favor of GetImGuiTextureIcon(uint iconId, bool highResolution) - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(uint iconId) - => this.GetImGuiTexture(this.GetIcon(iconId, false)); - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(uint iconId, bool highResolution) - => this.GetImGuiTexture(this.GetIcon(iconId, highResolution)); - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(bool isHq, uint iconId) - => this.GetImGuiTexture(this.GetIcon(isHq, iconId)); - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(ClientLanguage iconLanguage, uint iconId) - => this.GetImGuiTexture(this.GetIcon(iconLanguage, iconId)); - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(string type, uint iconId) - => this.GetImGuiTexture(this.GetIcon(type, iconId)); - - /// - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureHqIcon(uint iconId) - => this.GetImGuiTexture(this.GetHqIcon(iconId)); - #endregion /// diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 1975505a8..c9537eda6 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.IO; using System.Net; @@ -6,10 +5,12 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Dalamud.Common; using Dalamud.Configuration.Internal; using Dalamud.Logging.Internal; using Dalamud.Logging.Retention; using Dalamud.Plugin.Internal; +using Dalamud.Storage; using Dalamud.Support; using Dalamud.Utility; using Newtonsoft.Json; @@ -137,7 +138,8 @@ public sealed class EntryPoint SerilogEventSink.Instance.LogLine += SerilogOnLogLine; // Load configuration first to get some early persistent state, like log level - var configuration = DalamudConfiguration.Load(info.ConfigurationPath!); + var fs = new ReliableFileStorage(Path.GetDirectoryName(info.ConfigurationPath)!); + var configuration = DalamudConfiguration.Load(info.ConfigurationPath!, fs); // Set the appropriate logging level from the configuration if (!configuration.LogSynchronously) @@ -161,15 +163,18 @@ public sealed class EntryPoint Log.Information(new string('-', 80)); Log.Information("Initializing a session.."); + if (string.IsNullOrEmpty(info.WorkingDirectory)) + throw new Exception("Working directory was invalid"); + Reloaded.Hooks.Tools.Utilities.FasmBasePath = new DirectoryInfo(info.WorkingDirectory); // This is due to GitHub not supporting TLS 1.0, so we enable all TLS versions globally ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls; - if (!Util.IsLinux()) + if (!Util.IsWine()) InitSymbolHandler(info); - var dalamud = new Dalamud(info, configuration, mainThreadContinueEvent); + var dalamud = new Dalamud(info, fs, configuration, mainThreadContinueEvent); Log.Information("This is Dalamud - Core: {GitHash}, CS: {CsGitHash} [{CsVersion}]", Util.GetGitHash(), Util.GetGitHashClientStructs(), FFXIVClientStructs.Interop.Resolver.Version); dalamud.WaitForUnload(); diff --git a/Dalamud/Game/AddonEventManager/AddonCursorType.cs b/Dalamud/Game/Addon/Events/AddonCursorType.cs similarity index 97% rename from Dalamud/Game/AddonEventManager/AddonCursorType.cs rename to Dalamud/Game/Addon/Events/AddonCursorType.cs index 57d58c60c..83a81582c 100644 --- a/Dalamud/Game/AddonEventManager/AddonCursorType.cs +++ b/Dalamud/Game/Addon/Events/AddonCursorType.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Reimplementation of CursorType. diff --git a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs b/Dalamud/Game/Addon/Events/AddonEventEntry.cs similarity index 96% rename from Dalamud/Game/AddonEventManager/AddonEventEntry.cs rename to Dalamud/Game/Addon/Events/AddonEventEntry.cs index 48c3feb24..a7430acf0 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs +++ b/Dalamud/Game/Addon/Events/AddonEventEntry.cs @@ -1,10 +1,8 @@ -using System; - -using Dalamud.Memory; +using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// This class represents a registered event that a plugin registers with a native ui node. diff --git a/Dalamud/Game/AddonEventManager/AddonEventHandle.cs b/Dalamud/Game/Addon/Events/AddonEventHandle.cs similarity index 90% rename from Dalamud/Game/AddonEventManager/AddonEventHandle.cs rename to Dalamud/Game/Addon/Events/AddonEventHandle.cs index 48abba9a0..fb0e2886c 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventHandle.cs +++ b/Dalamud/Game/Addon/Events/AddonEventHandle.cs @@ -1,6 +1,4 @@ -using System; - -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Class that represents a addon event handle. diff --git a/Dalamud/Game/AddonEventManager/AddonEventListener.cs b/Dalamud/Game/Addon/Events/AddonEventListener.cs similarity index 98% rename from Dalamud/Game/AddonEventManager/AddonEventListener.cs rename to Dalamud/Game/Addon/Events/AddonEventListener.cs index 6f7c55c4c..ceac38108 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventListener.cs +++ b/Dalamud/Game/Addon/Events/AddonEventListener.cs @@ -1,9 +1,8 @@ -using System; using System.Runtime.InteropServices; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Event listener class for managing custom events. diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/Addon/Events/AddonEventManager.cs similarity index 97% rename from Dalamud/Game/AddonEventManager/AddonEventManager.cs rename to Dalamud/Game/Addon/Events/AddonEventManager.cs index dfc037e23..a91f5437c 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs +++ b/Dalamud/Game/Addon/Events/AddonEventManager.cs @@ -1,7 +1,8 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -11,7 +12,7 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Service provider for addon event management. @@ -40,7 +41,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType private AddonCursorType? cursorOverride; [ServiceManager.ServiceConstructor] - private AddonEventManager(SigScanner sigScanner) + private AddonEventManager(TargetSigScanner sigScanner) { this.address = new AddonEventManagerAddressResolver(); this.address.Setup(sigScanner); diff --git a/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs similarity index 94% rename from Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs rename to Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs index 71a6093bb..1405446fe 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs +++ b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// AddonEventManager memory address resolver. diff --git a/Dalamud/Game/AddonEventManager/AddonEventType.cs b/Dalamud/Game/Addon/Events/AddonEventType.cs similarity index 98% rename from Dalamud/Game/AddonEventManager/AddonEventType.cs rename to Dalamud/Game/Addon/Events/AddonEventType.cs index 74f35c257..2c6c96334 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventType.cs +++ b/Dalamud/Game/Addon/Events/AddonEventType.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Reimplementation of AtkEventType. diff --git a/Dalamud/Game/AddonEventManager/IAddonEventHandle.cs b/Dalamud/Game/Addon/Events/IAddonEventHandle.cs similarity index 93% rename from Dalamud/Game/AddonEventManager/IAddonEventHandle.cs rename to Dalamud/Game/Addon/Events/IAddonEventHandle.cs index 3b2c5c3ae..f9272c92a 100644 --- a/Dalamud/Game/AddonEventManager/IAddonEventHandle.cs +++ b/Dalamud/Game/Addon/Events/IAddonEventHandle.cs @@ -1,6 +1,4 @@ -using System; - -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Interface representing the data used for managing AddonEvents. diff --git a/Dalamud/Game/AddonEventManager/PluginEventController.cs b/Dalamud/Game/Addon/Events/PluginEventController.cs similarity index 98% rename from Dalamud/Game/AddonEventManager/PluginEventController.cs rename to Dalamud/Game/Addon/Events/PluginEventController.cs index b66bbc99e..7847dd482 100644 --- a/Dalamud/Game/AddonEventManager/PluginEventController.cs +++ b/Dalamud/Game/Addon/Events/PluginEventController.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Dalamud.Game.Gui; @@ -8,7 +7,7 @@ using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Events; /// /// Class to manage creating and cleaning up events per-plugin. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs similarity index 96% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs index 949d3fde9..334542c71 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs @@ -1,7 +1,7 @@ using Dalamud.Memory; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Base class for AddonLifecycle AddonArgTypes. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs similarity index 77% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs index d93001d1c..6bb72f567 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Finalize events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs similarity index 79% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs index ed7aa1b3c..782943955 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Finalize events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs similarity index 92% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs index 60ccaf8ea..a50dc68f6 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs @@ -1,7 +1,6 @@ -using System; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Finalize events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs similarity index 89% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs index a31369aaf..e73d11e23 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Finalize events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs similarity index 86% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs index 17c87967a..df2ec26be 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs @@ -1,8 +1,6 @@ -using System; +using FFXIVClientStructs.FFXIV.Component.GUI; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Setup events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs similarity index 86% rename from Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs index 993883d77..6870746db 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Finalize events. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgsType.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs similarity index 94% rename from Dalamud/Game/AddonLifecycle/AddonArgsType.cs rename to Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs index 8a07d445b..11f73a4de 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgsType.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle; /// /// Enumeration for available AddonLifecycle arg data. diff --git a/Dalamud/Game/AddonLifecycle/AddonEvent.cs b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs similarity index 97% rename from Dalamud/Game/AddonLifecycle/AddonEvent.cs rename to Dalamud/Game/Addon/Lifecycle/AddonEvent.cs index cfc83fb8a..75a77482d 100644 --- a/Dalamud/Game/AddonLifecycle/AddonEvent.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle; /// /// Enumeration for available AddonLifecycle events. diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs similarity index 98% rename from Dalamud/Game/AddonLifecycle/AddonLifecycle.cs rename to Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs index 17afbaeac..d4e45688d 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs @@ -1,8 +1,8 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Hooking; using Dalamud.Hooking.Internal; using Dalamud.IoC; @@ -11,7 +11,7 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle; /// /// This class provides events for in-game addon lifecycles. @@ -38,7 +38,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType private readonly List eventListeners = new(); [ServiceManager.ServiceConstructor] - private AddonLifecycle(SigScanner sigScanner) + private AddonLifecycle(TargetSigScanner sigScanner) { this.address = new AddonLifecycleAddressResolver(); this.address.Setup(sigScanner); @@ -97,7 +97,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType } // Used to prevent concurrency issues if plugins try to register during iteration of listeners. - private void OnFrameworkUpdate(Framework unused) + private void OnFrameworkUpdate(IFramework unused) { if (this.newEventListeners.Any()) { diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs similarity index 97% rename from Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs rename to Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs index 16fd54832..7690db50d 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle; /// /// AddonLifecycleService memory address resolver. diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs similarity index 97% rename from Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs rename to Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs index 12ccf5e8f..6464a1edd 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs @@ -1,6 +1,6 @@ using Dalamud.Plugin.Services; -namespace Dalamud.Game.Addon; +namespace Dalamud.Game.Addon.Lifecycle; /// /// This class is a helper for tracking and invoking listener delegates. diff --git a/Dalamud/Game/BaseAddressResolver.cs b/Dalamud/Game/BaseAddressResolver.cs index 24e7dffe8..cd1ef8fd2 100644 --- a/Dalamud/Game/BaseAddressResolver.cs +++ b/Dalamud/Game/BaseAddressResolver.cs @@ -10,7 +10,7 @@ namespace Dalamud.Game; /// /// Base memory address resolver. /// -public abstract class BaseAddressResolver +internal abstract class BaseAddressResolver { /// /// Gets a list of memory addresses that were found, to list in /xldata. @@ -22,15 +22,6 @@ public abstract class BaseAddressResolver /// protected bool IsResolved { get; set; } - /// - /// Setup the resolver, calling the appropriate method based on the process architecture, - /// using the default SigScanner. - /// - /// For plugins. Not intended to be called from Dalamud Service{T} constructors. - /// - [UsedImplicitly] - public void Setup() => this.Setup(Service.Get()); - /// /// Setup the resolver, calling the appropriate method based on the process architecture. /// diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index ed69b7bbe..d2f4b30c7 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -14,8 +13,6 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Windows; -using Dalamud.IoC; -using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal; using Dalamud.Utility; using Serilog; @@ -25,10 +22,8 @@ namespace Dalamud.Game; /// /// Chat events and public helper functions. /// -[PluginInterface] -[InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public class ChatHandlers : IServiceType +internal class ChatHandlers : IServiceType { // private static readonly Dictionary UnicodeToDiscordEmojiDict = new() // { @@ -106,6 +101,9 @@ public class ChatHandlers : IServiceType private readonly DalamudLinkPayload openInstallerWindowLink; + [ServiceManager.ServiceDependency] + private readonly Dalamud dalamud = Service.Get(); + [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -134,22 +132,6 @@ public class ChatHandlers : IServiceType /// public bool IsAutoUpdateComplete { get; private set; } - /// - /// Convert a TextPayload to SeString and wrap in italics payloads. - /// - /// Text to convert. - /// SeString payload of italicized text. - public static SeString MakeItalics(string text) - => MakeItalics(new TextPayload(text)); - - /// - /// Convert a TextPayload to SeString and wrap in italics payloads. - /// - /// Text to convert. - /// SeString payload of italicized text. - public static SeString MakeItalics(TextPayload text) - => new(EmphasisItalicPayload.ItalicsOn, text, EmphasisItalicPayload.ItalicsOff); - private void OnCheckMessageHandled(XivChatType type, uint senderid, ref SeString sender, ref SeString message, ref bool isHandled) { var textVal = message.TextValue; @@ -178,7 +160,6 @@ public class ChatHandlers : IServiceType private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) { - var startInfo = Service.Get(); var clientState = Service.GetNullable(); if (clientState == null) return; @@ -200,7 +181,7 @@ public class ChatHandlers : IServiceType if (type == XivChatType.RetainerSale) { - foreach (var regex in this.retainerSaleRegexes[startInfo.Language]) + foreach (var regex in this.retainerSaleRegexes[(ClientLanguage)this.dalamud.StartInfo.Language]) { var matchInfo = regex.Match(message.TextValue); @@ -264,7 +245,7 @@ public class ChatHandlers : IServiceType if (string.IsNullOrEmpty(this.configuration.LastVersion) || !assemblyVersion.StartsWith(this.configuration.LastVersion)) { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = Loc.Localize("DalamudUpdated", "Dalamud has been updated successfully! Please check the discord for a full changelog."), Type = XivChatType.Notice, @@ -321,7 +302,7 @@ public class ChatHandlers : IServiceType } else { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = new SeString(new List() { diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index 17b468d70..e6af6e1df 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -18,7 +18,7 @@ namespace Dalamud.Game.ClientState.Aetherytes; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList +internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList { [ServiceManager.ServiceDependency] private readonly ClientState clientState = Service.Get(); @@ -78,7 +78,7 @@ public sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList /// /// This collection represents the list of available Aetherytes in the Teleport window. /// -public sealed partial class AetheryteList +internal sealed partial class AetheryteList { /// public int Count => this.Length; diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index dc2cb9fae..5d0098187 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -20,7 +20,7 @@ namespace Dalamud.Game.ClientState.Buddy; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed partial class BuddyList : IServiceType, IBuddyList +internal sealed partial class BuddyList : IServiceType, IBuddyList { private const uint InvalidObjectID = 0xE0000000; @@ -55,18 +55,6 @@ public sealed partial class BuddyList : IServiceType, IBuddyList } } - /// - /// Gets a value indicating whether the local player's companion is present. - /// - [Obsolete("Use CompanionBuddy != null", false)] - public bool CompanionBuddyPresent => this.CompanionBuddy != null; - - /// - /// Gets a value indicating whether the local player's pet is present. - /// - [Obsolete("Use PetBuddy != null", false)] - public bool PetBuddyPresent => this.PetBuddy != null; - /// public BuddyMember? CompanionBuddy { @@ -147,7 +135,7 @@ public sealed partial class BuddyList : IServiceType, IBuddyList /// /// This collection represents the buddies present in your squadron or trust party. /// -public sealed partial class BuddyList +internal sealed partial class BuddyList { /// int IReadOnlyCollection.Count => this.Length; diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index fed0ec3c4..3b3f65128 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Dalamud.Data; @@ -9,24 +8,25 @@ using Dalamud.Game.Network.Internal; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game; -using Serilog; +using Lumina.Excel.GeneratedSheets; + +using Action = System.Action; namespace Dalamud.Game.ClientState; /// /// This class represents the state of the game client at the time of access. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public sealed class ClientState : IDisposable, IServiceType, IClientState +internal sealed class ClientState : IDisposable, IServiceType, IClientState { + private static readonly ModuleLog Log = new("ClientState"); + private readonly GameLifecycle lifecycle; private readonly ClientStateAddressResolver address; private readonly Hook setupTerritoryTypeHook; @@ -38,10 +38,10 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState private readonly NetworkHandlers networkHandlers = Service.Get(); private bool lastConditionNone = true; - private bool lastFramePvP = false; + private bool lastFramePvP; [ServiceManager.ServiceConstructor] - private ClientState(SigScanner sigScanner, DalamudStartInfo startInfo, GameLifecycle lifecycle) + private ClientState(TargetSigScanner sigScanner, Dalamud dalamud, GameLifecycle lifecycle) { this.lifecycle = lifecycle; this.address = new ClientStateAddressResolver(); @@ -49,7 +49,7 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState Log.Verbose("===== C L I E N T S T A T E ====="); - this.ClientLanguage = startInfo.Language; + this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language; Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}"); @@ -64,22 +64,22 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState private delegate IntPtr SetupTerritoryTypeDelegate(IntPtr manager, ushort terriType); /// - public event EventHandler TerritoryChanged; + public event Action? TerritoryChanged; /// - public event EventHandler Login; + public event Action? Login; /// - public event EventHandler Logout; + public event Action? Logout; /// - public event Action EnterPvP; + public event Action? EnterPvP; /// - public event Action LeavePvP; + public event Action? LeavePvP; /// - public event EventHandler CfPop; + public event Action? CfPop; /// public ClientLanguage ClientLanguage { get; } @@ -102,6 +102,9 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState /// public bool IsPvPExcludingDen { get; private set; } + /// + public bool IsGPosing => GameMain.IsInGPose(); + /// /// Gets client state address resolver. /// @@ -126,19 +129,19 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType) { this.TerritoryType = terriType; - this.TerritoryChanged?.InvokeSafely(this, terriType); + this.TerritoryChanged?.InvokeSafely(terriType); Log.Debug("TerritoryType changed: {0}", terriType); return this.setupTerritoryTypeHook.Original(manager, terriType); } - private void NetworkHandlersOnCfPop(object sender, Lumina.Excel.GeneratedSheets.ContentFinderCondition e) + private void NetworkHandlersOnCfPop(ContentFinderCondition e) { - this.CfPop?.InvokeSafely(this, e); + this.CfPop?.InvokeSafely(e); } - private void FrameworkOnOnUpdateEvent(Framework framework1) + private void FrameworkOnOnUpdateEvent(IFramework framework1) { var condition = Service.GetNullable(); var gameGui = Service.GetNullable(); @@ -147,12 +150,12 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState if (condition == null || gameGui == null || data == null) return; - if (condition.Any() && this.lastConditionNone == true && this.LocalPlayer != null) + if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null) { Log.Debug("Is login"); this.lastConditionNone = false; this.IsLoggedIn = true; - this.Login?.InvokeSafely(this, null); + this.Login?.InvokeSafely(); gameGui.ResetUiHideState(); this.lifecycle.ResetLogout(); @@ -163,7 +166,7 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState Log.Debug("Is logout"); this.lastConditionNone = true; this.IsLoggedIn = false; - this.Logout?.InvokeSafely(this, null); + this.Logout?.InvokeSafely(); gameGui.ResetUiHideState(); this.lifecycle.SetLogout(); @@ -187,3 +190,103 @@ public sealed class ClientState : IDisposable, IServiceType, IClientState } } } + +/// +/// Plugin-scoped version of a GameConfig service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class ClientStatePluginScoped : IDisposable, IServiceType, IClientState +{ + [ServiceManager.ServiceDependency] + private readonly ClientState clientStateService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal ClientStatePluginScoped() + { + this.clientStateService.TerritoryChanged += this.TerritoryChangedForward; + this.clientStateService.Login += this.LoginForward; + this.clientStateService.Logout += this.LogoutForward; + this.clientStateService.EnterPvP += this.EnterPvPForward; + this.clientStateService.LeavePvP += this.ExitPvPForward; + this.clientStateService.CfPop += this.ContentFinderPopForward; + } + + /// + public event Action? TerritoryChanged; + + /// + public event Action? Login; + + /// + public event Action? Logout; + + /// + public event Action? EnterPvP; + + /// + public event Action? LeavePvP; + + /// + public event Action? CfPop; + + /// + public ClientLanguage ClientLanguage => this.clientStateService.ClientLanguage; + + /// + public ushort TerritoryType => this.clientStateService.TerritoryType; + + /// + public PlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer; + + /// + public ulong LocalContentId => this.clientStateService.LocalContentId; + + /// + public bool IsLoggedIn => this.clientStateService.IsLoggedIn; + + /// + public bool IsPvP => this.clientStateService.IsPvP; + + /// + public bool IsPvPExcludingDen => this.clientStateService.IsPvPExcludingDen; + + /// + public bool IsGPosing => this.clientStateService.IsGPosing; + + /// + public void Dispose() + { + this.clientStateService.TerritoryChanged -= this.TerritoryChangedForward; + this.clientStateService.Login -= this.LoginForward; + this.clientStateService.Logout -= this.LogoutForward; + this.clientStateService.EnterPvP -= this.EnterPvPForward; + this.clientStateService.LeavePvP -= this.ExitPvPForward; + this.clientStateService.CfPop -= this.ContentFinderPopForward; + + this.TerritoryChanged = null; + this.Login = null; + this.Logout = null; + this.EnterPvP = null; + this.LeavePvP = null; + this.CfPop = null; + } + + private void TerritoryChangedForward(ushort territoryId) => this.TerritoryChanged?.Invoke(territoryId); + + private void LoginForward() => this.Login?.Invoke(); + + private void LogoutForward() => this.Logout?.Invoke(); + + private void EnterPvPForward() => this.EnterPvP?.Invoke(); + + private void ExitPvPForward() => this.LeavePvP?.Invoke(); + + private void ContentFinderPopForward(ContentFinderCondition cfc) => this.CfPop?.Invoke(cfc); +} diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 369e620be..305dda454 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -5,7 +5,7 @@ namespace Dalamud.Game.ClientState; /// /// Client state memory address resolver. /// -public sealed class ClientStateAddressResolver : BaseAddressResolver +internal sealed class ClientStateAddressResolver : BaseAddressResolver { // Static offsets diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs index f611a01c6..2db47ea4d 100644 --- a/Dalamud/Game/ClientState/Conditions/Condition.cs +++ b/Dalamud/Game/ClientState/Conditions/Condition.cs @@ -1,7 +1,6 @@ -using System; - using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Serilog; namespace Dalamud.Game.ClientState.Conditions; @@ -9,16 +8,15 @@ namespace Dalamud.Game.ClientState.Conditions; /// /// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed partial class Condition : IServiceType +internal sealed partial class Condition : IServiceType, ICondition { /// - /// The current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has. + /// Gets the current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has. /// - public const int MaxConditionEntries = 104; - + internal const int MaxConditionEntries = 104; + private readonly bool[] cache = new bool[MaxConditionEntries]; [ServiceManager.ServiceConstructor] @@ -27,29 +25,17 @@ public sealed partial class Condition : IServiceType var resolver = clientState.AddressResolver; this.Address = resolver.ConditionFlags; } + + /// + public event ICondition.ConditionChangeDelegate? ConditionChange; - /// - /// A delegate type used with the event. - /// - /// The changed condition. - /// The value the condition is set to. - public delegate void ConditionChangeDelegate(ConditionFlag flag, bool value); + /// + public int MaxEntries => MaxConditionEntries; - /// - /// Event that gets fired when a condition is set. - /// Should only get fired for actual changes, so the previous value will always be !value. - /// - public event ConditionChangeDelegate? ConditionChange; - - /// - /// Gets the condition array base pointer. - /// + /// public IntPtr Address { get; private set; } - /// - /// Check the value of a specific condition/state flag. - /// - /// The condition flag to check. + /// public unsafe bool this[int flag] { get @@ -61,14 +47,11 @@ public sealed partial class Condition : IServiceType } } - /// - public unsafe bool this[ConditionFlag flag] + /// + public bool this[ConditionFlag flag] => this[(int)flag]; - /// - /// Check if any condition flags are set. - /// - /// Whether any single flag is set. + /// public bool Any() { for (var i = 0; i < MaxConditionEntries; i++) @@ -81,6 +64,21 @@ public sealed partial class Condition : IServiceType return false; } + + /// + public bool Any(params ConditionFlag[] flags) + { + foreach (var flag in flags) + { + // this[i] performs range checking, so no need to check here + if (this[flag]) + { + return true; + } + } + + return false; + } [ServiceManager.CallWhenServicesReady] private void ContinueConstruction(Framework framework) @@ -92,7 +90,7 @@ public sealed partial class Condition : IServiceType framework.Update += this.FrameworkUpdate; } - private void FrameworkUpdate(Framework framework) + private void FrameworkUpdate(IFramework framework) { for (var i = 0; i < MaxConditionEntries; i++) { @@ -118,7 +116,7 @@ public sealed partial class Condition : IServiceType /// /// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. /// -public sealed partial class Condition : IDisposable +internal sealed partial class Condition : IDisposable { private bool isDisposed; @@ -152,3 +150,54 @@ public sealed partial class Condition : IDisposable this.isDisposed = true; } } + +/// +/// Plugin-scoped version of a Condition service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class ConditionPluginScoped : IDisposable, IServiceType, ICondition +{ + [ServiceManager.ServiceDependency] + private readonly Condition conditionService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal ConditionPluginScoped() + { + this.conditionService.ConditionChange += this.ConditionChangedForward; + } + + /// + public event ICondition.ConditionChangeDelegate? ConditionChange; + + /// + public int MaxEntries => this.conditionService.MaxEntries; + + /// + public IntPtr Address => this.conditionService.Address; + + /// + public bool this[int flag] => this.conditionService[flag]; + + /// + public void Dispose() + { + this.conditionService.ConditionChange -= this.ConditionChangedForward; + + this.ConditionChange = null; + } + + /// + public bool Any() => this.conditionService.Any(); + + /// + public bool Any(params ConditionFlag[] flags) => this.conditionService.Any(flags); + + private void ConditionChangedForward(ConditionFlag flag, bool value) => this.ConditionChange?.Invoke(flag, value); +} diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 53196d5df..e9400842f 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -18,7 +18,7 @@ namespace Dalamud.Game.ClientState.Fates; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed partial class FateTable : IServiceType, IFateTable +internal sealed partial class FateTable : IServiceType, IFateTable { private readonly ClientStateAddressResolver address; @@ -110,7 +110,7 @@ public sealed partial class FateTable : IServiceType, IFateTable /// /// This collection represents the currently available Fate events. /// -public sealed partial class FateTable +internal sealed partial class FateTable { /// int IReadOnlyCollection.Count => this.Length; diff --git a/Dalamud/Game/ClientState/GamePad/GamepadState.cs b/Dalamud/Game/ClientState/GamePad/GamepadState.cs index bc5744047..b03db6df2 100644 --- a/Dalamud/Game/ClientState/GamePad/GamepadState.cs +++ b/Dalamud/Game/ClientState/GamePad/GamepadState.cs @@ -21,7 +21,7 @@ namespace Dalamud.Game.ClientState.GamePad; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public unsafe class GamepadState : IDisposable, IServiceType, IGamepadState +internal unsafe class GamepadState : IDisposable, IServiceType, IGamepadState { private readonly Hook? gamepadPoll; @@ -55,54 +55,6 @@ public unsafe class GamepadState : IDisposable, IServiceType, IGamepadState public Vector2 RightStick => new(this.rightStickX, this.rightStickY); - /// - /// Gets the state of the left analogue stick in the left direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.LeftStick.X", false)] - public float LeftStickLeft => this.leftStickX < 0 ? -this.leftStickX / 100f : 0; - - /// - /// Gets the state of the left analogue stick in the right direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.LeftStick.X", false)] - public float LeftStickRight => this.leftStickX > 0 ? this.leftStickX / 100f : 0; - - /// - /// Gets the state of the left analogue stick in the up direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.LeftStick.Y", false)] - public float LeftStickUp => this.leftStickY > 0 ? this.leftStickY / 100f : 0; - - /// - /// Gets the state of the left analogue stick in the down direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.LeftStick.Y", false)] - public float LeftStickDown => this.leftStickY < 0 ? -this.leftStickY / 100f : 0; - - /// - /// Gets the state of the right analogue stick in the left direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.RightStick.X", false)] - public float RightStickLeft => this.rightStickX < 0 ? -this.rightStickX / 100f : 0; - - /// - /// Gets the state of the right analogue stick in the right direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.RightStick.X", false)] - public float RightStickRight => this.rightStickX > 0 ? this.rightStickX / 100f : 0; - - /// - /// Gets the state of the right analogue stick in the up direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.RightStick.Y", false)] - public float RightStickUp => this.rightStickY > 0 ? this.rightStickY / 100f : 0; - - /// - /// Gets the state of the right analogue stick in the down direction between 0 (not tilted) and 1 (max tilt). - /// - [Obsolete("Use IGamepadState.RightStick.Y", false)] - public float RightStickDown => this.rightStickY < 0 ? -this.rightStickY / 100f : 0; - /// /// Gets buttons pressed bitmask, set once when the button is pressed. See for the mapping. /// diff --git a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs index 683f5c61f..74e22ddbe 100644 --- a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs +++ b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs @@ -19,7 +19,7 @@ namespace Dalamud.Game.ClientState.JobGauge; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public class JobGauges : IServiceType, IJobGauges +internal class JobGauges : IServiceType, IJobGauges { private Dictionary cache = new(); diff --git a/Dalamud/Game/ClientState/Keys/KeyState.cs b/Dalamud/Game/ClientState/Keys/KeyState.cs index 685973e17..76bee51bf 100644 --- a/Dalamud/Game/ClientState/Keys/KeyState.cs +++ b/Dalamud/Game/ClientState/Keys/KeyState.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Serilog; namespace Dalamud.Game.ClientState.Keys; @@ -23,7 +25,10 @@ namespace Dalamud.Game.ClientState.Keys; [PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public class KeyState : IServiceType +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class KeyState : IServiceType, IKeyState { // The array is accessed in a way that this limit doesn't appear to exist // but there is other state data past this point, and keys beyond here aren't @@ -31,10 +36,10 @@ public class KeyState : IServiceType private const int MaxKeyCode = 0xF0; private readonly IntPtr bufferBase; private readonly IntPtr indexBase; - private VirtualKey[] validVirtualKeyCache = null; + private VirtualKey[]? validVirtualKeyCache; [ServiceManager.ServiceConstructor] - private KeyState(SigScanner sigScanner, ClientState clientState) + private KeyState(TargetSigScanner sigScanner, ClientState clientState) { var moduleBaseAddress = sigScanner.Module.BaseAddress; var addressResolver = clientState.AddressResolver; @@ -44,46 +49,29 @@ public class KeyState : IServiceType Log.Verbose($"Keyboard state buffer address 0x{this.bufferBase.ToInt64():X}"); } - /// - /// Get or set the key-pressed state for a given vkCode. - /// - /// The virtual key to change. - /// Whether the specified key is currently pressed. - /// If the vkCode is not valid. Refer to or . - /// If the set value is non-zero. - public unsafe bool this[int vkCode] + /// + public bool this[int vkCode] { get => this.GetRawValue(vkCode) != 0; set => this.SetRawValue(vkCode, value ? 1 : 0); } - /// + /// public bool this[VirtualKey vkCode] { get => this[(int)vkCode]; set => this[(int)vkCode] = value; } - /// - /// Gets the value in the index array. - /// - /// The virtual key to change. - /// The raw value stored in the index array. - /// If the vkCode is not valid. Refer to or . + /// public int GetRawValue(int vkCode) => this.GetRefValue(vkCode); - /// + /// public int GetRawValue(VirtualKey vkCode) => this.GetRawValue((int)vkCode); - /// - /// Sets the value in the index array. - /// - /// The virtual key to change. - /// The raw value to set in the index array. - /// If the vkCode is not valid. Refer to or . - /// If the set value is non-zero. + /// public void SetRawValue(int vkCode, int value) { if (value != 0) @@ -92,32 +80,23 @@ public class KeyState : IServiceType this.GetRefValue(vkCode) = value; } - /// + /// public void SetRawValue(VirtualKey vkCode, int value) => this.SetRawValue((int)vkCode, value); - /// - /// Gets a value indicating whether the given VirtualKey code is regarded as valid input by the game. - /// - /// Virtual key code. - /// If the code is valid. + /// public bool IsVirtualKeyValid(int vkCode) => this.ConvertVirtualKey(vkCode) != 0; - /// + /// public bool IsVirtualKeyValid(VirtualKey vkCode) => this.IsVirtualKeyValid((int)vkCode); - /// - /// Gets an array of virtual keys the game considers valid input. - /// - /// An array of valid virtual keys. - public VirtualKey[] GetValidVirtualKeys() - => this.validVirtualKeyCache ??= Enum.GetValues().Where(vk => this.IsVirtualKeyValid(vk)).ToArray(); + /// + public IEnumerable GetValidVirtualKeys() + => this.validVirtualKeyCache ??= Enum.GetValues().Where(this.IsVirtualKeyValid).ToArray(); - /// - /// Clears the pressed state for all keys. - /// + /// public void ClearAll() { foreach (var vk in this.GetValidVirtualKeys()) diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 16cf7c277..c6320ccbb 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -21,7 +21,7 @@ namespace Dalamud.Game.ClientState.Objects; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed partial class ObjectTable : IServiceType, IObjectTable +internal sealed partial class ObjectTable : IServiceType, IObjectTable { private const int ObjectTableLength = 596; @@ -109,7 +109,7 @@ public sealed partial class ObjectTable : IServiceType, IObjectTable /// /// This collection represents the currently spawned FFXIV game objects. /// -public sealed partial class ObjectTable +internal sealed partial class ObjectTable { /// int IReadOnlyCollection.Count => this.Length; diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs index 59f32e33d..add7a7f9f 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs @@ -1,5 +1,3 @@ -using System; - using Dalamud.Game.ClientState.Objects.Enums; namespace Dalamud.Game.ClientState.Objects.Types; @@ -25,5 +23,5 @@ public unsafe class BattleNpc : BattleChara public BattleNpcSubKind BattleNpcKind => (BattleNpcSubKind)this.Struct->Character.GameObject.SubKind; /// - public override ulong TargetObjectId => this.Struct->Character.TargetObjectID; + public override ulong TargetObjectId => this.Struct->Character.TargetId; } diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs index 7fc9c0079..9de11e3ec 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs @@ -1,5 +1,3 @@ -using System; - using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Resolvers; @@ -33,5 +31,5 @@ public unsafe class PlayerCharacter : BattleChara /// /// Gets the target actor ID of the PlayerCharacter. /// - public override ulong TargetObjectId => this.Struct->Character.PlayerTargetObjectID; + public override ulong TargetObjectId => this.Struct->Character.LookTargetId; } diff --git a/Dalamud/Game/ClientState/Objects/TargetManager.cs b/Dalamud/Game/ClientState/Objects/TargetManager.cs index 00bcaac7d..fcb242c1e 100644 --- a/Dalamud/Game/ClientState/Objects/TargetManager.cs +++ b/Dalamud/Game/ClientState/Objects/TargetManager.cs @@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.Objects; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed unsafe class TargetManager : IServiceType, ITargetManager +internal sealed unsafe class TargetManager : IServiceType, ITargetManager { [ServiceManager.ServiceDependency] private readonly ClientState clientState = Service.Get(); @@ -39,35 +39,35 @@ public sealed unsafe class TargetManager : IServiceType, ITargetManager public GameObject? Target { get => this.objectTable.CreateObjectReference((IntPtr)Struct->Target); - set => this.SetTarget(value); + set => Struct->Target = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public GameObject? MouseOverTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->MouseOverTarget); - set => this.SetMouseOverTarget(value); + set => Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public GameObject? FocusTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->FocusTarget); - set => this.SetFocusTarget(value); + set => Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public GameObject? PreviousTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->PreviousTarget); - set => this.SetPreviousTarget(value); + set => Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public GameObject? SoftTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->SoftTarget); - set => this.SetSoftTarget(value); + set => Struct->SoftTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// @@ -85,104 +85,4 @@ public sealed unsafe class TargetManager : IServiceType, ITargetManager } private FFXIVClientStructs.FFXIV.Client.Game.Control.TargetSystem* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Control.TargetSystem*)this.Address; - - /// - /// Sets the current target. - /// - /// Actor to target. - [Obsolete("Use Target Property", false)] - public void SetTarget(GameObject? actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); - - /// - /// Sets the mouseover target. - /// - /// Actor to target. - [Obsolete("Use MouseOverTarget Property", false)] - public void SetMouseOverTarget(GameObject? actor) => this.SetMouseOverTarget(actor?.Address ?? IntPtr.Zero); - - /// - /// Sets the focus target. - /// - /// Actor to target. - [Obsolete("Use FocusTarget Property", false)] - public void SetFocusTarget(GameObject? actor) => this.SetFocusTarget(actor?.Address ?? IntPtr.Zero); - - /// - /// Sets the previous target. - /// - /// Actor to target. - [Obsolete("Use PreviousTarget Property", false)] - public void SetPreviousTarget(GameObject? actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); - - /// - /// Sets the soft target. - /// - /// Actor to target. - [Obsolete("Use SoftTarget Property", false)] - public void SetSoftTarget(GameObject? actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); - - /// - /// Sets the current target. - /// - /// Actor (address) to target. - [Obsolete("Use Target Property", false)] - public void SetTarget(IntPtr actorAddress) => Struct->Target = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; - - /// - /// Sets the mouseover target. - /// - /// Actor (address) to target. - [Obsolete("Use MouseOverTarget Property", false)] - public void SetMouseOverTarget(IntPtr actorAddress) => Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; - - /// - /// Sets the focus target. - /// - /// Actor (address) to target. - [Obsolete("Use FocusTarget Property", false)] - public void SetFocusTarget(IntPtr actorAddress) => Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; - - /// - /// Sets the previous target. - /// - /// Actor (address) to target. - [Obsolete("Use PreviousTarget Property", false)] - public void SetPreviousTarget(IntPtr actorAddress) => Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; - - /// - /// Sets the soft target. - /// - /// Actor (address) to target. - [Obsolete("Use SoftTarget Property", false)] - public void SetSoftTarget(IntPtr actorAddress) => Struct->SoftTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; - - /// - /// Clears the current target. - /// - [Obsolete("Use Target Property", false)] - public void ClearTarget() => this.SetTarget(IntPtr.Zero); - - /// - /// Clears the mouseover target. - /// - [Obsolete("Use MouseOverTarget Property", false)] - public void ClearMouseOverTarget() => this.SetMouseOverTarget(IntPtr.Zero); - - /// - /// Clears the focus target. - /// - [Obsolete("Use FocusTarget Property", false)] - public void ClearFocusTarget() => this.SetFocusTarget(IntPtr.Zero); - - /// - /// Clears the previous target. - /// - [Obsolete("Use PreviousTarget Property", false)] - public void ClearPreviousTarget() => this.SetPreviousTarget(IntPtr.Zero); - - /// - /// Clears the soft target. - /// - [Obsolete("Use SoftTarget Property", false)] - public void ClearSoftTarget() => this.SetSoftTarget(IntPtr.Zero); } diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs index ee8418362..a1eb52edc 100644 --- a/Dalamud/Game/ClientState/Objects/Types/Character.cs +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -1,5 +1,3 @@ -using System; - using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.Text.SeStringHandling; @@ -87,7 +85,7 @@ public unsafe class Character : GameObject /// /// Gets the target object ID of the character. /// - public override ulong TargetObjectId => this.Struct->TargetObjectID; + public override ulong TargetObjectId => this.Struct->TargetId; /// /// Gets the name ID of the character. @@ -115,5 +113,6 @@ public unsafe class Character : GameObject /// /// Gets the underlying structure. /// - protected internal new FFXIVClientStructs.FFXIV.Client.Game.Character.Character* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)this.Address; + protected internal new FFXIVClientStructs.FFXIV.Client.Game.Character.Character* Struct => + (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)this.Address; } diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index 529b57b6f..946c73245 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -19,7 +19,7 @@ namespace Dalamud.Game.ClientState.Party; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed unsafe partial class PartyList : IServiceType, IPartyList +internal sealed unsafe partial class PartyList : IServiceType, IPartyList { private const int GroupLength = 8; private const int AllianceLength = 20; @@ -130,7 +130,7 @@ public sealed unsafe partial class PartyList : IServiceType, IPartyList /// /// This collection represents the party members present in your party or alliance. /// -public sealed partial class PartyList +internal sealed partial class PartyList { /// int IReadOnlyCollection.Count => this.Length; diff --git a/Dalamud/Game/Command/CommandInfo.cs b/Dalamud/Game/Command/CommandInfo.cs index 9b559599a..bc0250a66 100644 --- a/Dalamud/Game/Command/CommandInfo.cs +++ b/Dalamud/Game/Command/CommandInfo.cs @@ -15,7 +15,6 @@ public sealed class CommandInfo public CommandInfo(HandlerDelegate handler) { this.Handler = handler; - this.LoaderAssemblyName = Assembly.GetCallingAssembly()?.GetName()?.Name; } /// diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs index 63a1a3d09..6b67f1892 100644 --- a/Dalamud/Game/Command/CommandManager.cs +++ b/Dalamud/Game/Command/CommandManager.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -9,22 +8,21 @@ using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; -using Serilog; namespace Dalamud.Game.Command; /// /// This class manages registered in-game slash commands. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public sealed class CommandManager : IServiceType, IDisposable, ICommandManager +internal sealed class CommandManager : IServiceType, IDisposable, ICommandManager { + private static readonly ModuleLog Log = new("Command"); + private readonly ConcurrentDictionary commandMap = new(); private readonly Regex commandRegexEn = new(@"^The command (?.+) does not exist\.$", RegexOptions.Compiled); private readonly Regex commandRegexJp = new(@"^そのコマンドはありません。: (?.+)$", RegexOptions.Compiled); @@ -37,15 +35,15 @@ public sealed class CommandManager : IServiceType, IDisposable, ICommandManager private readonly ChatGui chatGui = Service.Get(); [ServiceManager.ServiceConstructor] - private CommandManager(DalamudStartInfo startInfo) + private CommandManager(Dalamud dalamud) { - this.currentLangCommandRegex = startInfo.Language switch + this.currentLangCommandRegex = (ClientLanguage)dalamud.StartInfo.Language switch { ClientLanguage.Japanese => this.commandRegexJp, ClientLanguage.English => this.commandRegexEn, ClientLanguage.German => this.commandRegexDe, ClientLanguage.French => this.commandRegexFr, - _ => this.currentLangCommandRegex, + _ => this.commandRegexEn, }; this.chatGui.CheckMessageHandled += this.OnCheckMessageHandled; @@ -84,7 +82,7 @@ public sealed class CommandManager : IServiceType, IDisposable, ICommandManager // => command: 0-12 (12 chars) // => argument: 13-17 (4 chars) // => content.IndexOf(' ') == 12 - command = content.Substring(0, separatorPosition); + command = content[..separatorPosition]; var argStart = separatorPosition + 1; argument = content[argStart..]; @@ -162,3 +160,93 @@ public sealed class CommandManager : IServiceType, IDisposable, ICommandManager } } } + +/// +/// Plugin-scoped version of a AddonLifecycle service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class CommandManagerPluginScoped : IDisposable, IServiceType, ICommandManager +{ + private static readonly ModuleLog Log = new("Command"); + + [ServiceManager.ServiceDependency] + private readonly CommandManager commandManagerService = Service.Get(); + + private readonly List pluginRegisteredCommands = new(); + private readonly LocalPlugin pluginInfo; + + /// + /// Initializes a new instance of the class. + /// + /// Info for the plugin that requests this service. + public CommandManagerPluginScoped(LocalPlugin localPlugin) + { + this.pluginInfo = localPlugin; + } + + /// + public ReadOnlyDictionary Commands => this.commandManagerService.Commands; + + /// + public void Dispose() + { + foreach (var command in this.pluginRegisteredCommands) + { + this.commandManagerService.RemoveHandler(command); + } + + this.pluginRegisteredCommands.Clear(); + } + + /// + public bool ProcessCommand(string content) + => this.commandManagerService.ProcessCommand(content); + + /// + public void DispatchCommand(string command, string argument, CommandInfo info) + => this.commandManagerService.DispatchCommand(command, argument, info); + + /// + public bool AddHandler(string command, CommandInfo info) + { + if (!this.pluginRegisteredCommands.Contains(command)) + { + info.LoaderAssemblyName = this.pluginInfo.InternalName; + if (this.commandManagerService.AddHandler(command, info)) + { + this.pluginRegisteredCommands.Add(command); + return true; + } + } + else + { + Log.Error($"Command {command} is already registered."); + } + + return false; + } + + /// + public bool RemoveHandler(string command) + { + if (this.pluginRegisteredCommands.Contains(command)) + { + if (this.commandManagerService.RemoveHandler(command)) + { + this.pluginRegisteredCommands.Remove(command); + return true; + } + } + else + { + Log.Error($"Command {command} not found."); + } + + return false; + } +} diff --git a/Dalamud/Game/Config/GameConfig.cs b/Dalamud/Game/Config/GameConfig.cs index dfdb8b5d2..ae3205abc 100644 --- a/Dalamud/Game/Config/GameConfig.cs +++ b/Dalamud/Game/Config/GameConfig.cs @@ -1,5 +1,4 @@ -using System; -using Dalamud.Hooking; +using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -13,18 +12,14 @@ namespace Dalamud.Game.Config; /// This class represents the game's configuration. /// [InterfaceVersion("1.0")] -[PluginInterface] [ServiceManager.EarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public sealed class GameConfig : IServiceType, IGameConfig, IDisposable +internal sealed class GameConfig : IServiceType, IGameConfig, IDisposable { private readonly GameConfigAddressResolver address = new(); private Hook? configChangeHook; [ServiceManager.ServiceConstructor] - private unsafe GameConfig(Framework framework, SigScanner sigScanner) + private unsafe GameConfig(Framework framework, TargetSigScanner sigScanner) { framework.RunOnTick(() => { @@ -37,15 +32,32 @@ public sealed class GameConfig : IServiceType, IGameConfig, IDisposable this.address.Setup(sigScanner); this.configChangeHook = Hook.FromAddress(this.address.ConfigChangeAddress, this.OnConfigChanged); - this.configChangeHook?.Enable(); + this.configChangeHook.Enable(); }); } private unsafe delegate nint ConfigChangeDelegate(ConfigBase* configBase, ConfigEntry* configEntry); /// - public event EventHandler Changed; + public event EventHandler? Changed; + +#pragma warning disable 67 + /// + /// Unused internally, used as a proxy for System.Changed via GameConfigPluginScoped + /// + public event EventHandler? SystemChanged; + /// + /// Unused internally, used as a proxy for UiConfig.Changed via GameConfigPluginScoped + /// + public event EventHandler? UiConfigChanged; + + /// + /// Unused internally, used as a proxy for UiControl.Changed via GameConfigPluginScoped + /// + public event EventHandler? UiControlChanged; +#pragma warning restore 67 + /// public GameConfigSection System { get; private set; } @@ -193,3 +205,204 @@ public sealed class GameConfig : IServiceType, IGameConfig, IDisposable return returnValue; } } + +/// +/// Plugin-scoped version of a GameConfig service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class GameConfigPluginScoped : IDisposable, IServiceType, IGameConfig +{ + [ServiceManager.ServiceDependency] + private readonly GameConfig gameConfigService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal GameConfigPluginScoped() + { + this.gameConfigService.Changed += this.ConfigChangedForward; + this.gameConfigService.System.Changed += this.SystemConfigChangedForward; + this.gameConfigService.UiConfig.Changed += this.UiConfigConfigChangedForward; + this.gameConfigService.UiControl.Changed += this.UiControlConfigChangedForward; + } + + /// + public event EventHandler? Changed; + + /// + public event EventHandler? SystemChanged; + + /// + public event EventHandler? UiConfigChanged; + + /// + public event EventHandler? UiControlChanged; + + /// + public GameConfigSection System => this.gameConfigService.System; + + /// + public GameConfigSection UiConfig => this.gameConfigService.UiConfig; + + /// + public GameConfigSection UiControl => this.gameConfigService.UiControl; + + /// + public void Dispose() + { + this.gameConfigService.Changed -= this.ConfigChangedForward; + this.gameConfigService.System.Changed -= this.SystemConfigChangedForward; + this.gameConfigService.UiConfig.Changed -= this.UiConfigConfigChangedForward; + this.gameConfigService.UiControl.Changed -= this.UiControlConfigChangedForward; + + this.Changed = null; + this.SystemChanged = null; + this.UiConfigChanged = null; + this.UiControlChanged = null; + } + + /// + public bool TryGet(SystemConfigOption option, out bool value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(SystemConfigOption option, out uint value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(SystemConfigOption option, out float value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(SystemConfigOption option, out string value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(SystemConfigOption option, out UIntConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(SystemConfigOption option, out FloatConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(SystemConfigOption option, out StringConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiConfigOption option, out bool value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiConfigOption option, out uint value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiConfigOption option, out float value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiConfigOption option, out string value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiConfigOption option, out UIntConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiConfigOption option, out FloatConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiConfigOption option, out StringConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiControlOption option, out bool value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiControlOption option, out uint value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiControlOption option, out float value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiControlOption option, out string value) + => this.gameConfigService.TryGet(option, out value); + + /// + public bool TryGet(UiControlOption option, out UIntConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiControlOption option, out FloatConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public bool TryGet(UiControlOption option, out StringConfigProperties? properties) + => this.gameConfigService.TryGet(option, out properties); + + /// + public void Set(SystemConfigOption option, bool value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(SystemConfigOption option, uint value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(SystemConfigOption option, float value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(SystemConfigOption option, string value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiConfigOption option, bool value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiConfigOption option, uint value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiConfigOption option, float value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiConfigOption option, string value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiControlOption option, bool value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiControlOption option, uint value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiControlOption option, float value) + => this.gameConfigService.Set(option, value); + + /// + public void Set(UiControlOption option, string value) + => this.gameConfigService.Set(option, value); + + private void ConfigChangedForward(object sender, ConfigChangeEvent data) => this.Changed?.Invoke(sender, data); + + private void SystemConfigChangedForward(object sender, ConfigChangeEvent data) => this.SystemChanged?.Invoke(sender, data); + + private void UiConfigConfigChangedForward(object sender, ConfigChangeEvent data) => this.UiConfigChanged?.Invoke(sender, data); + + private void UiControlConfigChangedForward(object sender, ConfigChangeEvent data) => this.UiControlChanged?.Invoke(sender, data); +} diff --git a/Dalamud/Game/Config/GameConfigAddressResolver.cs b/Dalamud/Game/Config/GameConfigAddressResolver.cs index 6a207807a..674ee4764 100644 --- a/Dalamud/Game/Config/GameConfigAddressResolver.cs +++ b/Dalamud/Game/Config/GameConfigAddressResolver.cs @@ -3,7 +3,7 @@ /// /// Game config system address resolver. /// -public sealed class GameConfigAddressResolver : BaseAddressResolver +internal sealed class GameConfigAddressResolver : BaseAddressResolver { /// /// Gets the address of the method called when any config option is changed. diff --git a/Dalamud/Game/Config/GameConfigSection.cs b/Dalamud/Game/Config/GameConfigSection.cs index 6c87ad3cf..31e4a0b3f 100644 --- a/Dalamud/Game/Config/GameConfigSection.cs +++ b/Dalamud/Game/Config/GameConfigSection.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Diagnostics; using Dalamud.Memory; @@ -18,11 +17,6 @@ public class GameConfigSection private readonly ConcurrentDictionary indexMap = new(); private readonly ConcurrentDictionary enumMap = new(); - /// - /// Event which is fired when a game config option is changed within the section. - /// - public event EventHandler Changed; - /// /// Initializes a new instance of the class. /// @@ -54,6 +48,11 @@ public class GameConfigSection /// Pointer to unmanaged ConfigBase. internal unsafe delegate ConfigBase* GetConfigBaseDelegate(); + /// + /// Event which is fired when a game config option is changed within the section. + /// + internal event EventHandler? Changed; + /// /// Gets the number of config entries contained within the section. /// Some entries may be empty with no data. diff --git a/Dalamud/Game/DutyState/DutyState.cs b/Dalamud/Game/DutyState/DutyState.cs index 49fc874e3..6dda95a66 100644 --- a/Dalamud/Game/DutyState/DutyState.cs +++ b/Dalamud/Game/DutyState/DutyState.cs @@ -1,25 +1,19 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Conditions; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using Dalamud.Utility; namespace Dalamud.Game.DutyState; /// /// This class represents the state of the currently occupied duty. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.EarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public unsafe class DutyState : IDisposable, IServiceType, IDutyState +internal unsafe class DutyState : IDisposable, IServiceType, IDutyState { private readonly DutyStateAddressResolver address; private readonly Hook contentDirectorNetworkMessageHook; @@ -34,7 +28,7 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState private readonly ClientState.ClientState clientState = Service.Get(); [ServiceManager.ServiceConstructor] - private DutyState(SigScanner sigScanner) + private DutyState(TargetSigScanner sigScanner) { this.address = new DutyStateAddressResolver(); this.address.Setup(sigScanner); @@ -49,16 +43,16 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState private delegate byte SetupContentDirectNetworkMessageDelegate(IntPtr a1, IntPtr a2, ushort* a3); /// - public event EventHandler DutyStarted; + public event EventHandler? DutyStarted; /// - public event EventHandler DutyWiped; + public event EventHandler? DutyWiped; /// - public event EventHandler DutyRecommenced; + public event EventHandler? DutyRecommenced; /// - public event EventHandler DutyCompleted; + public event EventHandler? DutyCompleted; /// public bool IsDutyStarted { get; private set; } @@ -66,7 +60,7 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState private bool CompletedThisTerritory { get; set; } /// - void IDisposable.Dispose() + public void Dispose() { this.contentDirectorNetworkMessageHook.Dispose(); this.framework.Update -= this.FrameworkOnUpdateEvent; @@ -92,33 +86,33 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState // Duty Commenced case 0x4000_0001: this.IsDutyStarted = true; - this.DutyStarted.InvokeSafely(this, this.clientState.TerritoryType); + this.DutyStarted?.Invoke(this, this.clientState.TerritoryType); break; // Party Wipe case 0x4000_0005: this.IsDutyStarted = false; - this.DutyWiped.InvokeSafely(this, this.clientState.TerritoryType); + this.DutyWiped?.Invoke(this, this.clientState.TerritoryType); break; // Duty Recommence case 0x4000_0006: this.IsDutyStarted = true; - this.DutyRecommenced.InvokeSafely(this, this.clientState.TerritoryType); + this.DutyRecommenced?.Invoke(this, this.clientState.TerritoryType); break; // Duty Completed Flytext Shown case 0x4000_0002 when !this.CompletedThisTerritory: this.IsDutyStarted = false; this.CompletedThisTerritory = true; - this.DutyCompleted.InvokeSafely(this, this.clientState.TerritoryType); + this.DutyCompleted?.Invoke(this, this.clientState.TerritoryType); break; // Duty Completed case 0x4000_0003 when !this.CompletedThisTerritory: this.IsDutyStarted = false; this.CompletedThisTerritory = true; - this.DutyCompleted.InvokeSafely(this, this.clientState.TerritoryType); + this.DutyCompleted?.Invoke(this, this.clientState.TerritoryType); break; } } @@ -126,7 +120,7 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState return this.contentDirectorNetworkMessageHook.Original(a1, a2, a3); } - private void TerritoryOnChangedEvent(object? sender, ushort e) + private void TerritoryOnChangedEvent(ushort territoryId) { if (this.IsDutyStarted) { @@ -141,7 +135,7 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState /// Joining a duty in progress, or disconnecting and reconnecting will cause the player to miss the event. /// /// Framework reference. - private void FrameworkOnUpdateEvent(Framework framework1) + private void FrameworkOnUpdateEvent(IFramework framework1) { // If the duty hasn't been started, and has not been completed yet this territory if (!this.IsDutyStarted && !this.CompletedThisTerritory) @@ -161,11 +155,73 @@ public unsafe class DutyState : IDisposable, IServiceType, IDutyState } private bool IsBoundByDuty() + => this.condition.Any(ConditionFlag.BoundByDuty, + ConditionFlag.BoundByDuty56, + ConditionFlag.BoundByDuty95); + + private bool IsInCombat() + => this.condition.Any(ConditionFlag.InCombat); +} + +/// +/// Plugin scoped version of DutyState. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class DutyStatePluginScoped : IDisposable, IServiceType, IDutyState +{ + [ServiceManager.ServiceDependency] + private readonly DutyState dutyStateService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal DutyStatePluginScoped() { - return this.condition[ConditionFlag.BoundByDuty] || - this.condition[ConditionFlag.BoundByDuty56] || - this.condition[ConditionFlag.BoundByDuty95]; + this.dutyStateService.DutyStarted += this.DutyStartedForward; + this.dutyStateService.DutyWiped += this.DutyWipedForward; + this.dutyStateService.DutyRecommenced += this.DutyRecommencedForward; + this.dutyStateService.DutyCompleted += this.DutyCompletedForward; } - private bool IsInCombat() => this.condition[ConditionFlag.InCombat]; + /// + public event EventHandler? DutyStarted; + + /// + public event EventHandler? DutyWiped; + + /// + public event EventHandler? DutyRecommenced; + + /// + public event EventHandler? DutyCompleted; + + /// + public bool IsDutyStarted => this.dutyStateService.IsDutyStarted; + + /// + public void Dispose() + { + this.dutyStateService.DutyStarted -= this.DutyStartedForward; + this.dutyStateService.DutyWiped -= this.DutyWipedForward; + this.dutyStateService.DutyRecommenced -= this.DutyRecommencedForward; + this.dutyStateService.DutyCompleted -= this.DutyCompletedForward; + + this.DutyStarted = null; + this.DutyWiped = null; + this.DutyRecommenced = null; + this.DutyCompleted = null; + } + + private void DutyStartedForward(object sender, ushort territoryId) => this.DutyStarted?.Invoke(sender, territoryId); + + private void DutyWipedForward(object sender, ushort territoryId) => this.DutyWiped?.Invoke(sender, territoryId); + + private void DutyRecommencedForward(object sender, ushort territoryId) => this.DutyRecommenced?.Invoke(sender, territoryId); + + private void DutyCompletedForward(object sender, ushort territoryId) => this.DutyCompleted?.Invoke(sender, territoryId); } diff --git a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs index 801e5ef55..772af79a8 100644 --- a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs +++ b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.DutyState; /// /// Duty state memory address resolver. /// -public class DutyStateAddressResolver : BaseAddressResolver +internal class DutyStateAddressResolver : BaseAddressResolver { /// /// Gets the address of the method which is called when the client receives a content director update. diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs index b3083e913..22343fd8e 100644 --- a/Dalamud/Game/Framework.cs +++ b/Dalamud/Game/Framework.cs @@ -12,6 +12,7 @@ using Dalamud.Game.Gui.Toast; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Dalamud.Utility; using Serilog; @@ -23,7 +24,10 @@ namespace Dalamud.Game; [PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed class Framework : IDisposable, IServiceType +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal sealed class Framework : IDisposable, IServiceType, IFramework { private static readonly Stopwatch StatsStopwatch = new(); @@ -35,6 +39,8 @@ public sealed class Framework : IDisposable, IServiceType private readonly Hook updateHook; private readonly Hook destroyHook; + private readonly FrameworkAddressResolver addressResolver; + [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -45,24 +51,18 @@ public sealed class Framework : IDisposable, IServiceType private Thread? frameworkUpdateThread; [ServiceManager.ServiceConstructor] - private Framework(SigScanner sigScanner, GameLifecycle lifecycle) + private Framework(TargetSigScanner sigScanner, GameLifecycle lifecycle) { this.lifecycle = lifecycle; this.hitchDetector = new HitchDetector("FrameworkUpdate", this.configuration.FrameworkUpdateHitch); - this.Address = new FrameworkAddressResolver(); - this.Address.Setup(sigScanner); + this.addressResolver = new FrameworkAddressResolver(); + this.addressResolver.Setup(sigScanner); - this.updateHook = Hook.FromAddress(this.Address.TickAddress, this.HandleFrameworkUpdate); - this.destroyHook = Hook.FromAddress(this.Address.DestroyAddress, this.HandleFrameworkDestroy); + this.updateHook = Hook.FromAddress(this.addressResolver.TickAddress, this.HandleFrameworkUpdate); + this.destroyHook = Hook.FromAddress(this.addressResolver.DestroyAddress, this.HandleFrameworkDestroy); } - /// - /// A delegate type used with the event. - /// - /// The Framework instance. - public delegate void OnUpdateDelegate(Framework framework); - /// /// A delegate type used during the native Framework::destroy. /// @@ -81,10 +81,8 @@ public sealed class Framework : IDisposable, IServiceType private delegate IntPtr OnDestroyDetour(); // OnDestroyDelegate - /// - /// Event that gets fired every time the game framework updates. - /// - public event OnUpdateDelegate Update; + /// + public event IFramework.OnUpdateDelegate Update; /// /// Gets or sets a value indicating whether the collection of stats is enabled. @@ -96,34 +94,19 @@ public sealed class Framework : IDisposable, IServiceType /// public static Dictionary> StatsHistory { get; } = new(); - /// - /// Gets a raw pointer to the instance of Client::Framework. - /// - public FrameworkAddressResolver Address { get; } - - /// - /// Gets the last time that the Framework Update event was triggered. - /// + /// public DateTime LastUpdate { get; private set; } = DateTime.MinValue; - /// - /// Gets the last time in UTC that the Framework Update event was triggered. - /// + /// public DateTime LastUpdateUTC { get; private set; } = DateTime.MinValue; - /// - /// Gets the delta between the last Framework Update and the currently executing one. - /// + /// public TimeSpan UpdateDelta { get; private set; } = TimeSpan.Zero; - /// - /// Gets a value indicating whether currently executing code is running in the game's framework update thread. - /// + /// public bool IsInFrameworkUpdateThread => Thread.CurrentThread == this.frameworkUpdateThread; - /// - /// Gets a value indicating whether game Framework is unloading. - /// + /// public bool IsFrameworkUnloading { get; internal set; } /// @@ -131,20 +114,11 @@ public sealed class Framework : IDisposable, IServiceType /// internal bool DispatchUpdateEvents { get; set; } = true; - /// - /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. - /// - /// Return type. - /// Function to call. - /// Task representing the pending or already completed function. + /// public Task RunOnFrameworkThread(Func func) => this.IsInFrameworkUpdateThread || this.IsFrameworkUnloading ? Task.FromResult(func()) : this.RunOnTick(func); - /// - /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. - /// - /// Function to call. - /// Task representing the pending or already completed function. + /// public Task RunOnFrameworkThread(Action action) { if (this.IsInFrameworkUpdateThread || this.IsFrameworkUnloading) @@ -165,32 +139,15 @@ public sealed class Framework : IDisposable, IServiceType } } - /// - /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. - /// - /// Return type. - /// Function to call. - /// Task representing the pending or already completed function. + /// public Task RunOnFrameworkThread(Func> func) => this.IsInFrameworkUpdateThread || this.IsFrameworkUnloading ? func() : this.RunOnTick(func); - /// - /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. - /// - /// Function to call. - /// Task representing the pending or already completed function. + /// public Task RunOnFrameworkThread(Func func) => this.IsInFrameworkUpdateThread || this.IsFrameworkUnloading ? func() : this.RunOnTick(func); - /// - /// Run given function in upcoming Framework.Tick call. - /// - /// Return type. - /// Function to call. - /// Wait for given timespan before calling this function. - /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. - /// Cancellation token which will prevent the execution of this function if wait conditions are not met. - /// Task representing the pending function. + /// public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default) { if (this.IsFrameworkUnloading) @@ -219,14 +176,7 @@ public sealed class Framework : IDisposable, IServiceType return tcs.Task; } - /// - /// Run given function in upcoming Framework.Tick call. - /// - /// Function to call. - /// Wait for given timespan before calling this function. - /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. - /// Cancellation token which will prevent the execution of this function if wait conditions are not met. - /// Task representing the pending function. + /// public Task RunOnTick(Action action, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default) { if (this.IsFrameworkUnloading) @@ -255,15 +205,7 @@ public sealed class Framework : IDisposable, IServiceType return tcs.Task; } - /// - /// Run given function in upcoming Framework.Tick call. - /// - /// Return type. - /// Function to call. - /// Wait for given timespan before calling this function. - /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. - /// Cancellation token which will prevent the execution of this function if wait conditions are not met. - /// Task representing the pending function. + /// public Task RunOnTick(Func> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default) { if (this.IsFrameworkUnloading) @@ -292,14 +234,7 @@ public sealed class Framework : IDisposable, IServiceType return tcs.Task.ContinueWith(x => x.Result, cancellationToken).Unwrap(); } - /// - /// Run given function in upcoming Framework.Tick call. - /// - /// Function to call. - /// Wait for given timespan before calling this function. - /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. - /// Cancellation token which will prevent the execution of this function if wait conditions are not met. - /// Task representing the pending function. + /// public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default) { if (this.IsFrameworkUnloading) diff --git a/Dalamud/Game/FrameworkAddressResolver.cs b/Dalamud/Game/FrameworkAddressResolver.cs index e3d128f0f..c47469a01 100644 --- a/Dalamud/Game/FrameworkAddressResolver.cs +++ b/Dalamud/Game/FrameworkAddressResolver.cs @@ -5,14 +5,8 @@ namespace Dalamud.Game; /// /// The address resolver for the class. /// -public sealed unsafe class FrameworkAddressResolver : BaseAddressResolver +internal sealed class FrameworkAddressResolver : BaseAddressResolver { - /// - /// Gets the base address of the Framework object. - /// - [Obsolete("Please use FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance() instead.")] - public IntPtr BaseAddress => new(FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()); - /// /// Gets the address for the function that is called once the Framework is destroyed. /// diff --git a/Dalamud/Game/GameLifecycle.cs b/Dalamud/Game/GameLifecycle.cs index 5c1acc989..4192d055b 100644 --- a/Dalamud/Game/GameLifecycle.cs +++ b/Dalamud/Game/GameLifecycle.cs @@ -15,7 +15,7 @@ namespace Dalamud.Game; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public class GameLifecycle : IServiceType, IGameLifecycle +internal class GameLifecycle : IServiceType, IGameLifecycle { private readonly CancellationTokenSource dalamudUnloadCts = new(); private readonly CancellationTokenSource gameShutdownCts = new(); diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 93185caf9..5bf6232fa 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -11,6 +11,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Dalamud.Utility; using Serilog; @@ -19,10 +20,9 @@ namespace Dalamud.Game.Gui; /// /// This class handles interacting with the native chat UI. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed class ChatGui : IDisposable, IServiceType +internal sealed class ChatGui : IDisposable, IServiceType, IChatGui { private readonly ChatGuiAddressResolver address; @@ -42,7 +42,7 @@ public sealed class ChatGui : IDisposable, IServiceType private IntPtr baseAddress = IntPtr.Zero; [ServiceManager.ServiceConstructor] - private ChatGui(SigScanner sigScanner) + private ChatGui(TargetSigScanner sigScanner) { this.address = new ChatGuiAddressResolver(); this.address.Setup(sigScanner); @@ -51,45 +51,7 @@ public sealed class ChatGui : IDisposable, IServiceType this.populateItemLinkHook = Hook.FromAddress(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour); this.interactableLinkClickedHook = Hook.FromAddress(this.address.InteractableLinkClicked, this.InteractableLinkClickedDetour); } - - /// - /// A delegate type used with the event. - /// - /// The type of chat. - /// The sender ID. - /// The sender name. - /// The message sent. - /// A value indicating whether the message was handled or should be propagated. - public delegate void OnMessageDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled); - - /// - /// A delegate type used with the event. - /// - /// The type of chat. - /// The sender ID. - /// The sender name. - /// The message sent. - /// A value indicating whether the message was handled or should be propagated. - public delegate void OnCheckMessageHandledDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled); - - /// - /// A delegate type used with the event. - /// - /// The type of chat. - /// The sender ID. - /// The sender name. - /// The message sent. - public delegate void OnMessageHandledDelegate(XivChatType type, uint senderId, SeString sender, SeString message); - - /// - /// A delegate type used with the event. - /// - /// The type of chat. - /// The sender ID. - /// The sender name. - /// The message sent. - public delegate void OnMessageUnhandledDelegate(XivChatType type, uint senderId, SeString sender, SeString message); - + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName, IntPtr message, uint senderId, IntPtr parameter); @@ -99,34 +61,22 @@ public sealed class ChatGui : IDisposable, IServiceType [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void InteractableLinkClickedDelegate(IntPtr managerPtr, IntPtr messagePtr); - /// - /// Event that will be fired when a chat message is sent to chat by the game. - /// - public event OnMessageDelegate ChatMessage; + /// + public event IChatGui.OnMessageDelegate? ChatMessage; - /// - /// Event that allows you to stop messages from appearing in chat by setting the isHandled parameter to true. - /// - public event OnCheckMessageHandledDelegate CheckMessageHandled; + /// + public event IChatGui.OnCheckMessageHandledDelegate? CheckMessageHandled; - /// - /// Event that will be fired when a chat message is handled by Dalamud or a Plugin. - /// - public event OnMessageHandledDelegate ChatMessageHandled; + /// + public event IChatGui.OnMessageHandledDelegate? ChatMessageHandled; - /// - /// Event that will be fired when a chat message is not handled by Dalamud or a Plugin. - /// - public event OnMessageUnhandledDelegate ChatMessageUnhandled; + /// + public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; - /// - /// Gets the ID of the last linked item. - /// + /// public int LastLinkedItemId { get; private set; } - /// - /// Gets the flags of the last linked item. - /// + /// public byte LastLinkedItemFlags { get; private set; } /// @@ -139,76 +89,36 @@ public sealed class ChatGui : IDisposable, IServiceType this.interactableLinkClickedHook.Dispose(); } - /// - /// Queue a chat message. While method is named as PrintChat, it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. - /// - /// A message to send. - public void PrintChat(XivChatEntry chat) + /// + public void Print(XivChatEntry chat) { this.chatQueue.Enqueue(chat); } - - /// - /// Queue a chat message. While method is named as PrintChat (it calls it internally), it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. - /// - /// A message to send. - public void Print(string message) + + /// + public void Print(string message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT REGULAR]{0}", message); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = this.configuration.GeneralChatType, - }); + this.PrintTagged(message, this.configuration.GeneralChatType, messageTag, tagColor); } - - /// - /// Queue a chat message. While method is named as PrintChat (it calls it internally), it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. - /// - /// A message to send. - public void Print(SeString message) + + /// + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT SESTRING]{0}", message.TextValue); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = this.configuration.GeneralChatType, - }); + this.PrintTagged(message, this.configuration.GeneralChatType, messageTag, tagColor); } - - /// - /// Queue an error chat message. While method is named as PrintChat (it calls it internally), it only add a entry to - /// the queue, later to be processed when UpdateQueue() is called. - /// - /// A message to send. - public void PrintError(string message) + + /// + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT REGULAR ERROR]{0}", message); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = XivChatType.Urgent, - }); + this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor); } - - /// - /// Queue an error chat message. While method is named as PrintChat (it calls it internally), it only add a entry to - /// the queue, later to be processed when UpdateQueue() is called. - /// - /// A message to send. - public void PrintError(SeString message) + + /// + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT SESTRING ERROR]{0}", message.TextValue); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = XivChatType.Urgent, - }); + this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor); } - + /// /// Process a chat queue. /// @@ -242,7 +152,7 @@ public sealed class ChatGui : IDisposable, IServiceType /// A payload for handling. internal DalamudLinkPayload AddChatLinkHandler(string pluginName, uint commandId, Action commandAction) { - var payload = new DalamudLinkPayload() { Plugin = pluginName, CommandId = commandId }; + var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId }; this.dalamudLinkHandlers.Add((pluginName, commandId), commandAction); return payload; } @@ -266,20 +176,63 @@ public sealed class ChatGui : IDisposable, IServiceType /// The ID of the command to be removed. internal void RemoveChatLinkHandler(string pluginName, uint commandId) { - if (this.dalamudLinkHandlers.ContainsKey((pluginName, commandId))) - { - this.dalamudLinkHandlers.Remove((pluginName, commandId)); - } + this.dalamudLinkHandlers.Remove((pluginName, commandId)); } [ServiceManager.CallWhenServicesReady] - private void ContinueConstruction(GameGui gameGui, LibcFunction libcFunction) + private void ContinueConstruction() { this.printMessageHook.Enable(); this.populateItemLinkHook.Enable(); this.interactableLinkClickedHook.Enable(); } + private void PrintTagged(string message, XivChatType channel, string? tag, ushort? color) + { + var builder = new SeStringBuilder(); + + if (!tag.IsNullOrEmpty()) + { + if (color is not null) + { + builder.AddUiForeground($"[{tag}] ", color.Value); + } + else + { + builder.AddText($"[{tag}] "); + } + } + + this.Print(new XivChatEntry + { + Message = builder.AddText(message).Build(), + Type = channel, + }); + } + + private void PrintTagged(SeString message, XivChatType channel, string? tag, ushort? color) + { + var builder = new SeStringBuilder(); + + if (!tag.IsNullOrEmpty()) + { + if (color is not null) + { + builder.AddUiForeground($"[{tag}] ", color.Value); + } + else + { + builder.AddText($"[{tag}] "); + } + } + + this.Print(new XivChatEntry + { + Message = builder.Build().Append(message), + Type = channel, + }); + } + private void HandlePopulateItemLinkDetour(IntPtr linkObjectPtr, IntPtr itemInfoPtr) { try @@ -298,7 +251,7 @@ public sealed class ChatGui : IDisposable, IServiceType } } - private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage, uint senderid, IntPtr parameter) + private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chatType, IntPtr pSenderName, IntPtr pMessage, uint senderId, IntPtr parameter) { var retVal = IntPtr.Zero; @@ -325,13 +278,13 @@ public sealed class ChatGui : IDisposable, IServiceType // Call events var isHandled = false; - var invocationList = this.CheckMessageHandled.GetInvocationList(); + var invocationList = this.CheckMessageHandled!.GetInvocationList(); foreach (var @delegate in invocationList) { try { - var messageHandledDelegate = @delegate as OnCheckMessageHandledDelegate; - messageHandledDelegate!.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled); + var messageHandledDelegate = @delegate as IChatGui.OnCheckMessageHandledDelegate; + messageHandledDelegate!.Invoke(chatType, senderId, ref parsedSender, ref parsedMessage, ref isHandled); } catch (Exception e) { @@ -341,13 +294,13 @@ public sealed class ChatGui : IDisposable, IServiceType if (!isHandled) { - invocationList = this.ChatMessage.GetInvocationList(); + invocationList = this.ChatMessage!.GetInvocationList(); foreach (var @delegate in invocationList) { try { - var messageHandledDelegate = @delegate as OnMessageDelegate; - messageHandledDelegate!.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled); + var messageHandledDelegate = @delegate as IChatGui.OnMessageDelegate; + messageHandledDelegate!.Invoke(chatType, senderId, ref parsedSender, ref parsedMessage, ref isHandled); } catch (Exception e) { @@ -390,12 +343,12 @@ public sealed class ChatGui : IDisposable, IServiceType // Print the original chat if it's handled. if (isHandled) { - this.ChatMessageHandled?.Invoke(chattype, senderid, parsedSender, parsedMessage); + this.ChatMessageHandled?.Invoke(chatType, senderId, parsedSender, parsedMessage); } else { - retVal = this.printMessageHook.Original(manager, chattype, senderPtr, messagePtr, senderid, parameter); - this.ChatMessageUnhandled?.Invoke(chattype, senderid, parsedSender, parsedMessage); + retVal = this.printMessageHook.Original(manager, chatType, senderPtr, messagePtr, senderId, parameter); + this.ChatMessageUnhandled?.Invoke(chatType, senderId, parsedSender, parsedMessage); } if (this.baseAddress == IntPtr.Zero) @@ -407,7 +360,7 @@ public sealed class ChatGui : IDisposable, IServiceType catch (Exception ex) { Log.Error(ex, "Exception on OnChatMessage hook."); - retVal = this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter); + retVal = this.printMessageHook.Original(manager, chatType, pSenderName, pMessage, senderId, parameter); } return retVal; @@ -439,10 +392,10 @@ public sealed class ChatGui : IDisposable, IServiceType var linkPayload = payloads[0]; if (linkPayload is DalamudLinkPayload link) { - if (this.dalamudLinkHandlers.ContainsKey((link.Plugin, link.CommandId))) + if (this.dalamudLinkHandlers.TryGetValue((link.Plugin, link.CommandId), out var value)) { Log.Verbose($"Sending DalamudLink to {link.Plugin}: {link.CommandId}"); - this.dalamudLinkHandlers[(link.Plugin, link.CommandId)].Invoke(link.CommandId, new SeString(payloads)); + value.Invoke(link.CommandId, new SeString(payloads)); } else { @@ -456,3 +409,93 @@ public sealed class ChatGui : IDisposable, IServiceType } } } + +/// +/// Plugin scoped version of ChatGui. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class ChatGuiPluginScoped : IDisposable, IServiceType, IChatGui +{ + [ServiceManager.ServiceDependency] + private readonly ChatGui chatGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal ChatGuiPluginScoped() + { + this.chatGuiService.ChatMessage += this.OnMessageForward; + this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward; + this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward; + this.chatGuiService.ChatMessageUnhandled += this.OnMessageUnhandledForward; + } + + /// + public event IChatGui.OnMessageDelegate? ChatMessage; + + /// + public event IChatGui.OnCheckMessageHandledDelegate? CheckMessageHandled; + + /// + public event IChatGui.OnMessageHandledDelegate? ChatMessageHandled; + + /// + public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; + + /// + public int LastLinkedItemId => this.chatGuiService.LastLinkedItemId; + + /// + public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags; + + /// + public void Dispose() + { + this.chatGuiService.ChatMessage -= this.OnMessageForward; + this.chatGuiService.CheckMessageHandled -= this.OnCheckMessageForward; + this.chatGuiService.ChatMessageHandled -= this.OnMessageHandledForward; + this.chatGuiService.ChatMessageUnhandled -= this.OnMessageUnhandledForward; + + this.ChatMessage = null; + this.CheckMessageHandled = null; + this.ChatMessageHandled = null; + this.ChatMessageUnhandled = null; + } + + /// + public void Print(XivChatEntry chat) + => this.chatGuiService.Print(chat); + + /// + public void Print(string message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.Print(message, messageTag, tagColor); + + /// + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.Print(message, messageTag, tagColor); + + /// + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.PrintError(message, messageTag, tagColor); + + /// + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.PrintError(message, messageTag, tagColor); + + private void OnMessageForward(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + => this.ChatMessage?.Invoke(type, senderId, ref sender, ref message, ref isHandled); + + private void OnCheckMessageForward(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + => this.CheckMessageHandled?.Invoke(type, senderId, ref sender, ref message, ref isHandled); + + private void OnMessageHandledForward(XivChatType type, uint senderId, SeString sender, SeString message) + => this.ChatMessageHandled?.Invoke(type, senderId, sender, message); + + private void OnMessageUnhandledForward(XivChatType type, uint senderId, SeString sender, SeString message) + => this.ChatMessageUnhandled?.Invoke(type, senderId, sender, message); +} diff --git a/Dalamud/Game/Gui/ChatGuiAddressResolver.cs b/Dalamud/Game/Gui/ChatGuiAddressResolver.cs index 4686d5725..494e0b3ed 100644 --- a/Dalamud/Game/Gui/ChatGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/ChatGuiAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.Gui; /// /// The address resolver for the class. /// -public sealed class ChatGuiAddressResolver : BaseAddressResolver +internal sealed class ChatGuiAddressResolver : BaseAddressResolver { /// /// Gets the address of the native PrintMessage method. diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 390f58b1e..06d37e7ec 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -1,10 +1,12 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Dalamud.Configuration.Internal; using Dalamud.Game.Addon; +using Dalamud.Game.Addon.Events; +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Game.Text.SeStringHandling; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -19,10 +21,9 @@ namespace Dalamud.Game.Gui.Dtr; /// /// Class used to interface with the server info bar. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar +internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar { private const uint BaseNodeId = 1000; @@ -165,7 +166,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar private AtkUnitBase* GetDtr() => (AtkUnitBase*)this.gameGui.GetAddonByName("_DTR").ToPointer(); - private void Update(Framework unused) + private void Update(IFramework unused) { this.HandleRemovedNodes(); this.HandleAddedNodes(); diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs index f2222a7cd..3da8dc2a9 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs @@ -7,6 +7,7 @@ using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Memory; +using Dalamud.Plugin.Services; using Serilog; namespace Dalamud.Game.Gui.FlyText; @@ -14,10 +15,9 @@ namespace Dalamud.Game.Gui.FlyText; /// /// This class facilitates interacting with and creating native in-game "fly text". /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed class FlyTextGui : IDisposable, IServiceType +internal sealed class FlyTextGui : IDisposable, IServiceType, IFlyTextGui { /// /// The native function responsible for adding fly text to the UI. See . @@ -30,7 +30,7 @@ public sealed class FlyTextGui : IDisposable, IServiceType private readonly Hook createFlyTextHook; [ServiceManager.ServiceConstructor] - private FlyTextGui(SigScanner sigScanner) + private FlyTextGui(TargetSigScanner sigScanner) { this.Address = new FlyTextGuiAddressResolver(); this.Address.Setup(sigScanner); @@ -39,32 +39,6 @@ public sealed class FlyTextGui : IDisposable, IServiceType this.createFlyTextHook = Hook.FromAddress(this.Address.CreateFlyText, this.CreateFlyTextDetour); } - /// - /// The delegate defining the type for the FlyText event. - /// - /// The FlyTextKind. See . - /// Value1 passed to the native flytext function. - /// Value2 passed to the native flytext function. Seems unused. - /// Text1 passed to the native flytext function. - /// Text2 passed to the native flytext function. - /// Color passed to the native flytext function. Changes flytext color. - /// Icon ID passed to the native flytext function. Only displays with select FlyTextKind. - /// Damage Type Icon ID passed to the native flytext function. Displayed next to damage values to denote damage type. - /// The vertical offset to place the flytext at. 0 is default. Negative values result - /// in text appearing higher on the screen. This does not change where the element begins to fade. - /// Whether this flytext has been handled. If a subscriber sets this to true, the FlyText will not appear. - public delegate void OnFlyTextCreatedDelegate( - ref FlyTextKind kind, - ref int val1, - ref int val2, - ref SeString text1, - ref SeString text2, - ref uint color, - ref uint icon, - ref uint damageTypeIcon, - ref float yOffset, - ref bool handled); - /// /// Private delegate for the native CreateFlyText function's hook. /// @@ -95,12 +69,8 @@ public sealed class FlyTextGui : IDisposable, IServiceType uint offsetStrMax, int unknown); - /// - /// The FlyText event that can be subscribed to. - /// - public event OnFlyTextCreatedDelegate? FlyTextCreated; - - private Dalamud Dalamud { get; } + /// + public event IFlyTextGui.OnFlyTextCreatedDelegate? FlyTextCreated; private FlyTextGuiAddressResolver Address { get; } @@ -112,18 +82,7 @@ public sealed class FlyTextGui : IDisposable, IServiceType this.createFlyTextHook.Dispose(); } - /// - /// Displays a fly text in-game on the local player. - /// - /// The FlyTextKind. See . - /// The index of the actor to place flytext on. Indexing unknown. 1 places flytext on local player. - /// Value1 passed to the native flytext function. - /// Value2 passed to the native flytext function. Seems unused. - /// Text1 passed to the native flytext function. - /// Text2 passed to the native flytext function. - /// Color passed to the native flytext function. Changes flytext color. - /// Icon ID passed to the native flytext function. Only displays with select FlyTextKind. - /// Damage Type Icon ID passed to the native flytext function. Displayed next to damage values to denote damage type. + /// public unsafe void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon, uint damageTypeIcon) { // Known valid flytext region within the atk arrays @@ -318,3 +277,46 @@ public sealed class FlyTextGui : IDisposable, IServiceType return retVal; } } + +/// +/// Plugin scoped version of FlyTextGui. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class FlyTextGuiPluginScoped : IDisposable, IServiceType, IFlyTextGui +{ + [ServiceManager.ServiceDependency] + private readonly FlyTextGui flyTextGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal FlyTextGuiPluginScoped() + { + this.flyTextGuiService.FlyTextCreated += this.FlyTextCreatedForward; + } + + /// + public event IFlyTextGui.OnFlyTextCreatedDelegate? FlyTextCreated; + + /// + public void Dispose() + { + this.flyTextGuiService.FlyTextCreated -= this.FlyTextCreatedForward; + + this.FlyTextCreated = null; + } + + /// + public void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon, uint damageTypeIcon) + { + this.flyTextGuiService.AddFlyText(kind, actorIndex, val1, val2, text1, text2, color, icon, damageTypeIcon); + } + + private void FlyTextCreatedForward(ref FlyTextKind kind, ref int val1, ref int val2, ref SeString text1, ref SeString text2, ref uint color, ref uint icon, ref uint damageTypeIcon, ref float yOffset, ref bool handled) + => this.FlyTextCreated?.Invoke(ref kind, ref val1, ref val2, ref text1, ref text2, ref color, ref icon, ref damageTypeIcon, ref yOffset, ref handled); +} diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs b/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs index 588177032..677d92e57 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.Gui.FlyText; /// /// An address resolver for the class. /// -public class FlyTextGuiAddressResolver : BaseAddressResolver +internal class FlyTextGuiAddressResolver : BaseAddressResolver { /// /// Gets the address of the native AddFlyText method, which occurs diff --git a/Dalamud/Game/Gui/FlyText/FlyTextKind.cs b/Dalamud/Game/Gui/FlyText/FlyTextKind.cs index 68650fb5c..3727fd0f8 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextKind.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextKind.cs @@ -1,57 +1,58 @@ namespace Dalamud.Game.Gui.FlyText; /// -/// Enum of FlyTextKind values. Members suffixed with -/// a number seem to be a duplicate, or perform duplicate behavior. +/// Enum of FlyTextKind values. /// public enum FlyTextKind : int { /// /// Val1 in serif font, Text2 in sans-serif as subtitle. - /// Used for autos and incoming DoTs. /// - AutoAttack = 0, + AutoAttackOrDot = 0, /// /// Val1 in serif font, Text2 in sans-serif as subtitle. /// Does a bounce effect on appearance. /// - DirectHit = 1, + AutoAttackOrDotDh = 1, /// /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle. /// Does a bigger bounce effect on appearance. /// - CriticalHit = 2, + AutoAttackOrDotCrit = 2, /// - /// Val1 in even larger serif font with 2 exclamations, Text2 in - /// sans-serif as subtitle. Does a large bounce effect on appearance. - /// Does not scroll up or down the screen. + /// Val1 in even larger serif font with 2 exclamations, Text2 in sans-serif as subtitle. + /// Does a large bounce effect on appearance. Does not scroll up or down the screen. /// - CriticalDirectHit = 3, + AutoAttackOrDotCritDh = 3, /// - /// AutoAttack with sans-serif Text1 to the left of the Val1. + /// Val1 in serif font, Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. /// - NamedAttack = 4, + Damage = 4, /// - /// DirectHit with sans-serif Text1 to the left of the Val1. + /// Val1 in serif font, Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. + /// Does a bounce effect on appearance. /// - NamedDirectHit = 5, + DamageDh = 5, /// - /// CriticalHit with sans-serif Text1 to the left of the Val1. + /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. + /// Does a bigger bounce effect on appearance. /// - NamedCriticalHit = 6, + DamageCrit = 6, /// - /// CriticalDirectHit with sans-serif Text1 to the left of the Val1. + /// Val1 in even larger serif font with 2 exclamations, Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. + /// Does a large bounce effect on appearance. Does not scroll up or down the screen. /// - NamedCriticalDirectHit = 7, + DamageCritDh = 7, /// + /// The text changes to DODGE under certain circumstances. /// All caps, serif MISS. /// Miss = 8, @@ -74,12 +75,12 @@ public enum FlyTextKind : int /// /// Icon next to sans-serif Text1. /// - NamedIcon = 12, + Buff = 12, /// - /// Icon next to sans-serif Text1 (2). + /// Icon next to sans-serif Text1. /// - NamedIcon2 = 13, + Debuff = 13, /// /// Serif Val1 with all caps condensed font EXP with Text2 in sans-serif as subtitle. @@ -94,42 +95,44 @@ public enum FlyTextKind : int /// /// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle. /// - NamedMp = 16, + MpDrain = 16, /// + /// Currently not used by the game. /// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle. /// NamedTp = 17, /// - /// AutoAttack with sans-serif Text1 to the left of the Val1 (2). + /// Val1 in serif font, Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. /// - NamedAttack2 = 18, + Healing = 18, /// - /// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle (2). + /// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle. /// - NamedMp2 = 19, + MpRegen = 19, /// - /// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle (2). + /// Currently not used by the game. + /// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle. /// NamedTp2 = 20, /// /// Sans-serif Text1 next to serif Val1 with all caps condensed font EP with Text2 in sans-serif as subtitle. /// - NamedEp = 21, + EpRegen = 21, /// /// Sans-serif Text1 next to serif Val1 with all caps condensed font CP with Text2 in sans-serif as subtitle. /// - NamedCp = 22, + CpRegen = 22, /// /// Sans-serif Text1 next to serif Val1 with all caps condensed font GP with Text2 in sans-serif as subtitle. /// - NamedGp = 23, + GpRegen = 23, /// /// Displays nothing. @@ -149,57 +152,59 @@ public enum FlyTextKind : int Interrupted = 26, /// - /// AutoAttack with no Text2. + /// Val1 in serif font. /// - AutoAttackNoText = 27, + CraftingProgress = 27, /// - /// AutoAttack with no Text2 (2). + /// Val1 in serif font. /// - AutoAttackNoText2 = 28, + CraftingQuality = 28, /// - /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle. Does a bigger bounce effect on appearance (2). + /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle. Does a bigger bounce effect on appearance. /// - CriticalHit2 = 29, + CraftingQualityCrit = 29, /// - /// AutoAttack with no Text2 (3). + /// Currently not used by the game. + /// Val1 in serif font. /// AutoAttackNoText3 = 30, /// /// CriticalHit with sans-serif Text1 to the left of the Val1 (2). /// - NamedCriticalHit2 = 31, + HealingCrit = 31, /// - /// Same as NamedCriticalHit with a green (cannot change) MP in condensed font to the right of Val1. + /// Currently not used by the game. + /// Same as DamageCrit with a MP in condensed font to the right of Val1. /// Does a jiggle effect to the right on appearance. /// NamedCriticalHitWithMp = 32, /// - /// Same as NamedCriticalHit with a yellow (cannot change) TP in condensed font to the right of Val1. + /// Currently not used by the game. + /// Same as DamageCrit with a TP in condensed font to the right of Val1. /// Does a jiggle effect to the right on appearance. /// NamedCriticalHitWithTp = 33, /// - /// Same as NamedIcon with sans-serif "has no effect!" to the right. + /// Icon next to sans-serif Text1 with sans-serif "has no effect!" to the right. /// - NamedIconHasNoEffect = 34, + DebuffNoEffect = 34, /// - /// Same as NamedIcon but Text1 is slightly faded. Used for buff expiration. + /// Icon next to sans-serif slightly faded Text1. /// - NamedIconFaded = 35, + BuffFading = 35, /// - /// Same as NamedIcon but Text1 is slightly faded (2). - /// Used for buff expiration. + /// Icon next to sans-serif slightly faded Text1. /// - NamedIconFaded2 = 36, + DebuffFading = 36, /// /// Text1 in sans-serif font. @@ -207,9 +212,9 @@ public enum FlyTextKind : int Named = 37, /// - /// Same as NamedIcon with sans-serif "(fully resisted)" to the right. + /// Icon next to sans-serif Text1 with sans-serif "(fully resisted)" to the right. /// - NamedIconFullyResisted = 38, + DebuffResisted = 38, /// /// All caps serif 'INCAPACITATED!'. @@ -219,32 +224,34 @@ public enum FlyTextKind : int /// /// Text1 with sans-serif "(fully resisted)" to the right. /// - NamedFullyResisted = 40, + FullyResisted = 40, /// /// Text1 with sans-serif "has no effect!" to the right. /// - NamedHasNoEffect = 41, + HasNoEffect = 41, /// - /// AutoAttack with sans-serif Text1 to the left of the Val1 (3). + /// Val1 in serif font, Text2 in sans-serif as subtitle with sans-serif Text1 to the left of the Val1. /// - NamedAttack3 = 42, + HpDrain = 42, /// - /// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle (3). + /// Currently not used by the game. + /// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle. /// NamedMp3 = 43, /// - /// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle (3). + /// Currently not used by the game. + /// Sans-serif Text1 next to serif Val1 with all caps condensed font TP with Text2 in sans-serif as subtitle. /// NamedTp3 = 44, /// - /// Same as NamedIcon with serif "INVULNERABLE!" beneath the Text1. + /// Icon next to sans-serif Text1 with serif "INVULNERABLE!" beneath the Text1. /// - NamedIconInvulnerable = 45, + DebuffInvulnerable = 45, /// /// All caps serif RESIST. @@ -252,20 +259,20 @@ public enum FlyTextKind : int Resist = 46, /// - /// Same as NamedIcon but places the given icon in the item icon outline. + /// Icon with an item icon outline next to sans-serif Text1. /// - NamedIconWithItemOutline = 47, + LootedItem = 47, /// - /// AutoAttack with no Text2 (4). + /// Val1 in serif font. /// - AutoAttackNoText4 = 48, + Collectability = 48, /// - /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle (3). + /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle. /// Does a bigger bounce effect on appearance. /// - CriticalHit3 = 49, + CollectabilityCrit = 49, /// /// All caps serif REFLECT. @@ -278,20 +285,21 @@ public enum FlyTextKind : int Reflected = 51, /// - /// Val1 in serif font, Text2 in sans-serif as subtitle (2). + /// Val1 in serif font, Text2 in sans-serif as subtitle. /// Does a bounce effect on appearance. /// - DirectHit2 = 52, + CraftingQualityDh = 52, /// - /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle (4). + /// Currently not used by the game. + /// Val1 in larger serif font with exclamation, with Text2 in sans-serif as subtitle. /// Does a bigger bounce effect on appearance. /// CriticalHit4 = 53, /// - /// Val1 in even larger serif font with 2 exclamations, Text2 in sans-serif as subtitle (2). + /// Val1 in even larger serif font with 2 exclamations, Text2 in sans-serif as subtitle. /// Does a large bounce effect on appearance. Does not scroll up or down the screen. /// - CriticalDirectHit2 = 54, + CraftingQualityCritDh = 54, } diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 0235bef5a..a1a17436e 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -1,12 +1,12 @@ -using System; using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Hooking; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; @@ -15,7 +15,6 @@ using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -using Serilog; using SharpDX; using Vector2 = System.Numerics.Vector2; @@ -26,14 +25,12 @@ namespace Dalamud.Game.Gui; /// /// A class handling many aspects of the in-game UI. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui +internal sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui { + private static readonly ModuleLog Log = new("GameGui"); + private readonly GameGuiAddressResolver address; private readonly GetMatrixSingletonDelegate getMatrixSingleton; @@ -47,11 +44,11 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui private readonly Hook toggleUiHideHook; private readonly Hook utf8StringFromSequenceHook; - private GetUIMapObjectDelegate getUIMapObject; - private OpenMapWithFlagDelegate openMapWithFlag; + private GetUIMapObjectDelegate? getUIMapObject; + private OpenMapWithFlagDelegate? openMapWithFlag; [ServiceManager.ServiceConstructor] - private GameGui(SigScanner sigScanner) + private GameGui(TargetSigScanner sigScanner) { this.address = new GameGuiAddressResolver(); this.address.Setup(sigScanner); @@ -115,16 +112,16 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr ToggleUiHideDelegate(IntPtr thisPtr, byte unknownByte); + private delegate IntPtr ToggleUiHideDelegate(IntPtr thisPtr, bool uiVisible); /// - public event EventHandler UiHideToggled; + public event EventHandler? UiHideToggled; /// - public event EventHandler HoveredItemChanged; + public event EventHandler? HoveredItemChanged; /// - public event EventHandler HoveredActionChanged; + public event EventHandler? HoveredActionChanged; /// public bool GameUiHidden { get; private set; } @@ -146,7 +143,7 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui return false; } - this.getUIMapObject = this.address.GetVirtualFunction(uiModule, 0, 8); + this.getUIMapObject ??= this.address.GetVirtualFunction(uiModule, 0, 8); var uiMapObjectPtr = this.getUIMapObject(uiModule); @@ -156,7 +153,7 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui return false; } - this.openMapWithFlag = this.address.GetVirtualFunction(uiMapObjectPtr, 0, 63); + this.openMapWithFlag ??= this.address.GetVirtualFunction(uiMapObjectPtr, 0, 63); var mapLinkString = mapLink.DataString; @@ -216,14 +213,13 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui // Read current ViewProjectionMatrix plus game window size var viewProjectionMatrix = default(Matrix); - float width, height; var rawMatrix = (float*)(matrixSingleton + 0x1b4).ToPointer(); for (var i = 0; i < 16; i++, rawMatrix++) viewProjectionMatrix[i] = *rawMatrix; - width = *rawMatrix; - height = *(rawMatrix + 1); + var width = *rawMatrix; + var height = *(rawMatrix + 1); viewProjectionMatrix.Invert(); @@ -413,7 +409,7 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui this.HoveredItemChanged?.InvokeSafely(this, itemId); - Log.Verbose("HoverItemId:{0} this:{1}", itemId, hoverState.ToInt64().ToString("X")); + Log.Verbose($"HoverItemId:{itemId} this:{hoverState.ToInt64()}"); } return retVal; @@ -455,7 +451,7 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui this.HoveredAction.ActionID = (uint)Marshal.ReadInt32(hoverState, 0x3C); this.HoveredActionChanged?.InvokeSafely(this, this.HoveredAction); - Log.Verbose("HoverActionId: {0}/{1} this:{2}", actionKind, actionId, hoverState.ToInt64().ToString("X")); + Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{hoverState.ToInt64():X}"); } private IntPtr HandleActionOutDetour(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4) @@ -488,16 +484,16 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui return retVal; } - private IntPtr ToggleUiHideDetour(IntPtr thisPtr, byte unknownByte) + private IntPtr ToggleUiHideDetour(IntPtr thisPtr, bool unknownByte) { - // TODO(goat): We should read this from memory directly, instead of relying on catching every toggle. - this.GameUiHidden = !this.GameUiHidden; + var result = this.toggleUiHideHook.Original(thisPtr, unknownByte); + this.GameUiHidden = !RaptureAtkModule.Instance()->IsUiVisible; this.UiHideToggled?.InvokeSafely(this, this.GameUiHidden); Log.Debug("UiHide toggled: {0}", this.GameUiHidden); - return this.toggleUiHideHook.Original(thisPtr, unknownByte); + return result; } private char HandleImmDetour(IntPtr framework, char a2, byte a3) @@ -513,8 +509,109 @@ public sealed unsafe class GameGui : IDisposable, IServiceType, IGameGui if (sourcePtr != null) this.utf8StringFromSequenceHook.Original(thisPtr, sourcePtr, sourceLen); else - thisPtr->Ctor(); // this is in clientstructs but you could do it manually too + thisPtr->Ctor(); // this is in ClientStructs but you could do it manually too return thisPtr; // this function shouldn't need to return but the original asm moves this into rax before returning so be safe? } } + +/// +/// Plugin-scoped version of a AddonLifecycle service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class GameGuiPluginScoped : IDisposable, IServiceType, IGameGui +{ + [ServiceManager.ServiceDependency] + private readonly GameGui gameGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal GameGuiPluginScoped() + { + this.gameGuiService.UiHideToggled += this.UiHideToggledForward; + this.gameGuiService.HoveredItemChanged += this.HoveredItemForward; + this.gameGuiService.HoveredActionChanged += this.HoveredActionForward; + } + + /// + public event EventHandler? UiHideToggled; + + /// + public event EventHandler? HoveredItemChanged; + + /// + public event EventHandler? HoveredActionChanged; + + /// + public bool GameUiHidden => this.gameGuiService.GameUiHidden; + + /// + public ulong HoveredItem + { + get => this.gameGuiService.HoveredItem; + set => this.gameGuiService.HoveredItem = value; + } + + /// + public HoveredAction HoveredAction => this.gameGuiService.HoveredAction; + + /// + public void Dispose() + { + this.gameGuiService.UiHideToggled -= this.UiHideToggledForward; + this.gameGuiService.HoveredItemChanged -= this.HoveredItemForward; + this.gameGuiService.HoveredActionChanged -= this.HoveredActionForward; + + this.UiHideToggled = null; + this.HoveredItemChanged = null; + this.HoveredActionChanged = null; + } + + /// + public bool OpenMapWithMapLink(MapLinkPayload mapLink) + => this.gameGuiService.OpenMapWithMapLink(mapLink); + + /// + public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos) + => this.gameGuiService.WorldToScreen(worldPos, out screenPos); + + /// + public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos, out bool inView) + => this.gameGuiService.WorldToScreen(worldPos, out screenPos, out inView); + + /// + public bool ScreenToWorld(Vector2 screenPos, out Vector3 worldPos, float rayDistance = 100000) + => this.gameGuiService.ScreenToWorld(screenPos, out worldPos, rayDistance); + + /// + public IntPtr GetUIModule() + => this.gameGuiService.GetUIModule(); + + /// + public IntPtr GetAddonByName(string name, int index = 1) + => this.gameGuiService.GetAddonByName(name, index); + + /// + public IntPtr FindAgentInterface(string addonName) + => this.gameGuiService.FindAgentInterface(addonName); + + /// + public unsafe IntPtr FindAgentInterface(void* addon) + => this.gameGuiService.FindAgentInterface(addon); + + /// + public IntPtr FindAgentInterface(IntPtr addonPtr) + => this.gameGuiService.FindAgentInterface(addonPtr); + + private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled); + + private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId); + + private void HoveredActionForward(object sender, HoveredAction hoverAction) => this.HoveredActionChanged?.Invoke(sender, hoverAction); +} diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs index aa9d28cb1..c12721358 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.Gui.PartyFinder; /// /// The address resolver for the class. /// -public class PartyFinderAddressResolver : BaseAddressResolver +internal class PartyFinderAddressResolver : BaseAddressResolver { /// /// Gets the address of the native ReceiveListing method. diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs index 6427f2a54..61c0f62e4 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; using Dalamud.Game.Gui.PartyFinder.Internal; @@ -6,6 +5,7 @@ using Dalamud.Game.Gui.PartyFinder.Types; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Serilog; namespace Dalamud.Game.Gui.PartyFinder; @@ -13,10 +13,9 @@ namespace Dalamud.Game.Gui.PartyFinder; /// /// This class handles interacting with the native PartyFinder window. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed class PartyFinderGui : IDisposable, IServiceType +internal sealed class PartyFinderGui : IDisposable, IServiceType, IPartyFinderGui { private readonly PartyFinderAddressResolver address; private readonly IntPtr memory; @@ -28,32 +27,21 @@ public sealed class PartyFinderGui : IDisposable, IServiceType /// /// Sig scanner to use. [ServiceManager.ServiceConstructor] - private PartyFinderGui(SigScanner sigScanner) + private PartyFinderGui(TargetSigScanner sigScanner) { this.address = new PartyFinderAddressResolver(); this.address.Setup(sigScanner); this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize); - this.receiveListingHook = Hook.FromAddress(this.address.ReceiveListing, new ReceiveListingDelegate(this.HandleReceiveListingDetour)); + this.receiveListingHook = Hook.FromAddress(this.address.ReceiveListing, this.HandleReceiveListingDetour); } - /// - /// Event type fired each time the game receives an individual Party Finder listing. - /// Cannot modify listings but can hide them. - /// - /// The listings received. - /// Additional arguments passed by the game. - public delegate void PartyFinderListingEventDelegate(PartyFinderListing listing, PartyFinderListingEventArgs args); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void ReceiveListingDelegate(IntPtr managerPtr, IntPtr data); - /// - /// Event fired each time the game receives an individual Party Finder listing. - /// Cannot modify listings but can hide them. - /// - public event PartyFinderListingEventDelegate ReceiveListing; + /// + public event IPartyFinderGui.PartyFinderListingEventDelegate? ReceiveListing; /// /// Dispose of managed and unmanaged resources. @@ -138,3 +126,39 @@ public sealed class PartyFinderGui : IDisposable, IServiceType } } } + +/// +/// A scoped variant of the PartyFinderGui service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class PartyFinderGuiPluginScoped : IDisposable, IServiceType, IPartyFinderGui +{ + [ServiceManager.ServiceDependency] + private readonly PartyFinderGui partyFinderGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal PartyFinderGuiPluginScoped() + { + this.partyFinderGuiService.ReceiveListing += this.ReceiveListingForward; + } + + /// + public event IPartyFinderGui.PartyFinderListingEventDelegate? ReceiveListing; + + /// + public void Dispose() + { + this.partyFinderGuiService.ReceiveListing -= this.ReceiveListingForward; + + this.ReceiveListing = null; + } + + private void ReceiveListingForward(PartyFinderListing listing, PartyFinderListingEventArgs args) => this.ReceiveListing?.Invoke(listing, args); +} diff --git a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs index c7630acfa..46e83b972 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs @@ -1,4 +1,4 @@ -using Dalamud.Data; +using Dalamud.Plugin.Services; using Lumina.Excel.GeneratedSheets; namespace Dalamud.Game.Gui.PartyFinder.Types; @@ -14,7 +14,7 @@ public static class JobFlagsExtensions /// A JobFlags enum member. /// A DataManager to get the ClassJob from. /// A ClassJob if found or null if not. - public static ClassJob ClassJob(this JobFlags job, DataManager data) + public static ClassJob? ClassJob(this JobFlags job, IDataManager data) { var jobs = data.GetExcelSheet(); @@ -52,6 +52,6 @@ public static class JobFlagsExtensions _ => null, }; - return row == null ? null : jobs.GetRow((uint)row); + return row == null ? null : jobs?.GetRow((uint)row); } } diff --git a/Dalamud/Game/Gui/Toast/ToastGui.cs b/Dalamud/Game/Gui/Toast/ToastGui.cs index e65fa1444..362edb3be 100644 --- a/Dalamud/Game/Gui/Toast/ToastGui.cs +++ b/Dalamud/Game/Gui/Toast/ToastGui.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Text; @@ -6,16 +5,16 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; namespace Dalamud.Game.Gui.Toast; /// /// This class facilitates interacting with and creating native toast windows. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed partial class ToastGui : IDisposable, IServiceType +internal sealed partial class ToastGui : IDisposable, IServiceType, IToastGui { private const uint QuestToastCheckmarkMagic = 60081; @@ -34,43 +33,16 @@ public sealed partial class ToastGui : IDisposable, IServiceType /// /// Sig scanner to use. [ServiceManager.ServiceConstructor] - private ToastGui(SigScanner sigScanner) + private ToastGui(TargetSigScanner sigScanner) { this.address = new ToastGuiAddressResolver(); this.address.Setup(sigScanner); - this.showNormalToastHook = Hook.FromAddress(this.address.ShowNormalToast, new ShowNormalToastDelegate(this.HandleNormalToastDetour)); - this.showQuestToastHook = Hook.FromAddress(this.address.ShowQuestToast, new ShowQuestToastDelegate(this.HandleQuestToastDetour)); - this.showErrorToastHook = Hook.FromAddress(this.address.ShowErrorToast, new ShowErrorToastDelegate(this.HandleErrorToastDetour)); + this.showNormalToastHook = Hook.FromAddress(this.address.ShowNormalToast, this.HandleNormalToastDetour); + this.showQuestToastHook = Hook.FromAddress(this.address.ShowQuestToast, this.HandleQuestToastDetour); + this.showErrorToastHook = Hook.FromAddress(this.address.ShowErrorToast, this.HandleErrorToastDetour); } - #region Event delegates - - /// - /// A delegate type used when a normal toast window appears. - /// - /// The message displayed. - /// Assorted toast options. - /// Whether the toast has been handled or should be propagated. - public delegate void OnNormalToastDelegate(ref SeString message, ref ToastOptions options, ref bool isHandled); - - /// - /// A delegate type used when a quest toast window appears. - /// - /// The message displayed. - /// Assorted toast options. - /// Whether the toast has been handled or should be propagated. - public delegate void OnQuestToastDelegate(ref SeString message, ref QuestToastOptions options, ref bool isHandled); - - /// - /// A delegate type used when an error toast window appears. - /// - /// The message displayed. - /// Whether the toast has been handled or should be propagated. - public delegate void OnErrorToastDelegate(ref SeString message, ref bool isHandled); - - #endregion - #region Marshal delegates private delegate IntPtr ShowNormalToastDelegate(IntPtr manager, IntPtr text, int layer, byte isTop, byte isFast, int logMessageId); @@ -82,21 +54,15 @@ public sealed partial class ToastGui : IDisposable, IServiceType #endregion #region Events + + /// + public event IToastGui.OnNormalToastDelegate? Toast; - /// - /// Event that will be fired when a toast is sent by the game or a plugin. - /// - public event OnNormalToastDelegate Toast; + /// + public event IToastGui.OnQuestToastDelegate? QuestToast; - /// - /// Event that will be fired when a quest toast is sent by the game or a plugin. - /// - public event OnQuestToastDelegate QuestToast; - - /// - /// Event that will be fired when an error toast is sent by the game or a plugin. - /// - public event OnErrorToastDelegate ErrorToast; + /// + public event IToastGui.OnErrorToastDelegate? ErrorToast; #endregion @@ -172,31 +138,23 @@ public sealed partial class ToastGui : IDisposable, IServiceType /// /// Handles normal toasts. /// -public sealed partial class ToastGui +internal sealed partial class ToastGui { - /// - /// Show a toast message with the given content. - /// - /// The message to be shown. - /// Options for the toast. - public void ShowNormal(string message, ToastOptions options = null) + /// + public void ShowNormal(string message, ToastOptions? options = null) { options ??= new ToastOptions(); this.normalQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); } - - /// - /// Show a toast message with the given content. - /// - /// The message to be shown. - /// Options for the toast. - public void ShowNormal(SeString message, ToastOptions options = null) + + /// + public void ShowNormal(SeString message, ToastOptions? options = null) { options ??= new ToastOptions(); this.normalQueue.Enqueue((message.Encode(), options)); } - private void ShowNormal(byte[] bytes, ToastOptions options = null) + private void ShowNormal(byte[] bytes, ToastOptions? options = null) { options ??= new ToastOptions(); @@ -255,31 +213,23 @@ public sealed partial class ToastGui /// /// Handles quest toasts. /// -public sealed partial class ToastGui +internal sealed partial class ToastGui { - /// - /// Show a quest toast message with the given content. - /// - /// The message to be shown. - /// Options for the toast. - public void ShowQuest(string message, QuestToastOptions options = null) + /// + public void ShowQuest(string message, QuestToastOptions? options = null) { options ??= new QuestToastOptions(); this.questQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); } - - /// - /// Show a quest toast message with the given content. - /// - /// The message to be shown. - /// Options for the toast. - public void ShowQuest(SeString message, QuestToastOptions options = null) + + /// + public void ShowQuest(SeString message, QuestToastOptions? options = null) { options ??= new QuestToastOptions(); this.questQueue.Enqueue((message.Encode(), options)); } - private void ShowQuest(byte[] bytes, QuestToastOptions options = null) + private void ShowQuest(byte[] bytes, QuestToastOptions? options = null) { options ??= new QuestToastOptions(); @@ -365,21 +315,15 @@ public sealed partial class ToastGui /// /// Handles error toasts. /// -public sealed partial class ToastGui +internal sealed partial class ToastGui { - /// - /// Show an error toast message with the given content. - /// - /// The message to be shown. + /// public void ShowError(string message) { this.errorQueue.Enqueue(Encoding.UTF8.GetBytes(message)); } - /// - /// Show an error toast message with the given content. - /// - /// The message to be shown. + /// public void ShowError(SeString message) { this.errorQueue.Enqueue(message.Encode()); @@ -433,3 +377,76 @@ public sealed partial class ToastGui } } } + +/// +/// Plugin scoped version of ToastGui. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class ToastGuiPluginScoped : IDisposable, IServiceType, IToastGui +{ + [ServiceManager.ServiceDependency] + private readonly ToastGui toastGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal ToastGuiPluginScoped() + { + this.toastGuiService.Toast += this.ToastForward; + this.toastGuiService.QuestToast += this.QuestToastForward; + this.toastGuiService.ErrorToast += this.ErrorToastForward; + } + + /// + public event IToastGui.OnNormalToastDelegate? Toast; + + /// + public event IToastGui.OnQuestToastDelegate? QuestToast; + + /// + public event IToastGui.OnErrorToastDelegate? ErrorToast; + + /// + public void Dispose() + { + this.toastGuiService.Toast -= this.ToastForward; + this.toastGuiService.QuestToast -= this.QuestToastForward; + this.toastGuiService.ErrorToast -= this.ErrorToastForward; + + this.Toast = null; + this.QuestToast = null; + this.ErrorToast = null; + } + + /// + public void ShowNormal(string message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options); + + /// + public void ShowNormal(SeString message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options); + + /// + public void ShowQuest(string message, QuestToastOptions? options = null) => this.toastGuiService.ShowQuest(message, options); + + /// + public void ShowQuest(SeString message, QuestToastOptions? options = null) => this.toastGuiService.ShowQuest(message, options); + + /// + public void ShowError(string message) => this.toastGuiService.ShowError(message); + + /// + public void ShowError(SeString message) => this.toastGuiService.ShowError(message); + + private void ToastForward(ref SeString message, ref ToastOptions options, ref bool isHandled) + => this.Toast?.Invoke(ref message, ref options, ref isHandled); + + private void QuestToastForward(ref SeString message, ref QuestToastOptions options, ref bool isHandled) + => this.QuestToast?.Invoke(ref message, ref options, ref isHandled); + + private void ErrorToastForward(ref SeString message, ref bool isHandled) + => this.ErrorToast?.Invoke(ref message, ref isHandled); +} diff --git a/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs b/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs index 4f935b465..ae5426023 100644 --- a/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.Gui.Toast; /// /// An address resolver for the class. /// -public class ToastGuiAddressResolver : BaseAddressResolver +internal class ToastGuiAddressResolver : BaseAddressResolver { /// /// Gets the address of the native ShowNormalToast method. diff --git a/Dalamud/Game/Internal/AntiDebug.cs b/Dalamud/Game/Internal/AntiDebug.cs index ba482ef48..2f4ec28c0 100644 --- a/Dalamud/Game/Internal/AntiDebug.cs +++ b/Dalamud/Game/Internal/AntiDebug.cs @@ -19,7 +19,7 @@ internal sealed partial class AntiDebug : IServiceType private IntPtr debugCheckAddress; [ServiceManager.ServiceConstructor] - private AntiDebug(SigScanner sigScanner) + private AntiDebug(TargetSigScanner sigScanner) { try { diff --git a/Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs deleted file mode 100644 index ad79dff9f..000000000 --- a/Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; - -using Serilog; - -namespace Dalamud.Game.Internal.DXGI; - -/// -/// The address resolver for native D3D11 methods to facilitate displaying the Dalamud UI. -/// -[Obsolete("This has been deprecated in favor of the VTable resolver.")] -public sealed class SwapChainSigResolver : BaseAddressResolver, ISwapChainAddressResolver -{ - /// - public IntPtr Present { get; set; } - - /// - public IntPtr ResizeBuffers { get; set; } - - /// - protected override void Setup64Bit(SigScanner sig) - { - var module = Process.GetCurrentProcess().Modules.Cast().First(m => m.ModuleName == "dxgi.dll"); - - Log.Debug($"Found DXGI: 0x{module.BaseAddress.ToInt64():X}"); - - var scanner = new SigScanner(module); - - // This(code after the function head - offset of it) was picked to avoid running into issues with other hooks being installed into this function. - this.Present = scanner.ScanModule("41 8B F0 8B FA 89 54 24 ?? 48 8B D9 48 89 4D ?? C6 44 24 ?? 00") - 0x37; - - this.ResizeBuffers = scanner.ScanModule("48 8B C4 55 41 54 41 55 41 56 41 57 48 8D 68 B1 48 81 EC ?? ?? ?? ?? 48 C7 45 ?? ?? ?? ?? ?? 48 89 58 10 48 89 70 18 48 89 78 20 45 8B F9 45 8B E0 44 8B EA 48 8B F9 8B 45 7F 89 44 24 30 8B 75 77 89 74 24 28 44 89 4C 24"); - } -} diff --git a/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs index 603324175..50aae26ed 100644 --- a/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs +++ b/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; @@ -15,7 +14,7 @@ namespace Dalamud.Game.Internal.DXGI; /// /// If the normal signature based method of resolution fails, this is the backup. /// -public class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver +internal class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver { /// public IntPtr Present { get; set; } diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index 60e61b2f7..b45b35c4d 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -41,7 +41,7 @@ internal sealed unsafe partial class DalamudAtkTweaks : IServiceType private readonly string locDalamudSettings; [ServiceManager.ServiceConstructor] - private DalamudAtkTweaks(SigScanner sigScanner) + private DalamudAtkTweaks(TargetSigScanner sigScanner) { var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 32 C0 4C 8B AC 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ??"); diff --git a/Dalamud/Game/Libc/LibcFunction.cs b/Dalamud/Game/Libc/LibcFunction.cs index 7dfc26b3b..f1cd07080 100644 --- a/Dalamud/Game/Libc/LibcFunction.cs +++ b/Dalamud/Game/Libc/LibcFunction.cs @@ -17,14 +17,14 @@ namespace Dalamud.Game.Libc; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public sealed class LibcFunction : IServiceType, ILibcFunction +internal sealed class LibcFunction : IServiceType, ILibcFunction { private readonly LibcFunctionAddressResolver address; private readonly StdStringFromCStringDelegate stdStringCtorCString; private readonly StdStringDeallocateDelegate stdStringDeallocate; [ServiceManager.ServiceConstructor] - private LibcFunction(SigScanner sigScanner) + private LibcFunction(TargetSigScanner sigScanner) { this.address = new LibcFunctionAddressResolver(); this.address.Setup(sigScanner); diff --git a/Dalamud/Game/Libc/LibcFunctionAddressResolver.cs b/Dalamud/Game/Libc/LibcFunctionAddressResolver.cs index 89b721a87..4c3b7cdf8 100644 --- a/Dalamud/Game/Libc/LibcFunctionAddressResolver.cs +++ b/Dalamud/Game/Libc/LibcFunctionAddressResolver.cs @@ -5,7 +5,7 @@ namespace Dalamud.Game.Libc; /// /// The address resolver for the class. /// -public sealed class LibcFunctionAddressResolver : BaseAddressResolver +internal sealed class LibcFunctionAddressResolver : BaseAddressResolver { private delegate IntPtr StringFromCString(); diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs index d1fc0bfba..9ea3e491e 100644 --- a/Dalamud/Game/Network/GameNetwork.cs +++ b/Dalamud/Game/Network/GameNetwork.cs @@ -1,10 +1,10 @@ -using System; using System.Runtime.InteropServices; using Dalamud.Configuration.Internal; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; using Dalamud.Utility; using Serilog; @@ -13,10 +13,9 @@ namespace Dalamud.Game.Network; /// /// This class handles interacting with game network events. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -public sealed class GameNetwork : IDisposable, IServiceType +internal sealed class GameNetwork : IDisposable, IServiceType, IGameNetwork { private readonly GameNetworkAddressResolver address; private readonly Hook processZonePacketDownHook; @@ -31,7 +30,7 @@ public sealed class GameNetwork : IDisposable, IServiceType private IntPtr baseAddress; [ServiceManager.ServiceConstructor] - private GameNetwork(SigScanner sigScanner) + private GameNetwork(TargetSigScanner sigScanner) { this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch); this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch); @@ -47,30 +46,16 @@ public sealed class GameNetwork : IDisposable, IServiceType this.processZonePacketUpHook = Hook.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour); } - /// - /// The delegate type of a network message event. - /// - /// The pointer to the raw data. - /// The operation ID code. - /// The source actor ID. - /// The taret actor ID. - /// The direction of the packed. - public delegate void OnNetworkMessageDelegate(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void ProcessZonePacketDownDelegate(IntPtr a, uint targetId, IntPtr dataPtr); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4); - /// - /// Event that is called when a network message is sent/received. - /// - public event OnNetworkMessageDelegate NetworkMessage; + /// + public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage; - /// - /// Dispose of managed and unmanaged resources. - /// + /// void IDisposable.Dispose() { this.processZonePacketDownHook.Dispose(); @@ -154,3 +139,40 @@ public sealed class GameNetwork : IDisposable, IServiceType return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4); } } + +/// +/// Plugin-scoped version of a AddonLifecycle service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class GameNetworkPluginScoped : IDisposable, IServiceType, IGameNetwork +{ + [ServiceManager.ServiceDependency] + private readonly GameNetwork gameNetworkService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal GameNetworkPluginScoped() + { + this.gameNetworkService.NetworkMessage += this.NetworkMessageForward; + } + + /// + public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage; + + /// + public void Dispose() + { + this.gameNetworkService.NetworkMessage -= this.NetworkMessageForward; + + this.NetworkMessage = null; + } + + private void NetworkMessageForward(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction) + => this.NetworkMessage?.Invoke(dataPtr, opCode, sourceActorId, targetActorId, direction); +} diff --git a/Dalamud/Game/Network/GameNetworkAddressResolver.cs b/Dalamud/Game/Network/GameNetworkAddressResolver.cs index c698ee813..fa6af8c93 100644 --- a/Dalamud/Game/Network/GameNetworkAddressResolver.cs +++ b/Dalamud/Game/Network/GameNetworkAddressResolver.cs @@ -1,11 +1,9 @@ -using System; - namespace Dalamud.Game.Network; /// /// The address resolver for the class. /// -public sealed class GameNetworkAddressResolver : BaseAddressResolver +internal sealed class GameNetworkAddressResolver : BaseAddressResolver { /// /// Gets the address of the ProcessZonePacketDown method. diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 1ccf6c6d5..77bf99c1b 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -44,7 +44,7 @@ internal class NetworkHandlers : IDisposable, IServiceType private NetworkHandlers(GameNetwork gameNetwork) { this.uploader = new UniversalisMarketBoardUploader(); - this.CfPop = (_, _) => { }; + this.CfPop = _ => { }; this.messages = Observable.Create(observer => { @@ -75,7 +75,7 @@ internal class NetworkHandlers : IDisposable, IServiceType /// /// Event which gets fired when a duty is ready. /// - public event EventHandler CfPop; + public event Action CfPop; /// /// Disposes of managed and unmanaged resources. @@ -430,7 +430,7 @@ internal class NetworkHandlers : IDisposable, IServiceType Service.GetNullable()?.Print($"Duty pop: {cfcName}"); } - this.CfPop.InvokeSafely(this, cfCondition); + this.CfPop.InvokeSafely(cfCondition); }).ContinueWith( task => Log.Error(task.Exception, "CfPop.Invoke failed"), TaskContinuationOptions.OnlyOnFaulted); diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs index b5fe0b5b3..fe2d9083e 100644 --- a/Dalamud/Game/SigScanner.cs +++ b/Dalamud/Game/SigScanner.cs @@ -20,12 +20,7 @@ namespace Dalamud.Game; /// /// A SigScanner facilitates searching for memory signatures in a given ProcessModule. /// -[PluginInterface] -[InterfaceVersion("1.0")] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public class SigScanner : IDisposable, IServiceType, ISigScanner +public class SigScanner : IDisposable, ISigScanner { private readonly FileInfo? cacheFile; diff --git a/Dalamud/Game/TargetSigScanner.cs b/Dalamud/Game/TargetSigScanner.cs new file mode 100644 index 000000000..9242c5e83 --- /dev/null +++ b/Dalamud/Game/TargetSigScanner.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using System.IO; + +using Dalamud.IoC; +using Dalamud.IoC.Internal; + +namespace Dalamud.Game; + +/// +/// A SigScanner facilitates searching for memory signatures in a given ProcessModule. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.Service] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class TargetSigScanner : SigScanner, IServiceType +{ + /// + /// Initializes a new instance of the class. + /// + /// Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks. + /// File used to cached signatures. + public TargetSigScanner(bool doCopy = false, FileInfo? cacheFile = null) + : base(Process.GetCurrentProcess().MainModule!, doCopy, cacheFile) + { + } +} diff --git a/Dalamud/Game/Text/SeStringHandling/Payload.cs b/Dalamud/Game/Text/SeStringHandling/Payload.cs index 117606a7a..ff7332f12 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payload.cs @@ -5,6 +5,7 @@ using System.IO; using Dalamud.Data; using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Plugin.Services; using Newtonsoft.Json; using Serilog; @@ -27,12 +28,6 @@ public abstract partial class Payload // To force-invalidate it, Dirty can be set to true private byte[] encodedData; - /// - /// Gets the Lumina instance to use for any necessary data lookups. - /// - [JsonIgnore] - public DataManager DataResolver => Service.Get(); - /// /// Gets the type of this payload. /// @@ -43,6 +38,13 @@ public abstract partial class Payload /// public bool Dirty { get; protected set; } = true; + /// + /// Gets the Lumina instance to use for any necessary data lookups. + /// + [JsonIgnore] + // TODO: We should refactor this. It should not be possible to get IDataManager through here. + protected IDataManager DataResolver => Service.Get(); + /// /// Decodes a binary representation of a payload into its corresponding nice object payload. /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs index 50945a7ce..667b52e36 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs @@ -130,7 +130,13 @@ public class MapLinkPayload : Payload var y = Math.Truncate((this.YCoord + fudge) * 10.0f) / 10.0f; // the formatting and spacing the game uses - return $"( {x:0.0} , {y:0.0} )"; + var clientState = Service.Get(); + return clientState.ClientLanguage switch + { + ClientLanguage.German => $"( {x:0.0}, {y:0.0} )", + ClientLanguage.Japanese => $"({x:0.0}, {y:0.0})", + _ => $"( {x:0.0} , {y:0.0} )", + }; } } diff --git a/Dalamud/Game/Text/SeStringHandling/SeString.cs b/Dalamud/Game/Text/SeStringHandling/SeString.cs index 6d0c8b0fb..6132d0910 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeString.cs @@ -52,14 +52,27 @@ public class SeString /// with the appropriate glow and coloring. /// /// A list of all the payloads required to insert the link marker. - public static IEnumerable TextArrowPayloads => new List(new Payload[] + public static IEnumerable TextArrowPayloads { - new UIForegroundPayload(0x01F4), - new UIGlowPayload(0x01F5), - new TextPayload($"{(char)SeIconChar.LinkMarker}"), - UIGlowPayload.UIGlowOff, - UIForegroundPayload.UIForegroundOff, - }); + get + { + var clientState = Service.Get(); + var markerSpace = clientState.ClientLanguage switch + { + ClientLanguage.German => " ", + ClientLanguage.French => " ", + _ => string.Empty, + }; + return new List + { + new UIForegroundPayload(500), + new UIGlowPayload(501), + new TextPayload($"{(char)SeIconChar.LinkMarker}{markerSpace}"), + UIGlowPayload.UIGlowOff, + UIForegroundPayload.UIForegroundOff, + }; + } + } /// /// Gets an empty SeString. @@ -171,6 +184,7 @@ public class SeString var data = Service.Get(); var displayName = displayNameOverride; + var rarity = 1; // default: white if (displayName == null) { switch (kind) @@ -178,7 +192,9 @@ public class SeString case ItemPayload.ItemKind.Normal: case ItemPayload.ItemKind.Collectible: case ItemPayload.ItemKind.Hq: - displayName = data.GetExcelSheet()?.GetRow(itemId)?.Name; + var item = data.GetExcelSheet()?.GetRow(itemId); + displayName = item?.Name; + rarity = item?.Rarity ?? 1; break; case ItemPayload.ItemKind.EventItem: displayName = data.GetExcelSheet()?.GetRow(itemId)?.Name; @@ -202,21 +218,20 @@ public class SeString displayName += $" {(char)SeIconChar.Collectible}"; } - // TODO: probably a cleaner way to build these than doing the bulk+insert - var payloads = new List(new Payload[] - { - new UIForegroundPayload(0x0225), - new UIGlowPayload(0x0226), - new ItemPayload(itemId, kind), - // arrow goes here - new TextPayload(displayName), - RawPayload.LinkTerminator, - // sometimes there is another set of uiglow/foreground off payloads here - // might be necessary when including additional text after the item name - }); - payloads.InsertRange(3, TextArrowPayloads); + var textColor = (ushort)(549 + ((rarity - 1) * 2)); + var textGlowColor = (ushort)(textColor + 1); - return new SeString(payloads); + // Note: `SeStringBuilder.AddItemLink` uses this function, so don't call it here! + return new SeStringBuilder() + .AddUiForeground(textColor) + .AddUiGlow(textGlowColor) + .Add(new ItemPayload(itemId, kind)) + .Append(TextArrowPayloads) + .AddText(displayName) + .AddUiGlowOff() + .AddUiForegroundOff() + .Add(RawPayload.LinkTerminator) + .Build(); } /// @@ -406,7 +421,7 @@ public class SeString /// /// The Payloads to append. /// This object. - public SeString Append(List payloads) + public SeString Append(IEnumerable payloads) { this.Payloads.AddRange(payloads); return this; diff --git a/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs b/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs index 36bb10a2d..dae9e11a9 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs @@ -1,3 +1,6 @@ +using System.Collections.Generic; +using System.Linq; + using Dalamud.Game.Text.SeStringHandling.Payloads; namespace Dalamud.Game.Text.SeStringHandling; @@ -30,6 +33,17 @@ public class SeStringBuilder /// The current builder. public SeStringBuilder Append(string text) => this.AddText(text); + /// + /// Append payloads to the builder. + /// + /// A list of payloads. + /// The current builder. + public SeStringBuilder Append(IEnumerable payloads) + { + this.BuiltString.Payloads.AddRange(payloads); + return this; + } + /// /// Append raw text to the builder. /// @@ -104,7 +118,7 @@ public class SeStringBuilder /// Override for the item's name. /// The current builder. public SeStringBuilder AddItemLink(uint itemId, bool isHq, string? itemNameOverride = null) => - this.Add(new ItemPayload(itemId, isHq, itemNameOverride)); + this.Append(SeString.CreateItemLink(itemId, isHq, itemNameOverride)); /// /// Add an item link to the builder. @@ -113,14 +127,15 @@ public class SeStringBuilder /// Kind of item to encode. /// Override for the item's name. /// The current builder. - public SeStringBuilder AddItemLink(uint itemId, ItemPayload.ItemKind kind, string? itemNameOverride = null) => - this.Add(new ItemPayload(itemId, kind, itemNameOverride)); + public SeStringBuilder AddItemLink(uint itemId, ItemPayload.ItemKind kind = ItemPayload.ItemKind.Normal, string? itemNameOverride = null) => + this.Append(SeString.CreateItemLink(itemId, kind, itemNameOverride)); /// /// Add an item link to the builder. /// /// The raw item ID. /// The current builder. + /// To terminate this item link, add a . public SeStringBuilder AddItemLinkRaw(uint rawItemId) => this.Add(ItemPayload.FromRaw(rawItemId)); diff --git a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs b/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs deleted file mode 100644 index f0b38d429..000000000 --- a/Dalamud/Game/Text/SeStringHandling/SeStringManager.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; - -using Dalamud.IoC; -using Dalamud.IoC.Internal; -using Lumina.Excel.GeneratedSheets; - -namespace Dalamud.Game.Text.SeStringHandling; - -/// -/// This class facilitates creating new SeStrings and breaking down existing ones into their individual payload components. -/// -[PluginInterface] -[InterfaceVersion("1.0")] -[ServiceManager.BlockingEarlyLoadedService] -[Obsolete("This class is obsolete. Please use the static methods on SeString instead.")] -public sealed class SeStringManager : IServiceType -{ - [ServiceManager.ServiceConstructor] - private SeStringManager() - { - } - - /// - /// Parse a binary game message into an SeString. - /// - /// Pointer to the string's data in memory. - /// Length of the string's data in memory. - /// An SeString containing parsed Payload objects for each payload in the data. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public unsafe SeString Parse(byte* ptr, int len) => SeString.Parse(ptr, len); - - /// - /// Parse a binary game message into an SeString. - /// - /// Binary message payload data in SE's internal format. - /// An SeString containing parsed Payload objects for each payload in the data. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public unsafe SeString Parse(ReadOnlySpan data) => SeString.Parse(data); - - /// - /// Parse a binary game message into an SeString. - /// - /// Binary message payload data in SE's internal format. - /// An SeString containing parsed Payload objects for each payload in the data. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString Parse(byte[] bytes) => SeString.Parse(new ReadOnlySpan(bytes)); - - /// - /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. - /// - /// The id of the item to link. - /// Whether to link the high-quality variant of the item. - /// An optional name override to display, instead of the actual item name. - /// An SeString containing all the payloads necessary to display an item link in the chat log. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString CreateItemLink(uint itemId, bool isHQ, string displayNameOverride = null) => SeString.CreateItemLink(itemId, isHQ, displayNameOverride); - - /// - /// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log. - /// - /// The Lumina Item to link. - /// Whether to link the high-quality variant of the item. - /// An optional name override to display, instead of the actual item name. - /// An SeString containing all the payloads necessary to display an item link in the chat log. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null) => SeString.CreateItemLink(item, isHQ, displayNameOverride); - - /// - /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. - /// - /// The id of the TerritoryType for this map link. - /// The id of the Map for this map link. - /// The raw x-coordinate for this link. - /// The raw y-coordinate for this link.. - /// An SeString containing all of the payloads necessary to display a map link in the chat log. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString CreateMapLink(uint territoryId, uint mapId, int rawX, int rawY) => - SeString.CreateMapLink(territoryId, mapId, rawX, rawY); - - /// - /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log. - /// - /// The id of the TerritoryType for this map link. - /// The id of the Map for this map link. - /// The human-readable x-coordinate for this link. - /// The human-readable y-coordinate for this link. - /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. - /// An SeString containing all of the payloads necessary to display a map link in the chat log. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString CreateMapLink(uint territoryId, uint mapId, float xCoord, float yCoord, float fudgeFactor = 0.05f) => SeString.CreateMapLink(territoryId, mapId, xCoord, yCoord, fudgeFactor); - - /// - /// Creates an SeString representing an entire Payload chain that can be used to link a map position in the chat log, matching a specified zone name. - /// - /// The name of the location for this link. This should be exactly the name as seen in a displayed map link in-game for the same zone. - /// The human-readable x-coordinate for this link. - /// The human-readable y-coordinate for this link. - /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases. - /// An SeString containing all of the payloads necessary to display a map link in the chat log. - [Obsolete("This method is obsolete. Please use the static methods on SeString instead.", true)] - public SeString CreateMapLink(string placeName, float xCoord, float yCoord, float fudgeFactor = 0.05f) => SeString.CreateMapLink(placeName, xCoord, yCoord, fudgeFactor); - - /// - /// Creates a list of Payloads necessary to display the arrow link marker icon in chat - /// with the appropriate glow and coloring. - /// - /// A list of all the payloads required to insert the link marker. - [Obsolete("This data is obsolete. Please use the static version on SeString instead.", true)] - public List TextArrowPayloads() => new(SeString.TextArrowPayloads); -} diff --git a/Dalamud/GlobalSuppressions.cs b/Dalamud/GlobalSuppressions.cs index 7426ed5c8..1b869295b 100644 --- a/Dalamud/GlobalSuppressions.cs +++ b/Dalamud/GlobalSuppressions.cs @@ -15,3 +15,22 @@ using System.Diagnostics.CodeAnalysis; [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")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:ParametersMustBeOnSameLineOrSeparateLines", Justification = "I don't care anymore")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:ParametersMustBeOnSameLineOrSeparateLines", Justification = "I don't care anymore")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1407:ArithmeticExpressionsMustDeclarePrecedence", Justification = "I don't care anymore")] +[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1116:SplitParametersMustStartOnLineAfterDeclaration", Justification = "Reviewed.")] + +// ImRAII stuff +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:PartialElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Table")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:PartialElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Table")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.", Scope = "type", Target = "Dalamud.Interface.Utility.ImGuiClip")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:PartialElementsMustBeDocumented", Justification = "Reviewed.", Scope = "type", Target = "Dalamud.Interface.Utility.ImGuiClip")] +[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:CodeMustNotContainMultipleWhitespaceInARow", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Table")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Table")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] +[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Table")] diff --git a/Dalamud/GlobalUsings.cs b/Dalamud/GlobalUsings.cs new file mode 100644 index 000000000..062a3f981 --- /dev/null +++ b/Dalamud/GlobalUsings.cs @@ -0,0 +1 @@ +global using System; diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs index 74b9e6384..da65fedc7 100644 --- a/Dalamud/Hooking/Hook.cs +++ b/Dalamud/Hooking/Hook.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -13,7 +12,7 @@ namespace Dalamud.Hooking; /// This class is basically a thin wrapper around the LocalHook type to provide helper functions. /// /// Delegate type to represents a function prototype. This must be the same prototype as original function do. -public class Hook : IDisposable, IDalamudHook where T : Delegate +public abstract class Hook : IDalamudHook where T : Delegate { #pragma warning disable SA1310 // ReSharper disable once InconsistentNaming @@ -24,34 +23,6 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate private readonly IntPtr address; - private readonly Hook? compatHookImpl; - - /// - /// Initializes a new instance of the class. - /// Hook is not activated until Enable() method is called. - /// - /// A memory address to install a hook. - /// Callback function. Delegate must have a same original function prototype. - [Obsolete("Use Hook.FromAddress instead.")] - public Hook(IntPtr address, T detour) - : this(address, detour, false, Assembly.GetCallingAssembly()) - { - } - - /// - /// Initializes a new instance of the class. - /// Hook is not activated until Enable() method is called. - /// Please do not use MinHook unless you have thoroughly troubleshot why Reloaded does not work. - /// - /// A memory address to install a hook. - /// Callback function. Delegate must have a same original function prototype. - /// Use the MinHook hooking library instead of Reloaded. - [Obsolete("Use Hook.FromAddress instead.")] - public Hook(IntPtr address, T detour, bool useMinHook) - : this(address, detour, useMinHook, Assembly.GetCallingAssembly()) - { - } - /// /// Initializes a new instance of the class. /// @@ -61,19 +32,6 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate this.address = address; } - [Obsolete("Use Hook.FromAddress instead.")] - private Hook(IntPtr address, T detour, bool useMinHook, Assembly callingAssembly) - { - if (EnvironmentConfiguration.DalamudForceMinHook) - useMinHook = true; - - this.address = address = HookManager.FollowJmp(address); - if (useMinHook) - this.compatHookImpl = new MinHookHook(address, detour, callingAssembly); - else - this.compatHookImpl = new ReloadedHook(address, detour, callingAssembly); - } - /// /// Gets a memory address of the target function. /// @@ -91,28 +49,19 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// Gets a delegate function that can be used to call the actual function as if function is not hooked yet. /// /// Hook is already disposed. - public virtual T Original => this.compatHookImpl != null ? this.compatHookImpl!.Original : throw new NotImplementedException(); + public virtual T Original => throw new NotImplementedException(); /// /// Gets a delegate function that can be used to call the actual function as if function is not hooked yet. /// This can be called even after Dispose. /// public T OriginalDisposeSafe - { - get - { - if (this.compatHookImpl != null) - return this.compatHookImpl!.OriginalDisposeSafe; - if (this.IsDisposed) - return Marshal.GetDelegateForFunctionPointer(this.address); - return this.Original; - } - } + => this.IsDisposed ? Marshal.GetDelegateForFunctionPointer(this.address) : this.Original; /// /// Gets a value indicating whether or not the hook is enabled. /// - public virtual bool IsEnabled => this.compatHookImpl != null ? this.compatHookImpl!.IsEnabled : throw new NotImplementedException(); + public virtual bool IsEnabled => throw new NotImplementedException(); /// /// Gets a value indicating whether or not the hook has been disposed. @@ -120,7 +69,28 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate public bool IsDisposed { get; private set; } /// - public virtual string BackendName => this.compatHookImpl != null ? this.compatHookImpl!.BackendName : throw new NotImplementedException(); + public virtual string BackendName => throw new NotImplementedException(); + + /// + /// Remove a hook from the current process. + /// + public virtual void Dispose() + { + if (this.IsDisposed) + return; + + this.IsDisposed = true; + } + + /// + /// Starts intercepting a call to the function. + /// + public virtual void Enable() => throw new NotImplementedException(); + + /// + /// Stops intercepting a call to the function. + /// + public virtual void Disable() => throw new NotImplementedException(); /// /// Creates a hook by rewriting import table address. @@ -128,7 +98,7 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// A memory address to install a hook. /// Callback function. Delegate must have a same original function prototype. /// The hook with the supplied parameters. - public static unsafe Hook FromFunctionPointerVariable(IntPtr address, T detour) + internal static Hook FromFunctionPointerVariable(IntPtr address, T detour) { return new FunctionPointerVariableHook(address, detour, Assembly.GetCallingAssembly()); } @@ -142,7 +112,7 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// Hint or ordinal. 0 to unspecify. /// Callback function. Delegate must have a same original function prototype. /// The hook with the supplied parameters. - public static unsafe Hook FromImport(ProcessModule? module, string moduleName, string functionName, uint hintOrOrdinal, T detour) + internal static unsafe Hook FromImport(ProcessModule? module, string moduleName, string functionName, uint hintOrOrdinal, T detour) { module ??= Process.GetCurrentProcess().MainModule; if (module == null) @@ -207,7 +177,7 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// A name of the exported function name (e.g. send). /// Callback function. Delegate must have a same original function prototype. /// The hook with the supplied parameters. - public static Hook FromSymbol(string moduleName, string exportName, T detour) + internal static Hook FromSymbol(string moduleName, string exportName, T detour) => FromSymbol(moduleName, exportName, detour, false); /// @@ -220,7 +190,7 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// Callback function. Delegate must have a same original function prototype. /// Use the MinHook hooking library instead of Reloaded. /// The hook with the supplied parameters. - public static Hook FromSymbol(string moduleName, string exportName, T detour, bool useMinHook) + internal static Hook FromSymbol(string moduleName, string exportName, T detour, bool useMinHook) { if (EnvironmentConfiguration.DalamudForceMinHook) useMinHook = true; @@ -249,7 +219,7 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate /// Callback function. Delegate must have a same original function prototype. /// Use the MinHook hooking library instead of Reloaded. /// The hook with the supplied parameters. - public static Hook FromAddress(IntPtr procAddress, T detour, bool useMinHook = false) + internal static Hook FromAddress(IntPtr procAddress, T detour, bool useMinHook = false) { if (EnvironmentConfiguration.DalamudForceMinHook) useMinHook = true; @@ -261,41 +231,6 @@ public class Hook : IDisposable, IDalamudHook where T : Delegate return new ReloadedHook(procAddress, detour, Assembly.GetCallingAssembly()); } - /// - /// Remove a hook from the current process. - /// - public virtual void Dispose() - { - if (this.IsDisposed) - return; - - this.compatHookImpl?.Dispose(); - - this.IsDisposed = true; - } - - /// - /// Starts intercepting a call to the function. - /// - public virtual void Enable() - { - if (this.compatHookImpl != null) - this.compatHookImpl.Enable(); - else - throw new NotImplementedException(); - } - - /// - /// Stops intercepting a call to the function. - /// - public virtual void Disable() - { - if (this.compatHookImpl != null) - this.compatHookImpl.Disable(); - else - throw new NotImplementedException(); - } - /// /// Check if this object has been disposed already. /// diff --git a/Dalamud/Hooking/IDalamudHook.cs b/Dalamud/Hooking/IDalamudHook.cs index 1104597a1..bd7084d86 100644 --- a/Dalamud/Hooking/IDalamudHook.cs +++ b/Dalamud/Hooking/IDalamudHook.cs @@ -5,7 +5,7 @@ namespace Dalamud.Hooking; /// /// Interface describing a generic hook. /// -public interface IDalamudHook +public interface IDalamudHook : IDisposable { /// /// Gets the address to hook. diff --git a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs new file mode 100644 index 000000000..59f2d2684 --- /dev/null +++ b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs @@ -0,0 +1,99 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Linq; + +using Dalamud.Game; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Internal.Types; +using Dalamud.Plugin.Services; +using Dalamud.Utility.Signatures; +using Serilog; + +namespace Dalamud.Hooking.Internal; + +/// +/// Plugin-scoped version of service used to create hooks. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class GameInteropProviderPluginScoped : IGameInteropProvider, IServiceType, IDisposable +{ + private readonly LocalPlugin plugin; + private readonly SigScanner scanner; + + private readonly ConcurrentBag trackedHooks = new(); + + /// + /// Initializes a new instance of the class. + /// + /// Plugin this instance belongs to. + /// SigScanner instance for target module. + public GameInteropProviderPluginScoped(LocalPlugin plugin, TargetSigScanner scanner) + { + this.plugin = plugin; + this.scanner = scanner; + } + + /// + public void InitializeFromAttributes(object self) + { + foreach (var hook in SignatureHelper.Initialize(self)) + this.trackedHooks.Add(hook); + } + + /// + public Hook HookFromFunctionPointerVariable(IntPtr address, T detour) where T : Delegate + { + var hook = Hook.FromFunctionPointerVariable(address, detour); + this.trackedHooks.Add(hook); + return hook; + } + + /// + public Hook HookFromImport(ProcessModule? module, string moduleName, string functionName, uint hintOrOrdinal, T detour) where T : Delegate + { + var hook = Hook.FromImport(module, moduleName, functionName, hintOrOrdinal, detour); + this.trackedHooks.Add(hook); + return hook; + } + + /// + public Hook HookFromSymbol(string moduleName, string exportName, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate + { + var hook = Hook.FromSymbol(moduleName, exportName, detour, backend == IGameInteropProvider.HookBackend.MinHook); + this.trackedHooks.Add(hook); + return hook; + } + + /// + public Hook HookFromAddress(IntPtr procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate + { + var hook = Hook.FromAddress(procAddress, detour, backend == IGameInteropProvider.HookBackend.MinHook); + this.trackedHooks.Add(hook); + return hook; + } + + /// + public Hook HookFromSignature(string signature, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate + => this.HookFromAddress(this.scanner.ScanText(signature), detour, backend); + + /// + public void Dispose() + { + var notDisposed = this.trackedHooks.Where(x => !x.IsDisposed).ToArray(); + if (notDisposed.Length != 0) + Log.Warning("{PluginName} is leaking {Num} hooks! Make sure that all of them are disposed properly.", this.plugin.InternalName, notDisposed.Length); + + foreach (var hook in notDisposed) + { + hook.Dispose(); + } + + this.trackedHooks.Clear(); + } +} diff --git a/Dalamud/Interface/ColorHelpers.cs b/Dalamud/Interface/ColorHelpers.cs index 0239ad754..b2b489004 100644 --- a/Dalamud/Interface/ColorHelpers.cs +++ b/Dalamud/Interface/ColorHelpers.cs @@ -1,4 +1,4 @@ -using System; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Numerics; @@ -9,6 +9,17 @@ namespace Dalamud.Interface; /// public static class ColorHelpers { + /// + /// A struct representing a color using HSVA coordinates. + /// + /// The hue represented by this struct. + /// The saturation represented by this struct. + /// The value represented by this struct. + /// The alpha represented by this struct. + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", + Justification = "I don't like it.")] + public record struct HsvaColor(float H, float S, float V, float A); + /// /// Pack a vector4 color into a uint for use in ImGui APIs. /// @@ -23,7 +34,7 @@ public static class ColorHelpers return (uint)((a << 24) | (b << 16) | (g << 8) | r); } - + /// /// Convert a RGBA color in the range of 0.f to 1.f to a uint. /// @@ -38,7 +49,7 @@ public static class ColorHelpers return new Vector4(r, g, b, a); } - + /// /// Convert a RGBA color in the range of 0.f to 1.f to a HSV color. /// @@ -147,7 +158,7 @@ public static class ColorHelpers return new Vector4(r, g, b, hsv.A); } - + /// /// Lighten a color. /// @@ -160,7 +171,7 @@ public static class ColorHelpers hsv.V += amount; return HsvToRgb(hsv); } - + /// /// Lighten a color. /// @@ -169,7 +180,7 @@ public static class ColorHelpers /// The lightened color. public static uint Lighten(uint color, float amount) => RgbaVector4ToUint(Lighten(RgbaUintToVector4(color), amount)); - + /// /// Darken a color. /// @@ -182,7 +193,7 @@ public static class ColorHelpers hsv.V -= amount; return HsvToRgb(hsv); } - + /// /// Darken a color. /// @@ -191,7 +202,7 @@ public static class ColorHelpers /// The darkened color. public static uint Darken(uint color, float amount) => RgbaVector4ToUint(Darken(RgbaUintToVector4(color), amount)); - + /// /// Saturate a color. /// @@ -204,7 +215,7 @@ public static class ColorHelpers hsv.S += amount; return HsvToRgb(hsv); } - + /// /// Saturate a color. /// @@ -213,7 +224,7 @@ public static class ColorHelpers /// The saturated color. public static uint Saturate(uint color, float amount) => RgbaVector4ToUint(Saturate(RgbaUintToVector4(color), amount)); - + /// /// Desaturate a color. /// @@ -226,7 +237,7 @@ public static class ColorHelpers hsv.S -= amount; return HsvToRgb(hsv); } - + /// /// Desaturate a color. /// @@ -235,7 +246,7 @@ public static class ColorHelpers /// The desaturated color. public static uint Desaturate(uint color, float amount) => RgbaVector4ToUint(Desaturate(RgbaUintToVector4(color), amount)); - + /// /// Fade a color. /// @@ -248,7 +259,7 @@ public static class ColorHelpers hsv.A -= amount; return HsvToRgb(hsv); } - + /// /// Fade a color. /// @@ -259,7 +270,7 @@ public static class ColorHelpers => RgbaVector4ToUint(Fade(RgbaUintToVector4(color), amount)); /// - /// Convert a KnownColor to a RGBA vector with values between 0.0f and 1.0f + /// Convert a KnownColor to a RGBA vector with values between 0.0f and 1.0f. /// /// Known Color to convert. /// RGBA Vector with values between 0.0f and 1.0f. @@ -285,6 +296,4 @@ public static class ColorHelpers _ => color / 255.0f, }; - - public record struct HsvaColor(float H, float S, float V, float A); } diff --git a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs index e9db345cb..aa707aecb 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs @@ -1,5 +1,6 @@ using System.Numerics; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Components; diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs index 05e660b61..116b04bd2 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Components; @@ -109,11 +110,32 @@ public static partial class ImGuiComponents numColors++; } + var icon = iconText; + if (icon.Contains("#")) + icon = icon[..icon.IndexOf("#", StringComparison.Ordinal)]; + + ImGui.PushID(iconText); + ImGui.PushFont(UiBuilder.IconFont); - - var button = ImGui.Button(iconText); - + var iconSize = ImGui.CalcTextSize(icon); ImGui.PopFont(); + + var dl = ImGui.GetWindowDrawList(); + var cursor = ImGui.GetCursorScreenPos(); + + // Draw an ImGui button with the icon and text + var buttonWidth = iconSize.X + (ImGui.GetStyle().FramePadding.X * 2); + var buttonHeight = ImGui.GetFrameHeight(); + var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); + + // Draw the icon on the window drawlist + var iconPos = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X, cursor.Y + ImGui.GetStyle().FramePadding.Y); + + ImGui.PushFont(UiBuilder.IconFont); + dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon); + ImGui.PopFont(); + + ImGui.PopID(); if (numColors > 0) ImGui.PopStyleColor(numColors); @@ -166,7 +188,7 @@ public static partial class ImGuiComponents // Draw an ImGui button with the icon and text var buttonWidth = iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; - var buttonHeight = Math.Max(iconSize.Y, textSize.Y) + (ImGui.GetStyle().FramePadding.Y * 2); + var buttonHeight = ImGui.GetFrameHeight(); var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); // Draw the icon on the window drawlist diff --git a/Dalamud/Interface/DragDrop/DragDropInterop.cs b/Dalamud/Interface/DragDrop/DragDropInterop.cs index 68418d4b0..6a7043861 100644 --- a/Dalamud/Interface/DragDrop/DragDropInterop.cs +++ b/Dalamud/Interface/DragDrop/DragDropInterop.cs @@ -34,9 +34,9 @@ internal partial class DragDropManager internal struct POINTL { [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LONG")] - public int x; + public int X; [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LONG")] - public int y; + public int Y; } private static class DragDropInterop diff --git a/Dalamud/Interface/DragDrop/DragDropManager.cs b/Dalamud/Interface/DragDrop/DragDropManager.cs index 8336edc11..e8641035f 100644 --- a/Dalamud/Interface/DragDrop/DragDropManager.cs +++ b/Dalamud/Interface/DragDrop/DragDropManager.cs @@ -16,7 +16,9 @@ namespace Dalamud.Interface.DragDrop; /// [PluginInterface] [ServiceManager.EarlyLoadedService] +#pragma warning disable SA1015 [ResolveVia] +#pragma warning restore SA1015 internal partial class DragDropManager : IDisposable, IDragDropManager, IServiceType { private nint windowHandlePtr = nint.Zero; diff --git a/Dalamud/Interface/DragDrop/DragDropTarget.cs b/Dalamud/Interface/DragDrop/DragDropTarget.cs index 01a48173a..bbd7a5061 100644 --- a/Dalamud/Interface/DragDrop/DragDropTarget.cs +++ b/Dalamud/Interface/DragDrop/DragDropTarget.cs @@ -51,7 +51,7 @@ internal partial class DragDropManager : DragDropManager.IDropTarget this.Extensions = this.Files.Select(Path.GetExtension).Where(p => !p.IsNullOrEmpty()).Distinct().ToHashSet(); } - Log.Debug("[DragDrop] Entering external Drag and Drop with {KeyState} at {PtX}, {PtY} and with {N} files.", (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y, this.Files.Count + this.Directories.Count); + Log.Debug("[DragDrop] Entering external Drag and Drop with {KeyState} at {PtX}, {PtY} and with {N} files.", (DragDropInterop.ModifierKeys)grfKeyState, pt.X, pt.Y, this.Files.Count + this.Directories.Count); } /// Invoked every windows update-frame as long as the drag and drop process keeps hovering over an FFXIV-related viewport. @@ -67,7 +67,7 @@ internal partial class DragDropManager : DragDropManager.IDropTarget this.lastUpdateFrame = frame; this.lastKeyState = UpdateIo((DragDropInterop.ModifierKeys)grfKeyState, false); pdwEffect &= (uint)DragDropInterop.DropEffects.Copy; - Log.Verbose("[DragDrop] External Drag and Drop with {KeyState} at {PtX}, {PtY}.", (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y); + Log.Verbose("[DragDrop] External Drag and Drop with {KeyState} at {PtX}, {PtY}.", (DragDropInterop.ModifierKeys)grfKeyState, pt.X, pt.Y); } } @@ -101,7 +101,7 @@ internal partial class DragDropManager : DragDropManager.IDropTarget pdwEffect = 0; } - Log.Debug("[DragDrop] Dropping {N} files with {KeyState} at {PtX}, {PtY}.", this.Files.Count + this.Directories.Count, (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y); + Log.Debug("[DragDrop] Dropping {N} files with {KeyState} at {PtX}, {PtY}.", this.Files.Count + this.Directories.Count, (DragDropInterop.ModifierKeys)grfKeyState, pt.X, pt.Y); } private static DragDropInterop.ModifierKeys UpdateIo(DragDropInterop.ModifierKeys keys, bool entering) diff --git a/Dalamud/Interface/DragDrop/IDragDropManager.cs b/Dalamud/Interface/DragDrop/IDragDropManager.cs index 736c8af24..a8a0d63b0 100644 --- a/Dalamud/Interface/DragDrop/IDragDropManager.cs +++ b/Dalamud/Interface/DragDrop/IDragDropManager.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace Dalamud.Interface.DragDrop; @@ -23,20 +22,20 @@ public interface IDragDropManager /// Gets the list of directories currently being dragged from an external application over any of the games viewports. public IReadOnlyList Directories { get; } - /// Create an ImGui drag & drop source that is active only if anything is being dragged from an external source. - /// The label used for the drag & drop payload. + /// Create an ImGui drag and drop source that is active only if anything is being dragged from an external source. + /// The label used for the drag and drop payload. /// A function returning whether the current status is relevant for this source. Checked before creating the source but only if something is being dragged. public void CreateImGuiSource(string label, Func validityCheck) => this.CreateImGuiSource(label, validityCheck, _ => false); - /// Create an ImGui drag & drop source that is active only if anything is being dragged from an external source. - /// The label used for the drag & drop payload. + /// Create an ImGui drag and drop source that is active only if anything is being dragged from an external source. + /// The label used for the drag and drop payload. /// A function returning whether the current status is relevant for this source. Checked before creating the source but only if something is being dragged. /// Executes ImGui functions to build a tooltip. Should return true if it creates any tooltip and false otherwise. If multiple sources are active, only the first non-empty tooltip type drawn in a frame will be used. public void CreateImGuiSource(string label, Func validityCheck, Func tooltipBuilder); - /// Create an ImGui drag & drop target on the last ImGui object. - /// The label used for the drag & drop payload. + /// Create an ImGui drag and drop target on the last ImGui object. + /// The label used for the drag and drop payload. /// On success, contains the list of file paths dropped onto the target. /// On success, contains the list of directory paths dropped onto the target. /// True if items were dropped onto the target this frame, false otherwise. diff --git a/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs b/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs index 368ca55fe..f88d7f8f0 100644 --- a/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs +++ b/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // Generated by Dalamud.FASharpGen - don't modify this file directly. -// Font-Awesome Version: 6.3.0 +// Font-Awesome Version: 6.4.2 // //------------------------------------------------------------------------------ @@ -19,34 +19,6 @@ public enum FontAwesomeIcon /// None = 0, - /// - /// The Font Awesome "500px" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "500px" })] - _500Px = 0xF26E, - - /// - /// The Font Awesome "accessible-icon" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "accessible icon", "accessibility", "handicap", "person", "wheelchair", "wheelchair-alt" })] - [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Medical + Health", "Transportation", "Users + People" })] - AccessibleIcon = 0xF368, - - /// - /// The Font Awesome "accusoft" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "accusoft" })] - Accusoft = 0xF369, - - /// - /// The Font Awesome "acquisitionsincorporated" icon unicode character. - /// - [Obsolete] - AcquisitionsIncorporated = 0xF6AF, - /// /// The Font Awesome "rectangle-ad" icon unicode character. /// @@ -65,7 +37,7 @@ public enum FontAwesomeIcon /// The Font Awesome "address-card" icon unicode character. /// [FontAwesomeSearchTerms(new[] { "address card", "about", "contact", "id", "identification", "postcard", "profile", "registration" })] - [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Users + People" })] + [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Alphabet", "Business", "Communication", "Users + People" })] AddressCard = 0xF2BB, /// @@ -75,40 +47,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams", "Design", "Editing", "Photos + Images", "Shapes" })] Adjust = 0xF042, - /// - /// The Font Awesome "adn" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "adn" })] - Adn = 0xF170, - - /// - /// The Font Awesome "adobe" icon unicode character. - /// - [Obsolete] - Adobe = 0xF778, - - /// - /// The Font Awesome "adversal" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "adversal" })] - Adversal = 0xF36A, - - /// - /// The Font Awesome "affiliatetheme" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "affiliatetheme" })] - Affiliatetheme = 0xF36B, - - /// - /// The Font Awesome "airbnb" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "airbnb" })] - Airbnb = 0xF834, - /// /// The Font Awesome "spray-can-sparkles" icon unicode character. /// @@ -116,13 +54,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Automotive" })] AirFreshener = 0xF5D0, - /// - /// The Font Awesome "algolia" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "algolia" })] - Algolia = 0xF36C, - /// /// The Font Awesome "align-center" icon unicode character. /// @@ -151,14 +82,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] AlignRight = 0xF038, - /// - /// The Font Awesome "alipay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "alipay" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - Alipay = 0xF642, - /// /// The Font Awesome "hand-dots" icon unicode character. /// @@ -166,21 +89,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Hands", "Medical + Health" })] Allergies = 0xF461, - /// - /// The Font Awesome "amazon" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "amazon" })] - Amazon = 0xF270, - - /// - /// The Font Awesome "amazon-pay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "amazon pay" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - AmazonPay = 0xF42C, - /// /// The Font Awesome "truck-medical" icon unicode character. /// @@ -195,13 +103,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Communication" })] AmericanSignLanguageInterpreting = 0xF2A3, - /// - /// The Font Awesome "amilia" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "amilia" })] - Amilia = 0xF36D, - /// /// The Font Awesome "anchor" icon unicode character. /// @@ -237,20 +138,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime" })] AnchorLock = 0xE4AD, - /// - /// The Font Awesome "android" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "android", "robot" })] - Android = 0xF17B, - - /// - /// The Font Awesome "angellist" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "angellist" })] - Angellist = 0xF209, - /// /// The Font Awesome "angles-down" icon unicode character. /// @@ -314,20 +201,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] Angry = 0xF556, - /// - /// The Font Awesome "angrycreative" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "angrycreative" })] - Angrycreative = 0xF36E, - - /// - /// The Font Awesome "angular" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "angular" })] - Angular = 0xF420, - /// /// The Font Awesome "ankh" icon unicode character. /// @@ -335,20 +208,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion" })] Ankh = 0xF644, - /// - /// The Font Awesome "apper" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "apper" })] - Apper = 0xF371, - - /// - /// The Font Awesome "apple" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "apple", "fruit", "ios", "mac", "operating system", "os", "osx" })] - Apple = 0xF179, - /// /// The Font Awesome "apple-whole" icon unicode character. /// @@ -356,28 +215,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Education", "Food + Beverage", "Fruits + Vegetables" })] AppleAlt = 0xF5D1, - /// - /// The Font Awesome "apple-pay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "apple pay" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - ApplePay = 0xF415, - - /// - /// The Font Awesome "app-store" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "app store" })] - AppStore = 0xF36F, - - /// - /// The Font Awesome "app-store-ios" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "app store ios" })] - AppStoreIos = 0xF370, - /// /// The Font Awesome "box-archive" icon unicode character. /// @@ -728,13 +565,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowUpRightFromSquare = 0xF08E, - /// - /// The Font Awesome "artstation" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "artstation" })] - Artstation = 0xF77A, - /// /// The Font Awesome "ear-listen" icon unicode character. /// @@ -750,13 +580,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Punctuation + Symbols", "Spinners" })] Asterisk = 0xF069, - /// - /// The Font Awesome "asymmetrik" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "asymmetrik" })] - Asymmetrik = 0xF372, - /// /// The Font Awesome "at" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x40. @@ -772,13 +595,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Travel + Hotel" })] Atlas = 0xF558, - /// - /// The Font Awesome "atlassian" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "atlassian" })] - Atlassian = 0xF77B, - /// /// The Font Awesome "atom" icon unicode character. /// @@ -786,13 +602,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Education", "Energy", "Religion", "Science", "Science Fiction", "Spinners" })] Atom = 0xF5D2, - /// - /// The Font Awesome "audible" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "audible" })] - Audible = 0xF373, - /// /// The Font Awesome "audio-description" icon unicode character. /// @@ -807,27 +616,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] AustralSign = 0xE0A9, - /// - /// The Font Awesome "autoprefixer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "autoprefixer" })] - Autoprefixer = 0xF41C, - - /// - /// The Font Awesome "avianex" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "avianex" })] - Avianex = 0xF374, - - /// - /// The Font Awesome "aviato" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "aviato" })] - Aviato = 0xF421, - /// /// The Font Awesome "award" icon unicode character. /// @@ -835,13 +623,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Education", "Political" })] Award = 0xF559, - /// - /// The Font Awesome "aws" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "aws" })] - Aws = 0xF375, - /// /// The Font Awesome "baby" icon unicode character. /// @@ -940,13 +721,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Editing", "Medical + Health" })] BandAid = 0xF462, - /// - /// The Font Awesome "bandcamp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bandcamp" })] - Bandcamp = 0xF2D5, - /// /// The Font Awesome "bangladeshi-taka-sign" icon unicode character. /// @@ -1038,13 +812,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Energy" })] BatteryThreeQuarters = 0xF241, - /// - /// The Font Awesome "battle-net" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "battle net" })] - BattleNet = 0xF835, - /// /// The Font Awesome "bed" icon unicode character. /// @@ -1059,20 +826,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Maps" })] Beer = 0xF0FC, - /// - /// The Font Awesome "behance" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "behance" })] - Behance = 0xF1B4, - - /// - /// The Font Awesome "square-behance" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square behance" })] - BehanceSquare = 0xF1B5, - /// /// The Font Awesome "bell" icon unicode character. /// @@ -1115,18 +868,11 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Sports + Fitness", "Users + People" })] Biking = 0xF84A, - /// - /// The Font Awesome "bimobject" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bimobject" })] - Bimobject = 0xF378, - /// /// The Font Awesome "binoculars" icon unicode character. /// [FontAwesomeSearchTerms(new[] { "binoculars", "glasses", "magnify", "scenic", "spyglass", "view" })] - [FontAwesomeCategoriesAttribute(new[] { "Camping", "Maps", "Nature" })] + [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Camping", "Maps", "Nature" })] Binoculars = 0xF1E5, /// @@ -1143,21 +889,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Childhood", "Food + Beverage", "Maps", "Social" })] BirthdayCake = 0xF1FD, - /// - /// The Font Awesome "bitbucket" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bitbucket", "atlassian", "bitbucket-square", "git" })] - Bitbucket = 0xF171, - - /// - /// The Font Awesome "bitcoin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bitcoin" })] - [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] - Bitcoin = 0xF379, - /// /// The Font Awesome "bitcoin-sign" icon unicode character. /// @@ -1165,27 +896,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] BitcoinSign = 0xE0B4, - /// - /// The Font Awesome "bity" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bity" })] - Bity = 0xF37A, - - /// - /// The Font Awesome "blackberry" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "blackberry" })] - Blackberry = 0xF37B, - - /// - /// The Font Awesome "black-tie" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "black tie" })] - BlackTie = 0xF27E, - /// /// The Font Awesome "blender" icon unicode character. /// @@ -1214,36 +924,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Writing" })] Blog = 0xF781, - /// - /// The Font Awesome "blogger" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "blogger" })] - Blogger = 0xF37C, - - /// - /// The Font Awesome "blogger-b" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "blogger b" })] - BloggerB = 0xF37D, - - /// - /// The Font Awesome "bluetooth" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bluetooth", "signal" })] - [FontAwesomeCategoriesAttribute(new[] { "Connectivity" })] - Bluetooth = 0xF293, - - /// - /// The Font Awesome "bluetooth-b" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bluetooth b" })] - [FontAwesomeCategoriesAttribute(new[] { "Communication" })] - BluetoothB = 0xF294, - /// /// The Font Awesome "bold" icon unicode character. /// @@ -1342,13 +1022,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion" })] BookTanakh = 0xF827, - /// - /// The Font Awesome "bootstrap" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "bootstrap" })] - Bootstrap = 0xF836, - /// /// The Font Awesome "border-all" icon unicode character. /// @@ -1552,14 +1225,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Construction", "Design", "Editing" })] Brush = 0xF55D, - /// - /// The Font Awesome "btc" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "btc" })] - [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] - Btc = 0xF15A, - /// /// The Font Awesome "bucket" icon unicode character. /// @@ -1567,13 +1232,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Camping", "Childhood", "Construction", "Humanitarian" })] Bucket = 0xE4CF, - /// - /// The Font Awesome "buffer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "buffer" })] - Buffer = 0xF837, - /// /// The Font Awesome "bug" icon unicode character. /// @@ -1689,7 +1347,7 @@ public enum FontAwesomeIcon /// /// The Font Awesome "bullseye" icon unicode character. /// - [FontAwesomeSearchTerms(new[] { "bullseye", "archery", "goal", "objective", "target" })] + [FontAwesomeSearchTerms(new[] { "bullseye", "archery", "goal", "objective", "strategy", "target" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Marketing", "Toggle" })] Bullseye = 0xF140, @@ -1700,13 +1358,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Energy", "Humanitarian", "Medical + Health", "Science", "Sports + Fitness" })] Burn = 0xF46A, - /// - /// The Font Awesome "buromobelexperte" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "buromobelexperte" })] - Buromobelexperte = 0xF37F, - /// /// The Font Awesome "burst" icon unicode character. /// @@ -1735,20 +1386,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business" })] BusinessTime = 0xF64A, - /// - /// The Font Awesome "buy-n-large" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "buy n large" })] - BuyNLarge = 0xF8A6, - - /// - /// The Font Awesome "buysellads" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "buysellads" })] - Buysellads = 0xF20D, - /// /// The Font Awesome "calculator" icon unicode character. /// @@ -1840,13 +1477,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping" })] Campground = 0xF6BB, - /// - /// The Font Awesome "canadian-maple-leaf" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "canadian maple leaf", "canada", "flag", "flora", "nature", "plant" })] - CanadianMapleLeaf = 0xF785, - /// /// The Font Awesome "candy-cane" icon unicode character. /// @@ -2015,86 +1645,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals", "Halloween" })] Cat = 0xF6BE, - /// - /// The Font Awesome "cc-amazon-pay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc amazon pay" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcAmazonPay = 0xF42D, - - /// - /// The Font Awesome "cc-amex" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc amex", "amex" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcAmex = 0xF1F3, - - /// - /// The Font Awesome "cc-apple-pay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc apple pay" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcApplePay = 0xF416, - - /// - /// The Font Awesome "cc-diners-club" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc diners club" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcDinersClub = 0xF24C, - - /// - /// The Font Awesome "cc-discover" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc discover" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcDiscover = 0xF1F2, - - /// - /// The Font Awesome "cc-jcb" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc jcb" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcJcb = 0xF24B, - - /// - /// The Font Awesome "cc-mastercard" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc mastercard" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcMastercard = 0xF1F1, - - /// - /// The Font Awesome "cc-paypal" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc paypal" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcPaypal = 0xF1F4, - - /// - /// The Font Awesome "cc-stripe" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc stripe" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcStripe = 0xF1F5, - - /// - /// The Font Awesome "cc-visa" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cc visa" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - CcVisa = 0xF1F0, - /// /// The Font Awesome "cedi-sign" icon unicode character. /// @@ -2102,20 +1652,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] CediSign = 0xE0DF, - /// - /// The Font Awesome "centercode" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "centercode" })] - Centercode = 0xF380, - - /// - /// The Font Awesome "centos" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "centos", "linux", "operating system", "os" })] - Centos = 0xF789, - /// /// The Font Awesome "cent-sign" icon unicode character. /// @@ -2389,20 +1925,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Humanitarian", "Users + People" })] Children = 0xE4E1, - /// - /// The Font Awesome "chrome" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "chrome", "browser" })] - Chrome = 0xF268, - - /// - /// The Font Awesome "chromecast" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "chromecast" })] - Chromecast = 0xF838, - /// /// The Font Awesome "church" icon unicode character. /// @@ -2558,13 +2080,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Weather" })] CloudRain = 0xF73D, - /// - /// The Font Awesome "cloudscale" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cloudscale" })] - Cloudscale = 0xF383, - /// /// The Font Awesome "cloud-showers-heavy" icon unicode character. /// @@ -2579,13 +2094,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Weather" })] CloudShowersWater = 0xE4E4, - /// - /// The Font Awesome "cloudsmith" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cloudsmith" })] - Cloudsmith = 0xF384, - /// /// The Font Awesome "cloud-sun" icon unicode character. /// @@ -2608,13 +2116,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Connectivity" })] CloudUploadAlt = 0xF382, - /// - /// The Font Awesome "cloudversify" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cloudversify" })] - Cloudversify = 0xF385, - /// /// The Font Awesome "clover" icon unicode character. /// @@ -2671,13 +2172,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding" })] CodeMerge = 0xF387, - /// - /// The Font Awesome "codepen" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "codepen" })] - Codepen = 0xF1CB, - /// /// The Font Awesome "code-pull-request" icon unicode character. /// @@ -2685,13 +2179,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding" })] CodePullRequest = 0xE13C, - /// - /// The Font Awesome "codiepie" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "codiepie" })] - Codiepie = 0xF284, - /// /// The Font Awesome "mug-saucer" icon unicode character. /// @@ -2703,7 +2190,7 @@ public enum FontAwesomeIcon /// The Font Awesome "gear" icon unicode character. /// [FontAwesomeSearchTerms(new[] { "cog", "cogwheel", "gear", "mechanical", "settings", "sprocket", "tool", "wheel" })] - [FontAwesomeCategoriesAttribute(new[] { "Spinners" })] + [FontAwesomeCategoriesAttribute(new[] { "Coding", "Editing", "Spinners" })] Cog = 0xF013, /// @@ -2839,27 +2326,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] ConciergeBell = 0xF562, - /// - /// The Font Awesome "confluence" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "confluence", "atlassian" })] - Confluence = 0xF78D, - - /// - /// The Font Awesome "connectdevelop" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "connectdevelop" })] - Connectdevelop = 0xF20E, - - /// - /// The Font Awesome "contao" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "contao" })] - Contao = 0xF26D, - /// /// The Font Awesome "cookie" icon unicode character. /// @@ -2888,13 +2354,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business" })] Copyright = 0xF1F9, - /// - /// The Font Awesome "cotton-bureau" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cotton bureau", "clothing", "t-shirts", "tshirts" })] - CottonBureau = 0xF89E, - /// /// The Font Awesome "couch" icon unicode character. /// @@ -2909,111 +2368,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals", "Humanitarian" })] Cow = 0xF6C8, - /// - /// The Font Awesome "cpanel" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cpanel" })] - Cpanel = 0xF388, - - /// - /// The Font Awesome "creative-commons" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons" })] - CreativeCommons = 0xF25E, - - /// - /// The Font Awesome "creative-commons-by" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons by" })] - CreativeCommonsBy = 0xF4E7, - - /// - /// The Font Awesome "creative-commons-nc" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons nc" })] - CreativeCommonsNc = 0xF4E8, - - /// - /// The Font Awesome "creative-commons-nc-eu" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons nc eu" })] - CreativeCommonsNcEu = 0xF4E9, - - /// - /// The Font Awesome "creative-commons-nc-jp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons nc jp" })] - CreativeCommonsNcJp = 0xF4EA, - - /// - /// The Font Awesome "creative-commons-nd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons nd" })] - CreativeCommonsNd = 0xF4EB, - - /// - /// The Font Awesome "creative-commons-pd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons pd" })] - CreativeCommonsPd = 0xF4EC, - - /// - /// The Font Awesome "creative-commons-pd-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons pd alt" })] - CreativeCommonsPdAlt = 0xF4ED, - - /// - /// The Font Awesome "creative-commons-remix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons remix" })] - CreativeCommonsRemix = 0xF4EE, - - /// - /// The Font Awesome "creative-commons-sa" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons sa" })] - CreativeCommonsSa = 0xF4EF, - - /// - /// The Font Awesome "creative-commons-sampling" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons sampling" })] - CreativeCommonsSampling = 0xF4F0, - - /// - /// The Font Awesome "creative-commons-sampling-plus" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons sampling plus" })] - CreativeCommonsSamplingPlus = 0xF4F1, - - /// - /// The Font Awesome "creative-commons-share" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons share" })] - CreativeCommonsShare = 0xF4F2, - - /// - /// The Font Awesome "creative-commons-zero" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "creative commons zero" })] - CreativeCommonsZero = 0xF4F3, - /// /// The Font Awesome "credit-card" icon unicode character. /// @@ -3021,14 +2375,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] CreditCard = 0xF09D, - /// - /// The Font Awesome "critical-role" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "critical role", "dungeons & dragons", "d&d", "dnd", "fantasy", "game", "gaming", "tabletop" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - CriticalRole = 0xF6C9, - /// /// The Font Awesome "crop" icon unicode character. /// @@ -3085,20 +2431,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] CruzeiroSign = 0xE152, - /// - /// The Font Awesome "css3" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "css3", "code" })] - Css3 = 0xF13C, - - /// - /// The Font Awesome "css3-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "css3 alt" })] - Css3Alt = 0xF38B, - /// /// The Font Awesome "cube" icon unicode character. /// @@ -3127,43 +2459,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing", "Files" })] Cut = 0xF0C4, - /// - /// The Font Awesome "cuttlefish" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "cuttlefish" })] - Cuttlefish = 0xF38C, - - /// - /// The Font Awesome "dailymotion" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dailymotion" })] - Dailymotion = 0xF952, - - /// - /// The Font Awesome "d-and-d" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "d and d" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - DAndD = 0xF38D, - - /// - /// The Font Awesome "d-and-d-beyond" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "d and d beyond", "dungeons & dragons", "d&d", "dnd", "fantasy", "gaming", "tabletop" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - DAndDBeyond = 0xF6CA, - - /// - /// The Font Awesome "dashcube" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dashcube" })] - Dashcube = 0xF210, - /// /// The Font Awesome "database" icon unicode character. /// @@ -3178,13 +2473,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility" })] Deaf = 0xF2A4, - /// - /// The Font Awesome "delicious" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "delicious" })] - Delicious = 0xF1A5, - /// /// The Font Awesome "democrat" icon unicode character. /// @@ -3192,20 +2480,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Political" })] Democrat = 0xF747, - /// - /// The Font Awesome "deploydog" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "deploydog" })] - Deploydog = 0xF38E, - - /// - /// The Font Awesome "deskpro" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "deskpro" })] - Deskpro = 0xF38F, - /// /// The Font Awesome "desktop" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF390. @@ -3214,20 +2488,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] Desktop = 0xF108, - /// - /// The Font Awesome "dev" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dev" })] - Dev = 0xF6CC, - - /// - /// The Font Awesome "deviantart" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "deviantart" })] - Deviantart = 0xF1BD, - /// /// The Font Awesome "dharmachakra" icon unicode character. /// @@ -3235,13 +2495,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion", "Spinners" })] Dharmachakra = 0xF655, - /// - /// The Font Awesome "dhl" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dhl", "dalsey", "hillblom and lynn", "german", "package", "shipping" })] - Dhl = 0xF790, - /// /// The Font Awesome "person-dots-from-line" icon unicode character. /// @@ -3277,13 +2530,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Gaming", "Shapes" })] Diamond = 0xF219, - /// - /// The Font Awesome "diaspora" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "diaspora" })] - Diaspora = 0xF791, - /// /// The Font Awesome "dice" icon unicode character. /// @@ -3347,20 +2593,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] DiceTwo = 0xF528, - /// - /// The Font Awesome "digg" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "digg" })] - Digg = 0xF1A6, - - /// - /// The Font Awesome "digital-ocean" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "digital ocean" })] - DigitalOcean = 0xF391, - /// /// The Font Awesome "tachograph-digital" icon unicode character. /// @@ -3375,20 +2607,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps" })] Directions = 0xF5EB, - /// - /// The Font Awesome "discord" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "discord" })] - Discord = 0xF392, - - /// - /// The Font Awesome "discourse" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "discourse" })] - Discourse = 0xF393, - /// /// The Font Awesome "disease" icon unicode character. /// @@ -3424,20 +2642,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Science" })] Dna = 0xF471, - /// - /// The Font Awesome "dochub" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dochub" })] - Dochub = 0xF394, - - /// - /// The Font Awesome "docker" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "docker" })] - Docker = 0xF395, - /// /// The Font Awesome "dog" icon unicode character. /// @@ -3516,13 +2720,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Devices + Hardware" })] Download = 0xF019, - /// - /// The Font Awesome "draft2digital" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "draft2digital" })] - Draft2digital = 0xF396, - /// /// The Font Awesome "compass-drafting" icon unicode character. /// @@ -3544,27 +2741,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design", "Maps" })] DrawPolygon = 0xF5EE, - /// - /// The Font Awesome "dribbble" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dribbble" })] - Dribbble = 0xF17D, - - /// - /// The Font Awesome "square-dribbble" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square dribbble" })] - DribbbleSquare = 0xF397, - - /// - /// The Font Awesome "dropbox" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dropbox" })] - Dropbox = 0xF16B, - /// /// The Font Awesome "drum" icon unicode character. /// @@ -3586,13 +2762,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage" })] DrumstickBite = 0xF6D7, - /// - /// The Font Awesome "drupal" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "drupal" })] - Drupal = 0xF1A9, - /// /// The Font Awesome "dumbbell" icon unicode character. /// @@ -3621,20 +2790,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Gaming", "Household", "Security" })] Dungeon = 0xF6D9, - /// - /// The Font Awesome "dyalog" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "dyalog" })] - Dyalog = 0xF399, - - /// - /// The Font Awesome "earlybirds" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "earlybirds" })] - Earlybirds = 0xF39A, - /// /// The Font Awesome "earth-oceania" icon unicode character. /// @@ -3642,20 +2797,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] EarthOceania = 0xE47B, - /// - /// The Font Awesome "ebay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ebay" })] - Ebay = 0xF4F4, - - /// - /// The Font Awesome "edge" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "edge", "browser", "ie" })] - Edge = 0xF282, - /// /// The Font Awesome "pen-to-square" icon unicode character. /// @@ -3677,13 +2818,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] Eject = 0xF052, - /// - /// The Font Awesome "elementor" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "elementor" })] - Elementor = 0xF430, - /// /// The Font Awesome "elevator" icon unicode character. /// @@ -3705,27 +2839,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Editing" })] EllipsisV = 0xF142, - /// - /// The Font Awesome "ello" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ello" })] - Ello = 0xF5F1, - - /// - /// The Font Awesome "ember" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ember" })] - Ember = 0xF423, - - /// - /// The Font Awesome "empire" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "empire" })] - Empire = 0xF1D1, - /// /// The Font Awesome "envelope" icon unicode character. /// @@ -3761,13 +2874,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication" })] EnvelopeSquare = 0xF199, - /// - /// The Font Awesome "envira" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "envira", "leaf" })] - Envira = 0xF299, - /// /// The Font Awesome "equals" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x3D. @@ -3783,21 +2889,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Writing" })] Eraser = 0xF12D, - /// - /// The Font Awesome "erlang" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "erlang" })] - Erlang = 0xF39D, - - /// - /// The Font Awesome "ethereum" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ethereum" })] - [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] - Ethereum = 0xF42E, - /// /// The Font Awesome "ethernet" icon unicode character. /// @@ -3805,13 +2896,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Connectivity", "Devices + Hardware" })] Ethernet = 0xF796, - /// - /// The Font Awesome "etsy" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "etsy" })] - Etsy = 0xF2D7, - /// /// The Font Awesome "euro-sign" icon unicode character. /// @@ -3819,13 +2903,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] EuroSign = 0xF153, - /// - /// The Font Awesome "evernote" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "evernote" })] - Evernote = 0xF839, - /// /// The Font Awesome "right-left" icon unicode character. /// @@ -3876,13 +2953,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] ExpandArrowsAlt = 0xF31E, - /// - /// The Font Awesome "expeditedssl" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "expeditedssl" })] - Expeditedssl = 0xF23E, - /// /// The Font Awesome "explosion" icon unicode character. /// @@ -3925,34 +2995,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design", "Editing", "Maps", "Photos + Images", "Security" })] EyeSlash = 0xF070, - /// - /// The Font Awesome "facebook" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "facebook", "facebook-official", "social network" })] - Facebook = 0xF09A, - - /// - /// The Font Awesome "facebook-f" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "facebook f", "facebook" })] - FacebookF = 0xF39E, - - /// - /// The Font Awesome "facebook-messenger" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "facebook messenger" })] - FacebookMessenger = 0xF39F, - - /// - /// The Font Awesome "square-facebook" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square facebook", "social network" })] - FacebookSquare = 0xF082, - /// /// The Font Awesome "fan" icon unicode character. /// @@ -3960,14 +3002,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Energy", "Household", "Spinners" })] Fan = 0xF863, - /// - /// The Font Awesome "fantasy-flight-games" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fantasy flight games", "dungeons & dragons", "d&d", "dnd", "fantasy", "game", "gaming", "tabletop" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - FantasyFlightGames = 0xF6DC, - /// /// The Font Awesome "backward-fast" icon unicode character. /// @@ -4017,20 +3051,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals", "Nature" })] FeatherAlt = 0xF56B, - /// - /// The Font Awesome "fedex" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fedex", "federal express", "package", "shipping" })] - Fedex = 0xF797, - - /// - /// The Font Awesome "fedora" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fedora", "linux", "operating system", "os" })] - Fedora = 0xF798, - /// /// The Font Awesome "person-dress" icon unicode character. /// @@ -4052,13 +3072,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Transportation" })] FighterJet = 0xF0FB, - /// - /// The Font Awesome "figma" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "figma", "app", "design", "interface" })] - Figma = 0xF799, - /// /// The Font Awesome "file" icon unicode character. /// @@ -4346,20 +3359,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding", "Maps" })] FireExtinguisher = 0xF134, - /// - /// The Font Awesome "firefox" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "firefox", "browser" })] - Firefox = 0xF269, - - /// - /// The Font Awesome "firefox-browser" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "firefox browser", "browser" })] - FirefoxBrowser = 0xF907, - /// /// The Font Awesome "kit-medical" icon unicode character. /// @@ -4367,27 +3366,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Camping", "Medical + Health" })] FirstAid = 0xF479, - /// - /// The Font Awesome "firstdraft" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "firstdraft" })] - Firstdraft = 0xF3A1, - - /// - /// The Font Awesome "first-order" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "first order" })] - FirstOrder = 0xF2B0, - - /// - /// The Font Awesome "first-order-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "first order alt" })] - FirstOrderAlt = 0xF50A, - /// /// The Font Awesome "fish" icon unicode character. /// @@ -4433,31 +3411,17 @@ public enum FontAwesomeIcon /// /// The Font Awesome "flask" icon unicode character. /// - [FontAwesomeSearchTerms(new[] { "flask", "beaker", "experimental", "labs", "science" })] + [FontAwesomeSearchTerms(new[] { "flask", "beaker", "chemicals", "experiment", "experimental", "labs", "liquid", "potion", "science", "vial" })] [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Maps", "Medical + Health", "Science" })] Flask = 0xF0C3, /// /// The Font Awesome "flask-vial" icon unicode character. /// - [FontAwesomeSearchTerms(new[] { "flask vial", "ampule", "chemistry", "lab", "laboratory", "test", "test tube" })] + [FontAwesomeSearchTerms(new[] { "flask vial", " beaker", " chemicals", " experiment", " experimental", " labs", " liquid", " science", " vial", "ampule", "chemistry", "lab", "laboratory", "potion", "test", "test tube" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Science" })] FlaskVial = 0xE4F3, - /// - /// The Font Awesome "flickr" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "flickr" })] - Flickr = 0xF16E, - - /// - /// The Font Awesome "flipboard" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "flipboard" })] - Flipboard = 0xF44D, - /// /// The Font Awesome "florin-sign" icon unicode character. /// @@ -4472,13 +3436,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] Flushed = 0xF579, - /// - /// The Font Awesome "fly" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fly" })] - Fly = 0xF417, - /// /// The Font Awesome "folder" icon unicode character. /// @@ -4535,13 +3492,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding", "Design" })] FontAwesome = 0xF2B4, - /// - /// The Font Awesome "square-font-awesome-stroke" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square font awesome stroke" })] - FontAwesomeAlt = 0xF35C, - /// /// The Font Awesome "font-awesome" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF2B4. @@ -4558,20 +3508,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding", "Design" })] FontAwesomeLogoFull = 0xF4E6, - /// - /// The Font Awesome "fonticons" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fonticons" })] - Fonticons = 0xF280, - - /// - /// The Font Awesome "fonticons-fi" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fonticons fi" })] - FonticonsFi = 0xF3A2, - /// /// The Font Awesome "football" icon unicode character. /// @@ -4579,27 +3515,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] FootballBall = 0xF44E, - /// - /// The Font Awesome "fort-awesome" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fort awesome", "castle" })] - FortAwesome = 0xF286, - - /// - /// The Font Awesome "fort-awesome-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fort awesome alt", "castle" })] - FortAwesomeAlt = 0xF3A3, - - /// - /// The Font Awesome "forumbee" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "forumbee" })] - Forumbee = 0xF211, - /// /// The Font Awesome "forward" icon unicode character. /// @@ -4607,13 +3522,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] Forward = 0xF04E, - /// - /// The Font Awesome "foursquare" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "foursquare" })] - Foursquare = 0xF180, - /// /// The Font Awesome "franc-sign" icon unicode character. /// @@ -4621,20 +3529,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] FrancSign = 0xE18F, - /// - /// The Font Awesome "freebsd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "freebsd" })] - Freebsd = 0xF3A4, - - /// - /// The Font Awesome "free-code-camp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "free code camp" })] - FreeCodeCamp = 0xF2C5, - /// /// The Font Awesome "frog" icon unicode character. /// @@ -4656,13 +3550,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] FrownOpen = 0xF57A, - /// - /// The Font Awesome "fulcrum" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "fulcrum" })] - Fulcrum = 0xF50B, - /// /// The Font Awesome "filter-circle-dollar" icon unicode character. /// @@ -4677,22 +3564,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] Futbol = 0xF1E3, - /// - /// The Font Awesome "galactic-republic" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "galactic republic", "politics", "star wars" })] - [FontAwesomeCategoriesAttribute(new[] { "Science Fiction" })] - GalacticRepublic = 0xF50C, - - /// - /// The Font Awesome "galactic-senate" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "galactic senate", "star wars" })] - [FontAwesomeCategoriesAttribute(new[] { "Science Fiction" })] - GalacticSenate = 0xF50D, - /// /// The Font Awesome "gamepad" icon unicode character. /// @@ -4749,29 +3620,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Genders" })] Genderless = 0xF22D, - /// - /// The Font Awesome "get-pocket" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "get pocket" })] - GetPocket = 0xF265, - - /// - /// The Font Awesome "gg" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gg" })] - [FontAwesomeCategoriesAttribute(new[] { "Money" })] - Gg = 0xF260, - - /// - /// The Font Awesome "gg-circle" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gg circle" })] - [FontAwesomeCategoriesAttribute(new[] { "Money" })] - GgCircle = 0xF261, - /// /// The Font Awesome "ghost" icon unicode character. /// @@ -4793,69 +3641,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Holidays", "Shopping" })] Gifts = 0xF79C, - /// - /// The Font Awesome "git" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "git" })] - Git = 0xF1D3, - - /// - /// The Font Awesome "git-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "git alt" })] - GitAlt = 0xF841, - - /// - /// The Font Awesome "github" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "github", "octocat" })] - Github = 0xF09B, - - /// - /// The Font Awesome "github-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "github alt", "octocat" })] - GithubAlt = 0xF113, - - /// - /// The Font Awesome "square-github" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square github", "octocat" })] - GithubSquare = 0xF092, - - /// - /// The Font Awesome "gitkraken" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gitkraken" })] - Gitkraken = 0xF3A6, - - /// - /// The Font Awesome "gitlab" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gitlab", "axosoft" })] - Gitlab = 0xF296, - - /// - /// The Font Awesome "square-git" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square git" })] - GitSquare = 0xF1D2, - - /// - /// The Font Awesome "gitter" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gitter" })] - Gitter = 0xF426, - /// /// The Font Awesome "champagne-glasses" icon unicode character. /// @@ -4905,20 +3690,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage" })] GlassWhiskey = 0xF7A0, - /// - /// The Font Awesome "glide" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "glide" })] - Glide = 0xF2A5, - - /// - /// The Font Awesome "glide-g" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "glide g" })] - GlideG = 0xF2A6, - /// /// The Font Awesome "globe" icon unicode character. /// @@ -4954,13 +3725,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] GlobeEurope = 0xF7A2, - /// - /// The Font Awesome "gofore" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gofore" })] - Gofore = 0xF3A7, - /// /// The Font Awesome "golf-ball-tee" icon unicode character. /// @@ -4968,70 +3732,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] GolfBall = 0xF450, - /// - /// The Font Awesome "goodreads" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "goodreads" })] - Goodreads = 0xF3A8, - - /// - /// The Font Awesome "goodreads-g" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "goodreads g" })] - GoodreadsG = 0xF3A9, - - /// - /// The Font Awesome "google" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google" })] - Google = 0xF1A0, - - /// - /// The Font Awesome "google-drive" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google drive" })] - GoogleDrive = 0xF3AA, - - /// - /// The Font Awesome "google-play" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google play" })] - GooglePlay = 0xF3AB, - - /// - /// The Font Awesome "google-plus" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google plus", "google-plus-circle", "google-plus-official" })] - GooglePlus = 0xF2B3, - - /// - /// The Font Awesome "google-plus-g" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google plus g", "google-plus", "social network" })] - GooglePlusG = 0xF0D5, - - /// - /// The Font Awesome "square-google-plus" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square google plus", "social network" })] - GooglePlusSquare = 0xF0D4, - - /// - /// The Font Awesome "google-wallet" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "google wallet" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - GoogleWallet = 0xF1EE, - /// /// The Font Awesome "gopuram" icon unicode character. /// @@ -5046,20 +3746,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Clothing + Fashion", "Education", "Maps" })] GraduationCap = 0xF19D, - /// - /// The Font Awesome "gratipay" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gratipay", "favorite", "heart", "like", "love" })] - Gratipay = 0xF184, - - /// - /// The Font Awesome "grav" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "grav" })] - Grav = 0xF2D6, - /// /// The Font Awesome "greater-than" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x3E. @@ -5173,13 +3859,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] GrinWink = 0xF58C, - /// - /// The Font Awesome "gripfire" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gripfire" })] - Gripfire = 0xF3AC, - /// /// The Font Awesome "grip" icon unicode character. /// @@ -5215,13 +3894,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Marketing" })] GroupArrowsRotate = 0xE4F6, - /// - /// The Font Awesome "grunt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "grunt" })] - Grunt = 0xF3AD, - /// /// The Font Awesome "guarani-sign" icon unicode character. /// @@ -5236,13 +3908,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Music + Audio" })] Guitar = 0xF7A6, - /// - /// The Font Awesome "gulp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "gulp" })] - Gulp = 0xF3AE, - /// /// The Font Awesome "gun" icon unicode character. /// @@ -5250,27 +3915,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Security" })] Gun = 0xE19B, - /// - /// The Font Awesome "hacker-news" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hacker news" })] - HackerNews = 0xF1D4, - - /// - /// The Font Awesome "square-hacker-news" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square hacker news" })] - HackerNewsSquare = 0xF3AF, - - /// - /// The Font Awesome "hackerrank" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hackerrank" })] - Hackerrank = 0xF5F7, - /// /// The Font Awesome "burger" icon unicode character. /// @@ -5734,20 +4378,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals" })] Hippo = 0xF6ED, - /// - /// The Font Awesome "hips" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hips" })] - Hips = 0xF452, - - /// - /// The Font Awesome "hire-a-helper" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hire a helper" })] - HireAHelper = 0xF3B0, - /// /// The Font Awesome "clock-rotate-left" icon unicode character. /// @@ -5776,20 +4406,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Maps" })] Home = 0xF015, - /// - /// The Font Awesome "hooli" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hooli" })] - Hooli = 0xF427, - - /// - /// The Font Awesome "hornbill" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hornbill" })] - Hornbill = 0xF592, - /// /// The Font Awesome "horse" icon unicode character. /// @@ -5847,13 +4463,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Travel + Hotel" })] Hotel = 0xF594, - /// - /// The Font Awesome "hotjar" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hotjar" })] - Hotjar = 0xF3B1, - /// /// The Font Awesome "hot-tub-person" icon unicode character. /// @@ -6043,13 +4652,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Household", "Users + People" })] HouseUser = 0xE1B0, - /// - /// The Font Awesome "houzz" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "houzz" })] - Houzz = 0xF27C, - /// /// The Font Awesome "hryvnia-sign" icon unicode character. /// @@ -6064,20 +4666,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Alphabet", "Maps", "Medical + Health" })] HSquare = 0xF0FD, - /// - /// The Font Awesome "html5" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "html5" })] - Html5 = 0xF13B, - - /// - /// The Font Awesome "hubspot" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "hubspot" })] - Hubspot = 0xF3B2, - /// /// The Font Awesome "hurricane" icon unicode character. /// @@ -6134,13 +4722,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Security", "Users + People" })] IdCardAlt = 0xF47F, - /// - /// The Font Awesome "ideal" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ideal" })] - Ideal = 0xF913, - /// /// The Font Awesome "igloo" icon unicode character. /// @@ -6162,13 +4743,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Photos + Images", "Social" })] Images = 0xF302, - /// - /// The Font Awesome "imdb" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "imdb" })] - Imdb = 0xF2D8, - /// /// The Font Awesome "inbox" icon unicode character. /// @@ -6218,48 +4792,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Maps" })] InfoCircle = 0xF05A, - /// - /// The Font Awesome "instagram" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "instagram" })] - Instagram = 0xF16D, - - /// - /// The Font Awesome "square-instagram" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square instagram" })] - InstagramSquare = 0xF955, - - /// - /// The Font Awesome "intercom" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "intercom", "app", "customer", "messenger" })] - Intercom = 0xF7AF, - - /// - /// The Font Awesome "internet-explorer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "internet explorer", "browser", "ie" })] - InternetExplorer = 0xF26B, - - /// - /// The Font Awesome "invision" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "invision", "app", "design", "interface" })] - Invision = 0xF7B0, - - /// - /// The Font Awesome "ioxhost" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ioxhost" })] - Ioxhost = 0xF208, - /// /// The Font Awesome "italic" icon unicode character. /// @@ -6267,27 +4799,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Italic = 0xF033, - /// - /// The Font Awesome "itch-io" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "itch io" })] - ItchIo = 0xF83A, - - /// - /// The Font Awesome "itunes" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "itunes" })] - Itunes = 0xF3B4, - - /// - /// The Font Awesome "itunes-note" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "itunes note" })] - ItunesNote = 0xF3B5, - /// /// The Font Awesome "jar" icon unicode character. /// @@ -6302,13 +4813,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Household", "Humanitarian" })] JarWheat = 0xE517, - /// - /// The Font Awesome "java" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "java" })] - Java = 0xF4E4, - /// /// The Font Awesome "jedi" icon unicode character. /// @@ -6316,21 +4820,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion", "Science Fiction" })] Jedi = 0xF669, - /// - /// The Font Awesome "jedi-order" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "jedi order", "star wars" })] - [FontAwesomeCategoriesAttribute(new[] { "Science Fiction" })] - JediOrder = 0xF50E, - - /// - /// The Font Awesome "jenkins" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "jenkins" })] - Jenkins = 0xF3B6, - /// /// The Font Awesome "jet-fighter-up" icon unicode character. /// @@ -6338,20 +4827,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Transportation" })] JetFighterUp = 0xE518, - /// - /// The Font Awesome "jira" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "jira", "atlassian" })] - Jira = 0xF7B1, - - /// - /// The Font Awesome "joget" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "joget" })] - Joget = 0xF3B7, - /// /// The Font Awesome "joint" icon unicode character. /// @@ -6359,13 +4834,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] Joint = 0xF595, - /// - /// The Font Awesome "joomla" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "joomla" })] - Joomla = 0xF1AA, - /// /// The Font Awesome "book-journal-whills" icon unicode character. /// @@ -6373,27 +4841,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion", "Science Fiction" })] JournalWhills = 0xF66A, - /// - /// The Font Awesome "js" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "js" })] - Js = 0xF3B8, - - /// - /// The Font Awesome "jsfiddle" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "jsfiddle" })] - Jsfiddle = 0xF1CC, - - /// - /// The Font Awesome "square-js" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square js" })] - JsSquare = 0xF3B9, - /// /// The Font Awesome "jug-detergent" icon unicode character. /// @@ -6408,13 +4855,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Religion" })] Kaaba = 0xF66B, - /// - /// The Font Awesome "kaggle" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "kaggle" })] - Kaggle = 0xF5FA, - /// /// The Font Awesome "key" icon unicode character. /// @@ -6422,13 +4862,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Security", "Shopping", "Travel + Hotel" })] Key = 0xF084, - /// - /// The Font Awesome "keybase" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "keybase" })] - Keybase = 0xF4F5, - /// /// The Font Awesome "keyboard" icon unicode character. /// @@ -6436,13 +4869,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding", "Devices + Hardware", "Writing" })] Keyboard = 0xF11C, - /// - /// The Font Awesome "keycdn" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "keycdn" })] - Keycdn = 0xF3BA, - /// /// The Font Awesome "khanda" icon unicode character. /// @@ -6450,20 +4876,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion" })] Khanda = 0xF66D, - /// - /// The Font Awesome "kickstarter" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "kickstarter" })] - Kickstarter = 0xF3BB, - - /// - /// The Font Awesome "kickstarter-k" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "kickstarter k" })] - KickstarterK = 0xF3BC, - /// /// The Font Awesome "kip-sign" icon unicode character. /// @@ -6506,13 +4918,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals" })] KiwiBird = 0xF535, - /// - /// The Font Awesome "korvue" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "korvue" })] - Korvue = 0xF42F, - /// /// The Font Awesome "landmark" icon unicode character. /// @@ -6576,13 +4981,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] LaptopMedical = 0xF812, - /// - /// The Font Awesome "laravel" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "laravel" })] - Laravel = 0xF3BD, - /// /// The Font Awesome "lari-sign" icon unicode character. /// @@ -6590,20 +4988,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] LariSign = 0xE1C8, - /// - /// The Font Awesome "lastfm" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "lastfm" })] - Lastfm = 0xF202, - - /// - /// The Font Awesome "square-lastfm" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square lastfm" })] - LastfmSquare = 0xF203, - /// /// The Font Awesome "face-laugh" icon unicode character. /// @@ -6646,13 +5030,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Charity", "Energy", "Fruits + Vegetables", "Maps", "Nature" })] Leaf = 0xF06C, - /// - /// The Font Awesome "leanpub" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "leanpub" })] - Leanpub = 0xF212, - /// /// The Font Awesome "lemon" icon unicode character. /// @@ -6660,13 +5037,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Fruits + Vegetables", "Maps" })] Lemon = 0xF094, - /// - /// The Font Awesome "less" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "less" })] - Less = 0xF41D, - /// /// The Font Awesome "less-than" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x3C. @@ -6706,17 +5076,10 @@ public enum FontAwesomeIcon /// /// The Font Awesome "lightbulb" icon unicode character. /// - [FontAwesomeSearchTerms(new[] { "lightbulb", "bulb", "comic", "electric", "energy", "idea", "inspiration", "light", "light bulb" })] + [FontAwesomeSearchTerms(new[] { "lightbulb", " comic", " electric", " idea", " innovation", " inspiration", " light", " light bulb", " bulb", "bulb", "comic", "electric", "energy", "idea", "inspiration", "mechanical" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Household", "Maps", "Marketing" })] Lightbulb = 0xF0EB, - /// - /// The Font Awesome "line" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "line" })] - Line = 0xF3C0, - /// /// The Font Awesome "lines-leaning" icon unicode character. /// @@ -6731,34 +5094,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Editing" })] Link = 0xF0C1, - /// - /// The Font Awesome "linkedin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "linkedin", "linkedin-square" })] - Linkedin = 0xF08C, - - /// - /// The Font Awesome "linkedin-in" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "linkedin in", "linkedin" })] - LinkedinIn = 0xF0E1, - - /// - /// The Font Awesome "linode" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "linode" })] - Linode = 0xF2B8, - - /// - /// The Font Awesome "linux" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "linux", "tux" })] - Linux = 0xF17C, - /// /// The Font Awesome "lira-sign" icon unicode character. /// @@ -6899,20 +5234,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] LungsVirus = 0xE067, - /// - /// The Font Awesome "lyft" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "lyft" })] - Lyft = 0xF3C3, - - /// - /// The Font Awesome "magento" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "magento" })] - Magento = 0xF3C4, - /// /// The Font Awesome "wand-magic" icon unicode character. /// @@ -6937,7 +5258,7 @@ public enum FontAwesomeIcon /// /// The Font Awesome "magnifying-glass-chart" icon unicode character. /// - [FontAwesomeSearchTerms(new[] { "magnifying glass chart", "analysis", "chart" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass chart", " data", " graph", " intelligence", "analysis", "chart", "market" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Humanitarian", "Marketing" })] MagnifyingGlassChart = 0xE522, @@ -6948,13 +5269,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] MailBulk = 0xF674, - /// - /// The Font Awesome "mailchimp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mailchimp" })] - Mailchimp = 0xF59E, - /// /// The Font Awesome "person" icon unicode character. /// @@ -6969,13 +5283,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] ManatSign = 0xE1D5, - /// - /// The Font Awesome "mandalorian" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mandalorian" })] - Mandalorian = 0xF50F, - /// /// The Font Awesome "map" icon unicode character. /// @@ -7025,13 +5332,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Camping", "Maps", "Nature" })] MapSigns = 0xF277, - /// - /// The Font Awesome "markdown" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "markdown" })] - Markdown = 0xF60F, - /// /// The Font Awesome "marker" icon unicode character. /// @@ -7102,13 +5402,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] MaskVentilator = 0xE524, - /// - /// The Font Awesome "mastodon" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mastodon" })] - Mastodon = 0xF4F6, - /// /// The Font Awesome "mattress-pillow" icon unicode character. /// @@ -7116,20 +5409,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Camping", "Household", "Humanitarian" })] MattressPillow = 0xE525, - /// - /// The Font Awesome "maxcdn" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "maxcdn" })] - Maxcdn = 0xF136, - - /// - /// The Font Awesome "mdb" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mdb" })] - Mdb = 0xF8CA, - /// /// The Font Awesome "medal" icon unicode character. /// @@ -7137,28 +5416,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] Medal = 0xF5A2, - /// - /// The Font Awesome "medapps" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "medapps" })] - Medapps = 0xF3C6, - - /// - /// The Font Awesome "medium" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "medium" })] - Medium = 0xF23A, - - /// - /// The Font Awesome "medium" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF23A. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "medium" })] - MediumM = 0xF3C7, - /// /// The Font Awesome "suitcase-medical" icon unicode character. /// @@ -7166,27 +5423,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Maps", "Medical + Health" })] Medkit = 0xF0FA, - /// - /// The Font Awesome "medrt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "medrt" })] - Medrt = 0xF3C8, - - /// - /// The Font Awesome "meetup" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "meetup" })] - Meetup = 0xF2E0, - - /// - /// The Font Awesome "megaport" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "megaport" })] - Megaport = 0xF5A3, - /// /// The Font Awesome "face-meh" icon unicode character. /// @@ -7215,13 +5451,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] Memory = 0xF538, - /// - /// The Font Awesome "mendeley" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mendeley" })] - Mendeley = 0xF7B3, - /// /// The Font Awesome "menorah" icon unicode character. /// @@ -7243,12 +5472,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Weather" })] Meteor = 0xF753, - /// - /// The Font Awesome "microblog" icon unicode character. - /// - [Obsolete] - Microblog = 0xF91A, - /// /// The Font Awesome "microchip" icon unicode character. /// @@ -7291,13 +5514,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Education", "Humanitarian", "Medical + Health", "Science" })] Microscope = 0xF610, - /// - /// The Font Awesome "microsoft" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "microsoft" })] - Microsoft = 0xF3CA, - /// /// The Font Awesome "mill-sign" icon unicode character. /// @@ -7333,34 +5549,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Clothing + Fashion" })] Mitten = 0xF7B5, - /// - /// The Font Awesome "mix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mix" })] - Mix = 0xF3CB, - - /// - /// The Font Awesome "mixcloud" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mixcloud" })] - Mixcloud = 0xF289, - - /// - /// The Font Awesome "mixer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mixer" })] - Mixer = 0xF956, - - /// - /// The Font Awesome "mizuni" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "mizuni" })] - Mizuni = 0xF3CC, - /// /// The Font Awesome "mobile" icon unicode character. /// @@ -7396,20 +5584,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Communication", "Devices + Hardware", "Humanitarian" })] MobileScreen = 0xF3CF, - /// - /// The Font Awesome "modx" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "modx" })] - Modx = 0xF285, - - /// - /// The Font Awesome "monero" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "monero" })] - Monero = 0xF3D0, - /// /// The Font Awesome "money-bill" icon unicode character. /// @@ -7484,7 +5658,7 @@ public enum FontAwesomeIcon /// The Font Awesome "monument" icon unicode character. /// [FontAwesomeSearchTerms(new[] { "monument", "building", "historic", "landmark", "memorable" })] - [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Travel + Hotel" })] + [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Maps", "Travel + Hotel" })] Monument = 0xF5A6, /// @@ -7592,21 +5766,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] NairaSign = 0xE1F6, - /// - /// The Font Awesome "napster" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "napster" })] - [FontAwesomeCategoriesAttribute(new[] { "Music + Audio" })] - Napster = 0xF3D2, - - /// - /// The Font Awesome "neos" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "neos" })] - Neos = 0xF612, - /// /// The Font Awesome "network-wired" icon unicode character. /// @@ -7628,27 +5787,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Writing" })] Newspaper = 0xF1EA, - /// - /// The Font Awesome "nimblr" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "nimblr" })] - Nimblr = 0xF5A8, - - /// - /// The Font Awesome "node" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "node" })] - Node = 0xF419, - - /// - /// The Font Awesome "node-js" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "node js" })] - NodeJs = 0xF3D3, - /// /// The Font Awesome "notdef" icon unicode character. /// @@ -7670,27 +5808,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] NotesMedical = 0xF481, - /// - /// The Font Awesome "npm" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "npm" })] - Npm = 0xF3D4, - - /// - /// The Font Awesome "ns8" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ns8" })] - Ns8 = 0xF3D5, - - /// - /// The Font Awesome "nutritionix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "nutritionix" })] - Nutritionix = 0xF3D6, - /// /// The Font Awesome "object-group" icon unicode character. /// @@ -7705,20 +5822,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design" })] ObjectUngroup = 0xF248, - /// - /// The Font Awesome "odnoklassniki" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "odnoklassniki" })] - Odnoklassniki = 0xF263, - - /// - /// The Font Awesome "square-odnoklassniki" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square odnoklassniki" })] - OdnoklassnikiSquare = 0xF264, - /// /// The Font Awesome "oil-can" icon unicode character. /// @@ -7733,14 +5836,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Energy", "Humanitarian" })] OilWell = 0xE532, - /// - /// The Font Awesome "old-republic" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "old republic", "politics", "star wars" })] - [FontAwesomeCategoriesAttribute(new[] { "Science Fiction" })] - OldRepublic = 0xF510, - /// /// The Font Awesome "om" icon unicode character. /// @@ -7748,48 +5843,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion" })] Om = 0xF679, - /// - /// The Font Awesome "opencart" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "opencart" })] - Opencart = 0xF23D, - - /// - /// The Font Awesome "openid" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "openid" })] - Openid = 0xF19B, - - /// - /// The Font Awesome "opera" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "opera" })] - Opera = 0xF26A, - - /// - /// The Font Awesome "optin-monster" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "optin monster" })] - OptinMonster = 0xF23C, - - /// - /// The Font Awesome "orcid" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "orcid" })] - Orcid = 0xF8D2, - - /// - /// The Font Awesome "osi" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "osi" })] - Osi = 0xF41A, - /// /// The Font Awesome "otter" icon unicode character. /// @@ -7804,20 +5857,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Outdent = 0xF03B, - /// - /// The Font Awesome "page4" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "page4" })] - Page4 = 0xF3D7, - - /// - /// The Font Awesome "pagelines" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pagelines", "eco", "flora", "leaf", "leaves", "nature", "plant", "tree" })] - Pagelines = 0xF18C, - /// /// The Font Awesome "pager" icon unicode character. /// @@ -7846,13 +5885,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design", "Spinners" })] Palette = 0xF53F, - /// - /// The Font Awesome "palfed" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "palfed" })] - Palfed = 0xF3D8, - /// /// The Font Awesome "pallet" icon unicode character. /// @@ -7923,13 +5955,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Files" })] Paste = 0xF0EA, - /// - /// The Font Awesome "patreon" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "patreon" })] - Patreon = 0xF3D9, - /// /// The Font Awesome "pause" icon unicode character. /// @@ -7951,14 +5976,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals", "Maps" })] Paw = 0xF1B0, - /// - /// The Font Awesome "paypal" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "paypal" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - Paypal = 0xF1ED, - /// /// The Font Awesome "peace" icon unicode character. /// @@ -8008,12 +6025,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing" })] PenNib = 0xF5AD, - /// - /// The Font Awesome "pennyarcade" icon unicode character. - /// - [Obsolete] - PennyArcade = 0xF704, - /// /// The Font Awesome "square-pen" icon unicode character. /// @@ -8093,13 +6104,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Mathematics", "Money", "Punctuation + Symbols" })] Percentage = 0xF541, - /// - /// The Font Awesome "periscope" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "periscope" })] - Periscope = 0xF3DA, - /// /// The Font Awesome "person-arrow-down-to-line" icon unicode character. /// @@ -8338,27 +6342,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] PesoSign = 0xE222, - /// - /// The Font Awesome "phabricator" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "phabricator" })] - Phabricator = 0xF3DB, - - /// - /// The Font Awesome "phoenix-framework" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "phoenix framework" })] - PhoenixFramework = 0xF3DC, - - /// - /// The Font Awesome "phoenix-squadron" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "phoenix squadron" })] - PhoenixSquadron = 0xF511, - /// /// The Font Awesome "phone" icon unicode character. /// @@ -8408,47 +6391,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Files", "Film + Video", "Photos + Images", "Social" })] PhotoVideo = 0xF87C, - /// - /// The Font Awesome "php" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "php" })] - Php = 0xF457, - - /// - /// The Font Awesome "pied-piper" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pied piper" })] - PiedPiper = 0xF2AE, - - /// - /// The Font Awesome "pied-piper-alt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pied piper alt" })] - PiedPiperAlt = 0xF1A8, - - /// - /// The Font Awesome "pied-piper-hat" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pied piper hat", "clothing" })] - PiedPiperHat = 0xF4E5, - - /// - /// The Font Awesome "pied-piper-pp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pied piper pp" })] - PiedPiperPp = 0xF1A7, - - /// - /// The Font Awesome "piedpipersquare" icon unicode character. - /// - [Obsolete] - PiedPiperSquare = 0xF91E, - /// /// The Font Awesome "piggy-bank" icon unicode character. /// @@ -8463,27 +6405,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Science" })] Pills = 0xF484, - /// - /// The Font Awesome "pinterest" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pinterest" })] - Pinterest = 0xF0D2, - - /// - /// The Font Awesome "pinterest-p" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pinterest p" })] - PinterestP = 0xF231, - - /// - /// The Font Awesome "square-pinterest" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square pinterest" })] - PinterestSquare = 0xF0D3, - /// /// The Font Awesome "pizza-slice" icon unicode character. /// @@ -8589,14 +6510,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] PlayCircle = 0xF144, - /// - /// The Font Awesome "playstation" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "playstation" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - Playstation = 0xF3DF, - /// /// The Font Awesome "plug" icon unicode character. /// @@ -8787,13 +6700,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] Procedures = 0xF487, - /// - /// The Font Awesome "product-hunt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "product hunt" })] - ProductHunt = 0xF288, - /// /// The Font Awesome "diagram-project" icon unicode character. /// @@ -8815,13 +6721,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Household", "Humanitarian" })] PumpSoap = 0xE06B, - /// - /// The Font Awesome "pushed" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "pushed" })] - Pushed = 0xF3E1, - /// /// The Font Awesome "puzzle-piece" icon unicode character. /// @@ -8829,20 +6728,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Gaming" })] PuzzlePiece = 0xF12E, - /// - /// The Font Awesome "python" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "python" })] - Python = 0xF3E2, - - /// - /// The Font Awesome "qq" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "qq" })] - Qq = 0xF1D6, - /// /// The Font Awesome "qrcode" icon unicode character. /// @@ -8872,20 +6757,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] Quidditch = 0xF458, - /// - /// The Font Awesome "quinscape" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "quinscape" })] - Quinscape = 0xF459, - - /// - /// The Font Awesome "quora" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "quora" })] - Quora = 0xF2C4, - /// /// The Font Awesome "quote-left" icon unicode character. /// @@ -8949,48 +6820,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Marketing", "Sports + Fitness" })] RankingStar = 0xE561, - /// - /// The Font Awesome "raspberry-pi" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "raspberry pi" })] - RaspberryPi = 0xF7BB, - - /// - /// The Font Awesome "ravelry" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ravelry" })] - Ravelry = 0xF2D9, - - /// - /// The Font Awesome "react" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "react" })] - React = 0xF41B, - - /// - /// The Font Awesome "reacteurope" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "reacteurope" })] - Reacteurope = 0xF75D, - - /// - /// The Font Awesome "readme" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "readme" })] - Readme = 0xF4D5, - - /// - /// The Font Awesome "rebel" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "rebel" })] - Rebel = 0xF1D0, - /// /// The Font Awesome "receipt" icon unicode character. /// @@ -9012,34 +6841,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Maps" })] Recycle = 0xF1B8, - /// - /// The Font Awesome "reddit" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "reddit" })] - Reddit = 0xF1A1, - - /// - /// The Font Awesome "reddit-alien" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "reddit alien" })] - RedditAlien = 0xF281, - - /// - /// The Font Awesome "square-reddit" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square reddit" })] - RedditSquare = 0xF1A2, - - /// - /// The Font Awesome "redhat" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "redhat", "linux", "operating system", "os" })] - Redhat = 0xF7BC, - /// /// The Font Awesome "arrow-rotate-right" icon unicode character. /// @@ -9054,13 +6855,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] RedoAlt = 0xF2F9, - /// - /// The Font Awesome "red-river" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "red river" })] - RedRiver = 0xF3E3, - /// /// The Font Awesome "registered" icon unicode character. /// @@ -9075,13 +6869,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] RemoveFormat = 0xF87D, - /// - /// The Font Awesome "renren" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "renren" })] - Renren = 0xF18B, - /// /// The Font Awesome "repeat" icon unicode character. /// @@ -9103,13 +6890,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ReplyAll = 0xF122, - /// - /// The Font Awesome "replyd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "replyd" })] - Replyd = 0xF3E6, - /// /// The Font Awesome "republican" icon unicode character. /// @@ -9117,20 +6897,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Political" })] Republican = 0xF75E, - /// - /// The Font Awesome "researchgate" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "researchgate" })] - Researchgate = 0xF4F8, - - /// - /// The Font Awesome "resolving" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "resolving" })] - Resolving = 0xF3E7, - /// /// The Font Awesome "restroom" icon unicode character. /// @@ -9145,13 +6911,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Social" })] Retweet = 0xF079, - /// - /// The Font Awesome "rev" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "rev" })] - Rev = 0xF5B2, - /// /// The Font Awesome "ribbon" icon unicode character. /// @@ -9236,20 +6995,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps", "Science Fiction", "Transportation" })] Rocket = 0xF135, - /// - /// The Font Awesome "rocketchat" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "rocketchat" })] - Rocketchat = 0xF3E8, - - /// - /// The Font Awesome "rockrms" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "rockrms" })] - Rockrms = 0xF3E9, - /// /// The Font Awesome "route" icon unicode character. /// @@ -9257,13 +7002,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Camping", "Maps", "Moving" })] Route = 0xF4D7, - /// - /// The Font Awesome "r-project" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "r project" })] - RProject = 0xF4F7, - /// /// The Font Awesome "rss" icon unicode character. /// @@ -9369,13 +7107,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] SadTear = 0xF5B4, - /// - /// The Font Awesome "safari" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "safari", "browser" })] - Safari = 0xF267, - /// /// The Font Awesome "sailboat" icon unicode character. /// @@ -9383,20 +7114,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime", "Transportation" })] Sailboat = 0xE445, - /// - /// The Font Awesome "salesforce" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "salesforce" })] - Salesforce = 0xF83B, - - /// - /// The Font Awesome "sass" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sass" })] - Sass = 0xF41E, - /// /// The Font Awesome "satellite" icon unicode character. /// @@ -9418,13 +7135,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Devices + Hardware", "Files" })] Save = 0xF0C7, - /// - /// The Font Awesome "schlix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "schlix" })] - Schlix = 0xF3EA, - /// /// The Font Awesome "school" icon unicode character. /// @@ -9474,13 +7184,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Construction" })] Screwdriver = 0xF54A, - /// - /// The Font Awesome "scribd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "scribd" })] - Scribd = 0xF28A, - /// /// The Font Awesome "scroll" icon unicode character. /// @@ -9509,13 +7212,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] SearchDollar = 0xF688, - /// - /// The Font Awesome "searchengin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "searchengin" })] - Searchengin = 0xF3EB, - /// /// The Font Awesome "magnifying-glass-location" icon unicode character. /// @@ -9551,20 +7247,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Charity", "Energy", "Food + Beverage", "Fruits + Vegetables", "Humanitarian", "Nature", "Science" })] Seedling = 0xF4D8, - /// - /// The Font Awesome "sellcast" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sellcast", "eercast" })] - Sellcast = 0xF2DA, - - /// - /// The Font Awesome "sellsy" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sellsy" })] - Sellsy = 0xF213, - /// /// The Font Awesome "server" icon unicode character. /// @@ -9572,13 +7254,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] Server = 0xF233, - /// - /// The Font Awesome "servicestack" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "servicestack" })] - Servicestack = 0xF3EC, - /// /// The Font Awesome "shapes" icon unicode character. /// @@ -9684,13 +7359,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Logistics", "Shopping" })] ShippingFast = 0xF48B, - /// - /// The Font Awesome "shirtsinbulk" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "shirtsinbulk" })] - Shirtsinbulk = 0xF214, - /// /// The Font Awesome "shoe-prints" icon unicode character. /// @@ -9698,13 +7366,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Clothing + Fashion", "Maps", "Sports + Fitness" })] ShoePrints = 0xF54B, - /// - /// The Font Awesome "shopify" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "shopify" })] - Shopify = 0xF957, - /// /// The Font Awesome "shop-lock" icon unicode character. /// @@ -9740,13 +7401,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] ShopSlash = 0xE070, - /// - /// The Font Awesome "shopware" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "shopware" })] - Shopware = 0xF5B5, - /// /// The Font Awesome "shower" icon unicode character. /// @@ -9817,13 +7471,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] SimCard = 0xF7C4, - /// - /// The Font Awesome "simplybuilt" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "simplybuilt" })] - Simplybuilt = 0xF215, - /// /// The Font Awesome "sink" icon unicode character. /// @@ -9831,13 +7478,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Household" })] Sink = 0xE06D, - /// - /// The Font Awesome "sistrix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sistrix" })] - Sistrix = 0xF3EE, - /// /// The Font Awesome "sitemap" icon unicode character. /// @@ -9845,13 +7485,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Coding" })] Sitemap = 0xF0E8, - /// - /// The Font Awesome "sith" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sith" })] - Sith = 0xF512, - /// /// The Font Awesome "person-skating" icon unicode character. /// @@ -9859,13 +7492,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] Skating = 0xF7C5, - /// - /// The Font Awesome "sketch" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sketch", "app", "design", "interface" })] - Sketch = 0xF7C6, - /// /// The Font Awesome "person-skiing" icon unicode character. /// @@ -9894,35 +7520,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Alert", "Gaming", "Halloween", "Humanitarian", "Medical + Health", "Science", "Security" })] SkullCrossbones = 0xF714, - /// - /// The Font Awesome "skyatlas" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "skyatlas" })] - Skyatlas = 0xF216, - - /// - /// The Font Awesome "skype" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "skype" })] - Skype = 0xF17E, - - /// - /// The Font Awesome "slack" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "slack", "anchor", "hash", "hashtag" })] - Slack = 0xF198, - - /// - /// The Font Awesome "slack" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF198. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "slack", "anchor", "hash", "hashtag" })] - SlackHash = 0xF3EF, - /// /// The Font Awesome "slash" icon unicode character. /// @@ -9944,13 +7541,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Editing", "Media Playback", "Music + Audio", "Photos + Images" })] SlidersH = 0xF1DE, - /// - /// The Font Awesome "slideshare" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "slideshare" })] - Slideshare = 0xF1E7, - /// /// The Font Awesome "face-smile" icon unicode character. /// @@ -10000,28 +7590,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Communication" })] Sms = 0xF7CD, - /// - /// The Font Awesome "snapchat" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "snapchat" })] - Snapchat = 0xF2AB, - - /// - /// The Font Awesome "snapchat" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF2AB. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "snapchat" })] - SnapchatGhost = 0xF2AC, - - /// - /// The Font Awesome "square-snapchat" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square snapchat" })] - SnapchatSquare = 0xF2AD, - /// /// The Font Awesome "person-snowboarding" icon unicode character. /// @@ -10176,21 +7744,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] SortUp = 0xF0DE, - /// - /// The Font Awesome "soundcloud" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "soundcloud" })] - [FontAwesomeCategoriesAttribute(new[] { "Music + Audio" })] - Soundcloud = 0xF1BE, - - /// - /// The Font Awesome "sourcetree" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sourcetree" })] - Sourcetree = 0xF7D3, - /// /// The Font Awesome "spa" icon unicode character. /// @@ -10205,20 +7758,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Transportation" })] SpaceShuttle = 0xF197, - /// - /// The Font Awesome "speakap" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "speakap" })] - Speakap = 0xF3F3, - - /// - /// The Font Awesome "speaker-deck" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "speaker deck" })] - SpeakerDeck = 0xF83C, - /// /// The Font Awesome "spell-check" icon unicode character. /// @@ -10247,14 +7786,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design" })] Splotch = 0xF5BC, - /// - /// The Font Awesome "spotify" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "spotify" })] - [FontAwesomeCategoriesAttribute(new[] { "Music + Audio" })] - Spotify = 0xF1BC, - /// /// The Font Awesome "spray-can" icon unicode character. /// @@ -10304,13 +7835,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] SquareRootAlt = 0xF698, - /// - /// The Font Awesome "squarespace" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "squarespace" })] - Squarespace = 0xF5BE, - /// /// The Font Awesome "square-virus" icon unicode character. /// @@ -10325,27 +7849,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] SquareXmark = 0xF2D3, - /// - /// The Font Awesome "stack-exchange" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stack exchange" })] - StackExchange = 0xF18D, - - /// - /// The Font Awesome "stack-overflow" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stack overflow" })] - StackOverflow = 0xF16C, - - /// - /// The Font Awesome "stackpath" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stackpath" })] - Stackpath = 0xF842, - /// /// The Font Awesome "staff-snake" icon unicode character. /// @@ -10416,37 +7919,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] StarOfLife = 0xF621, - /// - /// The Font Awesome "staylinked" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "staylinked" })] - Staylinked = 0xF3F5, - - /// - /// The Font Awesome "steam" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "steam" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - Steam = 0xF1B6, - - /// - /// The Font Awesome "square-steam" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square steam" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - SteamSquare = 0xF1B7, - - /// - /// The Font Awesome "steam-symbol" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "steam symbol" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - SteamSymbol = 0xF3F6, - /// /// The Font Awesome "backward-step" icon unicode character. /// @@ -10468,13 +7940,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] Stethoscope = 0xF0F1, - /// - /// The Font Awesome "sticker-mule" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "sticker mule" })] - StickerMule = 0xF3F7, - /// /// The Font Awesome "note-sticky" icon unicode character. /// @@ -10531,13 +7996,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] StoreSlash = 0xE071, - /// - /// The Font Awesome "strava" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "strava" })] - Strava = 0xF428, - /// /// The Font Awesome "bars-staggered" icon unicode character. /// @@ -10559,22 +8017,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Strikethrough = 0xF0CC, - /// - /// The Font Awesome "stripe" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stripe" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - Stripe = 0xF429, - - /// - /// The Font Awesome "stripe-s" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stripe s" })] - [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] - StripeS = 0xF42A, - /// /// The Font Awesome "stroopwafel" icon unicode character. /// @@ -10582,27 +8024,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Spinners" })] Stroopwafel = 0xF551, - /// - /// The Font Awesome "studiovinari" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "studiovinari" })] - Studiovinari = 0xF3F8, - - /// - /// The Font Awesome "stumbleupon" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stumbleupon" })] - Stumbleupon = 0xF1A4, - - /// - /// The Font Awesome "stumbleupon-circle" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "stumbleupon circle" })] - StumbleuponCircle = 0xF1A3, - /// /// The Font Awesome "subscript" icon unicode character. /// @@ -10645,13 +8066,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Weather" })] SunPlantWilt = 0xE57A, - /// - /// The Font Awesome "superpowers" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "superpowers" })] - Superpowers = 0xF2DD, - /// /// The Font Awesome "superscript" icon unicode character. /// @@ -10659,13 +8073,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Mathematics", "Text Formatting" })] Superscript = 0xF12B, - /// - /// The Font Awesome "supple" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "supple" })] - Supple = 0xF3F9, - /// /// The Font Awesome "face-surprise" icon unicode character. /// @@ -10673,13 +8080,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] Surprise = 0xF5C2, - /// - /// The Font Awesome "suse" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "suse", "linux", "operating system", "os" })] - Suse = 0xF7D6, - /// /// The Font Awesome "swatchbook" icon unicode character. /// @@ -10687,13 +8087,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design" })] Swatchbook = 0xF5C3, - /// - /// The Font Awesome "swift" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "swift" })] - Swift = 0xF8E1, - /// /// The Font Awesome "person-swimming" icon unicode character. /// @@ -10708,13 +8101,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] SwimmingPool = 0xF5C5, - /// - /// The Font Awesome "symfony" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "symfony" })] - Symfony = 0xF83D, - /// /// The Font Awesome "synagogue" icon unicode character. /// @@ -10842,13 +8228,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Maps", "Transportation", "Travel + Hotel" })] Taxi = 0xF1BA, - /// - /// The Font Awesome "teamspeak" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "teamspeak" })] - Teamspeak = 0xF4F9, - /// /// The Font Awesome "teeth" icon unicode character. /// @@ -10863,21 +8242,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] TeethOpen = 0xF62F, - /// - /// The Font Awesome "telegram" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "telegram" })] - Telegram = 0xF2C6, - - /// - /// The Font Awesome "telegram" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF2C6. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "telegram" })] - TelegramPlane = 0xF3FE, - /// /// The Font Awesome "temperature-arrow-down" icon unicode character. /// @@ -10906,13 +8270,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Science", "Weather" })] TemperatureLow = 0xF76B, - /// - /// The Font Awesome "tencent-weibo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "tencent weibo" })] - TencentWeibo = 0xF1D5, - /// /// The Font Awesome "tenge-sign" icon unicode character. /// @@ -10997,27 +8354,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Education" })] TheaterMasks = 0xF630, - /// - /// The Font Awesome "themeco" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "themeco" })] - Themeco = 0xF5C6, - - /// - /// The Font Awesome "themeisle" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "themeisle" })] - Themeisle = 0xF2B2, - - /// - /// The Font Awesome "the-red-yeti" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "the red yeti" })] - TheRedYeti = 0xF69D, - /// /// The Font Awesome "thermometer" icon unicode character. /// @@ -11060,13 +8396,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Weather" })] ThermometerThreeQuarters = 0xF2C8, - /// - /// The Font Awesome "think-peaks" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "think peaks" })] - ThinkPeaks = 0xF731, - /// /// The Font Awesome "table-cells-large" icon unicode character. /// @@ -11270,13 +8599,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Transportation" })] Tractor = 0xF722, - /// - /// The Font Awesome "trade-federation" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "trade federation" })] - TradeFederation = 0xF513, - /// /// The Font Awesome "trademark" icon unicode character. /// @@ -11291,14 +8613,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Maps" })] TrafficLight = 0xF637, - /// - /// The Font Awesome "trailer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "trailer", "carry", "haul", "moving", "travel" })] - [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Camping", "Moving" })] - Trailer = 0xF941, - /// /// The Font Awesome "train" icon unicode character. /// @@ -11376,19 +8690,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Travel + Hotel" })] TreeCity = 0xE587, - /// - /// The Font Awesome "trello" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "trello", "atlassian" })] - Trello = 0xF181, - - /// - /// The Font Awesome "tripadvisor" icon unicode character. - /// - [Obsolete] - Tripadvisor = 0xF262, - /// /// The Font Awesome "trophy" icon unicode character. /// @@ -11501,20 +8802,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Communication", "Maps" })] Tty = 0xF1E4, - /// - /// The Font Awesome "tumblr" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "tumblr" })] - Tumblr = 0xF173, - - /// - /// The Font Awesome "square-tumblr" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square tumblr" })] - TumblrSquare = 0xF174, - /// /// The Font Awesome "turkish-lira-sign" icon unicode character. /// @@ -11529,63 +8816,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware", "Film + Video", "Household", "Travel + Hotel" })] Tv = 0xF26C, - /// - /// The Font Awesome "twitch" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "twitch" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - Twitch = 0xF1E8, - - /// - /// The Font Awesome "twitter" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "twitter", "social network", "tweet" })] - Twitter = 0xF099, - - /// - /// The Font Awesome "square-twitter" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square twitter", "social network", "tweet" })] - TwitterSquare = 0xF081, - - /// - /// The Font Awesome "typo3" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "typo3" })] - Typo3 = 0xF42B, - - /// - /// The Font Awesome "uber" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "uber" })] - Uber = 0xF402, - - /// - /// The Font Awesome "ubuntu" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ubuntu", "linux", "operating system", "os" })] - Ubuntu = 0xF7DF, - - /// - /// The Font Awesome "uikit" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "uikit" })] - Uikit = 0xF403, - - /// - /// The Font Awesome "umbraco" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "umbraco" })] - Umbraco = 0xF8E8, - /// /// The Font Awesome "umbrella" icon unicode character. /// @@ -11621,19 +8851,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] UndoAlt = 0xF2EA, - /// - /// The Font Awesome "uniregistry" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "uniregistry" })] - Uniregistry = 0xF404, - - /// - /// The Font Awesome "unity" icon unicode character. - /// - [Obsolete] - Unity = 0xF949, - /// /// The Font Awesome "universal-access" icon unicode character. /// @@ -11669,13 +8886,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Security" })] UnlockAlt = 0xF13E, - /// - /// The Font Awesome "untappd" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "untappd" })] - Untappd = 0xF405, - /// /// The Font Awesome "upload" icon unicode character. /// @@ -11683,20 +8893,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Devices + Hardware" })] Upload = 0xF093, - /// - /// The Font Awesome "ups" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ups", "united parcel service", "package", "shipping" })] - Ups = 0xF7E0, - - /// - /// The Font Awesome "usb" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "usb" })] - Usb = 0xF287, - /// /// The Font Awesome "user" icon unicode character. /// @@ -11921,20 +9117,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserTimes = 0xF235, - /// - /// The Font Awesome "usps" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "usps", "american", "package", "shipping", "usa" })] - Usps = 0xF7E1, - - /// - /// The Font Awesome "ussunnah" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "ussunnah" })] - Ussunnah = 0xF407, - /// /// The Font Awesome "utensils" icon unicode character. /// @@ -11949,13 +9131,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Household", "Maps" })] UtensilSpoon = 0xF2E5, - /// - /// The Font Awesome "vaadin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vaadin" })] - Vaadin = 0xF408, - /// /// The Font Awesome "vault" icon unicode character. /// @@ -12005,27 +9180,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Clothing + Fashion", "Maps" })] VestPatches = 0xE086, - /// - /// The Font Awesome "viacoin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "viacoin" })] - Viacoin = 0xF237, - - /// - /// The Font Awesome "viadeo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "viadeo" })] - Viadeo = 0xF2A9, - - /// - /// The Font Awesome "square-viadeo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square viadeo" })] - ViadeoSquare = 0xF2AA, - /// /// The Font Awesome "vial" icon unicode character. /// @@ -12054,13 +9208,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Science" })] VialVirus = 0xE597, - /// - /// The Font Awesome "viber" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "viber" })] - Viber = 0xF409, - /// /// The Font Awesome "video" icon unicode character. /// @@ -12082,34 +9229,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Religion" })] Vihara = 0xF6A7, - /// - /// The Font Awesome "vimeo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vimeo" })] - Vimeo = 0xF40A, - - /// - /// The Font Awesome "square-vimeo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square vimeo" })] - VimeoSquare = 0xF194, - - /// - /// The Font Awesome "vimeo-v" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vimeo v", "vimeo" })] - VimeoV = 0xF27D, - - /// - /// The Font Awesome "vine" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vine" })] - Vine = 0xF1CA, - /// /// The Font Awesome "virus" icon unicode character. /// @@ -12145,20 +9264,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] VirusSlash = 0xE075, - /// - /// The Font Awesome "vk" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vk" })] - Vk = 0xF189, - - /// - /// The Font Awesome "vnv" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vnv" })] - Vnv = 0xF40B, - /// /// The Font Awesome "voicemail" icon unicode character. /// @@ -12222,13 +9327,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] VrCardboard = 0xF729, - /// - /// The Font Awesome "vuejs" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "vuejs" })] - Vuejs = 0xF41F, - /// /// The Font Awesome "walkie-talkie" icon unicode character. /// @@ -12285,27 +9383,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] WaveSquare = 0xF83E, - /// - /// The Font Awesome "waze" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "waze" })] - Waze = 0xF83F, - - /// - /// The Font Awesome "weebly" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "weebly" })] - Weebly = 0xF5CC, - - /// - /// The Font Awesome "weibo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "weibo" })] - Weibo = 0xF18A, - /// /// The Font Awesome "weight-scale" icon unicode character. /// @@ -12320,27 +9397,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] WeightHanging = 0xF5CD, - /// - /// The Font Awesome "weixin" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "weixin" })] - Weixin = 0xF1D7, - - /// - /// The Font Awesome "whatsapp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "whatsapp" })] - Whatsapp = 0xF232, - - /// - /// The Font Awesome "square-whatsapp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square whatsapp" })] - WhatsappSquare = 0xF40C, - /// /// The Font Awesome "wheat-awn" icon unicode character. /// @@ -12369,13 +9425,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Humanitarian", "Maps", "Medical + Health", "Transportation", "Travel + Hotel", "Users + People" })] WheelchairMove = 0xE2CE, - /// - /// The Font Awesome "whmcs" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "whmcs" })] - Whmcs = 0xF40D, - /// /// The Font Awesome "wifi" icon unicode character. /// @@ -12383,13 +9432,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Connectivity", "Humanitarian", "Maps", "Toggle", "Travel + Hotel" })] Wifi = 0xF1EB, - /// - /// The Font Awesome "wikipedia-w" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wikipedia w" })] - WikipediaW = 0xF266, - /// /// The Font Awesome "wind" icon unicode character. /// @@ -12425,13 +9467,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Coding" })] WindowRestore = 0xF2D2, - /// - /// The Font Awesome "windows" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "windows", "microsoft", "operating system", "os" })] - Windows = 0xF17A, - /// /// The Font Awesome "wine-bottle" icon unicode character. /// @@ -12453,28 +9488,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Travel + Hotel" })] WineGlassAlt = 0xF5CE, - /// - /// The Font Awesome "wix" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wix" })] - Wix = 0xF5CF, - - /// - /// The Font Awesome "wizards-of-the-coast" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wizards of the coast", "dungeons & dragons", "d&d", "dnd", "fantasy", "game", "gaming", "tabletop" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - WizardsOfTheCoast = 0xF730, - - /// - /// The Font Awesome "wolf-pack-battalion" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wolf pack battalion" })] - WolfPackBattalion = 0xF514, - /// /// The Font Awesome "won-sign" icon unicode character. /// @@ -12482,20 +9495,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Money" })] WonSign = 0xF159, - /// - /// The Font Awesome "wordpress" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wordpress" })] - Wordpress = 0xF19A, - - /// - /// The Font Awesome "wordpress-simple" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wordpress simple" })] - WordpressSimple = 0xF411, - /// /// The Font Awesome "worm" icon unicode character. /// @@ -12503,34 +9502,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Animals", "Disaster + Crisis", "Humanitarian", "Nature" })] Worm = 0xE599, - /// - /// The Font Awesome "wpbeginner" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wpbeginner" })] - Wpbeginner = 0xF297, - - /// - /// The Font Awesome "wpexplorer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wpexplorer" })] - Wpexplorer = 0xF2DE, - - /// - /// The Font Awesome "wpforms" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wpforms" })] - Wpforms = 0xF298, - - /// - /// The Font Awesome "wpressr" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "wpressr", "rendact" })] - Wpressr = 0xF3E4, - /// /// The Font Awesome "wrench" icon unicode character. /// @@ -12538,28 +9509,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Construction", "Maps" })] Wrench = 0xF0AD, - /// - /// The Font Awesome "xbox" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "xbox" })] - [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] - Xbox = 0xF412, - - /// - /// The Font Awesome "xing" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "xing" })] - Xing = 0xF168, - - /// - /// The Font Awesome "square-xing" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square xing" })] - XingSquare = 0xF169, - /// /// The Font Awesome "xmarks-lines" icon unicode character. /// @@ -12574,55 +9523,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] XRay = 0xF497, - /// - /// The Font Awesome "yahoo" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yahoo" })] - Yahoo = 0xF19E, - - /// - /// The Font Awesome "yammer" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yammer" })] - Yammer = 0xF840, - - /// - /// The Font Awesome "yandex" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yandex" })] - Yandex = 0xF413, - - /// - /// The Font Awesome "yandex-international" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yandex international" })] - YandexInternational = 0xF414, - - /// - /// The Font Awesome "yarn" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yarn" })] - Yarn = 0xF7E3, - - /// - /// The Font Awesome "y-combinator" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "y combinator" })] - YCombinator = 0xF23B, - - /// - /// The Font Awesome "yelp" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yelp" })] - Yelp = 0xF1E9, - /// /// The Font Awesome "yen-sign" icon unicode character. /// @@ -12637,33 +9537,4 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Religion", "Spinners" })] YinYang = 0xF6AD, - /// - /// The Font Awesome "yoast" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "yoast" })] - Yoast = 0xF2B1, - - /// - /// The Font Awesome "youtube" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "youtube", "film", "video", "youtube-play", "youtube-square" })] - [FontAwesomeCategoriesAttribute(new[] { "Film + Video" })] - Youtube = 0xF167, - - /// - /// The Font Awesome "square-youtube" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "square youtube" })] - YoutubeSquare = 0xF431, - - /// - /// The Font Awesome "zhihu" icon unicode character. - /// - [Obsolete] - [FontAwesomeSearchTerms(new[] { "zhihu" })] - Zhihu = 0xF63F, - } diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs index 3a1ab737e..48a1f7271 100644 --- a/Dalamud/Interface/GameFonts/GameFontManager.cs +++ b/Dalamud/Interface/GameFonts/GameFontManager.cs @@ -9,12 +9,13 @@ using System.Threading.Tasks; using Dalamud.Data; using Dalamud.Game; using Dalamud.Interface.Internal; +using Dalamud.Interface.Utility; using Dalamud.Utility.Timing; using ImGuiNET; using Lumina.Data.Files; using Serilog; -using static Dalamud.Interface.ImGuiHelpers; +using static Dalamud.Interface.Utility.ImGuiHelpers; namespace Dalamud.Interface.GameFonts; diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs index d3be8da95..0dd1410d5 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Numerics; +using Dalamud.Interface.Utility; using Dalamud.Utility; using ImGuiNET; diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs index aec5e9af4..411f203cc 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs @@ -123,16 +123,6 @@ public partial class FileDialog return this.isOk; } - /// - /// Gets the result of the selection. - /// - /// The result of the selection (file or folder path). If multiple entries were selected, they are separated with commas. - [Obsolete("Use GetResults() instead.", true)] - public string GetResult() - { - return string.Join(',', this.GetResults()); - } - /// /// Gets the result of the selection. /// diff --git a/Dalamud/Interface/Internal/Branding.cs b/Dalamud/Interface/Internal/Branding.cs new file mode 100644 index 000000000..4162cabeb --- /dev/null +++ b/Dalamud/Interface/Internal/Branding.cs @@ -0,0 +1,58 @@ +using System.IO; + +using Dalamud.IoC.Internal; + +namespace Dalamud.Interface.Internal; + +/// +/// Class containing various textures used by Dalamud windows for branding purposes. +/// +[ServiceManager.EarlyLoadedService] +#pragma warning disable SA1015 +[InherentDependency] // Can't load textures before this +#pragma warning restore SA1015 +internal class Branding : IServiceType, IDisposable +{ + private readonly Dalamud dalamud; + private readonly TextureManager tm; + + /// + /// Initializes a new instance of the class. + /// + /// Dalamud instance. + /// TextureManager instance. + [ServiceManager.ServiceConstructor] + public Branding(Dalamud dalamud, TextureManager tm) + { + this.dalamud = dalamud; + this.tm = tm; + + this.LoadTextures(); + } + + /// + /// Gets a full-size Dalamud logo texture. + /// + public IDalamudTextureWrap Logo { get; private set; } = null!; + + /// + /// Gets a small Dalamud logo texture. + /// + public IDalamudTextureWrap LogoSmall { get; private set; } = null!; + + /// + public void Dispose() + { + this.Logo.Dispose(); + this.LogoSmall.Dispose(); + } + + private void LoadTextures() + { + this.Logo = this.tm.GetTextureFromFile(new FileInfo(Path.Combine(this.dalamud.AssetDirectory.FullName, "UIRes", "logo.png"))) + ?? throw new Exception("Could not load logo."); + + this.LogoSmall = this.tm.GetTextureFromFile(new FileInfo(Path.Combine(this.dalamud.AssetDirectory.FullName, "UIRes", "tsmLogo.png"))) + ?? throw new Exception("Could not load TSM logo."); + } +} diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index a8a769070..a7b1e80b5 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -21,8 +21,9 @@ using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.SelfTest; using Dalamud.Interface.Internal.Windows.Settings; using Dalamud.Interface.Internal.Windows.StyleEditor; -using Dalamud.Interface.Raii; using Dalamud.Interface.Style; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging; using Dalamud.Logging.Internal; @@ -65,9 +66,6 @@ internal class DalamudInterface : IDisposable, IServiceType private readonly BranchSwitcherWindow branchSwitcherWindow; private readonly HitchSettingsWindow hitchSettingsWindow; - private readonly TextureWrap logoTexture; - private readonly TextureWrap tsmLogoTexture; - private bool isCreditsDarkening = false; private OutCubic creditsDarkeningAnimation = new(TimeSpan.FromSeconds(10)); @@ -91,7 +89,8 @@ internal class DalamudInterface : IDisposable, IServiceType Dalamud dalamud, DalamudConfiguration configuration, InterfaceManager.InterfaceManagerWithScene interfaceManagerWithScene, - PluginImageCache pluginImageCache) + PluginImageCache pluginImageCache, + Branding branding) { var interfaceManager = interfaceManagerWithScene.Manager; this.WindowSystem = new WindowSystem("DalamudCore"); @@ -135,26 +134,13 @@ internal class DalamudInterface : IDisposable, IServiceType interfaceManager.Draw += this.OnDraw; - var logoTex = - interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png")); - var tsmLogoTex = - interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "tsmLogo.png")); - - if (logoTex == null || tsmLogoTex == null) - { - throw new Exception("Failed to load logo textures"); - } - - this.logoTexture = logoTex; - this.tsmLogoTexture = tsmLogoTex; - var tsm = Service.Get(); - tsm.AddEntryCore(Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), this.tsmLogoTexture, this.OpenPluginInstaller); - tsm.AddEntryCore(Loc.Localize("TSMDalamudSettings", "Dalamud Settings"), this.tsmLogoTexture, this.OpenSettings); + tsm.AddEntryCore(Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), branding.LogoSmall, this.OpenPluginInstaller); + tsm.AddEntryCore(Loc.Localize("TSMDalamudSettings", "Dalamud Settings"), branding.LogoSmall, this.OpenSettings); if (!configuration.DalamudBetaKind.IsNullOrEmpty()) { - tsm.AddEntryCore(Loc.Localize("TSMDalamudDevMenu", "Developer Menu"), this.tsmLogoTexture, () => this.isImGuiDrawDevMenu = true); + tsm.AddEntryCore(Loc.Localize("TSMDalamudDevMenu", "Developer Menu"), branding.LogoSmall, () => this.isImGuiDrawDevMenu = true); } this.creditsDarkeningAnimation.Point1 = Vector2.Zero; @@ -191,9 +177,6 @@ internal class DalamudInterface : IDisposable, IServiceType this.consoleWindow.Dispose(); this.pluginWindow.Dispose(); this.titleScreenMenuWindow.Dispose(); - - this.logoTexture.Dispose(); - this.tsmLogoTexture.Dispose(); } #region Open @@ -634,9 +617,7 @@ internal class DalamudInterface : IDisposable, IServiceType ImGui.EndMenu(); } - - var startInfo = Service.Get(); - + var logSynchronously = configuration.LogSynchronously; if (ImGui.MenuItem("Log Synchronously", null, ref logSynchronously)) { @@ -644,10 +625,10 @@ internal class DalamudInterface : IDisposable, IServiceType configuration.QueueSave(); EntryPoint.InitLogging( - startInfo.LogPath!, - startInfo.BootShowConsole, + dalamud.StartInfo.LogPath!, + dalamud.StartInfo.BootShowConsole, configuration.LogSynchronously, - startInfo.LogName); + dalamud.StartInfo.LogName); } var antiDebug = Service.Get(); @@ -766,7 +747,7 @@ internal class DalamudInterface : IDisposable, IServiceType } ImGui.MenuItem(Util.AssemblyVersion, false); - ImGui.MenuItem(startInfo.GameVersion?.ToString() ?? "Unknown version", false); + ImGui.MenuItem(dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false); ImGui.MenuItem($"D: {Util.GetGitHash()}[{Util.GetGitCommitCount()}] CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.Interop.Resolver.Version}]", false); ImGui.MenuItem($"CLR: {Environment.Version}", false); diff --git a/Dalamud/Interface/Internal/DalamudTextureWrap.cs b/Dalamud/Interface/Internal/DalamudTextureWrap.cs index 039873f1f..9737d9f7b 100644 --- a/Dalamud/Interface/Internal/DalamudTextureWrap.cs +++ b/Dalamud/Interface/Internal/DalamudTextureWrap.cs @@ -1,4 +1,4 @@ -using System; +using System.Numerics; using ImGuiScene; @@ -8,8 +8,27 @@ namespace Dalamud.Interface.Internal; /// Base TextureWrap interface for all Dalamud-owned texture wraps. /// Used to avoid referencing ImGuiScene. /// -public interface IDalamudTextureWrap : TextureWrap +public interface IDalamudTextureWrap : IDisposable { + /// + /// Gets a texture handle suitable for direct use with ImGui functions. + /// + IntPtr ImGuiHandle { get; } + + /// + /// Gets the width of the texture. + /// + int Width { get; } + + /// + /// Gets the height of the texture. + /// + int Height { get; } + + /// + /// Gets the size vector of the texture using Width, Height. + /// + Vector2 Size => new(this.Width, this.Height); } /// diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 1e1c25324..93d9bb1dd 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -19,6 +19,7 @@ using Dalamud.Interface.GameFonts; using Dalamud.Interface.Internal.ManagedAsserts; using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Style; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; using Dalamud.Utility.Timing; @@ -240,7 +241,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// /// The filepath to load. /// A texture, ready to use in ImGui. - public TextureWrap? LoadImage(string filePath) + public IDalamudTextureWrap? LoadImage(string filePath) { if (this.scene == null) throw new InvalidOperationException("Scene isn't ready."); @@ -263,7 +264,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// /// The data to load. /// A texture, ready to use in ImGui. - public TextureWrap? LoadImage(byte[] imageData) + public IDalamudTextureWrap? LoadImage(byte[] imageData) { if (this.scene == null) throw new InvalidOperationException("Scene isn't ready."); @@ -289,7 +290,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// The height in pixels. /// The number of channels. /// A texture, ready to use in ImGui. - public TextureWrap? LoadImageRaw(byte[] imageData, int width, int height, int numChannels) + public IDalamudTextureWrap? LoadImageRaw(byte[] imageData, int width, int height, int numChannels) { if (this.scene == null) throw new InvalidOperationException("Scene isn't ready."); @@ -325,7 +326,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// The height in pixels. /// Format of the texture. /// A texture, ready to use in ImGui. - public IDalamudTextureWrap LoadImageFromDxgiFormat(Span data, int pitch, int width, int height, Format dxgiFormat) + public DalamudTextureWrap LoadImageFromDxgiFormat(Span data, int pitch, int width, int height, Format dxgiFormat) { if (this.scene == null) throw new InvalidOperationException("Scene isn't ready."); @@ -562,10 +563,10 @@ internal class InterfaceManager : IDisposable, IServiceType return; } - var startInfo = Service.Get(); + var startInfo = Service.Get().StartInfo; var configuration = Service.Get(); - var iniFileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(startInfo.ConfigurationPath), "dalamudUI.ini")); + var iniFileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(startInfo.ConfigurationPath)!, "dalamudUI.ini")); try { @@ -1054,7 +1055,7 @@ internal class InterfaceManager : IDisposable, IServiceType } [ServiceManager.CallWhenServicesReady] - private void ContinueConstruction(SigScanner sigScanner, Framework framework) + private void ContinueConstruction(TargetSigScanner sigScanner, Framework framework) { this.address.Setup(sigScanner); framework.RunOnFrameworkThread(() => @@ -1275,6 +1276,7 @@ internal class InterfaceManager : IDisposable, IServiceType /// /// Represents an instance of InstanceManager with scene ready for use. /// + [ServiceManager.Service] public class InterfaceManagerWithScene : IServiceType { /// diff --git a/Dalamud/Interface/Internal/Notifications/NotificationManager.cs b/Dalamud/Interface/Internal/Notifications/NotificationManager.cs index e941db7a4..67ad3ee8f 100644 --- a/Dalamud/Interface/Internal/Notifications/NotificationManager.cs +++ b/Dalamud/Interface/Internal/Notifications/NotificationManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Utility; using ImGuiNET; @@ -105,7 +106,7 @@ internal class NotificationManager : IServiceType ImGuiHelpers.ForceNextWindowMainViewport(); ImGui.SetNextWindowBgAlpha(opacity); - ImGui.SetNextWindowPos(new Vector2(viewportSize.X - NotifyPaddingX, viewportSize.Y - NotifyPaddingY - height), ImGuiCond.Always, Vector2.One); + ImGui.SetNextWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(viewportSize.X - NotifyPaddingX, viewportSize.Y - NotifyPaddingY - height), ImGuiCond.Always, Vector2.One); ImGui.Begin(windowName, NotifyToastFlags); ImGui.PushTextWrapPos(viewportSize.X / 3.0f); diff --git a/Dalamud/Interface/Internal/TextureManager.cs b/Dalamud/Interface/Internal/TextureManager.cs index 1bc5198e3..40aa72913 100644 --- a/Dalamud/Interface/Internal/TextureManager.cs +++ b/Dalamud/Interface/Internal/TextureManager.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Numerics; using Dalamud.Data; @@ -11,11 +9,14 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; -using ImGuiScene; using Lumina.Data.Files; +using Lumina.Data.Parsing.Tex.Buffers; +using SharpDX.DXGI; namespace Dalamud.Interface.Internal; +// TODO API10: Remove keepAlive from public APIs + /// /// Service responsible for loading and disposing ImGui texture wraps. /// @@ -23,9 +24,10 @@ namespace Dalamud.Interface.Internal; [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] #pragma warning disable SA1015 +[ResolveVia] [ResolveVia] #pragma warning restore SA1015 -internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionProvider +internal class TextureManager : IDisposable, IServiceType, ITextureProvider, ITextureSubstitutionProvider { private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex"; private const string HighResolutionIconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}_hr1.tex"; @@ -37,26 +39,28 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP private readonly Framework framework; private readonly DataManager dataManager; private readonly InterfaceManager im; - private readonly DalamudStartInfo startInfo; + + private readonly ClientLanguage language; private readonly Dictionary activeTextures = new(); - private TextureWrap? fallbackTextureWrap; + private IDalamudTextureWrap? fallbackTextureWrap; /// /// Initializes a new instance of the class. /// + /// Dalamud instance. /// Framework instance. /// DataManager instance. /// InterfaceManager instance. - /// DalamudStartInfo instance. [ServiceManager.ServiceConstructor] - public TextureManager(Framework framework, DataManager dataManager, InterfaceManager im, DalamudStartInfo startInfo) + public TextureManager(Dalamud dalamud, Framework framework, DataManager dataManager, InterfaceManager im) { this.framework = framework; this.dataManager = dataManager; this.im = im; - this.startInfo = startInfo; + + this.language = (ClientLanguage)dalamud.StartInfo.Language; this.framework.Update += this.FrameworkOnUpdate; @@ -76,16 +80,16 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP /// If null, default to the game's current language. /// /// - /// Prevent Dalamud from automatically unloading this icon to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// /// /// Null, if the icon does not exist in the specified configuration, or a texture wrap that can be used /// to render the icon. /// - public TextureManagerTextureWrap? GetIcon(uint iconId, ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.HiRes, ClientLanguage? language = null, bool keepAlive = false) + public IDalamudTextureWrap? GetIcon(uint iconId, ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.HiRes, ClientLanguage? language = null, bool keepAlive = false) { var path = this.GetIconPath(iconId, flags, language); - return path == null ? null : this.CreateWrap(path, keepAlive); + return path == null ? null : this.CreateWrap(path); } /// @@ -113,7 +117,7 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP if (this.dataManager.FileExists(path)) return path; - language ??= this.startInfo.Language; + language ??= this.language; var languageFolder = language switch { ClientLanguage.Japanese => "ja/", @@ -169,16 +173,16 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP /// You may only specify paths in the game's VFS. /// /// The path to the texture in the game's VFS. - /// Prevent Dalamud from automatically unloading this texture to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// Null, if the icon does not exist, or a texture wrap that can be used to render the texture. - public TextureManagerTextureWrap? GetTextureFromGame(string path, bool keepAlive = false) + public IDalamudTextureWrap? GetTextureFromGame(string path, bool keepAlive = false) { ArgumentException.ThrowIfNullOrEmpty(path); if (Path.IsPathRooted(path)) throw new ArgumentException("Use GetTextureFromFile() to load textures directly from a file.", nameof(path)); - return !this.dataManager.FileExists(path) ? null : this.CreateWrap(path, keepAlive); + return !this.dataManager.FileExists(path) ? null : this.CreateWrap(path); } /// @@ -188,12 +192,12 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP /// This API can load .png and .tex files. /// /// The FileInfo describing the image or texture file. - /// Prevent Dalamud from automatically unloading this texture to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// Null, if the file does not exist, or a texture wrap that can be used to render the texture. - public TextureManagerTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive = false) + public IDalamudTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive = false) { ArgumentNullException.ThrowIfNull(file); - return !file.Exists ? null : this.CreateWrap(file.FullName, keepAlive); + return !file.Exists ? null : this.CreateWrap(file.FullName); } /// @@ -201,16 +205,32 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP /// /// The texture to obtain a handle to. /// A texture wrap that can be used to render the texture. - public IDalamudTextureWrap? GetTexture(TexFile file) + /// Thrown when the graphics system is not available yet. Relevant for plugins when LoadRequiredState is set to 0 or 1. + /// Thrown when the given is not supported. Most likely is that the file is corrupt. + public IDalamudTextureWrap GetTexture(TexFile file) { ArgumentNullException.ThrowIfNull(file); if (!this.im.IsReady) throw new InvalidOperationException("Cannot create textures before scene is ready"); - -#pragma warning disable CS0618 - return (IDalamudTextureWrap)this.dataManager.GetImGuiTexture(file); -#pragma warning restore CS0618 + + var buffer = file.TextureBuffer; + var bpp = 1 << (((int)file.Header.Format & (int)TexFile.TextureFormat.BppMask) >> + (int)TexFile.TextureFormat.BppShift); + + var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false); + if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.im.SupportsDxgiFormat((Format)dxgiFormat)) + { + dxgiFormat = (int)Format.B8G8R8A8_UNorm; + buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8); + bpp = 32; + } + + var pitch = buffer is BlockCompressionTextureBuffer + ? Math.Max(1, (buffer.Width + 3) / 4) * 2 * bpp + : ((buffer.Width * bpp) + 7) / 8; + + return this.im.LoadImageFromDxgiFormat(buffer.RawData, pitch, buffer.Width, buffer.Height, (Format)dxgiFormat); } /// @@ -277,23 +297,21 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP TextureInfo? info; lock (this.activeTextures) { + // This either is a new texture, or it had been evicted and now wants to be drawn again. if (!this.activeTextures.TryGetValue(path, out info)) { - Debug.Assert(rethrow, "This should never run when getting outside of creator"); - info = new TextureInfo(); this.activeTextures.Add(path, info); } if (info == null) throw new Exception("null info in activeTextures"); - } - - if (info.KeepAliveCount == 0) + info.LastAccess = DateTime.UtcNow; - if (info is { Wrap: not null }) - return info; + if (info is { Wrap: not null }) + return info; + } if (!this.im.IsReady) throw new InvalidOperationException("Cannot create textures before scene is ready"); @@ -301,7 +319,7 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP // Substitute the path here for loading, instead of when getting the respective TextureInfo path = this.GetSubstitutedPath(path); - TextureWrap? wrap; + IDalamudTextureWrap? wrap; try { // We want to load this from the disk, probably, if the path has a root @@ -366,33 +384,6 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP return info; } - /// - /// Notify the system about an instance of a texture wrap being disposed. - /// If required conditions are met, the texture will be unloaded at the next update. - /// - /// The path to the texture. - /// Whether or not this handle was created in keep-alive mode. - internal void NotifyTextureDisposed(string path, bool keepAlive) - { - lock (this.activeTextures) - { - if (!this.activeTextures.TryGetValue(path, out var info)) - { - Log.Warning("Disposing texture that didn't exist: {Path}", path); - return; - } - - info.RefCount--; - - if (keepAlive) - info.KeepAliveCount--; - - // Clean it up by the next update. If it's re-requested in-between, we don't reload it. - if (info.RefCount <= 0) - info.LastAccess = default; - } - } - private static string FormatIconPath(uint iconId, string? type, bool highResolution) { var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat; @@ -404,27 +395,19 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP return string.Format(format, iconId / 1000, type, iconId); } - private TextureManagerTextureWrap? CreateWrap(string path, bool keepAlive) + private TextureManagerTextureWrap? CreateWrap(string path) { lock (this.activeTextures) { // This will create the texture. // That's fine, it's probably used immediately and this will let the plugin catch load errors. var info = this.GetInfo(path, rethrow: true); - - // We need to increase the refcounts here while locking the collection! - // Otherwise, if this is loaded from a task, cleanup might already try to delete it - // before it can be increased. - info.RefCount++; - - if (keepAlive) - info.KeepAliveCount++; - return new TextureManagerTextureWrap(path, info.Extents, keepAlive, this); + return new TextureManagerTextureWrap(path, info.Extents, this); } } - private void FrameworkOnUpdate(Framework fw) + private void FrameworkOnUpdate(IFramework fw) { lock (this.activeTextures) { @@ -432,19 +415,7 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP foreach (var texInfo in this.activeTextures) { - if (texInfo.Value.RefCount == 0) - { - Log.Verbose("Evicting {Path} since no refs", texInfo.Key); - - Debug.Assert(texInfo.Value.KeepAliveCount == 0, "texInfo.Value.KeepAliveCount == 0"); - - texInfo.Value.Wrap?.Dispose(); - texInfo.Value.Wrap = null; - toRemove.Add(texInfo.Key); - continue; - } - - if (texInfo.Value.KeepAliveCount > 0 || texInfo.Value.Wrap == null) + if (texInfo.Value.Wrap == null) continue; if (DateTime.UtcNow - texInfo.Value.LastAccess > TimeSpan.FromMilliseconds(MillisecondsEvictionTime)) @@ -452,6 +423,7 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP Log.Verbose("Evicting {Path} since too old", texInfo.Key); texInfo.Value.Wrap.Dispose(); texInfo.Value.Wrap = null; + toRemove.Add(texInfo.Key); } } @@ -477,22 +449,12 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP /// /// Gets or sets the actual texture wrap. May be unpopulated. /// - public TextureWrap? Wrap { get; set; } + public IDalamudTextureWrap? Wrap { get; set; } /// /// Gets or sets the time the texture was last accessed. /// public DateTime LastAccess { get; set; } - - /// - /// Gets or sets the number of active holders of this texture. - /// - public uint RefCount { get; set; } - - /// - /// Gets or sets the number of active holders that want this texture to stay alive forever. - /// - public uint KeepAliveCount { get; set; } /// /// Gets or sets the extents of the texture. @@ -501,90 +463,6 @@ internal class TextureManager : IDisposable, IServiceType, ITextureSubstitutionP } } -/// -/// Plugin-scoped version of a texture manager. -/// -[PluginInterface] -[InterfaceVersion("1.0")] -[ServiceManager.ScopedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -internal class TextureManagerPluginScoped : ITextureProvider, IServiceType, IDisposable -{ - private readonly TextureManager textureManager; - - private readonly List trackedTextures = new(); - - /// - /// Initializes a new instance of the class. - /// - /// TextureManager instance. - public TextureManagerPluginScoped(TextureManager textureManager) - { - this.textureManager = textureManager; - } - - /// - public IDalamudTextureWrap? GetIcon( - uint iconId, - ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.ItemHighQuality, - ClientLanguage? language = null, - bool keepAlive = false) - { - var wrap = this.textureManager.GetIcon(iconId, flags, language, keepAlive); - if (wrap == null) - return null; - - this.trackedTextures.Add(wrap); - return wrap; - } - - /// - public string? GetIconPath(uint iconId, ITextureProvider.IconFlags flags = ITextureProvider.IconFlags.HiRes, ClientLanguage? language = null) - => this.textureManager.GetIconPath(iconId, flags, language); - - /// - public IDalamudTextureWrap? GetTextureFromGame(string path, bool keepAlive = false) - { - ArgumentException.ThrowIfNullOrEmpty(path); - - var wrap = this.textureManager.GetTextureFromGame(path, keepAlive); - if (wrap == null) - return null; - - this.trackedTextures.Add(wrap); - return wrap; - } - - /// - public IDalamudTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive) - { - ArgumentNullException.ThrowIfNull(file); - - var wrap = this.textureManager.GetTextureFromFile(file, keepAlive); - if (wrap == null) - return null; - - this.trackedTextures.Add(wrap); - return wrap; - } - - /// - public IDalamudTextureWrap? GetTexture(TexFile file) - => this.textureManager.GetTexture(file); - - /// - public void Dispose() - { - // Dispose all leaked textures - foreach (var textureWrap in this.trackedTextures.Where(x => !x.IsDisposed)) - { - textureWrap.Dispose(); - } - } -} - /// /// Wrap. /// @@ -592,19 +470,16 @@ internal class TextureManagerTextureWrap : IDalamudTextureWrap { private readonly TextureManager manager; private readonly string path; - private readonly bool keepAlive; /// /// Initializes a new instance of the class. /// /// The path to the texture. /// The extents of the texture. - /// Keep alive or not. /// Manager that we obtained this from. - internal TextureManagerTextureWrap(string path, Vector2 extents, bool keepAlive, TextureManager manager) + internal TextureManagerTextureWrap(string path, Vector2 extents, TextureManager manager) { this.path = path; - this.keepAlive = keepAlive; this.manager = manager; this.Width = (int)extents.X; this.Height = (int)extents.Y; @@ -630,12 +505,7 @@ internal class TextureManagerTextureWrap : IDalamudTextureWrap /// public void Dispose() { - lock (this) - { - if (!this.IsDisposed) - this.manager.NotifyTextureDisposed(this.path, this.keepAlive); - - this.IsDisposed = true; - } + this.IsDisposed = true; + // This is a no-op. The manager cleans up textures that are not being drawn. } } diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index d1e7a6b78..524759f4a 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -1,9 +1,11 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Game; using Dalamud.Game.Gui; +using Dalamud.Interface.Utility; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; @@ -415,7 +417,7 @@ internal unsafe class UiDebug $"MultiplyRGB: {node->MultiplyRed} {node->MultiplyGreen} {node->MultiplyBlue}"); } - private bool DrawUnitListHeader(int index, uint count, ulong ptr, bool highlight) + private bool DrawUnitListHeader(int index, ushort count, ulong ptr, bool highlight) { ImGui.PushStyleColor(ImGuiCol.Text, highlight ? 0xFFAAAA00 : 0xFFFFFFFF); if (!string.IsNullOrEmpty(this.searchInput) && !this.doingSearch) @@ -454,8 +456,6 @@ internal unsafe class UiDebug this.selectedInList[i] = false; var unitManager = &unitManagers[i]; - var unitBaseArray = &unitManager->AtkUnitEntries; - var headerOpen = true; if (!searching) @@ -467,7 +467,7 @@ internal unsafe class UiDebug for (var j = 0; j < unitManager->Count && headerOpen; j++) { - var unitBase = unitBaseArray[j]; + var unitBase = *(AtkUnitBase**)Unsafe.AsPointer(ref unitManager->EntriesSpan[j]); if (this.selectedUnitBase != null && unitBase == this.selectedUnitBase) { this.selectedInList[i] = true; @@ -512,7 +512,8 @@ internal unsafe class UiDebug { for (var j = 0; j < unitManager->Count; j++) { - if (this.selectedUnitBase == null || unitBaseArray[j] != this.selectedUnitBase) continue; + var unitBase = *(AtkUnitBase**)Unsafe.AsPointer(ref unitManager->EntriesSpan[j]); + if (this.selectedUnitBase == null || unitBase != this.selectedUnitBase) continue; this.selectedInList[i] = true; foundSelected = true; } diff --git a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs index b599fb58f..dcde7d008 100644 --- a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs +++ b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Networking.Http; using ImGuiNET; @@ -65,7 +66,7 @@ public class BranchSwitcherWindow : Window return; } - var si = Service.Get(); + var si = Service.Get().StartInfo; var itemsArray = this.branches.Select(x => x.Key).ToArray(); ImGui.ListBox("Branch", ref this.selectedBranchIndex, itemsArray, itemsArray.Length); diff --git a/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs b/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs index 05854210e..cd4618f24 100644 --- a/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs @@ -3,6 +3,7 @@ using System.IO; using System.Numerics; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; using ImGuiNET; @@ -35,7 +36,7 @@ Thanks and have fun!"; private readonly string assemblyVersion = Util.AssemblyVersion; - private readonly TextureWrap logoTexture; + private readonly IDalamudTextureWrap logoTexture; /// /// Initializes a new instance of the class. @@ -48,11 +49,7 @@ Thanks and have fun!"; this.Size = new Vector2(885, 463); this.SizeCondition = ImGuiCond.Appearing; - var interfaceManager = Service.Get(); - var dalamud = Service.Get(); - - this.logoTexture = - interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"))!; + this.logoTexture = Service.Get().Logo; } /// diff --git a/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs b/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs index 638b30e66..8c5458557 100644 --- a/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs @@ -6,6 +6,7 @@ using Dalamud.Interface.Animation; using Dalamud.Interface.Animation.EasingFunctions; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs index 4bd41c025..fe3c25784 100644 --- a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs @@ -11,6 +11,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Game.Command; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal; diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs index 05d5bff3f..ba47d2c8e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs @@ -1,9 +1,10 @@ -using System; using System.Linq; using System.Numerics; using Dalamud.Game.Gui; using Dalamud.Interface.Components; +using Dalamud.Interface.Internal.Windows.Data.Widgets; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using ImGuiNET; using Serilog; @@ -17,7 +18,7 @@ internal class DataWindow : Window { private readonly IDataWindowWidget[] modules = { - new ServerOpcodeWidget(), + new ServicesWidget(), new AddressesWidget(), new ObjectTableWidget(), new FateTableWidget(), @@ -48,6 +49,7 @@ internal class DataWindow : Window new UIColorWidget(), new DataShareWidget(), new NetworkMonitorWidget(), + new IconBrowserWidget(), }; private readonly IOrderedEnumerable orderedModules; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs index b39dfcc4f..d4bea2931 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying addon inspector. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs index 8d11e6285..1056b434e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs @@ -3,7 +3,7 @@ using Dalamud.Memory; using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying Addon Data. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs index 2beea905e..dfa6f173d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs @@ -3,7 +3,7 @@ using Dalamud.Game; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget to display resolved .text sigs. @@ -36,7 +36,7 @@ internal class AddressesWidget : IDataWindowWidget { try { - var sigScanner = Service.Get(); + var sigScanner = Service.Get(); this.sigResult = sigScanner.ScanText(this.inputSig); } catch (KeyNotFoundException) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs index fd4a76544..fbb945368 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Game.ClientState.Aetherytes; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying aetheryte table. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs index 9aac779b3..4da2011a6 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs @@ -1,10 +1,9 @@ -using System; -using System.Numerics; +using System.Numerics; using Dalamud.Memory; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying AtkArrayData. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs index 664b4205e..c35280f92 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs @@ -2,7 +2,7 @@ using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying data about the Buddy List. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs index c7f6564d1..8ec704888 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Game.Command; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying command info. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs index be19d7ae2..7725df5bf 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Game.ClientState.Conditions; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying current character condition flags. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs index 5b85eb814..f66b50fca 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Utility; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying configuration info. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs index d89be3357..570b63332 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs @@ -1,7 +1,8 @@ -using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Interface.Utility; +using Dalamud.Plugin.Ipc.Internal; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying plugin data share modules. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs index 125d6dbbf..cc4e97779 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs @@ -2,7 +2,7 @@ using Dalamud.Game.Gui.Dtr; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying dtr test. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs index ca77f089f..de9af9aa2 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Game.ClientState.Fates; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying the Fate Table. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs index ff937996e..ddbf61342 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs @@ -1,10 +1,9 @@ -using System; -using System.Numerics; +using System.Numerics; using Dalamud.Game.Gui.FlyText; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying fly text info. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs index 036ea7000..26bd2e623 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Numerics; +using Dalamud.Interface.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget to display FontAwesome Symbols. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs index b20e0132e..0a8a15580 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs @@ -1,9 +1,7 @@ -using System; - -using Dalamud.Game.ClientState.GamePad; +using Dalamud.Game.ClientState.GamePad; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying gamepad info. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs index dee7999ee..df350e730 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs @@ -4,7 +4,7 @@ using Dalamud.Game.ClientState.JobGauge.Types; using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying job gauge data. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs index d5c566e52..b24587d6c 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs @@ -1,12 +1,11 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using Dalamud.Hooking; using ImGuiNET; using PInvoke; using Serilog; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying hook information. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs new file mode 100644 index 000000000..dcae6e689 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs @@ -0,0 +1,216 @@ +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Numerics; + +using Dalamud.Data; +using Dalamud.Interface.Utility; +using Dalamud.Utility; +using ImGuiNET; + +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; + +/// +/// Data widget for browsing in-game icons. +/// +public class IconBrowserWidget : IDataWindowWidget +{ + // Remove range 170,000 -> 180,000 by default, this specific range causes exceptions. + private readonly HashSet nullValues = Enumerable.Range(170000, 9999).ToHashSet(); + + private Vector2 iconSize = new(64.0f, 64.0f); + private Vector2 editIconSize = new(64.0f, 64.0f); + + private List valueRange = Enumerable.Range(0, 200000).ToList(); + + private int lastNullValueCount; + private int startRange; + private int stopRange = 200000; + private bool showTooltipImage; + + private Vector2 mouseDragStart; + private bool dragStarted; + private Vector2 lastWindowSize = Vector2.Zero; + + /// + public string[]? CommandShortcuts { get; init; } = { "icon", "icons" }; + + /// + public string DisplayName { get; init; } = "Icon Browser"; + + /// + public bool Ready { get; set; } = true; + + /// + public void Load() + { + } + + /// + public void Draw() + { + this.DrawOptions(); + + if (ImGui.BeginChild("ScrollableSection", ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoMove)) + { + var itemsPerRow = (int)MathF.Floor(ImGui.GetContentRegionMax().X / (this.iconSize.X + ImGui.GetStyle().ItemSpacing.X)); + var itemHeight = this.iconSize.Y + ImGui.GetStyle().ItemSpacing.Y; + + ImGuiClip.ClippedDraw(this.valueRange, this.DrawIcon, itemsPerRow, itemHeight); + } + + ImGui.EndChild(); + + this.ProcessMouseDragging(); + + if (this.lastNullValueCount != this.nullValues.Count) + { + this.RecalculateIndexRange(); + this.lastNullValueCount = this.nullValues.Count; + } + } + + // Limit the popup image to half our screen size. + private static float GetImageScaleFactor(IDalamudTextureWrap texture) + { + var workArea = ImGui.GetMainViewport().Size / 2.0f; + var scale = 1.0f; + + if (texture.Width > workArea.X || texture.Height > workArea.Y) + { + var widthRatio = workArea.X / texture.Width; + var heightRatio = workArea.Y / texture.Height; + + scale = MathF.Min(widthRatio, heightRatio); + } + + return scale; + } + + private void DrawOptions() + { + ImGui.Columns(2); + + ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.InputInt("##StartRange", ref this.startRange, 0, 0)) this.RecalculateIndexRange(); + + ImGui.NextColumn(); + ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.InputInt("##StopRange", ref this.stopRange, 0, 0)) this.RecalculateIndexRange(); + + ImGui.NextColumn(); + ImGui.Checkbox("Show Image in Tooltip", ref this.showTooltipImage); + + ImGui.NextColumn(); + ImGui.InputFloat2("Icon Size", ref this.editIconSize); + if (ImGui.IsItemDeactivatedAfterEdit()) + { + this.iconSize = this.editIconSize; + } + + ImGui.Columns(1); + } + + private void DrawIcon(int iconId) + { + try + { + var cursor = ImGui.GetCursorScreenPos(); + + if (!this.IsIconValid(iconId)) + { + this.nullValues.Add(iconId); + return; + } + + if (Service.Get().GetIcon((uint)iconId) is { } texture) + { + ImGui.Image(texture.ImGuiHandle, this.iconSize); + + // If we have the option to show a tooltip image, draw the image, but make sure it's not too big. + if (ImGui.IsItemHovered() && this.showTooltipImage) + { + ImGui.BeginTooltip(); + + var scale = GetImageScaleFactor(texture); + + var textSize = ImGui.CalcTextSize(iconId.ToString()); + ImGui.SetCursorPosX(texture.Size.X * scale / 2.0f - textSize.X / 2.0f + ImGui.GetStyle().FramePadding.X * 2.0f); + ImGui.Text(iconId.ToString()); + + ImGui.Image(texture.ImGuiHandle, texture.Size * scale); + ImGui.EndTooltip(); + } + + // else, just draw the iconId. + else if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(iconId.ToString()); + } + } + else + { + // This texture was null, draw nothing, and prevent from trying to show it again. + this.nullValues.Add(iconId); + } + + ImGui.GetWindowDrawList().AddRect(cursor, cursor + this.iconSize, ImGui.GetColorU32(KnownColor.White.Vector())); + } + catch (Exception) + { + // If something went wrong, prevent from trying to show this icon again. + this.nullValues.Add(iconId); + } + } + + private void ProcessMouseDragging() + { + if (ImGui.IsItemHovered() || this.dragStarted) + { + if (ImGui.GetWindowSize() == this.lastWindowSize) + { + if (ImGui.IsItemClicked(ImGuiMouseButton.Left) && !this.dragStarted) + { + this.mouseDragStart = ImGui.GetMousePos(); + this.dragStarted = true; + } + } + else + { + this.lastWindowSize = ImGui.GetWindowSize(); + this.dragStarted = false; + } + } + + if (ImGui.IsMouseDragging(ImGuiMouseButton.Left) && this.dragStarted) + { + var delta = this.mouseDragStart - ImGui.GetMousePos(); + ImGui.GetIO().AddMouseWheelEvent(0.0f, -delta.Y / 85.0f); + this.mouseDragStart = ImGui.GetMousePos(); + } + else if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + { + this.dragStarted = false; + } + } + + // Check if the icon has a valid filepath, and exists in the game data. + private bool IsIconValid(int iconId) + { + var filePath = Service.Get().GetIconPath((uint)iconId); + return !filePath.IsNullOrEmpty() && Service.Get().FileExists(filePath); + } + + private void RecalculateIndexRange() + { + if (this.stopRange <= this.startRange || this.stopRange <= 0 || this.startRange < 0) + { + this.valueRange = new List(); + } + else + { + this.valueRange = Enumerable.Range(this.startRange, this.stopRange - this.startRange).ToList(); + this.valueRange.RemoveAll(value => this.nullValues.Contains(value)); + } + } +} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs index 311004f2d..2c7ceb95b 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs @@ -1,10 +1,8 @@ -using System; - -using Dalamud.Interface.Internal.Notifications; +using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Windowing; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying ImGui test. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs index ce072abc4..14fb7a5f2 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs @@ -2,7 +2,7 @@ using Dalamud.Interface.Colors; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying keyboard state. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs index d1c0150dc..e7bce0b84 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -7,11 +6,12 @@ using System.Text.RegularExpressions; using Dalamud.Data; using Dalamud.Game.Network; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Memory; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget to display the current packets. @@ -98,6 +98,11 @@ internal class NetworkMonitorWidget : IDataWindowWidget this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512); } + if (ImGui.Button("Clear Stored Packets")) + { + this.packets.Clear(); + } + this.DrawFilterInput(); this.DrawNegativeFilterInput(); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 42ce3ced6..b34eef6c8 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -1,5 +1,4 @@ -using System; -using System.Numerics; +using System.Numerics; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; @@ -7,7 +6,7 @@ using Dalamud.Game.Gui; using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget to display the Object Table. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs index 369fd7620..01c0b74b3 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs @@ -2,7 +2,7 @@ using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying information about the current party. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs index b89ead526..8004aa474 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs @@ -1,12 +1,10 @@ -using System; - -using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Internal; using Dalamud.Utility; using ImGuiNET; using Serilog; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for testing plugin IPC systems. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs index 89dc5735a..b59abbff1 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Game.Text; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying test data for SE Font Symbols. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs deleted file mode 100644 index 6adf02b3d..000000000 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Dalamud.Data; -using ImGuiNET; -using Newtonsoft.Json; - -namespace Dalamud.Interface.Internal.Windows.Data; - -/// -/// Widget to display the currently set server opcodes. -/// -internal class ServerOpcodeWidget : IDataWindowWidget -{ - private string? serverOpString; - - /// - public string[]? CommandShortcuts { get; init; } = { "opcode", "serveropcode" }; - - /// - public string DisplayName { get; init; } = "Server Opcode"; - - /// - public bool Ready { get; set; } - - /// - public void Load() - { - var dataManager = Service.Get(); - - if (dataManager.IsDataReady) - { - this.serverOpString = JsonConvert.SerializeObject(dataManager.ServerOpCodes, Formatting.Indented); - this.Ready = true; - } - } - - /// - public void Draw() - { - ImGui.TextUnformatted(this.serverOpString ?? "serverOpString not initialized"); - } -} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs new file mode 100644 index 000000000..49f3c1b90 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs @@ -0,0 +1,59 @@ +using System.Linq; + +using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.IoC.Internal; +using ImGuiNET; + +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; + +/// +/// Widget for displaying start info. +/// +internal class ServicesWidget : IDataWindowWidget +{ + /// + public string[]? CommandShortcuts { get; init; } = { "services" }; + + /// + public string DisplayName { get; init; } = "Service Container"; + + /// + public bool Ready { get; set; } + + /// + public void Load() + { + this.Ready = true; + } + + /// + public void Draw() + { + var container = Service.Get(); + + foreach (var instance in container.Instances) + { + var hasInterface = container.InterfaceToTypeMap.Values.Any(x => x == instance.Key); + var isPublic = instance.Key.IsPublic; + + ImGui.BulletText($"{instance.Key.FullName} ({instance.Key.GetServiceKind()})"); + + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, !hasInterface)) + { + ImGui.Text(hasInterface + ? $"\t => Provided via interface: {container.InterfaceToTypeMap.First(x => x.Value == instance.Key).Key.FullName}" + : "\t => NO INTERFACE!!!"); + } + + if (isPublic) + { + using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.Text("\t => PUBLIC!!!"); + } + + ImGuiHelpers.ScaledDummy(2); + } + } +} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs index e635b55e0..4dee316c5 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs @@ -1,7 +1,7 @@ using ImGuiNET; using Newtonsoft.Json; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying start info. @@ -26,7 +26,7 @@ internal class StartInfoWidget : IDataWindowWidget /// public void Draw() { - var startInfo = Service.Get(); + var startInfo = Service.Get().StartInfo; ImGui.Text(JsonConvert.SerializeObject(startInfo, Formatting.Indented)); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs index 39c6d5b18..68e00799d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs @@ -1,9 +1,10 @@ using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; +using Dalamud.Interface.Utility; using Dalamud.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying target info. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs index 35d449443..d1ac51ad5 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs @@ -1,16 +1,17 @@ // ReSharper disable MethodSupportsCancellation // Using alternative method of cancelling tasks by throwing exceptions. -using System; + using System.Reflection; using System.Threading; using System.Threading.Tasks; using Dalamud.Game; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; using ImGuiNET; using Serilog; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying task scheduler test. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 9c1f93b0b..0cbc401e7 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -1,21 +1,21 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Numerics; +using Dalamud.Interface.Utility; using Dalamud.Plugin.Services; using ImGuiNET; using ImGuiScene; using Serilog; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying texture test. /// internal class TexWidget : IDataWindowWidget { - private readonly List addedTextures = new(); + private readonly List addedTextures = new(); private string iconId = "18"; private bool hiRes = true; @@ -104,7 +104,7 @@ internal class TexWidget : IDataWindowWidget ImGuiHelpers.ScaledDummy(10); - TextureWrap? toRemove = null; + IDalamudTextureWrap? toRemove = null; for (var i = 0; i < this.addedTextures.Count; i++) { if (ImGui.CollapsingHeader($"Tex #{i}")) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs index 336312e87..4bca6a839 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs @@ -1,9 +1,10 @@ using System.Numerics; using Dalamud.Game.Gui.Toast; +using Dalamud.Interface.Utility; using ImGuiNET; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying toast test. diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs index d2d480fff..3308325bc 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs @@ -4,7 +4,7 @@ using Dalamud.Data; using ImGuiNET; using Lumina.Excel.GeneratedSheets; -namespace Dalamud.Interface.Internal.Windows.Data; +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget for displaying all UI Colors from Lumina. diff --git a/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs b/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs index e95c510d3..ff5af1556 100644 --- a/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs +++ b/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs @@ -1,6 +1,7 @@ using System.Numerics; using CheapLoc; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs index 766f80b23..b721b08c3 100644 --- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs +++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs @@ -50,6 +50,9 @@ internal class PluginImageCache : IDisposable, IServiceType [ServiceManager.ServiceDependency] private readonly InterfaceManager.InterfaceManagerWithScene imWithScene = Service.Get(); + [ServiceManager.ServiceDependency] + private readonly Branding branding = Service.Get(); + [ServiceManager.ServiceDependency] private readonly HappyHttpClient happyHttpClient = Service.Get(); @@ -59,24 +62,24 @@ internal class PluginImageCache : IDisposable, IServiceType private readonly Task downloadTask; private readonly Task loadTask; - private readonly ConcurrentDictionary pluginIconMap = new(); - private readonly ConcurrentDictionary pluginImagesMap = new(); + private readonly ConcurrentDictionary pluginIconMap = new(); + private readonly ConcurrentDictionary pluginImagesMap = new(); - private readonly Task emptyTextureTask; - private readonly Task disabledIconTask; - private readonly Task outdatedInstallableIconTask; - private readonly Task defaultIconTask; - private readonly Task troubleIconTask; - private readonly Task updateIconTask; - private readonly Task installedIconTask; - private readonly Task thirdIconTask; - private readonly Task thirdInstalledIconTask; - private readonly Task corePluginIconTask; + private readonly Task emptyTextureTask; + private readonly Task disabledIconTask; + private readonly Task outdatedInstallableIconTask; + private readonly Task defaultIconTask; + private readonly Task troubleIconTask; + private readonly Task updateIconTask; + private readonly Task installedIconTask; + private readonly Task thirdIconTask; + private readonly Task thirdInstalledIconTask; + private readonly Task corePluginIconTask; [ServiceManager.ServiceConstructor] private PluginImageCache(Dalamud dalamud) { - Task? TaskWrapIfNonNull(TextureWrap? tw) => tw == null ? null : Task.FromResult(tw!); + Task? TaskWrapIfNonNull(IDalamudTextureWrap? tw) => tw == null ? null : Task.FromResult(tw!); var imwst = Task.Run(() => this.imWithScene); this.emptyTextureTask = imwst.ContinueWith(task => task.Result.Manager.LoadImageRaw(new byte[64], 8, 8, 4)!); @@ -88,7 +91,7 @@ internal class PluginImageCache : IDisposable, IServiceType this.installedIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "installedIcon.png"))) ?? this.emptyTextureTask).Unwrap(); this.thirdIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "thirdIcon.png"))) ?? this.emptyTextureTask).Unwrap(); this.thirdInstalledIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "thirdInstalledIcon.png"))) ?? this.emptyTextureTask).Unwrap(); - this.corePluginIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "tsmLogo.png"))) ?? this.emptyTextureTask).Unwrap(); + this.corePluginIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(this.branding.LogoSmall)).Unwrap(); this.downloadTask = Task.Factory.StartNew( () => this.DownloadTask(8), TaskCreationOptions.LongRunning); @@ -99,70 +102,70 @@ internal class PluginImageCache : IDisposable, IServiceType /// /// Gets the fallback empty texture. /// - public TextureWrap EmptyTexture => this.emptyTextureTask.IsCompleted - ? this.emptyTextureTask.Result - : this.emptyTextureTask.GetAwaiter().GetResult(); + public IDalamudTextureWrap EmptyTexture => this.emptyTextureTask.IsCompleted + ? this.emptyTextureTask.Result + : this.emptyTextureTask.GetAwaiter().GetResult(); /// /// Gets the disabled plugin icon. /// - public TextureWrap DisabledIcon => this.disabledIconTask.IsCompleted + public IDalamudTextureWrap DisabledIcon => this.disabledIconTask.IsCompleted ? this.disabledIconTask.Result : this.disabledIconTask.GetAwaiter().GetResult(); /// /// Gets the outdated installable plugin icon. /// - public TextureWrap OutdatedInstallableIcon => this.outdatedInstallableIconTask.IsCompleted + public IDalamudTextureWrap OutdatedInstallableIcon => this.outdatedInstallableIconTask.IsCompleted ? this.outdatedInstallableIconTask.Result : this.outdatedInstallableIconTask.GetAwaiter().GetResult(); /// /// Gets the default plugin icon. /// - public TextureWrap DefaultIcon => this.defaultIconTask.IsCompleted + public IDalamudTextureWrap DefaultIcon => this.defaultIconTask.IsCompleted ? this.defaultIconTask.Result : this.defaultIconTask.GetAwaiter().GetResult(); /// /// Gets the plugin trouble icon overlay. /// - public TextureWrap TroubleIcon => this.troubleIconTask.IsCompleted + public IDalamudTextureWrap TroubleIcon => this.troubleIconTask.IsCompleted ? this.troubleIconTask.Result : this.troubleIconTask.GetAwaiter().GetResult(); /// /// Gets the plugin update icon overlay. /// - public TextureWrap UpdateIcon => this.updateIconTask.IsCompleted + public IDalamudTextureWrap UpdateIcon => this.updateIconTask.IsCompleted ? this.updateIconTask.Result : this.updateIconTask.GetAwaiter().GetResult(); /// /// Gets the plugin installed icon overlay. /// - public TextureWrap InstalledIcon => this.installedIconTask.IsCompleted + public IDalamudTextureWrap InstalledIcon => this.installedIconTask.IsCompleted ? this.installedIconTask.Result : this.installedIconTask.GetAwaiter().GetResult(); /// /// Gets the third party plugin icon overlay. /// - public TextureWrap ThirdIcon => this.thirdIconTask.IsCompleted + public IDalamudTextureWrap ThirdIcon => this.thirdIconTask.IsCompleted ? this.thirdIconTask.Result : this.thirdIconTask.GetAwaiter().GetResult(); /// /// Gets the installed third party plugin icon overlay. /// - public TextureWrap ThirdInstalledIcon => this.thirdInstalledIconTask.IsCompleted + public IDalamudTextureWrap ThirdInstalledIcon => this.thirdInstalledIconTask.IsCompleted ? this.thirdInstalledIconTask.Result : this.thirdInstalledIconTask.GetAwaiter().GetResult(); /// /// Gets the core plugin icon. /// - public TextureWrap CorePluginIcon => this.corePluginIconTask.IsCompleted + public IDalamudTextureWrap CorePluginIcon => this.corePluginIconTask.IsCompleted ? this.corePluginIconTask.Result : this.corePluginIconTask.GetAwaiter().GetResult(); @@ -233,7 +236,7 @@ internal class PluginImageCache : IDisposable, IServiceType /// If the plugin was third party sourced. /// Cached image textures, or an empty array. /// True if an entry exists, may be null if currently downloading. - public bool TryGetIcon(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out TextureWrap? iconTexture) + public bool TryGetIcon(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out IDalamudTextureWrap? iconTexture) { iconTexture = null; @@ -275,16 +278,16 @@ internal class PluginImageCache : IDisposable, IServiceType /// If the plugin was third party sourced. /// Cached image textures, or an empty array. /// True if the image array exists, may be empty if currently downloading. - public bool TryGetImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out TextureWrap?[] imageTextures) + public bool TryGetImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out IDalamudTextureWrap?[] imageTextures) { if (!this.pluginImagesMap.TryAdd(manifest.InternalName, null)) { var found = this.pluginImagesMap[manifest.InternalName]; - imageTextures = found ?? Array.Empty(); + imageTextures = found ?? Array.Empty(); return true; } - var target = new TextureWrap?[5]; + var target = new IDalamudTextureWrap?[5]; this.pluginImagesMap[manifest.InternalName] = target; imageTextures = target; @@ -304,7 +307,7 @@ internal class PluginImageCache : IDisposable, IServiceType return false; } - private async Task TryLoadImage( + private async Task TryLoadImage( byte[]? bytes, string name, string? loc, @@ -319,7 +322,7 @@ internal class PluginImageCache : IDisposable, IServiceType var interfaceManager = this.imWithScene.Manager; var framework = await Service.GetAsync(); - TextureWrap? image; + IDalamudTextureWrap? image; // FIXME(goat): This is a hack around this call failing randomly in certain situations. Might be related to not being called on the main thread. try { @@ -492,7 +495,7 @@ internal class PluginImageCache : IDisposable, IServiceType Log.Debug("Plugin image loader has shutdown"); } - private async Task DownloadPluginIconAsync(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) + private async Task DownloadPluginIconAsync(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) { if (plugin is { IsDev: true }) { @@ -559,7 +562,7 @@ internal class PluginImageCache : IDisposable, IServiceType return icon; } - private async Task DownloadPluginImagesAsync(TextureWrap?[] pluginImages, LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) + private async Task DownloadPluginImagesAsync(IDalamudTextureWrap?[] pluginImages, LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) { if (plugin is { IsDev: true }) { diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 27bf83dbd..7ff2f61e0 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -15,8 +15,8 @@ using Dalamud.Game.Command; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Internal.Notifications; -using Dalamud.Interface.Raii; -using Dalamud.Interface.Style; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using Dalamud.Plugin; @@ -62,8 +62,8 @@ internal class PluginInstallerWindow : Window, IDisposable private string[] testerImagePaths = new string[5]; private string testerIconPath = string.Empty; - private TextureWrap?[] testerImages; - private TextureWrap? testerIcon; + private IDalamudTextureWrap?[] testerImages; + private IDalamudTextureWrap? testerIcon; private bool testerError = false; private bool testerUpdateAvailable = false; @@ -1525,7 +1525,7 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.ScaledDummy(20); - static void CheckImageSize(TextureWrap? image, int maxWidth, int maxHeight, bool requireSquare) + static void CheckImageSize(IDalamudTextureWrap? image, int maxWidth, int maxHeight, bool requireSquare) { if (image == null) return; @@ -1570,7 +1570,7 @@ internal class PluginInstallerWindow : Window, IDisposable this.testerIcon = im.LoadImage(this.testerIconPath); } - this.testerImages = new TextureWrap[this.testerImagePaths.Length]; + this.testerImages = new IDalamudTextureWrap[this.testerImagePaths.Length]; for (var i = 0; i < this.testerImagePaths.Length; i++) { @@ -1822,7 +1822,7 @@ internal class PluginInstallerWindow : Window, IDisposable var rectOffset = ImGui.GetWindowContentRegionMin() + ImGui.GetWindowPos(); if (ImGui.IsRectVisible(rectOffset + cursorBeforeImage, rectOffset + cursorBeforeImage + iconSize)) { - TextureWrap icon; + IDalamudTextureWrap icon; if (log is PluginChangelogEntry pluginLog) { icon = this.imageCache.DefaultIcon; @@ -1994,7 +1994,6 @@ internal class PluginInstallerWindow : Window, IDisposable { var configuration = Service.Get(); var pluginManager = Service.Get(); - var startInfo = Service.Get(); if (ImGui.BeginPopupContextItem("ItemContextMenu")) { @@ -2022,10 +2021,10 @@ internal class PluginInstallerWindow : Window, IDisposable Task.Run(() => { pluginManager.PluginConfigs.Delete(manifest.InternalName); + var dir = pluginManager.PluginConfigs.GetDirectory(manifest.InternalName); - var path = Path.Combine(startInfo.PluginDirectory, manifest.InternalName); - if (Directory.Exists(path)) - Directory.Delete(path, true); + if (Directory.Exists(dir)) + Directory.Delete(dir, true); }) .ContinueWith(task => { @@ -2226,8 +2225,7 @@ internal class PluginInstallerWindow : Window, IDisposable { var commands = commandManager.Commands .Where(cInfo => - cInfo.Value != null && - cInfo.Value.ShowInHelp && + cInfo.Value is { ShowInHelp: true } && cInfo.Value.LoaderAssemblyName == plugin.Manifest.InternalName) .ToArray(); diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs index 835a8a60c..039877158 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs @@ -8,7 +8,8 @@ using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Internal.Notifications; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Profiles; using Dalamud.Utility; diff --git a/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs b/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs index 2d0f54912..16f253da9 100644 --- a/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility.Numerics; using Dalamud.Utility.Timing; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs index a9948430f..b2229e4e4 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using Dalamud.Game.Addon; +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/EnterTerritoryAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/EnterTerritoryAgingStep.cs index d301cb1ff..4f5c758d6 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/EnterTerritoryAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/EnterTerritoryAgingStep.cs @@ -59,9 +59,9 @@ internal class EnterTerritoryAgingStep : IAgingStep this.subscribed = false; } - private void ClientStateOnTerritoryChanged(object sender, ushort e) + private void ClientStateOnTerritoryChanged(ushort territoryId) { - if (e == this.territory) + if (territoryId == this.territory) { this.hasPassed = true; } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LoginEventAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LoginEventAgingStep.cs index c1dba389f..23b0b903a 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LoginEventAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LoginEventAgingStep.cs @@ -51,7 +51,7 @@ internal class LoginEventAgingStep : IAgingStep } } - private void ClientStateOnOnLogin(object sender, EventArgs e) + private void ClientStateOnOnLogin() { this.hasPassed = true; } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs index 060c0bcc8..c4c6ebfce 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs @@ -51,7 +51,7 @@ internal class LogoutEventAgingStep : IAgingStep } } - private void ClientStateOnOnLogout(object sender, EventArgs e) + private void ClientStateOnOnLogout() { this.hasPassed = true; } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index 68d197208..8e43d30a6 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -6,6 +6,7 @@ using System.Numerics; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs index 49a8935df..d06fe0fb6 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs @@ -1,5 +1,6 @@ -using System; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; + +using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Settings; diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs index 97d9eac5c..4f77c0502 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs @@ -5,7 +5,8 @@ using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Internal.Windows.Settings.Tabs; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Plugin.Internal; using Dalamud.Utility; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs index 325d0b8b7..9b6a32617 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs @@ -8,7 +8,8 @@ using System.Numerics; using CheapLoc; using Dalamud.Game.Gui; using Dalamud.Interface.GameFonts; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.UI; @@ -170,7 +171,7 @@ Dalamud is licensed under AGPL v3 or later. Contribute at: https://github.com/goatcorp/Dalamud "; - private readonly TextureWrap logoTexture; + private readonly IDalamudTextureWrap logoTexture; private readonly Stopwatch creditsThrottler; private string creditsText; @@ -180,10 +181,9 @@ Contribute at: https://github.com/goatcorp/Dalamud public SettingsTabAbout() { - var dalamud = Service.Get(); - var interfaceManager = Service.Get(); + var branding = Service.Get(); - this.logoTexture = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"))!; + this.logoTexture = branding.Logo; this.creditsThrottler = new(); } diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs index 85cb8219f..7dd0fa5d1 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs @@ -8,6 +8,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Game.Gui.Dtr; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs index 15dab7d94..de9d1bae4 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs @@ -6,6 +6,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.Settings.Widgets; +using Dalamud.Interface.Utility; using Dalamud.Plugin.Internal; using Dalamud.Utility; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs index 3e801a8c3..b34a13cc5 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs @@ -5,6 +5,7 @@ using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Internal.Windows.Settings.Widgets; +using Dalamud.Interface.Utility; using Dalamud.Utility; using ImGuiNET; using Serilog; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs index 9c635fb99..6adddbc82 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs index 3e73454f3..55deb61bc 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs @@ -11,7 +11,8 @@ using Dalamud.Configuration; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs index bc5c2fd0a..1db3c4756 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs index d1eb43c1f..3edd3ae1d 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs @@ -2,6 +2,7 @@ using System.Numerics; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs index 0bb373576..85f8a826f 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs @@ -7,6 +7,7 @@ using System.Linq; using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs index 83be6a052..dcbb42089 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs @@ -7,7 +7,8 @@ using System.Linq; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs index 2fac28070..1d6aab1bd 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -10,7 +9,8 @@ using Dalamud.Configuration; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using Dalamud.Utility; using ImGuiNET; @@ -79,25 +79,28 @@ public class ThirdRepoSettingsEntry : SettingsEntry var disclaimerDismissed = config.ThirdRepoSpeedbumpDismissed.Value; ImGui.PushFont(InterfaceManager.IconFont); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, FontAwesomeIcon.ExclamationTriangle.ToIconString()); + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudOrange); + ImGuiHelpers.SafeTextWrapped(FontAwesomeIcon.ExclamationTriangle.ToIconString()); ImGui.PopFont(); ImGui.SameLine(); ImGuiHelpers.ScaledDummy(2); ImGui.SameLine(); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarningReadThis", "READ THIS FIRST!")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarningReadThis", "READ THIS FIRST!")); ImGui.SameLine(); ImGuiHelpers.ScaledDummy(2); ImGui.SameLine(); ImGui.PushFont(InterfaceManager.IconFont); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, FontAwesomeIcon.ExclamationTriangle.ToIconString()); + ImGuiHelpers.SafeTextWrapped(FontAwesomeIcon.ExclamationTriangle.ToIconString()); ImGui.PopFont(); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarning", "We cannot take any responsibility for custom plugins and repositories.")); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarning5", "If someone told you to copy/paste something here, it's very possible that you are being scammed or taken advantage of.")); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarning2", "Plugins have full control over your PC, like any other program, and may cause harm or crashes.")); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarning4", "They can delete your character, steal your FC or Discord account, and burn down your house.")); - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingCustomRepoWarning3", "Please make absolutely sure that you only install plugins from developers you trust.")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarning", "We cannot take any responsibility for custom plugins and repositories.")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarning5", "If someone told you to copy/paste something here, it's very possible that you are being scammed or taken advantage of.")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarning2", "Plugins have full control over your PC, like any other program, and may cause harm or crashes.")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarning4", "They can delete your character, steal your FC or Discord account, and burn down your house.")); + ImGuiHelpers.SafeTextWrapped(Loc.Localize("DalamudSettingCustomRepoWarning3", "Please make absolutely sure that you only install plugins from developers you trust.")); + ImGui.PopStyleColor(); + if (!disclaimerDismissed) { const int speedbumpTime = 15; diff --git a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs index 419361b3b..3a3e871b0 100644 --- a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs +++ b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs @@ -10,6 +10,7 @@ using Dalamud.Data; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Style; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; using ImGuiNET; diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index 8c835c76a..e77a3db4e 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,8 +8,10 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.Gui; using Dalamud.Interface.Animation.EasingFunctions; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; +using Dalamud.Plugin.Services; using ImGuiNET; using ImGuiScene; @@ -24,7 +25,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable private const float TargetFontSizePt = 18f; private const float TargetFontSizePx = TargetFontSizePt * 4 / 3; - private readonly TextureWrap shadeTexture; + private readonly IDalamudTextureWrap shadeTexture; private readonly Dictionary shadeEasings = new(); private readonly Dictionary moveEasings = new(); @@ -229,7 +230,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable } private bool DrawEntry( - TitleScreenMenu.TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha, bool interactable) + TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha, bool interactable) { InterfaceManager.SpecialGlyphRequest fontHandle; if (this.specialGlyphRequests.TryGetValue(entry.Name, out fontHandle) && fontHandle.Size != TargetFontSizePx) @@ -358,7 +359,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable return isHover; } - private void FrameworkOnUpdate(Framework framework) + private void FrameworkOnUpdate(IFramework framework) { var clientState = Service.Get(); this.IsOpen = !clientState.IsLoggedIn; diff --git a/Dalamud/Interface/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu.cs deleted file mode 100644 index c9e1458d6..000000000 --- a/Dalamud/Interface/TitleScreenMenu.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using Dalamud.IoC; -using Dalamud.IoC.Internal; -using ImGuiScene; - -namespace Dalamud.Interface; - -/// -/// Class responsible for managing elements in the title screen menu. -/// -[PluginInterface] -[InterfaceVersion("1.0")] -[ServiceManager.BlockingEarlyLoadedService] -public class TitleScreenMenu : IServiceType -{ - /// - /// Gets the texture size needed for title screen menu logos. - /// - internal const uint TextureSize = 64; - - private readonly List entries = new(); - - [ServiceManager.ServiceConstructor] - private TitleScreenMenu() - { - } - - /// - /// Gets the list of entries in the title screen menu. - /// - public IReadOnlyList Entries => this.entries; - - /// - /// Adds a new entry to the title screen menu. - /// - /// The text to show. - /// The texture to show. - /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. - /// Thrown when the texture provided does not match the required resolution(64x64). - public TitleScreenMenuEntry AddEntry(string text, TextureWrap texture, Action onTriggered) - { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - - lock (this.entries) - { - var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == Assembly.GetCallingAssembly()).ToList(); - var priority = entriesOfAssembly.Any() - ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) - : 0; - var entry = new TitleScreenMenuEntry(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered); - var i = this.entries.BinarySearch(entry); - if (i < 0) - i = ~i; - this.entries.Insert(i, entry); - return entry; - } - } - - /// - /// Adds a new entry to the title screen menu. - /// - /// Priority of the entry. - /// The text to show. - /// The texture to show. - /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. - /// Thrown when the texture provided does not match the required resolution(64x64). - public TitleScreenMenuEntry AddEntry(ulong priority, string text, TextureWrap texture, Action onTriggered) - { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - - lock (this.entries) - { - var entry = new TitleScreenMenuEntry(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered); - var i = this.entries.BinarySearch(entry); - if (i < 0) - i = ~i; - this.entries.Insert(i, entry); - return entry; - } - } - - /// - /// Remove an entry from the title screen menu. - /// - /// The entry to remove. - public void RemoveEntry(TitleScreenMenuEntry entry) - { - lock (this.entries) - { - this.entries.Remove(entry); - } - } - - /// - /// Adds a new entry to the title screen menu. - /// - /// Priority of the entry. - /// The text to show. - /// The texture to show. - /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. - /// Thrown when the texture provided does not match the required resolution(64x64). - internal TitleScreenMenuEntry AddEntryCore(ulong priority, string text, TextureWrap texture, Action onTriggered) - { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - - lock (this.entries) - { - var entry = new TitleScreenMenuEntry(null, priority, text, texture, onTriggered) - { - IsInternal = true, - }; - this.entries.Add(entry); - return entry; - } - } - - /// - /// Adds a new entry to the title screen menu. - /// - /// The text to show. - /// The texture to show. - /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. - /// Thrown when the texture provided does not match the required resolution(64x64). - internal TitleScreenMenuEntry AddEntryCore(string text, TextureWrap texture, Action onTriggered) - { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - - lock (this.entries) - { - var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == null).ToList(); - var priority = entriesOfAssembly.Any() - ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) - : 0; - var entry = new TitleScreenMenuEntry(null, priority, text, texture, onTriggered) - { - IsInternal = true, - }; - this.entries.Add(entry); - return entry; - } - } - - /// - /// Class representing an entry in the title screen menu. - /// - public class TitleScreenMenuEntry : IComparable - { - private readonly Action onTriggered; - - /// - /// Initializes a new instance of the class. - /// - /// The calling assembly. - /// The priority of this entry. - /// The text to show. - /// The texture to show. - /// The action to execute when the option is selected. - internal TitleScreenMenuEntry(Assembly? callingAssembly, ulong priority, string text, TextureWrap texture, Action onTriggered) - { - this.CallingAssembly = callingAssembly; - this.Priority = priority; - this.Name = text; - this.Texture = texture; - this.onTriggered = onTriggered; - } - - /// - /// Gets the priority of this entry. - /// - public ulong Priority { get; init; } - - /// - /// Gets or sets the name of this entry. - /// - public string Name { get; set; } - - /// - /// Gets or sets the texture of this entry. - /// - public TextureWrap Texture { get; set; } - - /// - /// Gets or sets a value indicating whether or not this entry is internal. - /// - internal bool IsInternal { get; set; } - - /// - /// Gets the calling assembly of this entry. - /// - internal Assembly? CallingAssembly { get; init; } - - /// - /// Gets the internal ID of this entry. - /// - internal Guid Id { get; init; } = Guid.NewGuid(); - - /// - public int CompareTo(TitleScreenMenuEntry? other) - { - if (other == null) - return 1; - if (this.CallingAssembly != other.CallingAssembly) - { - if (this.CallingAssembly == null && other.CallingAssembly == null) - return 0; - if (this.CallingAssembly == null && other.CallingAssembly != null) - return -1; - if (this.CallingAssembly != null && other.CallingAssembly == null) - return 1; - return string.Compare( - this.CallingAssembly!.FullName!, - other.CallingAssembly!.FullName!, - StringComparison.CurrentCultureIgnoreCase); - } - - if (this.Priority != other.Priority) - return this.Priority.CompareTo(other.Priority); - if (this.Name != other.Name) - return string.Compare(this.Name, other.Name, StringComparison.InvariantCultureIgnoreCase); - return string.Compare(this.Name, other.Name, StringComparison.InvariantCulture); - } - - /// - /// Trigger the action associated with this entry. - /// - internal void Trigger() - { - this.onTriggered(); - } - } -} diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs new file mode 100644 index 000000000..6665bbafb --- /dev/null +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs @@ -0,0 +1,196 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using Dalamud.Interface.Internal; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; +using ImGuiScene; + +namespace Dalamud.Interface; + +/// +/// Class responsible for managing elements in the title screen menu. +/// +[InterfaceVersion("1.0")] +[ServiceManager.BlockingEarlyLoadedService] +internal class TitleScreenMenu : IServiceType, ITitleScreenMenu +{ + /// + /// Gets the texture size needed for title screen menu logos. + /// + internal const uint TextureSize = 64; + + private readonly List entries = new(); + + [ServiceManager.ServiceConstructor] + private TitleScreenMenu() + { + } + + /// + public IReadOnlyList Entries => this.entries; + + /// + public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + { + if (texture.Height != TextureSize || texture.Width != TextureSize) + { + throw new ArgumentException("Texture must be 64x64"); + } + + lock (this.entries) + { + var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == Assembly.GetCallingAssembly()).ToList(); + var priority = entriesOfAssembly.Any() + ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) + : 0; + var entry = new TitleScreenMenuEntry(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered); + var i = this.entries.BinarySearch(entry); + if (i < 0) + i = ~i; + this.entries.Insert(i, entry); + return entry; + } + } + + /// + public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + { + if (texture.Height != TextureSize || texture.Width != TextureSize) + { + throw new ArgumentException("Texture must be 64x64"); + } + + lock (this.entries) + { + var entry = new TitleScreenMenuEntry(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered); + var i = this.entries.BinarySearch(entry); + if (i < 0) + i = ~i; + this.entries.Insert(i, entry); + return entry; + } + } + + /// + public void RemoveEntry(TitleScreenMenuEntry entry) + { + lock (this.entries) + { + this.entries.Remove(entry); + } + } + + /// + /// Adds a new entry to the title screen menu. + /// + /// Priority of the entry. + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + internal TitleScreenMenuEntry AddEntryCore(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + { + if (texture.Height != TextureSize || texture.Width != TextureSize) + { + throw new ArgumentException("Texture must be 64x64"); + } + + lock (this.entries) + { + var entry = new TitleScreenMenuEntry(null, priority, text, texture, onTriggered) + { + IsInternal = true, + }; + this.entries.Add(entry); + return entry; + } + } + + /// + /// Adds a new entry to the title screen menu. + /// + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + internal TitleScreenMenuEntry AddEntryCore(string text, IDalamudTextureWrap texture, Action onTriggered) + { + if (texture.Height != TextureSize || texture.Width != TextureSize) + { + throw new ArgumentException("Texture must be 64x64"); + } + + lock (this.entries) + { + var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == null).ToList(); + var priority = entriesOfAssembly.Any() + ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) + : 0; + var entry = new TitleScreenMenuEntry(null, priority, text, texture, onTriggered) + { + IsInternal = true, + }; + this.entries.Add(entry); + return entry; + } + } +} + +/// +/// Plugin-scoped version of a TitleScreenMenu service. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class TitleScreenMenuPluginScoped : IDisposable, IServiceType, ITitleScreenMenu +{ + [ServiceManager.ServiceDependency] + private readonly TitleScreenMenu titleScreenMenuService = Service.Get(); + + private readonly List pluginEntries = new(); + + /// + public IReadOnlyList? Entries => this.titleScreenMenuService.Entries; + + /// + public void Dispose() + { + foreach (var entry in this.pluginEntries) + { + this.titleScreenMenuService.RemoveEntry(entry); + } + } + + /// + public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + { + var entry = this.titleScreenMenuService.AddEntry(text, texture, onTriggered); + this.pluginEntries.Add(entry); + + return entry; + } + + /// + public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + { + var entry = this.titleScreenMenuService.AddEntry(priority, text, texture, onTriggered); + this.pluginEntries.Add(entry); + + return entry; + } + + /// + public void RemoveEntry(TitleScreenMenuEntry entry) + { + this.pluginEntries.Remove(entry); + this.titleScreenMenuService.RemoveEntry(entry); + } +} diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs new file mode 100644 index 000000000..76382ace2 --- /dev/null +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs @@ -0,0 +1,94 @@ +using System.Reflection; + +using Dalamud.Interface.Internal; + +namespace Dalamud.Interface; + +/// +/// Class representing an entry in the title screen menu. +/// +public class TitleScreenMenuEntry : IComparable +{ + private readonly Action onTriggered; + + /// + /// Initializes a new instance of the class. + /// + /// The calling assembly. + /// The priority of this entry. + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + internal TitleScreenMenuEntry(Assembly? callingAssembly, ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + { + this.CallingAssembly = callingAssembly; + this.Priority = priority; + this.Name = text; + this.Texture = texture; + this.onTriggered = onTriggered; + } + + /// + /// Gets the priority of this entry. + /// + public ulong Priority { get; init; } + + /// + /// Gets or sets the name of this entry. + /// + public string Name { get; set; } + + /// + /// Gets or sets the texture of this entry. + /// + public IDalamudTextureWrap Texture { get; set; } + + /// + /// Gets or sets a value indicating whether or not this entry is internal. + /// + internal bool IsInternal { get; set; } + + /// + /// Gets the calling assembly of this entry. + /// + internal Assembly? CallingAssembly { get; init; } + + /// + /// Gets the internal ID of this entry. + /// + internal Guid Id { get; init; } = Guid.NewGuid(); + + /// + public int CompareTo(TitleScreenMenuEntry? other) + { + if (other == null) + return 1; + if (this.CallingAssembly != other.CallingAssembly) + { + if (this.CallingAssembly == null && other.CallingAssembly == null) + return 0; + if (this.CallingAssembly == null && other.CallingAssembly != null) + return -1; + if (this.CallingAssembly != null && other.CallingAssembly == null) + return 1; + return string.Compare( + this.CallingAssembly!.FullName!, + other.CallingAssembly!.FullName!, + StringComparison.CurrentCultureIgnoreCase); + } + + if (this.Priority != other.Priority) + return this.Priority.CompareTo(other.Priority); + if (this.Name != other.Name) + return string.Compare(this.Name, other.Name, StringComparison.InvariantCultureIgnoreCase); + return 0; + } + + /// + /// Trigger the action associated with this entry. + /// + internal void Trigger() + { + this.onTriggered(); + } +} diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index b440a0705..dd2e5bad3 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Game; +using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.Gui; using Dalamud.Interface.GameFonts; @@ -179,20 +180,6 @@ public sealed class UiBuilder : IDisposable } } - /// - /// Gets a value indicating whether or not gpose is active. - /// - public bool GposeActive - { - get - { - var condition = Service.GetNullable(); - if (condition == null) - return false; - return condition[ConditionFlag.WatchingCutscene]; - } - } - /// /// Gets a value indicating whether this plugin should modify the game's interface at this time. /// @@ -248,7 +235,7 @@ public sealed class UiBuilder : IDisposable /// /// The full filepath to the image. /// A object wrapping the created image. Use inside ImGui.Image(). - public TextureWrap LoadImage(string filePath) + public IDalamudTextureWrap LoadImage(string filePath) => this.InterfaceManagerWithScene?.LoadImage(filePath) ?? throw new InvalidOperationException("Load failed."); @@ -257,7 +244,7 @@ public sealed class UiBuilder : IDisposable /// /// A byte array containing the raw image data. /// A object wrapping the created image. Use inside ImGui.Image(). - public TextureWrap LoadImage(byte[] imageData) + public IDalamudTextureWrap LoadImage(byte[] imageData) => this.InterfaceManagerWithScene?.LoadImage(imageData) ?? throw new InvalidOperationException("Load failed."); @@ -269,7 +256,7 @@ public sealed class UiBuilder : IDisposable /// The height of the image contained in . /// The number of channels (bytes per pixel) of the image contained in . This should usually be 4. /// A object wrapping the created image. Use inside ImGui.Image(). - public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) + public IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) => this.InterfaceManagerWithScene?.LoadImageRaw(imageData, width, height, numChannels) ?? throw new InvalidOperationException("Load failed."); @@ -286,7 +273,7 @@ public sealed class UiBuilder : IDisposable /// /// The full filepath to the image. /// A object wrapping the created image. Use inside ImGui.Image(). - public Task LoadImageAsync(string filePath) => Task.Run( + public Task LoadImageAsync(string filePath) => Task.Run( async () => (await this.InterfaceManagerWithSceneAsync).LoadImage(filePath) ?? throw new InvalidOperationException("Load failed.")); @@ -296,7 +283,7 @@ public sealed class UiBuilder : IDisposable /// /// A byte array containing the raw image data. /// A object wrapping the created image. Use inside ImGui.Image(). - public Task LoadImageAsync(byte[] imageData) => Task.Run( + public Task LoadImageAsync(byte[] imageData) => Task.Run( async () => (await this.InterfaceManagerWithSceneAsync).LoadImage(imageData) ?? throw new InvalidOperationException("Load failed.")); @@ -309,7 +296,7 @@ public sealed class UiBuilder : IDisposable /// The height of the image contained in . /// The number of channels (bytes per pixel) of the image contained in . This should usually be 4. /// A object wrapping the created image. Use inside ImGui.Image(). - public Task LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) => Task.Run( + public Task LoadImageRawAsync(byte[] imageData, int width, int height, int numChannels) => Task.Run( async () => (await this.InterfaceManagerWithSceneAsync).LoadImageRaw(imageData, width, height, numChannels) ?? throw new InvalidOperationException("Load failed.")); @@ -448,6 +435,7 @@ public sealed class UiBuilder : IDisposable { this.hitchDetector.Start(); + var clientState = Service.Get(); var configuration = Service.Get(); var gameGui = Service.GetNullable(); if (gameGui == null) @@ -457,7 +445,7 @@ public sealed class UiBuilder : IDisposable !(this.DisableUserUiHide || this.DisableAutomaticUiHide)) || (this.CutsceneActive && configuration.ToggleUiHideDuringCutscenes && !(this.DisableCutsceneUiHide || this.DisableAutomaticUiHide)) || - (this.GposeActive && configuration.ToggleUiHideDuringGpose && + (clientState.IsGPosing && configuration.ToggleUiHideDuringGpose && !(this.DisableGposeUiHide || this.DisableAutomaticUiHide))) { if (!this.lastFrameUiHideState) diff --git a/Dalamud/Interface/UldWrapper.cs b/Dalamud/Interface/UldWrapper.cs index d41256fa2..e78546ed9 100644 --- a/Dalamud/Interface/UldWrapper.cs +++ b/Dalamud/Interface/UldWrapper.cs @@ -1,8 +1,8 @@ -using System; using System.Collections.Generic; using System.Linq; using Dalamud.Data; +using Dalamud.Interface.Internal; using Dalamud.Utility; using ImGuiScene; using Lumina.Data.Files; @@ -38,7 +38,7 @@ public class UldWrapper : IDisposable /// The path of the requested texture. /// The index of the desired icon. /// A TextureWrap containing the requested part if it exists and null otherwise. - public TextureWrap? LoadTexturePart(string texturePath, int part) + public IDalamudTextureWrap? LoadTexturePart(string texturePath, int part) { if (!this.Valid) { @@ -67,7 +67,7 @@ public class UldWrapper : IDisposable this.Uld = null; } - private TextureWrap? CreateTexture(uint id, int width, int height, bool hd, byte[] rgbaData, int partIdx) + private IDalamudTextureWrap? CreateTexture(uint id, int width, int height, bool hd, byte[] rgbaData, int partIdx) { var idx = 0; UldRoot.PartData? partData = null; @@ -105,7 +105,7 @@ public class UldWrapper : IDisposable return this.CopyRect(width, height, rgbaData, d); } - private TextureWrap? CopyRect(int width, int height, byte[] rgbaData, UldRoot.PartData part) + private IDalamudTextureWrap? CopyRect(int width, int height, byte[] rgbaData, UldRoot.PartData part) { if (part.V + part.W > width || part.U + part.H > height) { diff --git a/Dalamud.Interface/ImGuiClip.cs b/Dalamud/Interface/Utility/ImGuiClip.cs similarity index 73% rename from Dalamud.Interface/ImGuiClip.cs rename to Dalamud/Interface/Utility/ImGuiClip.cs index dc1845a35..c9321fe4c 100644 --- a/Dalamud.Interface/ImGuiClip.cs +++ b/Dalamud/Interface/Utility/ImGuiClip.cs @@ -1,8 +1,11 @@ +using System.Collections.Generic; +using System.Linq; using System.Numerics; -using Dalamud.Interface.Raii; + +using Dalamud.Interface.Utility.Raii; using ImGuiNET; -namespace Dalamud.Interface; +namespace Dalamud.Interface.Utility; public static class ImGuiClip { @@ -56,6 +59,56 @@ public static class ImGuiClip clipper.Destroy(); } + /// + /// Draws the enumerable data with number of items per line. + /// + /// Enumerable containing data to draw. + /// The function to draw a single item. + /// How many items to draw per line. + /// How tall each line is. + /// The type of data to draw. + public static void ClippedDraw(IReadOnlyList data, Action draw, int itemsPerLine, float lineHeight) + { + ImGuiListClipperPtr clipper; + unsafe + { + clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper()); + } + + var maxRows = (int)MathF.Ceiling((float)data.Count / itemsPerLine); + + clipper.Begin(maxRows, lineHeight); + while (clipper.Step()) + { + for (var actualRow = clipper.DisplayStart; actualRow < clipper.DisplayEnd; actualRow++) + { + if (actualRow >= maxRows) + return; + + if (actualRow < 0) + continue; + + var itemsForRow = data + .Skip(actualRow * itemsPerLine) + .Take(itemsPerLine); + + var currentIndex = 0; + foreach (var item in itemsForRow) + { + if (currentIndex++ != 0 && currentIndex < itemsPerLine + 1) + { + ImGui.SameLine(); + } + + draw(item); + } + } + } + + clipper.End(); + clipper.Destroy(); + } + // Draw a clipped random-access collection of consistent height lineHeight. // Uses ImGuiListClipper and thus handles start- and end-dummies itself, but acts on type and index. public static void ClippedDraw(IReadOnlyList data, Action draw, float lineHeight) @@ -132,7 +185,6 @@ public static class ImGuiClip return ~idx; } - // Draw non-random-access data that gets filtered without storing state. // Use GetNecessarySkips first and use its return value for skips. // checkFilter should return true for items that should be displayed and false for those that should be skipped. diff --git a/Dalamud/Interface/ImGuiExtensions.cs b/Dalamud/Interface/Utility/ImGuiExtensions.cs similarity index 98% rename from Dalamud/Interface/ImGuiExtensions.cs rename to Dalamud/Interface/Utility/ImGuiExtensions.cs index be1b99430..21a0d3747 100644 --- a/Dalamud/Interface/ImGuiExtensions.cs +++ b/Dalamud/Interface/Utility/ImGuiExtensions.cs @@ -1,10 +1,9 @@ -using System; using System.Numerics; using System.Text; using ImGuiNET; -namespace Dalamud.Interface; +namespace Dalamud.Interface.Utility; /// /// Class containing various extensions to ImGui, aiding with building custom widgets. diff --git a/Dalamud/Interface/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs similarity index 99% rename from Dalamud/Interface/ImGuiHelpers.cs rename to Dalamud/Interface/Utility/ImGuiHelpers.cs index 2356d90e2..dbb873edf 100644 --- a/Dalamud/Interface/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -1,15 +1,14 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; using Dalamud.Game.ClientState.Keys; -using Dalamud.Interface.Raii; +using Dalamud.Interface.Utility.Raii; using ImGuiNET; using ImGuiScene; -namespace Dalamud.Interface; +namespace Dalamud.Interface.Utility; /// /// Class containing various helper methods for use with ImGui inside Dalamud. @@ -300,7 +299,6 @@ public static class ImGuiHelpers internal static void NewFrame() { GlobalScale = ImGui.GetIO().FontGlobalScale; - InterfaceHelpers.GlobalScale = GlobalScale; } /// diff --git a/Dalamud/Interface/Utility/ImGuiTable.cs b/Dalamud/Interface/Utility/ImGuiTable.cs new file mode 100644 index 000000000..c74bc0a2f --- /dev/null +++ b/Dalamud/Interface/Utility/ImGuiTable.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; + +namespace Dalamud.Interface.Utility; + +#pragma warning disable SA1618 // GenericTypeParametersMustBeDocumented +#pragma warning disable SA1611 // ElementParametersMustBeDocumented + +/// +/// Helpers for drawing tables. +/// +public static class ImGuiTable +{ + /// + /// Draw a simple table with the given data using the drawRow action. + /// Headers and thus columns and column count are defined by columnTitles. + /// + public static void DrawTable(string label, IEnumerable data, Action drawRow, ImGuiTableFlags flags = ImGuiTableFlags.None, + params string[] columnTitles) + { + if (columnTitles.Length == 0) + return; + + using var table = ImRaii.Table(label, columnTitles.Length, flags); + if (!table) + return; + + foreach (var title in columnTitles) + { + ImGui.TableNextColumn(); + ImGui.TableHeader(title); + } + + foreach (var datum in data) + { + ImGui.TableNextRow(); + drawRow(datum); + } + } + + /// + /// Draw a simple table with the given data using the drawRow action inside a collapsing header. + /// Headers and thus columns and column count are defined by columnTitles. + /// + public static void DrawTabbedTable(string label, IEnumerable data, Action drawRow, ImGuiTableFlags flags = ImGuiTableFlags.None, + params string[] columnTitles) + { + if (ImGui.CollapsingHeader(label)) + DrawTable($"{label}##Table", data, drawRow, flags, columnTitles); + } +} + +#pragma warning restore SA1611 // ElementParametersMustBeDocumented +#pragma warning restore SA1618 // GenericTypeParametersMustBeDocumented diff --git a/Dalamud.Interface/Raii/Color.cs b/Dalamud/Interface/Utility/Raii/Color.cs similarity index 83% rename from Dalamud.Interface/Raii/Color.cs rename to Dalamud/Interface/Utility/Raii/Color.cs index 388e6e737..3cf93b65c 100644 --- a/Dalamud.Interface/Raii/Color.cs +++ b/Dalamud/Interface/Utility/Raii/Color.cs @@ -1,7 +1,10 @@ +using System.Collections.Generic; +using System.Linq; using System.Numerics; + using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; // Push an arbitrary amount of colors into an object that are all popped when it is disposed. // If condition is false, no color is pushed. @@ -26,7 +29,7 @@ public static partial class ImRaii public sealed class Color : IDisposable { internal static readonly List<(ImGuiCol, uint)> Stack = new(); - private int _count; + private int count; public Color Push(ImGuiCol idx, uint color, bool condition = true) { @@ -34,7 +37,7 @@ public static partial class ImRaii { Stack.Add((idx, ImGui.GetColorU32(idx))); ImGui.PushStyleColor(idx, color); - ++this._count; + ++this.count; } return this; @@ -46,7 +49,7 @@ public static partial class ImRaii { Stack.Add((idx, ImGui.GetColorU32(idx))); ImGui.PushStyleColor(idx, color); - ++this._count; + ++this.count; } return this; @@ -54,13 +57,13 @@ public static partial class ImRaii public void Pop(int num = 1) { - num = Math.Min(num, this._count); - this._count -= num; + num = Math.Min(num, this.count); + this.count -= num; ImGui.PopStyleColor(num); Stack.RemoveRange(Stack.Count - num, num); } public void Dispose() - => this.Pop(this._count); + => this.Pop(this.count); } } diff --git a/Dalamud.Interface/Raii/EndObjects.cs b/Dalamud/Interface/Utility/Raii/EndObjects.cs similarity index 94% rename from Dalamud.Interface/Raii/EndObjects.cs rename to Dalamud/Interface/Utility/Raii/EndObjects.cs index 032f09621..3f2a016b3 100644 --- a/Dalamud.Interface/Raii/EndObjects.cs +++ b/Dalamud/Interface/Utility/Raii/EndObjects.cs @@ -1,13 +1,14 @@ using System.Numerics; + using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; // Most ImGui widgets with IDisposable interface that automatically destroys them // when created with using variables. public static partial class ImRaii { - private static int _disabledCount = 0; + private static int disabledCount = 0; public static IEndObject Child(string strId) => new EndUnconditionally(ImGui.EndChild, ImGui.BeginChild(strId)); @@ -120,7 +121,7 @@ public static partial class ImRaii public static IEndObject Disabled() { ImGui.BeginDisabled(); - ++_disabledCount; + ++disabledCount; return DisabledEnd(); } @@ -130,24 +131,24 @@ public static partial class ImRaii return new EndConditionally(Nop, false); ImGui.BeginDisabled(); - ++_disabledCount; + ++disabledCount; return DisabledEnd(); } public static IEndObject Enabled() { - var oldCount = _disabledCount; + var oldCount = disabledCount; if (oldCount == 0) return new EndConditionally(Nop, false); void Restore() { - _disabledCount += oldCount; + disabledCount += oldCount; while (--oldCount >= 0) ImGui.BeginDisabled(); } - for (; _disabledCount > 0; --_disabledCount) + for (; disabledCount > 0; --disabledCount) ImGui.EndDisabled(); return new EndUnconditionally(Restore, true); @@ -156,7 +157,7 @@ public static partial class ImRaii private static IEndObject DisabledEnd() => new EndUnconditionally(() => { - --_disabledCount; + --disabledCount; ImGui.EndDisabled(); }, true); @@ -173,6 +174,11 @@ public static partial class ImRaii return new EndUnconditionally(Widget.EndFramedGroup, true); } */ + + // Used to avoid tree pops when flag for no push is set. + private static void Nop() + { + } // Exported interface for RAII. public interface IEndObject : IDisposable @@ -203,7 +209,9 @@ public static partial class ImRaii private struct EndUnconditionally : IEndObject { private Action EndAction { get; } + public bool Success { get; } + public bool Disposed { get; private set; } public EndUnconditionally(Action endAction, bool success) @@ -226,16 +234,18 @@ public static partial class ImRaii // Use end-function only on success. private struct EndConditionally : IEndObject { - private Action EndAction { get; } - public bool Success { get; } - public bool Disposed { get; private set; } - public EndConditionally(Action endAction, bool success) { this.EndAction = endAction; - this.Success = success; - this.Disposed = false; + this.Success = success; + this.Disposed = false; } + + public bool Success { get; } + + public bool Disposed { get; private set; } + + private Action EndAction { get; } public void Dispose() { @@ -247,8 +257,4 @@ public static partial class ImRaii this.Disposed = true; } } - - // Used to avoid tree pops when flag for no push is set. - private static void Nop() - { } } diff --git a/Dalamud.Interface/Raii/Font.cs b/Dalamud/Interface/Utility/Raii/Font.cs similarity index 81% rename from Dalamud.Interface/Raii/Font.cs rename to Dalamud/Interface/Utility/Raii/Font.cs index cdecf457c..2d11bb071 100644 --- a/Dalamud.Interface/Raii/Font.cs +++ b/Dalamud/Interface/Utility/Raii/Font.cs @@ -1,6 +1,6 @@ using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; // Push an arbitrary amount of fonts into an object that are all popped when it is disposed. // If condition is false, no font is pushed. @@ -18,10 +18,10 @@ public static partial class ImRaii internal static int FontPushCounter = 0; internal static ImFontPtr DefaultPushed; - private int _count; + private int count; public Font() - => this._count = 0; + => this.count = 0; public Font Push(ImFontPtr font, bool condition = true) { @@ -30,7 +30,7 @@ public static partial class ImRaii if (FontPushCounter++ == 0) DefaultPushed = ImGui.GetFont(); ImGui.PushFont(font); - ++this._count; + ++this.count; } return this; @@ -38,14 +38,14 @@ public static partial class ImRaii public void Pop(int num = 1) { - num = Math.Min(num, this._count); - this._count -= num; + num = Math.Min(num, this.count); + this.count -= num; FontPushCounter -= num; while (num-- > 0) ImGui.PopFont(); } public void Dispose() - => this.Pop(this._count); + => this.Pop(this.count); } } diff --git a/Dalamud.Interface/Raii/Id.cs b/Dalamud/Interface/Utility/Raii/Id.cs similarity index 82% rename from Dalamud.Interface/Raii/Id.cs rename to Dalamud/Interface/Utility/Raii/Id.cs index 1248b92f3..51c6438c4 100644 --- a/Dalamud.Interface/Raii/Id.cs +++ b/Dalamud/Interface/Utility/Raii/Id.cs @@ -1,6 +1,6 @@ using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; // Push an arbitrary amount of ids into an object that are all popped when it is disposed. // If condition is false, no id is pushed. @@ -17,14 +17,14 @@ public static partial class ImRaii public sealed class Id : IDisposable { - private int _count; + private int count; public Id Push(string id, bool condition = true) { if (condition) { ImGui.PushID(id); - ++this._count; + ++this.count; } return this; @@ -35,7 +35,7 @@ public static partial class ImRaii if (condition) { ImGui.PushID(id); - ++this._count; + ++this.count; } return this; @@ -46,7 +46,7 @@ public static partial class ImRaii if (condition) { ImGui.PushID(id); - ++this._count; + ++this.count; } return this; @@ -54,13 +54,13 @@ public static partial class ImRaii public void Pop(int num = 1) { - num = Math.Min(num, this._count); - this._count -= num; + num = Math.Min(num, this.count); + this.count -= num; while (num-- > 0) ImGui.PopID(); } public void Dispose() - => this.Pop(this._count); + => this.Pop(this.count); } } diff --git a/Dalamud.Interface/Raii/Indent.cs b/Dalamud/Interface/Utility/Raii/Indent.cs similarity index 91% rename from Dalamud.Interface/Raii/Indent.cs rename to Dalamud/Interface/Utility/Raii/Indent.cs index 99eab8783..3c8f0f1da 100644 --- a/Dalamud.Interface/Raii/Indent.cs +++ b/Dalamud/Interface/Utility/Raii/Indent.cs @@ -1,6 +1,6 @@ using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; public static partial class ImRaii { @@ -19,7 +19,7 @@ public static partial class ImRaii if (condition) { if (scaled) - indent *= InterfaceHelpers.GlobalScale; + indent *= ImGuiHelpers.GlobalScale; IndentInternal(indent); this.Indentation += indent; @@ -43,7 +43,7 @@ public static partial class ImRaii public void Pop(float indent, bool scaled = true) { if (scaled) - indent *= InterfaceHelpers.GlobalScale; + indent *= ImGuiHelpers.GlobalScale; IndentInternal(-indent); this.Indentation -= indent; diff --git a/Dalamud.Interface/Raii/Style.cs b/Dalamud/Interface/Utility/Raii/Style.cs similarity index 95% rename from Dalamud.Interface/Raii/Style.cs rename to Dalamud/Interface/Utility/Raii/Style.cs index 2f1fea538..82f51bf88 100644 --- a/Dalamud.Interface/Raii/Style.cs +++ b/Dalamud/Interface/Utility/Raii/Style.cs @@ -1,7 +1,10 @@ +using System.Collections.Generic; +using System.Linq; using System.Numerics; + using ImGuiNET; -namespace Dalamud.Interface.Raii; +namespace Dalamud.Interface.Utility.Raii; // Push an arbitrary amount of styles into an object that are all popped when it is disposed. // If condition is false, no style is pushed. @@ -17,7 +20,7 @@ public static partial class ImRaii // Push styles that revert all current style changes made temporarily. public static Style DefaultStyle() { - var ret = new Style(); + var ret = new Style(); var reverseStack = Style.Stack.GroupBy(p => p.Item1).Select(p => (p.Key, p.First().Item2)).ToArray(); foreach (var (idx, val) in reverseStack) { @@ -34,7 +37,7 @@ public static partial class ImRaii { internal static readonly List<(ImGuiStyleVar, Vector2)> Stack = new(); - private int _count; + private int count; [System.Diagnostics.Conditional("DEBUG")] private static void CheckStyleIdx(ImGuiStyleVar idx, Type type) @@ -115,7 +118,7 @@ public static partial class ImRaii CheckStyleIdx(idx, typeof(float)); Stack.Add((idx, GetStyle(idx))); ImGui.PushStyleVar(idx, value); - ++this._count; + ++this.count; return this; } @@ -128,20 +131,20 @@ public static partial class ImRaii CheckStyleIdx(idx, typeof(Vector2)); Stack.Add((idx, GetStyle(idx))); ImGui.PushStyleVar(idx, value); - ++this._count; + ++this.count; return this; } public void Pop(int num = 1) { - num = Math.Min(num, this._count); - this._count -= num; + num = Math.Min(num, this.count); + this.count -= num; ImGui.PopStyleVar(num); Stack.RemoveRange(Stack.Count - num, num); } public void Dispose() - => this.Pop(this._count); + => this.Pop(this.count); } } diff --git a/Dalamud.Interface/Table/Column.cs b/Dalamud/Interface/Utility/Table/Column.cs similarity index 92% rename from Dalamud.Interface/Table/Column.cs rename to Dalamud/Interface/Utility/Table/Column.cs index 7460ec189..412ba87dc 100644 --- a/Dalamud.Interface/Table/Column.cs +++ b/Dalamud/Interface/Utility/Table/Column.cs @@ -1,6 +1,6 @@ using ImGuiNET; -namespace Dalamud.Interface.Table; +namespace Dalamud.Interface.Utility.Table; public class Column { @@ -27,7 +27,8 @@ public class Column => 0; public virtual void DrawColumn(TItem item, int idx) - { } + { + } public int CompareInv(TItem lhs, TItem rhs) => this.Compare(rhs, lhs); diff --git a/Dalamud.Interface/Table/ColumnFlags.cs b/Dalamud/Interface/Utility/Table/ColumnFlags.cs similarity index 89% rename from Dalamud.Interface/Table/ColumnFlags.cs rename to Dalamud/Interface/Utility/Table/ColumnFlags.cs index 815ddcf76..24670adfc 100644 --- a/Dalamud.Interface/Table/ColumnFlags.cs +++ b/Dalamud/Interface/Utility/Table/ColumnFlags.cs @@ -1,7 +1,9 @@ -using ImGuiNET; -using ImRaii = Dalamud.Interface.Raii.ImRaii; +using System.Collections.Generic; -namespace Dalamud.Interface.Table; +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; + +namespace Dalamud.Interface.Utility.Table; public class ColumnFlags : Column where T : struct, Enum { @@ -17,13 +19,14 @@ public class ColumnFlags : Column where T : struct, Enum => default; protected virtual void SetValue(T value, bool enable) - { } + { + } public override bool DrawFilter() { using var id = ImRaii.PushId(this.FilterLabel); using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0); - ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale); + ImGui.SetNextItemWidth(-Table.ArrowWidth * ImGuiHelpers.GlobalScale); var all = this.FilterValue.HasFlag(this.AllFlags); using var color = ImRaii.PushColor(ImGuiCol.FrameBg, 0x803030A0, !all); using var combo = ImRaii.Combo(string.Empty, this.Label, ImGuiComboFlags.NoArrowButton); diff --git a/Dalamud.Interface/Table/ColumnSelect.cs b/Dalamud/Interface/Utility/Table/ColumnSelect.cs similarity index 66% rename from Dalamud.Interface/Table/ColumnSelect.cs rename to Dalamud/Interface/Utility/Table/ColumnSelect.cs index 5ef276b06..fb463700c 100644 --- a/Dalamud.Interface/Table/ColumnSelect.cs +++ b/Dalamud/Interface/Utility/Table/ColumnSelect.cs @@ -1,7 +1,9 @@ -using ImGuiNET; -using ImRaii = Dalamud.Interface.Raii.ImRaii; +using System.Collections.Generic; -namespace Dalamud.Interface.Table; +using Dalamud.Interface.Utility.Raii; +using ImGuiNET; + +namespace Dalamud.Interface.Utility.Table; public class ColumnSelect : Column where T : struct, Enum, IEquatable { @@ -18,26 +20,26 @@ public class ColumnSelect : Column where T : struct, Enum, IEqu => this.FilterValue = value; public T FilterValue; - protected int Idx = -1; + protected int idx = -1; public override bool DrawFilter() { using var id = ImRaii.PushId(this.FilterLabel); using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0); - ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale); - using var combo = ImRaii.Combo(string.Empty, this.Idx < 0 ? this.Label : this.Names[this.Idx]); - if(!combo) + ImGui.SetNextItemWidth(-Table.ArrowWidth * ImGuiHelpers.GlobalScale); + using var combo = ImRaii.Combo(string.Empty, this.idx < 0 ? this.Label : this.Names[this.idx]); + if (!combo) return false; var ret = false; for (var i = 0; i < this.Names.Length; ++i) { if (this.FilterValue.Equals(this.Values[i])) - this.Idx = i; - if (!ImGui.Selectable(this.Names[i], this.Idx == i) || this.Idx == i) + this.idx = i; + if (!ImGui.Selectable(this.Names[i], this.idx == i) || this.idx == i) continue; - this.Idx = i; + this.idx = i; this.SetValue(this.Values[i]); ret = true; } diff --git a/Dalamud.Interface/Table/ColumnString.cs b/Dalamud/Interface/Utility/Table/ColumnString.cs similarity index 75% rename from Dalamud.Interface/Table/ColumnString.cs rename to Dalamud/Interface/Utility/Table/ColumnString.cs index dcd43b23c..3f9d2df91 100644 --- a/Dalamud.Interface/Table/ColumnString.cs +++ b/Dalamud/Interface/Utility/Table/ColumnString.cs @@ -1,8 +1,9 @@ using System.Text.RegularExpressions; -using Dalamud.Interface.Raii; + +using Dalamud.Interface.Utility.Raii; using ImGuiNET; -namespace Dalamud.Interface.Table; +namespace Dalamud.Interface.Utility.Table; public class ColumnString : Column { @@ -10,7 +11,7 @@ public class ColumnString : Column => this.Flags &= ~ImGuiTableColumnFlags.NoResize; public string FilterValue = string.Empty; - protected Regex? FilterRegex; + protected Regex? filterRegex; public virtual string ToName(TItem item) => item!.ToString() ?? string.Empty; @@ -22,7 +23,7 @@ public class ColumnString : Column { using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0); - ImGui.SetNextItemWidth(-Table.ArrowWidth * InterfaceHelpers.GlobalScale); + ImGui.SetNextItemWidth(-Table.ArrowWidth * ImGuiHelpers.GlobalScale); var tmp = this.FilterValue; if (!ImGui.InputTextWithHint(this.FilterLabel, this.Label, ref tmp, 256) || tmp == this.FilterValue) return false; @@ -30,11 +31,11 @@ public class ColumnString : Column this.FilterValue = tmp; try { - this.FilterRegex = new Regex(this.FilterValue, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + this.filterRegex = new Regex(this.FilterValue, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } catch { - this.FilterRegex = null; + this.filterRegex = null; } return true; @@ -46,10 +47,10 @@ public class ColumnString : Column if (this.FilterValue.Length == 0) return true; - return this.FilterRegex?.IsMatch(name) ?? name.Contains(this.FilterValue, StringComparison.OrdinalIgnoreCase); + return this.filterRegex?.IsMatch(name) ?? name.Contains(this.FilterValue, StringComparison.OrdinalIgnoreCase); } - public override void DrawColumn(TItem item, int _) + public override void DrawColumn(TItem item, int idx) { ImGui.TextUnformatted(this.ToName(item)); } diff --git a/Dalamud.Interface/Table/Table.cs b/Dalamud/Interface/Utility/Table/Table.cs similarity index 64% rename from Dalamud.Interface/Table/Table.cs rename to Dalamud/Interface/Utility/Table/Table.cs index 74fb0bc5c..86653e834 100644 --- a/Dalamud.Interface/Table/Table.cs +++ b/Dalamud/Interface/Utility/Table/Table.cs @@ -1,8 +1,12 @@ +using System.Collections.Generic; +using System.Linq; using System.Numerics; -using ImGuiNET; -using ImRaii = Dalamud.Interface.Raii.ImRaii; -namespace Dalamud.Interface.Table; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility; +using ImGuiNET; + +namespace Dalamud.Interface.Utility.Table; public static class Table { @@ -11,18 +15,20 @@ public static class Table public class Table { - protected bool FilterDirty = true; - protected bool SortDirty = true; protected readonly ICollection Items; - internal readonly List<(T, int)> FilteredItems; + internal readonly List<(T, int)> FilteredItems; - protected readonly string Label; + protected readonly string Label; protected readonly Column[] Headers; - protected float ItemHeight { get; set; } - public float ExtraHeight { get; set; } = 0; + protected bool filterDirty = true; + protected bool sortDirty = true; - private int _currentIdx = 0; + protected float ItemHeight { get; set; } + + public float ExtraHeight { get; set; } = 0; + + private int currentIdx = 0; protected bool Sortable { @@ -30,7 +36,7 @@ public class Table set => this.Flags = value ? this.Flags | ImGuiTableFlags.Sortable : this.Flags & ~ImGuiTableFlags.Sortable; } - protected int SortIdx = -1; + protected int sortIdx = -1; public ImGuiTableFlags Flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.Sortable @@ -54,10 +60,10 @@ public class Table public Table(string label, ICollection items, params Column[] headers) { - this.Label = label; - this.Items = items; - this.Headers = headers; - this.FilteredItems = new List<(T, int)>(this.Items.Count); + this.Label = label; + this.Items = items; + this.Headers = headers; + this.FilteredItems = new List<(T, int)>(this.Items.Count); this.VisibleColumns = this.Headers.Length; } @@ -73,7 +79,8 @@ public class Table => throw new NotImplementedException(); protected virtual void PreDraw() - { } + { + } private void SortInternal() { @@ -81,29 +88,30 @@ public class Table return; var sortSpecs = ImGui.TableGetSortSpecs(); - this.SortDirty |= sortSpecs.SpecsDirty; + this.sortDirty |= sortSpecs.SpecsDirty; - if (!this.SortDirty) + if (!this.sortDirty) return; - this.SortIdx = sortSpecs.Specs.ColumnIndex; + this.sortIdx = sortSpecs.Specs.ColumnIndex; - if (this.Headers.Length <= this.SortIdx) - this.SortIdx = 0; + if (this.Headers.Length <= this.sortIdx) + this.sortIdx = 0; - if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending) - this.FilteredItems.StableSort((a, b) => this.Headers[this.SortIdx].Compare(a.Item1, b.Item1)); - else if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending) - this.FilteredItems.StableSort((a, b) => this.Headers[this.SortIdx].CompareInv(a.Item1, b.Item1)); - else - this.SortIdx = -1; - this.SortDirty = false; - sortSpecs.SpecsDirty = false; + if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending) + this.FilteredItems.StableSort((a, b) => this.Headers[this.sortIdx].Compare(a.Item1, b.Item1)); + else if (sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending) + this.FilteredItems.StableSort((a, b) => this.Headers[this.sortIdx].CompareInv(a.Item1, b.Item1)); + else + this.sortIdx = -1; + + this.sortDirty = false; + sortSpecs.SpecsDirty = false; } private void UpdateFilter() { - if (!this.FilterDirty) + if (!this.filterDirty) return; this.FilteredItems.Clear(); @@ -115,20 +123,20 @@ public class Table idx++; } - this.FilterDirty = false; - this.SortDirty = true; + this.filterDirty = false; + this.sortDirty = true; } - private void DrawItem((T, int) pair) + private void DrawItem((T Item, int Index) pair) { - var column = 0; - using var id = ImRaii.PushId(this._currentIdx); - this._currentIdx = pair.Item2; + var column = 0; + using var id = ImRaii.PushId(this.currentIdx); + this.currentIdx = pair.Index; foreach (var header in this.Headers) { id.Push(column++); if (ImGui.TableNextColumn()) - header.DrawColumn(pair.Item1, pair.Item2); + header.DrawColumn(pair.Item, pair.Index); id.Pop(); } } @@ -136,7 +144,7 @@ public class Table private void DrawTableInternal() { using var table = ImRaii.Table("Table", this.Headers.Length, this.Flags, - ImGui.GetContentRegionAvail() - this.ExtraHeight * Vector2.UnitY * InterfaceHelpers.GlobalScale); + ImGui.GetContentRegionAvail() - this.ExtraHeight * Vector2.UnitY * ImGuiHelpers.GlobalScale); if (!table) return; @@ -162,11 +170,11 @@ public class Table ImGui.SameLine(); style.Pop(); if (header.DrawFilter()) - this.FilterDirty = true; + this.filterDirty = true; } this.SortInternal(); - this._currentIdx = 0; + this.currentIdx = 0; ImGuiClip.ClippedDraw(this.FilteredItems, this.DrawItem, this.ItemHeight); } } diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs index 277fb46c8..ad424f59a 100644 --- a/Dalamud/Interface/Windowing/Window.cs +++ b/Dalamud/Interface/Windowing/Window.cs @@ -1,8 +1,8 @@ -using System; using System.Numerics; using Dalamud.Configuration.Internal; using Dalamud.Game.ClientState.Keys; +using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; using FFXIVClientStructs.FFXIV.Client.UI; using ImGuiNET; @@ -227,6 +227,7 @@ public abstract class Window /// /// Draw the window via ImGui. /// + /// Configuration instance used to check if certain window management features should be enabled. internal void DrawInternal(DalamudConfiguration? configuration) { this.PreOpenCheck(); diff --git a/Dalamud/Interface/Windowing/WindowSystem.cs b/Dalamud/Interface/Windowing/WindowSystem.cs index 8e12d8f68..3e2a95a8d 100644 --- a/Dalamud/Interface/Windowing/WindowSystem.cs +++ b/Dalamud/Interface/Windowing/WindowSystem.cs @@ -94,14 +94,6 @@ public class WindowSystem /// public void RemoveAllWindows() => this.windows.Clear(); - /// - /// Get a window by name. - /// - /// The name of the . - /// The object with matching name or null. - [Obsolete("WindowSystem does not own your window - you should store a reference to it and use that instead.")] - public Window? GetWindow(string windowName) => this.windows.FirstOrDefault(w => w.WindowName == windowName); - /// /// Draw all registered windows using ImGui. /// diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index db748303e..ce7ce25a1 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -16,6 +16,7 @@ namespace Dalamud.IoC.Internal; /// This is only used to resolve dependencies for plugins. /// Dalamud services are constructed via Service{T}.ConstructObject at the moment. /// +[ServiceManager.Service] internal class ServiceContainer : IServiceProvider, IServiceType { private static readonly ModuleLog Log = new("SERVICECONTAINER"); @@ -29,6 +30,16 @@ internal class ServiceContainer : IServiceProvider, IServiceType public ServiceContainer() { } + + /// + /// Gets a dictionary of all registered instances. + /// + public IReadOnlyDictionary Instances => this.instances; + + /// + /// Gets a dictionary mapping interfaces to their implementations. + /// + public IReadOnlyDictionary InterfaceToTypeMap => this.interfaceToTypeMap; /// /// Register a singleton object of any type into the current IOC container. @@ -43,7 +54,6 @@ internal class ServiceContainer : IServiceProvider, IServiceType } this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T)); - this.RegisterInterfaces(typeof(T)); } /// @@ -59,7 +69,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType foreach (var resolvableType in resolveViaTypes) { Log.Verbose("=> {InterfaceName} provides for {TName}", resolvableType.FullName ?? "???", type.FullName ?? "???"); - + Debug.Assert(!this.interfaceToTypeMap.ContainsKey(resolvableType), "A service already implements this interface, this is not allowed"); Debug.Assert(type.IsAssignableTo(resolvableType), "Service does not inherit from indicated ResolveVia type"); diff --git a/Dalamud/Logging/Internal/TaskTracker.cs b/Dalamud/Logging/Internal/TaskTracker.cs index a8729893f..b65f0efa7 100644 --- a/Dalamud/Logging/Internal/TaskTracker.cs +++ b/Dalamud/Logging/Internal/TaskTracker.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; @@ -6,6 +5,7 @@ using System.Reflection; using System.Threading.Tasks; using Dalamud.Game; +using Dalamud.Plugin.Services; namespace Dalamud.Logging.Internal; @@ -141,7 +141,7 @@ internal class TaskTracker : IDisposable, IServiceType return true; } - private void FrameworkOnUpdate(Framework framework) + private void FrameworkOnUpdate(IFramework framework) { UpdateData(); } diff --git a/Dalamud/Logging/PluginLog.cs b/Dalamud/Logging/PluginLog.cs index 3ac98f15a..decf10b4c 100644 --- a/Dalamud/Logging/PluginLog.cs +++ b/Dalamud/Logging/PluginLog.cs @@ -1,6 +1,6 @@ -using System; using System.Reflection; +using Dalamud.Plugin.Services; using Serilog; using Serilog.Events; @@ -9,6 +9,11 @@ namespace Dalamud.Logging; /// /// Class offering various static methods to allow for logging in plugins. /// +/// +/// PluginLog has been obsoleted and replaced by the service. Developers are encouraged to +/// move over as soon as reasonably possible for performance reasons. +/// +[Obsolete("Static PluginLog will be removed in API 10. Developers should use IPluginLog.")] public static class PluginLog { #region "Log" prefixed Serilog style methods diff --git a/Dalamud/Logging/ScopedPluginLogService.cs b/Dalamud/Logging/ScopedPluginLogService.cs index 8c502fcf0..924b4885d 100644 --- a/Dalamud/Logging/ScopedPluginLogService.cs +++ b/Dalamud/Logging/ScopedPluginLogService.cs @@ -1,6 +1,4 @@ -using System; - -using Dalamud.IoC; +using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; @@ -19,7 +17,7 @@ namespace Dalamud.Logging; #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -public class ScopedPluginLogService : IServiceType, IPluginLog, IDisposable +internal class ScopedPluginLogService : IServiceType, IPluginLog, IDisposable { private readonly LocalPlugin localPlugin; @@ -50,7 +48,9 @@ public class ScopedPluginLogService : IServiceType, IPluginLog, IDisposable set => this.levelSwitch.MinimumLevel = value; } - /// + /// + /// Gets a logger that may be exposed to plugins some day. + /// public ILogger Logger { get; } /// @@ -90,6 +90,14 @@ public class ScopedPluginLogService : IServiceType, IPluginLog, IDisposable /// public void Information(Exception? exception, string messageTemplate, params object[] values) => this.Write(LogEventLevel.Information, exception, messageTemplate, values); + + /// + public void Info(string messageTemplate, params object[] values) => + this.Information(messageTemplate, values); + + /// + public void Info(Exception? exception, string messageTemplate, params object[] values) => + this.Information(exception, messageTemplate, values); /// public void Debug(string messageTemplate, params object[] values) => diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index 5b640f64c..3ceecf6a6 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -167,7 +167,7 @@ public static unsafe class MemoryHelper /// Read a UTF-8 encoded string from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The read in string. @@ -178,7 +178,7 @@ public static unsafe class MemoryHelper /// Read a string with the given encoding from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The encoding to use to decode the string. @@ -193,7 +193,7 @@ public static unsafe class MemoryHelper /// Read a UTF-8 encoded string from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The maximum length of the string. @@ -205,7 +205,7 @@ public static unsafe class MemoryHelper /// Read a string with the given encoding from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The encoding to use to decode the string. @@ -284,7 +284,7 @@ public static unsafe class MemoryHelper /// Read a UTF-8 encoded string from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The read in string. @@ -295,7 +295,7 @@ public static unsafe class MemoryHelper /// Read a string with the given encoding from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The encoding to use to decode the string. @@ -307,7 +307,7 @@ public static unsafe class MemoryHelper /// Read a UTF-8 encoded string from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The read in string. @@ -319,7 +319,7 @@ public static unsafe class MemoryHelper /// Read a string with the given encoding from a specified memory address. /// /// - /// Attention! If this is an SeString, use the to decode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to read from. /// The encoding to use to decode the string. @@ -426,7 +426,7 @@ public static unsafe class MemoryHelper /// Write a UTF-8 encoded string to a specified memory address. /// /// - /// Attention! If this is an SeString, use the to encode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to write to. /// The string to write. @@ -437,7 +437,7 @@ public static unsafe class MemoryHelper /// Write a string with the given encoding to a specified memory address. /// /// - /// Attention! If this is an SeString, use the to encode or the applicable helper method. + /// Attention! If this is an , use the applicable helper methods to decode. /// /// The memory address to write to. /// The string to write. diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 7d788726d..004b7196c 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -206,18 +206,6 @@ public sealed class DalamudPluginInterface : IDisposable /// public XivChatType GeneralChatType { get; private set; } - /// - /// Gets a list of installed plugin names. - /// - [Obsolete($"This property is obsolete. Use {nameof(InstalledPlugins)} instead.")] - public List PluginNames => Service.Get().InstalledPlugins.Select(p => p.Manifest.Name).ToList(); - - /// - /// Gets a list of installed plugin internal names. - /// - [Obsolete($"This property is obsolete. Use {nameof(InstalledPlugins)} instead.")] - public List PluginInternalNames => Service.Get().InstalledPlugins.Select(p => p.Manifest.InternalName).ToList(); - /// /// Gets a list of installed plugins along with their current state. /// @@ -355,7 +343,7 @@ public sealed class DalamudPluginInterface : IDisposable if (currentConfig == null) return; - this.configs.Save(currentConfig, this.plugin.InternalName); + this.configs.Save(currentConfig, this.plugin.InternalName, this.plugin.Manifest.WorkingPluginId); } /// @@ -382,7 +370,7 @@ public sealed class DalamudPluginInterface : IDisposable } // this shouldn't be a thing, I think, but just in case - return this.configs.Load(this.plugin.InternalName); + return this.configs.Load(this.plugin.InternalName, this.plugin.Manifest.WorkingPluginId); } /// diff --git a/Dalamud/Plugin/IDalamudPlugin.cs b/Dalamud/Plugin/IDalamudPlugin.cs index c752df3d6..b48d55d1c 100644 --- a/Dalamud/Plugin/IDalamudPlugin.cs +++ b/Dalamud/Plugin/IDalamudPlugin.cs @@ -7,8 +7,4 @@ namespace Dalamud.Plugin; /// public interface IDalamudPlugin : IDisposable { - /// - /// Gets the name of the plugin. - /// - string Name { get; } } diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index e66f226ea..dc658792c 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -53,11 +53,6 @@ namespace Dalamud.Plugin.Internal; #pragma warning restore SA1015 internal partial class PluginManager : IDisposable, IServiceType { - /// - /// The current Dalamud API level, used to handle breaking changes. Only plugins with this level will be loaded. - /// - public const int DalamudApiLevel = 8; - /// /// Default time to wait between plugin unload and plugin assembly unload. /// @@ -79,7 +74,7 @@ internal partial class PluginManager : IDisposable, IServiceType private readonly DalamudConfiguration configuration = Service.Get(); [ServiceManager.ServiceDependency] - private readonly DalamudStartInfo startInfo = Service.Get(); + private readonly Dalamud dalamud = Service.Get(); [ServiceManager.ServiceDependency] private readonly ProfileManager profileManager = Service.Get(); @@ -87,15 +82,20 @@ internal partial class PluginManager : IDisposable, IServiceType [ServiceManager.ServiceDependency] private readonly HappyHttpClient happyHttpClient = Service.Get(); + static PluginManager() + { + DalamudApiLevel = typeof(PluginManager).Assembly.GetName().Version!.Major; + } + [ServiceManager.ServiceConstructor] private PluginManager() { - this.pluginDirectory = new DirectoryInfo(this.startInfo.PluginDirectory!); + this.pluginDirectory = new DirectoryInfo(this.dalamud.StartInfo.PluginDirectory!); if (!this.pluginDirectory.Exists) this.pluginDirectory.Create(); - this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins; + this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.dalamud.StartInfo.NoLoadPlugins; try { @@ -119,9 +119,9 @@ internal partial class PluginManager : IDisposable, IServiceType this.configuration.QueueSave(); } - this.PluginConfigs = new PluginConfigurations(Path.Combine(Path.GetDirectoryName(this.startInfo.ConfigurationPath) ?? string.Empty, "pluginConfigs")); + this.PluginConfigs = new PluginConfigurations(Path.Combine(Path.GetDirectoryName(this.dalamud.StartInfo.ConfigurationPath) ?? string.Empty, "pluginConfigs")); - var bannedPluginsJson = File.ReadAllText(Path.Combine(this.startInfo.AssetDirectory!, "UIRes", "bannedplugin.json")); + var bannedPluginsJson = File.ReadAllText(Path.Combine(this.dalamud.StartInfo.AssetDirectory!, "UIRes", "bannedplugin.json")); this.bannedPlugins = JsonConvert.DeserializeObject(bannedPluginsJson); if (this.bannedPlugins == null) { @@ -148,6 +148,12 @@ internal partial class PluginManager : IDisposable, IServiceType /// public event Action? OnAvailablePluginsChanged; + /// + /// Gets the current Dalamud API level, used to handle breaking changes. Only plugins with this level will be loaded. + /// As of Dalamud 9.x, this always matches the major version number of Dalamud. + /// + public static int DalamudApiLevel { get; private set; } + /// /// Gets a copy of the list of all loaded plugins. /// @@ -279,7 +285,7 @@ internal partial class PluginManager : IDisposable, IServiceType if (updateMetadata is { Count: > 0 }) { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = new SeString(new List() { @@ -302,7 +308,7 @@ internal partial class PluginManager : IDisposable, IServiceType } else { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version), Type = XivChatType.Urgent, @@ -617,7 +623,7 @@ internal partial class PluginManager : IDisposable, IServiceType Log.Error(e, "Failed to load at least one plugin"); } - var sigScanner = await Service.GetAsync().ConfigureAwait(false); + var sigScanner = await Service.GetAsync().ConfigureAwait(false); this.PluginsReady = true; this.NotifyinstalledPluginsListChanged(); sigScanner.Save(); @@ -625,23 +631,6 @@ internal partial class PluginManager : IDisposable, IServiceType tokenSource.Token); } - /// - /// Reload all loaded plugins. - /// - /// A task. - [Obsolete("This method should no longer be used and will be removed in a future release.")] - public Task ReloadAllPluginsAsync() - { - lock (this.pluginListLock) - { - return Task.WhenAll(this.installedPluginsList - .Where(x => x.IsLoaded) - .ToList() - .Select(x => Task.Run(async () => await x.ReloadAsync())) - .ToList()); - } - } - /// /// Reload the PluginMaster for each repo, filter, and event that the list has updated. /// @@ -847,7 +836,7 @@ internal partial class PluginManager : IDisposable, IServiceType var manifestFile = LocalPluginManifest.GetManifestFile(dllFile); // We need to save the repoManifest due to how the repo fills in some fields that authors are not expected to use. - File.WriteAllText(manifestFile.FullName, JsonConvert.SerializeObject(repoManifest, Formatting.Indented)); + Util.WriteAllTextSafe(manifestFile.FullName, JsonConvert.SerializeObject(repoManifest, Formatting.Indented)); // Reload as a local manifest, add some attributes, and save again. var manifest = LocalPluginManifest.Load(manifestFile); @@ -1179,7 +1168,7 @@ internal partial class PluginManager : IDisposable, IServiceType } // Applicable version - if (manifest.ApplicableVersion < this.startInfo.GameVersion) + if (manifest.ApplicableVersion < this.dalamud.StartInfo.GameVersion) { Log.Verbose($"Game version: {manifest.InternalName} - {manifest.AssemblyVersion} - {manifest.TestingAssemblyVersion}"); return false; diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs index 8ea55856c..7001e4d7b 100644 --- a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs +++ b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -7,6 +6,7 @@ using CheapLoc; using Dalamud.Game; using Dalamud.Game.Command; using Dalamud.Game.Gui; +using Dalamud.Plugin.Services; using Dalamud.Utility; using Serilog; @@ -78,7 +78,7 @@ internal class ProfileCommandHandler : IServiceType, IDisposable this.framework.Update += this.FrameworkOnUpdate; } - private void FrameworkOnUpdate(Framework framework1) + private void FrameworkOnUpdate(IFramework framework1) { if (this.profileManager.IsBusy) return; diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index f7306b5a7..57bea0f57 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -1,10 +1,10 @@ -using System; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Dalamud.Common.Game; using Dalamud.Configuration.Internal; using Dalamud.Game; using Dalamud.Game.Gui.Dtr; @@ -336,7 +336,7 @@ internal class LocalPlugin : IDisposable var framework = await Service.GetAsync(); var ioc = await Service.GetAsync(); var pluginManager = await Service.GetAsync(); - var startInfo = await Service.GetAsync(); + var dalamud = await Service.GetAsync(); // UiBuilder constructor requires the following two. await Service.GetAsync(); @@ -392,7 +392,7 @@ internal class LocalPlugin : IDisposable if (pluginManager.IsManifestBanned(this.manifest) && !this.IsDev) throw new BannedPluginException($"Unable to load {this.Name}, banned"); - if (this.manifest.ApplicableVersion < startInfo.GameVersion) + if (this.manifest.ApplicableVersion < dalamud.StartInfo.GameVersion) throw new InvalidPluginOperationException($"Unable to load {this.Name}, no applicable version"); if (this.manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels) @@ -505,13 +505,6 @@ internal class LocalPlugin : IDisposable return; } - // In-case the manifest name was a placeholder. Can occur when no manifest was included. - if (this.manifest.Name.IsNullOrEmpty() && !this.IsDev) - { - this.manifest.Name = this.instance.Name; - this.manifest.Save(this.manifestFile, "manifest name null or empty"); - } - this.State = PluginState.Loaded; Log.Information($"Finished loading {this.DllFile.Name}"); } @@ -631,7 +624,7 @@ internal class LocalPlugin : IDisposable /// Whether or not this plugin shouldn't load. public bool CheckPolicy() { - var startInfo = Service.Get(); + var startInfo = Service.Get().StartInfo; var manager = Service.Get(); if (startInfo.NoLoadPlugins) diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs index 0b5ec26fc..34fa04f6e 100644 --- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs @@ -1,7 +1,6 @@ -using System; using System.Collections.Generic; -using Dalamud.Game; +using Dalamud.Common.Game; using Dalamud.Plugin.Internal.Types.Manifest; using Newtonsoft.Json; diff --git a/Dalamud/Plugin/Services/IAddonEventManager.cs b/Dalamud/Plugin/Services/IAddonEventManager.cs index 52f836b4f..e696bbaae 100644 --- a/Dalamud/Plugin/Services/IAddonEventManager.cs +++ b/Dalamud/Plugin/Services/IAddonEventManager.cs @@ -1,4 +1,5 @@ using Dalamud.Game.Addon; +using Dalamud.Game.Addon.Events; namespace Dalamud.Plugin.Services; diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs index 2bc41a366..6f44349d5 100644 --- a/Dalamud/Plugin/Services/IAddonLifecycle.cs +++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs @@ -2,6 +2,8 @@ using System.Runtime.InteropServices; using Dalamud.Game.Addon; +using Dalamud.Game.Addon.Lifecycle; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; namespace Dalamud.Plugin.Services; diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs new file mode 100644 index 000000000..bafdabbb5 --- /dev/null +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -0,0 +1,117 @@ +using Dalamud.Game.Gui; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; + +namespace Dalamud.Plugin.Services; + +/// +/// This class handles interacting with the native chat UI. +/// +public interface IChatGui +{ + /// + /// A delegate type used with the event. + /// + /// The type of chat. + /// The sender ID. + /// The sender name. + /// The message sent. + /// A value indicating whether the message was handled or should be propagated. + public delegate void OnMessageDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled); + + /// + /// A delegate type used with the event. + /// + /// The type of chat. + /// The sender ID. + /// The sender name. + /// The message sent. + /// A value indicating whether the message was handled or should be propagated. + public delegate void OnCheckMessageHandledDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled); + + /// + /// A delegate type used with the event. + /// + /// The type of chat. + /// The sender ID. + /// The sender name. + /// The message sent. + public delegate void OnMessageHandledDelegate(XivChatType type, uint senderId, SeString sender, SeString message); + + /// + /// A delegate type used with the event. + /// + /// The type of chat. + /// The sender ID. + /// The sender name. + /// The message sent. + public delegate void OnMessageUnhandledDelegate(XivChatType type, uint senderId, SeString sender, SeString message); + + /// + /// Event that will be fired when a chat message is sent to chat by the game. + /// + public event OnMessageDelegate ChatMessage; + + /// + /// Event that allows you to stop messages from appearing in chat by setting the isHandled parameter to true. + /// + public event OnCheckMessageHandledDelegate CheckMessageHandled; + + /// + /// Event that will be fired when a chat message is handled by Dalamud or a Plugin. + /// + public event OnMessageHandledDelegate ChatMessageHandled; + + /// + /// Event that will be fired when a chat message is not handled by Dalamud or a Plugin. + /// + public event OnMessageUnhandledDelegate ChatMessageUnhandled; + + /// + /// Gets the ID of the last linked item. + /// + public int LastLinkedItemId { get; } + + /// + /// Gets the flags of the last linked item. + /// + public byte LastLinkedItemFlags { get; } + + /// + /// Queue a chat message. Dalamud will send queued messages on the next framework event. + /// + /// A message to send. + public void Print(XivChatEntry chat); + + /// + /// Queue a chat message. Dalamud will send queued messages on the next framework event. + /// + /// A message to send. + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void Print(string message, string? messageTag = null, ushort? tagColor = null); + + /// + /// Queue a chat message. Dalamud will send queued messages on the next framework event. + /// + /// A message to send. + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null); + + /// + /// Queue a chat message. Dalamud will send queued messages on the next framework event. + /// + /// A message to send. + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null); + + /// + /// Queue a chat message. Dalamud will send queued messages on the next framework event. + /// + /// A message to send. + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null); +} diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index d66db9cc9..652a6c888 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -12,17 +12,17 @@ public interface IClientState /// /// Event that gets fired when the current Territory changes. /// - public event EventHandler TerritoryChanged; + public event Action TerritoryChanged; /// /// Event that fires when a character is logging in, and the local character object is available. /// - public event EventHandler Login; + public event Action Login; /// /// Event that fires when a character is logging out. /// - public event EventHandler Logout; + public event Action Logout; /// /// Event that fires when a character is entering PvP. @@ -37,7 +37,7 @@ public interface IClientState /// /// Event that gets fired when a duty is ready. /// - public event EventHandler CfPop; + public event Action CfPop; /// /// Gets the language of the client. @@ -73,4 +73,9 @@ public interface IClientState /// Gets a value indicating whether or not the user is playing PvP, excluding the Wolves' Den. /// public bool IsPvPExcludingDen { get; } + + /// + /// Gets a value indicating whether the client is currently in Group Pose (GPose) mode. + /// + public bool IsGPosing { get; } } diff --git a/Dalamud/Plugin/Services/ICondition.cs b/Dalamud/Plugin/Services/ICondition.cs new file mode 100644 index 000000000..9700cef5a --- /dev/null +++ b/Dalamud/Plugin/Services/ICondition.cs @@ -0,0 +1,54 @@ +using Dalamud.Game.ClientState.Conditions; + +namespace Dalamud.Plugin.Services; + +/// +/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. +/// +public interface ICondition +{ + /// + /// A delegate type used with the event. + /// + /// The changed condition. + /// The value the condition is set to. + public delegate void ConditionChangeDelegate(ConditionFlag flag, bool value); + + /// + /// Event that gets fired when a condition is set. + /// Should only get fired for actual changes, so the previous value will always be !value. + /// + public event ConditionChangeDelegate? ConditionChange; + + /// + /// Gets the current max number of conditions. + /// + public int MaxEntries { get; } + + /// + /// Gets the condition array base pointer. + /// + public nint Address { get; } + + /// + /// Check the value of a specific condition/state flag. + /// + /// The condition flag to check. + public bool this[int flag] { get; } + + /// + public bool this[ConditionFlag flag] => this[(int)flag]; + + /// + /// Check if any condition flags are set. + /// + /// Whether any single flag is set. + public bool Any(); + + /// + /// Check if any provided condition flags are set. + /// + /// Whether any single provided flag is set. + /// The condition flags to check. + public bool Any(params ConditionFlag[] flags); +} diff --git a/Dalamud/Plugin/Services/IDataManager.cs b/Dalamud/Plugin/Services/IDataManager.cs index ff9b40605..4977b65b3 100644 --- a/Dalamud/Plugin/Services/IDataManager.cs +++ b/Dalamud/Plugin/Services/IDataManager.cs @@ -19,17 +19,7 @@ public interface IDataManager /// Gets the current game client language. /// public ClientLanguage Language { get; } - - /// - /// Gets the OpCodes sent by the server to the client. - /// - public ReadOnlyDictionary ServerOpCodes { get; } - - /// - /// Gets the OpCodes sent by the client to the server. - /// - public ReadOnlyDictionary ClientOpCodes { get; } - + /// /// Gets a object which gives access to any excel/game data. /// @@ -40,11 +30,6 @@ public interface IDataManager /// public ExcelModule Excel { get; } - /// - /// Gets a value indicating whether Game Data is ready to be read. - /// - public bool IsDataReady { get; } - /// /// Gets a value indicating whether the game data files have been modified by another third-party tool. /// @@ -86,111 +71,4 @@ public interface IDataManager /// The path inside of the game files. /// True if the file exists. public bool FileExists(string path); - - /// - /// Get a containing the icon with the given ID. - /// - /// The icon ID. - /// Return high resolution version. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(uint iconId, bool highResolution = false); - - /// - /// Get a containing the icon with the given ID, of the given language. - /// - /// The requested language. - /// The icon ID. - /// Return high resolution version. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(ClientLanguage iconLanguage, uint iconId, bool highResolution = false); - - /// - /// Get a containing the icon with the given ID, of the given type. - /// - /// The type of the icon (e.g. 'hq' to get the HQ variant of an item icon). - /// The icon ID. - /// Return high resolution version. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(string? type, uint iconId, bool highResolution = false); - - /// - /// Get a containing the icon with the given ID. - /// - /// The icon ID. - /// Return the high resolution version. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(uint iconId, bool highResolution = false); - - /// - /// Get a containing the icon with the given ID, of the given quality. - /// - /// A value indicating whether the icon should be HQ. - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetIcon(bool isHq, uint iconId); - - /// - /// Get a containing the HQ icon with the given ID. - /// - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TexFile? GetHqIcon(uint iconId); - - /// - /// Get the passed as a drawable ImGui TextureWrap. - /// - /// The Lumina . - /// A that can be used to draw the texture. - [Obsolete("Use ITextureProvider instead")] - [return: NotNullIfNotNull(nameof(tex))] - public TextureWrap? GetImGuiTexture(TexFile? tex); - - /// - /// Get the passed texture path as a drawable ImGui TextureWrap. - /// - /// The internal path to the texture. - /// A that can be used to draw the texture. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTexture(string path); - - /// - /// Get a containing the icon with the given ID, of the given quality. - /// - /// A value indicating whether the icon should be HQ. - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(bool isHq, uint iconId); - - /// - /// Get a containing the icon with the given ID, of the given language. - /// - /// The requested language. - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(ClientLanguage iconLanguage, uint iconId); - - /// - /// Get a containing the icon with the given ID, of the given type. - /// - /// The type of the icon (e.g. 'hq' to get the HQ variant of an item icon). - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureIcon(string type, uint iconId); - - /// - /// Get a containing the HQ icon with the given ID. - /// - /// The icon ID. - /// The containing the icon. - [Obsolete("Use ITextureProvider instead")] - public TextureWrap? GetImGuiTextureHqIcon(uint iconId); } diff --git a/Dalamud/Plugin/Services/IDutyState.cs b/Dalamud/Plugin/Services/IDutyState.cs index a2331364c..3d49f68cb 100644 --- a/Dalamud/Plugin/Services/IDutyState.cs +++ b/Dalamud/Plugin/Services/IDutyState.cs @@ -1,6 +1,4 @@ -using System; - -namespace Dalamud.Plugin.Services; +namespace Dalamud.Plugin.Services; /// /// This class represents the state of the currently occupied duty. diff --git a/Dalamud/Plugin/Services/IFlyTextGui.cs b/Dalamud/Plugin/Services/IFlyTextGui.cs new file mode 100644 index 000000000..04fae351d --- /dev/null +++ b/Dalamud/Plugin/Services/IFlyTextGui.cs @@ -0,0 +1,55 @@ +using Dalamud.Game.Gui.FlyText; +using Dalamud.Game.Text.SeStringHandling; + +namespace Dalamud.Plugin.Services; + +/// +/// This class facilitates interacting with and creating native in-game "fly text". +/// +public interface IFlyTextGui +{ + /// + /// The delegate defining the type for the FlyText event. + /// + /// The FlyTextKind. See . + /// Value1 passed to the native flytext function. + /// Value2 passed to the native flytext function. Seems unused. + /// Text1 passed to the native flytext function. + /// Text2 passed to the native flytext function. + /// Color passed to the native flytext function. Changes flytext color. + /// Icon ID passed to the native flytext function. Only displays with select FlyTextKind. + /// Damage Type Icon ID passed to the native flytext function. Displayed next to damage values to denote damage type. + /// The vertical offset to place the flytext at. 0 is default. Negative values result + /// in text appearing higher on the screen. This does not change where the element begins to fade. + /// Whether this flytext has been handled. If a subscriber sets this to true, the FlyText will not appear. + public delegate void OnFlyTextCreatedDelegate( + ref FlyTextKind kind, + ref int val1, + ref int val2, + ref SeString text1, + ref SeString text2, + ref uint color, + ref uint icon, + ref uint damageTypeIcon, + ref float yOffset, + ref bool handled); + + /// + /// The FlyText event that can be subscribed to. + /// + public event OnFlyTextCreatedDelegate? FlyTextCreated; + + /// + /// Displays a fly text in-game on the local player. + /// + /// The FlyTextKind. See . + /// The index of the actor to place flytext on. Indexing unknown. 1 places flytext on local player. + /// Value1 passed to the native flytext function. + /// Value2 passed to the native flytext function. Seems unused. + /// Text1 passed to the native flytext function. + /// Text2 passed to the native flytext function. + /// Color passed to the native flytext function. Changes flytext color. + /// Icon ID passed to the native flytext function. Only displays with select FlyTextKind. + /// Damage Type Icon ID passed to the native flytext function. Displayed next to damage values to denote damage type. + public void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon, uint damageTypeIcon); +} diff --git a/Dalamud/Plugin/Services/IFramework.cs b/Dalamud/Plugin/Services/IFramework.cs new file mode 100644 index 000000000..334577b92 --- /dev/null +++ b/Dalamud/Plugin/Services/IFramework.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using Dalamud.Game; + +namespace Dalamud.Plugin.Services; + +/// +/// This class represents the Framework of the native game client and grants access to various subsystems. +/// +public interface IFramework +{ + /// + /// A delegate type used with the event. + /// + /// The Framework instance. + public delegate void OnUpdateDelegate(IFramework framework); + + /// + /// Event that gets fired every time the game framework updates. + /// + public event OnUpdateDelegate Update; + + /// + /// Gets the last time that the Framework Update event was triggered. + /// + public DateTime LastUpdate { get; } + + /// + /// Gets the last time in UTC that the Framework Update event was triggered. + /// + public DateTime LastUpdateUTC { get; } + + /// + /// Gets the delta between the last Framework Update and the currently executing one. + /// + public TimeSpan UpdateDelta { get; } + + /// + /// Gets a value indicating whether currently executing code is running in the game's framework update thread. + /// + public bool IsInFrameworkUpdateThread { get; } + + /// + /// Gets a value indicating whether game Framework is unloading. + /// + public bool IsFrameworkUnloading { get; } + + /// + /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. + /// + /// Return type. + /// Function to call. + /// Task representing the pending or already completed function. + public Task RunOnFrameworkThread(Func func); + + /// + /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. + /// + /// Function to call. + /// Task representing the pending or already completed function. + public Task RunOnFrameworkThread(Action action); + + /// + /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. + /// + /// Return type. + /// Function to call. + /// Task representing the pending or already completed function. + public Task RunOnFrameworkThread(Func> func); + + /// + /// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call. + /// + /// Function to call. + /// Task representing the pending or already completed function. + public Task RunOnFrameworkThread(Func func); + + /// + /// Run given function in upcoming Framework.Tick call. + /// + /// Return type. + /// Function to call. + /// Wait for given timespan before calling this function. + /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. + /// Cancellation token which will prevent the execution of this function if wait conditions are not met. + /// Task representing the pending function. + public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default); + + /// + /// Run given function in upcoming Framework.Tick call. + /// + /// Function to call. + /// Wait for given timespan before calling this function. + /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. + /// Cancellation token which will prevent the execution of this function if wait conditions are not met. + /// Task representing the pending function. + public Task RunOnTick(Action action, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default); + + /// + /// Run given function in upcoming Framework.Tick call. + /// + /// Return type. + /// Function to call. + /// Wait for given timespan before calling this function. + /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. + /// Cancellation token which will prevent the execution of this function if wait conditions are not met. + /// Task representing the pending function. + public Task RunOnTick(Func> func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default); + + /// + /// Run given function in upcoming Framework.Tick call. + /// + /// Function to call. + /// Wait for given timespan before calling this function. + /// Count given number of Framework.Tick calls before calling this function. This takes precedence over delay parameter. + /// Cancellation token which will prevent the execution of this function if wait conditions are not met. + /// Task representing the pending function. + public Task RunOnTick(Func func, TimeSpan delay = default, int delayTicks = default, CancellationToken cancellationToken = default); +} diff --git a/Dalamud/Plugin/Services/IGameConfig.cs b/Dalamud/Plugin/Services/IGameConfig.cs index 98f6160cc..8e9b48d83 100644 --- a/Dalamud/Plugin/Services/IGameConfig.cs +++ b/Dalamud/Plugin/Services/IGameConfig.cs @@ -12,10 +12,25 @@ namespace Dalamud.Plugin.Services; public interface IGameConfig { /// - /// Event which is fired when a game config option is changed. + /// Event which is fired when any game config option is changed. /// public event EventHandler Changed; + /// + /// Event which is fired when a system config option is changed. + /// + public event EventHandler SystemChanged; + + /// + /// Event which is fired when a UiConfig option is changed. + /// + public event EventHandler UiConfigChanged; + + /// + /// Event which is fired when a UiControl config option is changed. + /// + public event EventHandler UiControlChanged; + /// /// Gets the collection of config options that persist between characters. /// @@ -83,7 +98,7 @@ public interface IGameConfig /// Attempts to get the properties of a String option from the System section. /// /// Option to get the properties of. - /// Details of the option: Default Value + /// Details of the option: Default Value. /// A value representing the success. public bool TryGet(SystemConfigOption option, out StringConfigProperties? properties); @@ -139,7 +154,7 @@ public interface IGameConfig /// Attempts to get the properties of a String option from the UiConfig section. /// /// Option to get the properties of. - /// Details of the option: Default Value + /// Details of the option: Default Value. /// A value representing the success. public bool TryGet(UiConfigOption option, out StringConfigProperties? properties); @@ -195,7 +210,7 @@ public interface IGameConfig /// Attempts to get the properties of a String option from the UiControl section. /// /// Option to get the properties of. - /// Details of the option: Default Value + /// Details of the option: Default Value. /// A value representing the success. public bool TryGet(UiControlOption option, out StringConfigProperties? properties); diff --git a/Dalamud/Plugin/Services/IGameInteropProvider.cs b/Dalamud/Plugin/Services/IGameInteropProvider.cs new file mode 100644 index 000000000..217e08445 --- /dev/null +++ b/Dalamud/Plugin/Services/IGameInteropProvider.cs @@ -0,0 +1,99 @@ +using System.Diagnostics; + +using Dalamud.Hooking; +using Dalamud.Utility.Signatures; + +namespace Dalamud.Plugin.Services; + +/// +/// Service responsible for the creation of hooks. +/// +public interface IGameInteropProvider +{ + /// + /// Available hooking backends. + /// + public enum HookBackend + { + /// + /// Choose the best backend automatically. + /// + Automatic, + + /// + /// Use Reloaded hooks. + /// + Reloaded, + + /// + /// Use MinHook. + /// You should never have to use this without talking to us first. + /// + MinHook, + } + + /// + /// Initialize members decorated with the . + /// Initialize any delegate members decorated with the . + /// Fill out any IntPtr members decorated with the with the resolved address. + /// Errors for fallible signatures will be logged. + /// + /// The object to initialize. + public void InitializeFromAttributes(object self); + + /// + /// Creates a hook by replacing the original address with an address pointing to a newly created jump to the detour. + /// + /// A memory address to install a hook. + /// Callback function. Delegate must have a same original function prototype. + /// The hook with the supplied parameters. + /// Delegate of detour. + public Hook HookFromFunctionPointerVariable(IntPtr address, T detour) where T : Delegate; + + /// + /// Creates a hook by rewriting import table address. + /// + /// Module to check for. Current process' main module if null. + /// Name of the DLL, including the extension. + /// Decorated name of the function. + /// Hint or ordinal. 0 to unspecify. + /// Callback function. Delegate must have a same original function prototype. + /// The hook with the supplied parameters. + /// Delegate of detour. + public Hook HookFromImport(ProcessModule? module, string moduleName, string functionName, uint hintOrOrdinal, T detour) where T : Delegate; + + /// + /// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function. + /// The hook is not activated until Enable() method is called. + /// Please do not use MinHook unless you have thoroughly troubleshot why Reloaded does not work. + /// + /// A name of the module currently loaded in the memory. (e.g. ws2_32.dll). + /// A name of the exported function name (e.g. send). + /// Callback function. Delegate must have a same original function prototype. + /// Hooking library to use. + /// The hook with the supplied parameters. + /// Delegate of detour. + Hook HookFromSymbol(string moduleName, string exportName, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate; + + /// + /// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function. + /// The hook is not activated until Enable() method is called. + /// Please do not use MinHook unless you have thoroughly troubleshot why Reloaded does not work. + /// + /// A memory address to install a hook. + /// Callback function. Delegate must have a same original function prototype. + /// Hooking library to use. + /// The hook with the supplied parameters. + /// Delegate of detour. + Hook HookFromAddress(IntPtr procAddress, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate; + + /// + /// Creates a hook from a signature into the Dalamud target module. + /// + /// Signature of function to hook. + /// Callback function. Delegate must have a same original function prototype. + /// Hooking library to use. + /// The hook with the supplied parameters. + /// Delegate of detour. + Hook HookFromSignature(string signature, T detour, HookBackend backend = HookBackend.Automatic) where T : Delegate; +} diff --git a/Dalamud/Plugin/Services/IGameNetwork.cs b/Dalamud/Plugin/Services/IGameNetwork.cs new file mode 100644 index 000000000..eed79b4af --- /dev/null +++ b/Dalamud/Plugin/Services/IGameNetwork.cs @@ -0,0 +1,26 @@ +using Dalamud.Game.Network; + +namespace Dalamud.Plugin.Services; + +/// +/// This class handles interacting with game network events. +/// +public interface IGameNetwork +{ + // TODO(v9): we shouldn't be passing pointers to the actual data here + + /// + /// The delegate type of a network message event. + /// + /// The pointer to the raw data. + /// The operation ID code. + /// The source actor ID. + /// The taret actor ID. + /// The direction of the packed. + public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction); + + /// + /// Event that is called when a network message is sent/received. + /// + public event OnNetworkMessageDelegate NetworkMessage; +} diff --git a/Dalamud/Plugin/Services/IKeyState.cs b/Dalamud/Plugin/Services/IKeyState.cs new file mode 100644 index 000000000..de78978ca --- /dev/null +++ b/Dalamud/Plugin/Services/IKeyState.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +using Dalamud.Game.ClientState.Keys; + +namespace Dalamud.Plugin.Services; + +/// +/// Wrapper around the game keystate buffer, which contains the pressed state for all keyboard keys, indexed by virtual vkCode. +/// +/// +/// The stored key state is actually a combination field, however the below ephemeral states are consumed each frame. Setting +/// the value may be mildly useful, however retrieving the value is largely pointless. In testing, it wasn't possible without +/// setting the statue manually. +/// index & 0 = key pressed. +/// index & 1 = key down (ephemeral). +/// index & 2 = key up (ephemeral). +/// index & 3 = short key press (ephemeral). +/// +public interface IKeyState +{ + /// + /// Get or set the key-pressed state for a given vkCode. + /// + /// The virtual key to change. + /// Whether the specified key is currently pressed. + /// If the vkCode is not valid. Refer to or . + /// If the set value is non-zero. + public bool this[int vkCode] { get; set; } + + /// + public bool this[VirtualKey vkCode] { get; set; } + + /// + /// Gets the value in the index array. + /// + /// The virtual key to change. + /// The raw value stored in the index array. + /// If the vkCode is not valid. Refer to or . + public int GetRawValue(int vkCode); + + /// + public int GetRawValue(VirtualKey vkCode); + + /// + /// Sets the value in the index array. + /// + /// The virtual key to change. + /// The raw value to set in the index array. + /// If the vkCode is not valid. Refer to or . + /// If the set value is non-zero. + public void SetRawValue(int vkCode, int value); + + /// + public void SetRawValue(VirtualKey vkCode, int value); + + /// + /// Gets a value indicating whether the given VirtualKey code is regarded as valid input by the game. + /// + /// Virtual key code. + /// If the code is valid. + public bool IsVirtualKeyValid(int vkCode); + + /// + public bool IsVirtualKeyValid(VirtualKey vkCode); + + /// + /// Gets an array of virtual keys the game considers valid input. + /// + /// An array of valid virtual keys. + public IEnumerable GetValidVirtualKeys(); + + /// + /// Clears the pressed state for all keys. + /// + public void ClearAll(); +} diff --git a/Dalamud/Plugin/Services/IPartyFinderGui.cs b/Dalamud/Plugin/Services/IPartyFinderGui.cs new file mode 100644 index 000000000..f656963db --- /dev/null +++ b/Dalamud/Plugin/Services/IPartyFinderGui.cs @@ -0,0 +1,23 @@ +using Dalamud.Game.Gui.PartyFinder.Types; + +namespace Dalamud.Plugin.Services; + +/// +/// This class handles interacting with the native PartyFinder window. +/// +public interface IPartyFinderGui +{ + /// + /// Event type fired each time the game receives an individual Party Finder listing. + /// Cannot modify listings but can hide them. + /// + /// The listings received. + /// Additional arguments passed by the game. + public delegate void PartyFinderListingEventDelegate(PartyFinderListing listing, PartyFinderListingEventArgs args); + + /// + /// Event fired each time the game receives an individual Party Finder listing. + /// Cannot modify listings but can hide them. + /// + public event PartyFinderListingEventDelegate ReceiveListing; +} diff --git a/Dalamud/Plugin/Services/IPluginLog.cs b/Dalamud/Plugin/Services/IPluginLog.cs index 87876f36f..aac321092 100644 --- a/Dalamud/Plugin/Services/IPluginLog.cs +++ b/Dalamud/Plugin/Services/IPluginLog.cs @@ -1,8 +1,8 @@ -using System; - -using Serilog; +using Serilog; using Serilog.Events; +#pragma warning disable CS1573 // See https://github.com/dotnet/roslyn/issues/40325 + namespace Dalamud.Plugin.Services; /// @@ -20,15 +20,6 @@ public interface IPluginLog /// LogEventLevel MinimumLogLevel { get; set; } - /// - /// Gets an instance of the Serilog for advanced use cases. The provided logger will handle - /// tagging all log messages with the appropriate context variables and properties. - /// - /// - /// Not currently part of public API - will be added after some formatter work has been completed. - /// - internal ILogger Logger { get; } - /// /// Log a message to the Dalamud log for this plugin. This log level should be /// used primarily for unrecoverable errors or critical faults in a plugin. @@ -76,6 +67,12 @@ public interface IPluginLog /// /// An (optional) exception that should be recorded alongside this event. void Information(Exception? exception, string messageTemplate, params object[] values); + + /// + void Info(string messageTemplate, params object[] values); + + /// + void Info(Exception? exception, string messageTemplate, params object[] values); /// /// Log a message to the Dalamud log for this plugin. This log level should be diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index 091b2ed67..f91d4ee8e 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -44,7 +44,7 @@ public interface ITextureProvider /// If null, default to the game's current language. /// /// - /// Prevent Dalamud from automatically unloading this icon to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// /// /// Null, if the icon does not exist in the specified configuration, or a texture wrap that can be used @@ -72,7 +72,7 @@ public interface ITextureProvider /// You may only specify paths in the game's VFS. /// /// The path to the texture in the game's VFS. - /// Prevent Dalamud from automatically unloading this texture to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// Null, if the icon does not exist, or a texture wrap that can be used to render the texture. public IDalamudTextureWrap? GetTextureFromGame(string path, bool keepAlive = false); @@ -83,7 +83,7 @@ public interface ITextureProvider /// This API can load .png and .tex files. /// /// The FileInfo describing the image or texture file. - /// Prevent Dalamud from automatically unloading this texture to save memory. Usually does not need to be set. + /// Not used. This parameter is ignored. /// Null, if the file does not exist, or a texture wrap that can be used to render the texture. public IDalamudTextureWrap? GetTextureFromFile(FileInfo file, bool keepAlive = false); diff --git a/Dalamud/Plugin/Services/ITitleScreenMenu.cs b/Dalamud/Plugin/Services/ITitleScreenMenu.cs new file mode 100644 index 000000000..b4af06e71 --- /dev/null +++ b/Dalamud/Plugin/Services/ITitleScreenMenu.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +using Dalamud.Interface; +using Dalamud.Interface.Internal; +using ImGuiScene; + +namespace Dalamud.Plugin.Services; + +/// +/// Interface for class responsible for managing elements in the title screen menu. +/// +public interface ITitleScreenMenu +{ + /// + /// Gets the list of entries in the title screen menu. + /// + public IReadOnlyList Entries { get; } + + /// + /// Adds a new entry to the title screen menu. + /// + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered); + + /// + /// Adds a new entry to the title screen menu. + /// + /// Priority of the entry. + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered); + + /// + /// Remove an entry from the title screen menu. + /// + /// The entry to remove. + public void RemoveEntry(TitleScreenMenuEntry entry); +} diff --git a/Dalamud/Plugin/Services/IToastGui.cs b/Dalamud/Plugin/Services/IToastGui.cs new file mode 100644 index 000000000..ef83e95ac --- /dev/null +++ b/Dalamud/Plugin/Services/IToastGui.cs @@ -0,0 +1,88 @@ +using Dalamud.Game.Gui.Toast; +using Dalamud.Game.Text.SeStringHandling; + +namespace Dalamud.Plugin.Services; + +/// +/// This class facilitates interacting with and creating native toast windows. +/// +public interface IToastGui +{ + /// + /// A delegate type used when a normal toast window appears. + /// + /// The message displayed. + /// Assorted toast options. + /// Whether the toast has been handled or should be propagated. + public delegate void OnNormalToastDelegate(ref SeString message, ref ToastOptions options, ref bool isHandled); + + /// + /// A delegate type used when a quest toast window appears. + /// + /// The message displayed. + /// Assorted toast options. + /// Whether the toast has been handled or should be propagated. + public delegate void OnQuestToastDelegate(ref SeString message, ref QuestToastOptions options, ref bool isHandled); + + /// + /// A delegate type used when an error toast window appears. + /// + /// The message displayed. + /// Whether the toast has been handled or should be propagated. + public delegate void OnErrorToastDelegate(ref SeString message, ref bool isHandled); + + /// + /// Event that will be fired when a toast is sent by the game or a plugin. + /// + public event OnNormalToastDelegate Toast; + + /// + /// Event that will be fired when a quest toast is sent by the game or a plugin. + /// + public event OnQuestToastDelegate QuestToast; + + /// + /// Event that will be fired when an error toast is sent by the game or a plugin. + /// + public event OnErrorToastDelegate ErrorToast; + + /// + /// Show a toast message with the given content. + /// + /// The message to be shown. + /// Options for the toast. + public void ShowNormal(string message, ToastOptions? options = null); + + /// + /// Show a toast message with the given content. + /// + /// The message to be shown. + /// Options for the toast. + public void ShowNormal(SeString message, ToastOptions? options = null); + + /// + /// Show a quest toast message with the given content. + /// + /// The message to be shown. + /// Options for the toast. + public void ShowQuest(string message, QuestToastOptions? options = null); + + /// + /// Show a quest toast message with the given content. + /// + /// The message to be shown. + /// Options for the toast. + public void ShowQuest(SeString message, QuestToastOptions? options = null); + + /// + /// Show an error toast message with the given content. + /// + /// The message to be shown. + public void ShowError(string message); + + /// + /// Show an error toast message with the given content. + /// + /// The message to be shown. + public void ShowError(SeString message); +} diff --git a/Dalamud/ServiceManager.cs b/Dalamud/ServiceManager.cs index d1c1002bd..453fa3530 100644 --- a/Dalamud/ServiceManager.cs +++ b/Dalamud/ServiceManager.cs @@ -1,7 +1,5 @@ -using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; using System.Threading; @@ -11,13 +9,14 @@ using Dalamud.Configuration.Internal; using Dalamud.Game; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; +using Dalamud.Storage; using Dalamud.Utility.Timing; using JetBrains.Annotations; namespace Dalamud; // TODO: -// - Unify dependency walking code(load/unload +// - Unify dependency walking code(load/unload) // - Visualize/output .dot or imgui thing /// @@ -48,9 +47,9 @@ internal static class ServiceManager None = 0, /// - /// Regular service. + /// Service that is loaded manually. /// - ManualService = 1 << 0, + ProvidedService = 1 << 0, /// /// Service that is loaded asynchronously while the game starts. @@ -82,39 +81,25 @@ internal static class ServiceManager /// Initializes Provided Services and FFXIVClientStructs. /// /// Instance of . - /// Instance of . + /// Instance of . /// Instance of . - public static void InitializeProvidedServicesAndClientStructs(Dalamud dalamud, DalamudStartInfo startInfo, DalamudConfiguration configuration) + /// Instance of . + public static void InitializeProvidedServices(Dalamud dalamud, ReliableFileStorage fs, DalamudConfiguration configuration, TargetSigScanner scanner) { - // Initialize the process information. - var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs")); - if (!cacheDir.Exists) - cacheDir.Create(); - lock (LoadedServices) { - Service.Provide(dalamud); - LoadedServices.Add(typeof(Dalamud)); - - Service.Provide(startInfo); - LoadedServices.Add(typeof(DalamudStartInfo)); - - Service.Provide(configuration); - LoadedServices.Add(typeof(DalamudConfiguration)); - - Service.Provide(new ServiceContainer()); - LoadedServices.Add(typeof(ServiceContainer)); - - Service.Provide( - new SigScanner( - true, new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json")))); - LoadedServices.Add(typeof(SigScanner)); - } - - using (Timings.Start("CS Resolver Init")) - { - FFXIVClientStructs.Interop.Resolver.GetInstance.SetupSearchSpace(Service.Get().SearchBase, new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json"))); - FFXIVClientStructs.Interop.Resolver.GetInstance.Resolve(); + void ProvideService(T service) where T : IServiceType + { + Debug.Assert(typeof(T).GetServiceKind().HasFlag(ServiceKind.ProvidedService), "Provided service must have Service attribute"); + Service.Provide(service); + LoadedServices.Add(typeof(T)); + } + + ProvideService(dalamud); + ProvideService(fs); + ProvideService(configuration); + ProvideService(new ServiceContainer()); + ProvideService(scanner); } } @@ -134,22 +119,17 @@ internal static class ServiceManager var serviceContainer = Service.Get(); - foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes()) + foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsAssignableTo(typeof(IServiceType)) && !x.IsInterface && !x.IsAbstract)) { var serviceKind = serviceType.GetServiceKind(); - if (serviceKind is ServiceKind.None) - continue; + Debug.Assert(serviceKind != ServiceKind.None, $"Service<{serviceType.FullName}> did not specify a kind"); - // Scoped service do not go through Service, so we must let ServiceContainer know what their interfaces map to - if (serviceKind is ServiceKind.ScopedService) - { - serviceContainer.RegisterInterfaces(serviceType); - continue; - } + // Let IoC know about the interfaces this service implements + serviceContainer.RegisterInterfaces(serviceType); - Debug.Assert( - !serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService), - "Regular and scoped services should never be loaded early"); + // Scoped service do not go through Service and are never early loaded + if (serviceKind.HasFlag(ServiceKind.ScopedService)) + continue; var genericWrappedServiceType = typeof(Service<>).MakeGenericType(serviceType); @@ -161,9 +141,19 @@ internal static class ServiceManager null, null); + getAsyncTaskMap[serviceType] = getTask; + + // We don't actually need to load provided services, something else does + if (serviceKind.HasFlag(ServiceKind.ProvidedService)) + continue; + + Debug.Assert( + serviceKind.HasFlag(ServiceKind.EarlyLoadedService) || + serviceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService), + "At this point, service must be either early loaded or blocking early loaded"); + if (serviceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService)) { - getAsyncTaskMap[serviceType] = getTask; blockingEarlyLoadingServices.Add(serviceType); } else @@ -171,15 +161,10 @@ internal static class ServiceManager earlyLoadingServices.Add(serviceType); } - dependencyServicesMap[serviceType] = - (List)typeof(Service<>) - .MakeGenericType(serviceType) - .InvokeMember( - "GetDependencyServices", - BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, - null, - null, - null); + var typeAsServiceT = ServiceHelpers.GetAsService(serviceType); + dependencyServicesMap[serviceType] = ServiceHelpers.GetDependencies(typeAsServiceT) + .Select(x => typeof(Service<>).MakeGenericType(x)) + .ToList(); } _ = Task.Run(async () => @@ -210,13 +195,13 @@ internal static class ServiceManager var hasDeps = true; foreach (var dependency in dependencyServicesMap[serviceType]) { - var depServiceKind = dependency.GetServiceKind(); - var depResolveTask = getAsyncTaskMap.GetValueOrDefault(dependency); + var depUnderlyingServiceType = dependency.GetGenericArguments().First(); + var depResolveTask = getAsyncTaskMap.GetValueOrDefault(depUnderlyingServiceType); - if (depResolveTask == null && (depServiceKind.HasFlag(ServiceKind.EarlyLoadedService) || depServiceKind.HasFlag(ServiceKind.BlockingEarlyLoadedService))) + if (depResolveTask == null) { - Log.Error("{Type}: {Dependency} has no resolver task, is it early loaded or blocking early loaded?", serviceType.FullName!, dependency.FullName!); - Debug.Assert(false, $"No resolver for dependent service {dependency.FullName}"); + Log.Error("{Type}: {Dependency} has no resolver task", serviceType.FullName!, dependency.FullName!); + Debug.Assert(false, $"No resolver for dependent service {depUnderlyingServiceType.FullName}"); } else if (depResolveTask is { IsCompleted: false }) { @@ -322,16 +307,8 @@ internal static class ServiceManager Log.Verbose("Calling GetDependencyServices for '{ServiceName}'", serviceType.FullName!); - dependencyServicesMap[serviceType] = - ((List)typeof(Service<>) - .MakeGenericType(serviceType) - .InvokeMember( - "GetDependencyServices", - BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, - null, - null, - null))! - .Select(x => x.GetGenericArguments()[0]).ToList(); + var typeAsServiceT = ServiceHelpers.GetAsService(serviceType); + dependencyServicesMap[serviceType] = ServiceHelpers.GetDependencies(typeAsServiceT); allToUnload.Add(serviceType); } @@ -413,7 +390,7 @@ internal static class ServiceManager if (attr.IsAssignableTo(typeof(ScopedService))) return ServiceKind.ScopedService; - return ServiceKind.ManualService; + return ServiceKind.ProvidedService; } /// diff --git a/Dalamud/Service{T}.cs b/Dalamud/Service{T}.cs index aa10ead6e..b609c9082 100644 --- a/Dalamud/Service{T}.cs +++ b/Dalamud/Service{T}.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -122,6 +123,8 @@ internal static class Service where T : IServiceType public static List GetDependencyServices() { var res = new List(); + + ServiceManager.Log.Verbose("Service<{0}>: Getting dependencies", typeof(T).Name); var ctor = GetServiceConstructor(); if (ctor != null) @@ -133,8 +136,8 @@ internal static class Service where T : IServiceType res.AddRange(typeof(T) .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Select(x => x.FieldType) - .Where(x => x.GetCustomAttribute(true) != null)); + .Where(x => x.GetCustomAttribute(true) != null) + .Select(x => x.FieldType)); res.AddRange(typeof(T) .GetCustomAttributes() @@ -148,23 +151,46 @@ internal static class Service where T : IServiceType { if (!serviceType.IsAssignableTo(typeof(IServiceType))) continue; - - var attr = serviceType.GetCustomAttribute(true); - if (attr == null) + + if (serviceType == typeof(PluginManager)) continue; - + // Scoped plugin services lifetime is tied to their scopes. They go away when LocalPlugin goes away. + // Nonetheless, their direct dependencies must be considered. if (serviceType.GetServiceKind() == ServiceManager.ServiceKind.ScopedService) + { + var typeAsServiceT = ServiceHelpers.GetAsService(serviceType); + var dependencies = ServiceHelpers.GetDependencies(typeAsServiceT); + ServiceManager.Log.Verbose("Found dependencies of scoped plugin service {Type} ({Cnt})", serviceType.FullName!, dependencies!.Count); + + foreach (var scopedDep in dependencies) + { + if (scopedDep == typeof(PluginManager)) + throw new Exception("Scoped plugin services cannot depend on PluginManager."); + + ServiceManager.Log.Verbose("PluginManager MUST depend on {Type} via {BaseType}", scopedDep.FullName!, serviceType.FullName!); + res.Add(scopedDep); + } + + continue; + } + + var pluginInterfaceAttribute = serviceType.GetCustomAttribute(true); + if (pluginInterfaceAttribute == null) continue; ServiceManager.Log.Verbose("PluginManager MUST depend on {Type}", serviceType.FullName!); res.Add(serviceType); } } + + foreach (var type in res) + { + ServiceManager.Log.Verbose("Service<{0}>: => Dependency: {1}", typeof(T).Name, type.Name); + } return res .Distinct() - .Select(x => typeof(Service<>).MakeGenericType(x)) .ToList(); } @@ -295,3 +321,37 @@ internal static class Service where T : IServiceType } } } + +/// +/// Helper functions for services. +/// +internal static class ServiceHelpers +{ + /// + /// Get a list of dependencies for a service. Only accepts Service<T> types. + /// These are returned as Service<T> types. + /// + /// The dependencies for this service. + /// A list of dependencies. + public static List GetDependencies(Type serviceType) + { + return (List)serviceType.InvokeMember( + "GetDependencyServices", + BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, + null, + null, + null) ?? new List(); + } + + /// + /// Get the Service<T> type for a given service type. + /// This will throw if the service type is not a valid service. + /// + /// The type to obtain a Service<T> for. + /// The Service<T>. + public static Type GetAsService(Type type) + { + return typeof(Service<>) + .MakeGenericType(type); + } +} diff --git a/Dalamud/Storage/FileReadException.cs b/Dalamud/Storage/FileReadException.cs new file mode 100644 index 000000000..09f7ff4fb --- /dev/null +++ b/Dalamud/Storage/FileReadException.cs @@ -0,0 +1,16 @@ +namespace Dalamud.Storage; + +/// +/// Thrown if all read operations fail. +/// +public class FileReadException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + /// Inner error that caused this exception. + internal FileReadException(Exception inner) + : base("Failed to read file", inner) + { + } +} diff --git a/Dalamud/Storage/ReliableFileStorage.cs b/Dalamud/Storage/ReliableFileStorage.cs new file mode 100644 index 000000000..6bc6cabcc --- /dev/null +++ b/Dalamud/Storage/ReliableFileStorage.cs @@ -0,0 +1,325 @@ +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +using Dalamud.Logging.Internal; +using Dalamud.Utility; +using PInvoke; +using SQLite; + +namespace Dalamud.Storage; + +/* + * TODO: A file that is read frequently, but written very rarely, might not have offline changes by users persisted + * into the backup database, since it is only written to the backup database when it is written to the filesystem. + */ + +/// +/// A service that provides a reliable file storage. +/// Implements a VFS that writes files to the disk, and additionally keeps files in a SQLite database +/// for journaling/backup purposes. +/// Consumers can choose to receive a backup if they think that the file is corrupt. +/// +/// +/// This is not an early-loaded service, as it is needed before they are initialized. +/// +[ServiceManager.Service] +public class ReliableFileStorage : IServiceType, IDisposable +{ + private static readonly ModuleLog Log = new("VFS"); + + private SQLiteConnection? db; + + /// + /// Initializes a new instance of the class. + /// + /// Path to the VFS. + public ReliableFileStorage(string vfsDbPath) + { + var databasePath = Path.Combine(vfsDbPath, "dalamudVfs.db"); + + Log.Verbose("Initializing VFS database at {Path}", databasePath); + + try + { + this.SetupDb(databasePath); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to load VFS database, starting fresh"); + + try + { + if (File.Exists(databasePath)) + File.Delete(databasePath); + + this.SetupDb(databasePath); + } + catch (Exception) + { + // ignored, we can run without one + } + } + } + + /// + /// Check if a file exists. + /// This will return true if the file does not exist on the filesystem, but in the transparent backup. + /// You must then use this instance to read the file to ensure consistency. + /// + /// The path to check. + /// The container to check in. + /// True if the file exists. + public bool Exists(string path, Guid containerId = default) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + if (File.Exists(path)) + return true; + + if (this.db == null) + return false; + + // If the file doesn't actually exist on the FS, but it does in the DB, we can say YES and read operations will read from the DB instead + var normalizedPath = NormalizePath(path); + var file = this.db.Table().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId); + return file != null; + } + + /// + /// Write all text to a file. + /// + /// Path to write to. + /// The contents of the file. + /// Container to write to. + public void WriteAllText(string path, string? contents, Guid containerId = default) + => this.WriteAllText(path, contents, Encoding.UTF8, containerId); + + /// + /// Write all text to a file. + /// + /// Path to write to. + /// The contents of the file. + /// The encoding to write with. + /// Container to write to. + public void WriteAllText(string path, string? contents, Encoding encoding, Guid containerId = default) + { + var bytes = encoding.GetBytes(contents ?? string.Empty); + this.WriteAllBytes(path, bytes, containerId); + } + + /// + /// Write all bytes to a file. + /// + /// Path to write to. + /// The contents of the file. + /// Container to write to. + public void WriteAllBytes(string path, byte[] bytes, Guid containerId = default) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + if (this.db == null) + { + Util.WriteAllBytesSafe(path, bytes); + return; + } + + this.db.RunInTransaction(() => + { + var normalizedPath = NormalizePath(path); + var file = this.db.Table().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId); + if (file == null) + { + file = new DbFile + { + ContainerId = containerId, + Path = normalizedPath, + Data = bytes, + }; + this.db.Insert(file); + } + else + { + file.Data = bytes; + this.db.Update(file); + } + + Util.WriteAllBytesSafe(path, bytes); + }); + } + + /// + /// Read all text from a file. + /// If the file does not exist on the filesystem, a read is attempted from the backup. The backup is not + /// automatically written back to disk, however. + /// + /// The path to read from. + /// Whether or not the backup of the file should take priority. + /// The container to read from. + /// All text stored in this file. + /// Thrown if the file does not exist on the filesystem or in the backup. + public string ReadAllText(string path, bool forceBackup = false, Guid containerId = default) + => this.ReadAllText(path, Encoding.UTF8, forceBackup, containerId); + + /// + /// Read all text from a file. + /// If the file does not exist on the filesystem, a read is attempted from the backup. The backup is not + /// automatically written back to disk, however. + /// + /// The path to read from. + /// The encoding to read with. + /// Whether or not the backup of the file should take priority. + /// The container to read from. + /// All text stored in this file. + /// Thrown if the file does not exist on the filesystem or in the backup. + public string ReadAllText(string path, Encoding encoding, bool forceBackup = false, Guid containerId = default) + { + var bytes = this.ReadAllBytes(path, forceBackup, containerId); + return encoding.GetString(bytes); + } + + /// + /// Read all text from a file, and automatically try again with the backup if the file does not exist or + /// the function throws an exception. If the backup read also throws an exception, + /// or the file does not exist in the backup, a is thrown. + /// + /// The path to read from. + /// Lambda that reads the file. Throw here to automatically attempt a read from the backup. + /// The container to read from. + /// Thrown if the file does not exist on the filesystem or in the backup. + /// Thrown here if the file and the backup fail their read. + public void ReadAllText(string path, Action reader, Guid containerId = default) + => this.ReadAllText(path, Encoding.UTF8, reader, containerId); + + /// + /// Read all text from a file, and automatically try again with the backup if the file does not exist or + /// the function throws an exception. If the backup read also throws an exception, + /// or the file does not exist in the backup, a is thrown. + /// + /// The path to read from. + /// The encoding to read with. + /// Lambda that reads the file. Throw here to automatically attempt a read from the backup. + /// The container to read from. + /// Thrown if the file does not exist on the filesystem or in the backup. + /// Thrown here if the file and the backup fail their read. + public void ReadAllText(string path, Encoding encoding, Action reader, Guid containerId = default) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + // TODO: We are technically reading one time too many here, if the file does not exist on the FS, ReadAllText + // fails over to the backup, and then the backup fails to read in the lambda. We should do something about that, + // but it's not a big deal. Would be nice if ReadAllText could indicate if it did fail over. + + // 1.) Try without using the backup + try + { + var text = this.ReadAllText(path, encoding, false, containerId); + reader(text); + return; + } + catch (FileNotFoundException) + { + // We can't do anything about this. + throw; + } + catch (Exception ex) + { + Log.Verbose(ex, "First chance read from {Path} failed, trying backup", path); + } + + // 2.) Try using the backup + try + { + var text = this.ReadAllText(path, encoding, true, containerId); + reader(text); + } + catch (Exception ex) + { + Log.Error(ex, "Second chance read from {Path} failed, giving up", path); + throw new FileReadException(ex); + } + } + + /// + /// Read all bytes from a file. + /// If the file does not exist on the filesystem, a read is attempted from the backup. The backup is not + /// automatically written back to disk, however. + /// + /// The path to read from. + /// Whether or not the backup of the file should take priority. + /// The container to read from. + /// All bytes stored in this file. + /// Thrown if the file does not exist on the filesystem or in the backup. + public byte[] ReadAllBytes(string path, bool forceBackup = false, Guid containerId = default) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + if (forceBackup) + { + // If the db failed to load, act as if the file does not exist + if (this.db == null) + throw new FileNotFoundException("Backup database was not available"); + + var normalizedPath = NormalizePath(path); + var file = this.db.Table().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId); + if (file == null) + throw new FileNotFoundException(); + + return file.Data; + } + + // If the file doesn't exist, immediately check the backup db + if (!File.Exists(path)) + return this.ReadAllBytes(path, true, containerId); + + try + { + return File.ReadAllBytes(path); + } + catch (Exception e) + { + Log.Error(e, "Failed to read file from disk, falling back to database"); + return this.ReadAllBytes(path, true, containerId); + } + } + + /// + public void Dispose() + { + this.db?.Dispose(); + } + + /// + /// Replace possible non-portable parts of a path with portable versions. + /// + /// The path to normalize. + /// The normalized path. + private static string NormalizePath(string path) + { + // Replace users folder + var usersFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + path = path.Replace(usersFolder, "%USERPROFILE%"); + + return path; + } + + private void SetupDb(string path) + { + this.db = new SQLiteConnection(path, + SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex); + this.db.CreateTable(); + } + + private class DbFile + { + [PrimaryKey] + [AutoIncrement] + public int Id { get; set; } + + public Guid ContainerId { get; set; } + + public string Path { get; set; } = null!; + + public byte[] Data { get; set; } = null!; + } +} diff --git a/Dalamud/Support/Troubleshooting.cs b/Dalamud/Support/Troubleshooting.cs index 9893451f4..59ebcaa16 100644 --- a/Dalamud/Support/Troubleshooting.cs +++ b/Dalamud/Support/Troubleshooting.cs @@ -58,7 +58,7 @@ public static class Troubleshooting /// internal static void LogTroubleshooting() { - var startInfo = Service.Get(); + var startInfo = Service.Get().StartInfo; var configuration = Service.Get(); var interfaceManager = Service.GetNullable(); var pluginManager = Service.GetNullable(); @@ -72,7 +72,7 @@ public static class Troubleshooting EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(), DalamudVersion = Util.AssemblyVersion, DalamudGitHash = Util.GetGitHash(), - GameVersion = startInfo.GameVersion.ToString(), + GameVersion = startInfo.GameVersion?.ToString() ?? "Unknown", Language = startInfo.Language.ToString(), BetaKey = configuration.DalamudBetaKey, DoPluginTest = configuration.DoPluginTest, diff --git a/Dalamud.Interface/ArrayExtensions.cs b/Dalamud/Utility/ArrayExtensions.cs similarity index 80% rename from Dalamud.Interface/ArrayExtensions.cs rename to Dalamud/Utility/ArrayExtensions.cs index 68bf52a29..afb1511e3 100644 --- a/Dalamud.Interface/ArrayExtensions.cs +++ b/Dalamud/Utility/ArrayExtensions.cs @@ -1,7 +1,13 @@ +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; -namespace Dalamud.Interface; +namespace Dalamud.Utility; +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1618:Generic type parameters should be documented", Justification = "Reviewed,")] +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Reviewed,")] +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:Element return value should be documented", Justification = "Reviewed,")] +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:Element parameters should be documented", Justification = "Reviewed,")] internal static class ArrayExtensions { /// Iterate over enumerables with additional index. @@ -16,7 +22,6 @@ internal static class ArrayExtensions public static IEnumerable WithoutValue(this IEnumerable<(T Value, int Index)> list) => list.Select(x => x.Index); - // Find the index of the first object fulfilling predicate's criteria in the given list. // Returns -1 if no such object is found. public static int IndexOf(this IEnumerable array, Predicate predicate) diff --git a/Dalamud/Utility/EventHandlerExtensions.cs b/Dalamud/Utility/EventHandlerExtensions.cs index bce815a7b..d05ad6ea5 100644 --- a/Dalamud/Utility/EventHandlerExtensions.cs +++ b/Dalamud/Utility/EventHandlerExtensions.cs @@ -1,11 +1,9 @@ -using System; using System.Linq; using Dalamud.Game; +using Dalamud.Plugin.Services; using Serilog; -using static Dalamud.Game.Framework; - namespace Dalamud.Utility; /// @@ -20,7 +18,7 @@ internal static class EventHandlerExtensions /// The EventHandler in question. /// Default sender for Invoke equivalent. /// Default EventArgs for Invoke equivalent. - public static void InvokeSafely(this EventHandler eh, object sender, EventArgs e) + public static void InvokeSafely(this EventHandler? eh, object sender, EventArgs e) { if (eh == null) return; @@ -39,7 +37,7 @@ internal static class EventHandlerExtensions /// Default sender for Invoke equivalent. /// Default EventArgs for Invoke equivalent. /// Type of EventArgs. - public static void InvokeSafely(this EventHandler eh, object sender, T e) + public static void InvokeSafely(this EventHandler? eh, object sender, T e) { if (eh == null) return; @@ -55,7 +53,7 @@ internal static class EventHandlerExtensions /// of a thrown Exception inside of an invocation. /// /// The Action in question. - public static void InvokeSafely(this Action act) + public static void InvokeSafely(this Action? act) { if (act == null) return; @@ -66,18 +64,36 @@ internal static class EventHandlerExtensions } } + /// + /// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case + /// of a thrown Exception inside of an invocation. + /// + /// The Action in question. + /// Templated argument for Action. + /// Type of Action args. + public static void InvokeSafely(this Action? act, T argument) + { + if (act == null) + return; + + foreach (var action in act.GetInvocationList().Cast>()) + { + HandleInvoke(action, argument); + } + } + /// /// Replacement for Invoke() on OnUpdateDelegate to catch exceptions that stop event propagation in case /// of a thrown Exception inside of an invocation. /// /// The OnUpdateDelegate in question. /// Framework to be passed on to OnUpdateDelegate. - public static void InvokeSafely(this OnUpdateDelegate updateDelegate, Framework framework) + public static void InvokeSafely(this IFramework.OnUpdateDelegate? updateDelegate, Framework framework) { if (updateDelegate == null) return; - foreach (var action in updateDelegate.GetInvocationList().Cast()) + foreach (var action in updateDelegate.GetInvocationList().Cast()) { HandleInvoke(() => action(framework)); } @@ -94,4 +110,16 @@ internal static class EventHandlerExtensions Log.Error(ex, "Exception during raise of {handler}", act.Method); } } + + private static void HandleInvoke(Action act, T argument) + { + try + { + act(argument); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", act.Method); + } + } } diff --git a/Dalamud/Utility/FuzzyMatcher.cs b/Dalamud/Utility/FuzzyMatcher.cs index 647c9586d..9ac71d8bb 100644 --- a/Dalamud/Utility/FuzzyMatcher.cs +++ b/Dalamud/Utility/FuzzyMatcher.cs @@ -6,6 +6,9 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +#pragma warning disable SA1600 +#pragma warning disable SA1602 + internal readonly ref struct FuzzyMatcher { private static readonly (int, int)[] EmptySegArray = Array.Empty<(int, int)>(); @@ -13,31 +16,31 @@ internal readonly ref struct FuzzyMatcher private readonly string needleString = string.Empty; private readonly ReadOnlySpan needleSpan = ReadOnlySpan.Empty; private readonly int needleFinalPosition = -1; - private readonly (int start, int end)[] needleSegments = EmptySegArray; + private readonly (int Start, int End)[] needleSegments = EmptySegArray; private readonly MatchMode mode = MatchMode.Simple; public FuzzyMatcher(string term, MatchMode matchMode) { - needleString = term; - needleSpan = needleString.AsSpan(); - needleFinalPosition = needleSpan.Length - 1; - mode = matchMode; + this.needleString = term; + this.needleSpan = this.needleString.AsSpan(); + this.needleFinalPosition = this.needleSpan.Length - 1; + this.mode = matchMode; switch (matchMode) { case MatchMode.FuzzyParts: - needleSegments = FindNeedleSegments(needleSpan); + this.needleSegments = FindNeedleSegments(this.needleSpan); break; case MatchMode.Fuzzy: case MatchMode.Simple: - needleSegments = EmptySegArray; + this.needleSegments = EmptySegArray; break; default: throw new ArgumentOutOfRangeException(nameof(matchMode), matchMode, null); } } - private static (int start, int end)[] FindNeedleSegments(ReadOnlySpan span) + private static (int Start, int End)[] FindNeedleSegments(ReadOnlySpan span) { var segments = new List<(int, int)>(); var wordStart = -1; @@ -66,37 +69,39 @@ internal readonly ref struct FuzzyMatcher return segments.ToArray(); } +#pragma warning disable SA1202 public int Matches(string value) +#pragma warning restore SA1202 { - if (needleFinalPosition < 0) + if (this.needleFinalPosition < 0) { return 0; } - if (mode == MatchMode.Simple) + if (this.mode == MatchMode.Simple) { - return value.Contains(needleString) ? 1 : 0; + return value.Contains(this.needleString) ? 1 : 0; } var haystack = value.AsSpan(); - if (mode == MatchMode.Fuzzy) + if (this.mode == MatchMode.Fuzzy) { - return GetRawScore(haystack, 0, needleFinalPosition); + return this.GetRawScore(haystack, 0, this.needleFinalPosition); } - if (mode == MatchMode.FuzzyParts) + if (this.mode == MatchMode.FuzzyParts) { - if (needleSegments.Length < 2) + if (this.needleSegments.Length < 2) { - return GetRawScore(haystack, 0, needleFinalPosition); + return this.GetRawScore(haystack, 0, this.needleFinalPosition); } var total = 0; - for (var i = 0; i < needleSegments.Length; i++) + for (var i = 0; i < this.needleSegments.Length; i++) { - var (start, end) = needleSegments[i]; - var cur = GetRawScore(haystack, start, end); + var (start, end) = this.needleSegments[i]; + var cur = this.GetRawScore(haystack, start, end); if (cur == 0) { return 0; @@ -116,7 +121,7 @@ internal readonly ref struct FuzzyMatcher var max = 0; for (var i = 0; i < values.Length; i++) { - var cur = Matches(values[i]); + var cur = this.Matches(values[i]); if (cur > max) { max = cur; @@ -128,7 +133,7 @@ internal readonly ref struct FuzzyMatcher private int GetRawScore(ReadOnlySpan haystack, int needleStart, int needleEnd) { - var (startPos, gaps, consecutive, borderMatches, endPos) = FindForward(haystack, needleStart, needleEnd); + var (startPos, gaps, consecutive, borderMatches, endPos) = this.FindForward(haystack, needleStart, needleEnd); if (startPos < 0) { return 0; @@ -140,7 +145,7 @@ internal readonly ref struct FuzzyMatcher // PluginLog.Debug( // $"['{needleString.Substring(needleStart, needleEnd - needleStart + 1)}' in '{haystack}'] fwd: needleSize={needleSize} startPos={startPos} gaps={gaps} consecutive={consecutive} borderMatches={borderMatches} score={score}"); - (startPos, gaps, consecutive, borderMatches) = FindReverse(haystack, endPos, needleStart, needleEnd); + (startPos, gaps, consecutive, borderMatches) = this.FindReverse(haystack, endPos, needleStart, needleEnd); var revScore = CalculateRawScore(needleSize, startPos, gaps, consecutive, borderMatches); // PluginLog.Debug( // $"['{needleString.Substring(needleStart, needleEnd - needleStart + 1)}' in '{haystack}'] rev: needleSize={needleSize} startPos={startPos} gaps={gaps} consecutive={consecutive} borderMatches={borderMatches} score={revScore}"); @@ -149,7 +154,9 @@ internal readonly ref struct FuzzyMatcher } [MethodImpl(MethodImplOptions.AggressiveInlining)] +#pragma warning disable SA1204 private static int CalculateRawScore(int needleSize, int startPos, int gaps, int consecutive, int borderMatches) +#pragma warning restore SA1204 { var score = 100 + needleSize * 3 @@ -162,7 +169,7 @@ internal readonly ref struct FuzzyMatcher return score < 1 ? 1 : score; } - private (int startPos, int gaps, int consecutive, int borderMatches, int haystackIndex) FindForward( + private (int StartPos, int Gaps, int Consecutive, int BorderMatches, int HaystackIndex) FindForward( ReadOnlySpan haystack, int needleStart, int needleEnd) { var needleIndex = needleStart; @@ -175,7 +182,7 @@ internal readonly ref struct FuzzyMatcher for (var haystackIndex = 0; haystackIndex < haystack.Length; haystackIndex++) { - if (haystack[haystackIndex] == needleSpan[needleIndex]) + if (haystack[haystackIndex] == this.needleSpan[needleIndex]) { #if BORDER_MATCHING if (haystackIndex > 0) @@ -217,8 +224,8 @@ internal readonly ref struct FuzzyMatcher return (-1, 0, 0, 0, 0); } - private (int startPos, int gaps, int consecutive, int borderMatches) FindReverse(ReadOnlySpan haystack, - int haystackLastMatchIndex, int needleStart, int needleEnd) + private (int StartPos, int Gaps, int Consecutive, int BorderMatches) FindReverse( + ReadOnlySpan haystack, int haystackLastMatchIndex, int needleStart, int needleEnd) { var needleIndex = needleEnd; var revLastMatchIndex = haystack.Length + 10; @@ -229,7 +236,7 @@ internal readonly ref struct FuzzyMatcher for (var haystackIndex = haystackLastMatchIndex; haystackIndex >= 0; haystackIndex--) { - if (haystack[haystackIndex] == needleSpan[needleIndex]) + if (haystack[haystackIndex] == this.needleSpan[needleIndex]) { #if BORDER_MATCHING if (haystackIndex > 0) @@ -265,9 +272,12 @@ internal readonly ref struct FuzzyMatcher } } -public enum MatchMode +internal enum MatchMode { Simple, Fuzzy, - FuzzyParts + FuzzyParts, } + +#pragma warning restore SA1600 +#pragma warning restore SA1602 diff --git a/Dalamud/Utility/Signatures/SignatureHelper.cs b/Dalamud/Utility/Signatures/SignatureHelper.cs index bd99b8515..51f59bba2 100755 --- a/Dalamud/Utility/Signatures/SignatureHelper.cs +++ b/Dalamud/Utility/Signatures/SignatureHelper.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; @@ -6,6 +7,7 @@ using System.Runtime.InteropServices; using Dalamud.Game; using Dalamud.Hooking; using Dalamud.Logging; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures.Wrappers; using Serilog; @@ -14,25 +16,28 @@ namespace Dalamud.Utility.Signatures; /// /// A utility class to help reduce signature boilerplate code. /// -public static class SignatureHelper +internal static class SignatureHelper { private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; /// - /// Initialises an object's fields and properties that are annotated with a + /// Initializes an object's fields and properties that are annotated with a /// . /// - /// The object to initialise. + /// The object to initialize. /// If warnings should be logged using . - public static void Initialise(object self, bool log = true) + /// Collection of created IDalamudHooks. + internal static IEnumerable Initialize(object self, bool log = true) { - var scanner = Service.Get(); + var scanner = Service.Get(); var selfType = self.GetType(); var fields = selfType.GetFields(Flags).Select(field => (IFieldOrPropertyInfo)new FieldInfoWrapper(field)) .Concat(selfType.GetProperties(Flags).Select(prop => new PropertyInfoWrapper(prop))) .Select(field => (field, field.GetCustomAttribute())) .Where(field => field.Item2 != null); + var createdHooks = new List(); + foreach (var (info, sig) in fields) { var wasWrapped = false; @@ -61,7 +66,7 @@ public static class SignatureHelper : message; if (fallible) { - PluginLog.Warning(errorMsg); + Log.Warning(errorMsg); } else { @@ -149,15 +154,16 @@ public static class SignatureHelper detour = del; } - var ctor = actualType.GetConstructor(new[] { typeof(IntPtr), hookDelegateType }); - if (ctor == null) + var creator = actualType.GetMethod("FromAddress", BindingFlags.Static | BindingFlags.NonPublic); + if (creator == null) { - Log.Error("Error in SignatureHelper: could not find Hook constructor"); + Log.Error("Error in SignatureHelper: could not find Hook creator"); continue; } - var hook = ctor.Invoke(new object?[] { ptr, detour }); + var hook = creator.Invoke(null, new object?[] { ptr, detour, false }) as IDalamudHook; info.SetValue(self, hook); + createdHooks.Add(hook); break; } @@ -182,5 +188,7 @@ public static class SignatureHelper } } } + + return createdHooks; } } diff --git a/Dalamud.Interface/StableInsertionSortExtension.cs b/Dalamud/Utility/StableInsertionSortExtension.cs similarity index 57% rename from Dalamud.Interface/StableInsertionSortExtension.cs rename to Dalamud/Utility/StableInsertionSortExtension.cs index d2884f838..f7c9b43be 100644 --- a/Dalamud.Interface/StableInsertionSortExtension.cs +++ b/Dalamud/Utility/StableInsertionSortExtension.cs @@ -1,9 +1,21 @@ +using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; -namespace Dalamud.Interface; +namespace Dalamud.Utility; +/// +/// Extensions methods providing stable insertion sorts for IList. +/// internal static class StableInsertionSortExtension { + /// + /// Perform a stable sort on a list. + /// + /// The list to sort. + /// Selector to order by. + /// Element type. + /// Selected type. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void StableSort(this IList list, Func selector) { @@ -13,6 +25,12 @@ internal static class StableInsertionSortExtension list[i] = tmpList[i]; } + /// + /// Perform a stable sort on a list. + /// + /// The list to sort. + /// Comparer to use when comparing items. + /// Element type. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void StableSort(this IList list, Comparison comparer) { diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 038273eb6..36918abd2 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -1,13 +1,12 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; -using System.Net.Http; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Dalamud.Configuration.Internal; @@ -15,13 +14,13 @@ using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Interface; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; -using Dalamud.Networking.Http; +using Dalamud.Memory; using ImGuiNET; using Lumina.Excel.GeneratedSheets; -using Microsoft.Win32; +using PInvoke; using Serilog; namespace Dalamud.Utility; @@ -37,13 +36,6 @@ public static class Util private static ulong moduleStartAddr; private static ulong moduleEndAddr; - - /// - /// Gets an httpclient for usage. - /// Do NOT await this. - /// - [Obsolete($"Use Service<{nameof(HappyHttpClient)}> instead.")] - public static HttpClient HttpClient { get; } = Service.Get().SharedHttpClient; /// /// Gets the assembly version of Dalamud. @@ -497,47 +489,57 @@ public static class Util } /// - /// Copy one stream to another. + /// Determine if Dalamud is currently running within a Wine context (e.g. either on macOS or Linux). This method + /// will not return information about the host operating system. /// - /// The source stream. - /// The destination stream. - /// The maximum length to copy. - [Obsolete("Use Stream.CopyTo() instead", true)] - public static void CopyTo(Stream src, Stream dest, int len = 4069) + /// Returns true if Wine is detected, false otherwise. + public static bool IsWine() { - var bytes = new byte[len]; - int cnt; + if (EnvironmentConfiguration.XlWineOnLinux) return true; + if (Environment.GetEnvironmentVariable("XL_PLATFORM") is not null and not "Windows") return true; - while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) dest.Write(bytes, 0, cnt); + var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll"); + + // Test to see if any Wine specific exports exist. If they do, then we are running on Wine. + // The exports "wine_get_version", "wine_get_build_id", and "wine_get_host_version" will tend to be hidden + // by most Linux users (else FFXIV will want a macOS license), so we will additionally check some lesser-known + // exports as well. + return AnyProcExists( + ntdll, + "wine_get_version", + "wine_get_build_id", + "wine_get_host_version", + "wine_server_call", + "wine_unix_to_nt_file_name"); + + bool AnyProcExists(nint handle, params string[] procs) => + procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero); } /// - /// Heuristically determine if Dalamud is running on Linux/WINE. + /// Gets the best guess for the current host's platform based on the XL_PLATFORM environment variable or + /// heuristics. /// - /// Whether or not Dalamud is running on Linux/WINE. - public static bool IsLinux() + /// + /// macOS users running without XL_PLATFORM being set will be reported as Linux users. Due to the way our + /// Wines work, there isn't a great (consistent) way to split the two apart if we're not told. + /// + /// Returns the that Dalamud is currently running on. + public static OSPlatform GetHostPlatform() { - bool Check1() + switch (Environment.GetEnvironmentVariable("XL_PLATFORM")) { - return EnvironmentConfiguration.XlWineOnLinux; + case "Windows": return OSPlatform.Windows; + case "MacOS": return OSPlatform.OSX; + case "Linux": return OSPlatform.Linux; } - - bool Check2() - { - var hModule = NativeFunctions.GetModuleHandleW("ntdll.dll"); - var proc1 = NativeFunctions.GetProcAddress(hModule, "wine_get_version"); - var proc2 = NativeFunctions.GetProcAddress(hModule, "wine_get_build_id"); - - return proc1 != IntPtr.Zero || proc2 != IntPtr.Zero; - } - - bool Check3() - { - return Registry.CurrentUser.OpenSubKey(@"Software\Wine") != null || - Registry.LocalMachine.OpenSubKey(@"Software\Wine") != null; - } - - return Check1() || Check2() || Check3(); + + // n.b. we had some fancy code here to check if the Wine host version returned "Darwin" but apparently + // *all* our Wines report Darwin if exports aren't hidden. As such, it is effectively impossible (without some + // (very cursed and inaccurate heuristics) to determine if we're on macOS or Linux unless we're explicitly told + // by our launcher. See commit a7aacb15e4603a367e2f980578271a9a639d8852 for the old check. + + return IsWine() ? OSPlatform.Linux : OSPlatform.Windows; } /// @@ -609,6 +611,68 @@ public static class Util } } + /// + /// Overwrite text in a file by first writing it to a temporary file, and then + /// moving that file to the path specified. + /// + /// The path of the file to write to. + /// The text to write. + public static void WriteAllTextSafe(string path, string text) + { + WriteAllTextSafe(path, text, Encoding.UTF8); + } + + /// + /// Overwrite text in a file by first writing it to a temporary file, and then + /// moving that file to the path specified. + /// + /// The path of the file to write to. + /// The text to write. + /// Encoding to use. + public static void WriteAllTextSafe(string path, string text, Encoding encoding) + { + WriteAllBytesSafe(path, encoding.GetBytes(text)); + } + + /// + /// Overwrite data in a file by first writing it to a temporary file, and then + /// moving that file to the path specified. + /// + /// The path of the file to write to. + /// The data to write. + public static void WriteAllBytesSafe(string path, byte[] bytes) + { + ArgumentException.ThrowIfNullOrEmpty(path); + + // Open the temp file + var tempPath = path + ".tmp"; + + using var tempFile = Kernel32 + .CreateFile(tempPath.AsSpan(), + new Kernel32.ACCESS_MASK(Kernel32.FileAccess.FILE_GENERIC_READ | Kernel32.FileAccess.FILE_GENERIC_WRITE), + Kernel32.FileShare.None, + null, + Kernel32.CreationDisposition.CREATE_ALWAYS, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL, + Kernel32.SafeObjectHandle.Null); + + if (tempFile.IsInvalid) + throw new Win32Exception(); + + // Write the data + var bytesWritten = Kernel32.WriteFile(tempFile, new ArraySegment(bytes)); + if (bytesWritten != bytes.Length) + throw new Exception($"Could not write all bytes to temp file ({bytesWritten} of {bytes.Length})"); + + if (!Kernel32.FlushFileBuffers(tempFile)) + throw new Win32Exception(); + + tempFile.Close(); + + if (!MoveFileEx(tempPath, path, MoveFileFlags.MovefileReplaceExisting | MoveFileFlags.MovefileWriteThrough)) + throw new Win32Exception(); + } + /// /// Dispose this object. /// @@ -644,22 +708,6 @@ public static class Util } } - /// - /// Overwrite text in a file by first writing it to a temporary file, and then - /// moving that file to the path specified. - /// - /// The path of the file to write to. - /// The text to write. - internal static void WriteAllTextSafe(string path, string text) - { - var tmpPath = path + ".tmp"; - if (File.Exists(tmpPath)) - File.Delete(tmpPath); - - File.WriteAllText(tmpPath, text); - File.Move(tmpPath, path, true); - } - /// /// Gets a random, inoffensive, human-friendly string. /// @@ -706,7 +754,7 @@ public static class Util ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X")); } } - + private static unsafe void ShowValue(ulong addr, IEnumerable path, Type type, object value) { if (type.IsPointer) @@ -761,4 +809,18 @@ public static class Util } } } + + [Flags] +#pragma warning disable SA1201 + private enum MoveFileFlags +#pragma warning restore SA1201 + { + MovefileReplaceExisting = 0x00000001, + MovefileWriteThrough = 0x00000008, + } + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)] + private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, + MoveFileFlags dwFlags); } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index a593cb163..bfcc02f0b 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit a593cb163e1c5d33b27d34df4d1ccc57d1e67643 +Subproject commit bfcc02f0ba0fa0ee14603ee6b6f385eaeebbe43c diff --git a/targets/Dalamud.Plugin.targets b/targets/Dalamud.Plugin.targets index 4a5f9e97e..2f8e029eb 100644 --- a/targets/Dalamud.Plugin.targets +++ b/targets/Dalamud.Plugin.targets @@ -18,7 +18,6 @@ -