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.
///