From e075a26ff784b2262088d87ce7bfcf121a107844 Mon Sep 17 00:00:00 2001 From: Ottermandias <70807659+Ottermandias@users.noreply.github.com> Date: Thu, 11 Apr 2024 23:19:19 +0200 Subject: [PATCH 01/24] Make IPC work with nullable types and null values. (#1765) --- Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs index 54adf2163..ead4d8df9 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs @@ -166,7 +166,12 @@ internal class CallGateChannel if (arg == null) { if (paramType.IsValueType) + { + if (paramType.IsGenericType && paramType.GetGenericTypeDefinition() == typeof(Nullable<>)) + continue; + throw new IpcValueNullError(this.Name, paramType, i); + } continue; } From e32fc002775b71a16663f0eb9ac3b69e2f70eabc Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Thu, 11 Apr 2024 14:23:13 -0700 Subject: [PATCH 02/24] IContextMenu Tweaks and Cleanup (#1753) * Tweaks and cleanup * Fix backwards logic --- Dalamud/Game/Gui/ContextMenu/ContextMenu.cs | 41 ++++++++++----------- Dalamud/Game/Gui/ContextMenu/MenuArgs.cs | 18 +++++++-- Dalamud/Game/Gui/ContextMenu/MenuItem.cs | 15 ++++++++ Dalamud/Plugin/Services/IContextMenu.cs | 7 ++-- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs index 70b0f53d2..80324a2d3 100644 --- a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs +++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs @@ -47,14 +47,14 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM this.addonContextMenuOnMenuSelectedHook.Enable(); } - private unsafe delegate ushort RaptureAtkModuleOpenAddonByAgentDelegate(RaptureAtkModule* module, byte* addonName, AtkUnitBase* addon, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, ushort parentAddonId); + private delegate ushort RaptureAtkModuleOpenAddonByAgentDelegate(RaptureAtkModule* module, byte* addonName, AtkUnitBase* addon, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, ushort parentAddonId); - private unsafe delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3); + private delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3); - private unsafe delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2); + private delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2); /// - public event IContextMenu.OnMenuOpenedDelegate OnMenuOpened; + public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened; private Dictionary> MenuItems { get; } = new(); @@ -171,7 +171,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM private void SetupGenericMenu(int headerCount, int sizeHeaderIdx, int returnHeaderIdx, int submenuHeaderIdx, IReadOnlyList items, ref int valueCount, ref AtkValue* values) { - var itemsWithIdx = items.Select((item, idx) => (item, idx)).OrderBy(i => i.item.Priority); + var itemsWithIdx = items.Select((item, idx) => (item, idx)).OrderBy(i => i.item.Priority).ToArray(); var prefixItems = itemsWithIdx.Where(i => i.item.Priority < 0).ToArray(); var suffixItems = itemsWithIdx.Where(i => i.item.Priority >= 0).ToArray(); @@ -268,10 +268,10 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM foreach (var item in items) { - if (!item.Prefix.HasValue) + if (!item.Prefix.HasValue && !item.UseDefaultPrefix) { - item.PrefixChar = 'D'; - item.PrefixColor = 539; + item.PrefixChar = MenuItem.DalamudDefaultPrefix.ToIconChar(); + item.PrefixColor = MenuItem.DalamudDefaultPrefixColor; Log.Warning($"Menu item \"{item.Name}\" has no prefix, defaulting to Dalamud's. Menu items outside of a submenu must have a prefix."); } } @@ -378,13 +378,13 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM { // The in game menu actually supports 32 items, but the last item can't have a visible submenu arrow. // As such, we'll only work with 31 items. - const int MaxMenuItems = 31; - if (items.Count + nativeMenuSize > MaxMenuItems) + const int maxMenuItems = 31; + if (items.Count + nativeMenuSize > maxMenuItems) { - Log.Warning($"Menu size exceeds {MaxMenuItems} items, truncating."); + Log.Warning($"Menu size exceeds {maxMenuItems} items, truncating."); var orderedItems = items.OrderBy(i => i.Priority).ToArray(); - var newItems = orderedItems[..(MaxMenuItems - nativeMenuSize - 1)]; - var submenuItems = orderedItems[(MaxMenuItems - nativeMenuSize - 1)..]; + var newItems = orderedItems[..(maxMenuItems - nativeMenuSize - 1)]; + var submenuItems = orderedItems[(maxMenuItems - nativeMenuSize - 1)..]; return newItems.Append(new MenuItem { Prefix = SeIconChar.BoxedLetterD, @@ -450,7 +450,6 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM if (callbackId < 0) { selectedIdx = -callbackId - 1; - goto original; } else { @@ -461,17 +460,17 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM { if (item.OnClicked == null) throw new InvalidOperationException("Item has no OnClicked handler"); - item.OnClicked.InvokeSafely(new( - (name, items) => + item.OnClicked.InvokeSafely(new MenuItemClickedArgs( + (name, submenuItems) => { short x, y; addon->AtkUnitBase.GetPosition(&x, &y); - this.OpenSubmenu(name ?? item.Name, items, x, y); + this.OpenSubmenu(name ?? item.Name, submenuItems, x, y); openedSubmenu = true; }, this.SelectedParentAddon, this.SelectedAgent, - this.SelectedMenuType.Value, + this.SelectedMenuType ?? default, this.SelectedEventInterfaces)); } catch (Exception e) @@ -479,14 +478,14 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM Log.Error(e, "Error while handling context menu click"); } - // Close with clicky sound + // Close with click sound if (!openedSubmenu) addon->AtkUnitBase.FireCallbackInt(-2); return false; } original: - // Eventually handled by inventorycontext here: 14022BBD0 (6.51) + // Eventually handled by inventory context here: 14022BBD0 (6.51) return this.addonContextMenuOnMenuSelectedHook.Original(addon, selectedIdx, a3); } } @@ -511,7 +510,7 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen } /// - public event IContextMenu.OnMenuOpenedDelegate OnMenuOpened; + public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened; private Dictionary> MenuItems { get; } = new(); diff --git a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs index d0d8ec0dc..cc58a839b 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs @@ -70,8 +70,18 @@ public abstract unsafe class MenuArgs /// Almost always an agent pointer. You can use this to find out what type of context menu it is. /// /// Thrown when the context menu is not a . - public IReadOnlySet EventInterfaces => - this.MenuType != ContextMenuType.Default ? - this.eventInterfaces : - throw new InvalidOperationException("Not a default context menu"); + public IReadOnlySet EventInterfaces + { + get + { + if (this.MenuType is ContextMenuType.Default) + { + return this.eventInterfaces ?? new HashSet(); + } + else + { + throw new InvalidOperationException("Not a default context menu"); + } + } + } } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs index fdeb64d13..ec111f19e 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs @@ -10,6 +10,16 @@ namespace Dalamud.Game.Gui.ContextMenu; /// public sealed record MenuItem { + /// + /// The default prefix used if no specific preset is specified. + /// + public const SeIconChar DalamudDefaultPrefix = SeIconChar.BoxedLetterD; + + /// + /// The default prefix color used if no specific preset is specified. + /// + public const ushort DalamudDefaultPrefixColor = 539; + /// /// Gets or sets the display name of the menu item. /// @@ -46,6 +56,11 @@ public sealed record MenuItem /// Gets or sets the color of the . Specifies a row id. /// public ushort PrefixColor { get; set; } + + /// + /// Gets or sets a value indicating whether the dev wishes to intentionally use the default prefix symbol and color. + /// + public bool UseDefaultPrefix { get; set; } /// /// Gets or sets the callback to be invoked when the menu item is clicked. diff --git a/Dalamud/Plugin/Services/IContextMenu.cs b/Dalamud/Plugin/Services/IContextMenu.cs index 4d792116d..a3bfa722a 100644 --- a/Dalamud/Plugin/Services/IContextMenu.cs +++ b/Dalamud/Plugin/Services/IContextMenu.cs @@ -1,6 +1,4 @@ using Dalamud.Game.Gui.ContextMenu; -using Dalamud.Game.Text; -using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Plugin.Services; @@ -16,8 +14,9 @@ public interface IContextMenu public delegate void OnMenuOpenedDelegate(MenuOpenedArgs args); /// - /// Event that gets fired every time the game framework updates. + /// Event that gets fired whenever any context menu is opened. /// + /// Use this event and then check if the triggering addon is the desired addon, then add custom context menu items to the provided args. event OnMenuOpenedDelegate OnMenuOpened; /// @@ -25,6 +24,7 @@ public interface IContextMenu /// /// The type of context menu to add the item to. /// The item to add. + /// Used to add a context menu entry to all context menus. void AddMenuItem(ContextMenuType menuType, MenuItem item); /// @@ -32,6 +32,7 @@ public interface IContextMenu /// /// The type of context menu to remove the item from. /// The item to add. + /// Used to remove a context menu entry from all context menus. /// if the item was removed, if it was not found. bool RemoveMenuItem(ContextMenuType menuType, MenuItem item); } From b8802f06098df0c489dac72b3e401d1c884f3a36 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Thu, 11 Apr 2024 20:25:04 -0700 Subject: [PATCH 03/24] fix: Use Prefix instead of PrefixChar for menu item tag (#1767) --- Dalamud/Game/Gui/ContextMenu/ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs index 80324a2d3..abbfc6a5b 100644 --- a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs +++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs @@ -270,7 +270,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM { if (!item.Prefix.HasValue && !item.UseDefaultPrefix) { - item.PrefixChar = MenuItem.DalamudDefaultPrefix.ToIconChar(); + item.Prefix = MenuItem.DalamudDefaultPrefix; item.PrefixColor = MenuItem.DalamudDefaultPrefixColor; Log.Warning($"Menu item \"{item.Name}\" has no prefix, defaulting to Dalamud's. Menu items outside of a submenu must have a prefix."); } From e97d95dba838c3a474cb8f5cab15a865e71b33a9 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Sun, 14 Apr 2024 23:19:27 +0200 Subject: [PATCH 04/24] Add FontAwesome fixed width (#1737) * Add FA-FW * remove braces (ask of kizer) * reuse iconfont glyphs * use FAFS for initial font --------- Co-authored-by: rootdarkarchon --- .../Interface/Internal/InterfaceManager.cs | 35 +++++++++++++++++-- .../IFontAtlasBuildToolkitPostBuild.cs | 9 ++++- .../FontAtlasFactory.BuildToolkit.cs | 31 +++++++++++++--- Dalamud/Interface/UiBuilder.cs | 23 +++++++++--- 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index f72f53777..0e28c1025 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -25,10 +25,15 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; using Dalamud.Utility.Timing; + using ImGuiNET; + using ImGuiScene; + using PInvoke; + using Serilog; + using SharpDX; using SharpDX.Direct3D; using SharpDX.Direct3D11; @@ -131,6 +136,13 @@ internal class InterfaceManager : IInternalDisposableService public static ImFontPtr IconFont => WhenFontsReady().IconFontHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault); + /// + /// Gets an included FontAwesome icon font with fixed width. + /// Accessing this static property outside of the main thread is dangerous and not supported. + /// + public static ImFontPtr IconFontFixedWidth => + WhenFontsReady().IconFontFixedWidthHandle!.LockUntilPostFrame().OrElse(ImGui.GetIO().FontDefault); + /// /// Gets an included monospaced font.
/// Accessing this static property outside of the main thread is dangerous and not supported. @@ -148,6 +160,11 @@ internal class InterfaceManager : IInternalDisposableService ///
public FontHandle? IconFontHandle { get; private set; } + /// + /// Gets the icon font handle with fixed width. + /// + public FontHandle? IconFontFixedWidthHandle { get; private set; } + /// /// Gets the mono font handle. /// @@ -402,7 +419,7 @@ internal class InterfaceManager : IInternalDisposableService }); } } - + // no sampler for now because the ImGui implementation we copied doesn't allow for changing it return new DalamudTextureWrap(new D3DTextureWrap(resView, width, height)); } @@ -498,7 +515,7 @@ internal class InterfaceManager : IInternalDisposableService atlas.BuildTask.GetAwaiter().GetResult(); return im; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void RenderImGui(RawDX11Scene scene) { @@ -732,6 +749,13 @@ internal class InterfaceManager : IInternalDisposableService GlyphMinAdvanceX = DefaultFontSizePx, GlyphMaxAdvanceX = DefaultFontSizePx, }))); + this.IconFontFixedWidthHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( + e => e.OnPreBuild(tk => tk.AddDalamudAssetFont( + DalamudAsset.FontAwesomeFreeSolid, + new() + { + GlyphRanges = new ushort[] { 0x20 }, + }))); this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( e => e.OnPreBuild( tk => tk.AddDalamudAssetFont( @@ -748,6 +772,13 @@ internal class InterfaceManager : IInternalDisposableService tk.GetFont(this.DefaultFontHandle), tk.GetFont(this.MonoFontHandle), missingOnly: true); + + // Fill missing glyphs in IconFontFixedWidth with IconFont and fit ratio + tk.CopyGlyphsAcrossFonts( + tk.GetFont(this.IconFontHandle), + tk.GetFont(this.IconFontFixedWidthHandle), + missingOnly: true); + tk.FitRatio(tk.GetFont(this.IconFontFixedWidthHandle)); }); this.DefaultFontHandle.ImFontChanged += (_, font) => { diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs index 827187063..2df0deae6 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontAtlasBuildToolkitPostBuild.cs @@ -1,4 +1,4 @@ -using Dalamud.Interface.Internal; +using Dalamud.Interface.Internal; using Dalamud.Utility; using ImGuiNET; @@ -27,6 +27,13 @@ public interface IFontAtlasBuildToolkitPostBuild : IFontAtlasBuildToolkit /// The texture index. int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError); + /// + /// Fits a font to a fixed 1:1 ratio adjusting glyph positions horizontally and vertically to fit within font size boundaries. + /// + /// The font to fit. + /// Whether to call target.BuildLookupTable(). + void FitRatio(ImFontPtr font, bool rebuildLookupTable = true); + /// /// Copies glyphs across fonts, in a safer way.
/// If the font does not belong to the current atlas, this function is a no-op. diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs index 34d28ccbd..d05e5a2e7 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs @@ -1,4 +1,4 @@ -using System.Buffers; +using System.Buffers; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -166,7 +166,7 @@ internal sealed partial class FontAtlasFactory /// public int StoreTexture(IDalamudTextureWrap textureWrap, bool disposeOnError) => this.data.AddNewTexture(textureWrap, disposeOnError); - + /// public void RegisterPostBuild(Action action) => this.registeredPostBuildActions.Add(action); @@ -391,12 +391,10 @@ internal sealed partial class FontAtlasFactory }); case DalamudAsset.LodestoneGameSymbol when !this.factory.HasGameSymbolsFontFile: - { return this.AddGameGlyphs( new(GameFontFamily.Axis, fontConfig.SizePx), fontConfig.GlyphRanges, fontConfig.MergeFont); - } default: return this.factory.AddFont( @@ -858,5 +856,30 @@ internal sealed partial class FontAtlasFactory } } } + + /// + public void FitRatio(ImFontPtr font, bool rebuildLookupTable = true) + { + var nsize = font.FontSize; + var glyphs = font.GlyphsWrapped(); + foreach (ref var glyph in glyphs.DataSpan) + { + var ratio = 1f; + if (glyph.X1 - glyph.X0 > nsize) + ratio = Math.Max(ratio, (glyph.X1 - glyph.X0) / nsize); + if (glyph.Y1 - glyph.Y0 > nsize) + ratio = Math.Max(ratio, (glyph.Y1 - glyph.Y0) / nsize); + var w = MathF.Round((glyph.X1 - glyph.X0) / ratio, MidpointRounding.ToZero); + var h = MathF.Round((glyph.Y1 - glyph.Y0) / ratio, MidpointRounding.AwayFromZero); + glyph.X0 = MathF.Round((nsize - w) / 2f, MidpointRounding.ToZero); + glyph.Y0 = MathF.Round((nsize - h) / 2f, MidpointRounding.AwayFromZero); + glyph.X1 = glyph.X0 + w; + glyph.Y1 = glyph.Y0 + h; + glyph.AdvanceX = nsize; + } + + if (rebuildLookupTable) + this.BuildLookupTable(font); + } } } diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index b80fe0b82..9440b89e6 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -21,9 +21,13 @@ using Dalamud.Plugin; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; using Dalamud.Utility; + using ImGuiNET; + using ImGuiScene; + using Serilog; + using SharpDX.Direct3D11; namespace Dalamud.Interface; @@ -53,6 +57,7 @@ public sealed class UiBuilder : IDisposable private IFontHandle? defaultFontHandle; private IFontHandle? iconFontHandle; private IFontHandle? monoFontHandle; + private IFontHandle? iconFontFixedWidthHandle; /// /// Initializes a new instance of the class and registers it. @@ -106,7 +111,7 @@ public sealed class UiBuilder : IDisposable /// Event that is fired when the plugin should open its configuration interface. /// public event Action OpenConfigUi; - + /// /// Event that is fired when the plugin should open its main interface. /// @@ -251,6 +256,16 @@ public sealed class UiBuilder : IDisposable this.InterfaceManagerWithScene?.IconFontHandle ?? throw new InvalidOperationException("Scene is not yet ready."))); + /// + /// Gets the default Dalamud icon font based on FontAwesome 5 free solid with a fixed width and vertically centered glyphs. + /// + public IFontHandle IconFontFixedWidthHandle => + this.iconFontFixedWidthHandle ??= + this.scopedFinalizer.Add( + new FontHandleWrapper( + this.InterfaceManagerWithScene?.IconFontFixedWidthHandle + ?? throw new InvalidOperationException("Scene is not yet ready."))); + /// /// Gets the default Dalamud monospaced font based on Inconsolata Regular. /// @@ -266,7 +281,7 @@ public sealed class UiBuilder : IDisposable /// new() { SizePx = UiBuilder.DefaultFontSizePx }))); /// /// - public IFontHandle MonoFontHandle => + public IFontHandle MonoFontHandle => this.monoFontHandle ??= this.scopedFinalizer.Add( new FontHandleWrapper( @@ -630,7 +645,7 @@ public sealed class UiBuilder : IDisposable { this.OpenConfigUi?.InvokeSafely(); } - + /// /// Open the registered configuration UI, if it exists. /// @@ -838,5 +853,5 @@ public sealed class UiBuilder : IDisposable private void WrappedOnImFontChanged(IFontHandle obj, ILockedImFont lockedFont) => this.ImFontChanged?.Invoke(obj, lockedFont); - } + } } From 39b6c4b701bffc4f3229c0cf63d034fae21c27fc Mon Sep 17 00:00:00 2001 From: bleatbot <106497096+bleatbot@users.noreply.github.com> Date: Sun, 14 Apr 2024 23:20:13 +0200 Subject: [PATCH 05/24] Update ClientStructs (#1764) Co-authored-by: github-actions[bot] --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 98c1de8b9..1ea76af5c 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 98c1de8b94bcdfce4dc79a61cc0e8b17773777f0 +Subproject commit 1ea76af5c796e0d84e23e07207cee9e78dd8bd02 From 12efd9e6792168d3d192237f7a8d15ec318dfeb2 Mon Sep 17 00:00:00 2001 From: goat Date: Mon, 15 Apr 2024 00:43:06 +0200 Subject: [PATCH 06/24] flush log on Util.Fatal() --- Dalamud/Utility/Util.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 43355ac2c..57417c6f6 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -344,7 +344,10 @@ public static class Util _ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags); if (exit) + { + Log.CloseAndFlush(); Environment.Exit(-1); + } } /// From d98708e9a8fd5079fdb0e68e05e21fa0c97c74b7 Mon Sep 17 00:00:00 2001 From: goat Date: Mon, 15 Apr 2024 00:44:51 +0200 Subject: [PATCH 07/24] fix fixed-width icon font build error, show error message if fonts fail to build --- .../Interface/Internal/InterfaceManager.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 0e28c1025..62506c331 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -17,12 +17,12 @@ using Dalamud.Hooking; using Dalamud.Hooking.WndProcHook; using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal.ManagedAsserts; -using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Style; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; +using Dalamud.Logging.Internal; using Dalamud.Utility; using Dalamud.Utility.Timing; @@ -32,8 +32,6 @@ using ImGuiScene; using PInvoke; -using Serilog; - using SharpDX; using SharpDX.Direct3D; using SharpDX.Direct3D11; @@ -69,6 +67,8 @@ internal class InterfaceManager : IInternalDisposableService /// public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f; + private static readonly ModuleLog Log = new("INTERFACE"); + private readonly ConcurrentBag deferredDisposeTextures = new(); private readonly ConcurrentBag deferredDisposeImFontLockeds = new(); @@ -686,7 +686,16 @@ internal class InterfaceManager : IInternalDisposableService Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception."); if (!this.dalamudAtlas!.HasBuiltAtlas) + { + if (this.dalamudAtlas.BuildTask.Exception != null) + { + // TODO: Can we do something more user-friendly here? Unload instead? + Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); + Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); + } + return this.presentHook!.Original(swapChain, syncInterval, presentFlags); + } if (this.address.IsReshade) { @@ -738,6 +747,7 @@ internal class InterfaceManager : IInternalDisposableService .CreateFontAtlas(nameof(InterfaceManager), FontAtlasAutoRebuildMode.Disable); using (this.dalamudAtlas.SuppressAutoRebuild()) { + var defaultSizePx = Service.Get().DefaultFontSpec.SizePx; this.DefaultFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(-1))); this.IconFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( @@ -745,7 +755,7 @@ internal class InterfaceManager : IInternalDisposableService tk => tk.AddFontAwesomeIconFont( new() { - SizePx = Service.Get().DefaultFontSpec.SizePx, + SizePx = defaultSizePx, GlyphMinAdvanceX = DefaultFontSizePx, GlyphMaxAdvanceX = DefaultFontSizePx, }))); @@ -754,7 +764,8 @@ internal class InterfaceManager : IInternalDisposableService DalamudAsset.FontAwesomeFreeSolid, new() { - GlyphRanges = new ushort[] { 0x20 }, + SizePx = defaultSizePx, + GlyphRanges = new ushort[] { 0x20, 0x20, 0x00 }, }))); this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( e => e.OnPreBuild( @@ -762,7 +773,7 @@ internal class InterfaceManager : IInternalDisposableService DalamudAsset.InconsolataRegular, new() { - SizePx = Service.Get().DefaultFontSpec.SizePx, + SizePx = defaultSizePx, }))); this.dalamudAtlas.BuildStepChange += e => e.OnPostBuild( tk => @@ -801,7 +812,7 @@ internal class InterfaceManager : IInternalDisposableService }); }; } - + // This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene. _ = this.dalamudAtlas.BuildFontsAsync(); From 0d7c8f1bd2843eeaf1d49debc6f5347ddc25d1f7 Mon Sep 17 00:00:00 2001 From: goat Date: Mon, 15 Apr 2024 00:45:01 +0200 Subject: [PATCH 08/24] add fixed-width font option to icon test widget --- .../Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs index 22f615e8a..a6d76f44f 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs @@ -18,6 +18,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget private int selectedIconCategory; private string iconSearchInput = string.Empty; private bool iconSearchChanged = true; + private bool useFixedWidth = false; /// public string[]? CommandShortcuts { get; init; } = { "fa", "fatest", "fontawesome" }; @@ -80,6 +81,8 @@ internal class FontAwesomeTestWidget : IDataWindowWidget { this.iconSearchChanged = true; } + + ImGui.Checkbox("Use fixed width font", ref this.useFixedWidth); ImGuiHelpers.ScaledDummy(10f); for (var i = 0; i < this.icons?.Count; i++) @@ -88,7 +91,7 @@ internal class FontAwesomeTestWidget : IDataWindowWidget ImGuiHelpers.ScaledRelativeSameLine(50f); ImGui.Text($"{this.iconNames?[i]}"); ImGuiHelpers.ScaledRelativeSameLine(280f); - ImGui.PushFont(UiBuilder.IconFont); + ImGui.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont); ImGui.Text(this.icons[i].ToIconString()); ImGui.PopFont(); ImGuiHelpers.ScaledDummy(2f); From 22d27fbb192d2d9b746853cfe376b01c1ebb6492 Mon Sep 17 00:00:00 2001 From: goat Date: Mon, 15 Apr 2024 00:45:50 +0200 Subject: [PATCH 09/24] swap around log/exit in IM font build error --- Dalamud/Interface/Internal/InterfaceManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 62506c331..ffcd5c679 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -690,8 +690,8 @@ internal class InterfaceManager : IInternalDisposableService if (this.dalamudAtlas.BuildTask.Exception != null) { // TODO: Can we do something more user-friendly here? Unload instead? - Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts"); + Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud"); } return this.presentHook!.Original(swapChain, syncInterval, presentFlags); From b40ffc24c13cf1b4606e56a2267039383fc4e116 Mon Sep 17 00:00:00 2001 From: goat Date: Mon, 15 Apr 2024 18:45:37 +0200 Subject: [PATCH 10/24] don't capture sizePx when building default fonts --- Dalamud/Interface/Internal/InterfaceManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index ffcd5c679..65d250cfd 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -747,7 +747,6 @@ internal class InterfaceManager : IInternalDisposableService .CreateFontAtlas(nameof(InterfaceManager), FontAtlasAutoRebuildMode.Disable); using (this.dalamudAtlas.SuppressAutoRebuild()) { - var defaultSizePx = Service.Get().DefaultFontSpec.SizePx; this.DefaultFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( e => e.OnPreBuild(tk => tk.AddDalamudDefaultFont(-1))); this.IconFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( @@ -755,7 +754,7 @@ internal class InterfaceManager : IInternalDisposableService tk => tk.AddFontAwesomeIconFont( new() { - SizePx = defaultSizePx, + SizePx = Service.Get().DefaultFontSpec.SizePx, GlyphMinAdvanceX = DefaultFontSizePx, GlyphMaxAdvanceX = DefaultFontSizePx, }))); @@ -764,7 +763,7 @@ internal class InterfaceManager : IInternalDisposableService DalamudAsset.FontAwesomeFreeSolid, new() { - SizePx = defaultSizePx, + SizePx = Service.Get().DefaultFontSpec.SizePx, GlyphRanges = new ushort[] { 0x20, 0x20, 0x00 }, }))); this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle( @@ -773,7 +772,7 @@ internal class InterfaceManager : IInternalDisposableService DalamudAsset.InconsolataRegular, new() { - SizePx = defaultSizePx, + SizePx = Service.Get().DefaultFontSpec.SizePx, }))); this.dalamudAtlas.BuildStepChange += e => e.OnPostBuild( tk => From 285d3bed42642081dd04acc6d811116558f4303e Mon Sep 17 00:00:00 2001 From: goat Date: Tue, 16 Apr 2024 00:58:12 +0200 Subject: [PATCH 11/24] pi: properly support changelogs for testing plugins --- .../PluginInstaller/PluginInstallerWindow.cs | 63 +++++++++++-------- .../Types/Manifest/RemotePluginManifest.cs | 5 ++ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 455c8e4d3..601348d07 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1128,34 +1128,33 @@ internal class PluginInstallerWindow : Window, IDisposable this.DrawChangelog(logEntry); } } - - private record PluginInstallerAvailablePluginProxy(RemotePluginManifest? RemoteManifest, LocalPlugin? LocalPlugin); - + #pragma warning disable SA1201 - private void DrawAvailablePluginList() -#pragma warning restore SA1201 + private record PluginInstallerAvailablePluginProxy(RemotePluginManifest? RemoteManifest, LocalPlugin? LocalPlugin); + + private IEnumerable GatherProxies() { + var proxies = new List(); + var availableManifests = this.pluginListAvailable; var installedPlugins = this.pluginListInstalled.ToList(); // Copy intended if (availableManifests.Count == 0) { ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.TabBody_SearchNoCompatible); - return; + return proxies; } var filteredAvailableManifests = availableManifests - .Where(rm => !this.IsManifestFiltered(rm)) - .ToList(); + .Where(rm => !this.IsManifestFiltered(rm)) + .ToList(); if (filteredAvailableManifests.Count == 0) { ImGui.TextColored(ImGuiColors.DalamudGrey2, Locs.TabBody_SearchNoMatching); - return; + return proxies; } - var proxies = new List(); - // Go through all AVAILABLE manifests, associate them with a NON-DEV local plugin, if one is available, and remove it from the pile foreach (var availableManifest in this.categoryManager.GetCurrentCategoryContent(filteredAvailableManifests).Cast()) { @@ -1168,7 +1167,7 @@ internal class PluginInstallerWindow : Window, IDisposable if (plugin != null) { installedPlugins.Remove(plugin); - proxies.Add(new PluginInstallerAvailablePluginProxy(null, plugin)); + proxies.Add(new PluginInstallerAvailablePluginProxy(availableManifest, plugin)); continue; } @@ -1187,8 +1186,14 @@ internal class PluginInstallerWindow : Window, IDisposable proxies.Add(new PluginInstallerAvailablePluginProxy(null, installedPlugin)); } + return proxies; + } +#pragma warning restore SA1201 + + private void DrawAvailablePluginList() + { var i = 0; - foreach (var proxy in proxies) + foreach (var proxy in this.GatherProxies()) { IPluginManifest applicableManifest = proxy.LocalPlugin != null ? proxy.LocalPlugin.Manifest : proxy.RemoteManifest; @@ -1199,7 +1204,7 @@ internal class PluginInstallerWindow : Window, IDisposable if (proxy.LocalPlugin != null) { - this.DrawInstalledPlugin(proxy.LocalPlugin, i++, true); + this.DrawInstalledPlugin(proxy.LocalPlugin, i++, proxy.RemoteManifest, true); } else if (proxy.RemoteManifest != null) { @@ -1237,7 +1242,12 @@ internal class PluginInstallerWindow : Window, IDisposable if (filterTesting && !manager.HasTestingOptIn(plugin.Manifest)) continue; - this.DrawInstalledPlugin(plugin, i++); + // Find the applicable remote manifest + var remoteManifest = this.pluginListAvailable + .FirstOrDefault(rm => rm.InternalName == plugin.Manifest.InternalName && + rm.RepoUrl == plugin.Manifest.RepoUrl); + + this.DrawInstalledPlugin(plugin, i++, remoteManifest); } } @@ -1266,7 +1276,7 @@ internal class PluginInstallerWindow : Window, IDisposable var i = 0; foreach (var plugin in filteredList) { - this.DrawInstalledPlugin(plugin, i++); + this.DrawInstalledPlugin(plugin, i++, null); } } @@ -2251,7 +2261,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - private void DrawInstalledPlugin(LocalPlugin plugin, int index, bool showInstalled = false) + private void DrawInstalledPlugin(LocalPlugin plugin, int index, RemotePluginManifest? remoteManifest, bool showInstalled = false) { var configuration = Service.Get(); var commandManager = Service.Get(); @@ -2376,7 +2386,9 @@ internal class PluginInstallerWindow : Window, IDisposable } ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}"); - var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty(); + + var applicableChangelog = plugin.IsTesting ? remoteManifest?.Changelog : remoteManifest?.TestingChangelog; + var hasChangelog = !applicableChangelog.IsNullOrWhitespace(); var didDrawChangelogInsideCollapsible = false; if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.IsThirdParty, trouble, availablePluginUpdate != default, false, false, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) @@ -2489,12 +2501,12 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.Unindent(); - if (hasChangelog) + if (!applicableChangelog.IsNullOrWhitespace()) { if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.EffectiveVersion))) { didDrawChangelogInsideCollapsible = true; - this.DrawInstalledPluginChangelog(plugin.Manifest); + this.DrawInstalledPluginChangelog(applicableChangelog); ImGui.TreePop(); } } @@ -2502,9 +2514,10 @@ internal class PluginInstallerWindow : Window, IDisposable if (availablePluginUpdate != default && !availablePluginUpdate.UpdateManifest.Changelog.IsNullOrWhitespace()) { var availablePluginUpdateVersion = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingAssemblyVersion : availablePluginUpdate.UpdateManifest.AssemblyVersion; - if (ImGui.TreeNode(Locs.PluginBody_UpdateChangeLog(availablePluginUpdateVersion))) + var availableChangelog = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingChangelog : availablePluginUpdate.UpdateManifest.Changelog; + if (!availableChangelog.IsNullOrWhitespace() && ImGui.TreeNode(Locs.PluginBody_UpdateChangeLog(availablePluginUpdateVersion))) { - this.DrawInstalledPluginChangelog(availablePluginUpdate.UpdateManifest); + this.DrawInstalledPluginChangelog(availableChangelog); ImGui.TreePop(); } } @@ -2512,13 +2525,13 @@ internal class PluginInstallerWindow : Window, IDisposable if (thisWasUpdated && hasChangelog && !didDrawChangelogInsideCollapsible) { - this.DrawInstalledPluginChangelog(plugin.Manifest); + this.DrawInstalledPluginChangelog(applicableChangelog); } ImGui.PopID(); } - private void DrawInstalledPluginChangelog(IPluginManifest manifest) + private void DrawInstalledPluginChangelog(string changelog) { ImGuiHelpers.ScaledDummy(5); @@ -2531,7 +2544,7 @@ internal class PluginInstallerWindow : Window, IDisposable { ImGui.Text("Changelog:"); ImGuiHelpers.ScaledDummy(2); - ImGuiHelpers.SafeTextWrapped(manifest.Changelog!); + ImGuiHelpers.SafeTextWrapped(changelog!); } ImGui.EndChild(); diff --git a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs index 952650c72..e3d99a85a 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs @@ -16,6 +16,11 @@ internal record RemotePluginManifest : PluginManifest ///
[JsonIgnore] public PluginRepository SourceRepo { get; set; } = null!; + + /// + /// Gets or sets the changelog to be shown when obtaining the testing version of the plugin. + /// + public string? TestingChangelog { get; set; } /// /// Gets a value indicating whether this plugin is eligible for testing. From b1a8ec7615a47d613a2a1dfbb58f1d4918878882 Mon Sep 17 00:00:00 2001 From: goat Date: Tue, 16 Apr 2024 01:32:56 +0200 Subject: [PATCH 12/24] pi: fix some leaked SameLine's --- .../PluginInstaller/PluginInstallerWindow.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 601348d07..80f821033 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -2183,17 +2183,18 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.ScaledDummy(10); ImGui.SameLine(); - this.DrawVisitRepoUrlButton(manifest.RepoUrl, true); + if (this.DrawVisitRepoUrlButton(manifest.RepoUrl, true)) + { + ImGui.SameLine(); + ImGuiHelpers.ScaledDummy(3); + } - ImGui.SameLine(); - ImGuiHelpers.ScaledDummy(3); - ImGui.SameLine(); - if (!manifest.SourceRepo.IsThirdParty && manifest.AcceptsFeedback) { + ImGui.SameLine(); this.DrawSendFeedbackButton(manifest, false, true); } - + ImGuiHelpers.ScaledDummy(5); if (this.DrawPluginImages(null, manifest, isThirdParty, index)) @@ -2485,11 +2486,15 @@ internal class PluginInstallerWindow : Window, IDisposable if (canFeedback) { + ImGui.SameLine(); this.DrawSendFeedbackButton(plugin.Manifest, plugin.IsTesting, false); } if (availablePluginUpdate != default && !plugin.IsDev) + { + ImGui.SameLine(); this.DrawUpdateSinglePluginButton(availablePluginUpdate); + } ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.EffectiveVersion}"); @@ -2501,7 +2506,7 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.Unindent(); - if (!applicableChangelog.IsNullOrWhitespace()) + if (hasChangelog) { if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.EffectiveVersion))) { @@ -2891,8 +2896,6 @@ internal class PluginInstallerWindow : Window, IDisposable private void DrawUpdateSinglePluginButton(AvailablePluginUpdate update) { - ImGui.SameLine(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.Download)) { Task.Run(() => this.UpdateSinglePlugin(update)); @@ -2962,8 +2965,6 @@ internal class PluginInstallerWindow : Window, IDisposable private void DrawSendFeedbackButton(IPluginManifest manifest, bool isTesting, bool big) { - ImGui.SameLine(); - var clicked = big ? ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Comment, Locs.FeedbackModal_Title) : ImGuiComponents.IconButton(FontAwesomeIcon.Comment); @@ -3208,7 +3209,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - private void DrawVisitRepoUrlButton(string? repoUrl, bool big) + private bool DrawVisitRepoUrlButton(string? repoUrl, bool big) { if (!string.IsNullOrEmpty(repoUrl) && repoUrl.StartsWith("https://")) { @@ -3235,7 +3236,11 @@ internal class PluginInstallerWindow : Window, IDisposable if (ImGui.IsItemHovered()) ImGui.SetTooltip(Locs.PluginButtonToolTip_VisitPluginUrl); + + return true; } + + return false; } private bool DrawPluginImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, int index) From cecb09dcedd20300a6f68844d5372af70c933493 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:32:13 +0200 Subject: [PATCH 13/24] ioc: cancel all blocking Get()'s when unloading (#1772) --- Dalamud/ServiceManager.cs | 11 +++++++++++ Dalamud/Service{T}.cs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Dalamud/ServiceManager.cs b/Dalamud/ServiceManager.cs index bdc0918b0..75daf9187 100644 --- a/Dalamud/ServiceManager.cs +++ b/Dalamud/ServiceManager.cs @@ -43,6 +43,7 @@ internal static class ServiceManager #endif private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new(); + private static readonly CancellationTokenSource UnloadCancellationTokenSource = new(); private static ManualResetEvent unloadResetEvent = new(false); @@ -107,6 +108,12 @@ internal static class ServiceManager /// Gets task that gets completed when all blocking early loading services are done loading. /// public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task; + + /// + /// Gets a cancellation token that will be cancelled once Dalamud needs to unload, be it due to a failure state + /// during initialization or during regular operation. + /// + public static CancellationToken UnloadCancellationToken => UnloadCancellationTokenSource.Token; /// /// Initializes Provided Services and FFXIVClientStructs. @@ -374,6 +381,8 @@ internal static class ServiceManager } catch (Exception e) { + UnloadCancellationTokenSource.Cancel(); + Log.Error(e, "Failed resolving services"); try { @@ -401,6 +410,8 @@ internal static class ServiceManager /// public static void UnloadAllServices() { + UnloadCancellationTokenSource.Cancel(); + var framework = Service.GetNullable(Service.ExceptionPropagationMode.None); if (framework is { IsInFrameworkUpdateThread: false, IsFrameworkUnloading: false }) { diff --git a/Dalamud/Service{T}.cs b/Dalamud/Service{T}.cs index 165257ef5..a4d404e18 100644 --- a/Dalamud/Service{T}.cs +++ b/Dalamud/Service{T}.cs @@ -116,7 +116,7 @@ internal static class Service where T : IServiceType #endif if (!instanceTcs.Task.IsCompleted) - instanceTcs.Task.Wait(); + instanceTcs.Task.Wait(ServiceManager.UnloadCancellationToken); return instanceTcs.Task.Result; } From 022ca9f710e26752548ac0c5f401fedc91ad0096 Mon Sep 17 00:00:00 2001 From: Harold Iedema Date: Wed, 17 Apr 2024 17:23:22 +0200 Subject: [PATCH 14/24] [FIX] Remove the .git suffix from CS submodule (#1773) --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e265f5fc0..dd184b54e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/goatcorp/ImGuiScene [submodule "lib/FFXIVClientStructs"] path = lib/FFXIVClientStructs - url = https://github.com/aers/FFXIVClientStructs.git + url = https://github.com/aers/FFXIVClientStructs [submodule "lib/Nomade040-nmd"] path = lib/Nomade040-nmd url = https://github.com/Nomade040/nmd.git From ddc7667ab570ccd61fc1729ccac59db0f53ada68 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Thu, 18 Apr 2024 22:03:13 +0200 Subject: [PATCH 15/24] build: 9.1.0.6 --- Dalamud/Dalamud.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index f80962a9d..a69857de4 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -8,7 +8,7 @@ - 9.1.0.5 + 9.1.0.6 XIV Launcher addon framework $(DalamudVersion) $(DalamudVersion) From 1d75c8edda36d03fff27e33a11c6dfb10494bb3d Mon Sep 17 00:00:00 2001 From: goat Date: Thu, 18 Apr 2024 22:26:44 +0200 Subject: [PATCH 16/24] ioc: actually make early blocking services blocking again --- Dalamud/Dalamud.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index f9d2aff3c..8cc47ed33 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -68,10 +68,16 @@ internal sealed class Dalamud : IServiceType // Set up FFXIVClientStructs this.SetupClientStructsResolver(cacheDir); + + void KickoffGameThread() + { + Log.Verbose("=============== GAME THREAD KICKOFF ==============="); + Timings.Event("Game thread kickoff"); + NativeFunctions.SetEvent(mainThreadContinueEvent); + } if (!configuration.IsResumeGameAfterPluginLoad) { - NativeFunctions.SetEvent(mainThreadContinueEvent); ServiceManager.InitializeEarlyLoadableServices() .ContinueWith(t => { @@ -83,6 +89,8 @@ internal sealed class Dalamud : IServiceType "Dalamud failed to load all necessary services.\n\nThe game will continue, but you may not be able to use plugins.", "Dalamud", false); }); + + ServiceManager.BlockingResolved.ContinueWith(_ => KickoffGameThread()); } else { @@ -101,7 +109,7 @@ internal sealed class Dalamud : IServiceType if (faultedTasks.Any()) throw new AggregateException(faultedTasks); - NativeFunctions.SetEvent(mainThreadContinueEvent); + KickoffGameThread(); await Task.WhenAll(tasks); } @@ -112,7 +120,7 @@ internal sealed class Dalamud : IServiceType } finally { - NativeFunctions.SetEvent(mainThreadContinueEvent); + KickoffGameThread(); } }); } From 62863becd52a22d32dd7c115536dac7f81978a17 Mon Sep 17 00:00:00 2001 From: goat Date: Sat, 20 Apr 2024 02:30:21 +0200 Subject: [PATCH 17/24] add InstalledPluginState api10 todo --- Dalamud/Plugin/InstalledPluginState.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dalamud/Plugin/InstalledPluginState.cs b/Dalamud/Plugin/InstalledPluginState.cs index 322db3423..ba9e6f879 100644 --- a/Dalamud/Plugin/InstalledPluginState.cs +++ b/Dalamud/Plugin/InstalledPluginState.cs @@ -1,5 +1,8 @@ using System; +using Dalamud.Utility; + namespace Dalamud.Plugin; +[Api10ToDo("Refactor into an interface, add wrappers for OpenMainUI and OpenConfigUI")] public record InstalledPluginState(string Name, string InternalName, bool IsLoaded, Version Version); From 808f66edc62a433429a0b7f26dbbd8260c94cc32 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Sat, 20 Apr 2024 14:46:04 +0200 Subject: [PATCH 18/24] Expose DTR entries in public interface (#1778) * expose DTR entries in public interface * add IReadOnlyDtrBarEntry.UserHidden --- Dalamud/Game/Gui/Dtr/DtrBar.cs | 12 ++- Dalamud/Game/Gui/Dtr/DtrBarEntry.cs | 121 ++++++++++++++++++++++++---- Dalamud/Plugin/Services/IDtrBar.cs | 8 ++ 3 files changed, 123 insertions(+), 18 deletions(-) diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index b28c9a7d9..8a7982b07 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -73,13 +73,16 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar this.configuration.QueueSave(); } + /// + public IReadOnlyList Entries => this.entries; + /// public DtrBarEntry Get(string title, SeString? text = null) { if (this.entries.Any(x => x.Title == title) || this.newEntries.Any(x => x.Title == title)) throw new ArgumentException("An entry with the same title already exists."); - var entry = new DtrBarEntry(title, null); + var entry = new DtrBarEntry(this.configuration, title, null); entry.Text = text; // Add the entry to the end of the order list, if it's not there already. @@ -196,7 +199,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar foreach (var data in this.entries) { - var isHide = this.configuration.DtrIgnore!.Any(x => x == data.Title) || !data.Shown; + var isHide = data.UserHidden || !data.Shown; if (data is { Dirty: true, Added: true, Text: not null, TextNode: not null }) { @@ -499,6 +502,9 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar private readonly DtrBar dtrBarService = Service.Get(); private readonly Dictionary pluginEntries = new(); + + /// + public IReadOnlyList Entries => this.dtrBarService.Entries; /// void IInternalDisposableService.DisposeService() @@ -510,7 +516,7 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar this.pluginEntries.Clear(); } - + /// public DtrBarEntry Get(string title, SeString? text = null) { diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index f04e1427d..ef4ce062b 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -1,37 +1,115 @@ using System; +using System.Linq; +using Dalamud.Configuration.Internal; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Utility; + using FFXIVClientStructs.FFXIV.Component.GUI; namespace Dalamud.Game.Gui.Dtr; +/// +/// Interface representing a read-only entry in the server info bar. +/// +public interface IReadOnlyDtrBarEntry +{ + /// + /// Gets the title of this entry. + /// + public string Title { get; } + + /// + /// Gets a value indicating whether this entry has a click action. + /// + public bool HasClickAction { get; } + + /// + /// Gets the text of this entry. + /// + public SeString Text { get; } + + /// + /// Gets a tooltip to be shown when the user mouses over the dtr entry. + /// + public SeString Tooltip { get; } + + /// + /// Gets a value indicating whether this entry should be shown. + /// + public bool Shown { get; } + + /// + /// Gets a value indicating whether or not the user has hidden this entry from view through the Dalamud settings. + /// + public bool UserHidden { get; } + + /// + /// Triggers the click action of this entry. + /// + /// True, if a click action was registered and executed. + public bool TriggerClickAction(); +} + +/// +/// Interface representing an entry in the server info bar. +/// +public interface IDtrBarEntry : IReadOnlyDtrBarEntry +{ + /// + /// Gets or sets the text of this entry. + /// + public new SeString? Text { get; set; } + + /// + /// Gets or sets a tooltip to be shown when the user mouses over the dtr entry. + /// + public new SeString? Tooltip { get; set; } + + /// + /// Gets or sets a value indicating whether this entry is visible. + /// + public new bool Shown { get; set; } + + /// + /// Gets or sets a action to be invoked when the user clicks on the dtr entry. + /// + public Action? OnClick { get; set; } + + /// + /// Remove this entry from the bar. + /// You will need to re-acquire it from DtrBar to reuse it. + /// + public void Remove(); +} + /// /// Class representing an entry in the server info bar. /// -public sealed unsafe class DtrBarEntry : IDisposable +public sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry { + private readonly DalamudConfiguration configuration; + private bool shownBacking = true; private SeString? textBacking = null; /// /// Initializes a new instance of the class. /// + /// Dalamud configuration, used to check if the entry is hidden by the user. /// The title of the bar entry. /// The corresponding text node. - internal DtrBarEntry(string title, AtkTextNode* textNode) + internal DtrBarEntry(DalamudConfiguration configuration, string title, AtkTextNode* textNode) { + this.configuration = configuration; this.Title = title; this.TextNode = textNode; } - /// - /// Gets the title of this entry. - /// + /// public string Title { get; init; } - /// - /// Gets or sets the text of this entry. - /// + /// public SeString? Text { get => this.textBacking; @@ -41,10 +119,8 @@ public sealed unsafe class DtrBarEntry : IDisposable this.Dirty = true; } } - - /// - /// Gets or sets a tooltip to be shown when the user mouses over the dtr entry. - /// + + /// public SeString? Tooltip { get; set; } /// @@ -52,9 +128,10 @@ public sealed unsafe class DtrBarEntry : IDisposable /// public Action? OnClick { get; set; } - /// - /// Gets or sets a value indicating whether this entry is visible. - /// + /// + public bool HasClickAction => this.OnClick != null; + + /// public bool Shown { get => this.shownBacking; @@ -65,6 +142,10 @@ public sealed unsafe class DtrBarEntry : IDisposable } } + /// + [Api10ToDo("Maybe make this config scoped to internalname?")] + public bool UserHidden => this.configuration.DtrIgnore?.Any(x => x == this.Title) ?? false; + /// /// Gets or sets the internal text node of this entry. /// @@ -84,6 +165,16 @@ public sealed unsafe class DtrBarEntry : IDisposable /// Gets or sets a value indicating whether this entry has just been added. ///
internal bool Added { get; set; } = false; + + /// + public bool TriggerClickAction() + { + if (this.OnClick == null) + return false; + + this.OnClick.Invoke(); + return true; + } /// /// Remove this entry from the bar. diff --git a/Dalamud/Plugin/Services/IDtrBar.cs b/Dalamud/Plugin/Services/IDtrBar.cs index a5a750cf6..6019bb1e6 100644 --- a/Dalamud/Plugin/Services/IDtrBar.cs +++ b/Dalamud/Plugin/Services/IDtrBar.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using Dalamud.Game.Gui.Dtr; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Utility; namespace Dalamud.Plugin.Services; @@ -10,6 +12,11 @@ namespace Dalamud.Plugin.Services; /// public interface IDtrBar { + /// + /// Gets a read-only list of all DTR bar entries. + /// + public IReadOnlyList Entries { get; } + /// /// Get a DTR bar entry. /// This allows you to add your own text, and users to sort it. @@ -18,6 +25,7 @@ public interface IDtrBar /// The text the entry shows. /// The entry object used to update, hide and remove the entry. /// Thrown when an entry with the specified title exists. + [Api10ToDo("Return IDtrBarEntry instead of DtrBarEntry")] public DtrBarEntry Get(string title, SeString? text = null); /// From d4fc0da7f48b5340e1528e0a167f417ab66a3b1d Mon Sep 17 00:00:00 2001 From: goat Date: Sat, 20 Apr 2024 17:08:29 +0200 Subject: [PATCH 19/24] tsm: fix some flickering, don't override game cursor --- .../Internal/Windows/TitleScreenMenuWindow.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index 9c385a99c..0600ec001 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -142,6 +142,12 @@ internal class TitleScreenMenuWindow : Window, IDisposable var scale = ImGui.GetIO().FontGlobalScale; var entries = this.titleScreenMenu.Entries; + var hovered = ImGui.IsWindowHovered( + ImGuiHoveredFlags.RootAndChildWindows | + ImGuiHoveredFlags.AllowWhenBlockedByActiveItem); + + Service.Get().OverrideGameCursor = !hovered; + switch (this.state) { case State.Show: @@ -187,8 +193,11 @@ internal class TitleScreenMenuWindow : Window, IDisposable i++; } - if (!ImGui.IsWindowHovered(ImGuiHoveredFlags.RootAndChildWindows | - ImGuiHoveredFlags.AllowWhenBlockedByActiveItem)) + // Don't check for hover if we're in the middle of an animation, as it will cause flickering. + if (this.moveEasings.Any(x => !x.Value.IsDone)) + break; + + if (!hovered) { this.state = State.FadeOut; } From 3d2cec77c04466a50f23199477a12ef145814c44 Mon Sep 17 00:00:00 2001 From: goat Date: Sat, 20 Apr 2024 17:08:40 +0200 Subject: [PATCH 20/24] Revert "ioc: actually make early blocking services blocking again" This reverts commit 1d75c8edda36d03fff27e33a11c6dfb10494bb3d. To be reapplied in #1779. --- Dalamud/Dalamud.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 8cc47ed33..f9d2aff3c 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -68,16 +68,10 @@ internal sealed class Dalamud : IServiceType // Set up FFXIVClientStructs this.SetupClientStructsResolver(cacheDir); - - void KickoffGameThread() - { - Log.Verbose("=============== GAME THREAD KICKOFF ==============="); - Timings.Event("Game thread kickoff"); - NativeFunctions.SetEvent(mainThreadContinueEvent); - } if (!configuration.IsResumeGameAfterPluginLoad) { + NativeFunctions.SetEvent(mainThreadContinueEvent); ServiceManager.InitializeEarlyLoadableServices() .ContinueWith(t => { @@ -89,8 +83,6 @@ internal sealed class Dalamud : IServiceType "Dalamud failed to load all necessary services.\n\nThe game will continue, but you may not be able to use plugins.", "Dalamud", false); }); - - ServiceManager.BlockingResolved.ContinueWith(_ => KickoffGameThread()); } else { @@ -109,7 +101,7 @@ internal sealed class Dalamud : IServiceType if (faultedTasks.Any()) throw new AggregateException(faultedTasks); - KickoffGameThread(); + NativeFunctions.SetEvent(mainThreadContinueEvent); await Task.WhenAll(tasks); } @@ -120,7 +112,7 @@ internal sealed class Dalamud : IServiceType } finally { - KickoffGameThread(); + NativeFunctions.SetEvent(mainThreadContinueEvent); } }); } From 1f608c4aee321e315c0f5bd98fe4909066ef9868 Mon Sep 17 00:00:00 2001 From: bleatbot <106497096+bleatbot@users.noreply.github.com> Date: Sun, 21 Apr 2024 05:56:07 +0200 Subject: [PATCH 21/24] Update ClientStructs (#1771) Co-authored-by: github-actions[bot] --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 1ea76af5c..3d572e530 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 1ea76af5c796e0d84e23e07207cee9e78dd8bd02 +Subproject commit 3d572e5301fbcb3194d59d7c995db067eba5cc0b From 7280744def71ce6970a0f50bc54c7f3fd794f190 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Sun, 21 Apr 2024 15:22:25 +0200 Subject: [PATCH 22/24] Update Lumina to 3.17.0 (#1780) * Update Lumina to 3.17.0 * Dispose GameData in DataManager * Update Microsoft.Extensions.ObjectPool --- Dalamud.CorePlugin/Dalamud.CorePlugin.csproj | 2 +- Dalamud/Dalamud.csproj | 4 ++-- Dalamud/Data/DataManager.cs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index f9959a910..3e480154c 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -27,7 +27,7 @@ - + diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index a69857de4..38bc0b886 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -68,9 +68,9 @@ - + - + all diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 564b10fae..1640379c7 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -161,6 +161,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager void IInternalDisposableService.DisposeService() { this.luminaCancellationTokenSource.Cancel(); + this.GameData.Dispose(); } private class LauncherTroubleshootingInfo From 8a516ff6bc8e33fe715126a9bf73e8f21dba20e8 Mon Sep 17 00:00:00 2001 From: srkizer Date: Sun, 21 Apr 2024 22:52:46 +0900 Subject: [PATCH 23/24] Enable Lumina GameData StreamPool (#1783) --- Dalamud/Data/DataManager.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 1640379c7..d274c9e72 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.IO; using System.Threading; @@ -52,15 +51,12 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager DefaultExcelLanguage = this.Language.ToLumina(), }; - var processModule = Process.GetCurrentProcess().MainModule; - if (processModule != null) + this.GameData = new( + Path.Combine(Path.GetDirectoryName(Environment.ProcessPath)!, "sqpack"), + luminaOptions) { - this.GameData = new GameData(Path.Combine(Path.GetDirectoryName(processModule.FileName)!, "sqpack"), luminaOptions); - } - else - { - throw new Exception("Could not main module."); - } + StreamPool = new(), + }; Log.Information("Lumina is ready: {0}", this.GameData.DataPath); From 93adea0ac9d9dcdf7591e989c30ceda12626e21c Mon Sep 17 00:00:00 2001 From: goat Date: Sun, 21 Apr 2024 17:12:46 +0200 Subject: [PATCH 24/24] rethrow Lumina setup exception --- Dalamud/Data/DataManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index d274c9e72..2398cdb16 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -103,7 +103,8 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager } catch (Exception ex) { - Log.Error(ex, "Could not download data."); + Log.Error(ex, "Could not initialize Lumina"); + throw; } }