Plugin manager cleanup (#815)

This commit is contained in:
kalilistic 2022-04-23 10:40:31 -04:00 committed by GitHub
parent d676473928
commit 0b6417fd7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 217 additions and 175 deletions

View file

@ -118,7 +118,11 @@ resharper_arrange_type_member_modifiers_highlighting = hint
resharper_arrange_type_modifiers_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = none
resharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting = none
resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = none
resharper_invert_if_highlighting = none
resharper_loop_can_be_converted_to_query_highlighting = none
resharper_method_has_async_overload_highlighting = none
resharper_redundant_base_qualifier_highlighting = none
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint

View file

@ -47,12 +47,15 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EFormat_002ESettingsUpgrade_002EAlignmentTabFillStyleMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=bannedplugin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=clientopcode/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dalamud/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flytext/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gpose/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lumina/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Materia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PLUGINM/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Refilter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=serveropcode/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Universalis/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsanitized/@EntryIndexedValue">True</s:Boolean>

View file

@ -18,6 +18,7 @@ using Dalamud.Interface.Internal.Windows;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
using Serilog;
@ -302,7 +303,7 @@ namespace Dalamud.Game
{
if (configuration.AutoUpdatePlugins)
{
pluginManager.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
PluginManager.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
notifications.AddNotification(Loc.Localize("NotificationUpdatedPlugins", "{0} of your plugins were updated.").Format(updatedPlugins.Count), Loc.Localize("NotificationAutoUpdate", "Auto-Update"), NotificationType.Info);
}
else

View file

@ -320,7 +320,7 @@ namespace Dalamud.Interface.Internal.Windows
isThirdParty = true;
}
var useTesting = pluginManager.UseTesting(manifest);
var useTesting = PluginManager.UseTesting(manifest);
var url = this.GetPluginIconUrl(manifest, isThirdParty, useTesting);
if (!url.IsNullOrEmpty())
@ -441,7 +441,7 @@ namespace Dalamud.Interface.Internal.Windows
isThirdParty = true;
}
var useTesting = pluginManager.UseTesting(manifest);
var useTesting = PluginManager.UseTesting(manifest);
var urls = this.GetPluginImageUrls(manifest, isThirdParty, useTesting);
if (urls != null)

View file

