mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge branch 'master' into feature/separate-atlas-per-plugin
This commit is contained in:
commit
5caed2f3b0
11 changed files with 129 additions and 30 deletions
43
.github/workflows/main.yml
vendored
43
.github/workflows/main.yml
vendored
|
|
@ -42,7 +42,48 @@ jobs:
|
|||
with:
|
||||
name: dalamud-artifact
|
||||
path: bin\Release
|
||||
|
||||
|
||||
check_api_compat:
|
||||
name: "Check API Compatibility"
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: "Install .NET SDK"
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7
|
||||
- name: "Install ApiCompat"
|
||||
run: |
|
||||
dotnet tool install -g Microsoft.DotNet.ApiCompat.Tool
|
||||
- name: "Download Proposed Artifacts"
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dalamud-artifact
|
||||
path: .\right
|
||||
- name: "Download Live (Stg) Artifacts"
|
||||
run: |
|
||||
Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip
|
||||
Expand-Archive -Force latest.zip "left"
|
||||
- name: "Verify Compatibility"
|
||||
run: |
|
||||
$FILES_TO_VALIDATE = "Dalamud.dll","FFXIVClientStructs.dll","Lumina.dll","Lumina.Excel.dll"
|
||||
|
||||
$retcode = 0
|
||||
|
||||
foreach ($file in $FILES_TO_VALIDATE) {
|
||||
$testout = ""
|
||||
Write-Output "::group::=== API COMPATIBILITY CHECK: ${file} ==="
|
||||
apicompat -l "left\${file}" -r "right\${file}" | Tee-Object -Variable testout
|
||||
Write-Output "::endgroup::"
|
||||
if ($testout -ne "APICompat ran successfully without finding any breaking changes.") {
|
||||
Write-Output "::error::${file} did not pass. Please review it for problems."
|
||||
$retcode = 1
|
||||
}
|
||||
}
|
||||
|
||||
exit $retcode
|
||||
|
||||
deploy_stg:
|
||||
name: Deploy dalamud-distrib staging
|
||||
if: ${{ github.repository_owner == 'goatcorp' && github.event_name == 'push' }}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<DalamudVersion>9.0.0.14</DalamudVersion>
|
||||
<DalamudVersion>9.0.0.16</DalamudVersion>
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using CheapLoc;
|
||||
|
|
@ -14,9 +15,9 @@ using Dalamud.Interface.Internal;
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Internal.Windows;
|
||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Dalamud.Utility;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game;
|
||||
|
||||
|
|
@ -60,6 +61,8 @@ internal class ChatHandlers : IServiceType
|
|||
// { XivChatType.Echo, Color.Gray },
|
||||
// };
|
||||
|
||||
private static readonly ModuleLog Log = new("CHATHANDLER");
|
||||
|
||||
private readonly Regex rmtRegex = new(
|
||||
@"4KGOLD|We have sufficient stock|VPK\.OM|[Gg]il for free|[Gg]il [Cc]heap|5GOLD|www\.so9\.com|Fast & Convenient|Cheap & Safety Guarantee|【Code|A O A U E|igfans|4KGOLD\.COM|Cheapest Gil with|pvp and bank on google|Selling Cheap GIL|ff14mogstation\.com|Cheap Gil 1000k|gilsforyou|server 1000K =|gils_selling|E A S Y\.C O M|bonus code|mins delivery guarantee|Sell cheap|Salegm\.com|cheap Mog|Off Code:|FF14Mog.com|使用する5%オ|[Oo][Ff][Ff] [Cc]ode( *)[:;]|offers Fantasia",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
|
@ -110,6 +113,7 @@ internal class ChatHandlers : IServiceType
|
|||
|
||||
private bool hasSeenLoadingMsg;
|
||||
private bool startedAutoUpdatingPlugins;
|
||||
private CancellationTokenSource deferredAutoUpdateCts = new();
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private ChatHandlers(ChatGui chatGui)
|
||||
|
|
@ -165,16 +169,19 @@ internal class ChatHandlers : IServiceType
|
|||
if (clientState == null)
|
||||
return;
|
||||
|
||||
if (type == XivChatType.Notice && !this.hasSeenLoadingMsg)
|
||||
this.PrintWelcomeMessage();
|
||||
if (type == XivChatType.Notice)
|
||||
{
|
||||
if (!this.hasSeenLoadingMsg)
|
||||
this.PrintWelcomeMessage();
|
||||
|
||||
if (!this.startedAutoUpdatingPlugins)
|
||||
this.AutoUpdatePluginsWithRetry();
|
||||
}
|
||||
|
||||
// For injections while logged in
|
||||
if (clientState.LocalPlayer != null && clientState.TerritoryType == 0 && !this.hasSeenLoadingMsg)
|
||||
this.PrintWelcomeMessage();
|
||||
|
||||
if (!this.startedAutoUpdatingPlugins)
|
||||
this.AutoUpdatePlugins();
|
||||
|
||||
#if !DEBUG && false
|
||||
if (!this.hasSeenLoadingMsg)
|
||||
return;
|
||||
|
|
@ -264,24 +271,42 @@ internal class ChatHandlers : IServiceType
|
|||
this.hasSeenLoadingMsg = true;
|
||||
}
|
||||
|
||||
private void AutoUpdatePlugins()
|
||||
private void AutoUpdatePluginsWithRetry()
|
||||
{
|
||||
var firstAttempt = this.AutoUpdatePlugins();
|
||||
if (!firstAttempt)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Task.Delay(30_000, this.deferredAutoUpdateCts.Token);
|
||||
this.AutoUpdatePlugins();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool AutoUpdatePlugins()
|
||||
{
|
||||
var chatGui = Service<ChatGui>.GetNullable();
|
||||
var pluginManager = Service<PluginManager>.GetNullable();
|
||||
var notifications = Service<NotificationManager>.GetNullable();
|
||||
|
||||
if (chatGui == null || pluginManager == null || notifications == null)
|
||||
return;
|
||||
{
|
||||
Log.Warning("Aborting auto-update because a required service was not loaded.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pluginManager.ReposReady || !pluginManager.InstalledPlugins.Any() || !pluginManager.AvailablePlugins.Any())
|
||||
{
|
||||
// Plugins aren't ready yet.
|
||||
// TODO: We should retry. This sucks, because it means we won't ever get here again until another notice.
|
||||
return;
|
||||
Log.Warning("Aborting auto-update because plugins weren't loaded or ready.");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.startedAutoUpdatingPlugins = true;
|
||||
|
||||
Log.Debug("Beginning plugin auto-update process...");
|
||||
Task.Run(() => pluginManager.UpdatePluginsAsync(true, !this.configuration.AutoUpdatePlugins, true)).ContinueWith(task =>
|
||||
{
|
||||
this.IsAutoUpdateComplete = true;
|
||||
|
|
@ -320,5 +345,7 @@ internal class ChatHandlers : IServiceType
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
|
@ -273,7 +273,10 @@ internal class TextureManager : IDisposable, IServiceType, ITextureProvider, ITe
|
|||
this.fallbackTextureWrap?.Dispose();
|
||||
this.framework.Update -= this.FrameworkOnUpdate;
|
||||
|
||||
Log.Verbose("Disposing {Num} left behind textures.");
|
||||
if (this.activeTextures.Count == 0)
|
||||
return;
|
||||
|
||||
Log.Verbose("Disposing {Num} left behind textures.", this.activeTextures.Count);
|
||||
|
||||
foreach (var activeTexture in this.activeTextures)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -146,18 +146,14 @@ 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);
|
||||
}
|
||||
|
||||
// This allows plugins to search for dependencies in the Dalamud directory when their assembly
|
||||
// load would otherwise fail, allowing them to resolve assemblies not already loaded by Dalamud
|
||||
// itself yet.
|
||||
builder.AddProbingPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
|
||||
|
||||
// Also make sure that plugins do not load their own Dalamud assembly.
|
||||
builder.PreferDefaultLoadContextAssembly(Assembly.GetExecutingAssembly().GetName());
|
||||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -958,7 +958,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;
|
||||
}
|
||||
|
|
@ -1581,6 +1581,8 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
|
||||
private void DetectAvailablePluginUpdates()
|
||||
{
|
||||
Log.Debug("Starting plugin update check...");
|
||||
|
||||
lock (this.pluginListLock)
|
||||
{
|
||||
this.updatablePluginsList.Clear();
|
||||
|
|
@ -1615,10 +1617,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();
|
||||
|
|
|
|||
|
|
@ -627,8 +627,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()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 97b814ca15d147911cdac3059623185a57984e0a
|
||||
Subproject commit 89e713c071dae13112550d3e754193704e230b03
|
||||
Loading…
Add table
Add a link
Reference in a new issue