From 642e8bf6d34a800608e2c462b3413763de33d316 Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Mon, 10 Apr 2023 19:17:00 +0200
Subject: [PATCH 01/68] Profiles (#1178)
---
.../Internal/DalamudConfiguration.cs | 16 +
.../Internal/PluginCategoryManager.cs | 10 +-
.../PluginInstaller/PluginInstallerWindow.cs | 211 ++++++---
.../PluginInstaller/ProfileManagerWidget.cs | 399 ++++++++++++++++++
.../Settings/Tabs/SettingsTabExperimental.cs | 8 +
.../Windows/StyleEditor/StyleEditorWindow.cs | 16 +-
Dalamud/Plugin/Internal/PluginManager.cs | 30 +-
Dalamud/Plugin/Internal/Profiles/Profile.cs | 214 ++++++++++
.../Profiles/ProfileCommandHandler.cs | 164 +++++++
.../Internal/Profiles/ProfileManager.cs | 356 ++++++++++++++++
.../Plugin/Internal/Profiles/ProfileModel.cs | 40 ++
.../Internal/Profiles/ProfileModelV1.cs | 27 ++
.../Internal/Profiles/ProfilePluginEntry.cs | 14 +
Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 37 +-
.../Internal/Types/LocalPluginManifest.cs | 1 +
.../Plugin/Internal/Types/PluginManifest.cs | 6 +
Dalamud/Plugin/PluginLoadReason.cs | 2 +
Dalamud/Utility/Util.cs | 15 +
18 files changed, 1492 insertions(+), 74 deletions(-)
create mode 100644 Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/Profile.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
create mode 100644 Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index f6cf88d90..89396bd66 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -6,6 +6,7 @@ using System.Linq;
using Dalamud.Game.Text;
using Dalamud.Interface.Style;
+using Dalamud.Plugin.Internal.Profiles;
using Dalamud.Utility;
using Newtonsoft.Json;
using Serilog;
@@ -266,6 +267,21 @@ internal sealed class DalamudConfiguration : IServiceType
///
public string ChosenStyle { get; set; } = "Dalamud Standard";
+ ///
+ /// Gets or sets a list of saved plugin profiles.
+ ///
+ public List? SavedProfiles { get; set; }
+
+ ///
+ /// Gets or sets the default plugin profile.
+ ///
+ public ProfileModel? DefaultProfile { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether or not profiles are enabled.
+ ///
+ public bool ProfilesEnabled { get; set; } = false;
+
///
/// Gets or sets a value indicating whether or not Dalamud RMT filtering should be disabled.
///
diff --git a/Dalamud/Interface/Internal/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs
index 06e306c50..2af5d2354 100644
--- a/Dalamud/Interface/Internal/PluginCategoryManager.cs
+++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs
@@ -28,6 +28,7 @@ internal class PluginCategoryManager
new(11, "special.devIconTester", () => Locs.Category_IconTester),
new(12, "special.dalamud", () => Locs.Category_Dalamud),
new(13, "special.plugins", () => Locs.Category_Plugins),
+ new(14, "special.profiles", () => Locs.Category_PluginProfiles, CategoryInfo.AppearCondition.ProfilesEnabled),
new(FirstTagBasedCategoryId + 0, "other", () => Locs.Category_Other),
new(FirstTagBasedCategoryId + 1, "jobs", () => Locs.Category_Jobs),
new(FirstTagBasedCategoryId + 2, "ui", () => Locs.Category_UI),
@@ -43,7 +44,7 @@ internal class PluginCategoryManager
private GroupInfo[] groupList =
{
new(GroupKind.DevTools, () => Locs.Group_DevTools, 10, 11),
- new(GroupKind.Installed, () => Locs.Group_Installed, 0, 1),
+ new(GroupKind.Installed, () => Locs.Group_Installed, 0, 1, 14),
new(GroupKind.Available, () => Locs.Group_Available, 0),
new(GroupKind.Changelog, () => Locs.Group_Changelog, 0, 12, 13),
@@ -352,6 +353,11 @@ internal class PluginCategoryManager
/// Check if plugin testing is enabled.
///
DoPluginTest,
+
+ ///
+ /// Check if plugin profiles are enabled.
+ ///
+ ProfilesEnabled,
}
///
@@ -429,6 +435,8 @@ internal class PluginCategoryManager
public static string Category_DevInstalled => Loc.Localize("InstallerInstalledDevPlugins", "Installed Dev Plugins");
public static string Category_IconTester => "Image/Icon Tester";
+
+ public static string Category_PluginProfiles => Loc.Localize("InstallerCategoryPluginProfiles", "Plugin Profiles");
public static string Category_Other => Loc.Localize("InstallerCategoryOther", "Other");
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index f6dad53bd..73ef523e9 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -21,6 +21,7 @@ using Dalamud.Logging.Internal;
using Dalamud.Plugin;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Exceptions;
+using Dalamud.Plugin.Internal.Profiles;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Support;
using Dalamud.Utility;
@@ -48,6 +49,8 @@ internal class PluginInstallerWindow : Window, IDisposable
private readonly object listLock = new();
+ private readonly ProfileManagerWidget profileManagerWidget;
+
private DalamudChangelogManager? dalamudChangelogManager;
private Task? dalamudChangelogRefreshTask;
private CancellationTokenSource? dalamudChangelogRefreshTaskCts;
@@ -149,6 +152,8 @@ internal class PluginInstallerWindow : Window, IDisposable
});
this.timeLoaded = DateTime.Now;
+
+ this.profileManagerWidget = new(this);
}
private enum OperationStatus
@@ -167,6 +172,7 @@ internal class PluginInstallerWindow : Window, IDisposable
UpdatingAll,
Installing,
Manager,
+ ProfilesLoading,
}
private enum PluginSortKind
@@ -213,6 +219,8 @@ internal class PluginInstallerWindow : Window, IDisposable
this.updatePluginCount = 0;
this.updatedPlugins = null;
}
+
+ this.profileManagerWidget.Reset();
}
///
@@ -285,17 +293,56 @@ internal class PluginInstallerWindow : Window, IDisposable
this.searchText = text;
}
+ ///
+ /// Start a plugin install and handle errors visually.
+ ///
+ /// The manifest to install.
+ /// Install the testing version.
+ public void StartInstall(RemotePluginManifest manifest, bool useTesting)
+ {
+ var pluginManager = Service.Get();
+ var notifications = Service.Get();
+
+ this.installStatus = OperationStatus.InProgress;
+ this.loadingIndicatorKind = LoadingIndicatorKind.Installing;
+
+ Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting || manifest.IsTestingExclusive, PluginLoadReason.Installer))
+ .ContinueWith(task =>
+ {
+ // There is no need to set as Complete for an individual plugin installation
+ this.installStatus = OperationStatus.Idle;
+ if (this.DisplayErrorContinuation(task, Locs.ErrorModal_InstallFail(manifest.Name)))
+ {
+ // Fine as long as we aren't in an error state
+ if (task.Result.State is PluginState.Loaded or PluginState.Unloaded)
+ {
+ notifications.AddNotification(Locs.Notifications_PluginInstalled(manifest.Name), Locs.Notifications_PluginInstalledTitle, NotificationType.Success);
+ }
+ else
+ {
+ notifications.AddNotification(Locs.Notifications_PluginNotInstalled(manifest.Name), Locs.Notifications_PluginNotInstalledTitle, NotificationType.Error);
+ this.ShowErrorModal(Locs.ErrorModal_InstallFail(manifest.Name));
+ }
+ }
+ });
+ }
+
private void DrawProgressOverlay()
{
var pluginManager = Service.Get();
+ var profileManager = Service.Get();
var isWaitingManager = !pluginManager.PluginsReady ||
!pluginManager.ReposReady;
+ var isWaitingProfiles = profileManager.IsBusy;
+
var isLoading = this.AnyOperationInProgress ||
- isWaitingManager;
+ isWaitingManager || isWaitingProfiles;
if (isWaitingManager)
this.loadingIndicatorKind = LoadingIndicatorKind.Manager;
+ else if (isWaitingProfiles)
+ this.loadingIndicatorKind = LoadingIndicatorKind.ProfilesLoading;
if (!isLoading)
return;
@@ -379,6 +426,9 @@ internal class PluginInstallerWindow : Window, IDisposable
}
}
+ break;
+ case LoadingIndicatorKind.ProfilesLoading:
+ ImGuiHelpers.CenteredText("Profiles are being applied...");
break;
default:
throw new ArgumentOutOfRangeException();
@@ -387,9 +437,7 @@ internal class PluginInstallerWindow : Window, IDisposable
if (DateTime.Now - this.timeLoaded > TimeSpan.FromSeconds(90) && !pluginManager.PluginsReady)
{
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
- ImGuiHelpers.CenteredText("This is embarrassing, but...");
- ImGuiHelpers.CenteredText("one of your plugins may be blocking the installer.");
- ImGuiHelpers.CenteredText("You should tell us about this, please keep this window open.");
+ ImGuiHelpers.CenteredText("One of your plugins may be blocking the installer.");
ImGui.PopStyleColor();
}
@@ -1111,6 +1159,10 @@ internal class PluginInstallerWindow : Window, IDisposable
if (!Service.Get().DoPluginTest)
continue;
break;
+ case PluginCategoryManager.CategoryInfo.AppearCondition.ProfilesEnabled:
+ if (!Service.Get().ProfilesEnabled)
+ continue;
+ break;
default:
throw new ArgumentOutOfRangeException();
}
@@ -1216,6 +1268,10 @@ internal class PluginInstallerWindow : Window, IDisposable
case 1:
this.DrawInstalledPluginList(true);
break;
+
+ case 2:
+ this.profileManagerWidget.Draw();
+ break;
}
break;
@@ -1555,7 +1611,7 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGui.SetCursorPos(startCursor);
- var pluginDisabled = plugin is { IsDisabled: true };
+ var pluginDisabled = plugin is { IsWantedByAnyProfile: false };
var iconSize = ImGuiHelpers.ScaledVector2(64, 64);
var cursorBeforeImage = ImGui.GetCursorPos();
@@ -1853,27 +1909,7 @@ internal class PluginInstallerWindow : Window, IDisposable
var buttonText = Locs.PluginButton_InstallVersion(versionString);
if (ImGui.Button($"{buttonText}##{buttonText}{index}"))
{
- this.installStatus = OperationStatus.InProgress;
- this.loadingIndicatorKind = LoadingIndicatorKind.Installing;
-
- Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting || manifest.IsTestingExclusive, PluginLoadReason.Installer))
- .ContinueWith(task =>
- {
- // There is no need to set as Complete for an individual plugin installation
- this.installStatus = OperationStatus.Idle;
- if (this.DisplayErrorContinuation(task, Locs.ErrorModal_InstallFail(manifest.Name)))
- {
- if (task.Result.State == PluginState.Loaded)
- {
- notifications.AddNotification(Locs.Notifications_PluginInstalled(manifest.Name), Locs.Notifications_PluginInstalledTitle, NotificationType.Success);
- }
- else
- {
- notifications.AddNotification(Locs.Notifications_PluginNotInstalled(manifest.Name), Locs.Notifications_PluginNotInstalledTitle, NotificationType.Error);
- this.ShowErrorModal(Locs.ErrorModal_InstallFail(manifest.Name));
- }
- }
- });
+ this.StartInstall(manifest, useTesting);
}
}
@@ -1980,7 +2016,7 @@ internal class PluginInstallerWindow : Window, IDisposable
}
// Disabled
- if (plugin.IsDisabled || !plugin.CheckPolicy())
+ if (!plugin.IsWantedByAnyProfile || !plugin.CheckPolicy())
{
label += Locs.PluginTitleMod_Disabled;
trouble = true;
@@ -2262,6 +2298,11 @@ internal class PluginInstallerWindow : Window, IDisposable
{
var notifications = Service.Get();
var pluginManager = Service.Get();
+ var profileManager = Service.Get();
+ var config = Service.Get();
+
+ var applicableForProfiles = plugin.Manifest.SupportsProfiles;
+ var isDefaultPlugin = profileManager.IsInDefaultProfile(plugin.Manifest.InternalName);
// Disable everything if the updater is running or another plugin is operating
var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress;
@@ -2276,15 +2317,70 @@ internal class PluginInstallerWindow : Window, IDisposable
// Disable everything if the plugin failed to load
disabled = disabled || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed;
- // Disable everything if we're working
+ // Disable everything if we're loading plugins
disabled = disabled || plugin.State == PluginState.Loading || plugin.State == PluginState.Unloading;
+ // Disable everything if we're applying profiles
+ disabled = disabled || profileManager.IsBusy;
+
var toggleId = plugin.Manifest.InternalName;
var isLoadedAndUnloadable = plugin.State == PluginState.Loaded ||
plugin.State == PluginState.DependencyResolutionFailed;
StyleModelV1.DalamudStandard.Push();
+ var profileChooserPopupName = $"###pluginProfileChooser{plugin.Manifest.InternalName}";
+ if (ImGui.BeginPopup(profileChooserPopupName))
+ {
+ var didAny = false;
+
+ foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile))
+ {
+ var inProfile = profile.WantsPlugin(plugin.Manifest.InternalName) != null;
+ if (ImGui.Checkbox($"###profilePick{profile.Guid}{plugin.Manifest.InternalName}", ref inProfile))
+ {
+ if (inProfile)
+ {
+ Task.Run(() => profile.AddOrUpdate(plugin.Manifest.InternalName, true))
+ .ContinueWith(this.DisplayErrorContinuation, "Couldn't add plugin to this profile.");
+ }
+ else
+ {
+ Task.Run(() => profile.Remove(plugin.Manifest.InternalName))
+ .ContinueWith(this.DisplayErrorContinuation, "Couldn't remove plugin from this profile.");
+ }
+ }
+
+ ImGui.SameLine();
+
+ ImGui.TextUnformatted(profile.Name);
+
+ didAny = true;
+ }
+
+ if (!didAny)
+ ImGui.TextColored(ImGuiColors.DalamudGrey, "No profiles! Go add some!");
+
+ ImGui.Separator();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
+ {
+ profileManager.DefaultProfile.AddOrUpdate(plugin.Manifest.InternalName, plugin.IsLoaded, false);
+ foreach (var profile in profileManager.Profiles.Where(x => !x.IsDefaultProfile && x.Plugins.Any(y => y.InternalName == plugin.Manifest.InternalName)))
+ {
+ profile.Remove(plugin.Manifest.InternalName, false);
+ }
+
+ // TODO error handling
+ Task.Run(() => profileManager.ApplyAllWantStates());
+ }
+
+ ImGui.SameLine();
+ ImGui.Text("Remove from all profiles");
+
+ ImGui.EndPopup();
+ }
+
if (plugin.State == PluginState.UnloadError && !plugin.IsDev)
{
ImGuiComponents.DisabledButton(FontAwesomeIcon.Frown);
@@ -2292,14 +2388,18 @@ internal class PluginInstallerWindow : Window, IDisposable
if (ImGui.IsItemHovered())
ImGui.SetTooltip(Locs.PluginButtonToolTip_UnloadFailed);
}
- else if (disabled)
+ else if (disabled || !isDefaultPlugin)
{
ImGuiComponents.DisabledToggleButton(toggleId, isLoadedAndUnloadable);
+
+ if (!isDefaultPlugin && ImGui.IsItemHovered())
+ ImGui.SetTooltip(Locs.PluginButtonToolTip_NeedsToBeInDefault);
}
else
{
if (ImGuiComponents.ToggleButton(toggleId, ref isLoadedAndUnloadable))
{
+ // TODO: We can technically let profile manager take care of unloading/loading the plugin, but we should figure out error handling first.
if (!isLoadedAndUnloadable)
{
this.enableDisableStatus = OperationStatus.InProgress;
@@ -2322,15 +2422,9 @@ internal class PluginInstallerWindow : Window, IDisposable
return;
}
- var disableTask = Task.Run(() => plugin.Disable())
- .ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_DisableFail(plugin.Name));
-
- disableTask.Wait();
+ profileManager.DefaultProfile.AddOrUpdate(plugin.Manifest.InternalName, false, false);
this.enableDisableStatus = OperationStatus.Complete;
- if (!disableTask.Result)
- return;
-
notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success);
});
}
@@ -2346,17 +2440,7 @@ internal class PluginInstallerWindow : Window, IDisposable
plugin.ReloadManifest();
}
- var enableTask = Task.Run(plugin.Enable)
- .ContinueWith(
- this.DisplayErrorContinuation,
- Locs.ErrorModal_EnableFail(plugin.Name));
-
- enableTask.Wait();
- if (!enableTask.Result)
- {
- this.enableDisableStatus = OperationStatus.Complete;
- return;
- }
+ profileManager.DefaultProfile.AddOrUpdate(plugin.Manifest.InternalName, true, false);
var loadTask = Task.Run(() => plugin.LoadAsync(PluginLoadReason.Installer))
.ContinueWith(
@@ -2409,6 +2493,29 @@ internal class PluginInstallerWindow : Window, IDisposable
// Only if the plugin isn't broken.
this.DrawOpenPluginSettingsButton(plugin);
}
+
+ if (applicableForProfiles && config.ProfilesEnabled)
+ {
+ ImGui.SameLine();
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Toolbox))
+ {
+ ImGui.OpenPopup(profileChooserPopupName);
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip(Locs.PluginButtonToolTip_PickProfiles);
+ }
+ else if (!applicableForProfiles && config.ProfilesEnabled)
+ {
+ ImGui.SameLine();
+
+ ImGui.BeginDisabled();
+ ImGuiComponents.IconButton(FontAwesomeIcon.Toolbox);
+ ImGui.EndDisabled();
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip(Locs.PluginButtonToolTip_ProfilesNotSupported);
+ }
}
private async Task UpdateSinglePlugin(AvailablePluginUpdate update)
@@ -2827,7 +2934,7 @@ internal class PluginInstallerWindow : Window, IDisposable
/// The previous task.
/// An error message to be displayed.
/// A value indicating whether to continue with the next task.
- private bool DisplayErrorContinuation(Task task, object state)
+ public bool DisplayErrorContinuation(Task task, object state)
{
if (task.IsFaulted)
{
@@ -2911,7 +3018,7 @@ internal class PluginInstallerWindow : Window, IDisposable
#region Header
- public static string Header_Hint => Loc.Localize("InstallerHint", "This window allows you to install and remove in-game plugins.\nThey are made by third-party developers.");
+ public static string Header_Hint => Loc.Localize("InstallerHint", "This window allows you to install and remove Dalamud plugins.\nThey are made by the community.");
public static string Header_SearchPlaceholder => Loc.Localize("InstallerSearch", "Search");
@@ -3068,6 +3175,10 @@ internal class PluginInstallerWindow : Window, IDisposable
public static string PluginButtonToolTip_OpenConfiguration => Loc.Localize("InstallerOpenConfig", "Open Configuration");
+ public static string PluginButtonToolTip_PickProfiles => Loc.Localize("InstallerPickProfiles", "Pick profiles for this plugin");
+
+ public static string PluginButtonToolTip_ProfilesNotSupported => Loc.Localize("InstallerProfilesNotSupported", "This plugin does not support profiles");
+
public static string PluginButtonToolTip_StartOnBoot => Loc.Localize("InstallerStartOnBoot", "Start on boot");
public static string PluginButtonToolTip_AutomaticReloading => Loc.Localize("InstallerAutomaticReloading", "Automatic reloading");
@@ -3088,6 +3199,8 @@ internal class PluginInstallerWindow : Window, IDisposable
public static string PluginButtonToolTip_UnloadFailed => Loc.Localize("InstallerUnloadFailedTooltip", "Plugin unload failed, please restart your game and try again.");
+ public static string PluginButtonToolTip_NeedsToBeInDefault => Loc.Localize("InstallerUnloadNeedsToBeInDefault", "This plugin is in one or more profiles. If you want to enable or disable it, please do so by enabling or disabling one of the profiles it is in.\nIf you want to manage it manually, remove it from all profiles.");
+
#endregion
#region Notifications
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
new file mode 100644
index 000000000..fcb941d95
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -0,0 +1,399 @@
+using System;
+using System.Linq;
+using System.Numerics;
+using System.Threading.Tasks;
+
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Components;
+using Dalamud.Interface.Internal.Notifications;
+using Dalamud.Interface.Raii;
+using Dalamud.Plugin.Internal;
+using Dalamud.Plugin.Internal.Profiles;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.PluginInstaller;
+
+internal class ProfileManagerWidget
+{
+ private readonly PluginInstallerWindow installer;
+ private Mode mode = Mode.Overview;
+ private Guid? editingProfileGuid;
+
+ private string? pickerSelectedPluginInternalName = null;
+ private string profileNameEdit = string.Empty;
+
+ public ProfileManagerWidget(PluginInstallerWindow installer)
+ {
+ this.installer = installer;
+ }
+
+ public void Draw()
+ {
+ switch (this.mode)
+ {
+ case Mode.Overview:
+ this.DrawOverview();
+ break;
+
+ case Mode.EditSingleProfile:
+ this.DrawEdit();
+ break;
+ }
+ }
+
+ public void Reset()
+ {
+ this.mode = Mode.Overview;
+ this.editingProfileGuid = null;
+ this.pickerSelectedPluginInternalName = null;
+ }
+
+ private void DrawOverview()
+ {
+ var didAny = false;
+ var profman = Service.Get();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
+ profman.AddNewProfile();
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Add a new profile");
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(5);
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.FileImport))
+ {
+ try
+ {
+ profman.ImportProfile(ImGui.GetClipboardText());
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Could not import profile");
+ }
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Import a shared profile from your clipboard");
+
+ ImGui.Separator();
+ ImGuiHelpers.ScaledDummy(5);
+
+ var windowSize = ImGui.GetWindowSize();
+
+ if (ImGui.BeginChild("###profileChooserScrolling"))
+ {
+ Guid? toCloneGuid = null;
+
+ foreach (var profile in profman.Profiles)
+ {
+ if (profile.IsDefaultProfile)
+ continue;
+
+ var isEnabled = profile.IsEnabled;
+ if (ImGuiComponents.ToggleButton($"###toggleButton{profile.Guid}", ref isEnabled))
+ {
+ Task.Run(() => profile.SetState(isEnabled))
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change profile state.");
+ }
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(3);
+ ImGui.SameLine();
+
+ ImGui.Text(profile.Name);
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30));
+
+ if (ImGuiComponents.IconButton($"###editButton{profile.Guid}", FontAwesomeIcon.PencilAlt))
+ {
+ this.mode = Mode.EditSingleProfile;
+ this.editingProfileGuid = profile.Guid;
+ this.profileNameEdit = profile.Name;
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Edit this profile");
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 2) - 5);
+
+ if (ImGuiComponents.IconButton($"###cloneButton{profile.Guid}", FontAwesomeIcon.Copy))
+ toCloneGuid = profile.Guid;
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Clone this profile");
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 3) - 5);
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.FileExport))
+ {
+ ImGui.SetClipboardText(profile.Model.Serialize());
+ Service.Get().AddNotification("Copied to clipboard!", type: NotificationType.Success);
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Copy profile to clipboard for sharing");
+
+ didAny = true;
+
+ ImGuiHelpers.ScaledDummy(2);
+ }
+
+ if (toCloneGuid != null)
+ {
+ profman.CloneProfile(profman.Profiles.First(x => x.Guid == toCloneGuid));
+ }
+
+ if (!didAny)
+ {
+ ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
+ ImGuiHelpers.CenteredText("No profiles! Add one!");
+ ImGui.PopStyleColor();
+ }
+
+ ImGui.EndChild();
+ }
+ }
+
+ private void DrawEdit()
+ {
+ if (this.editingProfileGuid == null)
+ {
+ Log.Error("Editing profile guid was null");
+ this.Reset();
+ return;
+ }
+
+ var profman = Service.Get();
+ var pm = Service.Get();
+ var pic = Service.Get();
+ var profile = profman.Profiles.FirstOrDefault(x => x.Guid == this.editingProfileGuid);
+
+ if (profile == null)
+ {
+ Log.Error("Could not find profile {Guid} for edit", this.editingProfileGuid);
+ this.Reset();
+ return;
+ }
+
+ const string addPluginToProfilePopup = "###addPluginToProfile";
+ if (ImGui.BeginPopup(addPluginToProfilePopup))
+ {
+ var selected =
+ pm.InstalledPlugins.FirstOrDefault(
+ x => x.Manifest.InternalName == this.pickerSelectedPluginInternalName);
+
+ if (ImGui.BeginCombo("###pluginPicker", selected == null ? "Pick one" : selected.Manifest.Name))
+ {
+ foreach (var plugin in pm.InstalledPlugins.Where(x => x.Manifest.SupportsProfiles))
+ {
+ if (ImGui.Selectable($"{plugin.Manifest.Name}###selector{plugin.Manifest.InternalName}"))
+ {
+ this.pickerSelectedPluginInternalName = plugin.Manifest.InternalName;
+ }
+ }
+
+ ImGui.EndCombo();
+ }
+
+ using (ImRaii.Disabled(this.pickerSelectedPluginInternalName == null))
+ {
+ if (ImGui.Button("Do it") && selected != null)
+ {
+ Task.Run(() => profile.AddOrUpdate(selected.Manifest.InternalName, selected.IsLoaded));
+ }
+ }
+
+ ImGui.EndPopup();
+ }
+
+ var didAny = false;
+
+ // ======== Top bar ========
+ var windowSize = ImGui.GetWindowSize();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.ArrowLeft))
+ this.Reset();
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Back to overview");
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(5);
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.FileExport))
+ {
+ ImGui.SetClipboardText(profile.Model.Serialize());
+ Service.Get().AddNotification("Copied to clipboard!", type: NotificationType.Success);
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Copy profile to clipboard for sharing");
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(5);
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash))
+ {
+ Task.Run(() => profman.DeleteProfile(profile))
+ .ContinueWith(t =>
+ {
+ this.Reset();
+ this.installer.DisplayErrorContinuation(t, "Could not refresh profiles.");
+ });
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Delete this profile");
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(5);
+ ImGui.SameLine();
+
+ ImGui.SetNextItemWidth(windowSize.X / 3);
+ if (ImGui.InputText("###profileNameInput", ref this.profileNameEdit, 255))
+ {
+ profile.Name = this.profileNameEdit;
+ }
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGui.GetFrameHeight() * 1.55f * ImGuiHelpers.GlobalScale));
+
+ var isEnabled = profile.IsEnabled;
+ if (ImGuiComponents.ToggleButton($"###toggleButton{profile.Guid}", ref isEnabled))
+ {
+ Task.Run(() => profile.SetState(isEnabled))
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change profile state.");
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Enable/Disable this profile");
+
+ ImGui.Separator();
+
+ ImGuiHelpers.ScaledDummy(5);
+
+ var enableAtBoot = profile.AlwaysEnableAtBoot;
+ if (ImGui.Checkbox("Always enable when game starts", ref enableAtBoot))
+ {
+ profile.AlwaysEnableAtBoot = enableAtBoot;
+ }
+
+ ImGuiHelpers.ScaledDummy(5);
+
+ ImGui.Separator();
+ var wantPluginAddPopup = false;
+
+ if (ImGui.BeginChild("###profileEditorPluginList"))
+ {
+ var pluginLineHeight = 32 * ImGuiHelpers.GlobalScale;
+
+ foreach (var plugin in profile.Plugins)
+ {
+ didAny = true;
+ var pmPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.Manifest.InternalName == plugin.InternalName);
+
+ if (pmPlugin != null)
+ {
+ pic.TryGetIcon(pmPlugin, pmPlugin.Manifest, pmPlugin.Manifest.IsThirdParty, out var icon);
+ icon ??= pic.DefaultIcon;
+
+ ImGui.Image(icon.ImGuiHandle, new Vector2(pluginLineHeight));
+ ImGui.SameLine();
+
+ var text = $"{pmPlugin.Name}";
+ var textHeight = ImGui.CalcTextSize(text);
+ var before = ImGui.GetCursorPos();
+
+ ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (textHeight.Y / 2));
+ ImGui.TextUnformatted(text);
+
+ ImGui.SetCursorPos(before);
+ }
+ else
+ {
+ ImGui.Image(pic.DefaultIcon.ImGuiHandle, new Vector2(pluginLineHeight));
+ ImGui.SameLine();
+
+ var text = $"{plugin.InternalName} (Not Installed)";
+ var textHeight = ImGui.CalcTextSize(text);
+ var before = ImGui.GetCursorPos();
+
+ ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (textHeight.Y / 2));
+ ImGui.TextUnformatted(text);
+
+ var available =
+ pm.AvailablePlugins.FirstOrDefault(
+ x => x.InternalName == plugin.InternalName && !x.SourceRepo.IsThirdParty);
+ if (available != null)
+ {
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 32 * 2));
+ ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (ImGui.GetFrameHeight() / 2));
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Download))
+ {
+ this.installer.StartInstall(available, false);
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Install this plugin");
+ }
+
+ ImGui.SetCursorPos(before);
+ }
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30));
+ ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (ImGui.GetFrameHeight() / 2));
+
+ var enabled = plugin.IsEnabled;
+ if (ImGui.Checkbox($"###{this.editingProfileGuid}-{plugin.InternalName}", ref enabled))
+ {
+ Task.Run(() => profile.AddOrUpdate(plugin.InternalName, enabled))
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
+ }
+ }
+
+ if (!didAny)
+ {
+ ImGui.TextColored(ImGuiColors.DalamudGrey, "Profile has no plugins!");
+ }
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ var addPluginsText = "Add a plugin!";
+ ImGuiHelpers.CenterCursorFor((int)(ImGui.CalcTextSize(addPluginsText).X + 30 + (ImGuiHelpers.GlobalScale * 5)));
+
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
+ wantPluginAddPopup = true;
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(5);
+ ImGui.SameLine();
+
+ ImGui.TextUnformatted(addPluginsText);
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.EndChild();
+ }
+
+ if (wantPluginAddPopup)
+ ImGui.OpenPopup(addPluginToProfilePopup);
+ }
+
+ private enum Mode
+ {
+ Overview,
+ EditSingleProfile,
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
index ec22ef8d7..6d5c03137 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
@@ -46,6 +46,14 @@ public class SettingsTabExperimental : SettingsTab
new GapSettingsEntry(5, true),
new ThirdRepoSettingsEntry(),
+
+ new GapSettingsEntry(5, true),
+
+ new SettingsEntry(
+ Loc.Localize("DalamudSettingsEnableProfiles", "Enable plugin profiles"),
+ Loc.Localize("DalamudSettingsEnableProfilesHint", "EXPERIMENTAL: Enables plugin profiles."),
+ c => c.ProfilesEnabled,
+ (v, c) => c.ProfilesEnabled = v),
};
public override string Title => Loc.Localize("DalamudSettingsExperimental", "Experimental");
diff --git a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs
index 8fe978ef1..419361b3b 100644
--- a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs
@@ -11,6 +11,7 @@ using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Style;
using Dalamud.Interface.Windowing;
+using Dalamud.Utility;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Serilog;
@@ -97,7 +98,7 @@ public class StyleEditorWindow : Window
this.SaveStyle();
var newStyle = StyleModelV1.DalamudStandard;
- newStyle.Name = GetRandomName();
+ newStyle.Name = Util.GetRandomName();
config.SavedStyles.Add(newStyle);
this.currentSel = config.SavedStyles.Count - 1;
@@ -167,11 +168,11 @@ public class StyleEditorWindow : Window
{
var newStyle = StyleModel.Deserialize(styleJson);
- newStyle.Name ??= GetRandomName();
+ newStyle.Name ??= Util.GetRandomName();
if (config.SavedStyles.Any(x => x.Name == newStyle.Name))
{
- newStyle.Name = $"{newStyle.Name} ({GetRandomName()} Mix)";
+ newStyle.Name = $"{newStyle.Name} ({Util.GetRandomName()} Mix)";
}
config.SavedStyles.Add(newStyle);
@@ -375,15 +376,6 @@ public class StyleEditorWindow : Window
}
}
- private static string GetRandomName()
- {
- var data = Service.Get();
- var names = data.GetExcelSheet(ClientLanguage.English);
- var rng = new Random();
-
- return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString;
- }
-
private void SaveStyle()
{
if (this.currentSel < 2)
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 311172c97..a647c1d03 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -23,6 +23,7 @@ using Dalamud.Interface.Internal;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Exceptions;
+using Dalamud.Plugin.Internal.Profiles;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
using Dalamud.Utility.Timing;
@@ -77,6 +78,9 @@ Thanks and have fun!";
[ServiceManager.ServiceDependency]
private readonly DalamudStartInfo startInfo = Service.Get();
+ [ServiceManager.ServiceDependency]
+ private readonly ProfileManager profileManager = Service.Get();
+
[ServiceManager.ServiceConstructor]
private PluginManager()
{
@@ -421,7 +425,7 @@ Thanks and have fun!";
try
{
- pluginDefs.Add(versionsDefs.OrderByDescending(x => x.Manifest!.EffectiveVersion).First());
+ pluginDefs.Add(versionsDefs.MaxBy(x => x.Manifest!.EffectiveVersion));
}
catch (Exception ex)
{
@@ -839,7 +843,7 @@ Thanks and have fun!";
/// If this plugin is being loaded at boot.
/// Don't load the plugin, just don't do it.
/// The loaded plugin.
- public async Task LoadPluginAsync(FileInfo dllFile, LocalPluginManifest? manifest, PluginLoadReason reason, bool isDev = false, bool isBoot = false, bool doNotLoad = false)
+ private async Task LoadPluginAsync(FileInfo dllFile, LocalPluginManifest? manifest, PluginLoadReason reason, bool isDev = false, bool isBoot = false, bool doNotLoad = false)
{
var name = manifest?.Name ?? dllFile.Name;
var loadPlugin = !doNotLoad;
@@ -859,8 +863,9 @@ Thanks and have fun!";
loadPlugin &= !isBoot || devPlugin.StartOnBoot;
// If we're not loading it, make sure it's disabled
- if (!loadPlugin && !devPlugin.IsDisabled)
- devPlugin.Disable();
+ // NOTE: Should be taken care of below by the profile code
+ // if (!loadPlugin && !devPlugin.IsDisabled)
+ // devPlugin.Disable();
plugin = devPlugin;
}
@@ -870,17 +875,24 @@ Thanks and have fun!";
plugin = new LocalPlugin(dllFile, manifest);
}
+#pragma warning disable CS0618
+ var defaultState = manifest?.Disabled != true && loadPlugin;
+#pragma warning restore CS0618
+
+ // Need to do this here, so plugins that don't load are still added to the default profile
+ var wantToLoad = this.profileManager.GetWantState(plugin.Manifest.InternalName, defaultState);
+
if (loadPlugin)
{
try
{
- if (!plugin.IsDisabled && !plugin.IsOrphaned)
+ if (wantToLoad && !plugin.IsOrphaned)
{
await plugin.LoadAsync(reason);
}
else
{
- Log.Verbose($"{name} not loaded, disabled:{plugin.IsDisabled} orphaned:{plugin.IsOrphaned}");
+ Log.Verbose($"{name} not loaded, wantToLoad:{wantToLoad} orphaned:{plugin.IsOrphaned}");
}
}
catch (InvalidPluginException)
@@ -1073,7 +1085,7 @@ Thanks and have fun!";
if (plugin.InstalledPlugin.IsDev)
continue;
- if (plugin.InstalledPlugin.Manifest.Disabled && ignoreDisabled)
+ if (!plugin.InstalledPlugin.IsWantedByAnyProfile && ignoreDisabled)
continue;
if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion)
@@ -1132,6 +1144,7 @@ Thanks and have fun!";
if (plugin.IsDev)
{
+ // TODO: Does this ever work? Why? We should never update devplugins
try
{
plugin.DllFile.Delete();
@@ -1151,8 +1164,11 @@ Thanks and have fun!";
{
try
{
+ // TODO: Why were we ever doing this? We should never be loading the old version in the first place
+ /*
if (!plugin.IsDisabled)
plugin.Disable();
+ */
lock (this.pluginListLock)
{
diff --git a/Dalamud/Plugin/Internal/Profiles/Profile.cs b/Dalamud/Plugin/Internal/Profiles/Profile.cs
new file mode 100644
index 000000000..bae3e6f95
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/Profile.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+using Dalamud.Configuration.Internal;
+using Dalamud.Logging.Internal;
+using Dalamud.Utility;
+
+namespace Dalamud.Plugin.Internal.Profiles;
+
+///
+/// Class representing a single runtime profile.
+///
+internal class Profile
+{
+ private static readonly ModuleLog Log = new("PROFILE");
+
+ private readonly ProfileManager manager;
+ private readonly ProfileModelV1 modelV1;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The manager this profile belongs to.
+ /// The model this profile is tied to.
+ /// Whether or not this profile is the default profile.
+ /// Whether or not this profile was initialized during bootup.
+ public Profile(ProfileManager manager, ProfileModel model, bool isDefaultProfile, bool isBoot)
+ {
+ this.manager = manager;
+ this.IsDefaultProfile = isDefaultProfile;
+ this.modelV1 = model as ProfileModelV1 ??
+ throw new ArgumentException("Model was null or unhandled version");
+
+ // We don't actually enable plugins here, PM will do it on bootup
+ if (isDefaultProfile)
+ {
+ // Default profile cannot be disabled
+ this.IsEnabled = this.modelV1.IsEnabled = true;
+ this.Name = this.modelV1.Name = "DEFAULT";
+ }
+ else if (this.modelV1.AlwaysEnableOnBoot && isBoot)
+ {
+ this.IsEnabled = true;
+ Log.Verbose("{Guid} set enabled because bootup", this.modelV1.Guid);
+ }
+ else if (this.modelV1.IsEnabled)
+ {
+ this.IsEnabled = true;
+ Log.Verbose("{Guid} set enabled because remember", this.modelV1.Guid);
+ }
+ else
+ {
+ Log.Verbose("{Guid} not enabled", this.modelV1.Guid);
+ }
+ }
+
+ ///
+ /// Gets or sets this profile's name.
+ ///
+ public string Name
+ {
+ get => this.modelV1.Name;
+ set
+ {
+ this.modelV1.Name = value;
+ Service.Get().QueueSave();
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether this profile shall always be enabled at boot.
+ ///
+ public bool AlwaysEnableAtBoot
+ {
+ get => this.modelV1.AlwaysEnableOnBoot;
+ set
+ {
+ this.modelV1.AlwaysEnableOnBoot = value;
+ Service.Get().QueueSave();
+ }
+ }
+
+ ///
+ /// Gets this profile's guid.
+ ///
+ public Guid Guid => this.modelV1.Guid;
+
+ ///
+ /// Gets a value indicating whether or not this profile is currently enabled.
+ ///
+ public bool IsEnabled { get; private set; }
+
+ ///
+ /// Gets a value indicating whether or not this profile is the default profile.
+ ///
+ public bool IsDefaultProfile { get; }
+
+ ///
+ /// Gets all plugins declared in this profile.
+ ///
+ public IEnumerable Plugins =>
+ this.modelV1.Plugins.Select(x => new ProfilePluginEntry(x.InternalName, x.IsEnabled));
+
+ ///
+ /// Gets this profile's underlying model.
+ ///
+ public ProfileModel Model => this.modelV1;
+
+ ///
+ /// Set this profile's state. This cannot be called for the default profile.
+ /// This will block until all states have been applied.
+ ///
+ /// Whether or not the profile is enabled.
+ /// Whether or not the current state should immediately be applied.
+ /// Thrown when an untoggleable profile is toggled.
+ public void SetState(bool enabled, bool apply = true)
+ {
+ if (this.IsDefaultProfile)
+ throw new InvalidOperationException("Cannot set state of default profile");
+
+ Debug.Assert(this.IsEnabled != enabled, "Trying to set state of a profile to the same state");
+ this.IsEnabled = this.modelV1.IsEnabled = enabled;
+ Log.Verbose("Set state {State} for {Guid}", enabled, this.modelV1.Guid);
+
+ Service.Get().QueueSave();
+
+ if (apply)
+ this.manager.ApplyAllWantStates();
+ }
+
+ ///
+ /// Check if this profile contains a specific plugin, and if it is enabled.
+ ///
+ /// The internal name of the plugin.
+ /// Null if this profile does not declare the plugin, true if the profile declares the plugin and wants it enabled, false if the profile declares the plugin and does not want it enabled.
+ public bool? WantsPlugin(string internalName)
+ {
+ var entry = this.modelV1.Plugins.FirstOrDefault(x => x.InternalName == internalName);
+ return entry?.IsEnabled;
+ }
+
+ ///
+ /// Add a plugin to this profile with the desired state, or change the state of a plugin in this profile.
+ /// This will block until all states have been applied.
+ ///
+ /// The internal name of the plugin.
+ /// Whether or not the plugin should be enabled.
+ /// Whether or not the current state should immediately be applied.
+ public void AddOrUpdate(string internalName, bool state, bool apply = true)
+ {
+ Debug.Assert(!internalName.IsNullOrEmpty(), "!internalName.IsNullOrEmpty()");
+
+ var existing = this.modelV1.Plugins.FirstOrDefault(x => x.InternalName == internalName);
+ if (existing != null)
+ {
+ existing.IsEnabled = state;
+ }
+ else
+ {
+ this.modelV1.Plugins.Add(new ProfileModelV1.ProfileModelV1Plugin
+ {
+ InternalName = internalName,
+ IsEnabled = state,
+ });
+ }
+
+ // We need to remove this plugin from the default profile, if it declares it.
+ if (!this.IsDefaultProfile && this.manager.DefaultProfile.WantsPlugin(internalName) != null)
+ {
+ this.manager.DefaultProfile.Remove(internalName, false);
+ }
+
+ Service.Get().QueueSave();
+
+ if (apply)
+ this.manager.ApplyAllWantStates();
+ }
+
+ ///
+ /// Remove a plugin from this profile.
+ /// This will block until all states have been applied.
+ ///
+ /// The internal name of the plugin.
+ /// Whether or not the current state should immediately be applied.
+ public void Remove(string internalName, bool apply = true)
+ {
+ var entry = this.modelV1.Plugins.FirstOrDefault(x => x.InternalName == internalName);
+ if (entry == null)
+ throw new ArgumentException($"No plugin \"{internalName}\" in profile \"{this.Guid}\"");
+
+ if (!this.modelV1.Plugins.Remove(entry))
+ throw new Exception("Couldn't remove plugin from model collection");
+
+ // We need to add this plugin back to the default profile, if we were the last profile to have it.
+ if (!this.manager.IsInAnyProfile(internalName))
+ {
+ if (!this.IsDefaultProfile)
+ {
+ this.manager.DefaultProfile.AddOrUpdate(internalName, entry.IsEnabled, false);
+ }
+ else
+ {
+ throw new Exception("Removed plugin from default profile, but wasn't in any other profile");
+ }
+ }
+
+ Service.Get().QueueSave();
+
+ if (apply)
+ this.manager.ApplyAllWantStates();
+ }
+}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
new file mode 100644
index 000000000..995f91f5f
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using CheapLoc;
+using Dalamud.Game;
+using Dalamud.Game.Command;
+using Dalamud.Game.Gui;
+using Dalamud.Utility;
+using Serilog;
+
+namespace Dalamud.Plugin.Internal.Profiles;
+
+[ServiceManager.EarlyLoadedService]
+internal class ProfileCommandHandler : IServiceType, IDisposable
+{
+ private readonly CommandManager cmd;
+ private readonly ProfileManager profileManager;
+ private readonly ChatGui chat;
+ private readonly Framework framework;
+
+ private Queue<(string, ProfileOp)> queue = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ [ServiceManager.ServiceConstructor]
+ public ProfileCommandHandler(CommandManager cmd, ProfileManager profileManager, ChatGui chat, Framework framework)
+ {
+ this.cmd = cmd;
+ this.profileManager = profileManager;
+ this.chat = chat;
+ this.framework = framework;
+
+ this.cmd.AddHandler("/xlenableprofile", new CommandInfo(this.OnEnableProfile)
+ {
+ HelpMessage = "",
+ ShowInHelp = true,
+ });
+
+ this.cmd.AddHandler("/xldisableprofile", new CommandInfo(this.OnDisableProfile)
+ {
+ HelpMessage = "",
+ ShowInHelp = true,
+ });
+
+ this.cmd.AddHandler("/xltoggleprofile", new CommandInfo(this.OnToggleProfile)
+ {
+ HelpMessage = "",
+ ShowInHelp = true,
+ });
+
+ this.framework.Update += this.FrameworkOnUpdate;
+ }
+
+ private void FrameworkOnUpdate(Framework framework1)
+ {
+ if (this.profileManager.IsBusy)
+ return;
+
+ if (this.queue.TryDequeue(out var op))
+ {
+ var profile = this.profileManager.Profiles.FirstOrDefault(x => x.Name == op.Item1);
+ if (profile == null || profile.IsDefaultProfile)
+ return;
+
+ switch (op.Item2)
+ {
+ case ProfileOp.Enable:
+ if (!profile.IsEnabled)
+ profile.SetState(true, false);
+ break;
+ case ProfileOp.Disable:
+ if (profile.IsEnabled)
+ profile.SetState(false, false);
+ break;
+ case ProfileOp.Toggle:
+ profile.SetState(!profile.IsEnabled, false);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if (profile.IsEnabled)
+ {
+ this.chat.Print(Loc.Localize("ProfileCommandsEnabling", "Enabling profile \"{0}\"...").Format(profile.Name));
+ }
+ else
+ {
+ this.chat.Print(Loc.Localize("ProfileCommandsDisabling", "Disabling profile \"{0}\"...").Format(profile.Name));
+ }
+
+ Task.Run(() => this.profileManager.ApplyAllWantStates()).ContinueWith(t =>
+ {
+ if (!t.IsCompletedSuccessfully && t.Exception != null)
+ {
+ Log.Error(t.Exception, "Could not apply profiles through commands");
+ this.chat.PrintError(Loc.Localize("ProfileCommandsApplyFailed", "Failed to apply all of your profiles. Please check the console for errors."));
+ }
+ else
+ {
+ this.chat.Print(Loc.Localize("ProfileCommandsApplySuccess", "Profiles applied."));
+ }
+ });
+ }
+ }
+
+ public void Dispose()
+ {
+ this.cmd.RemoveHandler("/xlenableprofile");
+ this.cmd.RemoveHandler("/xldisableprofile");
+ this.cmd.RemoveHandler("/xltoggleprofile");
+
+ this.framework.Update += this.FrameworkOnUpdate;
+ }
+
+ private void OnEnableProfile(string command, string arguments)
+ {
+ var name = this.ValidateName(arguments);
+ if (name == null)
+ return;
+
+ this.queue.Enqueue((name, ProfileOp.Enable));
+ }
+
+ private void OnDisableProfile(string command, string arguments)
+ {
+ var name = this.ValidateName(arguments);
+ if (name == null)
+ return;
+
+ this.queue.Enqueue((name, ProfileOp.Disable));
+ }
+
+ private void OnToggleProfile(string command, string arguments)
+ {
+ var name = this.ValidateName(arguments);
+ if (name == null)
+ return;
+
+ this.queue.Enqueue((name, ProfileOp.Toggle));
+ }
+
+ private string? ValidateName(string arguments)
+ {
+ var name = arguments.Replace("\"", string.Empty);
+ if (this.profileManager.Profiles.All(x => x.Name != name))
+ {
+ this.chat.PrintError($"No profile like \"{name}\".");
+ return null;
+ }
+
+ return name;
+ }
+
+ private enum ProfileOp
+ {
+ Enable,
+ Disable,
+ Toggle,
+ }
+}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
new file mode 100644
index 000000000..7a8772d74
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -0,0 +1,356 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+using CheapLoc;
+using Dalamud.Configuration.Internal;
+using Dalamud.Logging.Internal;
+using Dalamud.Utility;
+
+namespace Dalamud.Plugin.Internal.Profiles;
+
+///
+/// Class responsible for managing plugin profiles.
+///
+[ServiceManager.BlockingEarlyLoadedService]
+internal class ProfileManager : IServiceType
+{
+ private static readonly ModuleLog Log = new("PROFMAN");
+ private readonly DalamudConfiguration config;
+
+ private readonly List profiles = new();
+
+ private volatile bool isBusy = false;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Dalamud config.
+ [ServiceManager.ServiceConstructor]
+ public ProfileManager(DalamudConfiguration config)
+ {
+ this.config = config;
+
+ this.LoadProfilesFromConfigInitially();
+ }
+
+ ///
+ /// Gets the default profile.
+ ///
+ public Profile DefaultProfile => this.profiles.First(x => x.IsDefaultProfile);
+
+ ///
+ /// Gets all profiles, including the default profile.
+ ///
+ public IEnumerable Profiles => this.profiles;
+
+ ///
+ /// Gets a value indicating whether or not the profile manager is busy enabling/disabling plugins.
+ ///
+ public bool IsBusy => this.isBusy;
+
+ ///
+ /// Check if any enabled profile wants a specific plugin enabled.
+ ///
+ /// The internal name of the plugin.
+ /// The state the plugin shall be in, if it needs to be added.
+ /// Whether or not the plugin should be added to the default preset, if it's not present in any preset.
+ /// Whether or not the plugin shall be enabled.
+ public bool GetWantState(string internalName, bool defaultState, bool addIfNotDeclared = true)
+ {
+ var want = false;
+ var wasInAnyProfile = false;
+
+ foreach (var profile in this.profiles)
+ {
+ var state = profile.WantsPlugin(internalName);
+ Log.Verbose("Checking {Name} in {Profile}: {State}", internalName, profile.Guid, state == null ? "null" : state);
+
+ if (state.HasValue)
+ {
+ want = want || (profile.IsEnabled && state.Value);
+ wasInAnyProfile = true;
+ }
+ }
+
+ if (!wasInAnyProfile && addIfNotDeclared)
+ {
+ Log.Warning("{Name} was not in any profile, adding to default with {Default}", internalName, defaultState);
+ this.DefaultProfile.AddOrUpdate(internalName, defaultState, false);
+ return defaultState;
+ }
+
+ return want;
+ }
+
+ ///
+ /// Check whether a plugin is declared in any profile.
+ ///
+ /// The internal name of the plugin.
+ /// Whether or not the plugin is in any profile.
+ public bool IsInAnyProfile(string internalName)
+ => this.profiles.Any(x => x.WantsPlugin(internalName) != null);
+
+ ///
+ /// Check whether a plugin is only in the default profile.
+ /// A plugin can never be in the default profile if it is in any other profile.
+ ///
+ /// The internal name of the plugin.
+ /// Whether or not the plugin is in the default profile.
+ public bool IsInDefaultProfile(string internalName)
+ => this.DefaultProfile.WantsPlugin(internalName) != null;
+
+ ///
+ /// Add a new profile.
+ ///
+ /// The added profile.
+ public Profile AddNewProfile()
+ {
+ var model = new ProfileModelV1
+ {
+ Guid = Guid.NewGuid(),
+ Name = this.GenerateUniqueProfileName(Loc.Localize("PluginProfilesNewProfile", "New Profile")),
+ IsEnabled = false,
+ };
+
+ this.config.SavedProfiles!.Add(model);
+ this.config.QueueSave();
+
+ var profile = new Profile(this, model, false, false);
+ this.profiles.Add(profile);
+
+ return profile;
+ }
+
+ ///
+ /// Clone a specified profile.
+ ///
+ /// The profile to clone.
+ /// The newly cloned profile.
+ public Profile CloneProfile(Profile toClone)
+ {
+ var newProfile = this.ImportProfile(toClone.Model.Serialize());
+ if (newProfile == null)
+ throw new Exception("New profile was null while cloning");
+
+ return newProfile;
+ }
+
+ ///
+ /// Import a profile with a sharing string.
+ ///
+ /// The sharing string to import.
+ /// The imported profile, or null, if the string was invalid.
+ public Profile? ImportProfile(string data)
+ {
+ var newModel = ProfileModel.Deserialize(data);
+ if (newModel == null)
+ return null;
+
+ newModel.Guid = Guid.NewGuid();
+ newModel.Name = this.GenerateUniqueProfileName(newModel.Name.IsNullOrEmpty() ? "Unknown Profile" : newModel.Name);
+ if (newModel is ProfileModelV1 modelV1)
+ modelV1.IsEnabled = false;
+
+ this.config.SavedProfiles!.Add(newModel);
+ this.config.QueueSave();
+
+ var profile = new Profile(this, newModel, false, false);
+ this.profiles.Add(profile);
+
+ return profile;
+ }
+
+ ///
+ /// Go through all profiles and plugins, and enable/disable plugins they want active.
+ /// This will block until all plugins have been loaded/reloaded!
+ ///
+ public void ApplyAllWantStates()
+ {
+ this.isBusy = true;
+ Log.Information("Getting want states...");
+
+ var wantActive = this.profiles
+ .Where(x => x.IsEnabled)
+ .SelectMany(profile => profile.Plugins.Where(plugin => plugin.IsEnabled)
+ .Select(plugin => plugin.InternalName))
+ .Distinct().ToList();
+
+ foreach (var internalName in wantActive)
+ {
+ Log.Information("\t=> Want {Name}", internalName);
+ }
+
+ Log.Information("Applying want states...");
+
+ var pm = Service.Get();
+ var tasks = new List();
+
+ foreach (var installedPlugin in pm.InstalledPlugins)
+ {
+ if (installedPlugin.IsDev)
+ continue;
+
+ var wantThis = wantActive.Contains(installedPlugin.Manifest.InternalName);
+ switch (wantThis)
+ {
+ case true when !installedPlugin.IsLoaded:
+ if (installedPlugin.ApplicableForLoad)
+ {
+ Log.Information("\t=> Enabling {Name}", installedPlugin.Manifest.InternalName);
+ tasks.Add(installedPlugin.LoadAsync(PluginLoadReason.Installer));
+ }
+ else
+ {
+ Log.Warning("\t=> {Name} wanted active, but not applicable", installedPlugin.Manifest.InternalName);
+ }
+
+ break;
+ case false when installedPlugin.IsLoaded:
+ Log.Information("\t=> Disabling {Name}", installedPlugin.Manifest.InternalName);
+ tasks.Add(installedPlugin.UnloadAsync());
+ break;
+ }
+ }
+
+ // This is probably not ideal... Might need to rethink the error handling strategy for this.
+ try
+ {
+ Task.WaitAll(tasks.ToArray());
+ }
+ catch (Exception e)
+ {
+ Log.Error(e, "Couldn't apply state for one or more plugins");
+ }
+
+ Log.Information("Applied!");
+ this.isBusy = false;
+ }
+
+ ///
+ /// Delete a profile and re-apply all profiles.
+ ///
+ /// The profile to delete.
+ public void DeleteProfile(Profile profile)
+ {
+ Debug.Assert(this.config.SavedProfiles!.Remove(profile.Model), "this.config.SavedProfiles!.Remove(profile.Model)");
+ Debug.Assert(this.profiles.Remove(profile), "this.profiles.Remove(profile)");
+
+ this.ApplyAllWantStates();
+ this.config.QueueSave();
+ }
+
+ private string GenerateUniqueProfileName(string startingWith)
+ {
+ if (this.profiles.All(x => x.Name != startingWith))
+ return startingWith;
+
+ startingWith = Regex.Replace(startingWith, @" \(.* Mix\)", string.Empty);
+
+ while (true)
+ {
+ var newName = $"{startingWith} ({CultureInfo.InvariantCulture.TextInfo.ToTitleCase(Util.GetRandomName())} Mix)";
+
+ if (this.profiles.All(x => x.Name != newName))
+ return newName;
+ }
+ }
+
+ private void LoadProfilesFromConfigInitially()
+ {
+ var needMigration = false;
+ if (this.config.DefaultProfile == null)
+ {
+ this.config.DefaultProfile = new ProfileModelV1();
+ needMigration = true;
+ }
+
+ this.profiles.Add(new Profile(this, this.config.DefaultProfile, true, true));
+
+ if (needMigration)
+ {
+ // Don't think we need this here with the migration logic in GetWantState
+ //this.MigratePluginsIntoDefaultProfile();
+ }
+
+ this.config.SavedProfiles ??= new List();
+ foreach (var profileModel in this.config.SavedProfiles)
+ {
+ this.profiles.Add(new Profile(this, profileModel, false, true));
+ }
+
+ this.config.QueueSave();
+ }
+
+ // This duplicates some of the original handling in PM; don't care though
+ /*
+ private void MigratePluginsIntoDefaultProfile()
+ {
+ var pluginDirectory = new DirectoryInfo(Service.Get().PluginDirectory!);
+ var pluginDefs = new List();
+
+ Log.Information($"Now migrating plugins at {pluginDirectory} into profiles");
+
+ // Nothing to migrate
+ if (!pluginDirectory.Exists)
+ {
+ Log.Information("\t=> Plugin directory didn't exist, nothing to migrate");
+ return;
+ }
+
+ // Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
+ foreach (var pluginDir in pluginDirectory.GetDirectories())
+ {
+ var versionsDefs = new List();
+ foreach (var versionDir in pluginDir.GetDirectories())
+ {
+ try
+ {
+ var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll"));
+ var manifestFile = LocalPluginManifest.GetManifestFile(dllFile);
+
+ if (!manifestFile.Exists)
+ continue;
+
+ var manifest = LocalPluginManifest.Load(manifestFile);
+ versionsDefs.Add(new PluginDef(dllFile, manifest, false));
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Could not load manifest for installed at {Directory}", versionDir.FullName);
+ }
+ }
+
+ try
+ {
+ pluginDefs.Add(versionsDefs.MaxBy(x => x.Manifest!.EffectiveVersion));
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Couldn't choose best version for plugin: {Name}", pluginDir.Name);
+ }
+ }
+
+ var defaultProfile = this.DefaultProfile;
+ foreach (var pluginDef in pluginDefs)
+ {
+ if (pluginDef.Manifest == null)
+ {
+ Log.Information($"\t=> Skipping DLL at {pluginDef.DllFile.FullName}, no valid manifest");
+ continue;
+ }
+
+ // OK for migration code
+#pragma warning disable CS0618
+ defaultProfile.AddOrUpdate(pluginDef.Manifest.InternalName, !pluginDef.Manifest.Disabled, false);
+ Log.Information(
+ $"\t=> Added {pluginDef.Manifest.InternalName} to default profile with {!pluginDef.Manifest.Disabled}");
+#pragma warning restore CS0618
+ }
+ }
+ */
+}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
new file mode 100644
index 000000000..06075a07e
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
@@ -0,0 +1,40 @@
+using System;
+
+using Dalamud.Utility;
+using Newtonsoft.Json;
+
+namespace Dalamud.Plugin.Internal.Profiles;
+
+public abstract class ProfileModel
+{
+ [JsonProperty("id")]
+ public Guid Guid { get; set; } = Guid.Empty;
+
+ [JsonProperty("n")]
+ public string Name { get; set; } = "New Profile";
+
+ public static ProfileModel? Deserialize(string model)
+ {
+ var json = Util.DecompressString(Convert.FromBase64String(model.Substring(3)));
+
+ if (model.StartsWith(ProfileModelV1.SerializedPrefix))
+ return JsonConvert.DeserializeObject(json);
+
+ throw new ArgumentException("Was not a compressed style model.");
+ }
+
+ public string Serialize()
+ {
+ string prefix;
+ switch (this)
+ {
+ case ProfileModelV1:
+ prefix = ProfileModelV1.SerializedPrefix;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return prefix + Convert.ToBase64String(Util.CompressString(JsonConvert.SerializeObject(this)));
+ }
+}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
new file mode 100644
index 000000000..cae585c30
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Dalamud.Plugin.Internal.Profiles;
+
+public class ProfileModelV1 : ProfileModel
+{
+ public static string SerializedPrefix => "DP1";
+
+ [JsonProperty("b")]
+ public bool AlwaysEnableOnBoot { get; set; } = false;
+
+ [JsonProperty("e")]
+ public bool IsEnabled { get; set; } = false;
+
+ [JsonProperty("c")]
+ public uint Color { get; set; }
+
+ public List Plugins { get; set; } = new();
+
+ public class ProfileModelV1Plugin
+ {
+ public string InternalName { get; set; }
+
+ public bool IsEnabled { get; set; }
+ }
+}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs b/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
new file mode 100644
index 000000000..21856e572
--- /dev/null
+++ b/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
@@ -0,0 +1,14 @@
+namespace Dalamud.Plugin.Internal.Profiles;
+
+internal class ProfilePluginEntry
+{
+ public ProfilePluginEntry(string internalName, bool state)
+ {
+ this.InternalName = internalName;
+ this.IsEnabled = state;
+ }
+
+ public string InternalName { get; }
+
+ public bool IsEnabled { get; }
+}
diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
index 2c77ff528..db5556662 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
@@ -14,6 +14,7 @@ using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Loader;
+using Dalamud.Plugin.Internal.Profiles;
using Dalamud.Utility;
namespace Dalamud.Plugin.Internal.Types;
@@ -199,10 +200,20 @@ internal class LocalPlugin : IDisposable
///
public bool IsLoaded => this.State == PluginState.Loaded;
+ /*
///
/// Gets a value indicating whether the plugin is disabled.
///
+ [Obsolete("This is no longer accurate, use the profile manager instead.", true)]
public bool IsDisabled => this.Manifest.Disabled;
+ */
+
+ ///
+ /// Gets a value indicating whether this plugin is wanted active by any profile.
+ /// INCLUDES the default profile.
+ ///
+ public bool IsWantedByAnyProfile =>
+ Service.Get().GetWantState(this.Manifest.InternalName, false, false);
///
/// Gets a value indicating whether this plugin's API level is out of date.
@@ -238,6 +249,12 @@ internal class LocalPlugin : IDisposable
///
public bool IsDev => this is LocalDevPlugin;
+ ///
+ /// Gets a value indicating whether this plugin should be allowed to load.
+ ///
+ public bool ApplicableForLoad => !this.IsBanned && !this.IsDecommissioned && !this.IsOrphaned && !this.IsOutdated
+ && !(!this.IsDev && this.State == PluginState.UnloadError) && this.CheckPolicy();
+
///
public void Dispose()
{
@@ -275,7 +292,6 @@ internal class LocalPlugin : IDisposable
/// A task.
public async Task LoadAsync(PluginLoadReason reason, bool reloading = false)
{
- var configuration = await Service.GetAsync();
var framework = await Service.GetAsync();
var ioc = await Service.GetAsync();
var pluginManager = await Service.GetAsync();
@@ -297,6 +313,10 @@ internal class LocalPlugin : IDisposable
this.ReloadManifest();
}
+ // If we reload a plugin we don't want to delete it. Makes sense, right?
+ this.Manifest.ScheduledForDeletion = false;
+ this.SaveManifest();
+
switch (this.State)
{
case PluginState.Loaded:
@@ -329,8 +349,9 @@ internal class LocalPlugin : IDisposable
if (this.Manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels)
throw new InvalidPluginOperationException($"Unable to load {this.Name}, incompatible API level");
- if (this.Manifest.Disabled)
- throw new InvalidPluginOperationException($"Unable to load {this.Name}, disabled");
+ // TODO: should we throw here?
+ if (!this.IsWantedByAnyProfile)
+ Log.Warning("{Name} is loading, but isn't wanted by any profile", this.Name);
if (this.IsOrphaned)
throw new InvalidPluginOperationException($"Plugin {this.Name} had no associated repo.");
@@ -547,9 +568,11 @@ internal class LocalPlugin : IDisposable
await this.LoadAsync(PluginLoadReason.Reload, true);
}
+ /*
///
/// Revert a disable. Must be unloaded first, does not load.
///
+ [Obsolete("Profile API", true)]
public void Enable()
{
// Allowed: Unloaded, UnloadError
@@ -580,6 +603,7 @@ internal class LocalPlugin : IDisposable
this.Manifest.ScheduledForDeletion = false;
this.SaveManifest();
}
+ */
///
/// Check if anything forbids this plugin from loading.
@@ -602,9 +626,11 @@ internal class LocalPlugin : IDisposable
return true;
}
+ /*
///
/// Disable this plugin, must be unloaded first.
///
+ [Obsolete("Profile API", true)]
public void Disable()
{
// Allowed: Unloaded, UnloadError
@@ -631,6 +657,7 @@ internal class LocalPlugin : IDisposable
this.Manifest.Disabled = true;
this.SaveManifest();
}
+ */
///
/// Schedule the deletion of this plugin on next cleanup.
@@ -650,9 +677,9 @@ internal class LocalPlugin : IDisposable
var manifest = LocalPluginManifest.GetManifestFile(this.DllFile);
if (manifest.Exists)
{
- var isDisabled = this.IsDisabled; // saving the internal state because it could have been deleted
+ //var isDisabled = this.IsDisabled; // saving the internal state because it could have been deleted
this.Manifest = LocalPluginManifest.Load(manifest);
- this.Manifest.Disabled = isDisabled;
+ //this.Manifest.Disabled = isDisabled;
this.SaveManifest();
}
diff --git a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs
index 52cc55b23..8a21328c5 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs
@@ -28,6 +28,7 @@ internal record LocalPluginManifest : PluginManifest
/// Gets or sets a value indicating whether the plugin is disabled and should not be loaded.
/// This value supersedes the ".disabled" file functionality and should not be included in the plugin master.
///
+ [Obsolete("This is merely used for migrations now. Use the profile manager to check if a plugin shall be enabled.")]
public bool Disabled { get; set; }
///
diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs
index 66b17ed73..71051e666 100644
--- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs
+++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs
@@ -162,6 +162,12 @@ internal record PluginManifest
[JsonProperty]
public bool CanUnloadAsync { get; init; }
+ ///
+ /// Gets a value indicating whether the plugin supports profiles.
+ ///
+ [JsonProperty]
+ public bool SupportsProfiles { get; init; } = true;
+
///
/// Gets a list of screenshot image URLs to show in the plugin installer.
///
diff --git a/Dalamud/Plugin/PluginLoadReason.cs b/Dalamud/Plugin/PluginLoadReason.cs
index ade95ae67..d4c1a3b26 100644
--- a/Dalamud/Plugin/PluginLoadReason.cs
+++ b/Dalamud/Plugin/PluginLoadReason.cs
@@ -30,3 +30,5 @@ public enum PluginLoadReason
///
Boot,
}
+
+// TODO(api9): This should be a mask, so that we can combine Installer | ProfileLoaded
diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs
index 7837e1bab..e04be3334 100644
--- a/Dalamud/Utility/Util.cs
+++ b/Dalamud/Utility/Util.cs
@@ -11,12 +11,14 @@ using System.Runtime.CompilerServices;
using System.Text;
using Dalamud.Configuration.Internal;
+using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Logging.Internal;
using ImGuiNET;
+using Lumina.Excel.GeneratedSheets;
using Microsoft.Win32;
using Serilog;
@@ -605,6 +607,19 @@ public static class Util
File.Move(tmpPath, path, true);
}
+ ///
+ /// Gets a random, inoffensive, human-friendly string.
+ ///
+ /// A random human-friendly name.
+ internal static string GetRandomName()
+ {
+ var data = Service.Get();
+ var names = data.GetExcelSheet(ClientLanguage.English)!;
+ var rng = new Random();
+
+ return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString;
+ }
+
private static unsafe void ShowValue(ulong addr, IEnumerable path, Type type, object value)
{
if (type.IsPointer)
From e93e1cc806ec6e09e9dcda75aa0d4bf65283f0e9 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 20:08:10 +0200
Subject: [PATCH 02/68] add delete button to plugins in profiles
---
.../PluginInstaller/ProfileManagerWidget.cs | 27 ++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index fcb941d95..c9fe5ba84 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -131,7 +131,7 @@ internal class ProfileManagerWidget
ImGui.SameLine();
ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 3) - 5);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.FileExport))
+ if (ImGuiComponents.IconButton($"###exportButton{profile.Guid}", FontAwesomeIcon.FileExport))
{
ImGui.SetClipboardText(profile.Model.Serialize());
Service.Get().AddNotification("Copied to clipboard!", type: NotificationType.Success);
@@ -295,11 +295,13 @@ internal class ProfileManagerWidget
if (ImGui.BeginChild("###profileEditorPluginList"))
{
var pluginLineHeight = 32 * ImGuiHelpers.GlobalScale;
+ string? wantRemovePluginInternalName = null;
foreach (var plugin in profile.Plugins)
{
didAny = true;
var pmPlugin = pm.InstalledPlugins.FirstOrDefault(x => x.Manifest.InternalName == plugin.InternalName);
+ var btnOffset = 2;
if (pmPlugin != null)
{
@@ -336,10 +338,11 @@ internal class ProfileManagerWidget
if (available != null)
{
ImGui.SameLine();
- ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 32 * 2));
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 2) - 2);
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (ImGui.GetFrameHeight() / 2));
+ btnOffset = 3;
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Download))
+ if (ImGuiComponents.IconButton($"###installMissingPlugin{available.InternalName}", FontAwesomeIcon.Download))
{
this.installer.StartInstall(available, false);
}
@@ -361,6 +364,24 @@ internal class ProfileManagerWidget
Task.Run(() => profile.AddOrUpdate(plugin.InternalName, enabled))
.ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
}
+
+ ImGui.SameLine();
+ ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * btnOffset) - 5);
+ ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (ImGui.GetFrameHeight() / 2));
+
+ if (ImGuiComponents.IconButton($"###removePlugin{plugin.InternalName}", FontAwesomeIcon.Trash))
+ {
+ wantRemovePluginInternalName = plugin.InternalName;
+ }
+
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Remove plugin from this profile");
+ }
+
+ if (wantRemovePluginInternalName != null)
+ {
+ Task.Run(() => profile.Remove(wantRemovePluginInternalName))
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not remove plugin.");
}
if (!didAny)
From 2ed215b74b3f8be71f69981655dc0328d2123bd7 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 20:14:06 +0200
Subject: [PATCH 03/68] make sure no plugins are lost when deleting a profile
---
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index 7a8772d74..11214ba08 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -237,6 +237,12 @@ internal class ProfileManager : IServiceType
/// The profile to delete.
public void DeleteProfile(Profile profile)
{
+ // We need to remove all plugins from the profile first, so that they are re-added to the default profile if needed
+ foreach (var plugin in profile.Plugins)
+ {
+ profile.Remove(plugin.InternalName, false);
+ }
+
Debug.Assert(this.config.SavedProfiles!.Remove(profile.Model), "this.config.SavedProfiles!.Remove(profile.Model)");
Debug.Assert(this.profiles.Remove(profile), "this.profiles.Remove(profile)");
From 7fe004c8758889eeecb82ca65812c9b05cbb6fcd Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 20:25:46 +0200
Subject: [PATCH 04/68] remove plugins from deleting profile synchronously
---
.../Windows/PluginInstaller/ProfileManagerWidget.cs | 4 +++-
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 8 +++++---
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index c9fe5ba84..ab25c4c88 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -243,7 +243,9 @@ internal class ProfileManagerWidget
if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash))
{
- Task.Run(() => profman.DeleteProfile(profile))
+ // DeleteProfile() is sync, it doesn't apply and we are modifying the plugins collection. Will throw below when iterating
+ profman.DeleteProfile(profile);
+ Task.Run(() => profman.ApplyAllWantStates())
.ContinueWith(t =>
{
this.Reset();
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index 11214ba08..71835a9d6 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -232,13 +232,16 @@ internal class ProfileManager : IServiceType
}
///
- /// Delete a profile and re-apply all profiles.
+ /// Delete a profile.
///
+ ///
+ /// You should definitely apply states after this. It doesn't do it for you.
+ ///
/// The profile to delete.
public void DeleteProfile(Profile profile)
{
// We need to remove all plugins from the profile first, so that they are re-added to the default profile if needed
- foreach (var plugin in profile.Plugins)
+ foreach (var plugin in profile.Plugins.ToArray())
{
profile.Remove(plugin.InternalName, false);
}
@@ -246,7 +249,6 @@ internal class ProfileManager : IServiceType
Debug.Assert(this.config.SavedProfiles!.Remove(profile.Model), "this.config.SavedProfiles!.Remove(profile.Model)");
Debug.Assert(this.profiles.Remove(profile), "this.profiles.Remove(profile)");
- this.ApplyAllWantStates();
this.config.QueueSave();
}
From 8d7ae903a4644780235c2fdc5d7124cb3d4e4b50 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 20:28:06 +0200
Subject: [PATCH 05/68] actually delete profiles in release build
---
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index 71835a9d6..62ade19b2 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -246,8 +246,11 @@ internal class ProfileManager : IServiceType
profile.Remove(plugin.InternalName, false);
}
- Debug.Assert(this.config.SavedProfiles!.Remove(profile.Model), "this.config.SavedProfiles!.Remove(profile.Model)");
- Debug.Assert(this.profiles.Remove(profile), "this.profiles.Remove(profile)");
+ if (!this.config.SavedProfiles!.Remove(profile.Model))
+ throw new Exception("Couldn't remove profile from models");
+
+ if (!this.profiles.Remove(profile))
+ throw new Exception("Couldn't remove runtime profile");
this.config.QueueSave();
}
From 29ba86021c21c1ed50d02ce62864d172cb33d2a5 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 20:45:29 +0200
Subject: [PATCH 06/68] remove spammy log
---
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 2 --
1 file changed, 2 deletions(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index 62ade19b2..e5455cd44 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -68,8 +68,6 @@ internal class ProfileManager : IServiceType
foreach (var profile in this.profiles)
{
var state = profile.WantsPlugin(internalName);
- Log.Verbose("Checking {Name} in {Profile}: {State}", internalName, profile.Guid, state == null ? "null" : state);
-
if (state.HasValue)
{
want = want || (profile.IsEnabled && state.Value);
From 869ce9568d21e9673cf2d75cdd6c8eba0706ea27 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 21:03:15 +0200
Subject: [PATCH 07/68] make dev plugins work, maybe
---
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 3 ---
1 file changed, 3 deletions(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index e5455cd44..f189424a3 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -190,9 +190,6 @@ internal class ProfileManager : IServiceType
foreach (var installedPlugin in pm.InstalledPlugins)
{
- if (installedPlugin.IsDev)
- continue;
-
var wantThis = wantActive.Contains(installedPlugin.Manifest.InternalName);
switch (wantThis)
{
From 4328826c8682d8296d5607edde2c82e94e1d9f3e Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 21:07:21 +0200
Subject: [PATCH 08/68] never ever update dev plugins
---
.../PluginInstaller/PluginInstallerWindow.cs | 2 +-
Dalamud/Plugin/Internal/PluginManager.cs | 42 ++++++-------------
2 files changed, 14 insertions(+), 30 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index 73ef523e9..4c9074043 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -2176,7 +2176,7 @@ internal class PluginInstallerWindow : Window, IDisposable
this.DrawSendFeedbackButton(plugin.Manifest, plugin.IsTesting);
}
- if (availablePluginUpdate != default)
+ if (availablePluginUpdate != default && ! plugin.IsDev)
this.DrawUpdateSinglePluginButton(availablePluginUpdate);
ImGui.SameLine();
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index a647c1d03..15d975d0b 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -1144,44 +1144,28 @@ Thanks and have fun!";
if (plugin.IsDev)
{
- // TODO: Does this ever work? Why? We should never update devplugins
- try
- {
- plugin.DllFile.Delete();
- lock (this.pluginListLock)
- {
- this.InstalledPlugins = this.InstalledPlugins.Remove(plugin);
- }
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Error during delete (update)");
- updateStatus.WasUpdated = false;
- return updateStatus;
- }
+ throw new Exception("We should never update a dev plugin");
}
- else
+
+ try
{
- try
- {
- // TODO: Why were we ever doing this? We should never be loading the old version in the first place
- /*
+ // TODO: Why were we ever doing this? We should never be loading the old version in the first place
+ /*
if (!plugin.IsDisabled)
plugin.Disable();
*/
- lock (this.pluginListLock)
- {
- this.InstalledPlugins = this.InstalledPlugins.Remove(plugin);
- }
- }
- catch (Exception ex)
+ lock (this.pluginListLock)
{
- Log.Error(ex, "Error during disable (update)");
- updateStatus.WasUpdated = false;
- return updateStatus;
+ this.InstalledPlugins = this.InstalledPlugins.Remove(plugin);
}
}
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Error during disable (update)");
+ updateStatus.WasUpdated = false;
+ return updateStatus;
+ }
// We need to handle removed DTR nodes here, as otherwise, plugins will not be able to re-add their bar entries after updates.
var dtr = Service.Get();
From b0906af1ba8b89c32dbe8a8c95bba31b3f8e6268 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 21:18:23 +0200
Subject: [PATCH 09/68] prevent some collection is modified errors
---
.../Windows/PluginInstaller/ProfileManagerWidget.cs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index ab25c4c88..ebabe4436 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -206,7 +206,10 @@ internal class ProfileManagerWidget
{
if (ImGui.Button("Do it") && selected != null)
{
- Task.Run(() => profile.AddOrUpdate(selected.Manifest.InternalName, selected.IsLoaded));
+ // TODO: handle error
+ profile.AddOrUpdate(selected.Manifest.InternalName, selected.IsLoaded, false);
+ Task.Run(() => profman.ApplyAllWantStates())
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
}
}
@@ -382,7 +385,9 @@ internal class ProfileManagerWidget
if (wantRemovePluginInternalName != null)
{
- Task.Run(() => profile.Remove(wantRemovePluginInternalName))
+ // TODO: handle error
+ profile.Remove(wantRemovePluginInternalName, false);
+ Task.Run(() => profman.ApplyAllWantStates())
.ContinueWith(this.installer.DisplayErrorContinuation, "Could not remove plugin.");
}
From 91ada49fa7df78f2a82db07ac462050422bc617a Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 21:20:36 +0200
Subject: [PATCH 10/68] always add plugins as enabled in profile editor
---
.../Internal/Windows/PluginInstaller/ProfileManagerWidget.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index ebabe4436..005e38040 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -204,10 +204,10 @@ internal class ProfileManagerWidget
using (ImRaii.Disabled(this.pickerSelectedPluginInternalName == null))
{
- if (ImGui.Button("Do it") && selected != null)
+ if (ImGui.Button("Add plugin") && selected != null)
{
// TODO: handle error
- profile.AddOrUpdate(selected.Manifest.InternalName, selected.IsLoaded, false);
+ profile.AddOrUpdate(selected.Manifest.InternalName, true, false);
Task.Run(() => profman.ApplyAllWantStates())
.ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
}
From 4b2f649215c0a59c6dac709d246270c692b4775e Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 22:53:35 +0200
Subject: [PATCH 11/68] feedback for profile imports
---
.../Internal/Windows/PluginInstaller/ProfileManagerWidget.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 005e38040..0545b1656 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -69,10 +69,12 @@ internal class ProfileManagerWidget
try
{
profman.ImportProfile(ImGui.GetClipboardText());
+ Service.Get().AddNotification("Profile successfully imported!", type: NotificationType.Success);
}
catch (Exception ex)
{
Log.Error(ex, "Could not import profile");
+ Service.Get().AddNotification("Could not import profile.", type: NotificationType.Error);
}
}
From ea01e629364bab95fc2cb30844538102163130b8 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 22:54:19 +0200
Subject: [PATCH 12/68] reset widget before deleting profile
---
.../Internal/Windows/PluginInstaller/ProfileManagerWidget.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 0545b1656..6a60f6960 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -248,12 +248,13 @@ internal class ProfileManagerWidget
if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash))
{
+ this.Reset();
+
// DeleteProfile() is sync, it doesn't apply and we are modifying the plugins collection. Will throw below when iterating
profman.DeleteProfile(profile);
Task.Run(() => profman.ApplyAllWantStates())
.ContinueWith(t =>
{
- this.Reset();
this.installer.DisplayErrorContinuation(t, "Could not refresh profiles.");
});
}
From ffb0a0c41db1c57c076a89ab652cacd81e757344 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 22:59:44 +0200
Subject: [PATCH 13/68] disable search and sort when in profile editor
---
.../PluginInstaller/PluginInstallerWindow.cs | 83 ++++++++++---------
1 file changed, 42 insertions(+), 41 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index 4c9074043..e873b1047 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -15,6 +15,7 @@ using Dalamud.Game.Command;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.Notifications;
+using Dalamud.Interface.Raii;
using Dalamud.Interface.Style;
using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal;
@@ -219,7 +220,7 @@ internal class PluginInstallerWindow : Window, IDisposable
this.updatePluginCount = 0;
this.updatedPlugins = null;
}
-
+
this.profileManagerWidget.Reset();
}
@@ -482,54 +483,54 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - (style.ItemSpacing.X * 2) - searchInputWidth - searchClearButtonWidth);
- var searchTextChanged = false;
- ImGui.SetNextItemWidth(searchInputWidth);
- searchTextChanged |= ImGui.InputTextWithHint(
- "###XlPluginInstaller_Search",
- Locs.Header_SearchPlaceholder,
- ref this.searchText,
- 100);
-
- ImGui.SameLine();
- ImGui.SetCursorPosY(downShift);
-
- ImGui.SetNextItemWidth(searchClearButtonWidth);
- if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
+ // Disable search if profile editor
+ using (ImRaii.Disabled(this.categoryManager.CurrentCategoryIdx == 14))
{
- this.searchText = string.Empty;
- searchTextChanged = true;
- }
+ var searchTextChanged = false;
+ ImGui.SetNextItemWidth(searchInputWidth);
+ searchTextChanged |= ImGui.InputTextWithHint(
+ "###XlPluginInstaller_Search",
+ Locs.Header_SearchPlaceholder,
+ ref this.searchText,
+ 100);
- if (searchTextChanged)
- this.UpdateCategoriesOnSearchChange();
+ ImGui.SameLine();
+ ImGui.SetCursorPosY(downShift);
- // Changelog group
- var isSortDisabled = this.categoryManager.CurrentGroupIdx == 3;
- if (isSortDisabled)
- ImGui.BeginDisabled();
-
- ImGui.SameLine();
- ImGui.SetCursorPosY(downShift);
- ImGui.SetNextItemWidth(selectableWidth);
- if (ImGui.BeginCombo(sortByText, this.filterText, ImGuiComboFlags.NoArrowButton))
- {
- foreach (var selectable in sortSelectables)
+ ImGui.SetNextItemWidth(searchClearButtonWidth);
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
{
- if (ImGui.Selectable(selectable.Localization))
- {
- this.sortKind = selectable.SortKind;
- this.filterText = selectable.Localization;
-
- lock (this.listLock)
- this.ResortPlugins();
- }
+ this.searchText = string.Empty;
+ searchTextChanged = true;
}
- ImGui.EndCombo();
+ if (searchTextChanged)
+ this.UpdateCategoriesOnSearchChange();
}
- if (isSortDisabled)
- ImGui.EndDisabled();
+ // Disable sort if changelogs or profile editor
+ using (ImRaii.Disabled(this.categoryManager.CurrentGroupIdx == 3 || this.categoryManager.CurrentCategoryIdx == 14))
+ {
+ ImGui.SameLine();
+ ImGui.SetCursorPosY(downShift);
+ ImGui.SetNextItemWidth(selectableWidth);
+ if (ImGui.BeginCombo(sortByText, this.filterText, ImGuiComboFlags.NoArrowButton))
+ {
+ foreach (var selectable in sortSelectables)
+ {
+ if (ImGui.Selectable(selectable.Localization))
+ {
+ this.sortKind = selectable.SortKind;
+ this.filterText = selectable.Localization;
+
+ lock (this.listLock)
+ this.ResortPlugins();
+ }
+ }
+
+ ImGui.EndCombo();
+ }
+ }
}
private void DrawFooter()
From 92f638d1148ccdb6cc3068c11374a1fe92cc438e Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 10 Apr 2023 23:11:37 +0200
Subject: [PATCH 14/68] remove unprocessed profile commands when another one is
queued
---
.../Profiles/ProfileCommandHandler.cs | 48 ++++++++++++-------
1 file changed, 30 insertions(+), 18 deletions(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
index 995f91f5f..994069335 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
@@ -12,6 +12,9 @@ using Serilog;
namespace Dalamud.Plugin.Internal.Profiles;
+///
+/// Service responsible for profile-related chat commands.
+///
[ServiceManager.EarlyLoadedService]
internal class ProfileCommandHandler : IServiceType, IDisposable
{
@@ -20,12 +23,15 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
private readonly ChatGui chat;
private readonly Framework framework;
- private Queue<(string, ProfileOp)> queue = new();
+ private List<(string, ProfileOp)> queue = new();
///
/// Initializes a new instance of the class.
///
- ///
+ /// Command handler.
+ /// Profile manager.
+ /// Chat handler.
+ /// Framework.
[ServiceManager.ServiceConstructor]
public ProfileCommandHandler(CommandManager cmd, ProfileManager profileManager, ChatGui chat, Framework framework)
{
@@ -36,32 +42,45 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
this.cmd.AddHandler("/xlenableprofile", new CommandInfo(this.OnEnableProfile)
{
- HelpMessage = "",
+ HelpMessage = Loc.Localize("ProfileCommandsEnableHint", "Enable a profile. Usage: /xlenableprofile \"Profile Name\""),
ShowInHelp = true,
});
this.cmd.AddHandler("/xldisableprofile", new CommandInfo(this.OnDisableProfile)
{
- HelpMessage = "",
+ HelpMessage = Loc.Localize("ProfileCommandsDisableHint", "Disable a profile. Usage: /xldisableprofile \"Profile Name\""),
ShowInHelp = true,
});
this.cmd.AddHandler("/xltoggleprofile", new CommandInfo(this.OnToggleProfile)
{
- HelpMessage = "",
+ HelpMessage = Loc.Localize("ProfileCommandsToggleHint", "Toggle a profile. Usage: /xltoggleprofile \"Profile Name\""),
ShowInHelp = true,
});
this.framework.Update += this.FrameworkOnUpdate;
}
+ ///
+ public void Dispose()
+ {
+ this.cmd.RemoveHandler("/xlenableprofile");
+ this.cmd.RemoveHandler("/xldisableprofile");
+ this.cmd.RemoveHandler("/xltoggleprofile");
+
+ this.framework.Update += this.FrameworkOnUpdate;
+ }
+
private void FrameworkOnUpdate(Framework framework1)
{
if (this.profileManager.IsBusy)
return;
- if (this.queue.TryDequeue(out var op))
+ if (this.queue.Count > 0)
{
+ var op = this.queue[0];
+ this.queue.RemoveAt(0);
+
var profile = this.profileManager.Profiles.FirstOrDefault(x => x.Name == op.Item1);
if (profile == null || profile.IsDefaultProfile)
return;
@@ -107,22 +126,14 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
}
}
- public void Dispose()
- {
- this.cmd.RemoveHandler("/xlenableprofile");
- this.cmd.RemoveHandler("/xldisableprofile");
- this.cmd.RemoveHandler("/xltoggleprofile");
-
- this.framework.Update += this.FrameworkOnUpdate;
- }
-
private void OnEnableProfile(string command, string arguments)
{
var name = this.ValidateName(arguments);
if (name == null)
return;
- this.queue.Enqueue((name, ProfileOp.Enable));
+ this.queue = this.queue.Where(x => x.Item1 != name).ToList();
+ this.queue.Add((name, ProfileOp.Enable));
}
private void OnDisableProfile(string command, string arguments)
@@ -131,7 +142,8 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
if (name == null)
return;
- this.queue.Enqueue((name, ProfileOp.Disable));
+ this.queue = this.queue.Where(x => x.Item1 != name).ToList();
+ this.queue.Add((name, ProfileOp.Disable));
}
private void OnToggleProfile(string command, string arguments)
@@ -140,7 +152,7 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
if (name == null)
return;
- this.queue.Enqueue((name, ProfileOp.Toggle));
+ this.queue.Add((name, ProfileOp.Toggle));
}
private string? ValidateName(string arguments)
From 7028f5072bd09f09830810046404088f4e8fdb74 Mon Sep 17 00:00:00 2001
From: goat
Date: Tue, 11 Apr 2023 00:49:12 +0200
Subject: [PATCH 15/68] cool new plugin picker
---
.../PluginInstaller/ProfileManagerWidget.cs | 55 ++++++++++---------
1 file changed, 30 insertions(+), 25 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 6a60f6960..42dda032d 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -9,6 +9,7 @@ using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Raii;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Profiles;
+using Dalamud.Utility;
using ImGuiNET;
using Serilog;
@@ -20,7 +21,7 @@ internal class ProfileManagerWidget
private Mode mode = Mode.Overview;
private Guid? editingProfileGuid;
- private string? pickerSelectedPluginInternalName = null;
+ private string pickerSearch = string.Empty;
private string profileNameEdit = string.Empty;
public ProfileManagerWidget(PluginInstallerWindow installer)
@@ -46,7 +47,7 @@ internal class ProfileManagerWidget
{
this.mode = Mode.Overview;
this.editingProfileGuid = null;
- this.pickerSelectedPluginInternalName = null;
+ this.pickerSearch = string.Empty;
}
private void DrawOverview()
@@ -185,37 +186,38 @@ internal class ProfileManagerWidget
}
const string addPluginToProfilePopup = "###addPluginToProfile";
- if (ImGui.BeginPopup(addPluginToProfilePopup))
+ using (var popup = ImRaii.Popup(addPluginToProfilePopup))
{
- var selected =
- pm.InstalledPlugins.FirstOrDefault(
- x => x.Manifest.InternalName == this.pickerSelectedPluginInternalName);
-
- if (ImGui.BeginCombo("###pluginPicker", selected == null ? "Pick one" : selected.Manifest.Name))
+ if (popup.Success)
{
- foreach (var plugin in pm.InstalledPlugins.Where(x => x.Manifest.SupportsProfiles))
+ var width = ImGuiHelpers.GlobalScale * 300;
+
+ using var disabled = ImRaii.Disabled(profman.IsBusy);
+
+ ImGui.SetNextItemWidth(width);
+ ImGui.InputTextWithHint("###pluginPickerSearch", "Search...", ref this.pickerSearch, 255);
+
+ if (ImGui.BeginListBox("###pluginPicker", new Vector2(width, width - 80)))
{
- if (ImGui.Selectable($"{plugin.Manifest.Name}###selector{plugin.Manifest.InternalName}"))
+ // TODO: Plugin searching should be abstracted... installer and this should use the same search
+ foreach (var plugin in pm.InstalledPlugins.Where(x => x.Manifest.SupportsProfiles &&
+ (this.pickerSearch.IsNullOrWhitespace() || x.Manifest.Name.ToLowerInvariant().Contains(this.pickerSearch.ToLowerInvariant()))))
{
- this.pickerSelectedPluginInternalName = plugin.Manifest.InternalName;
+ using var disabled2 =
+ ImRaii.Disabled(profile.Plugins.Any(y => y.InternalName == plugin.Manifest.InternalName));
+
+ if (ImGui.Selectable($"{plugin.Manifest.Name}###selector{plugin.Manifest.InternalName}"))
+ {
+ // TODO this sucks
+ profile.AddOrUpdate(plugin.Manifest.InternalName, true, false);
+ Task.Run(() => profman.ApplyAllWantStates())
+ .ContinueWith(this.installer.DisplayErrorContinuation, "Could not apply profiles.");
+ }
}
- }
- ImGui.EndCombo();
- }
-
- using (ImRaii.Disabled(this.pickerSelectedPluginInternalName == null))
- {
- if (ImGui.Button("Add plugin") && selected != null)
- {
- // TODO: handle error
- profile.AddOrUpdate(selected.Manifest.InternalName, true, false);
- Task.Run(() => profman.ApplyAllWantStates())
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
+ ImGui.EndListBox();
}
}
-
- ImGui.EndPopup();
}
var didAny = false;
@@ -419,7 +421,10 @@ internal class ProfileManagerWidget
}
if (wantPluginAddPopup)
+ {
+ this.pickerSearch = string.Empty;
ImGui.OpenPopup(addPluginToProfilePopup);
+ }
}
private enum Mode
From 3f112376ebd3e772f3792ee059a9e98259c51418 Mon Sep 17 00:00:00 2001
From: goat
Date: Tue, 11 Apr 2023 21:45:26 +0200
Subject: [PATCH 16/68] make "start on boot" work as expected for dev plugins
---
.../PluginInstaller/PluginInstallerWindow.cs | 30 +++++++++++--------
Dalamud/Plugin/Internal/PluginManager.cs | 15 +++++++---
2 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index 560c1e61a..b2dfe445e 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -2581,26 +2581,32 @@ internal class PluginInstallerWindow : Window, IDisposable
if (localPlugin is LocalDevPlugin plugin)
{
+ var isInDefaultProfile =
+ Service.Get().IsInDefaultProfile(localPlugin.Manifest.InternalName);
+
// https://colorswall.com/palette/2868/
var greenColor = new Vector4(0x5C, 0xB8, 0x5C, 0xFF) / 0xFF;
var redColor = new Vector4(0xD9, 0x53, 0x4F, 0xFF) / 0xFF;
// Load on boot
- ImGui.PushStyleColor(ImGuiCol.Button, plugin.StartOnBoot ? greenColor : redColor);
- ImGui.PushStyleColor(ImGuiCol.ButtonHovered, plugin.StartOnBoot ? greenColor : redColor);
-
- ImGui.SameLine();
- if (ImGuiComponents.IconButton(FontAwesomeIcon.PowerOff))
+ using (ImRaii.Disabled(!isInDefaultProfile))
{
- plugin.StartOnBoot ^= true;
- configuration.QueueSave();
- }
+ ImGui.PushStyleColor(ImGuiCol.Button, plugin.StartOnBoot ? greenColor : redColor);
+ ImGui.PushStyleColor(ImGuiCol.ButtonHovered, plugin.StartOnBoot ? greenColor : redColor);
- ImGui.PopStyleColor(2);
+ ImGui.SameLine();
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.PowerOff))
+ {
+ plugin.StartOnBoot ^= true;
+ configuration.QueueSave();
+ }
- if (ImGui.IsItemHovered())
- {
- ImGui.SetTooltip(Locs.PluginButtonToolTip_StartOnBoot);
+ ImGui.PopStyleColor(2);
+
+ if (ImGui.IsItemHovered())
+ {
+ ImGui.SetTooltip(isInDefaultProfile ? Locs.PluginButtonToolTip_StartOnBoot : Locs.PluginButtonToolTip_NeedsToBeInDefault);
+ }
}
// Automatic reload
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index c382ce3d8..781732776 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -862,10 +862,17 @@ Thanks and have fun!";
var devPlugin = new LocalDevPlugin(dllFile, manifest);
loadPlugin &= !isBoot || devPlugin.StartOnBoot;
- // If we're not loading it, make sure it's disabled
- // NOTE: Should be taken care of below by the profile code
- // if (!loadPlugin && !devPlugin.IsDisabled)
- // devPlugin.Disable();
+ var probablyInternalNameForThisPurpose = manifest?.InternalName ?? dllFile.Name;
+ var wantsInDefaultProfile =
+ this.profileManager.DefaultProfile.WantsPlugin(probablyInternalNameForThisPurpose);
+ if (wantsInDefaultProfile == false && devPlugin.StartOnBoot)
+ {
+ this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, true, false);
+ }
+ else if (wantsInDefaultProfile == true && !devPlugin.StartOnBoot)
+ {
+ this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, false, false);
+ }
plugin = devPlugin;
}
From 75bf13597891756308d918654351f905a2149ed8 Mon Sep 17 00:00:00 2001
From: goat
Date: Tue, 11 Apr 2023 22:02:30 +0200
Subject: [PATCH 17/68] clean up commented out code
---
.../Internal/Profiles/ProfileManager.cs | 82 +------------------
Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 80 +-----------------
2 files changed, 2 insertions(+), 160 deletions(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index f189424a3..b7073ac99 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -268,21 +268,9 @@ internal class ProfileManager : IServiceType
private void LoadProfilesFromConfigInitially()
{
- var needMigration = false;
- if (this.config.DefaultProfile == null)
- {
- this.config.DefaultProfile = new ProfileModelV1();
- needMigration = true;
- }
-
+ this.config.DefaultProfile ??= new ProfileModelV1();
this.profiles.Add(new Profile(this, this.config.DefaultProfile, true, true));
- if (needMigration)
- {
- // Don't think we need this here with the migration logic in GetWantState
- //this.MigratePluginsIntoDefaultProfile();
- }
-
this.config.SavedProfiles ??= new List();
foreach (var profileModel in this.config.SavedProfiles)
{
@@ -291,72 +279,4 @@ internal class ProfileManager : IServiceType
this.config.QueueSave();
}
-
- // This duplicates some of the original handling in PM; don't care though
- /*
- private void MigratePluginsIntoDefaultProfile()
- {
- var pluginDirectory = new DirectoryInfo(Service.Get().PluginDirectory!);
- var pluginDefs = new List();
-
- Log.Information($"Now migrating plugins at {pluginDirectory} into profiles");
-
- // Nothing to migrate
- if (!pluginDirectory.Exists)
- {
- Log.Information("\t=> Plugin directory didn't exist, nothing to migrate");
- return;
- }
-
- // Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
- foreach (var pluginDir in pluginDirectory.GetDirectories())
- {
- var versionsDefs = new List();
- foreach (var versionDir in pluginDir.GetDirectories())
- {
- try
- {
- var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll"));
- var manifestFile = LocalPluginManifest.GetManifestFile(dllFile);
-
- if (!manifestFile.Exists)
- continue;
-
- var manifest = LocalPluginManifest.Load(manifestFile);
- versionsDefs.Add(new PluginDef(dllFile, manifest, false));
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Could not load manifest for installed at {Directory}", versionDir.FullName);
- }
- }
-
- try
- {
- pluginDefs.Add(versionsDefs.MaxBy(x => x.Manifest!.EffectiveVersion));
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Couldn't choose best version for plugin: {Name}", pluginDir.Name);
- }
- }
-
- var defaultProfile = this.DefaultProfile;
- foreach (var pluginDef in pluginDefs)
- {
- if (pluginDef.Manifest == null)
- {
- Log.Information($"\t=> Skipping DLL at {pluginDef.DllFile.FullName}, no valid manifest");
- continue;
- }
-
- // OK for migration code
-#pragma warning disable CS0618
- defaultProfile.AddOrUpdate(pluginDef.Manifest.InternalName, !pluginDef.Manifest.Disabled, false);
- Log.Information(
- $"\t=> Added {pluginDef.Manifest.InternalName} to default profile with {!pluginDef.Manifest.Disabled}");
-#pragma warning restore CS0618
- }
- }
- */
}
diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
index db5556662..2ef0269a3 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
@@ -200,14 +200,6 @@ internal class LocalPlugin : IDisposable
///
public bool IsLoaded => this.State == PluginState.Loaded;
- /*
- ///
- /// Gets a value indicating whether the plugin is disabled.
- ///
- [Obsolete("This is no longer accurate, use the profile manager instead.", true)]
- public bool IsDisabled => this.Manifest.Disabled;
- */
-
///
/// Gets a value indicating whether this plugin is wanted active by any profile.
/// INCLUDES the default profile.
@@ -349,7 +341,7 @@ internal class LocalPlugin : IDisposable
if (this.Manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels)
throw new InvalidPluginOperationException($"Unable to load {this.Name}, incompatible API level");
- // TODO: should we throw here?
+ // We might want to throw here?
if (!this.IsWantedByAnyProfile)
Log.Warning("{Name} is loading, but isn't wanted by any profile", this.Name);
@@ -568,43 +560,6 @@ internal class LocalPlugin : IDisposable
await this.LoadAsync(PluginLoadReason.Reload, true);
}
- /*
- ///
- /// Revert a disable. Must be unloaded first, does not load.
- ///
- [Obsolete("Profile API", true)]
- public void Enable()
- {
- // Allowed: Unloaded, UnloadError
- switch (this.State)
- {
- case PluginState.Loading:
- case PluginState.Unloading:
- case PluginState.Loaded:
- case PluginState.LoadError:
- throw new InvalidPluginOperationException($"Unable to enable {this.Name}, still loaded");
- case PluginState.Unloaded:
- break;
- case PluginState.UnloadError:
- break;
- case PluginState.DependencyResolutionFailed:
- throw new InvalidPluginOperationException($"Unable to enable {this.Name}, dependency resolution failed");
- default:
- throw new ArgumentOutOfRangeException(this.State.ToString());
- }
-
- // NOTE(goat): This is inconsequential, and we do have situations where a plugin can end up enabled but not loaded:
- // Orphaned plugins can have their repo added back, but may not have been loaded at boot and may still be enabled.
- // We don't want to disable orphaned plugins when they are orphaned so this is how it's going to be.
- // if (!this.Manifest.Disabled)
- // throw new InvalidPluginOperationException($"Unable to enable {this.Name}, not disabled");
-
- this.Manifest.Disabled = false;
- this.Manifest.ScheduledForDeletion = false;
- this.SaveManifest();
- }
- */
-
///
/// Check if anything forbids this plugin from loading.
///
@@ -626,39 +581,6 @@ internal class LocalPlugin : IDisposable
return true;
}
- /*
- ///
- /// Disable this plugin, must be unloaded first.
- ///
- [Obsolete("Profile API", true)]
- public void Disable()
- {
- // Allowed: Unloaded, UnloadError
- switch (this.State)
- {
- case PluginState.Loading:
- case PluginState.Unloading:
- case PluginState.Loaded:
- case PluginState.LoadError:
- throw new InvalidPluginOperationException($"Unable to disable {this.Name}, still loaded");
- case PluginState.Unloaded:
- break;
- case PluginState.UnloadError:
- break;
- case PluginState.DependencyResolutionFailed:
- return; // This is a no-op.
- default:
- throw new ArgumentOutOfRangeException(this.State.ToString());
- }
-
- if (this.Manifest.Disabled)
- throw new InvalidPluginOperationException($"Unable to disable {this.Name}, already disabled");
-
- this.Manifest.Disabled = true;
- this.SaveManifest();
- }
- */
-
///
/// Schedule the deletion of this plugin on next cleanup.
///
From cbfd4bbd2a09248913b0847ca8d70bd9e296e188 Mon Sep 17 00:00:00 2001
From: goat
Date: Wed, 12 Apr 2023 21:28:03 +0200
Subject: [PATCH 18/68] fix: skip a stack frame in the VEH callback handler
---
Dalamud/EntryPoint.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs
index 8fa9e70a0..d3c28011f 100644
--- a/Dalamud/EntryPoint.cs
+++ b/Dalamud/EntryPoint.cs
@@ -68,7 +68,7 @@ public sealed class EntryPoint
{
try
{
- return Marshal.StringToHGlobalUni(Environment.StackTrace);
+ return Marshal.StringToHGlobalUni(new StackTrace(1).ToString());
}
catch (Exception e)
{
From eb06636290122cadc3c824e6b5ddb85bb284357a Mon Sep 17 00:00:00 2001
From: goat
Date: Wed, 12 Apr 2023 21:43:12 +0200
Subject: [PATCH 19/68] enable profiles by default
---
Dalamud/Configuration/Internal/DalamudConfiguration.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 89396bd66..c870fa09d 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -280,7 +280,7 @@ internal sealed class DalamudConfiguration : IServiceType
///
/// Gets or sets a value indicating whether or not profiles are enabled.
///
- public bool ProfilesEnabled { get; set; } = false;
+ public bool ProfilesEnabled { get; set; } = true;
///
/// Gets or sets a value indicating whether or not Dalamud RMT filtering should be disabled.
From 5ad3ec123d0700d3065e4c902b107b832fe0ae95 Mon Sep 17 00:00:00 2001
From: goat
Date: Tue, 18 Apr 2023 20:37:05 +0200
Subject: [PATCH 20/68] localize profiles strings
---
.../Internal/PluginCategoryManager.cs | 4 +-
.../PluginInstaller/PluginInstallerWindow.cs | 31 +++--
.../PluginInstaller/ProfileManagerWidget.cs | 112 +++++++++++++-----
3 files changed, 110 insertions(+), 37 deletions(-)
diff --git a/Dalamud/Interface/Internal/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs
index 2af5d2354..9515a55b5 100644
--- a/Dalamud/Interface/Internal/PluginCategoryManager.cs
+++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs
@@ -435,8 +435,8 @@ internal class PluginCategoryManager
public static string Category_DevInstalled => Loc.Localize("InstallerInstalledDevPlugins", "Installed Dev Plugins");
public static string Category_IconTester => "Image/Icon Tester";
-
- public static string Category_PluginProfiles => Loc.Localize("InstallerCategoryPluginProfiles", "Plugin Profiles");
+
+ public static string Category_PluginProfiles => Loc.Localize("InstallerCategoryPluginProfiles", "Plugin Collections");
public static string Category_Other => Loc.Localize("InstallerCategoryOther", "Other");
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index b2dfe445e..a50be905f 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -428,7 +428,7 @@ internal class PluginInstallerWindow : Window, IDisposable
break;
case LoadingIndicatorKind.ProfilesLoading:
- ImGuiHelpers.CenteredText("Profiles are being applied...");
+ ImGuiHelpers.CenteredText("Collections are being applied...");
break;
default:
throw new ArgumentOutOfRangeException();
@@ -2321,12 +2321,12 @@ internal class PluginInstallerWindow : Window, IDisposable
if (inProfile)
{
Task.Run(() => profile.AddOrUpdate(plugin.Manifest.InternalName, true))
- .ContinueWith(this.DisplayErrorContinuation, "Couldn't add plugin to this profile.");
+ .ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotAdd);
}
else
{
Task.Run(() => profile.Remove(plugin.Manifest.InternalName))
- .ContinueWith(this.DisplayErrorContinuation, "Couldn't remove plugin from this profile.");
+ .ContinueWith(this.DisplayErrorContinuation, Locs.Profiles_CouldNotRemove);
}
}
@@ -2338,7 +2338,7 @@ internal class PluginInstallerWindow : Window, IDisposable
}
if (!didAny)
- ImGui.TextColored(ImGuiColors.DalamudGrey, "No profiles! Go add some!");
+ ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.Profiles_None);
ImGui.Separator();
@@ -2355,7 +2355,7 @@ internal class PluginInstallerWindow : Window, IDisposable
}
ImGui.SameLine();
- ImGui.Text("Remove from all profiles");
+ ImGui.Text(Locs.Profiles_RemoveFromAll);
ImGui.EndPopup();
}
@@ -3160,9 +3160,9 @@ internal class PluginInstallerWindow : Window, IDisposable
public static string PluginButtonToolTip_OpenConfiguration => Loc.Localize("InstallerOpenConfig", "Open Configuration");
- public static string PluginButtonToolTip_PickProfiles => Loc.Localize("InstallerPickProfiles", "Pick profiles for this plugin");
+ public static string PluginButtonToolTip_PickProfiles => Loc.Localize("InstallerPickProfiles", "Pick collections for this plugin");
- public static string PluginButtonToolTip_ProfilesNotSupported => Loc.Localize("InstallerProfilesNotSupported", "This plugin does not support profiles");
+ public static string PluginButtonToolTip_ProfilesNotSupported => Loc.Localize("InstallerProfilesNotSupported", "This plugin does not support collections");
public static string PluginButtonToolTip_StartOnBoot => Loc.Localize("InstallerStartOnBoot", "Start on boot");
@@ -3184,7 +3184,7 @@ internal class PluginInstallerWindow : Window, IDisposable
public static string PluginButtonToolTip_UnloadFailed => Loc.Localize("InstallerUnloadFailedTooltip", "Plugin unload failed, please restart your game and try again.");
- public static string PluginButtonToolTip_NeedsToBeInDefault => Loc.Localize("InstallerUnloadNeedsToBeInDefault", "This plugin is in one or more profiles. If you want to enable or disable it, please do so by enabling or disabling one of the profiles it is in.\nIf you want to manage it manually, remove it from all profiles.");
+ public static string PluginButtonToolTip_NeedsToBeInDefault => Loc.Localize("InstallerUnloadNeedsToBeInDefault", "This plugin is in one or more collections. If you want to enable or disable it, please do so by enabling or disabling the collections it is in.\nIf you want to manage it manually, remove it from all collections.");
#endregion
@@ -3345,5 +3345,20 @@ internal class PluginInstallerWindow : Window, IDisposable
public static string SafeModeDisclaimer => Loc.Localize("SafeModeDisclaimer", "You enabled safe mode, no plugins will be loaded.\nYou may delete plugins from the \"Installed plugins\" tab.\nSimply restart your game to disable safe mode.");
#endregion
+
+ #region Profiles
+
+ public static string Profiles_CouldNotAdd =>
+ Loc.Localize("InstallerProfilesCouldNotAdd", "Couldn't add plugin to this collection.");
+
+ public static string Profiles_CouldNotRemove =>
+ Loc.Localize("InstallerProfilesCouldNotRemove", "Couldn't remove plugin from this collection.");
+
+ public static string Profiles_None => Loc.Localize("InstallerProfilesNone", "No collections! Go add some in \"Plugin Collections\"!");
+
+ public static string Profiles_RemoveFromAll =>
+ Loc.Localize("InstallerProfilesRemoveFromAll", "Remove from all collections");
+
+ #endregion
}
}
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 42dda032d..1448074f3 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
+using CheapLoc;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.Notifications;
@@ -59,7 +60,7 @@ internal class ProfileManagerWidget
profman.AddNewProfile();
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Add a new profile");
+ ImGui.SetTooltip(Locs.AddProfile);
ImGui.SameLine();
ImGuiHelpers.ScaledDummy(5);
@@ -70,17 +71,17 @@ internal class ProfileManagerWidget
try
{
profman.ImportProfile(ImGui.GetClipboardText());
- Service.Get().AddNotification("Profile successfully imported!", type: NotificationType.Success);
+ Service.Get().AddNotification(Locs.NotificationImportSuccess, type: NotificationType.Success);
}
catch (Exception ex)
{
Log.Error(ex, "Could not import profile");
- Service.Get().AddNotification("Could not import profile.", type: NotificationType.Error);
+ Service.Get().AddNotification(Locs.NotificationImportError, type: NotificationType.Error);
}
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Import a shared profile from your clipboard");
+ ImGui.SetTooltip(Locs.ImportProfileHint);
ImGui.Separator();
ImGuiHelpers.ScaledDummy(5);
@@ -100,7 +101,7 @@ internal class ProfileManagerWidget
if (ImGuiComponents.ToggleButton($"###toggleButton{profile.Guid}", ref isEnabled))
{
Task.Run(() => profile.SetState(isEnabled))
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change profile state.");
+ .ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
}
ImGui.SameLine();
@@ -120,7 +121,7 @@ internal class ProfileManagerWidget
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Edit this profile");
+ ImGui.SetTooltip(Locs.EditProfileHint);
ImGui.SameLine();
ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 2) - 5);
@@ -129,7 +130,7 @@ internal class ProfileManagerWidget
toCloneGuid = profile.Guid;
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Clone this profile");
+ ImGui.SetTooltip(Locs.CloneProfileHint);
ImGui.SameLine();
ImGui.SetCursorPosX(windowSize.X - (ImGuiHelpers.GlobalScale * 30 * 3) - 5);
@@ -137,11 +138,11 @@ internal class ProfileManagerWidget
if (ImGuiComponents.IconButton($"###exportButton{profile.Guid}", FontAwesomeIcon.FileExport))
{
ImGui.SetClipboardText(profile.Model.Serialize());
- Service.Get().AddNotification("Copied to clipboard!", type: NotificationType.Success);
+ Service.Get().AddNotification(Locs.CopyToClipboardNotification, type: NotificationType.Success);
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Copy profile to clipboard for sharing");
+ ImGui.SetTooltip(Locs.CopyToClipboardHint);
didAny = true;
@@ -156,7 +157,7 @@ internal class ProfileManagerWidget
if (!didAny)
{
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
- ImGuiHelpers.CenteredText("No profiles! Add one!");
+ ImGuiHelpers.CenteredText(Locs.AddProfileHint);
ImGui.PopStyleColor();
}
@@ -195,7 +196,7 @@ internal class ProfileManagerWidget
using var disabled = ImRaii.Disabled(profman.IsBusy);
ImGui.SetNextItemWidth(width);
- ImGui.InputTextWithHint("###pluginPickerSearch", "Search...", ref this.pickerSearch, 255);
+ ImGui.InputTextWithHint("###pluginPickerSearch", Locs.SearchHint, ref this.pickerSearch, 255);
if (ImGui.BeginListBox("###pluginPicker", new Vector2(width, width - 80)))
{
@@ -211,7 +212,7 @@ internal class ProfileManagerWidget
// TODO this sucks
profile.AddOrUpdate(plugin.Manifest.InternalName, true, false);
Task.Run(() => profman.ApplyAllWantStates())
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not apply profiles.");
+ .ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
}
}
@@ -229,7 +230,7 @@ internal class ProfileManagerWidget
this.Reset();
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Back to overview");
+ ImGui.SetTooltip(Locs.BackToOverview);
ImGui.SameLine();
ImGuiHelpers.ScaledDummy(5);
@@ -238,11 +239,11 @@ internal class ProfileManagerWidget
if (ImGuiComponents.IconButton(FontAwesomeIcon.FileExport))
{
ImGui.SetClipboardText(profile.Model.Serialize());
- Service.Get().AddNotification("Copied to clipboard!", type: NotificationType.Success);
+ Service.Get().AddNotification(Locs.CopyToClipboardNotification, type: NotificationType.Success);
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Copy profile to clipboard for sharing");
+ ImGui.SetTooltip(Locs.CopyToClipboardHint);
ImGui.SameLine();
ImGuiHelpers.ScaledDummy(5);
@@ -257,12 +258,12 @@ internal class ProfileManagerWidget
Task.Run(() => profman.ApplyAllWantStates())
.ContinueWith(t =>
{
- this.installer.DisplayErrorContinuation(t, "Could not refresh profiles.");
+ this.installer.DisplayErrorContinuation(t, Locs.ErrorCouldNotChangeState);
});
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Delete this profile");
+ ImGui.SetTooltip(Locs.DeleteProfileHint);
ImGui.SameLine();
ImGuiHelpers.ScaledDummy(5);
@@ -281,18 +282,18 @@ internal class ProfileManagerWidget
if (ImGuiComponents.ToggleButton($"###toggleButton{profile.Guid}", ref isEnabled))
{
Task.Run(() => profile.SetState(isEnabled))
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change profile state.");
+ .ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Enable/Disable this profile");
+ ImGui.SetTooltip(Locs.TooltipEnableDisable);
ImGui.Separator();
ImGuiHelpers.ScaledDummy(5);
var enableAtBoot = profile.AlwaysEnableAtBoot;
- if (ImGui.Checkbox("Always enable when game starts", ref enableAtBoot))
+ if (ImGui.Checkbox(Locs.AlwaysEnableAtBoot, ref enableAtBoot))
{
profile.AlwaysEnableAtBoot = enableAtBoot;
}
@@ -335,7 +336,7 @@ internal class ProfileManagerWidget
ImGui.Image(pic.DefaultIcon.ImGuiHandle, new Vector2(pluginLineHeight));
ImGui.SameLine();
- var text = $"{plugin.InternalName} (Not Installed)";
+ var text = Locs.NotInstalled(plugin.InternalName);
var textHeight = ImGui.CalcTextSize(text);
var before = ImGui.GetCursorPos();
@@ -358,7 +359,7 @@ internal class ProfileManagerWidget
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Install this plugin");
+ ImGui.SetTooltip(Locs.InstallPlugin);
}
ImGui.SetCursorPos(before);
@@ -372,7 +373,7 @@ internal class ProfileManagerWidget
if (ImGui.Checkbox($"###{this.editingProfileGuid}-{plugin.InternalName}", ref enabled))
{
Task.Run(() => profile.AddOrUpdate(plugin.InternalName, enabled))
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not change plugin state.");
+ .ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotChangeState);
}
ImGui.SameLine();
@@ -385,7 +386,7 @@ internal class ProfileManagerWidget
}
if (ImGui.IsItemHovered())
- ImGui.SetTooltip("Remove plugin from this profile");
+ ImGui.SetTooltip(Locs.RemovePlugin);
}
if (wantRemovePluginInternalName != null)
@@ -393,17 +394,17 @@ internal class ProfileManagerWidget
// TODO: handle error
profile.Remove(wantRemovePluginInternalName, false);
Task.Run(() => profman.ApplyAllWantStates())
- .ContinueWith(this.installer.DisplayErrorContinuation, "Could not remove plugin.");
+ .ContinueWith(this.installer.DisplayErrorContinuation, Locs.ErrorCouldNotRemove);
}
if (!didAny)
{
- ImGui.TextColored(ImGuiColors.DalamudGrey, "Profile has no plugins!");
+ ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.NoPluginsInProfile);
}
ImGuiHelpers.ScaledDummy(10);
- var addPluginsText = "Add a plugin!";
+ var addPluginsText = Locs.AddPlugin;
ImGuiHelpers.CenterCursorFor((int)(ImGui.CalcTextSize(addPluginsText).X + 30 + (ImGuiHelpers.GlobalScale * 5)));
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
@@ -432,4 +433,61 @@ internal class ProfileManagerWidget
Overview,
EditSingleProfile,
}
+
+ private static class Locs
+ {
+ public static string TooltipEnableDisable =>
+ Loc.Localize("ProfileManagerEnableDisableHint", "Enable/Disable this profile");
+
+ public static string InstallPlugin => Loc.Localize("ProfileManagerInstall", "Install this plugin");
+
+ public static string RemovePlugin =>
+ Loc.Localize("ProfileManagerRemoveFromProfile", "Remove plugin from this profile");
+
+ public static string AddPlugin => Loc.Localize("ProfileManagerAddPlugin", "Add a plugin!");
+
+ public static string NoPluginsInProfile =>
+ Loc.Localize("ProfileManagerNoPluginsInProfile", "Profile has no plugins!");
+
+ public static string AlwaysEnableAtBoot =>
+ Loc.Localize("ProfileManagerAlwaysEnableAtBoot", "Always enable when game starts");
+
+ public static string DeleteProfileHint => Loc.Localize("ProfileManagerDeleteProfile", "Delete this profile");
+
+ public static string CopyToClipboardHint =>
+ Loc.Localize("ProfileManagerCopyToClipboard", "Copy profile to clipboard for sharing");
+
+ public static string CopyToClipboardNotification =>
+ Loc.Localize("ProfileManagerCopyToClipboardHint", "Copied to clipboard!");
+
+ public static string BackToOverview => Loc.Localize("ProfileManagerBackToOverview", "Back to overview");
+
+ public static string SearchHint => Loc.Localize("ProfileManagerSearchHint", "Search...");
+
+ public static string AddProfileHint => Loc.Localize("ProfileManagerAddProfileHint", "No profiles! Add one!");
+
+ public static string CloneProfileHint => Loc.Localize("ProfileManagerCloneProfile", "Clone this profile");
+
+ public static string EditProfileHint => Loc.Localize("ProfileManagerEditProfile", "Edit this profile");
+
+ public static string ImportProfileHint =>
+ Loc.Localize("ProfileManagerImportProfile", "Import a shared profile from your clipboard");
+
+ public static string AddProfile => Loc.Localize("ProfileManagerAddProfile", "Add a new profile");
+
+ public static string NotificationImportSuccess =>
+ Loc.Localize("ProfileManagerNotificationImportSuccess", "Profile successfully imported!");
+
+ public static string NotificationImportError =>
+ Loc.Localize("ProfileManagerNotificationImportError", "Could not import profile.");
+
+ public static string ErrorCouldNotRemove =>
+ Loc.Localize("ProfileManagerCouldNotRemove", "Could not remove plugin.");
+
+ public static string ErrorCouldNotChangeState =>
+ Loc.Localize("ProfileManagerCouldNotChangeState", "Could not change plugin state.");
+
+ public static string NotInstalled(string name) =>
+ Loc.Localize("ProfileManagerNotInstalled", "{0} (Not Installed)").Format(name);
+ }
}
From f375b8fe5b0f8591ff5b4f707253a47b19be7f38 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 24 Apr 2023 21:15:24 +0200
Subject: [PATCH 21/68] increase GameStart max tries to 1200(1min)
---
Dalamud.Injector/GameStart.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud.Injector/GameStart.cs b/Dalamud.Injector/GameStart.cs
index 44102d952..95e963a9a 100644
--- a/Dalamud.Injector/GameStart.cs
+++ b/Dalamud.Injector/GameStart.cs
@@ -127,7 +127,7 @@ namespace Dalamud.Injector
try
{
var tries = 0;
- const int maxTries = 420;
+ const int maxTries = 1200;
const int timeout = 50;
do
From 5361355ffdfc55fd8b5c3d1c12bad96ffbe42efd Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Thu, 25 May 2023 21:47:15 +0200
Subject: [PATCH 22/68] build: 7.5.2.0
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 61730b5ca..fac4af4f3 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.5.1.0
+ 7.5.2.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From 845a3fba4f338288ff6bf0bd27d959c9a5e720cb Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Fri, 26 May 2023 01:37:04 -0700
Subject: [PATCH 23/68] Remove support for legacy devplugins directory (#1219)
* remove support for legacy devPlugins directory
* bump CS
* fix copy
* remove legacy helptext
---
Dalamud.Injector/EntryPoint.cs | 7 +----
Dalamud/DalamudStartInfo.cs | 6 -----
.../Widgets/DevPluginsSettingsEntry.cs | 2 +-
Dalamud/Plugin/Internal/PluginManager.cs | 27 ++-----------------
lib/FFXIVClientStructs | 2 +-
5 files changed, 5 insertions(+), 39 deletions(-)
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index c29fada83..f6601ab43 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -247,7 +247,6 @@ namespace Dalamud.Injector
var workingDirectory = startInfo.WorkingDirectory;
var configurationPath = startInfo.ConfigurationPath;
var pluginDirectory = startInfo.PluginDirectory;
- var defaultPluginDirectory = startInfo.DefaultPluginDirectory;
var assetDirectory = startInfo.AssetDirectory;
var delayInitializeMs = startInfo.DelayInitializeMs;
var logName = startInfo.LogName;
@@ -262,8 +261,6 @@ namespace Dalamud.Injector
configurationPath = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-plugin-directory="))
pluginDirectory = args[i][key.Length..];
- else if (args[i].StartsWith(key = "--dalamud-dev-plugin-directory="))
- defaultPluginDirectory = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-asset-directory="))
assetDirectory = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-delay-initialize="))
@@ -287,7 +284,6 @@ namespace Dalamud.Injector
workingDirectory ??= Directory.GetCurrentDirectory();
configurationPath ??= Path.Combine(xivlauncherDir, "dalamudConfig.json");
pluginDirectory ??= Path.Combine(xivlauncherDir, "installedPlugins");
- defaultPluginDirectory ??= Path.Combine(xivlauncherDir, "devPlugins");
assetDirectory ??= Path.Combine(xivlauncherDir, "dalamudAssets", "dev");
ClientLanguage clientLanguage;
@@ -313,7 +309,6 @@ namespace Dalamud.Injector
startInfo.WorkingDirectory = workingDirectory;
startInfo.ConfigurationPath = configurationPath;
startInfo.PluginDirectory = pluginDirectory;
- startInfo.DefaultPluginDirectory = defaultPluginDirectory;
startInfo.AssetDirectory = assetDirectory;
startInfo.Language = clientLanguage;
startInfo.DelayInitializeMs = delayInitializeMs;
@@ -367,7 +362,7 @@ namespace Dalamud.Injector
}
Console.WriteLine("Specifying dalamud start info: [--dalamud-working-directory=path] [--dalamud-configuration-path=path]");
- Console.WriteLine(" [--dalamud-plugin-directory=path] [--dalamud-dev-plugin-directory=path]");
+ Console.WriteLine(" [--dalamud-plugin-directory=path]");
Console.WriteLine(" [--dalamud-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud/DalamudStartInfo.cs
index 658934005..4c8e7566d 100644
--- a/Dalamud/DalamudStartInfo.cs
+++ b/Dalamud/DalamudStartInfo.cs
@@ -30,7 +30,6 @@ public record DalamudStartInfo : IServiceType
this.ConfigurationPath = other.ConfigurationPath;
this.LogName = other.LogName;
this.PluginDirectory = other.PluginDirectory;
- this.DefaultPluginDirectory = other.DefaultPluginDirectory;
this.AssetDirectory = other.AssetDirectory;
this.Language = other.Language;
this.GameVersion = other.GameVersion;
@@ -72,11 +71,6 @@ public record DalamudStartInfo : IServiceType
///
public string? PluginDirectory { get; set; }
- ///
- /// Gets or sets the path to the directory for developer plugins.
- ///
- public string? DefaultPluginDirectory { get; set; }
-
///
/// Gets or sets the path to core Dalamud assets.
///
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
index 4e69dcb1a..3e73454f3 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
@@ -67,7 +67,7 @@ public class DevPluginsSettingsEntry : SettingsEntry
}
}
- ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add additional dev plugin load locations.\nThese can be either the directory or DLL path."));
+ ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add dev plugin load locations.\nThese can be either the directory or DLL path."));
ImGuiHelpers.ScaledDummy(5);
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 301e6bb68..9df249dd7 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -57,15 +57,6 @@ internal partial class PluginManager : IDisposable, IServiceType
///
public const int PluginWaitBeforeFreeDefault = 1000; // upped from 500ms, seems more stable
- private const string DevPluginsDisclaimerFilename = "DONT_USE_THIS_FOLDER.txt";
-
- private const string DevPluginsDisclaimerText = @"Hey!
-The devPlugins folder is deprecated and will be removed soon. Please don't use it anymore for plugin development.
-Instead, open the Dalamud settings and add the path to your plugins build output folder as a dev plugin location.
-Remove your devPlugin from this folder.
-
-Thanks and have fun!";
-
private static readonly ModuleLog Log = new("PLUGINM");
private readonly object pluginListLock = new();
@@ -88,18 +79,10 @@ Thanks and have fun!";
private PluginManager()
{
this.pluginDirectory = new DirectoryInfo(this.startInfo.PluginDirectory!);
- this.devPluginDirectory = new DirectoryInfo(this.startInfo.DefaultPluginDirectory!);
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
- var disclaimerFileName = Path.Combine(this.devPluginDirectory.FullName, DevPluginsDisclaimerFilename);
- if (!File.Exists(disclaimerFileName))
- File.WriteAllText(disclaimerFileName, DevPluginsDisclaimerText);
-
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins;
try
@@ -394,9 +377,6 @@ Thanks and have fun!";
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
// Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
{
@@ -437,7 +417,7 @@ Thanks and have fun!";
}
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
+ var devDllFiles = new List();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
@@ -660,11 +640,8 @@ Thanks and have fun!";
///
public void ScanDevPlugins()
{
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
+ var devDllFiles = new List();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index 010e878fe..9a32b1565 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 010e878febb631c8f3ff5ff90d656f318e35b1de
+Subproject commit 9a32b1565eeb0b237a427c242d7f0a78d4afbcfd
From 681cda91a45202e60f33ff66b2da88a2f834579f Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Fri, 26 May 2023 12:02:01 +0200
Subject: [PATCH 24/68] Revert "Remove support for legacy devplugins directory
(#1219)" (#1220)
This reverts commit 845a3fba4f338288ff6bf0bd27d959c9a5e720cb.
---
Dalamud.Injector/EntryPoint.cs | 7 ++++-
Dalamud/DalamudStartInfo.cs | 6 +++++
.../Widgets/DevPluginsSettingsEntry.cs | 2 +-
Dalamud/Plugin/Internal/PluginManager.cs | 27 +++++++++++++++++--
lib/FFXIVClientStructs | 2 +-
5 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index f6601ab43..c29fada83 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -247,6 +247,7 @@ namespace Dalamud.Injector
var workingDirectory = startInfo.WorkingDirectory;
var configurationPath = startInfo.ConfigurationPath;
var pluginDirectory = startInfo.PluginDirectory;
+ var defaultPluginDirectory = startInfo.DefaultPluginDirectory;
var assetDirectory = startInfo.AssetDirectory;
var delayInitializeMs = startInfo.DelayInitializeMs;
var logName = startInfo.LogName;
@@ -261,6 +262,8 @@ namespace Dalamud.Injector
configurationPath = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-plugin-directory="))
pluginDirectory = args[i][key.Length..];
+ else if (args[i].StartsWith(key = "--dalamud-dev-plugin-directory="))
+ defaultPluginDirectory = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-asset-directory="))
assetDirectory = args[i][key.Length..];
else if (args[i].StartsWith(key = "--dalamud-delay-initialize="))
@@ -284,6 +287,7 @@ namespace Dalamud.Injector
workingDirectory ??= Directory.GetCurrentDirectory();
configurationPath ??= Path.Combine(xivlauncherDir, "dalamudConfig.json");
pluginDirectory ??= Path.Combine(xivlauncherDir, "installedPlugins");
+ defaultPluginDirectory ??= Path.Combine(xivlauncherDir, "devPlugins");
assetDirectory ??= Path.Combine(xivlauncherDir, "dalamudAssets", "dev");
ClientLanguage clientLanguage;
@@ -309,6 +313,7 @@ namespace Dalamud.Injector
startInfo.WorkingDirectory = workingDirectory;
startInfo.ConfigurationPath = configurationPath;
startInfo.PluginDirectory = pluginDirectory;
+ startInfo.DefaultPluginDirectory = defaultPluginDirectory;
startInfo.AssetDirectory = assetDirectory;
startInfo.Language = clientLanguage;
startInfo.DelayInitializeMs = delayInitializeMs;
@@ -362,7 +367,7 @@ namespace Dalamud.Injector
}
Console.WriteLine("Specifying dalamud start info: [--dalamud-working-directory=path] [--dalamud-configuration-path=path]");
- Console.WriteLine(" [--dalamud-plugin-directory=path]");
+ Console.WriteLine(" [--dalamud-plugin-directory=path] [--dalamud-dev-plugin-directory=path]");
Console.WriteLine(" [--dalamud-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud/DalamudStartInfo.cs
index 4c8e7566d..658934005 100644
--- a/Dalamud/DalamudStartInfo.cs
+++ b/Dalamud/DalamudStartInfo.cs
@@ -30,6 +30,7 @@ public record DalamudStartInfo : IServiceType
this.ConfigurationPath = other.ConfigurationPath;
this.LogName = other.LogName;
this.PluginDirectory = other.PluginDirectory;
+ this.DefaultPluginDirectory = other.DefaultPluginDirectory;
this.AssetDirectory = other.AssetDirectory;
this.Language = other.Language;
this.GameVersion = other.GameVersion;
@@ -71,6 +72,11 @@ public record DalamudStartInfo : IServiceType
///
public string? PluginDirectory { get; set; }
+ ///
+ /// Gets or sets the path to the directory for developer plugins.
+ ///
+ public string? DefaultPluginDirectory { get; set; }
+
///
/// Gets or sets the path to core Dalamud assets.
///
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
index 3e73454f3..4e69dcb1a 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
@@ -67,7 +67,7 @@ public class DevPluginsSettingsEntry : SettingsEntry
}
}
- ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add dev plugin load locations.\nThese can be either the directory or DLL path."));
+ ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add additional dev plugin load locations.\nThese can be either the directory or DLL path."));
ImGuiHelpers.ScaledDummy(5);
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 9df249dd7..301e6bb68 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -57,6 +57,15 @@ internal partial class PluginManager : IDisposable, IServiceType
///
public const int PluginWaitBeforeFreeDefault = 1000; // upped from 500ms, seems more stable
+ private const string DevPluginsDisclaimerFilename = "DONT_USE_THIS_FOLDER.txt";
+
+ private const string DevPluginsDisclaimerText = @"Hey!
+The devPlugins folder is deprecated and will be removed soon. Please don't use it anymore for plugin development.
+Instead, open the Dalamud settings and add the path to your plugins build output folder as a dev plugin location.
+Remove your devPlugin from this folder.
+
+Thanks and have fun!";
+
private static readonly ModuleLog Log = new("PLUGINM");
private readonly object pluginListLock = new();
@@ -79,10 +88,18 @@ internal partial class PluginManager : IDisposable, IServiceType
private PluginManager()
{
this.pluginDirectory = new DirectoryInfo(this.startInfo.PluginDirectory!);
+ this.devPluginDirectory = new DirectoryInfo(this.startInfo.DefaultPluginDirectory!);
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
+ if (!this.devPluginDirectory.Exists)
+ this.devPluginDirectory.Create();
+
+ var disclaimerFileName = Path.Combine(this.devPluginDirectory.FullName, DevPluginsDisclaimerFilename);
+ if (!File.Exists(disclaimerFileName))
+ File.WriteAllText(disclaimerFileName, DevPluginsDisclaimerText);
+
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins;
try
@@ -377,6 +394,9 @@ internal partial class PluginManager : IDisposable, IServiceType
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
+ if (!this.devPluginDirectory.Exists)
+ this.devPluginDirectory.Create();
+
// Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
{
@@ -417,7 +437,7 @@ internal partial class PluginManager : IDisposable, IServiceType
}
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = new List();
+ var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
@@ -640,8 +660,11 @@ internal partial class PluginManager : IDisposable, IServiceType
///
public void ScanDevPlugins()
{
+ if (!this.devPluginDirectory.Exists)
+ this.devPluginDirectory.Create();
+
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = new List();
+ var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index 9a32b1565..010e878fe 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 9a32b1565eeb0b237a427c242d7f0a78d4afbcfd
+Subproject commit 010e878febb631c8f3ff5ff90d656f318e35b1de
From 5af1425c9760e38b2ac2b9c62ac3390436b135a2 Mon Sep 17 00:00:00 2001
From: goat
Date: Fri, 26 May 2023 22:59:32 +0200
Subject: [PATCH 25/68] hook Present and ResizeBuffers with MinHook potentially
works around a rare crash regarding discord hooks in functions called by
Framework::Update()
---
Dalamud/Interface/Internal/InterfaceManager.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 9a8da773c..f02baa890 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -1005,8 +1005,8 @@ internal class InterfaceManager : IDisposable, IServiceType
Log.Error(ex, "Could not enable immersive mode");
}
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+ this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour, true);
+ this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour, true);
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
From 95b0565976284d9ef98fc6e4b4b1dc4c5c9fbb51 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Fri, 26 May 2023 18:40:51 -0700
Subject: [PATCH 26/68] Fix devplugins again & bump CS (#1221)
* reapply devplugins directory removal, without breaking everything
Revert "Revert "Remove support for legacy devplugins directory (#1219)" (#1220)"
This reverts commit 681cda91a45202e60f33ff66b2da88a2f834579f.
* bump CS
---
Dalamud.Injector/EntryPoint.cs | 108 ++++++++++++++++--
Dalamud/DalamudStartInfo.cs | 6 -
.../Widgets/DevPluginsSettingsEntry.cs | 2 +-
Dalamud/Plugin/Internal/PluginManager.cs | 27 +----
lib/FFXIVClientStructs | 2 +-
5 files changed, 103 insertions(+), 42 deletions(-)
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index c29fada83..fbefbd92a 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -200,15 +200,21 @@ namespace Dalamud.Injector
var logFile = new FileInfo(logPath);
if (!logFile.Exists)
+ {
logFile.Create();
+ }
if (logFile.Length <= cullingFileSize)
+ {
return;
+ }
var amountToCull = logFile.Length - cullingFileSize;
if (amountToCull < bufferSize)
+ {
return;
+ }
using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
using var writer = new BinaryWriter(logFile.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite));
@@ -247,7 +253,6 @@ namespace Dalamud.Injector
var workingDirectory = startInfo.WorkingDirectory;
var configurationPath = startInfo.ConfigurationPath;
var pluginDirectory = startInfo.PluginDirectory;
- var defaultPluginDirectory = startInfo.DefaultPluginDirectory;
var assetDirectory = startInfo.AssetDirectory;
var delayInitializeMs = startInfo.DelayInitializeMs;
var logName = startInfo.LogName;
@@ -257,25 +262,41 @@ namespace Dalamud.Injector
for (var i = 2; i < args.Count; i++)
{
if (args[i].StartsWith(key = "--dalamud-working-directory="))
+ {
workingDirectory = args[i][key.Length..];
+ }
else if (args[i].StartsWith(key = "--dalamud-configuration-path="))
+ {
configurationPath = args[i][key.Length..];
+ }
else if (args[i].StartsWith(key = "--dalamud-plugin-directory="))
+ {
pluginDirectory = args[i][key.Length..];
- else if (args[i].StartsWith(key = "--dalamud-dev-plugin-directory="))
- defaultPluginDirectory = args[i][key.Length..];
+ }
else if (args[i].StartsWith(key = "--dalamud-asset-directory="))
+ {
assetDirectory = args[i][key.Length..];
+ }
else if (args[i].StartsWith(key = "--dalamud-delay-initialize="))
+ {
delayInitializeMs = int.Parse(args[i][key.Length..]);
+ }
else if (args[i].StartsWith(key = "--dalamud-client-language="))
+ {
languageStr = args[i][key.Length..].ToLowerInvariant();
+ }
else if (args[i].StartsWith(key = "--dalamud-tspack-b64="))
+ {
troubleshootingData = Encoding.UTF8.GetString(Convert.FromBase64String(args[i][key.Length..]));
+ }
else if (args[i].StartsWith(key = "--logname="))
+ {
logName = args[i][key.Length..];
+ }
else
+ {
continue;
+ }
args.RemoveAt(i);
i--;
@@ -287,33 +308,49 @@ namespace Dalamud.Injector
workingDirectory ??= Directory.GetCurrentDirectory();
configurationPath ??= Path.Combine(xivlauncherDir, "dalamudConfig.json");
pluginDirectory ??= Path.Combine(xivlauncherDir, "installedPlugins");
- defaultPluginDirectory ??= Path.Combine(xivlauncherDir, "devPlugins");
assetDirectory ??= Path.Combine(xivlauncherDir, "dalamudAssets", "dev");
ClientLanguage clientLanguage;
if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "english").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.English;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "japanese").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.Japanese;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "日本語").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.Japanese;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "german").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.German;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "deutsch").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.German;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "french").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.French;
+ }
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "français").Length))] == key[0..len])
+ {
clientLanguage = ClientLanguage.French;
+ }
else if (int.TryParse(languageStr, out var languageInt) && Enum.IsDefined((ClientLanguage)languageInt))
+ {
clientLanguage = (ClientLanguage)languageInt;
+ }
else
+ {
throw new CommandLineException($"\"{languageStr}\" is not a valid supported language.");
+ }
startInfo.WorkingDirectory = workingDirectory;
startInfo.ConfigurationPath = configurationPath;
startInfo.PluginDirectory = pluginDirectory;
- startInfo.DefaultPluginDirectory = defaultPluginDirectory;
startInfo.AssetDirectory = assetDirectory;
startInfo.Language = clientLanguage;
startInfo.DelayInitializeMs = delayInitializeMs;
@@ -350,10 +387,14 @@ namespace Dalamud.Injector
exeSpaces += " ";
if (particularCommand is null or "help")
+ {
Console.WriteLine("{0} help [command]", exeName);
+ }
if (particularCommand is null or "inject")
+ {
Console.WriteLine("{0} inject [-h/--help] [-a/--all] [--warn] [--fix-acl] [--se-debug-privilege] [pid1] [pid2] [pid3] ...", exeName);
+ }
if (particularCommand is null or "launch")
{
@@ -367,7 +408,7 @@ namespace Dalamud.Injector
}
Console.WriteLine("Specifying dalamud start info: [--dalamud-working-directory=path] [--dalamud-configuration-path=path]");
- Console.WriteLine(" [--dalamud-plugin-directory=path] [--dalamud-dev-plugin-directory=path]");
+ Console.WriteLine(" [--dalamud-plugin-directory=path]");
Console.WriteLine(" [--dalamud-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
@@ -431,7 +472,7 @@ namespace Dalamud.Injector
}
else
{
- throw new CommandLineException($"\"{args[i]}\" is not a command line argument.");
+ Log.Warning($"\"{args[i]}\" is not a valid command line argument, ignoring.");
}
}
@@ -505,29 +546,53 @@ namespace Dalamud.Injector
}
if (args[i] == "-h" || args[i] == "--help")
+ {
showHelp = true;
+ }
else if (args[i] == "-f" || args[i] == "--fake-arguments")
+ {
useFakeArguments = true;
+ }
else if (args[i] == "--without-dalamud")
+ {
withoutDalamud = true;
+ }
else if (args[i] == "--no-wait")
+ {
waitForGameWindow = false;
+ }
else if (args[i] == "--no-fix-acl" || args[i] == "--no-acl-fix")
+ {
noFixAcl = true;
+ }
else if (args[i] == "-g")
+ {
gamePath = args[++i];
+ }
else if (args[i].StartsWith("--game="))
+ {
gamePath = args[i].Split('=', 2)[1];
+ }
else if (args[i] == "-m")
+ {
mode = args[++i];
+ }
else if (args[i].StartsWith("--mode="))
+ {
mode = args[i].Split('=', 2)[1];
+ }
else if (args[i].StartsWith("--handle-owner="))
+ {
handleOwner = IntPtr.Parse(args[i].Split('=', 2)[1]);
+ }
else if (args[i] == "--")
+ {
parsingGameArgument = true;
+ }
else
- throw new CommandLineException($"\"{args[i]}\" is not a command line argument.");
+ {
+ Log.Warning($"\"{args[i]}\" is not a valid command line argument, ignoring.");
+ }
}
var checksumTable = "fX1pGtdS5CAP4_VL";
@@ -536,11 +601,15 @@ namespace Dalamud.Injector
gameArguments = gameArguments.SelectMany(x =>
{
if (!x.StartsWith("//**sqex0003") || !x.EndsWith("**//"))
+ {
return new List() { x };
+ }
var checksum = checksumTable.IndexOf(x[x.Length - 5]);
if (checksum == -1)
+ {
return new List() { x };
+ }
var encData = Convert.FromBase64String(x.Substring(12, x.Length - 12 - 5).Replace('-', '+').Replace('_', '/').Replace('*', '='));
var rawData = new byte[encData.Length];
@@ -554,13 +623,25 @@ namespace Dalamud.Injector
encryptArguments = true;
var args = argDelimiterRegex.Split(rawString).Skip(1).Select(y => string.Join('=', kvDelimiterRegex.Split(y, 2)).Replace(" ", " ")).ToList();
if (!args.Any())
+ {
continue;
+ }
+
if (!args.First().StartsWith("T="))
+ {
continue;
+ }
+
if (!uint.TryParse(args.First().Substring(2), out var tickCount))
+ {
continue;
+ }
+
if (tickCount >> 16 != i)
+ {
continue;
+ }
+
return args.Skip(1);
}
@@ -712,7 +793,9 @@ namespace Dalamud.Injector
if (handleOwner != IntPtr.Zero)
{
if (!DuplicateHandle(Process.GetCurrentProcess().Handle, process.Handle, handleOwner, out processHandleForOwner, 0, false, DuplicateOptions.SameAccess))
+ {
Log.Warning("Failed to call DuplicateHandle: Win32 error code {0}", Marshal.GetLastWin32Error());
+ }
}
Console.WriteLine($"{{\"pid\": {process.Id}, \"handle\": {processHandleForOwner}}}");
@@ -755,7 +838,9 @@ namespace Dalamud.Injector
helperProcess.BeginErrorReadLine();
helperProcess.WaitForExit();
if (helperProcess.ExitCode != 0)
+ {
return -1;
+ }
var result = JsonSerializer.CreateDefault().Deserialize>(new JsonTextReader(helperProcess.StandardOutput));
var pid = result["pid"];
@@ -812,7 +897,9 @@ namespace Dalamud.Injector
var startInfoAddress = startInfoBuffer.Add(startInfoBytes);
if (startInfoAddress == 0)
+ {
throw new Exception("Unable to allocate start info JSON");
+ }
injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress);
injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode);
@@ -847,7 +934,10 @@ namespace Dalamud.Injector
///
private static string EncodeParameterArgument(string argument, bool force = false)
{
- if (argument == null) throw new ArgumentNullException(nameof(argument));
+ if (argument == null)
+ {
+ throw new ArgumentNullException(nameof(argument));
+ }
// Unless we're told otherwise, don't quote unless we actually
// need to do so --- hopefully avoid problems if programs won't
diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud/DalamudStartInfo.cs
index 658934005..4c8e7566d 100644
--- a/Dalamud/DalamudStartInfo.cs
+++ b/Dalamud/DalamudStartInfo.cs
@@ -30,7 +30,6 @@ public record DalamudStartInfo : IServiceType
this.ConfigurationPath = other.ConfigurationPath;
this.LogName = other.LogName;
this.PluginDirectory = other.PluginDirectory;
- this.DefaultPluginDirectory = other.DefaultPluginDirectory;
this.AssetDirectory = other.AssetDirectory;
this.Language = other.Language;
this.GameVersion = other.GameVersion;
@@ -72,11 +71,6 @@ public record DalamudStartInfo : IServiceType
///
public string? PluginDirectory { get; set; }
- ///
- /// Gets or sets the path to the directory for developer plugins.
- ///
- public string? DefaultPluginDirectory { get; set; }
-
///
/// Gets or sets the path to core Dalamud assets.
///
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
index 4e69dcb1a..3e73454f3 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs
@@ -67,7 +67,7 @@ public class DevPluginsSettingsEntry : SettingsEntry
}
}
- ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add additional dev plugin load locations.\nThese can be either the directory or DLL path."));
+ ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add dev plugin load locations.\nThese can be either the directory or DLL path."));
ImGuiHelpers.ScaledDummy(5);
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 301e6bb68..9df249dd7 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -57,15 +57,6 @@ internal partial class PluginManager : IDisposable, IServiceType
///
public const int PluginWaitBeforeFreeDefault = 1000; // upped from 500ms, seems more stable
- private const string DevPluginsDisclaimerFilename = "DONT_USE_THIS_FOLDER.txt";
-
- private const string DevPluginsDisclaimerText = @"Hey!
-The devPlugins folder is deprecated and will be removed soon. Please don't use it anymore for plugin development.
-Instead, open the Dalamud settings and add the path to your plugins build output folder as a dev plugin location.
-Remove your devPlugin from this folder.
-
-Thanks and have fun!";
-
private static readonly ModuleLog Log = new("PLUGINM");
private readonly object pluginListLock = new();
@@ -88,18 +79,10 @@ Thanks and have fun!";
private PluginManager()
{
this.pluginDirectory = new DirectoryInfo(this.startInfo.PluginDirectory!);
- this.devPluginDirectory = new DirectoryInfo(this.startInfo.DefaultPluginDirectory!);
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
- var disclaimerFileName = Path.Combine(this.devPluginDirectory.FullName, DevPluginsDisclaimerFilename);
- if (!File.Exists(disclaimerFileName))
- File.WriteAllText(disclaimerFileName, DevPluginsDisclaimerText);
-
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins;
try
@@ -394,9 +377,6 @@ Thanks and have fun!";
if (!this.pluginDirectory.Exists)
this.pluginDirectory.Create();
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
// Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
{
@@ -437,7 +417,7 @@ Thanks and have fun!";
}
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
+ var devDllFiles = new List();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
@@ -660,11 +640,8 @@ Thanks and have fun!";
///
public void ScanDevPlugins()
{
- if (!this.devPluginDirectory.Exists)
- this.devPluginDirectory.Create();
-
// devPlugins are more freeform. Look for any dll and hope to get lucky.
- var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
+ var devDllFiles = new List();
foreach (var setting in this.configuration.DevPluginLoadLocations)
{
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index 010e878fe..08d8bfc64 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 010e878febb631c8f3ff5ff90d656f318e35b1de
+Subproject commit 08d8bfc64d31ac1a2bfa5bc2cb927459254bb635
From 76cf97d316272ee0fb3d90894cf437c2aa4c993d Mon Sep 17 00:00:00 2001
From: KazWolfe
Date: Fri, 26 May 2023 23:16:09 -0700
Subject: [PATCH 27/68] build: 7.6.0.0 (#1222)
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index fac4af4f3..116ebd008 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.5.2.0
+ 7.6.0.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From 37fc9d7284bf23fa27645e663592e75fc3a854c5 Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Sat, 27 May 2023 08:27:44 +0200
Subject: [PATCH 28/68] Update ClientStructs (#1218)
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 08d8bfc64..5e9568845 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 08d8bfc64d31ac1a2bfa5bc2cb927459254bb635
+Subproject commit 5e9568845ac302540ce1135abbff7bef98314714
From e1a56d6d64391c7bf6b297580f21fd52b9360c6a Mon Sep 17 00:00:00 2001
From: goat
Date: Sat, 27 May 2023 19:02:40 +0200
Subject: [PATCH 29/68] hook Present and ResizeBuffers immediately when
InterfaceManager is created Also seems to resolve the aforementioned issue
without breaking wine support
---
.../Interface/Internal/InterfaceManager.cs | 42 +++++++++----------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index f02baa890..0c5f80c21 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -76,7 +76,7 @@ internal class InterfaceManager : IDisposable, IServiceType
private bool isOverrideGameCursor = true;
[ServiceManager.ServiceConstructor]
- private InterfaceManager()
+ private InterfaceManager(SigScanner sigScanner)
{
this.dispatchMessageWHook = Hook.FromImport(
null, "user32.dll", "DispatchMessageW", 0, this.DispatchMessageWDetour);
@@ -86,6 +86,24 @@ internal class InterfaceManager : IDisposable, IServiceType
this.fontBuildSignal = new ManualResetEvent(false);
this.address = new SwapChainVtableResolver();
+ this.address.Setup();
+
+ this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
+ this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+
+ Log.Verbose("===== S W A P C H A I N =====");
+ Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
+ Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
+
+ var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
+ Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
+ this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
+
+ this.setCursorHook.Enable();
+ this.presentHook.Enable();
+ this.resizeBuffersHook.Enable();
+ this.dispatchMessageWHook.Enable();
+ this.processMessageHook.Enable();
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@@ -982,10 +1000,9 @@ internal class InterfaceManager : IDisposable, IServiceType
}
[ServiceManager.CallWhenServicesReady]
- private void ContinueConstruction(SigScanner sigScanner, Framework framework)
+ private void ContinueConstruction()
{
- this.address.Setup(sigScanner);
- framework.RunOnFrameworkThread(() =>
+ this.framework.RunOnFrameworkThread(() =>
{
while ((this.GameWindowHandle = NativeFunctions.FindWindowEx(IntPtr.Zero, this.GameWindowHandle, "FFXIVGAME", IntPtr.Zero)) != IntPtr.Zero)
{
@@ -1004,23 +1021,6 @@ internal class InterfaceManager : IDisposable, IServiceType
{
Log.Error(ex, "Could not enable immersive mode");
}
-
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour, true);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour, true);
-
- Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
-
- var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
- Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
- this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
-
- this.setCursorHook.Enable();
- this.presentHook.Enable();
- this.resizeBuffersHook.Enable();
- this.dispatchMessageWHook.Enable();
- this.processMessageHook.Enable();
});
}
From 6870c8e7d20cbf915a9c9757f1d3e31828b15de1 Mon Sep 17 00:00:00 2001
From: goat
Date: Sat, 27 May 2023 21:13:45 +0200
Subject: [PATCH 30/68] add a bunch of logging to IM setup
---
Dalamud/Interface/Internal/InterfaceManager.cs | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 0c5f80c21..9645b44a8 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -20,12 +20,12 @@ using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Style;
using Dalamud.Interface.Windowing;
+using Dalamud.Logging.Internal;
using Dalamud.Utility;
using Dalamud.Utility.Timing;
using ImGuiNET;
using ImGuiScene;
using PInvoke;
-using Serilog;
// general dev notes, here because it's easiest
@@ -47,6 +47,8 @@ namespace Dalamud.Interface.Internal;
[ServiceManager.BlockingEarlyLoadedService]
internal class InterfaceManager : IDisposable, IServiceType
{
+ private static ModuleLog Log = new ModuleLog("IM");
+
private const float DefaultFontSizePt = 12.0f;
private const float DefaultFontSizePx = DefaultFontSizePt * 4.0f / 3.0f;
private const ushort Fallback1Codepoint = 0x3013; // Geta mark; FFXIV uses this to indicate that a glyph is missing.
@@ -78,23 +80,28 @@ internal class InterfaceManager : IDisposable, IServiceType
[ServiceManager.ServiceConstructor]
private InterfaceManager(SigScanner sigScanner)
{
+ Log.Verbose("ctor called");
+
this.dispatchMessageWHook = Hook.FromImport(
null, "user32.dll", "DispatchMessageW", 0, this.DispatchMessageWDetour);
this.setCursorHook = Hook.FromImport(
null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
+ Log.Verbose("Import hooks applied");
this.fontBuildSignal = new ManualResetEvent(false);
this.address = new SwapChainVtableResolver();
this.address.Setup();
-
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+ Log.Verbose("Resolver setup complete");
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
+ this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
+ this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+ Log.Verbose("Present and ResizeBuffers hooked");
+
var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
@@ -104,6 +111,7 @@ internal class InterfaceManager : IDisposable, IServiceType
this.resizeBuffersHook.Enable();
this.dispatchMessageWHook.Enable();
this.processMessageHook.Enable();
+ Log.Verbose("Hooks enabled");
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
From 5d54705ec1c69feb2f006b3547c3df11b99b147a Mon Sep 17 00:00:00 2001
From: goat
Date: Sat, 27 May 2023 21:15:20 +0200
Subject: [PATCH 31/68] don't record states for non-dev plugins in
troubleshooting
---
Dalamud/Support/Troubleshooting.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Support/Troubleshooting.cs b/Dalamud/Support/Troubleshooting.cs
index ad6bacb0f..ef1897eeb 100644
--- a/Dalamud/Support/Troubleshooting.cs
+++ b/Dalamud/Support/Troubleshooting.cs
@@ -68,7 +68,7 @@ public static class Troubleshooting
var payload = new TroubleshootingPayload
{
LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest)?.OrderByDescending(x => x.InternalName).ToArray(),
- PluginStates = pluginManager?.InstalledPlugins?.ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()),
+ PluginStates = pluginManager?.InstalledPlugins?.Where(x => !x.IsDev).ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()),
EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(),
DalamudVersion = Util.AssemblyVersion,
DalamudGitHash = Util.GetGitHash(),
From 4139dc14e772273d508ebca0979b365895010f7e Mon Sep 17 00:00:00 2001
From: goat
Date: Sat, 27 May 2023 23:28:32 +0200
Subject: [PATCH 32/68] more IM logging tweaks
---
Dalamud/Interface/Internal/InterfaceManager.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 9645b44a8..5796056a3 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -95,8 +95,9 @@ internal class InterfaceManager : IDisposable, IServiceType
Log.Verbose("Resolver setup complete");
Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
+ Log.Verbose($"Is ReShade: {this.address.IsReshade}");
+ Log.Verbose($"Present address 0x{this.address.Present.ToInt64():X}");
+ Log.Verbose($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
From d393bd4e2adc90e5f6fd6ee1250f6c8aa7773cd7 Mon Sep 17 00:00:00 2001
From: goat
Date: Sun, 28 May 2023 13:16:32 +0200
Subject: [PATCH 33/68] IM logs on information
---
.../Interface/Internal/InterfaceManager.cs | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 5796056a3..cde3f1c42 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -80,31 +80,31 @@ internal class InterfaceManager : IDisposable, IServiceType
[ServiceManager.ServiceConstructor]
private InterfaceManager(SigScanner sigScanner)
{
- Log.Verbose("ctor called");
+ Log.Information("ctor called");
this.dispatchMessageWHook = Hook.FromImport(
null, "user32.dll", "DispatchMessageW", 0, this.DispatchMessageWDetour);
this.setCursorHook = Hook.FromImport(
null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
- Log.Verbose("Import hooks applied");
+ Log.Information("Import hooks applied");
this.fontBuildSignal = new ManualResetEvent(false);
this.address = new SwapChainVtableResolver();
this.address.Setup();
- Log.Verbose("Resolver setup complete");
+ Log.Information("Resolver setup complete");
- Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"Is ReShade: {this.address.IsReshade}");
- Log.Verbose($"Present address 0x{this.address.Present.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
+ Log.Information("===== S W A P C H A I N =====");
+ Log.Information($"Is ReShade: {this.address.IsReshade}");
+ Log.Information($"Present address 0x{this.address.Present.ToInt64():X}");
+ Log.Information($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
- Log.Verbose("Present and ResizeBuffers hooked");
+ Log.Information("Present and ResizeBuffers hooked");
var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
- Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
+ Log.Information($"WndProc address 0x{wndProcAddress.ToInt64():X}");
this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
this.setCursorHook.Enable();
@@ -112,7 +112,7 @@ internal class InterfaceManager : IDisposable, IServiceType
this.resizeBuffersHook.Enable();
this.dispatchMessageWHook.Enable();
this.processMessageHook.Enable();
- Log.Verbose("Hooks enabled");
+ Log.Information("Hooks enabled");
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
From 0014bfa6fcfa12ab025fd06eebfc070d8f32ecb5 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Sun, 28 May 2023 22:30:12 -0700
Subject: [PATCH 34/68] revert IM changes for now due to instability (#1226)
Revert "hook Present and ResizeBuffers immediately when InterfaceManager is created"
This reverts commit e1a56d6d64391c7bf6b297580f21fd52b9360c6a.
Revert "add a bunch of logging to IM setup"
This reverts commit 6870c8e7d20cbf915a9c9757f1d3e31828b15de1.
Revert "more IM logging tweaks"
This reverts commit 4139dc14e772273d508ebca0979b365895010f7e.
Revert "IM logs on information"
This reverts commit d393bd4e2adc90e5f6fd6ee1250f6c8aa7773cd7.
---
.../Interface/Internal/InterfaceManager.cs | 53 ++++++++-----------
1 file changed, 22 insertions(+), 31 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index cde3f1c42..f02baa890 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -20,12 +20,12 @@ using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Style;
using Dalamud.Interface.Windowing;
-using Dalamud.Logging.Internal;
using Dalamud.Utility;
using Dalamud.Utility.Timing;
using ImGuiNET;
using ImGuiScene;
using PInvoke;
+using Serilog;
// general dev notes, here because it's easiest
@@ -47,8 +47,6 @@ namespace Dalamud.Interface.Internal;
[ServiceManager.BlockingEarlyLoadedService]
internal class InterfaceManager : IDisposable, IServiceType
{
- private static ModuleLog Log = new ModuleLog("IM");
-
private const float DefaultFontSizePt = 12.0f;
private const float DefaultFontSizePx = DefaultFontSizePt * 4.0f / 3.0f;
private const ushort Fallback1Codepoint = 0x3013; // Geta mark; FFXIV uses this to indicate that a glyph is missing.
@@ -78,41 +76,16 @@ internal class InterfaceManager : IDisposable, IServiceType
private bool isOverrideGameCursor = true;
[ServiceManager.ServiceConstructor]
- private InterfaceManager(SigScanner sigScanner)
+ private InterfaceManager()
{
- Log.Information("ctor called");
-
this.dispatchMessageWHook = Hook.FromImport(
null, "user32.dll", "DispatchMessageW", 0, this.DispatchMessageWDetour);
this.setCursorHook = Hook.FromImport(
null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
- Log.Information("Import hooks applied");
this.fontBuildSignal = new ManualResetEvent(false);
this.address = new SwapChainVtableResolver();
- this.address.Setup();
- Log.Information("Resolver setup complete");
-
- Log.Information("===== S W A P C H A I N =====");
- Log.Information($"Is ReShade: {this.address.IsReshade}");
- Log.Information($"Present address 0x{this.address.Present.ToInt64():X}");
- Log.Information($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
-
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
- Log.Information("Present and ResizeBuffers hooked");
-
- var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
- Log.Information($"WndProc address 0x{wndProcAddress.ToInt64():X}");
- this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
-
- this.setCursorHook.Enable();
- this.presentHook.Enable();
- this.resizeBuffersHook.Enable();
- this.dispatchMessageWHook.Enable();
- this.processMessageHook.Enable();
- Log.Information("Hooks enabled");
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@@ -1009,9 +982,10 @@ internal class InterfaceManager : IDisposable, IServiceType
}
[ServiceManager.CallWhenServicesReady]
- private void ContinueConstruction()
+ private void ContinueConstruction(SigScanner sigScanner, Framework framework)
{
- this.framework.RunOnFrameworkThread(() =>
+ this.address.Setup(sigScanner);
+ framework.RunOnFrameworkThread(() =>
{
while ((this.GameWindowHandle = NativeFunctions.FindWindowEx(IntPtr.Zero, this.GameWindowHandle, "FFXIVGAME", IntPtr.Zero)) != IntPtr.Zero)
{
@@ -1030,6 +1004,23 @@ internal class InterfaceManager : IDisposable, IServiceType
{
Log.Error(ex, "Could not enable immersive mode");
}
+
+ this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour, true);
+ this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour, true);
+
+ Log.Verbose("===== S W A P C H A I N =====");
+ Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
+ Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
+
+ var wndProcAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 74 ?? B8");
+ Log.Verbose($"WndProc address 0x{wndProcAddress.ToInt64():X}");
+ this.processMessageHook = Hook.FromAddress(wndProcAddress, this.ProcessMessageDetour);
+
+ this.setCursorHook.Enable();
+ this.presentHook.Enable();
+ this.resizeBuffersHook.Enable();
+ this.dispatchMessageWHook.Enable();
+ this.processMessageHook.Enable();
});
}
From 956e310041fe7f0456ec158373261f873865daaf Mon Sep 17 00:00:00 2001
From: KazWolfe
Date: Sun, 28 May 2023 22:46:49 -0700
Subject: [PATCH 35/68] Clean up Happy Eyeballs Verbose Spam (#1224)
* Clean up HappyEyeballs verbose spam
- Cancellations should be properly suppressed now
* Bump ClientStructs
---
Dalamud/Networking/Http/HappyEyeballsCallback.cs | 10 +++-------
Dalamud/Utility/AsyncUtils.cs | 2 +-
lib/FFXIVClientStructs | 2 +-
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/Dalamud/Networking/Http/HappyEyeballsCallback.cs b/Dalamud/Networking/Http/HappyEyeballsCallback.cs
index af854fb2c..7ca77576c 100644
--- a/Dalamud/Networking/Http/HappyEyeballsCallback.cs
+++ b/Dalamud/Networking/Http/HappyEyeballsCallback.cs
@@ -91,11 +91,7 @@ public class HappyEyeballsCallback : IDisposable
private async Task AttemptConnection(IPAddress address, int port, CancellationToken token, CancellationToken delayToken)
{
await AsyncUtils.CancellableDelay(-1, delayToken).ConfigureAwait(false);
-
- if (token.IsCancellationRequested)
- {
- return Task.FromCanceled(token).Result;
- }
+ token.ThrowIfCancellationRequested();
var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
{
@@ -116,9 +112,9 @@ public class HappyEyeballsCallback : IDisposable
private async Task> GetSortedAddresses(string hostname, CancellationToken token)
{
- // This method abuses DNS ordering and LINQ a bit. We can normally assume that "addresses" will be provided in
+ // This method abuses DNS ordering and LINQ a bit. We can normally assume that addresses will be provided in
// the order the system wants to use. GroupBy will return its groups *in the order they're discovered*. Meaning,
- // the first group created will always be the "preferred" group, and all other groups are in preference order.
+ // the first group created will always be the preferred group, and all other groups are in preference order.
// This means a straight zipper merge is nice and clean and gives us most -> least preferred, repeating.
var dnsRecords = await Dns.GetHostAddressesAsync(hostname, this.forcedAddressFamily, token);
diff --git a/Dalamud/Utility/AsyncUtils.cs b/Dalamud/Utility/AsyncUtils.cs
index d252bd5d5..c0fbbb8a4 100644
--- a/Dalamud/Utility/AsyncUtils.cs
+++ b/Dalamud/Utility/AsyncUtils.cs
@@ -51,7 +51,7 @@ public static class AsyncUtils
{
try
{
- await Task.Delay(millisecondsDelay, cancellationToken);
+ await Task.Delay(millisecondsDelay, cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index 5e9568845..8e25367e5 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 5e9568845ac302540ce1135abbff7bef98314714
+Subproject commit 8e25367e5f7d3acbe8e2b2a81121852b1ea1291c
From 7573575985f6ce229b7819254e068b1d94c6a682 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Sun, 28 May 2023 22:59:43 -0700
Subject: [PATCH 36/68] bump clientstructs (#1227)
---
lib/FFXIVClientStructs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs
index 8e25367e5..7c59a8393 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 8e25367e5f7d3acbe8e2b2a81121852b1ea1291c
+Subproject commit 7c59a83931338d7124d3fd75a2d7734dcc5dc2a1
From dcdfd3c403d4f06920af77445ad9f474d882bace Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Sun, 28 May 2023 23:41:33 -0700
Subject: [PATCH 37/68] chore: ci + readme fixes, make editorconfig consistent
with usage in yml files (#1228)
---
.editorconfig | 5 +++++
.github/workflows/tag-build.yml | 7 ++++++-
README.md | 23 +++++++++++------------
3 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/.editorconfig b/.editorconfig
index a0cd5584f..1b1377fca 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -140,3 +140,8 @@ indent_style = space
indent_size = 4
tab_width = 4
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent
+
+[*.{yaml,yml}]
+indent_style = space
+indent_size = 2
+tab_width = 2
diff --git a/.github/workflows/tag-build.yml b/.github/workflows/tag-build.yml
index f367c2fc9..042630191 100644
--- a/.github/workflows/tag-build.yml
+++ b/.github/workflows/tag-build.yml
@@ -1,5 +1,10 @@
name: Tag Build
-on: [push]
+on:
+ push:
+ branches:
+ - master
+ tags-ignore:
+ - '*' # don't needlessly execute on tags
jobs:
tag:
diff --git a/README.md b/README.md
index f98961c3e..97dd4e9dd 100644
--- a/README.md
+++ b/README.md
@@ -26,23 +26,22 @@ Thanks to Mino, whose work has made this possible!
These components are used in order to load Dalamud into a target process.
Dalamud can be loaded via DLL injection, or by rewriting a process' entrypoint.
-| Name | Purpose |
-|---|---|
-| *Dalamud.Injector.Boot* (C++) | Loads the .NET Core runtime into a process via hostfxr and kicks off Dalamud.Injector |
-| *Dalamud.Injector* (C#) | Performs DLL injection on the target process |
-| *Dalamud.Boot* (C++) | Loads the .NET Core runtime into the active process and kicks off Dalamud, or rewrites a target process' entrypoint to do so |
-| *Dalamud* (C#) | Core API, game bindings, plugin framework |
-| *Dalamud.CorePlugin* (C#) | Testbed plugin that can access Dalamud internals, to prototype new Dalamud features |
+| Name | Purpose |
+|-------------------------------|------------------------------------------------------------------------------------------------------------------------------|
+| *Dalamud.Injector.Boot* (C++) | Loads the .NET Core runtime into a process via hostfxr and kicks off Dalamud.Injector |
+| *Dalamud.Injector* (C#) | Performs DLL injection on the target process |
+| *Dalamud.Boot* (C++) | Loads the .NET Core runtime into the active process and kicks off Dalamud, or rewrites a target process' entrypoint to do so |
+| *Dalamud* (C#) | Core API, game bindings, plugin framework |
+| *Dalamud.CorePlugin* (C#) | Testbed plugin that can access Dalamud internals, to prototype new Dalamud features |
## Branches
We are currently working from the following branches.
-| Name | Purpose | .NET Version | Track |
-|---|---|---|---|
-| *master* | Current release branch | .NET 6.0.3 (March 2022) | Release & Staging |
-| *net7* | Upgrade to .NET 7 | .NET 7.0.0 (November 2022) | net7 |
-| *api3* | Legacy version, no longer in active use | .NET Framework 4.7.2 (April 2017) | - |
+| Name | API Level | Purpose | .NET Version | Track |
+|----------|-----------|------------------------------------------------------------|----------------------------|-------------------|
+| *master* | **8** | Current release branch | .NET 7.0.0 (November 2022) | Release & Staging |
+| *v9* | **9** | Next major version, slated for release alongside Patch 6.5 | .NET 7.0.0 (November 2022) | v9 |
From ab39b58a38dff2460427ed84210f952f5b247737 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Mon, 29 May 2023 10:38:18 -0700
Subject: [PATCH 38/68] build: 7.6.1.0 (#1229)
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 116ebd008..7263072d0 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.6.0.0
+ 7.6.1.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From bf8c69b0f33de8dc84ed08b7a500af94b33336f1 Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Tue, 30 May 2023 02:50:02 +0200
Subject: [PATCH 39/68] Update ClientStructs (#1223)
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 7c59a8393..6e688b966 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 7c59a83931338d7124d3fd75a2d7734dcc5dc2a1
+Subproject commit 6e688b9666c0fc8051d64a7dd7190412903ecda9
From 384383140cb8f3d9e973bf91e36a5669bce388a4 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Mon, 29 May 2023 21:04:31 -0700
Subject: [PATCH 40/68] Add CODEOWNERS file (#1230)
This should simply ensuring that the Dalamud maintainers are notified of
pull requests.
---
.github/CODEOWNERS | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .github/CODEOWNERS
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..ea155a3f0
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @goatcorp/dalamud-maintainers
From 716ba59aae7fb10ebb23bd2df3507e2b0b070c29 Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Mon, 29 May 2023 23:39:21 -0700
Subject: [PATCH 41/68] Revert "hook Present and ResizeBuffers with MinHook"
(#1231)
This reverts commit 5af1425c9760e38b2ac2b9c62ac3390436b135a2.
---
Dalamud/Interface/Internal/InterfaceManager.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index f02baa890..9a8da773c 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -1005,8 +1005,8 @@ internal class InterfaceManager : IDisposable, IServiceType
Log.Error(ex, "Could not enable immersive mode");
}
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour, true);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour, true);
+ this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
+ this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
From 62b3ded6ad460aeddbdec6357ccaad874c16735b Mon Sep 17 00:00:00 2001
From: Ava Chaney
Date: Mon, 29 May 2023 23:49:01 -0700
Subject: [PATCH 42/68] build: 7.6.2.0 (#1232)
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 7263072d0..a979bb5e8 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.6.1.0
+ 7.6.2.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From 5a8fff515fc93becd1f96b598da25ada88b3a65f Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Wed, 31 May 2023 20:02:07 +0200
Subject: [PATCH 43/68] Update ClientStructs (#1233)
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 6e688b966..5ff6d9788 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 6e688b9666c0fc8051d64a7dd7190412903ecda9
+Subproject commit 5ff6d97883455d061c6524bb028e06b96603a7c3
From 64fddf10bb77a844cbb3410f09ba6c479c69be50 Mon Sep 17 00:00:00 2001
From: nebel
Date: Sat, 3 Jun 2023 22:44:23 +0900
Subject: [PATCH 44/68] Improve unhooking of hooked functions
---
Dalamud/Hooking/AsmHook.cs | 16 +---
.../Internal/FunctionPointerVariableHook.cs | 9 +-
Dalamud/Hooking/Internal/HookManager.cs | 48 +++++-----
Dalamud/Hooking/Internal/MinHookHook.cs | 11 +--
Dalamud/Hooking/Internal/ReloadedHook.cs | 11 +--
Dalamud/Hooking/Internal/Unhooker.cs | 91 +++++++++++++++++++
6 files changed, 127 insertions(+), 59 deletions(-)
create mode 100644 Dalamud/Hooking/Internal/Unhooker.cs
diff --git a/Dalamud/Hooking/AsmHook.cs b/Dalamud/Hooking/AsmHook.cs
index 4c551db04..b05347a0a 100644
--- a/Dalamud/Hooking/AsmHook.cs
+++ b/Dalamud/Hooking/AsmHook.cs
@@ -35,12 +35,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
{
address = HookManager.FollowJmp(address);
- var hasOtherHooks = HookManager.Originals.ContainsKey(address);
- if (!hasOtherHooks)
- {
- MemoryHelper.ReadRaw(address, 0x32, out var original);
- HookManager.Originals[address] = original;
- }
+ // We cannot call TrimAfterHook here because the hook is activated by the caller.
+ HookManager.RegisterUnhooker(address);
this.address = address;
this.hookImpl = ReloadedHooks.Instance.CreateAsmHook(assembly, address.ToInt64(), (Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour)asmHookBehaviour);
@@ -65,12 +61,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
{
address = HookManager.FollowJmp(address);
- var hasOtherHooks = HookManager.Originals.ContainsKey(address);
- if (!hasOtherHooks)
- {
- MemoryHelper.ReadRaw(address, 0x32, out var original);
- HookManager.Originals[address] = original;
- }
+ // We cannot call TrimAfterHook here because the hook is activated by the caller.
+ HookManager.RegisterUnhooker(address);
this.address = address;
this.hookImpl = ReloadedHooks.Instance.CreateAsmHook(assembly, address.ToInt64(), (Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour)asmHookBehaviour);
diff --git a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
index e75d9c180..1c0bcfa05 100644
--- a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
+++ b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
@@ -41,12 +41,7 @@ internal class FunctionPointerVariableHook : Hook
{
lock (HookManager.HookEnableSyncRoot)
{
- var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
- if (!hasOtherHooks)
- {
- MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
- HookManager.Originals[this.Address] = original;
- }
+ var unhooker = HookManager.RegisterUnhooker(this.Address);
if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList))
{
@@ -104,6 +99,8 @@ internal class FunctionPointerVariableHook : Hook
// Add afterwards, so the hookIdent starts at 0.
indexList.Add(this);
+ unhooker.TrimAfterHook();
+
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
}
}
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index 303802ff6..11a7be337 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -16,7 +16,10 @@ namespace Dalamud.Hooking.Internal;
[ServiceManager.EarlyLoadedService]
internal class HookManager : IDisposable, IServiceType
{
- private static readonly ModuleLog Log = new("HM");
+ ///
+ /// Logger shared with
+ ///
+ internal static readonly ModuleLog Log = new("HM");
[ServiceManager.ServiceConstructor]
private HookManager()
@@ -34,9 +37,21 @@ internal class HookManager : IDisposable, IServiceType
internal static ConcurrentDictionary TrackedHooks { get; } = new();
///
- /// Gets a static dictionary of original code for a hooked address.
+ /// Gets a static dictionary of unhookers for a hooked address.
///
- internal static ConcurrentDictionary Originals { get; } = new();
+ internal static ConcurrentDictionary Unhookers { get; } = new();
+
+ ///
+ /// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
+ /// an existing instance if the address registered previously.
+ ///
+ /// The address of the instruction.
+ /// A new Unhooker instance.
+ public static Unhooker RegisterUnhooker(IntPtr address)
+ {
+ Log.Verbose($"Registering hook at 0x{address.ToInt64():X}");
+ return Unhookers.GetOrAdd(address, adr => new Unhooker(adr));
+ }
///
/// Gets a static dictionary of the number of hooks on a given address.
@@ -48,7 +63,7 @@ internal class HookManager : IDisposable, IServiceType
{
RevertHooks();
TrackedHooks.Clear();
- Originals.Clear();
+ Unhookers.Clear();
}
///
@@ -60,7 +75,7 @@ internal class HookManager : IDisposable, IServiceType
{
while (true)
{
- var hasOtherHooks = HookManager.Originals.ContainsKey(address);
+ var hasOtherHooks = HookManager.Unhookers.ContainsKey(address);
if (hasOtherHooks)
{
// This address has been hooked already. Do not follow a jmp into a trampoline of our own making.
@@ -124,28 +139,11 @@ internal class HookManager : IDisposable, IServiceType
return address;
}
- private static unsafe void RevertHooks()
+ private static void RevertHooks()
{
- foreach (var (address, originalBytes) in Originals)
+ foreach (var unhooker in Unhookers.Values)
{
- var i = 0;
- var current = (byte*)address;
- // Find how many bytes have been modified by comparing to the saved original
- for (; i < originalBytes.Length; i++)
- {
- if (current[i] == originalBytes[i])
- break;
- }
-
- var snippet = originalBytes[0..i];
-
- if (i > 0)
- {
- Log.Verbose($"Reverting hook at 0x{address.ToInt64():X} ({snippet.Length} bytes)");
- MemoryHelper.ChangePermission(address, i, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
- MemoryHelper.WriteRaw(address, snippet);
- MemoryHelper.ChangePermission(address, i, oldPermissions);
- }
+ unhooker.Unhook();
}
}
}
diff --git a/Dalamud/Hooking/Internal/MinHookHook.cs b/Dalamud/Hooking/Internal/MinHookHook.cs
index 0da289371..89a7d9206 100644
--- a/Dalamud/Hooking/Internal/MinHookHook.cs
+++ b/Dalamud/Hooking/Internal/MinHookHook.cs
@@ -1,8 +1,6 @@
using System;
using System.Reflection;
-using Dalamud.Memory;
-
namespace Dalamud.Hooking.Internal;
///
@@ -24,12 +22,7 @@ internal class MinHookHook : Hook where T : Delegate
{
lock (HookManager.HookEnableSyncRoot)
{
- var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
- if (!hasOtherHooks)
- {
- MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
- HookManager.Originals[this.Address] = original;
- }
+ var unhooker = HookManager.RegisterUnhooker(this.Address);
if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList))
indexList = HookManager.MultiHookTracker[this.Address] = new();
@@ -41,6 +34,8 @@ internal class MinHookHook : Hook where T : Delegate
// Add afterwards, so the hookIdent starts at 0.
indexList.Add(this);
+ unhooker.TrimAfterHook();
+
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
}
}
diff --git a/Dalamud/Hooking/Internal/ReloadedHook.cs b/Dalamud/Hooking/Internal/ReloadedHook.cs
index 77c0c9c19..172bd9671 100644
--- a/Dalamud/Hooking/Internal/ReloadedHook.cs
+++ b/Dalamud/Hooking/Internal/ReloadedHook.cs
@@ -1,7 +1,6 @@
using System;
using System.Reflection;
-using Dalamud.Memory;
using Reloaded.Hooks;
namespace Dalamud.Hooking.Internal;
@@ -25,17 +24,13 @@ internal class ReloadedHook : Hook where T : Delegate
{
lock (HookManager.HookEnableSyncRoot)
{
- var hasOtherHooks = HookManager.Originals.ContainsKey(this.Address);
- if (!hasOtherHooks)
- {
- MemoryHelper.ReadRaw(this.Address, 0x32, out var original);
- HookManager.Originals[this.Address] = original;
- }
-
+ var unhooker = HookManager.RegisterUnhooker(address);
this.hookImpl = ReloadedHooks.Instance.CreateHook(detour, address.ToInt64());
this.hookImpl.Activate();
this.hookImpl.Disable();
+ unhooker.TrimAfterHook();
+
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
}
}
diff --git a/Dalamud/Hooking/Internal/Unhooker.cs b/Dalamud/Hooking/Internal/Unhooker.cs
new file mode 100644
index 000000000..e609dd85e
--- /dev/null
+++ b/Dalamud/Hooking/Internal/Unhooker.cs
@@ -0,0 +1,91 @@
+using System;
+
+using Dalamud.Memory;
+
+namespace Dalamud.Hooking.Internal;
+
+///
+/// A class which stores a copy of the bytes at a location which will be hooked in the future, such that those bytes can
+/// be restored later to "unhook" the function.
+///
+public class Unhooker
+{
+ private readonly IntPtr address;
+ private byte[] originalBytes;
+ private bool trimmed;
+
+ ///
+ /// Initializes a new instance of the class. Upon creation, the Unhooker stores a copy of
+ /// the bytes stored at the provided address, and can be used to restore these bytes when the hook should be
+ /// removed. As such this class should be instantiated before the function is actually hooked.
+ ///
+ /// The address which will be hooked.
+ public Unhooker(IntPtr address)
+ {
+ this.address = address;
+ MemoryHelper.ReadRaw(address, 0x32, out this.originalBytes);
+ }
+
+ ///
+ /// When called after a hook is created, checks the pre-hook original bytes and post-hook modified bytes, trimming
+ /// the original bytes stored and removing unmodified bytes from the end of the byte sequence. Assuming no
+ /// concurrent actions modified the same address space, this should result in storing only the minimum bytes
+ /// required to unhook the function.
+ ///
+ public void TrimAfterHook()
+ {
+ if (this.trimmed)
+ {
+ return;
+ }
+
+ this.originalBytes = this.originalBytes[..this.GetFullHookLength()];
+ this.trimmed = true;
+ }
+
+ ///
+ /// Attempts to unhook the function by replacing the hooked bytes with the original bytes. If
+ /// was called, the trimmed original bytes stored at that time will be used for
+ /// unhooking. Otherwise, a naive algorithm which only restores bytes until the first unchanged byte will be used in
+ /// order to avoid overwriting adjacent data.
+ ///
+ public void Unhook()
+ {
+ var len = this.trimmed ? this.originalBytes.Length : this.GetNaiveHookLength();
+ if (len > 0)
+ {
+ HookManager.Log.Verbose($"Reverting hook at 0x{this.address.ToInt64():X} ({len} bytes, trimmed={this.trimmed})");
+ MemoryHelper.ChangePermission(this.address, len, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
+ MemoryHelper.WriteRaw(this.address, this.originalBytes[..len]);
+ MemoryHelper.ChangePermission(this.address, len, oldPermissions);
+ }
+ }
+
+ private unsafe int GetNaiveHookLength()
+ {
+ var current = (byte*)this.address;
+ for (var i = 0; i < this.originalBytes.Length; i++)
+ {
+ if (current[i] == this.originalBytes[i])
+ {
+ return i;
+ }
+ }
+
+ return 0;
+ }
+
+ private unsafe int GetFullHookLength()
+ {
+ var current = (byte*)this.address;
+ for (var i = this.originalBytes.Length - 1; i >= 0; i--)
+ {
+ if (current[i] != this.originalBytes[i])
+ {
+ return i + 1;
+ }
+ }
+
+ return 0;
+ }
+}
From 429316747de4dd1921cd0bb1cb81fff2e1cbd9dd Mon Sep 17 00:00:00 2001
From: nebel
Date: Sun, 4 Jun 2023 01:48:18 +0900
Subject: [PATCH 45/68] Add minBytes and maxBytes to Unhooker
---
.../Internal/FunctionPointerVariableHook.cs | 2 +-
Dalamud/Hooking/Internal/HookManager.cs | 8 ++++---
Dalamud/Hooking/Internal/Unhooker.cs | 21 ++++++++++++++-----
3 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
index 1c0bcfa05..e1900a903 100644
--- a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
+++ b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs
@@ -41,7 +41,7 @@ internal class FunctionPointerVariableHook : Hook
{
lock (HookManager.HookEnableSyncRoot)
{
- var unhooker = HookManager.RegisterUnhooker(this.Address);
+ var unhooker = HookManager.RegisterUnhooker(this.Address, 8, 8);
if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList))
{
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index 11a7be337..43858d012 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -46,11 +46,13 @@ internal class HookManager : IDisposable, IServiceType
/// an existing instance if the address registered previously.
///
/// The address of the instruction.
+ /// The minimum amount of bytes to restore when unhooking. Defaults to 0.
+ /// The maximum amount of bytes to restore when unhooking. Defaults to 0x32.
/// A new Unhooker instance.
- public static Unhooker RegisterUnhooker(IntPtr address)
+ public static Unhooker RegisterUnhooker(IntPtr address, int minBytes = 0, int maxBytes = 0x32)
{
- Log.Verbose($"Registering hook at 0x{address.ToInt64():X}");
- return Unhookers.GetOrAdd(address, adr => new Unhooker(adr));
+ Log.Verbose($"Registering hook at 0x{address.ToInt64():X} (minBytes=0x{minBytes:X}, maxBytes=0x{maxBytes:X})");
+ return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
}
///
diff --git a/Dalamud/Hooking/Internal/Unhooker.cs b/Dalamud/Hooking/Internal/Unhooker.cs
index e609dd85e..09b071ee9 100644
--- a/Dalamud/Hooking/Internal/Unhooker.cs
+++ b/Dalamud/Hooking/Internal/Unhooker.cs
@@ -11,6 +11,7 @@ namespace Dalamud.Hooking.Internal;
public class Unhooker
{
private readonly IntPtr address;
+ private readonly int minBytes;
private byte[] originalBytes;
private bool trimmed;
@@ -20,10 +21,18 @@ public class Unhooker
/// removed. As such this class should be instantiated before the function is actually hooked.
///
/// The address which will be hooked.
- public Unhooker(IntPtr address)
+ /// The minimum amount of bytes to restore when unhooking.
+ /// The maximum amount of bytes to restore when unhooking.
+ internal Unhooker(IntPtr address, int minBytes, int maxBytes)
{
+ if (minBytes < 0 || minBytes > maxBytes)
+ {
+ throw new ArgumentException($"minBytes ({minBytes}) must be <= maxBytes ({maxBytes}) and nonnegative.");
+ }
+
this.address = address;
- MemoryHelper.ReadRaw(address, 0x32, out this.originalBytes);
+ this.minBytes = minBytes;
+ MemoryHelper.ReadRaw(address, maxBytes, out this.originalBytes);
}
///
@@ -39,7 +48,9 @@ public class Unhooker
return;
}
- this.originalBytes = this.originalBytes[..this.GetFullHookLength()];
+ var len = int.Max(this.GetFullHookLength(), this.minBytes);
+
+ this.originalBytes = this.originalBytes[..len];
this.trimmed = true;
}
@@ -51,7 +62,7 @@ public class Unhooker
///
public void Unhook()
{
- var len = this.trimmed ? this.originalBytes.Length : this.GetNaiveHookLength();
+ var len = this.trimmed ? this.originalBytes.Length : int.Max(this.GetNaiveHookLength(), this.minBytes);
if (len > 0)
{
HookManager.Log.Verbose($"Reverting hook at 0x{this.address.ToInt64():X} ({len} bytes, trimmed={this.trimmed})");
@@ -82,7 +93,7 @@ public class Unhooker
{
if (current[i] != this.originalBytes[i])
{
- return i + 1;
+ return int.Max(i + 1, this.minBytes);
}
}
From 60a29e36c2a0d2f7a26195c96093501cb4ada110 Mon Sep 17 00:00:00 2001
From: nebel
Date: Sun, 4 Jun 2023 02:49:59 +0900
Subject: [PATCH 46/68] Create RegisterUnhooker overload
---
Dalamud/Hooking/Internal/HookManager.cs | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index 43858d012..f185450eb 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -43,13 +43,26 @@ internal class HookManager : IDisposable, IServiceType
///
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
- /// an existing instance if the address registered previously.
+ /// an existing instance if the address was registered previously. By default, the unhooker will restore between 0
+ /// and 0x32 bytes depending on the detected size of the hook. To specify the minimum and maximum bytes restored
+ /// manually, use .
///
/// The address of the instruction.
- /// The minimum amount of bytes to restore when unhooking. Defaults to 0.
- /// The maximum amount of bytes to restore when unhooking. Defaults to 0x32.
/// A new Unhooker instance.
- public static Unhooker RegisterUnhooker(IntPtr address, int minBytes = 0, int maxBytes = 0x32)
+ public static Unhooker RegisterUnhooker(IntPtr address)
+ {
+ return RegisterUnhooker(address, 0, 0x32);
+ }
+
+ ///
+ /// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
+ /// an existing instance if the address was registered previously.
+ ///
+ /// The address of the instruction.
+ /// The minimum amount of bytes to restore when unhooking.
+ /// The maximum amount of bytes to restore when unhooking.
+ /// A new Unhooker instance.
+ public static Unhooker RegisterUnhooker(IntPtr address, int minBytes, int maxBytes)
{
Log.Verbose($"Registering hook at 0x{address.ToInt64():X} (minBytes=0x{minBytes:X}, maxBytes=0x{maxBytes:X})");
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
From 1f4d13193ebe2296b3539f21c08f5c2f18ecbe11 Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Sun, 4 Jun 2023 14:18:03 +0200
Subject: [PATCH 47/68] [master] Update ClientStructs (#1234)
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 5ff6d9788..146201d91 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 5ff6d97883455d061c6524bb028e06b96603a7c3
+Subproject commit 146201d91da7da9322ca21fe3c1ad8e21034f763
From 4c9bc12f65558f373174bcbc135d68054d99e429 Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Tue, 6 Jun 2023 11:35:26 +0200
Subject: [PATCH 48/68] build: 7.6.3.0
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index a979bb5e8..0eca99487 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.6.2.0
+ 7.6.3.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From c5ffd25b75ddf0ffb52e8643a3882760a8b6cab4 Mon Sep 17 00:00:00 2001
From: nebel
Date: Wed, 7 Jun 2023 10:45:59 +0900
Subject: [PATCH 49/68] Fix exception when using reset button in Plugin
Statistics window
---
Dalamud/Interface/Internal/Windows/PluginStatWindow.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
index c83df2a4c..1db4b21f3 100644
--- a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
@@ -104,11 +104,11 @@ internal class PluginStatWindow : Window
? loadedPlugins.OrderBy(plugin => plugin.Name)
: loadedPlugins.OrderByDescending(plugin => plugin.Name),
2 => sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending
- ? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime)
- : loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime),
+ ? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime ?? 0)
+ : loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.MaxDrawTime ?? 0),
3 => sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending
- ? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.Average())
- : loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.Average()),
+ ? loadedPlugins.OrderBy(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.DefaultIfEmpty().Average() ?? 0)
+ : loadedPlugins.OrderByDescending(plugin => plugin.DalamudInterface?.UiBuilder.DrawTimeHistory.DefaultIfEmpty().Average() ?? 0),
_ => loadedPlugins,
};
From 69725bbfe9fe8a8a3d159f3f340e87d3123145d3 Mon Sep 17 00:00:00 2001
From: goat
Date: Wed, 7 Jun 2023 19:57:19 +0200
Subject: [PATCH 50/68] feat: clear font, style, color stack when fonts are
rebuilt
---
Dalamud/Interface/Internal/DalamudInterface.cs | 5 +++++
Dalamud/Interface/Internal/InterfaceManager.cs | 10 ++++++++++
lib/ImGuiScene | 2 +-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 07d557967..75c3b2b03 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -802,6 +802,11 @@ internal class DalamudInterface : IDisposable, IServiceType
ImGui.SetWindowFocus(null);
}
+ if (ImGui.MenuItem("Clear stacks"))
+ {
+ Service.Get().ClearStacks();
+ }
+
if (ImGui.MenuItem("Dump style"))
{
var info = string.Empty;
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 9a8da773c..5de5f52de 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -435,6 +435,15 @@ internal class InterfaceManager : IDisposable, IServiceType
return null;
}
+ ///
+ /// Clear font, style, and color stack. Dangerous, only use when you know
+ /// no one else has something pushed they may try to pop.
+ ///
+ public void ClearStacks()
+ {
+ this.scene?.ClearStacksOnContext();
+ }
+
///
/// Toggle Windows 11 immersive mode on the game window.
///
@@ -892,6 +901,7 @@ internal class InterfaceManager : IDisposable, IServiceType
Log.Verbose("[FONT] ImGui.IO.Build will be called.");
ioFonts.Build();
gameFontManager.AfterIoFontsBuild();
+ this.ClearStacks();
Log.Verbose("[FONT] ImGui.IO.Build OK!");
gameFontManager.AfterBuildFonts();
diff --git a/lib/ImGuiScene b/lib/ImGuiScene
index 262d3b066..99b735d7a 160000
--- a/lib/ImGuiScene
+++ b/lib/ImGuiScene
@@ -1 +1 @@
-Subproject commit 262d3b0668196fb236e2191c4a37e9be94e5a7a3
+Subproject commit 99b735d7a7fe36aaf92c027c75173dd150871c6b
From 50c70a47cfc8f0773f4562cc1081c91387275802 Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Thu, 8 Jun 2023 19:14:09 +0200
Subject: [PATCH 51/68] [master] Update ClientStructs (#1239)
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 146201d91..3595efd84 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 146201d91da7da9322ca21fe3c1ad8e21034f763
+Subproject commit 3595efd84f467e2c388f6e6b006a4e0764c46fdf
From 305f11559fa6a77ad0381387d1beffaf9a1b1026 Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Thu, 8 Jun 2023 19:15:31 +0200
Subject: [PATCH 52/68] build: 7.6.4.0
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 0eca99487..00b7950aa 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.6.3.0
+ 7.6.4.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From 29d0c1f61f43c2dc74b8bda8c39260eeadbac7a3 Mon Sep 17 00:00:00 2001
From: goat
Date: Thu, 8 Jun 2023 19:49:59 +0200
Subject: [PATCH 53/68] chore: build ImGui with a static CRT
---
lib/ImGuiScene | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/ImGuiScene b/lib/ImGuiScene
index 99b735d7a..2f37349ff 160000
--- a/lib/ImGuiScene
+++ b/lib/ImGuiScene
@@ -1 +1 @@
-Subproject commit 99b735d7a7fe36aaf92c027c75173dd150871c6b
+Subproject commit 2f37349ffd778561a1103a650683116c43edc86c
From b3e20eec186bc8e7f6ddd8b3b103870dfbb51e3f Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Thu, 8 Jun 2023 20:08:49 +0200
Subject: [PATCH 54/68] build: 7.6.5.0
---
Dalamud/Dalamud.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 00b7950aa..c52065616 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -8,7 +8,7 @@
- 7.6.4.0
+ 7.6.5.0
XIV Launcher addon framework
$(DalamudVersion)
$(DalamudVersion)
From 9e139389eaaaaf53fffc0d252963fa8518e43bb0 Mon Sep 17 00:00:00 2001
From: goat
Date: Thu, 8 Jun 2023 21:35:46 +0200
Subject: [PATCH 55/68] feat: add /xlrestart
---
Dalamud/Dalamud.cs | 15 +++++++++++++++
Dalamud/Interface/Internal/DalamudCommands.cs | 15 +++++++++++++--
Dalamud/Interface/Internal/DalamudInterface.cs | 7 +------
3 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 73914d2a7..c38594771 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -1,7 +1,9 @@
using System;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -97,6 +99,19 @@ internal sealed class Dalamud : IServiceType
/// Gets location of stored assets.
///
internal DirectoryInfo AssetDirectory => new(Service.Get().AssetDirectory!);
+
+ ///
+ /// Signal to the crash handler process that we should restart the game.
+ ///
+ public static void RestartGame()
+ {
+ [DllImport("kernel32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern void RaiseException(uint dwExceptionCode, uint dwExceptionFlags, uint nNumberOfArguments, IntPtr lpArguments);
+
+ RaiseException(0x12345678, 0, 0, IntPtr.Zero);
+ Process.GetCurrentProcess().Kill();
+ }
///
/// Queue an unload of Dalamud when it gets the chance.
diff --git a/Dalamud/Interface/Internal/DalamudCommands.cs b/Dalamud/Interface/Internal/DalamudCommands.cs
index dfcccf79d..307f79436 100644
--- a/Dalamud/Interface/Internal/DalamudCommands.cs
+++ b/Dalamud/Interface/Internal/DalamudCommands.cs
@@ -29,13 +29,19 @@ internal class DalamudCommands : IServiceType
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
ShowInHelp = false,
});
-
+
commandManager.AddHandler("/xlkill", new CommandInfo(this.OnKillCommand)
{
HelpMessage = "Kill the game.",
ShowInHelp = false,
});
+ commandManager.AddHandler("/xlrestart", new CommandInfo(this.OnRestartCommand)
+ {
+ HelpMessage = "Restart the game.",
+ ShowInHelp = false,
+ });
+
commandManager.AddHandler("/xlhelp", new CommandInfo(this.OnHelpCommand)
{
HelpMessage = Loc.Localize("DalamudCmdInfoHelp", "Shows list of commands available. If an argument is provided, shows help for that command."),
@@ -147,12 +153,17 @@ internal class DalamudCommands : IServiceType
Service.Get().Print("Unloading...");
Service.Get().Unload();
}
-
+
private void OnKillCommand(string command, string arguments)
{
Process.GetCurrentProcess().Kill();
}
+ private void OnRestartCommand(string command, string arguments)
+ {
+ Dalamud.RestartGame();
+ }
+
private void OnHelpCommand(string command, string arguments)
{
var chatGui = Service.Get();
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 75c3b2b03..ca7cbe287 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -717,12 +717,7 @@ internal class DalamudInterface : IDisposable, IServiceType
if (ImGui.MenuItem("Restart game"))
{
- [DllImport("kernel32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- static extern void RaiseException(uint dwExceptionCode, uint dwExceptionFlags, uint nNumberOfArguments, IntPtr lpArguments);
-
- RaiseException(0x12345678, 0, 0, IntPtr.Zero);
- Process.GetCurrentProcess().Kill();
+ Dalamud.RestartGame();
}
if (ImGui.MenuItem("Kill game"))
From b9101a55e8284caa2ef9b90709e8e6dd1066a6ad Mon Sep 17 00:00:00 2001
From: nebel
Date: Fri, 9 Jun 2023 12:59:37 +0900
Subject: [PATCH 56/68] Add filter to plugin stats window draw and framework
times (#1244)
Co-authored-by: nebel
---
.../Internal/Windows/PluginStatWindow.cs | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
index 1db4b21f3..44e43fbd3 100644
--- a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs
@@ -23,6 +23,8 @@ namespace Dalamud.Interface.Internal.Windows;
internal class PluginStatWindow : Window
{
private bool showDalamudHooks;
+ private string drawSearchText = string.Empty;
+ private string frameworkSearchText = string.Empty;
private string hookSearchText = string.Empty;
///
@@ -79,6 +81,12 @@ internal class PluginStatWindow : Window
ImGui.SameLine();
ImGuiComponents.TextWithLabel("Collective Average", $"{(loadedPlugins.Any() ? totalAverage / loadedPlugins.Count() / 10000f : 0):F4}ms", "Average of all average draw times");
+ ImGui.InputTextWithHint(
+ "###PluginStatWindow_DrawSearch",
+ "Search",
+ ref this.drawSearchText,
+ 500);
+
if (ImGui.BeginTable(
"##PluginStatsDrawTimes",
4,
@@ -114,6 +122,12 @@ internal class PluginStatWindow : Window
foreach (var plugin in loadedPlugins)
{
+ if (!this.drawSearchText.IsNullOrEmpty()
+ && !plugin.Manifest.Name.Contains(this.drawSearchText, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
ImGui.TableNextRow();
ImGui.TableNextColumn();
@@ -168,6 +182,12 @@ internal class PluginStatWindow : Window
ImGui.SameLine();
ImGuiComponents.TextWithLabel("Collective Average", $"{(statsHistory.Any() ? totalAverage / statsHistory.Length : 0):F4}ms", "Average of all average update times");
+ ImGui.InputTextWithHint(
+ "###PluginStatWindow_FrameworkSearch",
+ "Search",
+ ref this.frameworkSearchText,
+ 500);
+
if (ImGui.BeginTable(
"##PluginStatsFrameworkTimes",
4,
@@ -208,6 +228,13 @@ internal class PluginStatWindow : Window
continue;
}
+ if (!this.frameworkSearchText.IsNullOrEmpty()
+ && handlerHistory.Key != null
+ && !handlerHistory.Key.Contains(this.frameworkSearchText, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
ImGui.TableNextRow();
ImGui.TableNextColumn();
From 166301f56ff26a263dbeb6312a3626c9acad0ec8 Mon Sep 17 00:00:00 2001
From: goat
Date: Fri, 9 Jun 2023 23:26:46 +0200
Subject: [PATCH 57/68] fix warnings in Dalamud
---
Dalamud.CorePlugin/GlobalSuppressions.cs | 16 +++---
Dalamud.Injector/GlobalSuppressions.cs | 8 +--
Dalamud/Game/ChatHandlers.cs | 3 +-
.../Objects/Enums/BattleNpcSubKind.cs | 4 +-
.../ClientState/Objects/Types/BattleChara.cs | 16 +++---
.../ClientState/Objects/Types/Character.cs | 2 +-
Dalamud/Game/Config/GameConfigSection.cs | 57 ++++++++++++-------
Dalamud/Game/Framework.cs | 24 ++++----
Dalamud/Game/Network/GameNetwork.cs | 9 ++-
Dalamud/GlobalSuppressions.cs | 16 +++---
Dalamud/Hooking/Internal/HookManager.cs | 12 ++--
.../Interface/Internal/Windows/DataWindow.cs | 3 +-
.../DalamudChangelogManager.cs | 1 +
.../Settings/Widgets/SettingsEntry{T}.cs | 12 +++-
.../Internal/Windows/TitleScreenMenuWindow.cs | 1 -
Dalamud/Interface/UiBuilder.cs | 6 +-
Dalamud/Logging/PluginLog.cs | 12 ++--
Dalamud/NativeFunctions.cs | 3 +-
Dalamud/Plugin/Internal/PluginManager.cs | 1 -
Dalamud/ServiceManager.cs | 11 ++--
Dalamud/Service{T}.cs | 1 -
Dalamud/Support/BugBait.cs | 1 +
Dalamud/Utility/AsyncUtils.cs | 2 +-
Dalamud/Utility/Numerics/VectorExtensions.cs | 1 +
24 files changed, 124 insertions(+), 98 deletions(-)
diff --git a/Dalamud.CorePlugin/GlobalSuppressions.cs b/Dalamud.CorePlugin/GlobalSuppressions.cs
index bfaba20d0..771e6f182 100644
--- a/Dalamud.CorePlugin/GlobalSuppressions.cs
+++ b/Dalamud.CorePlugin/GlobalSuppressions.cs
@@ -6,12 +6,12 @@
using System.Diagnostics.CodeAnalysis;
// General
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
diff --git a/Dalamud.Injector/GlobalSuppressions.cs b/Dalamud.Injector/GlobalSuppressions.cs
index e1978fae4..57d7f2d28 100644
--- a/Dalamud.Injector/GlobalSuppressions.cs
+++ b/Dalamud.Injector/GlobalSuppressions.cs
@@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis;
// General
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static")]
diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs
index 4827c0d4f..7cd0869aa 100644
--- a/Dalamud/Game/ChatHandlers.cs
+++ b/Dalamud/Game/ChatHandlers.cs
@@ -248,7 +248,8 @@ public class ChatHandlers : IServiceType
var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
- if (this.configuration.PrintDalamudWelcomeMsg) {
+ if (this.configuration.PrintDalamudWelcomeMsg)
+ {
chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud vD{0} loaded."), assemblyVersion)
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), pluginManager.InstalledPlugins.Count(x => x.IsLoaded)));
}
diff --git a/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs b/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs
index f9019ed77..9c10a84ab 100644
--- a/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs
+++ b/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs
@@ -10,9 +10,9 @@ public enum BattleNpcSubKind : byte
///
None = 0,
- ///
+ ///
/// Weak Spots / Battle NPC parts
- /// Eg: Titan's Heart (Naval), Tioman's left and right wing (Sohm Al), Golem Soulstone (The Sunken Temple of Qarn)
+ /// Eg: Titan's Heart (Naval), Tioman's left and right wing (Sohm Al), Golem Soulstone (The Sunken Temple of Qarn).
///
BattleNpcPart = 1,
diff --git a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs
index 651f97834..63a5b828a 100644
--- a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs
+++ b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs
@@ -22,42 +22,42 @@ public unsafe class BattleChara : Character
///
/// Gets the current status effects.
///
- public StatusList StatusList => new(&this.Struct->StatusManager);
+ public StatusList StatusList => new(this.Struct->GetStatusManager);
///
/// Gets a value indicating whether the chara is currently casting.
///
- public bool IsCasting => this.Struct->SpellCastInfo.IsCasting > 0;
+ public bool IsCasting => this.Struct->GetCastInfo->IsCasting > 0;
///
/// Gets a value indicating whether the cast is interruptible.
///
- public bool IsCastInterruptible => this.Struct->SpellCastInfo.Interruptible > 0;
+ public bool IsCastInterruptible => this.Struct->GetCastInfo->Interruptible > 0;
///
/// Gets the spell action type of the spell being cast by the actor.
///
- public byte CastActionType => (byte)this.Struct->SpellCastInfo.ActionType;
+ public byte CastActionType => (byte)this.Struct->GetCastInfo->ActionType;
///
/// Gets the spell action ID of the spell being cast by the actor.
///
- public uint CastActionId => this.Struct->SpellCastInfo.ActionID;
+ public uint CastActionId => this.Struct->GetCastInfo->ActionID;
///
/// Gets the object ID of the target currently being cast at by the chara.
///
- public uint CastTargetObjectId => this.Struct->SpellCastInfo.CastTargetID;
+ public uint CastTargetObjectId => this.Struct->GetCastInfo->CastTargetID;
///
/// Gets the current casting time of the spell being cast by the chara.
///
- public float CurrentCastTime => this.Struct->SpellCastInfo.CurrentCastTime;
+ public float CurrentCastTime => this.Struct->GetCastInfo->CurrentCastTime;
///
/// Gets the total casting time of the spell being cast by the chara.
///
- public float TotalCastTime => this.Struct->SpellCastInfo.TotalCastTime;
+ public float TotalCastTime => this.Struct->GetCastInfo->TotalCastTime;
///
/// Gets the underlying structure.
diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs
index afc94bd0f..cdd1515b0 100644
--- a/Dalamud/Game/ClientState/Objects/Types/Character.cs
+++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs
@@ -77,7 +77,7 @@ public unsafe class Character : GameObject
/// Gets a byte array describing the visual appearance of this Chara.
/// Indexed by .
///
- public byte[] Customize => MemoryHelper.Read((IntPtr)this.Struct->CustomizeData, 28);
+ public byte[] Customize => MemoryHelper.Read((IntPtr)this.Struct->DrawData.CustomizeData.Data, 28);
///
/// Gets the Free Company tag of this chara.
diff --git a/Dalamud/Game/Config/GameConfigSection.cs b/Dalamud/Game/Config/GameConfigSection.cs
index 89df468f0..107b0d4a8 100644
--- a/Dalamud/Game/Config/GameConfigSection.cs
+++ b/Dalamud/Game/Config/GameConfigSection.cs
@@ -67,7 +67,8 @@ public class GameConfigSection
/// Name of the config option.
/// The returned value of the config option.
/// A value representing the success.
- public unsafe bool TryGetBool(string name, out bool value) {
+ public unsafe bool TryGetBool(string name, out bool value)
+ {
value = false;
if (!this.TryGetIndex(name, out var index))
{
@@ -97,7 +98,8 @@ public class GameConfigSection
/// Name of the config option.
/// Value of the config option.
/// Thrown if the config option is not found.
- public bool GetBool(string name) {
+ public bool GetBool(string name)
+ {
if (!this.TryGetBool(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -114,7 +116,8 @@ public class GameConfigSection
/// New value of the config option.
/// Throw if the config option is not found.
/// Thrown if the name of the config option is found, but the struct was not.
- public unsafe void Set(string name, bool value) {
+ public unsafe void Set(string name, bool value)
+ {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -139,7 +142,8 @@ public class GameConfigSection
/// Name of the config option.
/// The returned value of the config option.
/// A value representing the success.
- public unsafe bool TryGetUInt(string name, out uint value) {
+ public unsafe bool TryGetUInt(string name, out uint value)
+ {
value = 0;
if (!this.TryGetIndex(name, out var index))
{
@@ -169,7 +173,8 @@ public class GameConfigSection
/// Name of the config option.
/// Value of the config option.
/// Thrown if the config option is not found.
- public uint GetUInt(string name) {
+ public uint GetUInt(string name)
+ {
if (!this.TryGetUInt(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -186,8 +191,10 @@ public class GameConfigSection
/// New value of the config option.
/// Throw if the config option is not found.
/// Thrown if the name of the config option is found, but the struct was not.
- public unsafe void Set(string name, uint value) {
- this.framework.RunOnFrameworkThread(() => {
+ public unsafe void Set(string name, uint value)
+ {
+ this.framework.RunOnFrameworkThread(() =>
+ {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -213,7 +220,8 @@ public class GameConfigSection
/// Name of the config option.
/// The returned value of the config option.
/// A value representing the success.
- public unsafe bool TryGetFloat(string name, out float value) {
+ public unsafe bool TryGetFloat(string name, out float value)
+ {
value = 0;
if (!this.TryGetIndex(name, out var index))
{
@@ -243,7 +251,8 @@ public class GameConfigSection
/// Name of the config option.
/// Value of the config option.
/// Thrown if the config option is not found.
- public float GetFloat(string name) {
+ public float GetFloat(string name)
+ {
if (!this.TryGetFloat(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -260,8 +269,10 @@ public class GameConfigSection
/// New value of the config option.
/// Throw if the config option is not found.
/// Thrown if the name of the config option is found, but the struct was not.
- public unsafe void Set(string name, float value) {
- this.framework.RunOnFrameworkThread(() => {
+ public unsafe void Set(string name, float value)
+ {
+ this.framework.RunOnFrameworkThread(() =>
+ {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -287,7 +298,8 @@ public class GameConfigSection
/// Name of the config option.
/// The returned value of the config option.
/// A value representing the success.
- public unsafe bool TryGetString(string name, out string value) {
+ public unsafe bool TryGetString(string name, out string value)
+ {
value = string.Empty;
if (!this.TryGetIndex(name, out var index))
{
@@ -327,7 +339,8 @@ public class GameConfigSection
/// Name of the config option.
/// Value of the config option.
/// Thrown if the config option is not found.
- public string GetString(string name) {
+ public string GetString(string name)
+ {
if (!this.TryGetString(name, out var value))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -344,8 +357,10 @@ public class GameConfigSection
/// New value of the config option.
/// Throw if the config option is not found.
/// Thrown if the name of the config option is found, but the struct was not.
- public unsafe void Set(string name, string value) {
- this.framework.RunOnFrameworkThread(() => {
+ public unsafe void Set(string name, string value)
+ {
+ this.framework.RunOnFrameworkThread(() =>
+ {
if (!this.TryGetIndex(name, out var index))
{
throw new ConfigOptionNotFoundException(this.SectionName, name);
@@ -365,7 +380,8 @@ public class GameConfigSection
});
}
- private unsafe bool TryGetIndex(string name, out uint index) {
+ private unsafe bool TryGetIndex(string name, out uint index)
+ {
if (this.indexMap.TryGetValue(name, out index))
{
return true;
@@ -373,14 +389,16 @@ public class GameConfigSection
var configBase = this.GetConfigBase();
var e = configBase->ConfigEntry;
- for (var i = 0U; i < configBase->ConfigCount; i++, e++) {
+ for (var i = 0U; i < configBase->ConfigCount; i++, e++)
+ {
if (e->Name == null)
{
continue;
}
var eName = MemoryHelper.ReadStringNullTerminated(new IntPtr(e->Name));
- if (eName.Equals(name)) {
+ if (eName.Equals(name))
+ {
this.indexMap.TryAdd(name, i);
this.nameMap.TryAdd(i, name);
index = i;
@@ -392,7 +410,8 @@ public class GameConfigSection
return false;
}
- private unsafe bool TryGetEntry(uint index, out ConfigEntry* entry) {
+ private unsafe bool TryGetEntry(uint index, out ConfigEntry* entry)
+ {
entry = null;
var configBase = this.GetConfigBase();
if (configBase->ConfigEntry == null || index >= configBase->ConfigCount)
diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs
index 21bd9e646..b3083e913 100644
--- a/Dalamud/Game/Framework.cs
+++ b/Dalamud/Game/Framework.cs
@@ -25,25 +25,25 @@ namespace Dalamud.Game;
[ServiceManager.BlockingEarlyLoadedService]
public sealed class Framework : IDisposable, IServiceType
{
+ private static readonly Stopwatch StatsStopwatch = new();
+
private readonly GameLifecycle lifecycle;
- private static Stopwatch statsStopwatch = new();
-
private readonly Stopwatch updateStopwatch = new();
private readonly HitchDetector hitchDetector;
private readonly Hook updateHook;
private readonly Hook destroyHook;
+ [ServiceManager.ServiceDependency]
+ private readonly DalamudConfiguration configuration = Service.Get();
+
private readonly object runOnNextTickTaskListSync = new();
private List runOnNextTickTaskList = new();
private List runOnNextTickTaskList2 = new();
private Thread? frameworkUpdateThread;
- [ServiceManager.ServiceDependency]
- private readonly DalamudConfiguration configuration = Service.Get();
-
[ServiceManager.ServiceConstructor]
private Framework(SigScanner sigScanner, GameLifecycle lifecycle)
{
@@ -346,7 +346,7 @@ public sealed class Framework : IDisposable, IServiceType
this.destroyHook.Dispose();
this.updateStopwatch.Reset();
- statsStopwatch.Reset();
+ StatsStopwatch.Reset();
}
[ServiceManager.CallWhenServicesReady]
@@ -420,11 +420,11 @@ public sealed class Framework : IDisposable, IServiceType
if (StatsEnabled)
{
- statsStopwatch.Restart();
+ StatsStopwatch.Restart();
this.RunPendingTickTasks();
- statsStopwatch.Stop();
+ StatsStopwatch.Stop();
- AddToStats(nameof(this.RunPendingTickTasks), statsStopwatch.Elapsed.TotalMilliseconds);
+ AddToStats(nameof(this.RunPendingTickTasks), StatsStopwatch.Elapsed.TotalMilliseconds);
}
else
{
@@ -440,7 +440,7 @@ public sealed class Framework : IDisposable, IServiceType
// Individually invoke OnUpdate handlers and time them.
foreach (var d in invokeList)
{
- statsStopwatch.Restart();
+ StatsStopwatch.Restart();
try
{
d.Method.Invoke(d.Target, new object[] { this });
@@ -450,13 +450,13 @@ public sealed class Framework : IDisposable, IServiceType
Log.Error(ex, "Exception while dispatching Framework::Update event.");
}
- statsStopwatch.Stop();
+ StatsStopwatch.Stop();
var key = $"{d.Target}::{d.Method.Name}";
if (notUpdated.Contains(key))
notUpdated.Remove(key);
- AddToStats(key, statsStopwatch.Elapsed.TotalMilliseconds);
+ AddToStats(key, StatsStopwatch.Elapsed.TotalMilliseconds);
}
// Cleanup handlers that are no longer being called
diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs
index 767c9bc9a..d1fc0bfba 100644
--- a/Dalamud/Game/Network/GameNetwork.cs
+++ b/Dalamud/Game/Network/GameNetwork.cs
@@ -1,13 +1,12 @@
using System;
-using System.Collections.Generic;
using System.Runtime.InteropServices;
+
using Dalamud.Configuration.Internal;
using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Utility;
using Serilog;
-using Serilog.Core;
namespace Dalamud.Game.Network;
@@ -26,11 +25,11 @@ public sealed class GameNetwork : IDisposable, IServiceType
private readonly HitchDetector hitchDetectorUp;
private readonly HitchDetector hitchDetectorDown;
- private IntPtr baseAddress;
-
[ServiceManager.ServiceDependency]
private readonly DalamudConfiguration configuration = Service.Get();
-
+
+ private IntPtr baseAddress;
+
[ServiceManager.ServiceConstructor]
private GameNetwork(SigScanner sigScanner)
{
diff --git a/Dalamud/GlobalSuppressions.cs b/Dalamud/GlobalSuppressions.cs
index 5c3b40ebc..7426ed5c8 100644
--- a/Dalamud/GlobalSuppressions.cs
+++ b/Dalamud/GlobalSuppressions.cs
@@ -6,12 +6,12 @@
using System.Diagnostics.CodeAnalysis;
// General
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
-[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "I don't care anymore", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better")]
+[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "I don't care anymore")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index f185450eb..9c288a276 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -17,7 +17,7 @@ namespace Dalamud.Hooking.Internal;
internal class HookManager : IDisposable, IServiceType
{
///
- /// Logger shared with
+ /// Logger shared with .
///
internal static readonly ModuleLog Log = new("HM");
@@ -41,6 +41,11 @@ internal class HookManager : IDisposable, IServiceType
///
internal static ConcurrentDictionary Unhookers { get; } = new();
+ ///
+ /// Gets a static dictionary of the number of hooks on a given address.
+ ///
+ internal static ConcurrentDictionary> MultiHookTracker { get; } = new();
+
///
/// Creates a new Unhooker instance for the provided address if no such unhooker was already registered, or returns
/// an existing instance if the address was registered previously. By default, the unhooker will restore between 0
@@ -68,11 +73,6 @@ internal class HookManager : IDisposable, IServiceType
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
}
- ///
- /// Gets a static dictionary of the number of hooks on a given address.
- ///
- internal static ConcurrentDictionary> MultiHookTracker { get; } = new();
-
///
public void Dispose()
{
diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs
index 2c328d7f1..c46916343 100644
--- a/Dalamud/Interface/Internal/Windows/DataWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs
@@ -1792,7 +1792,8 @@ internal class DataWindow : Window
ImGui.TableNextColumn();
ImGui.TextUnformatted(string.Join(", ", share.Users));
}
- } finally
+ }
+ finally
{
ImGui.EndTable();
}
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs
index a3a965e80..2dc182e9a 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Json;
using System.Threading.Tasks;
+
using Dalamud.Networking.Http;
using Dalamud.Plugin.Internal;
using Dalamud.Utility;
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs
index 6352d7b69..83be6a052 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs
@@ -23,8 +23,16 @@ internal sealed class SettingsEntry : SettingsEntry
private object? valueBacking;
private object? fallbackValue;
- public SettingsEntry(string name, string description, LoadSettingDelegate load, SaveSettingDelegate save, Action? change = null, Func? warning = null, Func? validity = null, Func? visibility = null,
- object? fallbackValue = null)
+ public SettingsEntry(
+ string name,
+ string description,
+ LoadSettingDelegate load,
+ SaveSettingDelegate save,
+ Action? change = null,
+ Func? warning = null,
+ Func? validity = null,
+ Func? visibility = null,
+ object? fallbackValue = null)
{
this.load = load;
this.save = save;
diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
index 3c70c644c..143fda6ab 100644
--- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs
@@ -254,7 +254,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
var initialCursor = ImGui.GetCursorPos();
-
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, (float)shadeEasing.Value))
{
ImGui.Image(this.shadeTexture.ImGuiHandle, new Vector2(this.shadeTexture.Width * scale, this.shadeTexture.Height * scale));
diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs
index da4f241ac..eca0f64a0 100644
--- a/Dalamud/Interface/UiBuilder.cs
+++ b/Dalamud/Interface/UiBuilder.cs
@@ -31,12 +31,12 @@ public sealed class UiBuilder : IDisposable
private readonly InterfaceManager interfaceManager = Service.Get();
private readonly GameFontManager gameFontManager = Service.Get();
- private bool hasErrorWindow = false;
- private bool lastFrameUiHideState = false;
-
[ServiceManager.ServiceDependency]
private readonly DalamudConfiguration configuration = Service.Get();
+ private bool hasErrorWindow = false;
+ private bool lastFrameUiHideState = false;
+
///
/// Initializes a new instance of the class and registers it.
/// You do not have to call this manually.
diff --git a/Dalamud/Logging/PluginLog.cs b/Dalamud/Logging/PluginLog.cs
index 83cc90bc4..acbd663e7 100644
--- a/Dalamud/Logging/PluginLog.cs
+++ b/Dalamud/Logging/PluginLog.cs
@@ -277,6 +277,12 @@ public class PluginLog : IServiceType, IDisposable
public static void LogRaw(LogEventLevel level, Exception? exception, string messageTemplate, params object[] values)
=> WriteLog(Assembly.GetCallingAssembly().GetName().Name, level, messageTemplate, exception, values);
+ ///
+ void IDisposable.Dispose()
+ {
+ // ignored
+ }
+
#region New instanced methods
///
@@ -290,12 +296,6 @@ public class PluginLog : IServiceType, IDisposable
#endregion
- ///
- void IDisposable.Dispose()
- {
- // ignored
- }
-
private static ILogger GetPluginLogger(string? pluginName)
{
return Serilog.Log.ForContext("SourceContext", pluginName ?? string.Empty);
diff --git a/Dalamud/NativeFunctions.cs b/Dalamud/NativeFunctions.cs
index 50a58bfe7..b77f71d08 100644
--- a/Dalamud/NativeFunctions.cs
+++ b/Dalamud/NativeFunctions.cs
@@ -1132,6 +1132,7 @@ internal static partial class NativeFunctions
///
/// Native kernel32 functions.
///
+[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "Stupid rule")]
internal static partial class NativeFunctions
{
///
@@ -1394,7 +1395,7 @@ internal static partial class NativeFunctions
}
///
- /// HEAP_* from heapapi
+ /// HEAP_* from heapapi.
///
[Flags]
public enum HeapOptions
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index 9df249dd7..5204b656e 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -61,7 +61,6 @@ internal partial class PluginManager : IDisposable, IServiceType
private readonly object pluginListLock = new();
private readonly DirectoryInfo pluginDirectory;
- private readonly DirectoryInfo devPluginDirectory;
private readonly BannedPlugin[]? bannedPlugins;
private readonly DalamudLinkPayload openInstallerWindowPluginChangelogsLink;
diff --git a/Dalamud/ServiceManager.cs b/Dalamud/ServiceManager.cs
index c13d39f4c..41ffe33ca 100644
--- a/Dalamud/ServiceManager.cs
+++ b/Dalamud/ServiceManager.cs
@@ -16,18 +16,15 @@ using JetBrains.Annotations;
namespace Dalamud;
+// TODO:
+// - Unify dependency walking code(load/unload
+// - Visualize/output .dot or imgui thing
+
///
/// Class to initialize Service<T>s.
///
internal static class ServiceManager
{
- /**
- * TODO:
- * - Unify dependency walking code(load/unload
- * - Visualize/output .dot or imgui thing
- */
-
-
///
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
///
diff --git a/Dalamud/Service{T}.cs b/Dalamud/Service{T}.cs
index a1a56a9f0..aa10ead6e 100644
--- a/Dalamud/Service{T}.cs
+++ b/Dalamud/Service{T}.cs
@@ -141,7 +141,6 @@ internal static class Service where T : IServiceType
.OfType()
.Select(x => x.GetType().GetGenericArguments().First()));
-
// HACK: PluginManager needs to depend on ALL plugin exposed services
if (typeof(T) == typeof(PluginManager))
{
diff --git a/Dalamud/Support/BugBait.cs b/Dalamud/Support/BugBait.cs
index ce74c03ec..cdbf94616 100644
--- a/Dalamud/Support/BugBait.cs
+++ b/Dalamud/Support/BugBait.cs
@@ -1,6 +1,7 @@
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
+
using Dalamud.Networking.Http;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
diff --git a/Dalamud/Utility/AsyncUtils.cs b/Dalamud/Utility/AsyncUtils.cs
index c0fbbb8a4..d02c90195 100644
--- a/Dalamud/Utility/AsyncUtils.cs
+++ b/Dalamud/Utility/AsyncUtils.cs
@@ -19,7 +19,7 @@ public static class AsyncUtils
/// A list of tasks to race.
/// The return type of all raced tasks.
/// Thrown when all tasks given to this method fail.
- /// Returns the first task that completes, according to .
+ /// Returns the first task that completes, according to .
public static Task FirstSuccessfulTask(ICollection> tasks)
{
var tcs = new TaskCompletionSource();
diff --git a/Dalamud/Utility/Numerics/VectorExtensions.cs b/Dalamud/Utility/Numerics/VectorExtensions.cs
index cd958deb8..910dbdd00 100644
--- a/Dalamud/Utility/Numerics/VectorExtensions.cs
+++ b/Dalamud/Utility/Numerics/VectorExtensions.cs
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
+
using FFXIVClientStructs.FFXIV.Client.Graphics;
namespace Dalamud.Utility.Numerics;
From aa2cd47ef347b1b8ab84556177d0f67c368b5355 Mon Sep 17 00:00:00 2001
From: goat
Date: Fri, 9 Jun 2023 23:37:42 +0200
Subject: [PATCH 58/68] fix warnings in injector
---
Dalamud.CorePlugin/PluginImpl.cs | 1 +
Dalamud.Injector/EntryPoint.cs | 36 ++++++++++++++++++------------
Dalamud.Injector/GameStart.cs | 20 +++++++++--------
Dalamud.Injector/LegacyBlowfish.cs | 13 +++++++++--
4 files changed, 45 insertions(+), 25 deletions(-)
diff --git a/Dalamud.CorePlugin/PluginImpl.cs b/Dalamud.CorePlugin/PluginImpl.cs
index d352ad2c8..9026ea0dd 100644
--- a/Dalamud.CorePlugin/PluginImpl.cs
+++ b/Dalamud.CorePlugin/PluginImpl.cs
@@ -54,6 +54,7 @@ namespace Dalamud.CorePlugin
/// Initializes a new instance of the class.
///
/// Dalamud plugin interface.
+ /// Logging service.
public PluginImpl(DalamudPluginInterface pluginInterface, PluginLog log)
{
try
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index fbefbd92a..c4c553a47 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -739,10 +739,12 @@ namespace Dalamud.Injector
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
[System.Runtime.InteropServices.DllImport("c")]
- static extern ulong clock_gettime_nsec_np(int clock_id);
+#pragma warning disable SA1300
+ static extern ulong clock_gettime_nsec_np(int clockId);
+#pragma warning restore SA1300
const int CLOCK_MONOTONIC_RAW = 4;
- var rawTickCountFixed = (clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) / 1000000);
+ var rawTickCountFixed = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) / 1000000;
Log.Information("ArgumentBuilder::DeriveKey() fixing up rawTickCount from {0} to {1} on macOS", rawTickCount, rawTickCountFixed);
rawTickCount = (uint)rawTickCountFixed;
}
@@ -764,21 +766,27 @@ namespace Dalamud.Injector
gameArgumentString = string.Join(" ", gameArguments.Select(x => EncodeParameterArgument(x)));
}
- var process = GameStart.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, noFixAcl, (Process p) =>
- {
- if (!withoutDalamud && mode == "entrypoint")
+ var process = GameStart.LaunchGame(
+ Path.GetDirectoryName(gamePath),
+ gamePath,
+ gameArgumentString,
+ noFixAcl,
+ p =>
{
- var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
- Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
- if (RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)) != 0)
+ if (!withoutDalamud && mode == "entrypoint")
{
- Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
- throw new Exception("RewriteRemoteEntryPointW failed");
- }
+ var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
+ Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
+ if (RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)) != 0)
+ {
+ Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
+ throw new Exception("RewriteRemoteEntryPointW failed");
+ }
- Log.Verbose("RewriteRemoteEntryPointW called!");
- }
- }, waitForGameWindow);
+ Log.Verbose("RewriteRemoteEntryPointW called!");
+ }
+ },
+ waitForGameWindow);
Log.Verbose("Game process started with PID {0}", process.Id);
diff --git a/Dalamud.Injector/GameStart.cs b/Dalamud.Injector/GameStart.cs
index 95e963a9a..e34048978 100644
--- a/Dalamud.Injector/GameStart.cs
+++ b/Dalamud.Injector/GameStart.cs
@@ -211,6 +211,9 @@ namespace Dalamud.Injector
}
}
+ ///
+ /// Claim a SE Debug Privilege.
+ ///
public static void ClaimSeDebug()
{
var hToken = PInvoke.INVALID_HANDLE_VALUE;
@@ -345,8 +348,6 @@ namespace Dalamud.Injector
private static class PInvoke
{
#region Constants
- public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
-
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
public const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000;
@@ -369,6 +370,8 @@ namespace Dalamud.Injector
public const UInt32 ERROR_NO_TOKEN = 0x000003F0;
+ public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
+
public enum MULTIPLE_TRUSTEE_OPERATION
{
NO_MULTIPLE_TRUSTEE,
@@ -431,7 +434,7 @@ namespace Dalamud.Injector
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
- SecurityDelegation
+ SecurityDelegation,
}
#endregion
@@ -485,8 +488,7 @@ namespace Dalamud.Injector
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateSelf(
- SECURITY_IMPERSONATION_LEVEL impersonationLevel
- );
+ SECURITY_IMPERSONATION_LEVEL impersonationLevel);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool OpenProcessToken(
@@ -496,10 +498,10 @@ namespace Dalamud.Injector
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool OpenThreadToken(
- IntPtr ThreadHandle,
- uint DesiredAccess,
- bool OpenAsSelf,
- out IntPtr TokenHandle);
+ IntPtr threadHandle,
+ uint desiredAccess,
+ bool openAsSelf,
+ out IntPtr tokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
diff --git a/Dalamud.Injector/LegacyBlowfish.cs b/Dalamud.Injector/LegacyBlowfish.cs
index 28cc584e4..99c514954 100644
--- a/Dalamud.Injector/LegacyBlowfish.cs
+++ b/Dalamud.Injector/LegacyBlowfish.cs
@@ -1,8 +1,16 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
namespace Dalamud.Injector
{
+ [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1413:Use trailing comma in multi-line initializers", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1519:Braces should not be omitted from multi-line child statement", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1414:Tuple types in signatures should have element names", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Legacy code")]
+ [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "Legacy code")]
internal class LegacyBlowfish
{
#region P-Array and S-Boxes
@@ -203,10 +211,9 @@ namespace Dalamud.Injector
private static readonly int Rounds = 16;
///
- /// Initialize a new blowfish.
+ /// Initializes a new instance of the class.
///
/// The key to use.
- /// Whether or not a sign confusion should be introduced during key init. This is needed for SE's implementation of blowfish.
public LegacyBlowfish(byte[] key)
{
foreach (var (i, keyFragment) in WrappingUInt32(key, this.p.Length))
@@ -306,7 +313,9 @@ namespace Dalamud.Injector
for (var j = 0; j < 4 && enumerator.MoveNext(); j++)
{
+#pragma warning disable CS0675
n = (uint)((n << 8) | (sbyte)enumerator.Current); // NOTE(goat): THIS IS A BUG! SE's implementation wrongly uses signed numbers for this, so we need to as well.
+#pragma warning restore CS0675
}
yield return (i, n);
From 0a829d304eea9c5a734839fec88520188fc38235 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:17:30 +0200
Subject: [PATCH 59/68] fix warnings
---
.../PluginInstaller/PluginInstallerWindow.cs | 82 +++---
.../PluginInstaller/ProfileManagerWidget.cs | 25 +-
Dalamud/Plugin/Internal/PluginManager.cs | 262 +++++++++---------
.../Profiles/ProfileCommandHandler.cs | 14 +-
.../Internal/Profiles/ProfileManager.cs | 2 +-
.../Plugin/Internal/Profiles/ProfileModel.cs | 20 ++
.../Internal/Profiles/ProfileModelV1.cs | 30 +-
.../Internal/Profiles/ProfilePluginEntry.cs | 14 +
Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 6 +-
9 files changed, 266 insertions(+), 189 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index 3b3804baa..d28401e73 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -327,6 +327,46 @@ internal class PluginInstallerWindow : Window, IDisposable
});
}
+ ///
+ /// A continuation task that displays any errors received into the error modal.
+ ///
+ /// The previous task.
+ /// An error message to be displayed.
+ /// 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;
+
+ foreach (var ex in task.Exception.InnerExceptions)
+ {
+ if (ex is PluginException)
+ {
+ Log.Error(ex, "Plugin installer threw an error");
+#if DEBUG
+ if (!string.IsNullOrEmpty(ex.Message))
+ errorModalMessage += $"\n\n{ex.Message}";
+#endif
+ }
+ else
+ {
+ Log.Error(ex, "Plugin installer threw an unexpected error");
+#if DEBUG
+ if (!string.IsNullOrEmpty(ex.Message))
+ errorModalMessage += $"\n\n{ex.Message}";
+#endif
+ }
+ }
+
+ this.ShowErrorModal(errorModalMessage);
+
+ return false;
+ }
+
+ return true;
+ }
+
private void DrawProgressOverlay()
{
var pluginManager = Service.Get();
@@ -2155,7 +2195,7 @@ internal class PluginInstallerWindow : Window, IDisposable
this.DrawSendFeedbackButton(plugin.Manifest, plugin.IsTesting);
}
- if (availablePluginUpdate != default && ! plugin.IsDev)
+ if (availablePluginUpdate != default && !plugin.IsDev)
this.DrawUpdateSinglePluginButton(availablePluginUpdate);
ImGui.SameLine();
@@ -2914,46 +2954,6 @@ internal class PluginInstallerWindow : Window, IDisposable
private bool WasPluginSeen(string internalName) =>
Service.Get().SeenPluginInternalName.Contains(internalName);
- ///
- /// A continuation task that displays any errors received into the error modal.
- ///
- /// The previous task.
- /// An error message to be displayed.
- /// 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;
-
- foreach (var ex in task.Exception.InnerExceptions)
- {
- if (ex is PluginException)
- {
- Log.Error(ex, "Plugin installer threw an error");
-#if DEBUG
- if (!string.IsNullOrEmpty(ex.Message))
- errorModalMessage += $"\n\n{ex.Message}";
-#endif
- }
- else
- {
- Log.Error(ex, "Plugin installer threw an unexpected error");
-#if DEBUG
- if (!string.IsNullOrEmpty(ex.Message))
- errorModalMessage += $"\n\n{ex.Message}";
-#endif
- }
- }
-
- this.ShowErrorModal(errorModalMessage);
-
- return false;
- }
-
- return true;
- }
-
private Task ShowErrorModal(string message)
{
this.errorModalMessage = message;
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 1448074f3..60f605f8e 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -16,6 +16,9 @@ using Serilog;
namespace Dalamud.Interface.Internal.Windows.PluginInstaller;
+///
+/// ImGui widget used to manage profiles.
+///
internal class ProfileManagerWidget
{
private readonly PluginInstallerWindow installer;
@@ -25,11 +28,24 @@ internal class ProfileManagerWidget
private string pickerSearch = string.Empty;
private string profileNameEdit = string.Empty;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The plugin installer.
public ProfileManagerWidget(PluginInstallerWindow installer)
{
this.installer = installer;
}
+ private enum Mode
+ {
+ Overview,
+ EditSingleProfile,
+ }
+
+ ///
+ /// Draw this widget's contents.
+ ///
public void Draw()
{
switch (this.mode)
@@ -44,6 +60,9 @@ internal class ProfileManagerWidget
}
}
+ ///
+ /// Reset the widget.
+ ///
public void Reset()
{
this.mode = Mode.Overview;
@@ -428,12 +447,6 @@ internal class ProfileManagerWidget
}
}
- private enum Mode
- {
- Overview,
- EditSingleProfile,
- }
-
private static class Locs
{
public static string TooltipEnableDisable =>
diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs
index a0283ca67..162b6c90b 100644
--- a/Dalamud/Plugin/Internal/PluginManager.cs
+++ b/Dalamud/Plugin/Internal/PluginManager.cs
@@ -816,137 +816,6 @@ internal partial class PluginManager : IDisposable, IServiceType
return plugin;
}
- ///
- /// Load a plugin.
- ///
- /// The associated with the main assembly of this plugin.
- /// The already loaded definition, if available.
- /// The reason this plugin was loaded.
- /// If this plugin should support development features.
- /// If this plugin is being loaded at boot.
- /// Don't load the plugin, just don't do it.
- /// The loaded plugin.
- private async Task LoadPluginAsync(FileInfo dllFile, LocalPluginManifest? manifest, PluginLoadReason reason, bool isDev = false, bool isBoot = false, bool doNotLoad = false)
- {
- var name = manifest?.Name ?? dllFile.Name;
- var loadPlugin = !doNotLoad;
-
- LocalPlugin plugin;
-
- if (manifest != null && manifest.InternalName == null)
- {
- Log.Error("{FileName}: Your manifest has no internal name set! Can't load this.", dllFile.FullName);
- throw new Exception("No internal name");
- }
-
- if (isDev)
- {
- Log.Information($"Loading dev plugin {name}");
- var devPlugin = new LocalDevPlugin(dllFile, manifest);
- loadPlugin &= !isBoot || devPlugin.StartOnBoot;
-
- var probablyInternalNameForThisPurpose = manifest?.InternalName ?? dllFile.Name;
- var wantsInDefaultProfile =
- this.profileManager.DefaultProfile.WantsPlugin(probablyInternalNameForThisPurpose);
- if (wantsInDefaultProfile == false && devPlugin.StartOnBoot)
- {
- this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, true, false);
- }
- else if (wantsInDefaultProfile == true && !devPlugin.StartOnBoot)
- {
- this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, false, false);
- }
-
- plugin = devPlugin;
- }
- else
- {
- Log.Information($"Loading plugin {name}");
- plugin = new LocalPlugin(dllFile, manifest);
- }
-
-#pragma warning disable CS0618
- var defaultState = manifest?.Disabled != true && loadPlugin;
-#pragma warning restore CS0618
-
- // Need to do this here, so plugins that don't load are still added to the default profile
- var wantToLoad = this.profileManager.GetWantState(plugin.Manifest.InternalName, defaultState);
-
- if (loadPlugin)
- {
- try
- {
- if (wantToLoad && !plugin.IsOrphaned)
- {
- await plugin.LoadAsync(reason);
- }
- else
- {
- Log.Verbose($"{name} not loaded, wantToLoad:{wantToLoad} orphaned:{plugin.IsOrphaned}");
- }
- }
- catch (InvalidPluginException)
- {
- PluginLocations.Remove(plugin.AssemblyName?.FullName ?? string.Empty, out _);
- throw;
- }
- catch (BannedPluginException)
- {
- // Out of date plugins get added so they can be updated.
- Log.Information($"Plugin was banned, adding anyways: {dllFile.Name}");
- }
- catch (Exception ex)
- {
- if (plugin.IsDev)
- {
- // Dev plugins always get added to the list so they can be fiddled with in the UI
- Log.Information(ex, $"Dev plugin failed to load, adding anyways: {dllFile.Name}");
-
- // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
- // plugin.Disable(); // Disable here, otherwise you can't enable+load later
- }
- else if (plugin.IsOutdated)
- {
- // Out of date plugins get added, so they can be updated.
- Log.Information(ex, $"Plugin was outdated, adding anyways: {dllFile.Name}");
- }
- else if (plugin.IsOrphaned)
- {
- // Orphaned plugins get added, so that users aren't confused.
- Log.Information(ex, $"Plugin was orphaned, adding anyways: {dllFile.Name}");
- }
- else if (isBoot)
- {
- // During boot load, plugins always get added to the list so they can be fiddled with in the UI
- Log.Information(ex, $"Regular plugin failed to load, adding anyways: {dllFile.Name}");
-
- // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
- // plugin.Disable(); // Disable here, otherwise you can't enable+load later
- }
- else if (!plugin.CheckPolicy())
- {
- // During boot load, plugins always get added to the list so they can be fiddled with in the UI
- Log.Information(ex, $"Plugin not loaded due to policy, adding anyways: {dllFile.Name}");
-
- // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
- // plugin.Disable(); // Disable here, otherwise you can't enable+load later
- }
- else
- {
- PluginLocations.Remove(plugin.AssemblyName?.FullName ?? string.Empty, out _);
- throw;
- }
- }
- }
-
- lock (this.pluginListLock)
- {
- this.InstalledPlugins = this.InstalledPlugins.Add(plugin);
- }
-
- return plugin;
- }
-
///
/// Remove a plugin.
///
@@ -1330,6 +1199,137 @@ internal partial class PluginManager : IDisposable, IServiceType
/// The calling plugin, or null.
public LocalPlugin? FindCallingPlugin() => this.FindCallingPlugin(new StackTrace());
+ ///
+ /// Load a plugin.
+ ///
+ /// The associated with the main assembly of this plugin.
+ /// The already loaded definition, if available.
+ /// The reason this plugin was loaded.
+ /// If this plugin should support development features.
+ /// If this plugin is being loaded at boot.
+ /// Don't load the plugin, just don't do it.
+ /// The loaded plugin.
+ private async Task LoadPluginAsync(FileInfo dllFile, LocalPluginManifest? manifest, PluginLoadReason reason, bool isDev = false, bool isBoot = false, bool doNotLoad = false)
+ {
+ var name = manifest?.Name ?? dllFile.Name;
+ var loadPlugin = !doNotLoad;
+
+ LocalPlugin plugin;
+
+ if (manifest != null && manifest.InternalName == null)
+ {
+ Log.Error("{FileName}: Your manifest has no internal name set! Can't load this.", dllFile.FullName);
+ throw new Exception("No internal name");
+ }
+
+ if (isDev)
+ {
+ Log.Information($"Loading dev plugin {name}");
+ var devPlugin = new LocalDevPlugin(dllFile, manifest);
+ loadPlugin &= !isBoot || devPlugin.StartOnBoot;
+
+ var probablyInternalNameForThisPurpose = manifest?.InternalName ?? dllFile.Name;
+ var wantsInDefaultProfile =
+ this.profileManager.DefaultProfile.WantsPlugin(probablyInternalNameForThisPurpose);
+ if (wantsInDefaultProfile == false && devPlugin.StartOnBoot)
+ {
+ this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, true, false);
+ }
+ else if (wantsInDefaultProfile == true && !devPlugin.StartOnBoot)
+ {
+ this.profileManager.DefaultProfile.AddOrUpdate(probablyInternalNameForThisPurpose, false, false);
+ }
+
+ plugin = devPlugin;
+ }
+ else
+ {
+ Log.Information($"Loading plugin {name}");
+ plugin = new LocalPlugin(dllFile, manifest);
+ }
+
+#pragma warning disable CS0618
+ var defaultState = manifest?.Disabled != true && loadPlugin;
+#pragma warning restore CS0618
+
+ // Need to do this here, so plugins that don't load are still added to the default profile
+ var wantToLoad = this.profileManager.GetWantState(plugin.Manifest.InternalName, defaultState);
+
+ if (loadPlugin)
+ {
+ try
+ {
+ if (wantToLoad && !plugin.IsOrphaned)
+ {
+ await plugin.LoadAsync(reason);
+ }
+ else
+ {
+ Log.Verbose($"{name} not loaded, wantToLoad:{wantToLoad} orphaned:{plugin.IsOrphaned}");
+ }
+ }
+ catch (InvalidPluginException)
+ {
+ PluginLocations.Remove(plugin.AssemblyName?.FullName ?? string.Empty, out _);
+ throw;
+ }
+ catch (BannedPluginException)
+ {
+ // Out of date plugins get added so they can be updated.
+ Log.Information($"Plugin was banned, adding anyways: {dllFile.Name}");
+ }
+ catch (Exception ex)
+ {
+ if (plugin.IsDev)
+ {
+ // Dev plugins always get added to the list so they can be fiddled with in the UI
+ Log.Information(ex, $"Dev plugin failed to load, adding anyways: {dllFile.Name}");
+
+ // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
+ // plugin.Disable(); // Disable here, otherwise you can't enable+load later
+ }
+ else if (plugin.IsOutdated)
+ {
+ // Out of date plugins get added, so they can be updated.
+ Log.Information(ex, $"Plugin was outdated, adding anyways: {dllFile.Name}");
+ }
+ else if (plugin.IsOrphaned)
+ {
+ // Orphaned plugins get added, so that users aren't confused.
+ Log.Information(ex, $"Plugin was orphaned, adding anyways: {dllFile.Name}");
+ }
+ else if (isBoot)
+ {
+ // During boot load, plugins always get added to the list so they can be fiddled with in the UI
+ Log.Information(ex, $"Regular plugin failed to load, adding anyways: {dllFile.Name}");
+
+ // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
+ // plugin.Disable(); // Disable here, otherwise you can't enable+load later
+ }
+ else if (!plugin.CheckPolicy())
+ {
+ // During boot load, plugins always get added to the list so they can be fiddled with in the UI
+ Log.Information(ex, $"Plugin not loaded due to policy, adding anyways: {dllFile.Name}");
+
+ // NOTE(goat): This can't work - plugins don't "unload" if they fail to load.
+ // plugin.Disable(); // Disable here, otherwise you can't enable+load later
+ }
+ else
+ {
+ PluginLocations.Remove(plugin.AssemblyName?.FullName ?? string.Empty, out _);
+ throw;
+ }
+ }
+ }
+
+ lock (this.pluginListLock)
+ {
+ this.InstalledPlugins = this.InstalledPlugins.Add(plugin);
+ }
+
+ return plugin;
+ }
+
private void DetectAvailablePluginUpdates()
{
var updatablePlugins = new List();
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
index 994069335..aa72200a1 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
@@ -61,6 +61,13 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
this.framework.Update += this.FrameworkOnUpdate;
}
+ private enum ProfileOp
+ {
+ Enable,
+ Disable,
+ Toggle,
+ }
+
///
public void Dispose()
{
@@ -166,11 +173,4 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
return name;
}
-
- private enum ProfileOp
- {
- Enable,
- Disable,
- Toggle,
- }
}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index b7073ac99..f7eac6d21 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -165,7 +165,7 @@ internal class ProfileManager : IServiceType
///
/// Go through all profiles and plugins, and enable/disable plugins they want active.
- /// This will block until all plugins have been loaded/reloaded!
+ /// This will block until all plugins have been loaded/reloaded.
///
public void ApplyAllWantStates()
{
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
index 06075a07e..c73e80450 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
@@ -5,14 +5,29 @@ using Newtonsoft.Json;
namespace Dalamud.Plugin.Internal.Profiles;
+///
+/// Class representing a profile.
+///
public abstract class ProfileModel
{
+ ///
+ /// Gets or sets the ID of the profile.
+ ///
[JsonProperty("id")]
public Guid Guid { get; set; } = Guid.Empty;
+ ///
+ /// Gets or sets the name of the profile.
+ ///
[JsonProperty("n")]
public string Name { get; set; } = "New Profile";
+ ///
+ /// Deserialize a profile into a model.
+ ///
+ /// The string to decompress.
+ /// The parsed model.
+ /// Thrown when the parsed string is not a valid profile.
public static ProfileModel? Deserialize(string model)
{
var json = Util.DecompressString(Convert.FromBase64String(model.Substring(3)));
@@ -23,6 +38,11 @@ public abstract class ProfileModel
throw new ArgumentException("Was not a compressed style model.");
}
+ ///
+ /// Serialize this model into a string usable for sharing.
+ ///
+ /// The serialized representation of the model.
+ /// Thrown when an unsupported model is serialized.
public string Serialize()
{
string prefix;
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
index cae585c30..2a851d234 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs
@@ -1,27 +1,55 @@
using System.Collections.Generic;
+
using Newtonsoft.Json;
namespace Dalamud.Plugin.Internal.Profiles;
+///
+/// Version 1 of the profile model.
+///
public class ProfileModelV1 : ProfileModel
{
+ ///
+ /// Gets the prefix of this version.
+ ///
public static string SerializedPrefix => "DP1";
+ ///
+ /// Gets or sets a value indicating whether or not this profile should always be enabled at boot.
+ ///
[JsonProperty("b")]
public bool AlwaysEnableOnBoot { get; set; } = false;
+ ///
+ /// Gets or sets a value indicating whether or not this profile is currently enabled.
+ ///
[JsonProperty("e")]
public bool IsEnabled { get; set; } = false;
+ ///
+ /// Gets or sets a value indicating this profile's color.
+ ///
[JsonProperty("c")]
public uint Color { get; set; }
+ ///
+ /// Gets or sets the list of plugins in this profile.
+ ///
public List Plugins { get; set; } = new();
+ ///
+ /// Class representing a single plugin in a profile.
+ ///
public class ProfileModelV1Plugin
{
- public string InternalName { get; set; }
+ ///
+ /// Gets or sets the internal name of the plugin.
+ ///
+ public string? InternalName { get; set; }
+ ///
+ /// Gets or sets a value indicating whether or not this entry is enabled.
+ ///
public bool IsEnabled { get; set; }
}
}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs b/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
index 21856e572..0a6f5140b 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfilePluginEntry.cs
@@ -1,14 +1,28 @@
namespace Dalamud.Plugin.Internal.Profiles;
+///
+/// Class representing a single plugin in a profile.
+///
internal class ProfilePluginEntry
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The internal name of the plugin.
+ /// A value indicating whether or not this entry is enabled.
public ProfilePluginEntry(string internalName, bool state)
{
this.InternalName = internalName;
this.IsEnabled = state;
}
+ ///
+ /// Gets the internal name of the plugin.
+ ///
public string InternalName { get; }
+ ///
+ /// Gets a value indicating whether or not this entry is enabled.
+ ///
public bool IsEnabled { get; }
}
diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
index 7811939b4..062fc94f1 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
@@ -136,7 +136,9 @@ internal class LocalPlugin : IDisposable
this.disabledFile = LocalPluginManifest.GetDisabledFile(this.DllFile);
if (this.disabledFile.Exists)
{
+#pragma warning disable CS0618
this.Manifest.Disabled = true;
+#pragma warning restore CS0618
this.disabledFile.Delete();
}
@@ -627,9 +629,9 @@ internal class LocalPlugin : IDisposable
var manifest = LocalPluginManifest.GetManifestFile(this.DllFile);
if (manifest.Exists)
{
- //var isDisabled = this.IsDisabled; // saving the internal state because it could have been deleted
+ // var isDisabled = this.IsDisabled; // saving the internal state because it could have been deleted
this.Manifest = LocalPluginManifest.Load(manifest);
- //this.Manifest.Disabled = isDisabled;
+ // this.Manifest.Disabled = isDisabled;
this.SaveManifest();
}
From a913192765d567337a0e9e2a71f38dec16763be4 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:21:28 +0200
Subject: [PATCH 60/68] profile => collection
---
.../PluginInstaller/ProfileManagerWidget.cs | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
index 60f605f8e..4f92cebb8 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs
@@ -450,25 +450,25 @@ internal class ProfileManagerWidget
private static class Locs
{
public static string TooltipEnableDisable =>
- Loc.Localize("ProfileManagerEnableDisableHint", "Enable/Disable this profile");
+ Loc.Localize("ProfileManagerEnableDisableHint", "Enable/Disable this collection");
public static string InstallPlugin => Loc.Localize("ProfileManagerInstall", "Install this plugin");
public static string RemovePlugin =>
- Loc.Localize("ProfileManagerRemoveFromProfile", "Remove plugin from this profile");
+ Loc.Localize("ProfileManagerRemoveFromProfile", "Remove plugin from this collection");
public static string AddPlugin => Loc.Localize("ProfileManagerAddPlugin", "Add a plugin!");
public static string NoPluginsInProfile =>
- Loc.Localize("ProfileManagerNoPluginsInProfile", "Profile has no plugins!");
+ Loc.Localize("ProfileManagerNoPluginsInProfile", "Collection has no plugins!");
public static string AlwaysEnableAtBoot =>
Loc.Localize("ProfileManagerAlwaysEnableAtBoot", "Always enable when game starts");
- public static string DeleteProfileHint => Loc.Localize("ProfileManagerDeleteProfile", "Delete this profile");
+ public static string DeleteProfileHint => Loc.Localize("ProfileManagerDeleteProfile", "Delete this collection");
public static string CopyToClipboardHint =>
- Loc.Localize("ProfileManagerCopyToClipboard", "Copy profile to clipboard for sharing");
+ Loc.Localize("ProfileManagerCopyToClipboard", "Copy collection to clipboard for sharing");
public static string CopyToClipboardNotification =>
Loc.Localize("ProfileManagerCopyToClipboardHint", "Copied to clipboard!");
@@ -477,22 +477,22 @@ internal class ProfileManagerWidget
public static string SearchHint => Loc.Localize("ProfileManagerSearchHint", "Search...");
- public static string AddProfileHint => Loc.Localize("ProfileManagerAddProfileHint", "No profiles! Add one!");
+ public static string AddProfileHint => Loc.Localize("ProfileManagerAddProfileHint", "No collections! Add one!");
- public static string CloneProfileHint => Loc.Localize("ProfileManagerCloneProfile", "Clone this profile");
+ public static string CloneProfileHint => Loc.Localize("ProfileManagerCloneProfile", "Clone this collection");
- public static string EditProfileHint => Loc.Localize("ProfileManagerEditProfile", "Edit this profile");
+ public static string EditProfileHint => Loc.Localize("ProfileManagerEditProfile", "Edit this collection");
public static string ImportProfileHint =>
- Loc.Localize("ProfileManagerImportProfile", "Import a shared profile from your clipboard");
+ Loc.Localize("ProfileManagerImportProfile", "Import a shared collection from your clipboard");
- public static string AddProfile => Loc.Localize("ProfileManagerAddProfile", "Add a new profile");
+ public static string AddProfile => Loc.Localize("ProfileManagerAddProfile", "Add a new collection");
public static string NotificationImportSuccess =>
- Loc.Localize("ProfileManagerNotificationImportSuccess", "Profile successfully imported!");
+ Loc.Localize("ProfileManagerNotificationImportSuccess", "Collection successfully imported!");
public static string NotificationImportError =>
- Loc.Localize("ProfileManagerNotificationImportError", "Could not import profile.");
+ Loc.Localize("ProfileManagerNotificationImportError", "Could not import collection.");
public static string ErrorCouldNotRemove =>
Loc.Localize("ProfileManagerCouldNotRemove", "Could not remove plugin.");
From 26ee5371a55833341fc8f1774de80678a5e37403 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:26:13 +0200
Subject: [PATCH 61/68] use correct cat + group idx to hide search bar
---
.../Windows/PluginInstaller/PluginInstallerWindow.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
index d28401e73..fb7714098 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -522,8 +522,11 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - (style.ItemSpacing.X * 2) - searchInputWidth - searchClearButtonWidth);
+ var isProfileManager =
+ this.categoryManager.CurrentGroupIdx == 1 && this.categoryManager.CurrentCategoryIdx == 2;
+
// Disable search if profile editor
- using (ImRaii.Disabled(this.categoryManager.CurrentCategoryIdx == 14))
+ using (ImRaii.Disabled(isProfileManager))
{
var searchTextChanged = false;
ImGui.SetNextItemWidth(searchInputWidth);
@@ -548,7 +551,7 @@ internal class PluginInstallerWindow : Window, IDisposable
}
// Disable sort if changelogs or profile editor
- using (ImRaii.Disabled(this.categoryManager.CurrentGroupIdx == 3 || this.categoryManager.CurrentCategoryIdx == 14))
+ using (ImRaii.Disabled(this.categoryManager.CurrentGroupIdx == 3 || isProfileManager))
{
ImGui.SameLine();
ImGui.SetCursorPosY(downShift);
From bcdbe06e3f80adbeec25c11bb3e39cab1b3a3295 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:31:34 +0200
Subject: [PATCH 62/68] disable profiles by default
---
Dalamud/Configuration/Internal/DalamudConfiguration.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index 0d29b8cb7..cb33e7070 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -285,7 +285,7 @@ internal sealed class DalamudConfiguration : IServiceType
///
/// Gets or sets a value indicating whether or not profiles are enabled.
///
- public bool ProfilesEnabled { get; set; } = true;
+ public bool ProfilesEnabled { get; set; } = false;
///
/// Gets or sets a value indicating whether or not Dalamud RMT filtering should be disabled.
From 66026bbc3e48df971bd5a472f1fef4f89bf37d8a Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:33:22 +0200
Subject: [PATCH 63/68] one more profile => collection
---
Dalamud/Plugin/Internal/Profiles/ProfileManager.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index f7eac6d21..1b2c16088 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -111,7 +111,7 @@ internal class ProfileManager : IServiceType
var model = new ProfileModelV1
{
Guid = Guid.NewGuid(),
- Name = this.GenerateUniqueProfileName(Loc.Localize("PluginProfilesNewProfile", "New Profile")),
+ Name = this.GenerateUniqueProfileName(Loc.Localize("PluginProfilesNewProfile", "New Collection")),
IsEnabled = false,
};
From 3d057c4e35714d1d6dea41a03030759ad27025d2 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:34:16 +0200
Subject: [PATCH 64/68] one one more profile => collection
---
Dalamud/Plugin/Internal/Profiles/ProfileModel.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
index c73e80450..80c47db78 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
@@ -20,7 +20,7 @@ public abstract class ProfileModel
/// Gets or sets the name of the profile.
///
[JsonProperty("n")]
- public string Name { get; set; } = "New Profile";
+ public string Name { get; set; } = "New Collection";
///
/// Deserialize a profile into a model.
From 6dd7188f6cdcdf82c85efaf08c455f7018d31180 Mon Sep 17 00:00:00 2001
From: goat
Date: Mon, 12 Jun 2023 20:44:28 +0200
Subject: [PATCH 65/68] even more profile => collection
---
.../Settings/Tabs/SettingsTabExperimental.cs | 4 ++--
.../Profiles/ProfileCommandHandler.cs | 22 +++++++++----------
.../Internal/Profiles/ProfileManager.cs | 2 +-
.../Plugin/Internal/Profiles/ProfileModel.cs | 2 +-
4 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
index 6d5c03137..62981f4a2 100644
--- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
+++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs
@@ -50,8 +50,8 @@ public class SettingsTabExperimental : SettingsTab
new GapSettingsEntry(5, true),
new SettingsEntry(
- Loc.Localize("DalamudSettingsEnableProfiles", "Enable plugin profiles"),
- Loc.Localize("DalamudSettingsEnableProfilesHint", "EXPERIMENTAL: Enables plugin profiles."),
+ Loc.Localize("DalamudSettingsEnableProfiles", "Enable plugin collections"),
+ Loc.Localize("DalamudSettingsEnableProfilesHint", "Enables plugin collections, which lets you create toggleable lists of plugins."),
c => c.ProfilesEnabled,
(v, c) => c.ProfilesEnabled = v),
};
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
index aa72200a1..81e63e0bc 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileCommandHandler.cs
@@ -42,19 +42,19 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
this.cmd.AddHandler("/xlenableprofile", new CommandInfo(this.OnEnableProfile)
{
- HelpMessage = Loc.Localize("ProfileCommandsEnableHint", "Enable a profile. Usage: /xlenableprofile \"Profile Name\""),
+ HelpMessage = Loc.Localize("ProfileCommandsEnableHint", "Enable a collection. Usage: /xlenablecollection \"Collection Name\""),
ShowInHelp = true,
});
this.cmd.AddHandler("/xldisableprofile", new CommandInfo(this.OnDisableProfile)
{
- HelpMessage = Loc.Localize("ProfileCommandsDisableHint", "Disable a profile. Usage: /xldisableprofile \"Profile Name\""),
+ HelpMessage = Loc.Localize("ProfileCommandsDisableHint", "Disable a collection. Usage: /xldisablecollection \"Collection Name\""),
ShowInHelp = true,
});
this.cmd.AddHandler("/xltoggleprofile", new CommandInfo(this.OnToggleProfile)
{
- HelpMessage = Loc.Localize("ProfileCommandsToggleHint", "Toggle a profile. Usage: /xltoggleprofile \"Profile Name\""),
+ HelpMessage = Loc.Localize("ProfileCommandsToggleHint", "Toggle a collection. Usage: /xltogglecollection \"Collection Name\""),
ShowInHelp = true,
});
@@ -71,9 +71,9 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
///
public void Dispose()
{
- this.cmd.RemoveHandler("/xlenableprofile");
- this.cmd.RemoveHandler("/xldisableprofile");
- this.cmd.RemoveHandler("/xltoggleprofile");
+ this.cmd.RemoveHandler("/xlenablecollection");
+ this.cmd.RemoveHandler("/xldisablecollection");
+ this.cmd.RemoveHandler("/xltogglecollection");
this.framework.Update += this.FrameworkOnUpdate;
}
@@ -111,11 +111,11 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
if (profile.IsEnabled)
{
- this.chat.Print(Loc.Localize("ProfileCommandsEnabling", "Enabling profile \"{0}\"...").Format(profile.Name));
+ this.chat.Print(Loc.Localize("ProfileCommandsEnabling", "Enabling collection \"{0}\"...").Format(profile.Name));
}
else
{
- this.chat.Print(Loc.Localize("ProfileCommandsDisabling", "Disabling profile \"{0}\"...").Format(profile.Name));
+ this.chat.Print(Loc.Localize("ProfileCommandsDisabling", "Disabling collection \"{0}\"...").Format(profile.Name));
}
Task.Run(() => this.profileManager.ApplyAllWantStates()).ContinueWith(t =>
@@ -123,11 +123,11 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
if (!t.IsCompletedSuccessfully && t.Exception != null)
{
Log.Error(t.Exception, "Could not apply profiles through commands");
- this.chat.PrintError(Loc.Localize("ProfileCommandsApplyFailed", "Failed to apply all of your profiles. Please check the console for errors."));
+ this.chat.PrintError(Loc.Localize("ProfileCommandsApplyFailed", "Failed to apply your collections. Please check the console for errors."));
}
else
{
- this.chat.Print(Loc.Localize("ProfileCommandsApplySuccess", "Profiles applied."));
+ this.chat.Print(Loc.Localize("ProfileCommandsApplySuccess", "Collections applied."));
}
});
}
@@ -167,7 +167,7 @@ internal class ProfileCommandHandler : IServiceType, IDisposable
var name = arguments.Replace("\"", string.Empty);
if (this.profileManager.Profiles.All(x => x.Name != name))
{
- this.chat.PrintError($"No profile like \"{name}\".");
+ this.chat.PrintError($"No collection like \"{name}\".");
return null;
}
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
index 1b2c16088..d91db1283 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs
@@ -150,7 +150,7 @@ internal class ProfileManager : IServiceType
return null;
newModel.Guid = Guid.NewGuid();
- newModel.Name = this.GenerateUniqueProfileName(newModel.Name.IsNullOrEmpty() ? "Unknown Profile" : newModel.Name);
+ newModel.Name = this.GenerateUniqueProfileName(newModel.Name.IsNullOrEmpty() ? "Unknown Collection" : newModel.Name);
if (newModel is ProfileModelV1 modelV1)
modelV1.IsEnabled = false;
diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
index 80c47db78..bf2a9c2c9 100644
--- a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
+++ b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs
@@ -35,7 +35,7 @@ public abstract class ProfileModel
if (model.StartsWith(ProfileModelV1.SerializedPrefix))
return JsonConvert.DeserializeObject(json);
- throw new ArgumentException("Was not a compressed style model.");
+ throw new ArgumentException("Was not a compressed profile.");
}
///
From 7724828ff0297d6f0d33869ba536bd94c41a4abc Mon Sep 17 00:00:00 2001
From: bleatbot <106497096+bleatbot@users.noreply.github.com>
Date: Tue, 13 Jun 2023 18:25:15 +0200
Subject: [PATCH 66/68] Update ClientStructs (#1245)
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 3595efd84..e61e4d8fa 160000
--- a/lib/FFXIVClientStructs
+++ b/lib/FFXIVClientStructs
@@ -1 +1 @@
-Subproject commit 3595efd84f467e2c388f6e6b006a4e0764c46fdf
+Subproject commit e61e4d8fa9e58e37b454314f0f9a9f305f173483
From 59ba5a1d3c1d5988dbb6ce6b5c8139d876901fa8 Mon Sep 17 00:00:00 2001
From: Cara
Date: Wed, 14 Jun 2023 01:55:37 +0930
Subject: [PATCH 67/68] Add new Bitmap Font Icons (#1248)
---
.../Text/SeStringHandling/BitmapFontIcon.cs | 255 ++++++++++++++++++
1 file changed, 255 insertions(+)
diff --git a/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs b/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs
index 64af2a2a9..ea181238a 100644
--- a/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs
+++ b/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs
@@ -459,4 +459,259 @@ public enum BitmapFontIcon : uint
/// The Island Sanctuary icon.
///
IslandSanctuary = 116,
+
+ ///
+ /// The Physical Damage icon.
+ ///
+ DamagePhysical = 117,
+
+ ///
+ /// The Magical Damage icon.
+ ///
+ DamageMagical = 118,
+
+ ///
+ /// The Special Damage icon.
+ ///
+ DamageSpecial = 119,
+
+ ///
+ /// A gold star icon with an orange exclamation mark.
+ ///
+ GoldStarProblem = 120,
+
+ ///
+ /// A blue star icon.
+ ///
+ BlueStar = 121,
+
+ ///
+ /// A blue star icon with an orange exclamation mark.
+ ///
+ BlueStarProblem = 121,
+
+ ///
+ /// The Disconnecting icon.
+ ///
+ Disconnecting = 124,
+
+ ///
+ /// The Do Not Disturb icon.
+ ///
+ DoNotDisturb = 125,
+
+ ///
+ /// The Meteor icon.
+ ///
+ Meteor = 126,
+
+ ///
+ /// The Role Playing icon.
+ ///
+ RolePlaying = 127,
+
+ ///
+ /// The Gladiator icon.
+ ///
+ Gladiator = 128,
+
+ ///
+ /// The Pugilist icon.
+ ///
+ Pugilist = 129,
+
+ ///
+ /// The Marauder icon.
+ ///
+ Marauder = 130,
+
+ ///
+ /// The Lancer icon.
+ ///
+ Lancer = 131,
+
+ ///
+ /// The Archer icon.
+ ///
+ Archer = 132,
+
+ ///
+ /// The Conjurer icon.
+ ///
+ Conjurer = 133,
+
+ ///
+ /// The Thaumaturge icon.
+ ///
+ Thaumaturge = 134,
+
+ ///
+ /// The Carpenter icon.
+ ///
+ Carpenter = 135,
+
+ ///
+ /// The Blacksmith icon.
+ ///
+ Blacksmith = 136,
+
+ ///
+ /// The Armorer icon.
+ ///
+ Armorer = 137,
+
+ ///
+ /// The Goldsmith icon.
+ ///
+ Goldsmith = 138,
+
+ ///
+ /// The Leatherworker icon.
+ ///
+ Leatherworker = 139,
+
+ ///
+ /// The Weaver icon.
+ ///
+ Weaver = 140,
+
+ ///
+ /// The Alchemist icon.
+ ///
+ Alchemist = 131,
+
+ ///
+ /// The Culinarian icon.
+ ///
+ Culinarian = 132,
+
+ ///
+ /// The Miner icon.
+ ///
+ Miner = 143,
+
+ ///
+ /// The Botanist icon.
+ ///
+ Botanist = 144,
+
+ ///
+ /// The Fisher icon.
+ ///
+ Fisher = 145,
+
+ ///
+ /// The Paladin icon.
+ ///
+ Paladin = 146,
+
+ ///
+ /// The Monk icon.
+ ///
+ Monk = 147,
+
+ ///
+ /// The Warrior icon.
+ ///
+ Warrior = 148,
+
+ ///
+ /// The Dragoon icon.
+ ///
+ Dragoon = 149,
+
+ ///
+ /// The Bard icon.
+ ///
+ Bard = 150,
+
+ ///
+ /// The White Mage icon.
+ ///
+ WhiteMage = 151,
+
+ ///
+ /// The Black Mage icon.
+ ///
+ BlackMage = 152,
+
+ ///
+ /// The Arcanist icon.
+ ///
+ Arcanist = 153,
+
+ ///
+ /// The Summoner icon.
+ ///
+ Summoner = 154,
+
+ ///
+ /// The Scholar icon.
+ ///
+ Scholar = 155,
+
+ ///
+ /// The Rogue icon.
+ ///
+ Rogue = 156,
+
+ ///
+ /// The Ninja icon.
+ ///
+ Ninja = 157,
+
+ ///
+ /// The Machinist icon.
+ ///
+ Machinist = 158,
+
+ ///
+ /// The Dark Knight icon.
+ ///
+ DarkKnight = 159,
+
+ ///
+ /// The Astrologian icon.
+ ///
+ Astrologian = 160,
+
+ ///
+ /// The Samurai icon.
+ ///
+ Samurai = 161,
+
+ ///
+ /// The Red Mage icon.
+ ///
+ RedMage = 162,
+
+ ///
+ /// The Blue Mage icon.
+ ///
+ BlueMage = 163,
+
+ ///
+ /// The Gunbreaker icon.
+ ///
+ Gunbreaker = 164,
+
+ ///
+ /// The Dancer icon.
+ ///
+ Dancer = 165,
+
+ ///
+ /// The Reaper icon.
+ ///
+ Reaper = 166,
+
+ ///
+ /// The Sage icon.
+ ///
+ Sage = 167,
+
+ ///
+ /// The Waiting For Duty Finder icon.
+ ///
+ WaitingForDutyFinder = 168,
}
From 284001ce6b1c7f4a72fab6494ea8eb2cb5c40d77 Mon Sep 17 00:00:00 2001
From: goat <16760685+goaaats@users.noreply.github.com>
Date: Tue, 13 Jun 2023 20:10:47 +0200
Subject: [PATCH 68/68] Interface support for plugin DI (#1235)
* feat: interface support for plugin DI
* attribute to indicate resolvability should be on the service instead of the interface
---
.../ClientState/Aetherytes/AetheryteList.cs | 16 +++++--------
Dalamud/IoC/Internal/ResolveViaAttribute.cs | 21 +++++++++++++++++
Dalamud/IoC/Internal/ServiceContainer.cs | 20 +++++++++++++++-
Dalamud/Plugin/Services/IAetheryteList.cs | 23 +++++++++++++++++++
4 files changed, 69 insertions(+), 11 deletions(-)
create mode 100644 Dalamud/IoC/Internal/ResolveViaAttribute.cs
create mode 100644 Dalamud/Plugin/Services/IAetheryteList.cs
diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
index 9f8a62faf..bbd94e505 100644
--- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
+++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
+using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Serilog;
@@ -14,7 +15,8 @@ namespace Dalamud.Game.ClientState.Aetherytes;
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
-public sealed unsafe partial class AetheryteList : IServiceType
+[ResolveVia]
+public sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList
{
[ServiceManager.ServiceDependency]
private readonly ClientState clientState = Service.Get();
@@ -27,9 +29,7 @@ public sealed unsafe partial class AetheryteList : IServiceType
Log.Verbose($"Teleport address 0x{((nint)this.telepoInstance).ToInt64():X}");
}
- ///
- /// Gets the amount of Aetherytes the local player has unlocked.
- ///
+ ///
public int Length
{
get
@@ -46,11 +46,7 @@ public sealed unsafe partial class AetheryteList : IServiceType
}
}
- ///
- /// Gets a Aetheryte Entry at the specified index.
- ///
- /// Index.
- /// A at the specified index.
+ ///
public AetheryteEntry? this[int index]
{
get
@@ -80,7 +76,7 @@ public sealed unsafe partial class AetheryteList : IServiceType
///
/// This collection represents the list of available Aetherytes in the Teleport window.
///
-public sealed partial class AetheryteList : IReadOnlyCollection
+public sealed partial class AetheryteList
{
///
public int Count => this.Length;
diff --git a/Dalamud/IoC/Internal/ResolveViaAttribute.cs b/Dalamud/IoC/Internal/ResolveViaAttribute.cs
new file mode 100644
index 000000000..002878525
--- /dev/null
+++ b/Dalamud/IoC/Internal/ResolveViaAttribute.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Dalamud.IoC.Internal;
+
+///
+/// Indicates that an interface a service can implement can be used to resolve that service.
+/// Take care: only one service can implement an interface with this attribute at a time.
+///
+/// The interface that can be used to resolve the service.
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+internal class ResolveViaAttribute : ResolveViaAttribute
+{
+}
+
+///
+/// Helper class used for matching. Use the generic version.
+///
+[AttributeUsage(AttributeTargets.Class)]
+internal class ResolveViaAttribute : Attribute
+{
+}
diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs
index 18d294a3e..feac634f3 100644
--- a/Dalamud/IoC/Internal/ServiceContainer.cs
+++ b/Dalamud/IoC/Internal/ServiceContainer.cs
@@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Dalamud.Logging.Internal;
-using Dalamud.Plugin.Internal.Types;
namespace Dalamud.IoC.Internal;
@@ -18,6 +18,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
private static readonly ModuleLog Log = new("SERVICECONTAINER");
private readonly Dictionary instances = new();
+ private readonly Dictionary interfaceToTypeMap = new();
///
/// Initializes a new instance of the class.
@@ -39,6 +40,20 @@ internal class ServiceContainer : IServiceProvider, IServiceType
}
this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T));
+
+ var resolveViaTypes = typeof(T)
+ .GetCustomAttributes()
+ .OfType()
+ .Select(x => x.GetType().GetGenericArguments().First());
+ foreach (var resolvableType in resolveViaTypes)
+ {
+ Log.Verbose("=> {InterfaceName} provides for {TName}", resolvableType.FullName ?? "???", typeof(T).FullName ?? "???");
+
+ Debug.Assert(!this.interfaceToTypeMap.ContainsKey(resolvableType), "A service already implements this interface, this is not allowed");
+ Debug.Assert(typeof(T).IsAssignableTo(resolvableType), "Service does not inherit from indicated ResolveVia type");
+
+ this.interfaceToTypeMap[resolvableType] = typeof(T);
+ }
}
///
@@ -233,6 +248,9 @@ internal class ServiceContainer : IServiceProvider, IServiceType
private async Task