chore: only expose manifests as interfaces

This commit is contained in:
goat 2023-06-28 12:27:50 +02:00
parent a88151de7f
commit 98bdec1e34
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
16 changed files with 257 additions and 181 deletions

View file

@ -220,7 +220,7 @@ public sealed class DalamudPluginInterface : IDisposable
/// <summary>
/// Gets a list of installed plugins along with their current state.
/// </summary>
public IEnumerable<InstalledPluginState> InstalledPlugins => Service<PluginManager>.Get().InstalledPlugins.Select(p => new InstalledPluginState(p.Name, p.Manifest.InternalName, p.IsLoaded, p.Manifest.EffectiveVersion));
public IEnumerable<InstalledPluginState> InstalledPlugins => Service<PluginManager>.Get().InstalledPlugins.Select(p => new InstalledPluginState(p.Name, p.Manifest.InternalName, p.IsLoaded, p.EffectiveVersion));
/// <summary>
/// Opens the <see cref="PluginInstallerWindow"/> with the plugin name set as search target.

View file

@ -246,7 +246,7 @@ internal partial class PluginManager : IDisposable, IServiceType
/// </summary>
/// <param name="manifest">The manifest to test.</param>
/// <returns>Whether or not a testing version is available.</returns>
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
/// </summary>
/// <param name="manifest">Manifest to check.</param>
/// <returns>A value indicating whether testing should be used.</returns>
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
/// </summary>
/// <param name="manifest">Manifest to check.</param>
/// <returns>A value indicating whether testing should be used.</returns>
public bool UseTesting(PluginManifest manifest)
public bool UseTesting(IPluginManifest manifest)
{
if (!this.configuration.DoPluginTest)
return false;

View file

@ -0,0 +1,19 @@
namespace Dalamud.Plugin.Internal.Types;
/// <summary>
/// Public interface for the local plugin manifest.
/// </summary>
public interface ILocalPluginManifest : IPluginManifest
{
/// <summary>
/// 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.
/// </summary>
public string InstalledFromUrl { get; }
/// <summary>
/// Gets a value indicating whether the plugin should be deleted during the next cleanup.
/// </summary>
public bool ScheduledForDeletion { get; }
}

View file

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
namespace Dalamud.Plugin.Internal.Types;
/// <summary>
/// Public interface for the base plugin manifest.
/// </summary>
public interface IPluginManifest
{
/// <summary>
/// Gets the internal name of the plugin, which should match the assembly name of the plugin.
/// </summary>
public string InternalName { get; }
/// <summary>
/// Gets the public name of the plugin.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets a punchline of the plugins functions.
/// </summary>
public string? Punchline { get; }
/// <summary>
/// Gets the author/s of the plugin.
/// </summary>
public string Author { get; }
/// <summary>
/// Gets a value indicating whether the plugin can be unloaded asynchronously.
/// </summary>
public bool CanUnloadAsync { get; }
/// <summary>
/// Gets the assembly version of the plugin.
/// </summary>
public Version AssemblyVersion { get; }
/// <summary>
/// Gets the assembly version of the plugin's testing variant.
/// </summary>
public Version? TestingAssemblyVersion { get; }
/// <summary>
/// Gets the DIP17 channel name.
/// </summary>
public string? Dip17Channel { get; }
/// <summary>
/// Gets the last time this plugin was updated.
/// </summary>
public long LastUpdate { get; }
/// <summary>
/// Gets a changelog, null if none exists.
/// </summary>
public string? Changelog { get; }
/// <summary>
/// Gets a list of tags that apply to this plugin.
/// </summary>
public List<string>? Tags { get; }
/// <summary>
/// Gets the API level of this plugin. For the current API level, please see <see cref="PluginManager.DalamudApiLevel"/>
/// for the currently used API level.
/// </summary>
public int DalamudApiLevel { get; }
/// <summary>
/// Gets the number of downloads this plugin has.
/// </summary>
public long DownloadCount { get; }
/// <summary>
/// Gets a value indicating whether the plugin supports profiles.
/// </summary>
public bool SupportsProfiles { get; }
/// <summary>
/// Gets an URL to the website or source code of the plugin.
/// </summary>
public string? RepoUrl { get; }
/// <summary>
/// Gets a description of the plugins functions.
/// </summary>
public string? Description { get; }
/// <summary>
/// Gets a message that is shown to users when sending feedback.
/// </summary>
public string? FeedbackMessage { get; }
/// <summary>
/// Gets a value indicating whether the plugin is only available for testing.
/// </summary>
public bool IsTestingExclusive { get; }
/// <summary>
/// Gets a list of screenshot image URLs to show in the plugin installer.
/// </summary>
public List<string>? ImageUrls { get; }
/// <summary>
/// Gets an URL for the plugin's icon.
/// </summary>
public string? IconUrl { get; }
}

View file

@ -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;
/// <summary>
/// Initializes a new instance of the <see cref="LocalPlugin"/> class.
/// </summary>
@ -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<PluginManager>.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; }
/// <summary>
/// Gets the plugin manifest, if one exists.
/// Gets the plugin manifest.
/// </summary>
public LocalPluginManifest Manifest { get; private set; }
public ILocalPluginManifest Manifest => this.manifest;
/// <summary>
/// Gets or sets the current state of the plugin.
@ -193,12 +194,12 @@ internal class LocalPlugin : IDisposable
/// <summary>
/// Gets the plugin name from the manifest.
/// </summary>
public string Name => this.Manifest.Name;
public string Name => this.manifest.Name;
/// <summary>
/// Gets the plugin internal name from the manifest.
/// </summary>
public string InternalName => this.Manifest.InternalName;
public string InternalName => this.manifest.InternalName;
/// <summary>
/// Gets an optional reason, if the plugin is banned.
@ -220,23 +221,23 @@ internal class LocalPlugin : IDisposable
/// INCLUDES the default profile.
/// </summary>
public bool IsWantedByAnyProfile =>
Service<ProfileManager>.Get().GetWantStateAsync(this.Manifest.InternalName, false, false).GetAwaiter().GetResult();
Service<ProfileManager>.Get().GetWantStateAsync(this.manifest.InternalName, false, false).GetAwaiter().GetResult();
/// <summary>
/// Gets a value indicating whether this plugin's API level is out of date.
/// </summary>
public bool IsOutdated => this.Manifest.DalamudApiLevel < PluginManager.DalamudApiLevel;
public bool IsOutdated => this.manifest.DalamudApiLevel < PluginManager.DalamudApiLevel;
/// <summary>
/// Gets a value indicating whether the plugin is for testing use only.
/// </summary>
public bool IsTesting => this.Manifest.IsTestingExclusive || this.Manifest.Testing;
public bool IsTesting => this.manifest.IsTestingExclusive || this.manifest.Testing;
/// <summary>
/// Gets a value indicating whether or not this plugin is orphaned(belongs to a repo) or not.
/// </summary>
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;
/// <summary>
@ -244,7 +245,7 @@ internal class LocalPlugin : IDisposable
/// </summary>
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;
/// <summary>
/// Gets a value indicating whether this plugin has been banned.
@ -256,12 +257,23 @@ internal class LocalPlugin : IDisposable
/// </summary>
public bool IsDev => this is LocalDevPlugin;
/// <summary>
/// Gets a value indicating whether this manifest is associated with a plugin that was installed from a third party
/// repo.
/// </summary>
public bool IsThirdParty => this.manifest.IsThirdParty;
/// <summary>
/// Gets a value indicating whether this plugin should be allowed to load.
/// </summary>
public bool ApplicableForLoad => !this.IsBanned && !this.IsDecommissioned && !this.IsOrphaned && !this.IsOutdated
&& !(!this.IsDev && this.State == PluginState.UnloadError) && this.CheckPolicy();
/// <summary>
/// Gets the effective version of this plugin.
/// </summary>
public Version EffectiveVersion => this.manifest.EffectiveVersion;
/// <summary>
/// Gets the service scope for this plugin.
/// </summary>
@ -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<InterfaceManager>.GetAsync();
await Service<GameFontManager>.GetAsync();
if (this.Manifest.LoadRequiredState == 0)
if (this.manifest.LoadRequiredState == 0)
_ = await Service<InterfaceManager.InterfaceManagerWithScene>.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<DalamudConfiguration>.Get();
var framework = Service<Framework>.GetNullable();
var ioc = await Service<ServiceContainer>.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
/// <param name="status">Schedule or cancel the deletion.</param>
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
/// </summary>
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<PluginManager>.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);
}

