mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 05:43:40 +01:00
Merge branch 'master' into net8
This commit is contained in:
commit
009151820d
32 changed files with 1612 additions and 573 deletions
|
|
@ -3,19 +3,14 @@ namespace Dalamud.Plugin.Internal.Exceptions;
|
|||
/// <summary>
|
||||
/// This represents a banned plugin that attempted an operation.
|
||||
/// </summary>
|
||||
internal class BannedPluginException : PluginException
|
||||
internal class BannedPluginException : PluginPreconditionFailedException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BannedPluginException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message describing the invalid operation.</param>
|
||||
public BannedPluginException(string message)
|
||||
: base(message)
|
||||
{
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message describing the invalid operation.
|
||||
/// </summary>
|
||||
public override string Message { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
namespace Dalamud.Plugin.Internal.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// An exception to be thrown when policy blocks a plugin from loading.
|
||||
/// </summary>
|
||||
internal class PluginPreconditionFailedException : InvalidPluginOperationException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginPreconditionFailedException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to associate with this exception.</param>
|
||||
public PluginPreconditionFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -131,9 +131,16 @@ internal class AssemblyLoadContextBuilder
|
|||
/// or the default app context.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <param name="recursive">Pull assmeblies recursively.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContextAssembly(AssemblyName assemblyName)
|
||||
public AssemblyLoadContextBuilder PreferDefaultLoadContextAssembly(AssemblyName assemblyName, bool recursive)
|
||||
{
|
||||
if (!recursive)
|
||||
{
|
||||
this.defaultAssemblies.Add(assemblyName.Name);
|
||||
return this;
|
||||
}
|
||||
|
||||
var names = new Queue<AssemblyName>(new[] { assemblyName });
|
||||
|
||||
while (names.TryDequeue(out var name))
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ internal class LoaderConfig
|
|||
/// Gets a list of assemblies which should be unified between the host and the plugin.
|
||||
/// </summary>
|
||||
/// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso>
|
||||
public ICollection<AssemblyName> SharedAssemblies { get; } = new List<AssemblyName>();
|
||||
public ICollection<(AssemblyName Name, bool Recursive)> SharedAssemblies { get; } = new List<(AssemblyName Name, bool Recursive)>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether attempt to unify all types from a plugin with the host.
|
||||
|
|
|
|||
|
|
@ -194,7 +194,18 @@ internal class ManagedLoadContext : AssemblyLoadContext
|
|||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/loading-managed#algorithm
|
||||
// > These assemblies are loaded (load-by-name) as needed by the runtime.
|
||||
// For load-by-name assembiles, the following will happen in order:
|
||||
// (1) this.Load will be called.
|
||||
// (2) AssemblyLoadContext.Default's cache will be referred for lookup.
|
||||
// (3) Default probing will be done from PLATFORM_RESOURCE_ROOTS and APP_PATHS.
|
||||
// https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing#managed-assembly-default-probing
|
||||
// > TRUSTED_PLATFORM_ASSEMBLIES: List of platform and application assembly file paths.
|
||||
// > APP_PATHS: is not populated by default and is omitted for most applications.
|
||||
// If we return null here, if the assembly has not been already loaded, the resolution will fail.
|
||||
// Therefore as the final attempt, we try loading from the default load context.
|
||||
return this.defaultLoadContext.LoadFromAssemblyName(assemblyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Nate McMaster, Dalamud team.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the Loader root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
|
|
@ -146,11 +146,15 @@ internal class PluginLoader : IDisposable
|
|||
builder.ShadowCopyNativeLibraries();
|
||||
}
|
||||
|
||||
foreach (var assemblyName in config.SharedAssemblies)
|
||||
foreach (var (assemblyName, recursive) in config.SharedAssemblies)
|
||||
{
|
||||
builder.PreferDefaultLoadContextAssembly(assemblyName);
|
||||
builder.PreferDefaultLoadContextAssembly(assemblyName, recursive);
|
||||
}
|
||||
|
||||
// Note: not adding Dalamud path here as a probing path.
|
||||
// It will be dealt as the last resort from ManagedLoadContext.Load.
|
||||
// See there for more details.
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -953,7 +953,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
autoUpdate ? PluginListInvalidationKind.AutoUpdate : PluginListInvalidationKind.Update,
|
||||
updatedList.Select(x => x.InternalName));
|
||||
|
||||
Log.Information("Plugin update OK.");
|
||||
Log.Information("Plugin update OK. {updateCount} plugins updated.", updatedList.Length);
|
||||
|
||||
return updatedList;
|
||||
}
|
||||
|
|
@ -1578,6 +1578,8 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
|
||||
private void DetectAvailablePluginUpdates()
|
||||
{
|
||||
Log.Debug("Starting plugin update check...");
|
||||
|
||||
lock (this.pluginListLock)
|
||||
{
|
||||
this.updatablePluginsList.Clear();
|
||||
|
|
@ -1612,10 +1614,12 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug("Update check found {updateCount} available updates.", this.updatablePluginsList.Count);
|
||||
}
|
||||
|
||||
private void NotifyAvailablePluginsChanged()
|
||||
{
|
||||
{
|
||||
this.DetectAvailablePluginUpdates();
|
||||
|
||||
this.OnAvailablePluginsChanged?.InvokeSafely();
|
||||
|
|
|
|||
|
|
@ -315,23 +315,23 @@ internal class LocalPlugin : IDisposable
|
|||
}
|
||||
|
||||
if (pluginManager.IsManifestBanned(this.manifest) && !this.IsDev)
|
||||
throw new BannedPluginException($"Unable to load {this.Name}, banned");
|
||||
throw new BannedPluginException($"Unable to load {this.Name} as it was banned");
|
||||
|
||||
if (this.manifest.ApplicableVersion < dalamud.StartInfo.GameVersion)
|
||||
throw new InvalidPluginOperationException($"Unable to load {this.Name}, no applicable version");
|
||||
throw new PluginPreconditionFailedException($"Unable to load {this.Name}, game is newer than applicable version {this.manifest.ApplicableVersion}");
|
||||
|
||||
if (this.manifest.DalamudApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels)
|
||||
throw new InvalidPluginOperationException($"Unable to load {this.Name}, incompatible API level");
|
||||
throw new PluginPreconditionFailedException($"Unable to load {this.Name}, incompatible API level {this.manifest.DalamudApiLevel}");
|
||||
|
||||
// We might want to throw here?
|
||||
if (!this.IsWantedByAnyProfile)
|
||||
Log.Warning("{Name} is loading, but isn't wanted by any profile", this.Name);
|
||||
|
||||
if (this.IsOrphaned)
|
||||
throw new InvalidPluginOperationException($"Plugin {this.Name} had no associated repo.");
|
||||
throw new PluginPreconditionFailedException($"Plugin {this.Name} had no associated repo");
|
||||
|
||||
if (!this.CheckPolicy())
|
||||
throw new InvalidPluginOperationException("Plugin was not loaded as per policy");
|
||||
throw new PluginPreconditionFailedException($"Unable to load {this.Name} as a load policy forbids it");
|
||||
|
||||
this.State = PluginState.Loading;
|
||||
Log.Information($"Loading {this.DllFile.Name}");
|
||||
|
|
@ -440,7 +440,10 @@ internal class LocalPlugin : IDisposable
|
|||
{
|
||||
this.State = PluginState.LoadError;
|
||||
|
||||
if (ex is not BannedPluginException)
|
||||
// If a precondition fails, don't record it as an error, as it isn't really.
|
||||
if (ex is PluginPreconditionFailedException)
|
||||
Log.Warning(ex.Message);
|
||||
else
|
||||
Log.Error(ex, $"Error while loading {this.Name}");
|
||||
|
||||
throw;
|
||||
|
|
@ -625,8 +628,18 @@ internal class LocalPlugin : IDisposable
|
|||
config.IsUnloadable = true;
|
||||
config.LoadInMemory = true;
|
||||
config.PreferSharedTypes = false;
|
||||
config.SharedAssemblies.Add(typeof(Lumina.GameData).Assembly.GetName());
|
||||
config.SharedAssemblies.Add(typeof(Lumina.Excel.ExcelSheetImpl).Assembly.GetName());
|
||||
|
||||
// Pin Lumina and its dependencies recursively (compatibility behavior).
|
||||
// It currently only pulls in System.* anyway.
|
||||
// TODO(api10): Remove this. We don't want to pin Lumina anymore, plugins should be able to provide their own.
|
||||
config.SharedAssemblies.Add((typeof(Lumina.GameData).Assembly.GetName(), true));
|
||||
config.SharedAssemblies.Add((typeof(Lumina.Excel.ExcelSheetImpl).Assembly.GetName(), true));
|
||||
|
||||
// Make sure that plugins do not load their own Dalamud assembly.
|
||||
// We do not pin this recursively; if a plugin loads its own assembly of Dalamud, it is always wrong,
|
||||
// but plugins may load other versions of assemblies that Dalamud depends on.
|
||||
config.SharedAssemblies.Add((typeof(EntryPoint).Assembly.GetName(), false));
|
||||
config.SharedAssemblies.Add((typeof(Common.DalamudStartInfo).Assembly.GetName(), false));
|
||||
}
|
||||
|
||||
private void EnsureLoader()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue