diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 19b4f841c..1975505a8 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -263,7 +263,7 @@ public sealed class EntryPoint { pluginInfo = $"Plugin that caused this:\n{plugin.Name}\n\nClick \"Yes\" and remove it.\n\n"; - if (plugin.Manifest.IsThirdParty) + if (plugin.IsThirdParty) supportText = string.Empty; } } diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs index 0aeb0722d..bcdf90fe4 100644 --- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs +++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs @@ -232,7 +232,7 @@ internal class PluginImageCache : IDisposable, IServiceType /// If the plugin was third party sourced. /// Cached image textures, or an empty array. /// True if an entry exists, may be null if currently downloading. - public bool TryGetIcon(LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, out TextureWrap? iconTexture) + public bool TryGetIcon(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out TextureWrap? iconTexture) { iconTexture = null; @@ -274,7 +274,7 @@ internal class PluginImageCache : IDisposable, IServiceType /// If the plugin was third party sourced. /// Cached image textures, or an empty array. /// True if the image array exists, may be empty if currently downloading. - public bool TryGetImages(LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, out TextureWrap?[] imageTextures) + public bool TryGetImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, out TextureWrap?[] imageTextures) { if (!this.pluginImagesMap.TryAdd(manifest.InternalName, null)) { @@ -307,7 +307,7 @@ internal class PluginImageCache : IDisposable, IServiceType byte[]? bytes, string name, string? loc, - PluginManifest manifest, + IPluginManifest manifest, int maxWidth, int maxHeight, bool requireSquare) @@ -491,7 +491,7 @@ internal class PluginImageCache : IDisposable, IServiceType Log.Debug("Plugin image loader has shutdown"); } - private async Task DownloadPluginIconAsync(LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, ulong requestedFrame) + private async Task DownloadPluginIconAsync(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) { if (plugin is { IsDev: true }) { @@ -558,7 +558,7 @@ internal class PluginImageCache : IDisposable, IServiceType return icon; } - private async Task DownloadPluginImagesAsync(TextureWrap?[] pluginImages, LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, ulong requestedFrame) + private async Task DownloadPluginImagesAsync(TextureWrap?[] pluginImages, LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, ulong requestedFrame) { if (plugin is { IsDev: true }) { @@ -671,18 +671,15 @@ internal class PluginImageCache : IDisposable, IServiceType } } - private string? GetPluginIconUrl(PluginManifest manifest, bool isThirdParty, bool isTesting) + private string? GetPluginIconUrl(IPluginManifest manifest, bool isThirdParty, bool isTesting) { if (isThirdParty) return manifest.IconUrl; - if (manifest.IsDip17Plugin) - return MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, "icon.png"); - - return MainRepoImageUrl.Format(isTesting ? "testing" : "plugins", manifest.InternalName, "icon.png"); + return MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, "icon.png"); } - private List? GetPluginImageUrls(PluginManifest manifest, bool isThirdParty, bool isTesting) + private List? GetPluginImageUrls(IPluginManifest manifest, bool isThirdParty, bool isTesting) { if (isThirdParty) { @@ -698,14 +695,7 @@ internal class PluginImageCache : IDisposable, IServiceType var output = new List(); for (var i = 1; i <= 5; i++) { - if (manifest.IsDip17Plugin) - { - output.Add(MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, $"image{i}.png")); - } - else - { - output.Add(MainRepoImageUrl.Format(isTesting ? "testing" : "plugins", manifest.InternalName, $"image{i}.png")); - } + output.Add(MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, $"image{i}.png")); } return output; diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs index 2dc182e9a..984732509 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs @@ -48,15 +48,12 @@ internal class DalamudChangelogManager foreach (var plugin in this.manager.InstalledPlugins) { - if (!plugin.Manifest.IsThirdParty) + if (!plugin.IsThirdParty) { - if (!plugin.Manifest.IsDip17Plugin) - continue; - var pluginChangelogs = await client.GetFromJsonAsync(string.Format( - PluginChangelogUrl, - plugin.Manifest.InternalName, - plugin.Manifest.Dip17Channel)); + PluginChangelogUrl, + plugin.Manifest.InternalName, + plugin.Manifest.Dip17Channel)); changelogs = changelogs.Concat(pluginChangelogs.Versions .Where(x => x.Dip17Track == plugin.Manifest.Dip17Channel) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs index 247e2d353..b4048536e 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs @@ -33,7 +33,7 @@ internal class PluginChangelogEntry : IChangelogEntry { this.Plugin = plugin; - this.Version = plugin.Manifest.EffectiveVersion.ToString(); + this.Version = plugin.EffectiveVersion.ToString(); this.Text = plugin.Manifest.Changelog ?? Loc.Localize("ChangelogNoText", "No changelog for this version."); this.Author = plugin.Manifest.Author; this.Date = DateTimeOffset.FromUnixTimeSeconds(this.Plugin.Manifest.LastUpdate).DateTime; diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index ba249e051..80b4656b0 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -88,7 +88,7 @@ internal class PluginInstallerWindow : Window, IDisposable private string feedbackModalBody = string.Empty; private string feedbackModalContact = string.Empty; private bool feedbackModalIncludeException = false; - private PluginManifest? feedbackPlugin = null; + private IPluginManifest? feedbackPlugin = null; private bool feedbackIsTesting = false; private int updatePluginCount = 0; @@ -1606,7 +1606,7 @@ internal class PluginInstallerWindow : Window, IDisposable return ready; } - private bool DrawPluginCollapsingHeader(string label, LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, bool trouble, bool updateAvailable, bool isNew, bool installableOutdated, bool isOrphan, Action drawContextMenuAction, int index) + private bool DrawPluginCollapsingHeader(string label, LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, bool trouble, bool updateAvailable, bool isNew, bool installableOutdated, bool isOrphan, Action drawContextMenuAction, int index) { ImGui.Separator(); @@ -1741,13 +1741,13 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.TextWrapped(Locs.PluginBody_Orphaned); ImGui.PopStyleColor(); } - else if (plugin is { IsDecommissioned: true } && !plugin.Manifest.IsThirdParty) + else if (plugin is { IsDecommissioned: true, IsThirdParty: false }) { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); ImGui.TextWrapped(Locs.PluginBody_NoServiceOfficial); ImGui.PopStyleColor(); } - else if (plugin is { IsDecommissioned: true } && plugin.Manifest.IsThirdParty) + else if (plugin is { IsDecommissioned: true, IsThirdParty: true }) { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); ImGui.TextWrapped(Locs.PluginBody_NoServiceThird); @@ -1808,7 +1808,7 @@ internal class PluginInstallerWindow : Window, IDisposable if (log is PluginChangelogEntry pluginLog) { icon = this.imageCache.DefaultIcon; - var hasIcon = this.imageCache.TryGetIcon(pluginLog.Plugin, pluginLog.Plugin.Manifest, pluginLog.Plugin.Manifest.IsThirdParty, out var cachedIconTex); + var hasIcon = this.imageCache.TryGetIcon(pluginLog.Plugin, pluginLog.Plugin.Manifest, pluginLog.Plugin.IsThirdParty, out var cachedIconTex); if (hasIcon && cachedIconTex != null) { icon = cachedIconTex; @@ -2031,15 +2031,18 @@ internal class PluginInstallerWindow : Window, IDisposable } // Testing - if (plugin.Manifest.Testing) + if (plugin.IsTesting) { label += Locs.PluginTitleMod_TestingVersion; } + // TODO: check with the repos instead + /* if (plugin.Manifest.IsAvailableForTesting && configuration.DoPluginTest && testingOptIn == null) { label += Locs.PluginTitleMod_TestingAvailable; } + */ // Freshly installed if (showInstalled) @@ -2132,7 +2135,7 @@ internal class PluginInstallerWindow : Window, IDisposable 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, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) + if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.IsThirdParty, trouble, availablePluginUpdate != default, false, false, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) { if (!this.WasPluginSeen(plugin.Manifest.InternalName)) configuration.SeenPluginInternalName.Add(plugin.Manifest.InternalName); @@ -2154,12 +2157,12 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadText); - var isThirdParty = manifest.IsThirdParty; + var isThirdParty = plugin.IsThirdParty; var canFeedback = !isThirdParty && !plugin.IsDev && !plugin.IsOrphaned && plugin.Manifest.DalamudApiLevel == PluginManager.DalamudApiLevel && - plugin.Manifest.AcceptsFeedback && + // plugin.Manifest.AcceptsFeedback && // TODO: check with the repos availablePluginUpdate == default; // Installed from @@ -2215,7 +2218,7 @@ internal class PluginInstallerWindow : Window, IDisposable this.DrawUpdateSinglePluginButton(availablePluginUpdate); ImGui.SameLine(); - ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.Manifest.EffectiveVersion}"); + ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.EffectiveVersion}"); ImGuiHelpers.ScaledDummy(5); @@ -2226,7 +2229,7 @@ internal class PluginInstallerWindow : Window, IDisposable if (hasChangelog) { - if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.Manifest.EffectiveVersion))) + if (ImGui.TreeNode(Locs.PluginBody_CurrentChangeLog(plugin.EffectiveVersion))) { this.DrawInstalledPluginChangelog(plugin.Manifest); ImGui.TreePop(); @@ -2252,7 +2255,7 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PopID(); } - private void DrawInstalledPluginChangelog(PluginManifest manifest) + private void DrawInstalledPluginChangelog(IPluginManifest manifest) { ImGuiHelpers.ScaledDummy(5); @@ -2265,7 +2268,7 @@ internal class PluginInstallerWindow : Window, IDisposable { ImGui.Text("Changelog:"); ImGuiHelpers.ScaledDummy(2); - ImGuiHelpers.SafeTextWrapped(manifest.Changelog); + ImGuiHelpers.SafeTextWrapped(manifest.Changelog!); } ImGui.EndChild(); @@ -2363,7 +2366,7 @@ internal class PluginInstallerWindow : Window, IDisposable var isLoadedAndUnloadable = plugin.State == PluginState.Loaded || plugin.State == PluginState.DependencyResolutionFailed; - //StyleModelV1.DalamudStandard.Push(); + // StyleModelV1.DalamudStandard.Push(); var profileChooserPopupName = $"###pluginProfileChooser{plugin.Manifest.InternalName}"; if (ImGui.BeginPopup(profileChooserPopupName)) @@ -2526,7 +2529,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - //StyleModelV1.DalamudStandard.Pop(); + // StyleModelV1.DalamudStandard.Pop(); ImGui.SameLine(); ImGuiHelpers.ScaledDummy(15, 0); @@ -2621,7 +2624,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - private void DrawSendFeedbackButton(PluginManifest manifest, bool isTesting) + private void DrawSendFeedbackButton(IPluginManifest manifest, bool isTesting) { ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Comment)) @@ -2796,7 +2799,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - private bool DrawPluginImages(LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, int index) + private bool DrawPluginImages(LocalPlugin? plugin, IPluginManifest manifest, bool isThirdParty, int index) { var hasImages = this.imageCache.TryGetImages(plugin, manifest, isThirdParty, out var imageTextures); if (!hasImages || imageTextures.All(x => x == null)) @@ -2871,7 +2874,7 @@ internal class PluginInstallerWindow : Window, IDisposable return true; } - private bool IsManifestFiltered(PluginManifest manifest) + private bool IsManifestFiltered(IPluginManifest manifest) { var searchString = this.searchText.ToLowerInvariant(); var hasSearchString = !string.IsNullOrWhiteSpace(searchString); @@ -2889,7 +2892,7 @@ internal class PluginInstallerWindow : Window, IDisposable (manifest.Tags != null && manifest.Tags.Any(tag => tag.ToLowerInvariant().Contains(searchString)))); } - private (bool IsInstalled, LocalPlugin Plugin) IsManifestInstalled(PluginManifest? manifest) + private (bool IsInstalled, LocalPlugin Plugin) IsManifestInstalled(IPluginManifest? manifest) { if (manifest == null) return (false, default); diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs index dca81c2a7..db455b985 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs @@ -77,9 +77,9 @@ internal class ProfileManagerWidget private void DrawTutorial(string modalTitle) { - var _ = true; + var open = true; ImGui.SetNextWindowSize(new Vector2(450, 350), ImGuiCond.Appearing); - using (var popup = ImRaii.PopupModal(modalTitle, ref _)) + using (var popup = ImRaii.PopupModal(modalTitle, ref open)) { if (popup) { @@ -399,7 +399,7 @@ internal class ProfileManagerWidget if (pmPlugin != null) { - pic.TryGetIcon(pmPlugin, pmPlugin.Manifest, pmPlugin.Manifest.IsThirdParty, out var icon); + pic.TryGetIcon(pmPlugin, pmPlugin.Manifest, pmPlugin.IsThirdParty, out var icon); icon ??= pic.DefaultIcon; ImGui.Image(icon.ImGuiHandle, new Vector2(pluginLineHeight)); @@ -596,6 +596,5 @@ internal class ProfileManagerWidget public static string TutorialCommandsEnd => Loc.Localize("ProfileManagerTutorialCommandsEnd", "If you run multiple of these commands, they will be executed in order."); - } } diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 35b8bbbc7..1b9f065ab 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -220,7 +220,7 @@ public sealed class DalamudPluginInterface : IDisposable /// /// Gets a list of installed plugins along with their current state. /// - public IEnumerable InstalledPlugins => Service.Get().InstalledPlugins.Select(p => new InstalledPluginState(p.Name, p.Manifest.InternalName, p.IsLoaded, p.Manifest.EffectiveVersion)); + public IEnumerable InstalledPlugins => Service.Get().InstalledPlugins.Select(p => new InstalledPluginState(p.Name, p.Manifest.InternalName, p.IsLoaded, p.EffectiveVersion)); /// /// Opens the with the plugin name set as search target. diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 82def29d0..a7fec04e1 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -246,7 +246,7 @@ internal partial class PluginManager : IDisposable, IServiceType /// /// The manifest to test. /// Whether or not a testing version is available. - public static bool HasTestingVersion(PluginManifest manifest) + public static bool HasTestingVersion(IPluginManifest manifest) { var av = manifest.AssemblyVersion; var tv = manifest.TestingAssemblyVersion; @@ -316,7 +316,7 @@ internal partial class PluginManager : IDisposable, IServiceType /// /// Manifest to check. /// A value indicating whether testing should be used. - public bool HasTestingOptIn(PluginManifest manifest) + public bool HasTestingOptIn(IPluginManifest manifest) { return this.configuration.PluginTestingOptIns!.Any(x => x.InternalName == manifest.InternalName); } @@ -327,7 +327,7 @@ internal partial class PluginManager : IDisposable, IServiceType /// /// Manifest to check. /// A value indicating whether testing should be used. - public bool UseTesting(PluginManifest manifest) + public bool UseTesting(IPluginManifest manifest) { if (!this.configuration.DoPluginTest) return false; diff --git a/Dalamud/Plugin/Internal/Types/ILocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/ILocalPluginManifest.cs new file mode 100644 index 000000000..bbd40d2dd --- /dev/null +++ b/Dalamud/Plugin/Internal/Types/ILocalPluginManifest.cs @@ -0,0 +1,19 @@ +namespace Dalamud.Plugin.Internal.Types; + +/// +/// Public interface for the local plugin manifest. +/// +public interface ILocalPluginManifest : IPluginManifest +{ + /// + /// Gets the 3rd party repo URL that this plugin was installed from. Used to display where the plugin was + /// sourced from on the installed plugin view. This should not be included in the plugin master. This value is null + /// when installed from the main repo. + /// + public string InstalledFromUrl { get; } + + /// + /// Gets a value indicating whether the plugin should be deleted during the next cleanup. + /// + public bool ScheduledForDeletion { get; } +} diff --git a/Dalamud/Plugin/Internal/Types/IPluginManifest.cs b/Dalamud/Plugin/Internal/Types/IPluginManifest.cs new file mode 100644 index 000000000..90f2fb8bd --- /dev/null +++ b/Dalamud/Plugin/Internal/Types/IPluginManifest.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace Dalamud.Plugin.Internal.Types; + +/// +/// Public interface for the base plugin manifest. +/// +public interface IPluginManifest +{ + /// + /// Gets the internal name of the plugin, which should match the assembly name of the plugin. + /// + public string InternalName { get; } + + /// + /// Gets the public name of the plugin. + /// + public string Name { get; } + + /// + /// Gets a punchline of the plugins functions. + /// + public string? Punchline { get; } + + /// + /// Gets the author/s of the plugin. + /// + public string Author { get; } + + /// + /// Gets a value indicating whether the plugin can be unloaded asynchronously. + /// + public bool CanUnloadAsync { get; } + + /// + /// Gets the assembly version of the plugin. + /// + public Version AssemblyVersion { get; } + + /// + /// Gets the assembly version of the plugin's testing variant. + /// + public Version? TestingAssemblyVersion { 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. + /// + public string? Changelog { get; } + + /// + /// Gets a list of tags that apply to this plugin. + /// + public List? Tags { get; } + + /// + /// Gets the API level of this plugin. For the current API level, please see + /// for the currently used API level. + /// + public int DalamudApiLevel { get; } + + /// + /// Gets the number of downloads this plugin has. + /// + public long DownloadCount { get; } + + /// + /// Gets a value indicating whether the plugin supports profiles. + /// + public bool SupportsProfiles { get; } + + /// + /// Gets an URL to the website or source code of the plugin. + /// + public string? RepoUrl { get; } + + /// + /// Gets a description of the plugins functions. + /// + public string? Description { get; } + + /// + /// Gets a message that is shown to users when sending feedback. + /// + public string? FeedbackMessage { get; } + + /// + /// Gets a value indicating whether the plugin is only available for testing. + /// + public bool IsTestingExclusive { get; } + + /// + /// Gets a list of screenshot image URLs to show in the plugin installer. + /// + public List? ImageUrls { get; } + + /// + /// Gets an URL for the plugin's icon. + /// + public string? IconUrl { get; } +} diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 42fb40f91..d4caed0fd 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -11,7 +11,6 @@ using Dalamud.Game.Gui.Dtr; using Dalamud.Interface.GameFonts; using Dalamud.Interface.Internal; using Dalamud.IoC.Internal; -using Dalamud.Logging; using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal.Exceptions; using Dalamud.Plugin.Internal.Loader; @@ -39,6 +38,8 @@ internal class LocalPlugin : IDisposable private Type? pluginType; private IDalamudPlugin? instance; + private LocalPluginManifest manifest; + /// /// Initializes a new instance of the class. /// @@ -111,7 +112,7 @@ internal class LocalPlugin : IDisposable // If the parameter manifest was null if (manifest == null) { - this.Manifest = new LocalPluginManifest() + this.manifest = new LocalPluginManifest() { Author = "developer", Name = Path.GetFileNameWithoutExtension(this.DllFile.Name), @@ -125,11 +126,11 @@ internal class LocalPlugin : IDisposable // Save the manifest to disk so there won't be any problems later. // We'll update the name property after it can be retrieved from the instance. - this.Manifest.Save(this.manifestFile, "manifest was null"); + this.manifest.Save(this.manifestFile, "manifest was null"); } else { - this.Manifest = manifest; + this.manifest = manifest; } var needsSaveDueToLegacyFiles = false; @@ -139,7 +140,7 @@ internal class LocalPlugin : IDisposable if (this.disabledFile.Exists) { #pragma warning disable CS0618 - this.Manifest.Disabled = true; + this.manifest.Disabled = true; #pragma warning restore CS0618 this.disabledFile.Delete(); @@ -150,15 +151,15 @@ internal class LocalPlugin : IDisposable this.testingFile = LocalPluginManifest.GetTestingFile(this.DllFile); if (this.testingFile.Exists) { - this.Manifest.Testing = true; + this.manifest.Testing = true; this.testingFile.Delete(); needsSaveDueToLegacyFiles = true; } var pluginManager = Service.Get(); - this.IsBanned = pluginManager.IsManifestBanned(this.Manifest) && !this.IsDev; - this.BanReason = pluginManager.GetBanReason(this.Manifest); + this.IsBanned = pluginManager.IsManifestBanned(this.manifest) && !this.IsDev; + this.BanReason = pluginManager.GetBanReason(this.manifest); if (needsSaveDueToLegacyFiles) this.SaveManifest("legacy"); @@ -175,9 +176,9 @@ internal class LocalPlugin : IDisposable public FileInfo DllFile { get; } /// - /// Gets the plugin manifest, if one exists. + /// Gets the plugin manifest. /// - public LocalPluginManifest Manifest { get; private set; } + public ILocalPluginManifest Manifest => this.manifest; /// /// Gets or sets the current state of the plugin. @@ -193,12 +194,12 @@ internal class LocalPlugin : IDisposable /// /// Gets the plugin name from the manifest. /// - public string Name => this.Manifest.Name; + public string Name => this.manifest.Name; /// /// Gets the plugin internal name from the manifest. /// - public string InternalName => this.Manifest.InternalName; + public string InternalName => this.manifest.InternalName; /// /// Gets an optional reason, if the plugin is banned. @@ -220,23 +221,23 @@ internal class LocalPlugin : IDisposable /// INCLUDES the default profile. /// public bool IsWantedByAnyProfile => - Service.Get().GetWantStateAsync(this.Manifest.InternalName, false, false).GetAwaiter().GetResult(); + Service.Get().GetWantStateAsync(this.manifest.InternalName, false, false).GetAwaiter().GetResult(); /// /// Gets a value indicating whether this plugin's API level is out of date. /// - public bool IsOutdated => this.Manifest.DalamudApiLevel < PluginManager.DalamudApiLevel; + public bool IsOutdated => this.manifest.DalamudApiLevel < PluginManager.DalamudApiLevel; /// /// Gets a value indicating whether the plugin is for testing use only. /// - public bool IsTesting => this.Manifest.IsTestingExclusive || this.Manifest.Testing; + public bool IsTesting => this.manifest.IsTestingExclusive || this.manifest.Testing; /// /// Gets a value indicating whether or not this plugin is orphaned(belongs to a repo) or not. /// public bool IsOrphaned => !this.IsDev && - !this.Manifest.InstalledFromUrl.IsNullOrEmpty() && // TODO(api8): Remove this, all plugins will have a proper flag + !this.manifest.InstalledFromUrl.IsNullOrEmpty() && // TODO(api8): Remove this, all plugins will have a proper flag this.GetSourceRepository() == null; /// @@ -244,7 +245,7 @@ internal class LocalPlugin : IDisposable /// public bool IsDecommissioned => !this.IsDev && this.GetSourceRepository()?.State == PluginRepositoryState.Success && - this.GetSourceRepository()?.PluginMaster?.FirstOrDefault(x => x.InternalName == this.Manifest.InternalName) == null; + this.GetSourceRepository()?.PluginMaster?.FirstOrDefault(x => x.InternalName == this.manifest.InternalName) == null; /// /// Gets a value indicating whether this plugin has been banned. @@ -256,12 +257,23 @@ internal class LocalPlugin : IDisposable /// public bool IsDev => this is LocalDevPlugin; + /// + /// Gets a value indicating whether this manifest is associated with a plugin that was installed from a third party + /// repo. + /// + public bool IsThirdParty => this.manifest.IsThirdParty; + /// /// 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(); + /// + /// Gets the effective version of this plugin. + /// + public Version EffectiveVersion => this.manifest.EffectiveVersion; + /// /// Gets the service scope for this plugin. /// @@ -277,7 +289,7 @@ internal class LocalPlugin : IDisposable if (this.instance != null) { didPluginDispose = true; - if (this.Manifest.CanUnloadAsync || framework == null) + if (this.manifest.CanUnloadAsync || framework == null) this.instance.Dispose(); else framework.RunOnFrameworkThread(() => this.instance.Dispose()).Wait(); @@ -316,7 +328,7 @@ internal class LocalPlugin : IDisposable await Service.GetAsync(); await Service.GetAsync(); - if (this.Manifest.LoadRequiredState == 0) + if (this.manifest.LoadRequiredState == 0) _ = await Service.GetAsync(); await this.pluginLoadStateLock.WaitAsync(); @@ -329,9 +341,9 @@ internal class LocalPlugin : IDisposable } // If we reload a plugin we don't want to delete it. Makes sense, right? - if (this.Manifest.ScheduledForDeletion) + if (this.manifest.ScheduledForDeletion) { - this.Manifest.ScheduledForDeletion = false; + this.manifest.ScheduledForDeletion = false; this.SaveManifest("Scheduled for deletion, but loading"); } @@ -363,13 +375,13 @@ internal class LocalPlugin : IDisposable throw new ArgumentOutOfRangeException(this.State.ToString()); } - if (pluginManager.IsManifestBanned(this.Manifest) && !this.IsDev) + if (pluginManager.IsManifestBanned(this.manifest) && !this.IsDev) throw new BannedPluginException($"Unable to load {this.Name}, banned"); - if (this.Manifest.ApplicableVersion < startInfo.GameVersion) + if (this.manifest.ApplicableVersion < startInfo.GameVersion) throw new InvalidPluginOperationException($"Unable to load {this.Name}, no applicable version"); - if (this.Manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels) + if (this.manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels) throw new InvalidPluginOperationException($"Unable to load {this.Name}, incompatible API level"); // We might want to throw here? @@ -390,8 +402,8 @@ internal class LocalPlugin : IDisposable { Log.Error( "==== IMPORTANT MESSAGE TO {0}, THE DEVELOPER OF {1} ====", - this.Manifest.Author!, - this.Manifest.InternalName); + this.manifest.Author!, + this.manifest.InternalName); Log.Error( "YOU ARE INCLUDING DALAMUD DEPENDENCIES IN YOUR BUILDS!!!"); Log.Error( @@ -459,7 +471,7 @@ internal class LocalPlugin : IDisposable this.ServiceScope = ioc.GetScope(); this.ServiceScope.RegisterPrivateScopes(this); // Add this LocalPlugin as a private scope, so services can get it - if (this.Manifest.LoadSync && this.Manifest.LoadRequiredState is 0 or 1) + if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1) { this.instance = await framework.RunOnFrameworkThread( () => this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!)) as IDalamudPlugin; @@ -480,10 +492,10 @@ internal class LocalPlugin : IDisposable } // In-case the manifest name was a placeholder. Can occur when no manifest was included. - if (this.Manifest.Name.IsNullOrEmpty() && !this.IsDev) + if (this.manifest.Name.IsNullOrEmpty() && !this.IsDev) { - this.Manifest.Name = this.instance.Name; - this.Manifest.Save(this.manifestFile, "manifest name null or empty"); + this.manifest.Name = this.instance.Name; + this.manifest.Save(this.manifestFile, "manifest name null or empty"); } this.State = PluginState.Loaded; @@ -515,7 +527,6 @@ internal class LocalPlugin : IDisposable { var configuration = Service.Get(); var framework = Service.GetNullable(); - var ioc = await Service.GetAsync(); await this.pluginLoadStateLock.WaitAsync(); try @@ -544,7 +555,7 @@ internal class LocalPlugin : IDisposable this.State = PluginState.Unloading; Log.Information($"Unloading {this.DllFile.Name}"); - if (this.Manifest.CanUnloadAsync || framework == null) + if (this.manifest.CanUnloadAsync || framework == null) this.instance?.Dispose(); else await framework.RunOnFrameworkThread(() => this.instance?.Dispose()); @@ -612,7 +623,7 @@ internal class LocalPlugin : IDisposable if (startInfo.NoLoadPlugins) return false; - if (startInfo.NoLoadThirdPartyPlugins && this.Manifest.IsThirdParty) + if (startInfo.NoLoadThirdPartyPlugins && this.manifest.IsThirdParty) return false; if (manager.SafeMode) @@ -627,7 +638,7 @@ internal class LocalPlugin : IDisposable /// Schedule or cancel the deletion. public void ScheduleDeletion(bool status = true) { - this.Manifest.ScheduledForDeletion = status; + this.manifest.ScheduledForDeletion = status; this.SaveManifest("scheduling for deletion"); } @@ -636,12 +647,12 @@ internal class LocalPlugin : IDisposable /// public void ReloadManifest() { - var manifest = LocalPluginManifest.GetManifestFile(this.DllFile); - if (manifest.Exists) + var manifestPath = LocalPluginManifest.GetManifestFile(this.DllFile); + if (manifestPath.Exists) { // var isDisabled = this.IsDisabled; // saving the internal state because it could have been deleted - this.Manifest = LocalPluginManifest.Load(manifest) ?? throw new Exception("Could not reload manifest."); - // this.Manifest.Disabled = isDisabled; + this.manifest = LocalPluginManifest.Load(manifestPath) ?? throw new Exception("Could not reload manifest."); + // this.manifest.Disabled = isDisabled; this.SaveManifest("dev reload"); } @@ -659,10 +670,10 @@ internal class LocalPlugin : IDisposable var repos = Service.Get().Repos; return repos.FirstOrDefault(x => { - if (!x.IsThirdParty && !this.Manifest.IsThirdParty) + if (!x.IsThirdParty && !this.manifest.IsThirdParty) return true; - return x.PluginMasterUrl == this.Manifest.InstalledFromUrl; + return x.PluginMasterUrl == this.manifest.InstalledFromUrl; }); } @@ -675,5 +686,5 @@ internal class LocalPlugin : IDisposable config.SharedAssemblies.Add(typeof(Lumina.Excel.ExcelSheetImpl).Assembly.GetName()); } - private void SaveManifest(string reason) => this.Manifest.Save(this.manifestFile, reason); + private void SaveManifest(string reason) => this.manifest.Save(this.manifestFile, reason); } diff --git a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs index bcb8cd9e7..d6f8f99bf 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs @@ -11,7 +11,7 @@ namespace Dalamud.Plugin.Internal.Types; /// Information about a plugin, packaged in a json file with the DLL. This variant includes additional information such as /// if the plugin is disabled and if it was installed from a testing URL. This is designed for use with manifests on disk. /// -internal record LocalPluginManifest : PluginManifest +internal record LocalPluginManifest : PluginManifest, ILocalPluginManifest { /// /// Flag indicating that a plugin was installed from the official repo. @@ -38,16 +38,10 @@ internal record LocalPluginManifest : PluginManifest /// public bool Testing { get; set; } - /// - /// Gets or sets a value indicating whether the plugin should be deleted during the next cleanup. - /// + /// public bool ScheduledForDeletion { get; set; } - /// - /// Gets or sets the 3rd party repo URL that this plugin was installed from. Used to display where the plugin was - /// sourced from on the installed plugin view. This should not be included in the plugin master. This value is null - /// when installed from the main repo. - /// + /// public string InstalledFromUrl { get; set; } = string.Empty; /// diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs index 71051e666..effff824a 100644 --- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs @@ -9,41 +9,29 @@ namespace Dalamud.Plugin.Internal.Types; /// /// Information about a plugin, packaged in a json file with the DLL. /// -internal record PluginManifest +internal record PluginManifest : IPluginManifest { - /// - /// Gets the author/s of the plugin. - /// + /// [JsonProperty] public string? Author { get; init; } - /// - /// Gets or sets the public name of the plugin. - /// + /// [JsonProperty] public string Name { get; set; } = null!; - /// - /// Gets a punchline of the plugins functions. - /// + /// [JsonProperty] public string? Punchline { get; init; } - /// - /// Gets a description of the plugins functions. - /// + /// [JsonProperty] public string? Description { get; init; } - /// - /// Gets a changelog. - /// + /// [JsonProperty] public string? Changelog { get; init; } - /// - /// Gets a list of tags defined on the plugin. - /// + /// [JsonProperty] public List? Tags { get; init; } @@ -60,33 +48,23 @@ internal record PluginManifest [JsonProperty] public bool IsHide { get; init; } - /// - /// Gets the internal name of the plugin, which should match the assembly name of the plugin. - /// + /// [JsonProperty] - public string InternalName { get; init; } = null!; + public string InternalName { get; set; } = null!; - /// - /// Gets the current assembly version of the plugin. - /// + /// [JsonProperty] - public Version AssemblyVersion { get; init; } = null!; + public Version AssemblyVersion { get; set; } = null!; - /// - /// Gets the current testing assembly version of the plugin. - /// + /// [JsonProperty] public Version? TestingAssemblyVersion { get; init; } - /// - /// Gets a value indicating whether the plugin is only available for testing. - /// + /// [JsonProperty] public bool IsTestingExclusive { get; init; } - /// - /// Gets an URL to the website or source code of the plugin. - /// + /// [JsonProperty] public string? RepoUrl { get; init; } @@ -97,24 +75,17 @@ internal record PluginManifest [JsonConverter(typeof(GameVersionConverter))] public GameVersion? ApplicableVersion { get; init; } = GameVersion.Any; - /// - /// Gets the API level of this plugin. For the current API level, please see - /// for the currently used API level. - /// + /// [JsonProperty] public int DalamudApiLevel { get; init; } = PluginManager.DalamudApiLevel; - /// - /// Gets the number of downloads this plugin has. - /// + /// [JsonProperty] public long DownloadCount { get; init; } - /// - /// Gets the last time this plugin was updated. - /// + /// [JsonProperty] - public long LastUpdate { get; init; } + public long LastUpdate { get; set; } /// /// Gets the download link used to install the plugin. @@ -156,26 +127,18 @@ internal record PluginManifest [JsonProperty] public int LoadPriority { get; init; } - /// - /// Gets a value indicating whether the plugin can be unloaded asynchronously. - /// + /// [JsonProperty] - public bool CanUnloadAsync { get; init; } + public bool CanUnloadAsync { get; set; } - /// - /// 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. - /// + /// public List? ImageUrls { get; init; } - /// - /// Gets an URL for the plugin's icon. - /// + /// public string? IconUrl { get; init; } /// @@ -183,21 +146,10 @@ internal record PluginManifest /// public bool AcceptsFeedback { get; init; } = true; - /// - /// Gets a message that is shown to users when sending feedback. - /// + /// public string? FeedbackMessage { get; init; } - /// - /// Gets a value indicating whether this plugin is DIP17. - /// To be removed. - /// - [JsonProperty("_isDip17Plugin")] - public bool IsDip17Plugin { get; init; } = false; - - /// - /// Gets the DIP17 channel name. - /// + /// [JsonProperty("_Dip17Channel")] public string? Dip17Channel { get; init; } } diff --git a/Dalamud/Support/BugBait.cs b/Dalamud/Support/BugBait.cs index cdbf94616..da0df6054 100644 --- a/Dalamud/Support/BugBait.cs +++ b/Dalamud/Support/BugBait.cs @@ -25,7 +25,7 @@ internal static class BugBait /// The reporter name. /// Whether or not the most recent exception to occur should be included in the report. /// A representing the asynchronous operation. - public static async Task SendFeedback(PluginManifest plugin, bool isTesting, string content, string reporter, bool includeException) + public static async Task SendFeedback(IPluginManifest plugin, bool isTesting, string content, string reporter, bool includeException) { if (content.IsNullOrWhitespace()) return; diff --git a/Dalamud/Support/Troubleshooting.cs b/Dalamud/Support/Troubleshooting.cs index ef1897eeb..e8cf8d2eb 100644 --- a/Dalamud/Support/Troubleshooting.cs +++ b/Dalamud/Support/Troubleshooting.cs @@ -67,7 +67,7 @@ public static class Troubleshooting { var payload = new TroubleshootingPayload { - LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest)?.OrderByDescending(x => x.InternalName).ToArray(), + LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest as LocalPluginManifest)?.OrderByDescending(x => x.InternalName).ToArray(), 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, diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 60b1901d0..038273eb6 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -674,7 +674,7 @@ public static class Util } /// - /// Print formatted GameObject Information to ImGui + /// Print formatted GameObject Information to ImGui. /// /// Game Object to Display. /// Display Tag.