View file

@ -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.
/// </summary>
internal record LocalPluginManifest : PluginManifest
internal record LocalPluginManifest : PluginManifest, ILocalPluginManifest
{
/// <summary>
/// Flag indicating that a plugin was installed from the official repo.
@ -38,16 +38,10 @@ internal record LocalPluginManifest : PluginManifest
/// </summary>
public bool Testing { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the plugin should be deleted during the next cleanup.
/// </summary>
/// <inheritdoc/>
public bool ScheduledForDeletion { get; set; }
/// <summary>
/// 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.
/// </summary>
/// <inheritdoc/>
public string InstalledFromUrl { get; set; } = string.Empty;
/// <summary>

View file

@ -9,41 +9,29 @@ namespace Dalamud.Plugin.Internal.Types;
/// <summary>
/// Information about a plugin, packaged in a json file with the DLL.
/// </summary>
internal record PluginManifest
internal record PluginManifest : IPluginManifest
{
/// <summary>
/// Gets the author/s of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string? Author { get; init; }
/// <summary>
/// Gets or sets the public name of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string Name { get; set; } = null!;
/// <summary>
/// Gets a punchline of the plugins functions.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string? Punchline { get; init; }
/// <summary>
/// Gets a description of the plugins functions.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string? Description { get; init; }
/// <summary>
/// Gets a changelog.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string? Changelog { get; init; }
/// <summary>
/// Gets a list of tags defined on the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public List<string>? Tags { get; init; }
@ -60,33 +48,23 @@ internal record PluginManifest
[JsonProperty]
public bool IsHide { get; init; }
/// <summary>
/// Gets the internal name of the plugin, which should match the assembly name of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string InternalName { get; init; } = null!;
public string InternalName { get; set; } = null!;
/// <summary>
/// Gets the current assembly version of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public Version AssemblyVersion { get; init; } = null!;
public Version AssemblyVersion { get; set; } = null!;
/// <summary>
/// Gets the current testing assembly version of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public Version? TestingAssemblyVersion { get; init; }
/// <summary>
/// Gets a value indicating whether the plugin is only available for testing.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public bool IsTestingExclusive { get; init; }
/// <summary>
/// Gets an URL to the website or source code of the plugin.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public string? RepoUrl { get; init; }
@ -97,24 +75,17 @@ internal record PluginManifest
[JsonConverter(typeof(GameVersionConverter))]
public GameVersion? ApplicableVersion { get; init; } = GameVersion.Any;
/// <summary>
/// Gets the API level of this plugin. For the current API level, please see <see cref="PluginManager.DalamudApiLevel"/>
/// for the currently used API level.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public int DalamudApiLevel { get; init; } = PluginManager.DalamudApiLevel;
/// <summary>
/// Gets the number of downloads this plugin has.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public long DownloadCount { get; init; }
/// <summary>
/// Gets the last time this plugin was updated.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public long LastUpdate { get; init; }
public long LastUpdate { get; set; }
/// <summary>
/// Gets the download link used to install the plugin.
@ -156,26 +127,18 @@ internal record PluginManifest
[JsonProperty]
public int LoadPriority { get; init; }
/// <summary>
/// Gets a value indicating whether the plugin can be unloaded asynchronously.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public bool CanUnloadAsync { get; init; }
public bool CanUnloadAsync { get; set; }
/// <summary>
/// Gets a value indicating whether the plugin supports profiles.
/// </summary>
/// <inheritdoc/>
[JsonProperty]
public bool SupportsProfiles { get; init; } = true;
/// <summary>
/// Gets a list of screenshot image URLs to show in the plugin installer.
/// </summary>
/// <inheritdoc/>
public List<string>? ImageUrls { get; init; }
/// <summary>
/// Gets an URL for the plugin's icon.
/// </summary>
/// <inheritdoc/>
public string? IconUrl { get; init; }
/// <summary>
@ -183,21 +146,10 @@ internal record PluginManifest
/// </summary>
public bool AcceptsFeedback { get; init; } = true;
/// <summary>
/// Gets a message that is shown to users when sending feedback.
/// </summary>
/// <inheritdoc/>
public string? FeedbackMessage { get; init; }
/// <summary>
/// Gets a value indicating whether this plugin is DIP17.
/// To be removed.
/// </summary>
[JsonProperty("_isDip17Plugin")]
public bool IsDip17Plugin { get; init; } = false;
/// <summary>
/// Gets the DIP17 channel name.
/// </summary>
/// <inheritdoc/>
[JsonProperty("_Dip17Channel")]
public string? Dip17Channel { get; init; }
}