diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 1b2a43c17..9ec6712aa 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -337,6 +337,11 @@ namespace Dalamud.Configuration.Internal /// public string LastFeedbackContactDetails { get; set; } = string.Empty; + /// + /// Gets or sets a list of plugins that testing builds should be downloaded for. + /// + public List? PluginTestingOptIns { get; set; } + /// /// Load a configuration from the provided path. /// diff --git a/Dalamud/Configuration/Internal/PluginTestingOptIn.cs b/Dalamud/Configuration/Internal/PluginTestingOptIn.cs new file mode 100644 index 000000000..f40740cf2 --- /dev/null +++ b/Dalamud/Configuration/Internal/PluginTestingOptIn.cs @@ -0,0 +1,24 @@ +namespace Dalamud.Configuration.Internal; + +public record PluginTestingOptIn +{ + /// + /// Initializes a new instance of the class. + /// + /// The internal name of the plugin. + public PluginTestingOptIn(string internalName) + { + this.InternalName = internalName; + this.Branch = "testing-live"; // TODO: make these do something, needs work in plogon + } + + /// + /// Gets the internal name of the plugin to test. + /// + public string InternalName { get; private set; } + + /// + /// Gets the testing branch to use. + /// + public string Branch { get; private set; } +} diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs index a16314598..8f7e61e1f 100644 --- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs +++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs @@ -513,7 +513,7 @@ namespace Dalamud.Interface.Internal.Windows isThirdParty = true; } - var useTesting = PluginManager.UseTesting(manifest); + var useTesting = Service.Get().UseTesting(manifest); var url = this.GetPluginIconUrl(manifest, isThirdParty, useTesting); if (url.IsNullOrEmpty()) @@ -599,7 +599,7 @@ namespace Dalamud.Interface.Internal.Windows isThirdParty = true; } - var useTesting = PluginManager.UseTesting(manifest); + var useTesting = Service.Get().UseTesting(manifest); var urls = this.GetPluginImageUrls(manifest, isThirdParty, useTesting); urls = urls?.Where(x => !string.IsNullOrEmpty(x)).ToList(); if (urls?.Any() != true) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 1e0edd64f..40b44d217 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1637,7 +1637,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var notifications = Service.Get(); var pluginManager = Service.Get(); - var useTesting = PluginManager.UseTesting(manifest); + var useTesting = pluginManager.UseTesting(manifest); var wasSeen = this.WasPluginSeen(manifest.InternalName); var isOutdated = manifest.DalamudApiLevel < PluginManager.DalamudApiLevel; @@ -1653,7 +1653,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var label = manifest.Name; // Testing - if (useTesting) + if (useTesting || manifest.IsTestingExclusive) { label += Locs.PluginTitleMod_TestingVersion; } @@ -1710,7 +1710,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller this.installStatus = OperationStatus.InProgress; this.loadingIndicatorKind = LoadingIndicatorKind.Installing; - Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting, PluginLoadReason.Installer)) + 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 @@ -1803,6 +1803,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var configuration = Service.Get(); var commandManager = Service.Get(); + var testingOptIn = + configuration.PluginTestingOptIns?.FirstOrDefault(x => x.InternalName == plugin.Manifest.InternalName); var trouble = false; // Name @@ -1820,6 +1822,11 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller label += Locs.PluginTitleMod_TestingVersion; } + if (plugin.Manifest.IsAvailableForTesting && configuration.DoPluginTest && testingOptIn == null) + { + label += Locs.PluginTitleMod_TestingAvailable; + } + // Freshly installed if (showInstalled) { @@ -1904,7 +1911,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}"); var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty(); - if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.Manifest.IsThirdParty, trouble, availablePluginUpdate != default, false, false, () => this.DrawInstalledPluginContextMenu(plugin), index)) + if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.Manifest.IsThirdParty, trouble, availablePluginUpdate != default, false, false, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) { if (!this.WasPluginSeen(plugin.Manifest.InternalName)) configuration.SeenPluginInternalName.Add(plugin.Manifest.InternalName); @@ -2038,13 +2045,35 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller ImGui.PopStyleColor(2); } - private void DrawInstalledPluginContextMenu(LocalPlugin plugin) + private void DrawInstalledPluginContextMenu(LocalPlugin plugin, PluginTestingOptIn? optIn) { var pluginManager = Service.Get(); + var configuration = Service.Get(); if (ImGui.BeginPopupContextItem("InstalledItemContextMenu")) { - if (ImGui.Selectable(Locs.PluginContext_DeletePluginConfigReload)) + var repoManifest = this.pluginListAvailable.FirstOrDefault(x => x.InternalName == plugin.Manifest.InternalName); + if (repoManifest?.IsTestingExclusive == true) + ImGui.BeginDisabled(); + + if (ImGui.MenuItem(Locs.PluginContext_TestingOptIn, string.Empty, optIn != null)) + { + if (optIn != null) + { + configuration.PluginTestingOptIns!.Remove(optIn); + } + else + { + configuration.PluginTestingOptIns!.Add(new PluginTestingOptIn(plugin.Manifest.InternalName)); + } + + configuration.Save(); + } + + if (repoManifest?.IsTestingExclusive == true) + ImGui.EndDisabled(); + + if (ImGui.MenuItem(Locs.PluginContext_DeletePluginConfigReload)) { Log.Debug($"Deleting config for {plugin.Manifest.InternalName}"); @@ -2751,6 +2780,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginTitleMod_TestingVersion => Loc.Localize("InstallerTestingVersion", " (testing version)"); + public static string PluginTitleMod_TestingAvailable => Loc.Localize("InstallerTestingAvailable", " (available for testing)"); + public static string PluginTitleMod_DevPlugin => Loc.Localize("InstallerDevPlugin", " (dev plugin)"); public static string PluginTitleMod_UpdateFailed => Loc.Localize("InstallerUpdateFailed", " (update failed)"); @@ -2773,6 +2804,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller #region Plugin context menu + public static string PluginContext_TestingOptIn => Loc.Localize("InstallerTestingOptIn", "Receive plugin testing versions"); + public static string PluginContext_MarkAllSeen => Loc.Localize("InstallerMarkAllSeen", "Mark all as seen"); public static string PluginContext_HidePlugin => Loc.Localize("InstallerHidePlugin", "Hide from installer"); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 15f8d2250..69fde3a25 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -118,11 +118,13 @@ Thanks and have fun!"; throw new InvalidDataException("Couldn't deserialize banned plugins manifest."); } - this.openInstallerWindowPluginChangelogsLink = Service.Get().AddChatLinkHandler("Dalamud", 1003, (i, m) => + this.openInstallerWindowPluginChangelogsLink = Service.Get().AddChatLinkHandler("Dalamud", 1003, (_, _) => { Service.GetNullable()?.OpenPluginInstallerPluginChangelogs(); }); + this.configuration.PluginTestingOptIns ??= new List(); + this.ApplyPatches(); } @@ -186,6 +188,23 @@ Thanks and have fun!"; /// public bool LoadBannedPlugins { get; set; } + /// + /// Gets a value indicating whether the given repo manifest should be visible to the user. + /// + /// Repo manifest. + /// If the manifest is visible. + public static bool IsManifestVisible(RemotePluginManifest manifest) + { + var configuration = Service.Get(); + + // Hidden by user + if (configuration.HiddenPluginInternalName.Contains(manifest.InternalName)) + return false; + + // Hidden by manifest + return !manifest.IsHide; + } + /// /// Print to chat any plugin updates and whether they were successful. /// @@ -236,11 +255,12 @@ Thanks and have fun!"; /// /// Manifest to check. /// A value indicating whether testing should be used. - public static bool UseTesting(PluginManifest manifest) + public bool UseTesting(PluginManifest manifest) { - var configuration = Service.Get(); + if (!this.configuration.DoPluginTest) + return false; - if (!configuration.DoPluginTest) + if (this.configuration.PluginTestingOptIns!.All(x => x.InternalName != manifest.InternalName)) return false; if (manifest.IsTestingExclusive) @@ -258,25 +278,6 @@ Thanks and have fun!"; return false; } - /// - /// Gets a value indicating whether the given repo manifest should be visible to the user. - /// - /// Repo manifest. - /// If the manifest is visible. - public static bool IsManifestVisible(RemotePluginManifest manifest) - { - var configuration = Service.Get(); - - // Hidden by user - if (configuration.HiddenPluginInternalName.Contains(manifest.InternalName)) - return false; - - return true; // TODO temporary - - // Hidden by manifest - return !manifest.IsHide; - } - /// public void Dispose() { @@ -687,6 +688,13 @@ Thanks and have fun!"; { Log.Debug($"Installing plugin {repoManifest.Name} (testing={useTesting})"); + // Ensure that we have a testing opt-in for this plugin if we are installing a testing version + if (useTesting && this.configuration.PluginTestingOptIns!.All(x => x.InternalName != repoManifest.InternalName)) + { + this.configuration.PluginTestingOptIns.Add(new PluginTestingOptIn(repoManifest.InternalName)); + this.configuration.Save(); + } + var downloadUrl = useTesting ? repoManifest.DownloadLinkTesting : repoManifest.DownloadLinkInstall; var version = useTesting ? repoManifest.TestingAssemblyVersion : repoManifest.AssemblyVersion; @@ -961,6 +969,7 @@ Thanks and have fun!"; versionDir.Delete(true); continue; } + var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll")); if (!dllFile.Exists) { @@ -1008,6 +1017,7 @@ Thanks and have fun!"; /// /// Update all non-dev plugins. /// + /// Ignore disabled plugins. /// Perform a dry run, don't install anything. /// Success or failure and a list of updated plugin metadata. public async Task> UpdatePluginsAsync(bool ignoreDisabled, bool dryRun) @@ -1265,7 +1275,7 @@ Thanks and have fun!"; .Where(remoteManifest => remoteManifest.DalamudApiLevel == DalamudApiLevel) .Select(remoteManifest => { - var useTesting = UseTesting(remoteManifest); + var useTesting = this.UseTesting(remoteManifest); var candidateVersion = useTesting ? remoteManifest.TestingAssemblyVersion : remoteManifest.AssemblyVersion; diff --git a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs index e3c44c21d..136ca8c19 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs @@ -50,6 +50,11 @@ internal record LocalPluginManifest : PluginManifest /// public Version EffectiveVersion => this.Testing && this.TestingAssemblyVersion != null ? this.TestingAssemblyVersion : this.AssemblyVersion; + /// + /// Gets a value indicating whether this plugin is eligible for testing. + /// + public bool IsAvailableForTesting => this.TestingAssemblyVersion != null && this.TestingAssemblyVersion > this.AssemblyVersion; + /// /// Save a plugin manifest to file. ///