diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs index db490315f..5b466cba2 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs @@ -20,7 +20,6 @@ public class FileDialogManager /// Additional quick access items for the sidebar. public readonly List<(string Name, string Path, FontAwesomeIcon Icon, int Position)> CustomSideBarItems = []; - /// Additional flags with which to draw the window. public ImGuiWindowFlags AddedWindowFlags = ImGuiWindowFlags.None; #pragma warning restore SA1401 diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 83adfa524..cfcad2ff4 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -228,6 +228,7 @@ internal class PluginInstallerWindow : Window, IDisposable IsInstallableOutdated = 1 << 5, IsOrphan = 1 << 6, IsTesting = 1 << 7, + IsIncompatible = 1 << 8, } private enum InstalledPluginListFilter @@ -2193,7 +2194,7 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleVar(ImGuiStyleVar.Alpha, overlayAlpha); if (flags.HasFlag(PluginHeaderFlags.UpdateAvailable)) ImGui.Image(this.imageCache.UpdateIcon.ImGuiHandle, iconSize); - else if ((flags.HasFlag(PluginHeaderFlags.HasTrouble) && !pluginDisabled) || flags.HasFlag(PluginHeaderFlags.IsOrphan)) + else if ((flags.HasFlag(PluginHeaderFlags.HasTrouble) && !pluginDisabled) || flags.HasFlag(PluginHeaderFlags.IsOrphan) || flags.HasFlag(PluginHeaderFlags.IsIncompatible)) ImGui.Image(this.imageCache.TroubleIcon.ImGuiHandle, iconSize); else if (flags.HasFlag(PluginHeaderFlags.IsInstallableOutdated)) ImGui.Image(this.imageCache.OutdatedInstallableIcon.ImGuiHandle, iconSize); @@ -2269,9 +2270,14 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.SetCursorPos(cursor); // Outdated warning - if (plugin is { IsOutdated: true, IsBanned: false } || flags.HasFlag(PluginHeaderFlags.IsInstallableOutdated)) + if (flags.HasFlag(PluginHeaderFlags.IsIncompatible)) { - ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.TextWrapped(Locs.PluginBody_Incompatible); + } + else if (plugin is { IsOutdated: true, IsBanned: false } || flags.HasFlag(PluginHeaderFlags.IsInstallableOutdated)) + { + using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed); var bodyText = Locs.PluginBody_Outdated + " "; if (flags.HasFlag(PluginHeaderFlags.UpdateAvailable)) @@ -2280,7 +2286,6 @@ internal class PluginInstallerWindow : Window, IDisposable bodyText += Locs.PluginBody_Outdated_WaitForUpdate; ImGui.TextWrapped(bodyText); - ImGui.PopStyleColor(); } else if (plugin is { IsBanned: true }) { @@ -2449,6 +2454,14 @@ internal class PluginInstallerWindow : Window, IDisposable var effectiveApiLevel = useTesting && manifest.TestingDalamudApiLevel != null ? manifest.TestingDalamudApiLevel.Value : manifest.DalamudApiLevel; var isOutdated = effectiveApiLevel < PluginManager.DalamudApiLevel; + var isIncompatible = manifest.MinimumDalamudVersion != null && + manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed; + + var enableInstallButton = this.updateStatus != OperationStatus.InProgress && + this.installStatus != OperationStatus.InProgress && + !isOutdated && + !isIncompatible; + // Check for valid versions if ((useTesting && manifest.TestingAssemblyVersion == null) || manifest.AssemblyVersion == null) { @@ -2473,6 +2486,11 @@ internal class PluginInstallerWindow : Window, IDisposable label += Locs.PluginTitleMod_TestingAvailable; } + if (isIncompatible) + { + label += Locs.PluginTitleMod_Incompatible; + } + var isThirdParty = manifest.SourceRepo.IsThirdParty; ImGui.PushID($"available{index}{manifest.InternalName}"); @@ -2486,6 +2504,8 @@ internal class PluginInstallerWindow : Window, IDisposable flags |= PluginHeaderFlags.IsInstallableOutdated; if (useTesting || manifest.IsTestingExclusive) flags |= PluginHeaderFlags.IsTesting; + if (isIncompatible) + flags |= PluginHeaderFlags.IsIncompatible; if (this.DrawPluginCollapsingHeader(label, null, manifest, flags, () => this.DrawAvailablePluginContextMenu(manifest), index)) { @@ -2513,9 +2533,6 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.ScaledDummy(5); - // Controls - var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress || isOutdated; - var versionString = useTesting ? $"{manifest.TestingAssemblyVersion}" : $"{manifest.AssemblyVersion}"; @@ -2524,7 +2541,7 @@ internal class PluginInstallerWindow : Window, IDisposable { ImGuiComponents.DisabledButton(Locs.PluginButton_SafeMode); } - else if (disabled) + else if (!enableInstallButton) { ImGuiComponents.DisabledButton(Locs.PluginButton_InstallVersion(versionString)); } @@ -4105,6 +4122,8 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginTitleMod_TestingAvailable => Loc.Localize("InstallerTestingAvailable", " (has testing version)"); + public static string PluginTitleMod_Incompatible => Loc.Localize("InstallerTitleModIncompatible", " (incompatible)"); + public static string PluginTitleMod_DevPlugin => Loc.Localize("InstallerDevPlugin", " (dev plugin)"); public static string PluginTitleMod_UpdateFailed => Loc.Localize("InstallerUpdateFailed", " (update failed)"); @@ -4161,6 +4180,8 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginBody_Outdated => Loc.Localize("InstallerOutdatedPluginBody ", "This plugin is outdated and incompatible."); + public static string PluginBody_Incompatible => Loc.Localize("InstallerIncompatiblePluginBody ", "This plugin is incompatible with your version of Dalamud. Please attempt to restart your game."); + public static string PluginBody_Outdated_WaitForUpdate => Loc.Localize("InstallerOutdatedWaitForUpdate", "Please wait for it to be updated by its author."); public static string PluginBody_Outdated_CanNowUpdate => Loc.Localize("InstallerOutdatedCanNowUpdate", "An update is available for installation."); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index e8283dd8f..fdcba0162 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -1760,6 +1760,7 @@ internal class PluginManager : IInternalDisposableService var updates = this.AvailablePlugins .Where(remoteManifest => plugin.Manifest.InternalName == remoteManifest.InternalName) .Where(remoteManifest => plugin.Manifest.InstalledFromUrl == remoteManifest.SourceRepo.PluginMasterUrl || !remoteManifest.SourceRepo.IsThirdParty) + .Where(remoteManifest => remoteManifest.MinimumDalamudVersion == null || Util.AssemblyVersionParsed >= remoteManifest.MinimumDalamudVersion) .Where(remoteManifest => { var useTesting = this.UseTesting(remoteManifest); diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 6478a5bb5..e05cbc190 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -15,6 +15,7 @@ using Dalamud.Plugin.Internal.Exceptions; using Dalamud.Plugin.Internal.Loader; using Dalamud.Plugin.Internal.Profiles; using Dalamud.Plugin.Internal.Types.Manifest; +using Dalamud.Utility; namespace Dalamud.Plugin.Internal.Types; @@ -313,6 +314,9 @@ internal class LocalPlugin : IAsyncDisposable if (!this.CheckPolicy()) throw new PluginPreconditionFailedException($"Unable to load {this.Name} as a load policy forbids it"); + if (this.Manifest.MinimumDalamudVersion != null && this.Manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed) + throw new PluginPreconditionFailedException($"Unable to load {this.Name}, Dalamud version is lower than minimum required version {this.Manifest.MinimumDalamudVersion}"); + this.State = PluginState.Loading; Log.Information($"Loading {this.DllFile.Name}"); diff --git a/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs index 4b0951397..5ab5abbc1 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs @@ -16,7 +16,7 @@ public interface IPluginManifest /// Gets the public name of the plugin. /// public string Name { get; } - + /// /// Gets a punchline of the plugins functions. /// @@ -26,7 +26,7 @@ public interface IPluginManifest /// Gets the author/s of the plugin. /// public string Author { get; } - + /// /// Gets a value indicating whether the plugin can be unloaded asynchronously. /// @@ -41,17 +41,22 @@ public interface IPluginManifest /// Gets the assembly version of the plugin's testing variant. /// public Version? TestingAssemblyVersion { get; } - + + /// + /// Gets the minimum Dalamud assembly version this plugin requires. + /// + public Version? MinimumDalamudVersion { get; } + /// /// Gets the DIP17 channel name. /// public string? Dip17Channel { get; } - + /// /// Gets the last time this plugin was updated. /// public long LastUpdate { get; } - + /// /// Gets a changelog, null if none exists. /// @@ -88,7 +93,7 @@ public interface IPluginManifest /// Gets an URL to the website or source code of the plugin. /// public string? RepoUrl { get; } - + /// /// Gets a description of the plugins functions. /// diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs index c9c231b4d..57001d63b 100644 --- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs @@ -75,6 +75,10 @@ internal record PluginManifest : IPluginManifest [JsonConverter(typeof(GameVersionConverter))] public GameVersion? ApplicableVersion { get; init; } = GameVersion.Any; + /// + [JsonProperty] + public Version? MinimumDalamudVersion { get; init; } + /// [JsonProperty] public int DalamudApiLevel { get; init; } = PluginManager.DalamudApiLevel; diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 4959fafb2..1d96523be 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -70,10 +70,17 @@ public static class Util private static ulong moduleEndAddr; /// - /// Gets the assembly version of Dalamud. + /// Gets the Dalamud version. /// + [Api13ToDo("Remove. Make both versions here internal. Add an API somewhere.")] public static string AssemblyVersion { get; } = - Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString(); + Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!.ToString(); + + /// + /// Gets the Dalamud version. + /// + internal static Version AssemblyVersionParsed { get; } = + Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!; /// /// Gets the SCM Version from the assembly, or null if it cannot be found. This method will generally return