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