@ -348,7 +348,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
if (this.updatePluginCount > 0)
{
pluginManager.PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
PluginManager.PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
notifications.AddNotification(Locs.Notifications_UpdatesInstalled(this.updatePluginCount), Locs.Notifications_UpdatesInstalledTitle, NotificationType.Success);
var installedGroupIdx = this.categoryManager.GroupList.TakeWhile(
@ -1251,7 +1251,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
var notifications = Service<NotificationManager>.Get();
var pluginManager = Service<PluginManager>.Get();
var useTesting = pluginManager.UseTesting(manifest);
var useTesting = PluginManager.UseTesting(manifest);
var wasSeen = this.WasPluginSeen(manifest.InternalName);
// Check for valid versions

View file

@ -104,7 +104,7 @@ namespace Dalamud.Plugin.Internal
/// <summary>
/// Gets a value indicating whether plugins are not still loading from boot.
/// </summary>
public bool PluginsReady { get; private set; } = false;
public bool PluginsReady { get; private set; }
/// <summary>
/// Gets a value indicating whether all added repos are not in progress.
@ -121,6 +121,82 @@ namespace Dalamud.Plugin.Internal
/// </summary>
public PluginConfigurations PluginConfigs { get; }
/// <summary>
/// Print to chat any plugin updates and whether they were successful.
/// </summary>
/// <param name="updateMetadata">The list of updated plugin metadata.</param>
/// <param name="header">The header text to send to chat prior to any update info.</param>
public static void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
{
var chatGui = Service<ChatGui>.Get();
if (updateMetadata is { Count: > 0 })
{
chatGui.Print(header);
foreach (var metadata in updateMetadata)
{
if (metadata.WasUpdated)
{
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
}
else
{
chatGui.PrintChat(new XivChatEntry
{
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version),
Type = XivChatType.Urgent,
});
}
}
}
}
/// <summary>
/// For a given manifest, determine if the testing version should be used over the normal version.
/// The higher of the two versions is calculated after checking other settings.
/// </summary>
/// <param name="manifest">Manifest to check.</param>
/// <returns>A value indicating whether testing should be used.</returns>
public static bool UseTesting(PluginManifest manifest)
{
var configuration = Service<DalamudConfiguration>.Get();
if (!configuration.DoPluginTest)
return false;
if (manifest.IsTestingExclusive)
return true;
var av = manifest.AssemblyVersion;
var tv = manifest.TestingAssemblyVersion;
var hasTv = tv != null;
if (hasTv)
{
return tv > av;
}
return false;
}
/// <summary>
/// Gets a value indicating whether the given repo manifest should be visible to the user.
/// </summary>
/// <param name="manifest">Repo manifest.</param>
/// <returns>If the manifest is visible.</returns>
public static bool IsManifestVisible(RemotePluginManifest manifest)
{
var configuration = Service<DalamudConfiguration>.Get();
// Hidden by user
if (configuration.HiddenPluginInternalName.Contains(manifest.InternalName))
return false;
// Hidden by manifest
return !manifest.IsHide;
}
/// <inheritdoc/>
public void Dispose()
{
@ -144,7 +220,7 @@ namespace Dalamud.Plugin.Internal
/// Set the list of repositories to use and downloads their contents.
/// Should be called when the Settings window has been updated or at instantiation.
/// </summary>
/// <param name="notify">Whether the available plugins changed should be evented after.</param>
/// <param name="notify">Whether the available plugins changed event should be sent after.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task SetPluginReposFromConfigAsync(bool notify)
{
@ -198,7 +274,7 @@ namespace Dalamud.Plugin.Internal
var manifest = LocalPluginManifest.Load(manifestFile);
pluginDefs.Add(new(dllFile, manifest, false));
pluginDefs.Add(new PluginDef(dllFile, manifest, false));
}
}
@ -225,7 +301,7 @@ namespace Dalamud.Plugin.Internal
// Manifests are not required for devPlugins. the Plugin type will handle any null manifests.
var manifestFile = LocalPluginManifest.GetManifestFile(dllFile);
var manifest = manifestFile.Exists ? LocalPluginManifest.Load(manifestFile) : null;
devPluginDefs.Add(new(dllFile, manifest, true));
devPluginDefs.Add(new PluginDef(dllFile, manifest, true));
}
// Sort for load order - unloaded definitions have default priority of 0
@ -235,9 +311,9 @@ namespace Dalamud.Plugin.Internal
// Dev plugins should load first.
pluginDefs.InsertRange(0, devPluginDefs);
void LoadPlugins(IEnumerable<PluginDef> pluginDefs)
void LoadPlugins(IEnumerable<PluginDef> pluginDefsList)
{
foreach (var pluginDef in pluginDefs)
foreach (var pluginDef in pluginDefsList)
{
try
{
@ -260,7 +336,7 @@ namespace Dalamud.Plugin.Internal
var asyncPlugins = pluginDefs.Where(def => def.Manifest == null || def.Manifest.LoadPriority <= 0);
Task.Run(() => LoadPlugins(asyncPlugins))
.ContinueWith(task =>
.ContinueWith(_ =>
{
this.PluginsReady = true;
this.NotifyInstalledPluginsChanged();
@ -318,7 +394,7 @@ namespace Dalamud.Plugin.Internal
this.AvailablePlugins = this.Repos
.SelectMany(repo => repo.PluginMaster)
.Where(this.IsManifestEligible)
.Where(this.IsManifestVisible)
.Where(IsManifestVisible)
.ToImmutableList();
if (notify)
@ -412,7 +488,7 @@ namespace Dalamud.Plugin.Internal
var response = await Util.HttpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
var outputDir = new DirectoryInfo(Path.Combine(this.pluginDirectory.FullName, repoManifest.InternalName, version.ToString()));
var outputDir = new DirectoryInfo(Path.Combine(this.pluginDirectory.FullName, repoManifest.InternalName, version?.ToString() ?? string.Empty));
try
{
@ -568,7 +644,6 @@ namespace Dalamud.Plugin.Internal
{
// Out of date plugins get added so they can be updated.
Log.Information(ex, $"Plugin was outdated, adding anyways: {dllFile.Name}");
// plugin.Disable(); // Don't disable, or it gets deleted next boot.
}
else
{
@ -625,14 +700,12 @@ namespace Dalamud.Plugin.Internal
return version;
})
.Where(version => version != null)
.ToArray();
if (versionDirs.Length == 0)
{
Log.Information($"No versions: cleaning up {pluginDir.FullName}");
pluginDir.Delete(true);
continue;
}
else
{
@ -675,7 +748,6 @@ namespace Dalamud.Plugin.Internal
{
Log.Information($"Inapplicable version: cleaning up {versionDir.FullName}");
versionDir.Delete(true);
continue;
}
}
catch (Exception ex)
@ -737,13 +809,12 @@ namespace Dalamud.Plugin.Internal
{
InternalName = plugin.Manifest.InternalName,
Name = plugin.Manifest.Name,
Version = metadata.UseTesting
Version = (metadata.UseTesting
? metadata.UpdateManifest.TestingAssemblyVersion
: metadata.UpdateManifest.AssemblyVersion,
: metadata.UpdateManifest.AssemblyVersion)!,
WasUpdated = true,
};
updateStatus.WasUpdated = true;
if (!dryRun)
{
// Unload if loaded
@ -836,88 +907,6 @@ namespace Dalamud.Plugin.Internal
plugin.Load(PluginLoadReason.Installer);
}
/// <summary>
/// Print to chat any plugin updates and whether they were successful.
/// </summary>
/// <param name="updateMetadata">The list of updated plugin metadata.</param>
/// <param name="header">The header text to send to chat prior to any update info.</param>
public void PrintUpdatedPlugins(List<PluginUpdateStatus> updateMetadata, string header)
{
var chatGui = Service<ChatGui>.Get();
if (updateMetadata != null && updateMetadata.Count > 0)
{
chatGui.Print(header);
foreach (var metadata in updateMetadata)
{
if (metadata.WasUpdated)
{
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
}
else
{
chatGui.PrintChat(new XivChatEntry
{
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version),
Type = XivChatType.Urgent,
});
}
}
}
}
/// <summary>
/// For a given manifest, determine if the testing version should be used over the normal version.
/// The higher of the two versions is calculated after checking other settings.
/// </summary>
/// <param name="manifest">Manifest to check.</param>
/// <returns>A value indicating whether testing should be used.</returns>
public bool UseTesting(PluginManifest manifest)
{
var configuration = Service<DalamudConfiguration>.Get();
if (!configuration.DoPluginTest)
return false;
if (manifest.IsTestingExclusive)
return true;
var av = manifest.AssemblyVersion;
var tv = manifest.TestingAssemblyVersion;
var hasAv = av != null;
var hasTv = tv != null;
if (hasAv && hasTv)
{
return tv > av;
}
else
{
return hasTv;
}
}
/// <summary>
/// Gets a value indicating whether the given repo manifest should be visible to the user.
/// </summary>
/// <param name="manifest">Repo manifest.</param>
/// <returns>If the manifest is visible.</returns>
public bool IsManifestVisible(RemotePluginManifest manifest)
{
var configuration = Service<DalamudConfiguration>.Get();
// Hidden by user
if (configuration.HiddenPluginInternalName.Contains(manifest.InternalName))
return false;
// Hidden by manifest
if (manifest.IsHide)
return false;
return true;
}
/// <summary>
/// Gets a value indicating whether the given manifest is eligible for ANYTHING. These are hard
/// checks that should not allow installation or loading.
@ -942,10 +931,7 @@ namespace Dalamud.Plugin.Internal
return false;
// Banned
if (this.IsManifestBanned(manifest))
return false;
return true;
return !this.IsManifestBanned(manifest);
}
/// <summary>
@ -984,7 +970,7 @@ namespace Dalamud.Plugin.Internal
.Where(remoteManifest => plugin.Manifest.InternalName == remoteManifest.InternalName)
.Select(remoteManifest =>
{
var useTesting = this.UseTesting(remoteManifest);
var useTesting = UseTesting(remoteManifest);
var candidateVersion = useTesting
? remoteManifest.TestingAssemblyVersion
: remoteManifest.AssemblyVersion;
@ -998,7 +984,7 @@ namespace Dalamud.Plugin.Internal
if (updates.Count > 0)
{
var update = updates.Aggregate((t1, t2) => t1.candidateVersion > t2.candidateVersion ? t1 : t2);
updatablePlugins.Add(new(plugin, update.remoteManifest, update.useTesting));
updatablePlugins.Add(new AvailablePluginUpdate(plugin, update.remoteManifest, update.useTesting));
}
}
@ -1033,41 +1019,6 @@ namespace Dalamud.Plugin.Internal
}
}
private struct BannedPlugin
{
[JsonProperty]
public string Name { get; private set; }
[JsonProperty]
public Version AssemblyVersion { get; private set; }
[JsonProperty]
public string Reason { get; private set; }
}
private struct PluginDef
{
public PluginDef(FileInfo dllFile, LocalPluginManifest? manifest, bool isDev)
{
this.DllFile = dllFile;
this.Manifest = manifest;
this.IsDev = isDev;
}
public FileInfo DllFile { get; init; }
public LocalPluginManifest? Manifest { get; init; }
public bool IsDev { get; init; }
public static int Sorter(PluginDef def1, PluginDef def2)
{
var prio1 = def1.Manifest?.LoadPriority ?? 0;
var prio2 = def2.Manifest?.LoadPriority ?? 0;
return prio2.CompareTo(prio1);
}
}
private static class Locs
{
public static string DalamudPluginUpdateSuccessful(string name, Version version) => Loc.Localize("DalamudPluginUpdateSuccessful", " 》 {0} updated to v{1}.").Format(name, version);
@ -1162,7 +1113,7 @@ namespace Dalamud.Plugin.Internal
if (methodBase == null)
continue;
yield return methodBase.Module.Assembly.FullName;
yield return methodBase.Module.Assembly.FullName!;
}
}
@ -1171,37 +1122,14 @@ namespace Dalamud.Plugin.Internal
var targetType = typeof(PluginManager).Assembly.GetType();
var locationTarget = targetType.GetProperty(nameof(Assembly.Location))!.GetGetMethod();
var locationPatch = typeof(PluginManager).GetMethod(nameof(PluginManager.AssemblyLocationPatch), BindingFlags.NonPublic | BindingFlags.Static);
var locationPatch = typeof(PluginManager).GetMethod(nameof(AssemblyLocationPatch), BindingFlags.NonPublic | BindingFlags.Static);
this.assemblyLocationMonoHook = new MonoMod.RuntimeDetour.Hook(locationTarget, locationPatch);
#pragma warning disable SYSLIB0012 // Type or member is obsolete
var codebaseTarget = targetType.GetProperty(nameof(Assembly.CodeBase)).GetGetMethod();
var codebasePatch = typeof(PluginManager).GetMethod(nameof(PluginManager.AssemblyCodeBasePatch), BindingFlags.NonPublic | BindingFlags.Static);
#pragma warning disable CS0618
var codebaseTarget = targetType.GetProperty(nameof(Assembly.CodeBase))?.GetGetMethod();
#pragma warning restore CS0618
var codebasePatch = typeof(PluginManager).GetMethod(nameof(AssemblyCodeBasePatch), BindingFlags.NonPublic | BindingFlags.Static);
this.assemblyCodeBaseMonoHook = new MonoMod.RuntimeDetour.Hook(codebaseTarget, codebasePatch);
#pragma warning restore SYSLIB0012 // Type or member is obsolete
}
internal record PluginPatchData
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginPatchData"/> class.
/// </summary>
/// <param name="dllFile">DLL file being loaded.</param>
public PluginPatchData(FileInfo dllFile)
{
this.Location = dllFile.FullName;
this.CodeBase = new Uri(dllFile.FullName).AbsoluteUri;
}
/// <summary>
/// Gets simulated Assembly.Location output.
/// </summary>
public string Location { get; }
/// <summary>
/// Gets simulated Assembly.CodeBase output.
/// </summary>
public string CodeBase { get; }
}
}
}

