diff --git a/.github/workflows/rollup.yml b/.github/workflows/rollup.yml index 8bc9a3c51..8fe049ad7 100644 --- a/.github/workflows/rollup.yml +++ b/.github/workflows/rollup.yml @@ -1,8 +1,8 @@ name: Rollup changes to next version on: -# push: -# branches: -# - master + push: + branches: + - master workflow_dispatch: jobs: @@ -11,7 +11,7 @@ jobs: strategy: matrix: branches: - - net9 + - api14 defaults: run: diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index bd8d8e1d7..2235c5ade 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -257,7 +257,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar /// The resources to remove. internal void RemoveEntry(DtrBarEntry toRemove) { - this.RemoveNode(toRemove.TextNode); + this.RemoveNode(toRemove); if (toRemove.Storage != null) { @@ -378,12 +378,12 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar var isHide = !data.Shown || data.UserHidden; var node = data.TextNode; - var nodeHidden = !node->AtkResNode.IsVisible(); + var nodeHidden = !node->IsVisible(); if (!isHide) { if (nodeHidden) - node->AtkResNode.ToggleVisibility(true); + node->ToggleVisibility(true); if (data is { Added: true, Text: not null, TextNode: not null } && (data.Dirty || nodeHidden)) { @@ -397,27 +397,27 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar ushort w = 0, h = 0; node->GetTextDrawSize(&w, &h, node->NodeText.StringPtr); - node->AtkResNode.SetWidth(w); + node->SetWidth(w); } - var elementWidth = data.TextNode->AtkResNode.Width + this.configuration.DtrSpacing; + var elementWidth = data.TextNode->Width + this.configuration.DtrSpacing; if (this.configuration.DtrSwapDirection) { - data.TextNode->AtkResNode.SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2); + data.TextNode->SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2); runningXPos += elementWidth; } else { runningXPos -= elementWidth; - data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2); + data.TextNode->SetPositionFloat(runningXPos, 2); } } else if (!nodeHidden) { // If we want the node hidden, shift it up, to prevent collision conflicts - node->AtkResNode.SetYFloat(-collisionNode->Height * dtr->RootNode->ScaleX); - node->AtkResNode.ToggleVisibility(false); + node->SetYFloat(-collisionNode->Height * dtr->RootNode->ScaleX); + node->ToggleVisibility(false); } data.Dirty = false; @@ -516,8 +516,8 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar var node = data.TextNode = this.MakeNode(++this.runningNodeIds); - this.eventHandles.TryAdd(node->AtkResNode.NodeId, new List()); - this.eventHandles[node->AtkResNode.NodeId].AddRange(new List + this.eventHandles.TryAdd(node->NodeId, new List()); + this.eventHandles[node->NodeId].AddRange(new List { this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler), this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler), @@ -528,8 +528,8 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode; Log.Debug($"Found last sibling: {(ulong)lastChild:X}"); lastChild->PrevSiblingNode = (AtkResNode*)node; - node->AtkResNode.ParentNode = lastChild->ParentNode; - node->AtkResNode.NextSiblingNode = lastChild; + node->ParentNode = lastChild->ParentNode; + node->NextSiblingNode = lastChild; dtr->RootNode->ChildCount = (ushort)(dtr->RootNode->ChildCount + 1); Log.Debug("Set last sibling of DTR and updated child count"); @@ -542,22 +542,31 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar return true; } - private void RemoveNode(AtkTextNode* node) + private void RemoveNode(DtrBarEntry data) { var dtr = this.GetDtr(); + var node = data.TextNode; if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return; - this.eventHandles[node->AtkResNode.NodeId].ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle)); - this.eventHandles[node->AtkResNode.NodeId].Clear(); + if (this.eventHandles.TryGetValue(node->NodeId, out var eventHandles)) + { + eventHandles.ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle)); + eventHandles.Clear(); + } + else + { + Log.Warning("Could not find AtkResNode with NodeId {nodeId} in eventHandles", node->NodeId); + } - var tmpPrevNode = node->AtkResNode.PrevSiblingNode; - var tmpNextNode = node->AtkResNode.NextSiblingNode; + var tmpPrevNode = node->PrevSiblingNode; + var tmpNextNode = node->NextSiblingNode; // if (tmpNextNode != null) tmpNextNode->PrevSiblingNode = tmpPrevNode; if (tmpPrevNode != null) tmpPrevNode->NextSiblingNode = tmpNextNode; - node->AtkResNode.Destroy(true); + node->Destroy(true); + data.TextNode = null; dtr->RootNode->ChildCount = (ushort)(dtr->RootNode->ChildCount - 1); Log.Debug("Set last sibling of DTR and updated child count"); @@ -575,13 +584,13 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar return null; } - newTextNode->AtkResNode.NodeId = nodeId; - newTextNode->AtkResNode.Type = NodeType.Text; - newTextNode->AtkResNode.NodeFlags = NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.RespondToMouse | NodeFlags.HasCollision | NodeFlags.EmitsEvents; - newTextNode->AtkResNode.DrawFlags = 12; - newTextNode->AtkResNode.SetWidth(22); - newTextNode->AtkResNode.SetHeight(22); - newTextNode->AtkResNode.SetPositionFloat(-200, 2); + newTextNode->NodeId = nodeId; + newTextNode->Type = NodeType.Text; + newTextNode->NodeFlags = NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.RespondToMouse | NodeFlags.HasCollision | NodeFlags.EmitsEvents; + newTextNode->DrawFlags = 12; + newTextNode->SetWidth(22); + newTextNode->SetHeight(22); + newTextNode->SetPositionFloat(-200, 2); newTextNode->LineSpacing = 12; newTextNode->AlignmentFontType = 5; diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index 6fdc504ca..f5b7011fe 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -1,4 +1,6 @@ -using Dalamud.Configuration.Internal; +using System.Numerics; + +using Dalamud.Configuration.Internal; using Dalamud.Game.Addon.Events.EventDataTypes; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Internal.Types; @@ -48,6 +50,11 @@ public interface IReadOnlyDtrBarEntry /// Gets an action to be invoked when the user clicks on the dtr entry. /// public Action? OnClick { get; } + + /// + /// Gets the axis-aligned bounding box of this entry, in screen coordinates. + /// + public (Vector2 Min, Vector2 Max) ScreenBounds { get; } } /// @@ -146,6 +153,17 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry [Api13ToDo("Maybe make this config scoped to internal name?")] public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false; + /// + public (Vector2 Min, Vector2 Max) ScreenBounds + => this.TextNode switch + { + null => default, + var node => node->IsVisible() + ? (new(node->ScreenX, node->ScreenY), + new(node->ScreenX + node->GetWidth(), node->ScreenY + node->GetHeight())) + : default, + }; + /// /// Gets or sets the internal text node of this entry. /// diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 39a4e7e4b..8455ce164 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using Dalamud.Configuration; @@ -103,7 +104,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// /// Gets a value indicating whether auto-updates have already completed this session. /// - public bool IsAutoUpdateComplete => Service.Get().IsAutoUpdateComplete; + public bool IsAutoUpdateComplete => Service.GetNullable()?.IsAutoUpdateComplete ?? false; /// /// Gets the repository from which this plugin was installed. @@ -269,6 +270,30 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } + /// + /// Gets the plugin the given assembly is part of. + /// + /// The assembly to check. + /// The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined. + public IExposedPlugin? GetPlugin(Assembly assembly) + => AssemblyLoadContext.GetLoadContext(assembly) switch + { + null => null, + var context => this.GetPlugin(context), + }; + + /// + /// Gets the plugin that loads in the given context. + /// + /// The context to check. + /// The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined. + public IExposedPlugin? GetPlugin(AssemblyLoadContext context) + => Service.Get().InstalledPlugins.FirstOrDefault(p => p.LoadsIn(context)) switch + { + null => null, + var p => new ExposedPlugin(p), + }; + #region IPC /// diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index b8ab55450..e1dd34f87 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using Dalamud.Configuration; @@ -180,6 +182,20 @@ public interface IDalamudPluginInterface /// Returns false if the DalamudInterface was null. bool OpenDeveloperMenu(); + /// + /// Gets the plugin the given assembly is part of. + /// + /// The assembly to check. + /// The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined. + IExposedPlugin? GetPlugin(Assembly assembly); + + /// + /// Gets the plugin that loads in the given context. + /// + /// The context to check. + /// The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined. + IExposedPlugin? GetPlugin(AssemblyLoadContext context); + /// T GetOrCreateData(string tag, Func dataGenerator) where T : class; diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index da8ec8ff9..0197683ef 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; +using System.Runtime.Loader; using System.Threading; using System.Threading.Tasks; @@ -553,6 +554,14 @@ internal class LocalPlugin : IAsyncDisposable }); } + /// + /// Checks whether this plugin loads in the given load context. + /// + /// The load context to check. + /// Whether this plugin loads in the given load context. + public bool LoadsIn(AssemblyLoadContext context) + => this.loader?.LoadContext == context; + /// /// Save this plugin manifest. ///