diff --git a/Dalamud/Configuration/Internal/PluginTestingOptIn.cs b/Dalamud/Configuration/Internal/PluginTestingOptIn.cs
index f40740cf2..30198d563 100644
--- a/Dalamud/Configuration/Internal/PluginTestingOptIn.cs
+++ b/Dalamud/Configuration/Internal/PluginTestingOptIn.cs
@@ -1,6 +1,9 @@
namespace Dalamud.Configuration.Internal;
-public record PluginTestingOptIn
+///
+/// Represents a plugin that has opted in to testing.
+///
+internal record PluginTestingOptIn
{
///
/// Initializes a new instance of the class.
diff --git a/Dalamud/Console/ConsoleArgumentType.cs b/Dalamud/Console/ConsoleArgumentType.cs
new file mode 100644
index 000000000..4b4a74ce4
--- /dev/null
+++ b/Dalamud/Console/ConsoleArgumentType.cs
@@ -0,0 +1,27 @@
+namespace Dalamud.Console;
+
+///
+/// Possible console argument types.
+///
+internal enum ConsoleArgumentType
+{
+ ///
+ /// A regular string.
+ ///
+ String,
+
+ ///
+ /// A signed integer.
+ ///
+ Integer,
+
+ ///
+ /// A floating point value.
+ ///
+ Float,
+
+ ///
+ /// A boolean value.
+ ///
+ Bool,
+}
diff --git a/Dalamud/Console/ConsoleEntry.cs b/Dalamud/Console/ConsoleEntry.cs
new file mode 100644
index 000000000..93f250228
--- /dev/null
+++ b/Dalamud/Console/ConsoleEntry.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+
+namespace Dalamud.Console;
+
+///
+/// Interface representing an entry in the console.
+///
+public interface IConsoleEntry
+{
+ ///
+ /// Gets the name of the entry.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the description of the entry.
+ ///
+ public string Description { get; }
+}
+
+///
+/// Interface representing a command in the console.
+///
+public interface IConsoleCommand : IConsoleEntry
+{
+ ///
+ /// Execute this command.
+ ///
+ /// Arguments to invoke the entry with.
+ /// Whether or not execution succeeded.
+ public bool Invoke(IEnumerable
public class AutoTranslatePayload : Payload, ITextProvider
{
- private string text;
-
- [JsonProperty("group")]
- public uint Group { get; private set; }
-
- [JsonProperty("key")]
- public uint Key { get; private set; }
+ private string? text;
///
/// Initializes a new instance of the class.
@@ -44,6 +38,18 @@ public class AutoTranslatePayload : Payload, ITextProvider
internal AutoTranslatePayload()
{
}
+
+ ///
+ /// Gets the autotranslate group.
+ ///
+ [JsonProperty("group")]
+ public uint Group { get; private set; }
+
+ ///
+ /// Gets the autotranslate key.
+ ///
+ [JsonProperty("key")]
+ public uint Key { get; private set; }
///
public override PayloadType Type => PayloadType.AutoTranslateText;
diff --git a/Dalamud/GlobalSuppressions.cs b/Dalamud/GlobalSuppressions.cs
index 1b869295b..8a9d31b12 100644
--- a/Dalamud/GlobalSuppressions.cs
+++ b/Dalamud/GlobalSuppressions.cs
@@ -19,6 +19,8 @@ using System.Diagnostics.CodeAnalysis;
[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.")]
+[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "This would be nice, but a big refactor")]
+[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:FileNameMustMatchTypeName", Justification = "I don't like this one so much")]
// ImRAII stuff
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")]
diff --git a/Dalamud/Interface/ColorHelpers.cs b/Dalamud/Interface/ColorHelpers.cs
index 971e546bc..fe4c19329 100644
--- a/Dalamud/Interface/ColorHelpers.cs
+++ b/Dalamud/Interface/ColorHelpers.cs
@@ -10,17 +10,6 @@ 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.
///
@@ -316,4 +305,15 @@ public static class ColorHelpers
_ => color / 255.0f,
};
+
+ ///
+ /// 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);
}
diff --git a/Dalamud/Interface/Internal/DalamudCommands.cs b/Dalamud/Interface/Internal/DalamudCommands.cs
index 9e6f7cf32..18936687a 100644
--- a/Dalamud/Interface/Internal/DalamudCommands.cs
+++ b/Dalamud/Interface/Internal/DalamudCommands.cs
@@ -25,19 +25,19 @@ internal class DalamudCommands : IServiceType
{
commandManager.AddHandler("/xldclose", new CommandInfo(this.OnUnloadCommand)
{
- HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
+ HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon. For debug use only!"),
ShowInHelp = false,
});
commandManager.AddHandler("/xlkill", new CommandInfo(this.OnKillCommand)
{
- HelpMessage = "Kill the game.",
+ HelpMessage = "Kill the game. For debug use only!",
ShowInHelp = false,
});
commandManager.AddHandler("/xlrestart", new CommandInfo(this.OnRestartCommand)
{
- HelpMessage = "Restart the game.",
+ HelpMessage = "Restart the game. For debug use only!",
ShowInHelp = false,
});
@@ -80,13 +80,11 @@ internal class DalamudCommands : IServiceType
commandManager.AddHandler("/xlstats", new CommandInfo(this.OnTogglePluginStats)
{
HelpMessage = Loc.Localize("DalamudPluginStats", "Draw plugin statistics window"),
- ShowInHelp = false,
});
commandManager.AddHandler("/xlbranch", new CommandInfo(this.OnToggleBranchSwitcher)
{
- HelpMessage = Loc.Localize("DalamudBranchSwitcher", "Draw branch switcher"),
- ShowInHelp = false,
+ HelpMessage = Loc.Localize("DalamudBranchSwitcher", "Open the branch switcher"),
});
commandManager.AddHandler("/xldata", new CommandInfo(this.OnDebugDrawDataMenu)
@@ -97,8 +95,7 @@ internal class DalamudCommands : IServiceType
commandManager.AddHandler("/xllog", new CommandInfo(this.OnOpenLog)
{
- HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open dev log DEBUG"),
- ShowInHelp = false,
+ HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open the plugin log window/console"),
});
commandManager.AddHandler("/xlplugins", new CommandInfo(this.OnOpenInstallerCommand)
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 06795aa53..f9b675112 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using CheapLoc;
using Dalamud.Configuration.Internal;
+using Dalamud.Console;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Keys;
@@ -101,7 +102,8 @@ internal class DalamudInterface : IInternalDisposableService
Game.Framework framework,
ClientState clientState,
TitleScreenMenu titleScreenMenu,
- GameGui gameGui)
+ GameGui gameGui,
+ ConsoleManager consoleManager)
{
this.dalamud = dalamud;
this.configuration = configuration;
@@ -126,7 +128,8 @@ internal class DalamudInterface : IInternalDisposableService
fontAtlasFactory,
framework,
gameGui,
- titleScreenMenu) { IsOpen = false };
+ titleScreenMenu,
+ consoleManager) { IsOpen = false };
this.changelogWindow = new ChangelogWindow(
this.titleScreenMenuWindow,
fontAtlasFactory,
diff --git a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs
index 83d49456e..12c3a2960 100644
--- a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Text.RegularExpressions;
using Dalamud.Configuration.Internal;
+using Dalamud.Console;
using Dalamud.Game;
using Dalamud.Game.Command;
using Dalamud.Interface.Colors;
@@ -89,6 +90,14 @@ internal class ConsoleWindow : Window, IDisposable
SerilogEventSink.Instance.LogLine += this.OnLogLine;
Service.GetAsync().ContinueWith(r => r.Result.Update += this.FrameworkOnUpdate);
+
+ var cm = Service.Get();
+ cm.AddCommand("clear", "Clear the console log", () =>
+ {
+ this.QueueClear();
+ return true;
+ });
+ cm.AddAlias("clear", "cls");
this.Size = new Vector2(500, 400);
this.SizeCondition = ImGuiCond.FirstUseEver;
@@ -786,11 +795,6 @@ internal class ConsoleWindow : Window, IDisposable
{
try
{
- if (this.commandText.StartsWith('/'))
- {
- this.commandText = this.commandText[1..];
- }
-
this.historyPos = -1;
for (var i = this.history.Count - 1; i >= 0; i--)
{
@@ -809,7 +813,7 @@ internal class ConsoleWindow : Window, IDisposable
return;
}
- this.lastCmdSuccess = Service.Get().ProcessCommand("/" + this.commandText);
+ this.lastCmdSuccess = Service.Get().ProcessCommand(this.commandText);
this.commandText = string.Empty;
// TODO: Force scroll to bottom
@@ -838,15 +842,21 @@ internal class ConsoleWindow : Window, IDisposable
if (words.Length > 1)
return 0;
- // TODO: Improve this, add partial completion
+ // TODO: Improve this, add partial completion, arguments, description, etc.
// https://github.com/ocornut/imgui/blob/master/imgui_demo.cpp#L6443-L6484
- var candidates = Service.Get().Commands
- .Where(x => x.Key.Contains("/" + words[0]))
- .ToList();
- if (candidates.Count > 0)
+ var candidates = Service.Get().Entries
+ .Where(x => x.Key.StartsWith(words[0]))
+ .Select(x => x.Key);
+
+ candidates = candidates.Union(
+ Service.Get().Commands
+ .Where(x => x.Key.StartsWith(words[0])).Select(x => x.Key));
+
+ var enumerable = candidates as string[] ?? candidates.ToArray();
+ if (enumerable.Length != 0)
{
ptr.DeleteChars(0, ptr.BufTextLen);
- ptr.InsertChars(0, candidates[0].Key.Replace("/", string.Empty));
+ ptr.InsertChars(0, enumerable[0]);
}
break;
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs
index 41b0904df..69a440713 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs
@@ -17,17 +17,6 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
///
internal class NetworkMonitorWidget : IDataWindowWidget
{
-#pragma warning disable SA1313
- private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId)
-#pragma warning restore SA1313
- {
- public readonly IReadOnlyList Data = Array.Empty();
-
- public NetworkPacketData(NetworkMonitorWidget widget, ushort opCode, NetworkMessageDirection direction, uint sourceActorId, uint targetActorId, nint dataPtr)
- : this(opCode, direction, sourceActorId, targetActorId)
- => this.Data = MemoryHelper.Read(dataPtr, widget.GetSizeFromOpCode(opCode), false);
- }
-
private readonly ConcurrentQueue packets = new();
private bool trackNetwork;
@@ -214,4 +203,15 @@ internal class NetworkMonitorWidget : IDataWindowWidget
/// The filter should find opCodes by number (decimal and hex) and name, if existing.
private string OpCodeToString(ushort opCode)
=> $"{opCode}\0{opCode:X}";
+
+#pragma warning disable SA1313
+ private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId)
+#pragma warning restore SA1313
+ {
+ public readonly IReadOnlyList Data = Array.Empty();
+
+ public NetworkPacketData(NetworkMonitorWidget widget, ushort opCode, NetworkMessageDirection direction, uint sourceActorId, uint targetActorId, nint dataPtr)
+ : this(opCode, direction, sourceActorId, targetActorId)
+ => this.Data = MemoryHelper.Read(dataPtr, widget.GetSizeFromOpCode(opCode), false);
+ }
}
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index e404f805c..0d65a2873 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Configuration.Internal;
+using Dalamud.Console;
using Dalamud.Game.Command;
using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
@@ -365,10 +366,13 @@ internal class PluginInstallerWindow : Window, IDisposable
/// A value indicating whether to continue with the next task.
public bool DisplayErrorContinuation(Task task, object state)
{
- if (task.IsFaulted)
- {
- var errorModalMessage = state as string;
+ if (!task.IsFaulted && !task.IsCanceled)
+ return true;
+
+ var newErrorMessage = state as string;
+ if (task.Exception != null)
+ {
foreach (var ex in task.Exception.InnerExceptions)
{
if (ex is PluginException)
@@ -376,7 +380,7 @@ internal class PluginInstallerWindow : Window, IDisposable
Log.Error(ex, "Plugin installer threw an error");
#if DEBUG
if (!string.IsNullOrEmpty(ex.Message))
- errorModalMessage += $"\n\n{ex.Message}";
+ newErrorMessage += $"\n\n{ex.Message}";
#endif
}
else
@@ -384,17 +388,18 @@ internal class PluginInstallerWindow : Window, IDisposable
Log.Error(ex, "Plugin installer threw an unexpected error");
#if DEBUG
if (!string.IsNullOrEmpty(ex.Message))
- errorModalMessage += $"\n\n{ex.Message}";
+ newErrorMessage += $"\n\n{ex.Message}";
#endif
}
}
-
- this.ShowErrorModal(errorModalMessage);
-
- return false;
}
+
+ if (task.IsCanceled)
+ Log.Error("A task was cancelled");
- return true;
+ this.ShowErrorModal(newErrorMessage ?? "An unknown error occurred.");
+
+ return false;
}
private void SetOpenPage(PluginInstallerOpenKind kind)
@@ -2472,6 +2477,7 @@ internal class PluginInstallerWindow : Window, IDisposable
{
ImGuiHelpers.ScaledDummy(3);
ImGui.TextColored(ImGuiColors.DalamudGrey, $"WorkingPluginId: {plugin.EffectiveWorkingPluginId}");
+ ImGui.TextColored(ImGuiColors.DalamudGrey, $"Command prefix: {ConsoleManagerPluginUtil.GetSanitizedNamespaceName(plugin.InternalName)}");
ImGuiHelpers.ScaledDummy(3);
}
diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
index 0600ec001..00b8d0c39 100644
--- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Numerics;
using Dalamud.Configuration.Internal;
+using Dalamud.Console;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Gui;
@@ -41,6 +42,8 @@ internal class TitleScreenMenuWindow : Window, IDisposable
private readonly Dictionary shadeEasings = new();
private readonly Dictionary moveEasings = new();
private readonly Dictionary logoEasings = new();
+
+ private readonly IConsoleVariable showTsm;
private InOutCubic? fadeOutEasing;
@@ -55,7 +58,8 @@ internal class TitleScreenMenuWindow : Window, IDisposable
/// An instance of .
/// An instance of .
/// An instance of .
- /// An instance of .
+ /// An instance of .
+ /// An instance of .
public TitleScreenMenuWindow(
ClientState clientState,
DalamudConfiguration configuration,
@@ -63,12 +67,15 @@ internal class TitleScreenMenuWindow : Window, IDisposable
FontAtlasFactory fontAtlasFactory,
Framework framework,
GameGui gameGui,
- TitleScreenMenu titleScreenMenu)
+ TitleScreenMenu titleScreenMenu,
+ ConsoleManager consoleManager)
: base(
"TitleScreenMenuOverlay",
ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar |
ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus)
{
+ this.showTsm = consoleManager.AddVariable("dalamud.show_tsm", "Show the Title Screen Menu", true);
+
this.clientState = clientState;
this.configuration = configuration;
this.gameGui = gameGui;
@@ -136,7 +143,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
///
public override void Draw()
{
- if (!this.AllowDrawing)
+ if (!this.AllowDrawing || !this.showTsm.Value)
return;
var scale = ImGui.GetIO().FontGlobalScale;
diff --git a/Dalamud/Interface/Utility/ImVectorWrapper.cs b/Dalamud/Interface/Utility/ImVectorWrapper.cs
index 5ba1aec2f..f350a6436 100644
--- a/Dalamud/Interface/Utility/ImVectorWrapper.cs
+++ b/Dalamud/Interface/Utility/ImVectorWrapper.cs
@@ -10,166 +10,6 @@ using JetBrains.Annotations;
namespace Dalamud.Interface.Utility;
-///
-/// Utility methods for .
-///
-public static class ImVectorWrapper
-{
- ///
- /// Creates a new instance of the struct, initialized with
- /// .
- /// You must call after use.
- ///
- /// The item type.
- /// The initial data.
- /// The destroyer function to call on item removal.
- /// The minimum capacity of the new vector.
- /// The new wrapped vector, that has to be disposed after use.
- public static ImVectorWrapper CreateFromEnumerable(
- IEnumerable sourceEnumerable,
- ImVectorWrapper.ImGuiNativeDestroyDelegate? destroyer = null,
- int minCapacity = 0)
- where T : unmanaged
- {
- var res = new ImVectorWrapper(0, destroyer);
- try
- {
- switch (sourceEnumerable)
- {
- case T[] c:
- res.SetCapacity(Math.Max(minCapacity, c.Length + 1));
- res.LengthUnsafe = c.Length;
- c.AsSpan().CopyTo(res.DataSpan);
- break;
- case ICollection c:
- res.SetCapacity(Math.Max(minCapacity, c.Count + 1));
- res.AddRange(sourceEnumerable);
- break;
- case ICollection c:
- res.SetCapacity(Math.Max(minCapacity, c.Count + 1));
- res.AddRange(sourceEnumerable);
- break;
- default:
- res.SetCapacity(minCapacity);
- res.AddRange(sourceEnumerable);
- res.EnsureCapacity(res.LengthUnsafe + 1);
- break;
- }
-
- // Null termination
- Debug.Assert(res.LengthUnsafe < res.CapacityUnsafe, "Capacity must be more than source length + 1");
- res.StorageSpan[res.LengthUnsafe] = default;
-
- return res;
- }
- catch
- {
- res.Dispose();
- throw;
- }
- }
-
- ///
- /// Creates a new instance of the struct, initialized with
- /// .
- /// You must call after use.
- ///
- /// The item type.
- /// The initial data.
- /// The destroyer function to call on item removal.
- /// The minimum capacity of the new vector.
- /// The new wrapped vector, that has to be disposed after use.
- public static ImVectorWrapper CreateFromSpan(
- ReadOnlySpan sourceSpan,
- ImVectorWrapper.ImGuiNativeDestroyDelegate? destroyer = null,
- int minCapacity = 0)
- where T : unmanaged
- {
- var res = new ImVectorWrapper(Math.Max(minCapacity, sourceSpan.Length + 1), destroyer);
- try
- {
- res.LengthUnsafe = sourceSpan.Length;
- sourceSpan.CopyTo(res.DataSpan);
-
- // Null termination
- Debug.Assert(res.LengthUnsafe < res.CapacityUnsafe, "Capacity must be more than source length + 1");
- res.StorageSpan[res.LengthUnsafe] = default;
- return res;
- }
- catch
- {
- res.Dispose();
- throw;
- }
- }
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper ConfigDataWrapped(this ImFontAtlasPtr obj) =>
- obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->ConfigData, ImGuiNative.ImFontConfig_destroy);
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper FontsWrapped(this ImFontAtlasPtr obj) =>
- obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->Fonts, x => ImGuiNative.ImFont_destroy(x->NativePtr));
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper TexturesWrapped(this ImFontAtlasPtr obj) =>
- obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->Textures);
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper GlyphsWrapped(this ImFontPtr obj) =>
- obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->Glyphs);
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper IndexedHotDataWrapped(this ImFontPtr obj)
- => obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->IndexedHotData);
-
- ///
- /// Wraps into a .
- /// This does not need to be disposed.
- ///
- /// The owner object.
- /// The wrapped vector.
- public static unsafe ImVectorWrapper IndexLookupWrapped(this ImFontPtr obj) =>
- obj.NativePtr is null
- ? throw new NullReferenceException()
- : new(&obj.NativePtr->IndexLookup);
-}
-
///
/// Wrapper for ImVector.
///
@@ -744,3 +584,163 @@ public unsafe struct ImVectorWrapper : IList, IList, IReadOnlyList, IDi
private int EnsureIndex(int i) => i >= 0 && i < this.LengthUnsafe ? i : throw new IndexOutOfRangeException();
}
+
+///
+/// Utility methods for .
+///
+public static class ImVectorWrapper
+{
+ ///
+ /// Creates a new instance of the struct, initialized with
+ /// .
+ /// You must call after use.
+ ///
+ /// The item type.
+ /// The initial data.
+ /// The destroyer function to call on item removal.
+ /// The minimum capacity of the new vector.
+ /// The new wrapped vector, that has to be disposed after use.
+ public static ImVectorWrapper CreateFromEnumerable(
+ IEnumerable sourceEnumerable,
+ ImVectorWrapper.ImGuiNativeDestroyDelegate? destroyer = null,
+ int minCapacity = 0)
+ where T : unmanaged
+ {
+ var res = new ImVectorWrapper(0, destroyer);
+ try
+ {
+ switch (sourceEnumerable)
+ {
+ case T[] c:
+ res.SetCapacity(Math.Max(minCapacity, c.Length + 1));
+ res.LengthUnsafe = c.Length;
+ c.AsSpan().CopyTo(res.DataSpan);
+ break;
+ case ICollection c:
+ res.SetCapacity(Math.Max(minCapacity, c.Count + 1));
+ res.AddRange(sourceEnumerable);
+ break;
+ case ICollection c:
+ res.SetCapacity(Math.Max(minCapacity, c.Count + 1));
+ res.AddRange(sourceEnumerable);
+ break;
+ default:
+ res.SetCapacity(minCapacity);
+ res.AddRange(sourceEnumerable);
+ res.EnsureCapacity(res.LengthUnsafe + 1);
+ break;
+ }
+
+ // Null termination
+ Debug.Assert(res.LengthUnsafe < res.CapacityUnsafe, "Capacity must be more than source length + 1");
+ res.StorageSpan[res.LengthUnsafe] = default;
+
+ return res;
+ }
+ catch
+ {
+ res.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a new instance of the struct, initialized with
+ /// .
+ /// You must call after use.
+ ///
+ /// The item type.
+ /// The initial data.
+ /// The destroyer function to call on item removal.
+ /// The minimum capacity of the new vector.
+ /// The new wrapped vector, that has to be disposed after use.
+ public static ImVectorWrapper CreateFromSpan(
+ ReadOnlySpan sourceSpan,
+ ImVectorWrapper.ImGuiNativeDestroyDelegate? destroyer = null,
+ int minCapacity = 0)
+ where T : unmanaged
+ {
+ var res = new ImVectorWrapper(Math.Max(minCapacity, sourceSpan.Length + 1), destroyer);
+ try
+ {
+ res.LengthUnsafe = sourceSpan.Length;
+ sourceSpan.CopyTo(res.DataSpan);
+
+ // Null termination
+ Debug.Assert(res.LengthUnsafe < res.CapacityUnsafe, "Capacity must be more than source length + 1");
+ res.StorageSpan[res.LengthUnsafe] = default;
+ return res;
+ }
+ catch
+ {
+ res.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper ConfigDataWrapped(this ImFontAtlasPtr obj) =>
+ obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->ConfigData, ImGuiNative.ImFontConfig_destroy);
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper FontsWrapped(this ImFontAtlasPtr obj) =>
+ obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->Fonts, x => ImGuiNative.ImFont_destroy(x->NativePtr));
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper TexturesWrapped(this ImFontAtlasPtr obj) =>
+ obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->Textures);
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper GlyphsWrapped(this ImFontPtr obj) =>
+ obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->Glyphs);
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper IndexedHotDataWrapped(this ImFontPtr obj)
+ => obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->IndexedHotData);
+
+ ///
+ /// Wraps into a .
+ /// This does not need to be disposed.
+ ///
+ /// The owner object.
+ /// The wrapped vector.
+ public static unsafe ImVectorWrapper IndexLookupWrapped(this ImFontPtr obj) =>
+ obj.NativePtr is null
+ ? throw new NullReferenceException()
+ : new(&obj.NativePtr->IndexLookup);
+}
diff --git a/Dalamud/Logging/Internal/ModuleLog.cs b/Dalamud/Logging/Internal/ModuleLog.cs
index 1fe955294..bcbb6e2b1 100644
--- a/Dalamud/Logging/Internal/ModuleLog.cs
+++ b/Dalamud/Logging/Internal/ModuleLog.cs
@@ -141,8 +141,15 @@ public class ModuleLog
public void Fatal(Exception? exception, string messageTemplate, params object?[] values)
=> this.WriteLog(LogEventLevel.Fatal, messageTemplate, exception, values);
+ ///
+ /// Log a templated message to the in-game debug log.
+ ///
+ /// The log level to log with.
+ /// The message template to log.
+ /// The exception to log.
+ /// Values to log.
[MessageTemplateFormatMethod("messageTemplate")]
- private void WriteLog(
+ public void WriteLog(
LogEventLevel level, string messageTemplate, Exception? exception = null, params object?[] values)
{
// FIXME: Eventually, the `pluginName` tag should be removed from here and moved over to the actual log
diff --git a/Dalamud/Plugin/InstalledPluginState.cs b/Dalamud/Plugin/InstalledPluginState.cs
index 79b9de1ee..3cdd03956 100644
--- a/Dalamud/Plugin/InstalledPluginState.cs
+++ b/Dalamud/Plugin/InstalledPluginState.cs
@@ -2,5 +2,12 @@
namespace Dalamud.Plugin;
+///
+/// State of an installed plugin.
+///
+/// The name of the plugin.
+/// The internal name of the plugin.
+/// Whether or not the plugin is loaded.
+/// The version of the plugin.
[Api10ToDo("Refactor into an interface, add wrappers for OpenMainUI and OpenConfigUI")]
public record InstalledPluginState(string Name, string InternalName, bool IsLoaded, Version Version);
diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
index 6c3ca8c0c..7d9b79e9b 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
@@ -178,7 +178,6 @@ internal class LocalPlugin : IDisposable
/// Gets a value indicating whether or not this plugin is orphaned(belongs to a repo) or not.
///
public bool IsOrphaned => !this.IsDev &&
- !this.manifest.InstalledFromUrl.IsNullOrEmpty() && // TODO(api8): Remove this, all plugins will have a proper flag
this.GetSourceRepository() == null;
///
@@ -413,28 +412,39 @@ internal class LocalPlugin : IDisposable
this.ServiceScope = ioc.GetScope();
this.ServiceScope.RegisterPrivateScopes(this); // Add this LocalPlugin as a private scope, so services can get it
- if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1)
+ try
{
- this.instance = await framework.RunOnFrameworkThread(
- () => this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!)) as IDalamudPlugin;
+ if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1)
+ {
+ this.instance = await framework.RunOnFrameworkThread(
+ () => this.ServiceScope.CreateAsync(
+ this.pluginType!,
+ this.DalamudInterface!)) as IDalamudPlugin;
+ }
+ else
+ {
+ this.instance =
+ await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin;
+ }
}
- else
+ catch (Exception ex)
{
- this.instance =
- await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin;
+ Log.Error(ex, "Exception in plugin constructor");
+ this.instance = null;
}
if (this.instance == null)
{
this.State = PluginState.LoadError;
- this.DalamudInterface.DisposeInternal();
+ this.UnloadAndDisposeState();
+
Log.Error(
- $"Error while loading {this.Name}, failed to bind and call the plugin constructor");
+ "Error while loading {PluginName}, failed to bind and call the plugin constructor", this.InternalName);
return;
}
this.State = PluginState.Loaded;
- Log.Information($"Finished loading {this.DllFile.Name}");
+ Log.Information("Finished loading {PluginName}", this.InternalName);
}
catch (Exception ex)
{
@@ -444,7 +454,7 @@ internal class LocalPlugin : IDisposable
if (ex is PluginPreconditionFailedException)
Log.Warning(ex.Message);
else
- Log.Error(ex, $"Error while loading {this.Name}");
+ Log.Error(ex, "Error while loading {PluginName}", this.InternalName);
throw;
}
@@ -499,15 +509,7 @@ internal class LocalPlugin : IDisposable
await framework.RunOnFrameworkThread(() => this.instance?.Dispose());
this.instance = null;
-
- this.DalamudInterface?.DisposeInternal();
- this.DalamudInterface = null;
-
- this.ServiceScope?.Dispose();
- this.ServiceScope = null;
-
- this.pluginType = null;
- this.pluginAssembly = null;
+ this.UnloadAndDisposeState();
if (!reloading)
{
@@ -676,4 +678,19 @@ internal class LocalPlugin : IDisposable
throw new InvalidPluginException(this.DllFile);
}
}
+
+ private void UnloadAndDisposeState()
+ {
+ if (this.instance != null)
+ throw new InvalidOperationException("Plugin instance should be disposed at this point");
+
+ this.DalamudInterface?.DisposeInternal();
+ this.DalamudInterface = null;
+
+ this.ServiceScope?.Dispose();
+ this.ServiceScope = null;
+
+ this.pluginType = null;
+ this.pluginAssembly = null;
+ }
}
diff --git a/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs
index 31469a914..dc05409b0 100644
--- a/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs
+++ b/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs
@@ -36,7 +36,7 @@ internal record LocalPluginManifest : PluginManifest, ILocalPluginManifest
///
/// Gets a value indicating whether this manifest is associated with a plugin that was installed from a third party
- /// repo. Unless the manifest has been manually modified, this is determined by the InstalledFromUrl being null.
+ /// repo.
///
public bool IsThirdParty => !this.InstalledFromUrl.IsNullOrEmpty() && this.InstalledFromUrl != SpecialPluginSource.MainRepo;
diff --git a/Dalamud/Plugin/Internal/Types/PluginPatchData.cs b/Dalamud/Plugin/Internal/Types/PluginPatchData.cs
deleted file mode 100644
index f713e4df0..000000000
--- a/Dalamud/Plugin/Internal/Types/PluginPatchData.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.IO;
-
-namespace Dalamud.Plugin.Internal.Types;
-
-internal record PluginPatchData
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// DLL file being loaded.
- public PluginPatchData(FileSystemInfo dllFile)
- {
- this.Location = dllFile.FullName;
- this.CodeBase = new Uri(dllFile.FullName).AbsoluteUri;
- }
-
- ///
- /// Gets simulated Assembly.Location output.
- ///
- public string Location { get; }
-
- ///
- /// Gets simulated Assembly.CodeBase output.
- ///
- public string CodeBase { get; }
-}
diff --git a/Dalamud/Plugin/Services/IConsole.cs b/Dalamud/Plugin/Services/IConsole.cs
new file mode 100644
index 000000000..0b6832efb
--- /dev/null
+++ b/Dalamud/Plugin/Services/IConsole.cs
@@ -0,0 +1,130 @@
+using System.Diagnostics.CodeAnalysis;
+
+using Dalamud.Console;
+
+namespace Dalamud.Plugin.Services;
+
+///
+/// Provides functions to register console commands and variables.
+///
+[Experimental("Dalamud001")]
+public interface IConsole
+{
+ ///
+ /// Gets this plugin's namespace prefix, derived off its internal name.
+ /// This is the prefix that all commands and variables registered by this plugin will have.
+ /// If the internal name is "SamplePlugin", the prefix will be "sampleplugin.".
+ ///
+ public string Prefix { get; }
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The added command.
+ public IConsoleCommand AddCommand(string name, string description, Func func);
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The first argument to the command.
+ /// The added command.
+ public IConsoleCommand AddCommand(string name, string description, Func func);
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The first argument to the command.
+ /// The second argument to the command.
+ /// The added command.
+ public IConsoleCommand AddCommand(string name, string description, Func func);
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The first argument to the command.
+ /// The second argument to the command.
+ /// The third argument to the command.
+ /// The added command.
+ public IConsoleCommand AddCommand(string name, string description, Func func);
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The first argument to the command.
+ /// The second argument to the command.
+ /// The third argument to the command.
+ /// The fourth argument to the command.
+ /// The added command.
+ public IConsoleCommand AddCommand(
+ string name, string description, Func func);
+
+ ///
+ /// Add a command to the console.
+ ///
+ /// The name of the command.
+ /// A description of the command.
+ /// Function to invoke when the command has been called. Must return a indicating success.
+ /// The first argument to the command.
+ /// The second argument to the command.
+ /// The third argument to the command.
+ /// The fourth argument to the command.
+ /// The fifth argument to the command.
+ /// The added command.
+ public IConsoleCommand AddCommand(
+ string name, string description, Func func);
+
+ ///
+ /// Add a variable to the console.
+ ///
+ /// The name of the variable.
+ /// A description of the variable.
+ /// The default value of the variable.
+ /// The type of the variable.
+ /// The added variable.
+ public IConsoleVariable AddVariable(string name, string description, T defaultValue);
+
+ ///
+ /// Add an alias to a console entry.
+ ///
+ /// The name of the entry to add an alias for.
+ /// The alias to use.
+ /// The added alias.
+ public IConsoleEntry AddAlias(string name, string alias);
+
+ ///
+ /// Get the value of a variable.
+ ///
+ /// The name of the variable.
+ /// The type of the variable.
+ /// The value of the variable.
+ public T GetVariable(string name);
+
+ ///
+ /// Set the value of a variable.
+ ///
+ /// The name of the variable.
+ /// The value to set.
+ /// The type of the value to set.
+ public void SetVariable(string name, T value);
+
+ ///
+ /// Remove an entry from the console.
+ ///
+ /// The entry to remove.
+ public void RemoveEntry(IConsoleEntry entry);
+}
diff --git a/Dalamud/Utility/FuzzyMatcher.cs b/Dalamud/Utility/FuzzyMatcher.cs
index 9ac71d8bb..03723da89 100644
--- a/Dalamud/Utility/FuzzyMatcher.cs
+++ b/Dalamud/Utility/FuzzyMatcher.cs
@@ -1,14 +1,20 @@
#define BORDER_MATCHING
-namespace Dalamud.Utility;
-
-using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+namespace Dalamud.Utility;
+
#pragma warning disable SA1600
#pragma warning disable SA1602
+internal enum MatchMode
+{
+ Simple,
+ Fuzzy,
+ FuzzyParts,
+}
+
internal readonly ref struct FuzzyMatcher
{
private static readonly (int, int)[] EmptySegArray = Array.Empty<(int, int)>();
@@ -272,12 +278,5 @@ internal readonly ref struct FuzzyMatcher
}
}
-internal enum MatchMode
-{
- Simple,
- Fuzzy,
- FuzzyParts,
-}
-
#pragma warning restore SA1600
#pragma warning restore SA1602