View file

@ -0,0 +1,29 @@
using System;
using Newtonsoft.Json;
namespace Dalamud.Plugin.Internal.Types;
/// <summary>
/// Banned plugin version that is blocked from installation.
/// </summary>
internal struct BannedPlugin
{
/// <summary>
/// Gets plugin name.
/// </summary>
[JsonProperty]
public string Name { get; private set; }
/// <summary>
/// Gets plugin assembly version.
/// </summary>
[JsonProperty]
public Version AssemblyVersion { get; private set; }
/// <summary>
/// Gets reason for the ban.
/// </summary>
[JsonProperty]
public string Reason { get; private set; }
}

View file

@ -0,0 +1,50 @@
using System.IO;
namespace Dalamud.Plugin.Internal.Types;
/// <summary>
/// Plugin Definition.
/// </summary>
internal struct PluginDef
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginDef"/> struct.
/// </summary>
/// <param name="dllFile">plugin dll file.</param>
/// <param name="manifest">plugin manifest.</param>
/// <param name="isDev">plugin dev indicator.</param>
public PluginDef(FileInfo dllFile, LocalPluginManifest? manifest, bool isDev)
{
this.DllFile = dllFile;
this.Manifest = manifest;
this.IsDev = isDev;
}
/// <summary>
/// Gets plugin DLL File.
/// </summary>
public FileInfo DllFile { get; init; }
/// <summary>
/// Gets plugin manifest.
/// </summary>
public LocalPluginManifest? Manifest { get; init; }
/// <summary>
/// Gets a value indicating whether plugin is a dev plugin.
/// </summary>
public bool IsDev { get; init; }
/// <summary>
/// Sort plugin definitions by priority.
/// </summary>
/// <param name="def1">plugin definition 1 to compare.</param>
/// <param name="def2">plugin definition 2 to compare.</param>
/// <returns>sort order.</returns>
public static int Sorter(PluginDef def1, PluginDef def2)
{
var priority1 = def1.Manifest?.LoadPriority ?? 0;
var priority2 = def2.Manifest?.LoadPriority ?? 0;
return priority2.CompareTo(priority1);
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.IO;
namespace Dalamud.Plugin.Internal.Types;
internal record PluginPatchData
{
/// <summary>
/// Initializes a new instance of the <see cref="PluginPatchData"/> class.
/// </summary>
/// <param name="dllFile">DLL file being loaded.</param>
public PluginPatchData(FileSystemInfo dllFile)
{
this.Location = dllFile.FullName;
this.CodeBase = new Uri(dllFile.FullName).AbsoluteUri;
}
/// <summary>
/// Gets simulated Assembly.Location output.
/// </summary>
public string Location { get; }
/// <summary>
/// Gets simulated Assembly.CodeBase output.
/// </summary>
public string CodeBase { get; }
}