mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-22 08:29:18 +01:00
Merge pull request #1952 from Soreepeong/fix/plugin-ctor-isolation
Miscellaneous fixes
This commit is contained in:
commit
910b12deb5
27 changed files with 456 additions and 204 deletions
|
|
@ -12,6 +12,24 @@
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (United States) resources
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
#pragma code_page(1252)
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// RT_MANIFEST
|
||||||
|
//
|
||||||
|
|
||||||
|
RT_MANIFEST_THEMES RT_MANIFEST "themes.manifest"
|
||||||
|
|
||||||
|
#endif // English (United States) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// English (United Kingdom) resources
|
// English (United Kingdom) resources
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,11 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="module.def" />
|
<None Include="module.def" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="themes.manifest" />
|
||||||
|
</ItemGroup>
|
||||||
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
|
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
|
||||||
<Delete Files="$(OutDir)$(TargetName).lib" />
|
<Delete Files="$(OutDir)$(TargetName).lib" />
|
||||||
<Delete Files="$(OutDir)$(TargetName).exp" />
|
<Delete Files="$(OutDir)$(TargetName).exp" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -163,4 +163,7 @@
|
||||||
<Filter>Dalamud.Boot DLL</Filter>
|
<Filter>Dalamud.Boot DLL</Filter>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="themes.manifest" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
9
Dalamud.Boot/themes.manifest
Normal file
9
Dalamud.Boot/themes.manifest
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<description>Windows Forms Common Control manifest</description>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</assembly>
|
||||||
|
|
@ -65,7 +65,12 @@ internal sealed class Dalamud : IServiceType
|
||||||
true, new FileInfo(Path.Combine(cacheDir.FullName, $"{this.StartInfo.GameVersion}.json")));
|
true, new FileInfo(Path.Combine(cacheDir.FullName, $"{this.StartInfo.GameVersion}.json")));
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceManager.InitializeProvidedServices(this, fs, configuration, scanner);
|
ServiceManager.InitializeProvidedServices(
|
||||||
|
this,
|
||||||
|
fs,
|
||||||
|
configuration,
|
||||||
|
scanner,
|
||||||
|
Localization.FromAssets(info.AssetDirectory!, configuration.LanguageOverride));
|
||||||
|
|
||||||
// Set up FFXIVClientStructs
|
// Set up FFXIVClientStructs
|
||||||
this.SetupClientStructsResolver(cacheDir);
|
this.SetupClientStructsResolver(cacheDir);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Build">
|
<PropertyGroup Label="Build">
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<DebugType>portable</DebugType>
|
<DebugType>portable</DebugType>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Common;
|
using Dalamud.Common;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
using Dalamud.Interface.Internal.Windows;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Logging.Retention;
|
using Dalamud.Logging.Retention;
|
||||||
using Dalamud.Plugin.Internal;
|
using Dalamud.Plugin.Internal;
|
||||||
|
|
@ -232,6 +233,10 @@ public sealed class EntryPoint
|
||||||
|
|
||||||
private static void SerilogOnLogLine(object? sender, (string Line, LogEvent LogEvent) ev)
|
private static void SerilogOnLogLine(object? sender, (string Line, LogEvent LogEvent) ev)
|
||||||
{
|
{
|
||||||
|
if (!LoadingDialog.IsGloballyHidden)
|
||||||
|
LoadingDialog.NewLogEntries.Enqueue(ev);
|
||||||
|
ConsoleWindow.NewLogEntries.Enqueue(ev);
|
||||||
|
|
||||||
if (ev.LogEvent.Exception == null)
|
if (ev.LogEvent.Exception == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,17 @@ namespace Dalamud.Game.Config;
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed class GameConfig : IInternalDisposableService, IGameConfig
|
internal sealed class GameConfig : IInternalDisposableService, IGameConfig
|
||||||
{
|
{
|
||||||
private readonly TaskCompletionSource tcsInitialization = new();
|
private readonly TaskCompletionSource tcsInitialization =
|
||||||
private readonly TaskCompletionSource<GameConfigSection> tcsSystem = new();
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
private readonly TaskCompletionSource<GameConfigSection> tcsUiConfig = new();
|
|
||||||
private readonly TaskCompletionSource<GameConfigSection> tcsUiControl = new();
|
private readonly TaskCompletionSource<GameConfigSection> tcsSystem =
|
||||||
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
private readonly TaskCompletionSource<GameConfigSection> tcsUiConfig =
|
||||||
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
private readonly TaskCompletionSource<GameConfigSection> tcsUiControl =
|
||||||
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
private readonly GameConfigAddressResolver address = new();
|
private readonly GameConfigAddressResolver address = new();
|
||||||
private Hook<ConfigChangeDelegate>? configChangeHook;
|
private Hook<ConfigChangeDelegate>? configChangeHook;
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
||||||
if (numTicks <= 0)
|
if (numTicks <= 0)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource();
|
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.tickDelayedTaskCompletionSources[tcs] = (this.tickCounter + (ulong)numTicks, cancellationToken);
|
this.tickDelayedTaskCompletionSources[tcs] = (this.tickCounter + (ulong)numTicks, cancellationToken);
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public sealed class SingleFontChooserDialog : IDisposable
|
||||||
|
|
||||||
private readonly int counter;
|
private readonly int counter;
|
||||||
private readonly byte[] fontPreviewText = new byte[2048];
|
private readonly byte[] fontPreviewText = new byte[2048];
|
||||||
private readonly TaskCompletionSource<SingleFontSpec> tcs = new();
|
private readonly TaskCompletionSource<SingleFontSpec> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
private readonly IFontAtlas atlas;
|
private readonly IFontAtlas atlas;
|
||||||
|
|
||||||
private string popupImGuiName;
|
private string popupImGuiName;
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// <returns>A <see cref="Task"/> that resolves once <paramref name="action"/> is run.</returns>
|
/// <returns>A <see cref="Task"/> that resolves once <paramref name="action"/> is run.</returns>
|
||||||
public Task RunBeforeImGuiRender(Action action)
|
public Task RunBeforeImGuiRender(Action action)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource();
|
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.runBeforeImGuiRender.Enqueue(
|
this.runBeforeImGuiRender.Enqueue(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
|
|
@ -359,7 +359,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// <returns>A <see cref="Task"/> that resolves once <paramref name="func"/> is run.</returns>
|
/// <returns>A <see cref="Task"/> that resolves once <paramref name="func"/> is run.</returns>
|
||||||
public Task<T> RunBeforeImGuiRender<T>(Func<T> func)
|
public Task<T> RunBeforeImGuiRender<T>(Func<T> func)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.runBeforeImGuiRender.Enqueue(
|
this.runBeforeImGuiRender.Enqueue(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
|
|
@ -380,7 +380,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// <returns>A <see cref="Task"/> that resolves once <paramref name="action"/> is run.</returns>
|
/// <returns>A <see cref="Task"/> that resolves once <paramref name="action"/> is run.</returns>
|
||||||
public Task RunAfterImGuiRender(Action action)
|
public Task RunAfterImGuiRender(Action action)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource();
|
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.runAfterImGuiRender.Enqueue(
|
this.runAfterImGuiRender.Enqueue(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
|
|
@ -403,7 +403,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
/// <returns>A <see cref="Task"/> that resolves once <paramref name="func"/> is run.</returns>
|
/// <returns>A <see cref="Task"/> that resolves once <paramref name="func"/> is run.</returns>
|
||||||
public Task<T> RunAfterImGuiRender<T>(Func<T> func)
|
public Task<T> RunAfterImGuiRender<T>(Func<T> func)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.runAfterImGuiRender.Enqueue(
|
this.runAfterImGuiRender.Enqueue(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
|
|
@ -817,8 +817,12 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
|
|
||||||
|
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
||||||
|
SwapChainHelper.DetectReShade();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Requires that game window to be there, which will be the case once game swap chain is initialized.
|
||||||
if (Service<DalamudConfiguration>.Get().WindowIsImmersive)
|
if (Service<DalamudConfiguration>.Get().WindowIsImmersive)
|
||||||
this.SetImmersiveMode(true);
|
this.SetImmersiveMode(true);
|
||||||
}
|
}
|
||||||
|
|
@ -834,9 +838,6 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
0,
|
0,
|
||||||
this.SetCursorDetour);
|
this.SetCursorDetour);
|
||||||
|
|
||||||
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
|
||||||
SwapChainHelper.DetectReShade();
|
|
||||||
|
|
||||||
Log.Verbose("===== S W A P C H A I N =====");
|
Log.Verbose("===== S W A P C H A I N =====");
|
||||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
||||||
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
|
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ using Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Logging.Internal;
|
|
||||||
using Dalamud.Plugin.Internal;
|
using Dalamud.Plugin.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
@ -39,9 +38,6 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
private const int LogLinesMaximum = 1000000;
|
private const int LogLinesMaximum = 1000000;
|
||||||
private const int HistorySize = 50;
|
private const int HistorySize = 50;
|
||||||
|
|
||||||
// Only this field may be touched from any thread.
|
|
||||||
private readonly ConcurrentQueue<(string Line, LogEvent LogEvent)> newLogEntries;
|
|
||||||
|
|
||||||
// Fields below should be touched only from the main thread.
|
// Fields below should be touched only from the main thread.
|
||||||
private readonly RollingList<LogEntry> logText;
|
private readonly RollingList<LogEntry> logText;
|
||||||
private readonly RollingList<LogEntry> filteredLogEntries;
|
private readonly RollingList<LogEntry> filteredLogEntries;
|
||||||
|
|
@ -94,7 +90,6 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
|
|
||||||
this.autoScroll = configuration.LogAutoScroll;
|
this.autoScroll = configuration.LogAutoScroll;
|
||||||
this.autoOpen = configuration.LogOpenAtStartup;
|
this.autoOpen = configuration.LogOpenAtStartup;
|
||||||
SerilogEventSink.Instance.LogLine += this.OnLogLine;
|
|
||||||
|
|
||||||
Service<Framework>.GetAsync().ContinueWith(r => r.Result.Update += this.FrameworkOnUpdate);
|
Service<Framework>.GetAsync().ContinueWith(r => r.Result.Update += this.FrameworkOnUpdate);
|
||||||
|
|
||||||
|
|
@ -114,7 +109,6 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
this.logLinesLimit = configuration.LogLinesLimit;
|
this.logLinesLimit = configuration.LogLinesLimit;
|
||||||
|
|
||||||
var limit = Math.Max(LogLinesMinimum, this.logLinesLimit);
|
var limit = Math.Max(LogLinesMinimum, this.logLinesLimit);
|
||||||
this.newLogEntries = new();
|
|
||||||
this.logText = new(limit);
|
this.logText = new(limit);
|
||||||
this.filteredLogEntries = new(limit);
|
this.filteredLogEntries = new(limit);
|
||||||
|
|
||||||
|
|
@ -126,6 +120,9 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the queue where log entries that are not processed yet are stored.</summary>
|
||||||
|
public static ConcurrentQueue<(string Line, LogEvent LogEvent)> NewLogEntries { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnOpen()
|
public override void OnOpen()
|
||||||
{
|
{
|
||||||
|
|
@ -136,7 +133,6 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
SerilogEventSink.Instance.LogLine -= this.OnLogLine;
|
|
||||||
this.configuration.DalamudConfigurationSaved -= this.OnDalamudConfigurationSaved;
|
this.configuration.DalamudConfigurationSaved -= this.OnDalamudConfigurationSaved;
|
||||||
if (Service<Framework>.GetNullable() is { } framework)
|
if (Service<Framework>.GetNullable() is { } framework)
|
||||||
framework.Update -= this.FrameworkOnUpdate;
|
framework.Update -= this.FrameworkOnUpdate;
|
||||||
|
|
@ -324,7 +320,7 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
ImGuiInputTextFlags.CallbackHistory | ImGuiInputTextFlags.CallbackEdit,
|
ImGuiInputTextFlags.CallbackHistory | ImGuiInputTextFlags.CallbackEdit,
|
||||||
this.CommandInputCallback))
|
this.CommandInputCallback))
|
||||||
{
|
{
|
||||||
this.newLogEntries.Enqueue((this.commandText, new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, new MessageTemplate(string.Empty, []), [])));
|
NewLogEntries.Enqueue((this.commandText, new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, new MessageTemplate(string.Empty, []), [])));
|
||||||
this.ProcessCommand();
|
this.ProcessCommand();
|
||||||
getFocus = true;
|
getFocus = true;
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +368,7 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
this.pendingClearLog = false;
|
this.pendingClearLog = false;
|
||||||
this.logText.Clear();
|
this.logText.Clear();
|
||||||
this.filteredLogEntries.Clear();
|
this.filteredLogEntries.Clear();
|
||||||
this.newLogEntries.Clear();
|
NewLogEntries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.pendingRefilter)
|
if (this.pendingRefilter)
|
||||||
|
|
@ -388,7 +384,7 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
|
|
||||||
var numPrevFilteredLogEntries = this.filteredLogEntries.Count;
|
var numPrevFilteredLogEntries = this.filteredLogEntries.Count;
|
||||||
var addedLines = 0;
|
var addedLines = 0;
|
||||||
while (this.newLogEntries.TryDequeue(out var logLine))
|
while (NewLogEntries.TryDequeue(out var logLine))
|
||||||
addedLines += this.HandleLogLine(logLine.Line, logLine.LogEvent);
|
addedLines += this.HandleLogLine(logLine.Line, logLine.LogEvent);
|
||||||
this.newRolledLines = addedLines - (this.filteredLogEntries.Count - numPrevFilteredLogEntries);
|
this.newRolledLines = addedLines - (this.filteredLogEntries.Count - numPrevFilteredLogEntries);
|
||||||
}
|
}
|
||||||
|
|
@ -1062,11 +1058,6 @@ internal class ConsoleWindow : Window, IDisposable
|
||||||
/// <summary>Queues filtering the log entries again, before next call to <see cref="Draw"/>.</summary>
|
/// <summary>Queues filtering the log entries again, before next call to <see cref="Draw"/>.</summary>
|
||||||
private void QueueRefilter() => this.pendingRefilter = true;
|
private void QueueRefilter() => this.pendingRefilter = true;
|
||||||
|
|
||||||
/// <summary>Enqueues the new log line to the log-to-be-processed queue.</summary>
|
|
||||||
/// <remarks>See <see cref="FrameworkOnUpdate"/> for the handler for the queued log entries.</remarks>
|
|
||||||
private void OnLogLine(object sender, (string Line, LogEvent LogEvent) logEvent) =>
|
|
||||||
this.newLogEntries.Enqueue(logEvent);
|
|
||||||
|
|
||||||
private bool DrawToggleButtonWithTooltip(
|
private bool DrawToggleButtonWithTooltip(
|
||||||
string buttonId, string tooltip, FontAwesomeIcon icon, ref bool enabledState)
|
string buttonId, string tooltip, FontAwesomeIcon icon, ref bool enabledState)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ internal class PluginImageCache : IInternalDisposableService
|
||||||
|
|
||||||
private Task<T> RunInDownloadQueue<T>(Func<Task<T>> func, ulong requestedFrame)
|
private Task<T> RunInDownloadQueue<T>(Func<Task<T>> func, ulong requestedFrame)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.downloadQueue.Add(Tuple.Create(requestedFrame, async () =>
|
this.downloadQueue.Add(Tuple.Create(requestedFrame, async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -332,7 +332,7 @@ internal class PluginImageCache : IInternalDisposableService
|
||||||
|
|
||||||
private Task<T> RunInLoadQueue<T>(Func<Task<T>> func)
|
private Task<T> RunInLoadQueue<T>(Func<Task<T>> func)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.loadQueue.Add(async () =>
|
this.loadQueue.Add(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -3774,7 +3774,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
this.errorModalMessage = message;
|
this.errorModalMessage = message;
|
||||||
this.errorModalDrawing = true;
|
this.errorModalDrawing = true;
|
||||||
this.errorModalOnNextFrame = true;
|
this.errorModalOnNextFrame = true;
|
||||||
this.errorModalTaskCompletionSource = new TaskCompletionSource();
|
this.errorModalTaskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
return this.errorModalTaskCompletionSource.Task;
|
return this.errorModalTaskCompletionSource.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3782,7 +3782,7 @@ internal class PluginInstallerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
this.updateModalOnNextFrame = true;
|
this.updateModalOnNextFrame = true;
|
||||||
this.updateModalPlugin = plugin;
|
this.updateModalPlugin = plugin;
|
||||||
this.updateModalTaskCompletionSource = new TaskCompletionSource<bool>();
|
this.updateModalTaskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
return this.updateModalTaskCompletionSource.Task;
|
return this.updateModalTaskCompletionSource.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -497,7 +497,7 @@ internal sealed partial class FontAtlasFactory
|
||||||
$"{nameof(FontAtlasAutoRebuildMode.Async)}.");
|
$"{nameof(FontAtlasAutoRebuildMode.Async)}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<FontAtlasBuiltData>();
|
var tcs = new TaskCompletionSource<FontAtlasBuiltData>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var rebuildIndex = Interlocked.Increment(ref this.buildIndex);
|
var rebuildIndex = Interlocked.Increment(ref this.buildIndex);
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ internal abstract class FontHandle : IFontHandle
|
||||||
if (this.Available)
|
if (this.Available)
|
||||||
return Task.FromResult<IFontHandle>(this);
|
return Task.FromResult<IFontHandle>(this);
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<IFontHandle>();
|
var tcs = new TaskCompletionSource<IFontHandle>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.ImFontChanged += OnImFontChanged;
|
this.ImFontChanged += OnImFontChanged;
|
||||||
this.Disposed += OnDisposed;
|
this.Disposed += OnDisposed;
|
||||||
if (this.Available)
|
if (this.Available)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos
|
||||||
private readonly string? debugName;
|
private readonly string? debugName;
|
||||||
private readonly LocalPlugin? ownerPlugin;
|
private readonly LocalPlugin? ownerPlugin;
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
private readonly TaskCompletionSource<IDalamudTextureWrap> firstUpdateTaskCompletionSource = new();
|
private readonly TaskCompletionSource<IDalamudTextureWrap> firstUpdateTaskCompletionSource =
|
||||||
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
private ImGuiViewportTextureArgs args;
|
private ImGuiViewportTextureArgs args;
|
||||||
private D3D11_TEXTURE2D_DESC desc;
|
private D3D11_TEXTURE2D_DESC desc;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
|
||||||
{
|
{
|
||||||
var first = true;
|
var first = true;
|
||||||
var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList();
|
var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList();
|
||||||
var tcs = new TaskCompletionSource<BitmapCodecInfo>();
|
var tcs = new TaskCompletionSource<BitmapCodecInfo>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
Service<InterfaceManager>.Get().Draw += DrawChoices;
|
Service<InterfaceManager>.Get().Draw += DrawChoices;
|
||||||
|
|
||||||
encoder = await tcs.Task;
|
encoder = await tcs.Task;
|
||||||
|
|
@ -108,7 +108,7 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService
|
||||||
|
|
||||||
string path;
|
string path;
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<string>();
|
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.fileDialogManager.SaveFileDialog(
|
this.fileDialogManager.SaveFileDialog(
|
||||||
"Save texture...",
|
"Save texture...",
|
||||||
$"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
|
$"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
|
@ -127,7 +128,26 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctor.Invoke(instance, resolvedParams);
|
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
var thr = new Thread(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctor.Invoke(instance, resolvedParams);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
tcs.SetException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcs.SetResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
thr.Start();
|
||||||
|
await tcs.Task.ConfigureAwait(false);
|
||||||
|
thr.Join();
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
using Dalamud.Configuration.Internal;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -13,7 +12,7 @@ namespace Dalamud;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class handling localization.
|
/// Class handling localization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.ProvidedService]
|
||||||
public class Localization : IServiceType
|
public class Localization : IServiceType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -43,16 +42,6 @@ public class Localization : IServiceType
|
||||||
this.assembly = Assembly.GetCallingAssembly();
|
this.assembly = Assembly.GetCallingAssembly();
|
||||||
}
|
}
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
|
||||||
private Localization(Dalamud dalamud, DalamudConfiguration configuration)
|
|
||||||
: this(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "loc", "dalamud"), "dalamud_")
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(configuration.LanguageOverride))
|
|
||||||
this.SetupWithLangCode(configuration.LanguageOverride);
|
|
||||||
else
|
|
||||||
this.SetupWithUiCulture();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for the <see cref="Localization.LocalizationChanged"/> event that occurs when the language is changed.
|
/// Delegate for the <see cref="Localization.LocalizationChanged"/> event that occurs when the language is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -167,6 +156,22 @@ public class Localization : IServiceType
|
||||||
Loc.ExportLocalizableForAssembly(this.assembly, ignoreInvalidFunctions);
|
Loc.ExportLocalizableForAssembly(this.assembly, ignoreInvalidFunctions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="Localization"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assetDirectory">Path to Dalamud assets.</param>
|
||||||
|
/// <param name="languageOverride">Optional language override.</param>
|
||||||
|
/// <returns>A new instance.</returns>
|
||||||
|
internal static Localization FromAssets(string assetDirectory, string? languageOverride)
|
||||||
|
{
|
||||||
|
var t = new Localization(Path.Combine(assetDirectory, "UIRes", "loc", "dalamud"), "dalamud_");
|
||||||
|
if (!string.IsNullOrEmpty(languageOverride))
|
||||||
|
t.SetupWithLangCode(languageOverride);
|
||||||
|
else
|
||||||
|
t.SetupWithUiCulture();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
private string ReadLocData(string langCode)
|
private string ReadLocData(string langCode)
|
||||||
{
|
{
|
||||||
if (this.useEmbedded)
|
if (this.useEmbedded)
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,46 @@
|
||||||
using System.Drawing;
|
using System.Collections.Concurrent;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using CheapLoc;
|
||||||
|
|
||||||
using Dalamud.Plugin.Internal;
|
using Dalamud.Plugin.Internal;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.TASKDIALOG_FLAGS;
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
namespace Dalamud;
|
namespace Dalamud;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class providing an early-loading dialog.
|
/// Class providing an early-loading dialog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LoadingDialog
|
[SuppressMessage(
|
||||||
|
"StyleCop.CSharp.LayoutRules",
|
||||||
|
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||||
|
Justification = "Multiple fixed blocks")]
|
||||||
|
internal sealed unsafe class LoadingDialog
|
||||||
{
|
{
|
||||||
// TODO: We can't localize any of what's in here at the moment, because Localization is an EarlyLoadedService.
|
private readonly RollingList<string> logs = new(20);
|
||||||
|
|
||||||
private static int wasGloballyHidden = 0;
|
|
||||||
|
|
||||||
private Thread? thread;
|
private Thread? thread;
|
||||||
private TaskDialogButton? inProgressHideButton;
|
private HWND hwndTaskDialog;
|
||||||
private TaskDialogPage? page;
|
|
||||||
private bool canHide;
|
|
||||||
private State currentState = State.LoadingDalamud;
|
|
||||||
private DateTime firstShowTime;
|
private DateTime firstShowTime;
|
||||||
|
private State currentState = State.LoadingDalamud;
|
||||||
|
private bool canHide;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enum representing the state of the dialog.
|
/// Enum representing the state of the dialog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -38,18 +50,25 @@ internal class LoadingDialog
|
||||||
/// Show a message stating that Dalamud is currently loading.
|
/// Show a message stating that Dalamud is currently loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LoadingDalamud,
|
LoadingDalamud,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show a message stating that Dalamud is currently loading plugins.
|
/// Show a message stating that Dalamud is currently loading plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LoadingPlugins,
|
LoadingPlugins,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show a message stating that Dalamud is currently updating plugins.
|
/// Show a message stating that Dalamud is currently updating plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AutoUpdatePlugins,
|
AutoUpdatePlugins,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the queue where log entries that are not processed yet are stored.</summary>
|
||||||
|
public static ConcurrentQueue<(string Line, LogEvent LogEvent)> NewLogEntries { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether the initial Dalamud loading dialog will not show again until next
|
||||||
|
/// game restart.</summary>
|
||||||
|
public static bool IsGloballyHidden { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the current state of the dialog.
|
/// Gets or sets the current state of the dialog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -58,13 +77,16 @@ internal class LoadingDialog
|
||||||
get => this.currentState;
|
get => this.currentState;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (this.currentState == value)
|
||||||
|
return;
|
||||||
|
|
||||||
this.currentState = value;
|
this.currentState = value;
|
||||||
this.UpdatePage();
|
this.UpdateMainInstructionText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether or not the dialog can be hidden by the user.
|
/// Gets or sets a value indicating whether the dialog can be hidden by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="InvalidOperationException">Thrown if called before the dialog has been created.</exception>
|
/// <exception cref="InvalidOperationException">Thrown if called before the dialog has been created.</exception>
|
||||||
public bool CanHide
|
public bool CanHide
|
||||||
|
|
@ -72,8 +94,11 @@ internal class LoadingDialog
|
||||||
get => this.canHide;
|
get => this.canHide;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (this.canHide == value)
|
||||||
|
return;
|
||||||
|
|
||||||
this.canHide = value;
|
this.canHide = value;
|
||||||
this.UpdatePage();
|
this.UpdateButtonEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,19 +107,19 @@ internal class LoadingDialog
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Show()
|
public void Show()
|
||||||
{
|
{
|
||||||
if (Volatile.Read(ref wasGloballyHidden) == 1)
|
if (IsGloballyHidden)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.thread?.IsAlive == true)
|
if (this.thread?.IsAlive == true)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.thread = new Thread(this.ThreadStart)
|
this.thread = new Thread(this.ThreadStart)
|
||||||
{
|
{
|
||||||
Name = "Dalamud Loading Dialog",
|
Name = "Dalamud Loading Dialog",
|
||||||
};
|
};
|
||||||
this.thread.SetApartmentState(ApartmentState.STA);
|
this.thread.SetApartmentState(ApartmentState.STA);
|
||||||
this.thread.Start();
|
this.thread.Start();
|
||||||
|
|
||||||
this.firstShowTime = DateTime.Now;
|
this.firstShowTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,150 +128,287 @@ internal class LoadingDialog
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void HideAndJoin()
|
public void HideAndJoin()
|
||||||
{
|
{
|
||||||
if (this.thread == null || !this.thread.IsAlive)
|
IsGloballyHidden = true;
|
||||||
|
if (this.thread?.IsAlive is not true)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.inProgressHideButton?.PerformClick();
|
SendMessageW(this.hwndTaskDialog, WM.WM_CLOSE, default, default);
|
||||||
this.thread!.Join();
|
this.thread.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePage()
|
private void UpdateMainInstructionText()
|
||||||
{
|
{
|
||||||
if (this.page == null)
|
if (this.hwndTaskDialog == default)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.page.Heading = this.currentState switch
|
fixed (void* pszText = this.currentState switch
|
||||||
|
{
|
||||||
|
State.LoadingDalamud => Loc.Localize(
|
||||||
|
"LoadingDialogMainInstructionLoadingDalamud",
|
||||||
|
"Dalamud is loading..."),
|
||||||
|
State.LoadingPlugins => Loc.Localize(
|
||||||
|
"LoadingDialogMainInstructionLoadingPlugins",
|
||||||
|
"Waiting for plugins to load..."),
|
||||||
|
State.AutoUpdatePlugins => Loc.Localize(
|
||||||
|
"LoadingDialogMainInstructionAutoUpdatePlugins",
|
||||||
|
"Updating plugins..."),
|
||||||
|
_ => string.Empty, // should not happen
|
||||||
|
})
|
||||||
{
|
{
|
||||||
State.LoadingDalamud => "Dalamud is loading...",
|
SendMessageW(
|
||||||
State.LoadingPlugins => "Waiting for plugins to load...",
|
this.hwndTaskDialog,
|
||||||
State.AutoUpdatePlugins => "Updating plugins...",
|
(uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT,
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
(WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_MAIN_INSTRUCTION,
|
||||||
};
|
(LPARAM)pszText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var context = string.Empty;
|
private void UpdateContentText()
|
||||||
if (this.currentState == State.LoadingPlugins)
|
{
|
||||||
|
if (this.hwndTaskDialog == default)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var contentBuilder = new StringBuilder(
|
||||||
|
Loc.Localize(
|
||||||
|
"LoadingDialogContentInfo",
|
||||||
|
"Some of the plugins you have installed through Dalamud are taking a long time to load.\n" +
|
||||||
|
"This is likely normal, please wait a little while longer."));
|
||||||
|
|
||||||
|
if (this.CurrentState == State.LoadingPlugins)
|
||||||
{
|
{
|
||||||
context = "\nPreparing...";
|
|
||||||
|
|
||||||
var tracker = Service<PluginManager>.GetNullable()?.StartupLoadTracking;
|
var tracker = Service<PluginManager>.GetNullable()?.StartupLoadTracking;
|
||||||
if (tracker != null)
|
if (tracker != null)
|
||||||
{
|
{
|
||||||
var nameString = tracker.GetPendingInternalNames()
|
var nameString = string.Join(
|
||||||
.Select(x => tracker.GetPublicName(x))
|
", ",
|
||||||
.Where(x => x != null)
|
tracker.GetPendingInternalNames()
|
||||||
.Aggregate(string.Empty, (acc, x) => acc + x + ", ");
|
.Select(x => tracker.GetPublicName(x))
|
||||||
|
.Where(x => x != null));
|
||||||
|
|
||||||
if (!nameString.IsNullOrEmpty())
|
if (!nameString.IsNullOrEmpty())
|
||||||
context = $"\nWaiting for: {nameString[..^2]}";
|
{
|
||||||
|
contentBuilder
|
||||||
|
.AppendLine()
|
||||||
|
.AppendLine()
|
||||||
|
.Append(
|
||||||
|
string.Format(
|
||||||
|
Loc.Localize("LoadingDialogContentCurrentPlugin", "Waiting for: {0}"),
|
||||||
|
nameString));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add some text if loading takes more than a few minutes
|
// Add some text if loading takes more than a few minutes
|
||||||
if (DateTime.Now - this.firstShowTime > TimeSpan.FromMinutes(3))
|
if (DateTime.Now - this.firstShowTime > TimeSpan.FromMinutes(3))
|
||||||
context += "\nIt's been a while now. Please report this issue on our Discord server.";
|
|
||||||
|
|
||||||
this.page.Text = this.currentState switch
|
|
||||||
{
|
{
|
||||||
State.LoadingDalamud => "Please wait while Dalamud loads...",
|
contentBuilder
|
||||||
State.LoadingPlugins => "Please wait while Dalamud loads plugins...",
|
.AppendLine()
|
||||||
State.AutoUpdatePlugins => "Please wait while Dalamud updates your plugins...",
|
.AppendLine()
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
.Append(
|
||||||
#pragma warning disable SA1513
|
Loc.Localize(
|
||||||
} + context;
|
"LoadingDialogContentTakingTooLong",
|
||||||
#pragma warning restore SA1513
|
"It's been a while now. Please report this issue on our Discord server."));
|
||||||
|
|
||||||
this.inProgressHideButton!.Enabled = this.canHide;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DialogStatePeriodicUpdate(CancellationToken token)
|
|
||||||
{
|
|
||||||
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(50));
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await timer.WaitForNextTickAsync(token);
|
|
||||||
this.UpdatePage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixed (void* pszText = contentBuilder.ToString())
|
||||||
|
{
|
||||||
|
SendMessageW(
|
||||||
|
this.hwndTaskDialog,
|
||||||
|
(uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT,
|
||||||
|
(WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_CONTENT,
|
||||||
|
(LPARAM)pszText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateExpandedInformation()
|
||||||
|
{
|
||||||
|
const int maxCharactersPerLine = 80;
|
||||||
|
|
||||||
|
if (NewLogEntries.IsEmpty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
while (NewLogEntries.TryDequeue(out var e))
|
||||||
|
{
|
||||||
|
var t = e.Line.AsSpan();
|
||||||
|
var first = true;
|
||||||
|
while (!t.IsEmpty)
|
||||||
|
{
|
||||||
|
var i = t.IndexOfAny('\r', '\n');
|
||||||
|
var line = i == -1 ? t : t[..i];
|
||||||
|
t = i == -1 ? ReadOnlySpan<char>.Empty : t[(i + 1)..];
|
||||||
|
if (line.IsEmpty)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sb.Clear();
|
||||||
|
if (first)
|
||||||
|
sb.Append($"{e.LogEvent.Timestamp:HH:mm:ss} | ");
|
||||||
|
else
|
||||||
|
sb.Append(" | ");
|
||||||
|
first = false;
|
||||||
|
if (line.Length < maxCharactersPerLine)
|
||||||
|
sb.Append(line);
|
||||||
|
else
|
||||||
|
sb.Append($"{line[..(maxCharactersPerLine - 3)]}...");
|
||||||
|
this.logs.Add(sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Clear();
|
||||||
|
foreach (var l in this.logs)
|
||||||
|
sb.AppendLine(l);
|
||||||
|
|
||||||
|
fixed (void* pszText = sb.ToString())
|
||||||
|
{
|
||||||
|
SendMessageW(
|
||||||
|
this.hwndTaskDialog,
|
||||||
|
(uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT,
|
||||||
|
(WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_EXPANDED_INFORMATION,
|
||||||
|
(LPARAM)pszText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateButtonEnabled()
|
||||||
|
{
|
||||||
|
if (this.hwndTaskDialog == default)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendMessageW(this.hwndTaskDialog, (uint)TASKDIALOG_MESSAGES.TDM_ENABLE_BUTTON, IDOK, this.canHide ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HRESULT TaskDialogCallback(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch ((TASKDIALOG_NOTIFICATIONS)msg)
|
||||||
|
{
|
||||||
|
case TASKDIALOG_NOTIFICATIONS.TDN_CREATED:
|
||||||
|
this.hwndTaskDialog = hwnd;
|
||||||
|
|
||||||
|
this.UpdateMainInstructionText();
|
||||||
|
this.UpdateContentText();
|
||||||
|
this.UpdateExpandedInformation();
|
||||||
|
this.UpdateButtonEnabled();
|
||||||
|
SendMessageW(hwnd, (int)TASKDIALOG_MESSAGES.TDM_SET_PROGRESS_BAR_MARQUEE, 1, 0);
|
||||||
|
|
||||||
|
// Bring to front
|
||||||
|
SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SWP.SWP_NOSIZE | SWP.SWP_NOMOVE);
|
||||||
|
SetWindowPos(hwnd, HWND.HWND_NOTOPMOST, 0, 0, 0, 0, SWP.SWP_NOSIZE | SWP.SWP_NOMOVE);
|
||||||
|
ShowWindow(hwnd, SW.SW_SHOW);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
SetFocus(hwnd);
|
||||||
|
SetActiveWindow(hwnd);
|
||||||
|
return S.S_OK;
|
||||||
|
|
||||||
|
case TASKDIALOG_NOTIFICATIONS.TDN_DESTROYED:
|
||||||
|
this.hwndTaskDialog = default;
|
||||||
|
return S.S_OK;
|
||||||
|
|
||||||
|
case TASKDIALOG_NOTIFICATIONS.TDN_TIMER:
|
||||||
|
this.UpdateContentText();
|
||||||
|
this.UpdateExpandedInformation();
|
||||||
|
return S.S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S.S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThreadStart()
|
private void ThreadStart()
|
||||||
{
|
{
|
||||||
Application.EnableVisualStyles();
|
|
||||||
|
|
||||||
this.inProgressHideButton = new TaskDialogButton("Hide", this.canHide);
|
|
||||||
|
|
||||||
// We don't have access to the asset service here.
|
// We don't have access to the asset service here.
|
||||||
var workingDirectory = Service<Dalamud>.Get().StartInfo.WorkingDirectory;
|
var workingDirectory = Service<Dalamud>.Get().StartInfo.WorkingDirectory;
|
||||||
TaskDialogIcon? dialogIcon = null;
|
using var extractedIcon =
|
||||||
if (!workingDirectory.IsNullOrEmpty())
|
string.IsNullOrEmpty(workingDirectory)
|
||||||
|
? null
|
||||||
|
: Icon.ExtractAssociatedIcon(Path.Combine(workingDirectory, "Dalamud.Injector.exe"));
|
||||||
|
|
||||||
|
fixed (void* pszEmpty = "-")
|
||||||
|
fixed (void* pszWindowTitle = "Dalamud")
|
||||||
|
fixed (void* pszDalamudBoot = "Dalamud.Boot.dll")
|
||||||
|
fixed (void* pszThemesManifestResourceName = "RT_MANIFEST_THEMES")
|
||||||
|
fixed (void* pszHide = Loc.Localize("LoadingDialogHide", "Hide"))
|
||||||
|
fixed (void* pszShowLatestLogs = Loc.Localize("LoadingDialogShowLatestLogs", "Show Latest Logs"))
|
||||||
|
fixed (void* pszHideLatestLogs = Loc.Localize("LoadingDialogHideLatestLogs", "Hide Latest Logs"))
|
||||||
{
|
{
|
||||||
var extractedIcon = Icon.ExtractAssociatedIcon(Path.Combine(workingDirectory, "Dalamud.Injector.exe"));
|
var taskDialogButton = new TASKDIALOG_BUTTON
|
||||||
if (extractedIcon != null)
|
|
||||||
{
|
{
|
||||||
dialogIcon = new TaskDialogIcon(extractedIcon);
|
nButtonID = IDOK,
|
||||||
|
pszButtonText = (ushort*)pszHide,
|
||||||
|
};
|
||||||
|
var taskDialogConfig = new TASKDIALOGCONFIG
|
||||||
|
{
|
||||||
|
cbSize = (uint)sizeof(TASKDIALOGCONFIG),
|
||||||
|
hwndParent = default,
|
||||||
|
hInstance = (HINSTANCE)Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule),
|
||||||
|
dwFlags = (int)TDF_CAN_BE_MINIMIZED |
|
||||||
|
(int)TDF_SHOW_MARQUEE_PROGRESS_BAR |
|
||||||
|
(int)TDF_EXPAND_FOOTER_AREA |
|
||||||
|
(int)TDF_CALLBACK_TIMER |
|
||||||
|
(extractedIcon is null ? 0 : (int)TDF_USE_HICON_MAIN),
|
||||||
|
dwCommonButtons = 0,
|
||||||
|
pszWindowTitle = (ushort*)pszWindowTitle,
|
||||||
|
pszMainIcon = extractedIcon is null ? TD.TD_INFORMATION_ICON : (ushort*)extractedIcon.Handle,
|
||||||
|
pszMainInstruction = null,
|
||||||
|
pszContent = null,
|
||||||
|
cButtons = 1,
|
||||||
|
pButtons = &taskDialogButton,
|
||||||
|
nDefaultButton = IDOK,
|
||||||
|
cRadioButtons = 0,
|
||||||
|
pRadioButtons = null,
|
||||||
|
nDefaultRadioButton = 0,
|
||||||
|
pszVerificationText = null,
|
||||||
|
pszExpandedInformation = (ushort*)pszEmpty,
|
||||||
|
pszExpandedControlText = (ushort*)pszShowLatestLogs,
|
||||||
|
pszCollapsedControlText = (ushort*)pszHideLatestLogs,
|
||||||
|
pszFooterIcon = null,
|
||||||
|
pszFooter = null,
|
||||||
|
pfCallback = &HResultFuncBinder,
|
||||||
|
lpCallbackData = 0,
|
||||||
|
cxWidth = 360,
|
||||||
|
};
|
||||||
|
|
||||||
|
HANDLE hActCtx = default;
|
||||||
|
GCHandle gch = default;
|
||||||
|
nuint cookie = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var actctx = new ACTCTXW
|
||||||
|
{
|
||||||
|
cbSize = (uint)sizeof(ACTCTXW),
|
||||||
|
dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID,
|
||||||
|
lpResourceName = (ushort*)pszThemesManifestResourceName,
|
||||||
|
hModule = GetModuleHandleW((ushort*)pszDalamudBoot),
|
||||||
|
};
|
||||||
|
hActCtx = CreateActCtxW(&actctx);
|
||||||
|
if (hActCtx == default)
|
||||||
|
throw new Win32Exception("CreateActCtxW failure.");
|
||||||
|
|
||||||
|
if (!ActivateActCtx(hActCtx, &cookie))
|
||||||
|
throw new Win32Exception("ActivateActCtx failure.");
|
||||||
|
|
||||||
|
gch = GCHandle.Alloc((Func<HWND, uint, WPARAM, LPARAM, HRESULT>)this.TaskDialogCallback);
|
||||||
|
taskDialogConfig.lpCallbackData = GCHandle.ToIntPtr(gch);
|
||||||
|
TaskDialogIndirect(&taskDialogConfig, null, null, null).ThrowOnError();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "TaskDialogIndirect failure.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (gch.IsAllocated)
|
||||||
|
gch.Free();
|
||||||
|
if (cookie != 0)
|
||||||
|
DeactivateActCtx(0, cookie);
|
||||||
|
ReleaseActCtx(hActCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogIcon ??= TaskDialogIcon.Information;
|
IsGloballyHidden = true;
|
||||||
this.page = new TaskDialogPage
|
|
||||||
{
|
|
||||||
ProgressBar = new TaskDialogProgressBar(TaskDialogProgressBarState.Marquee),
|
|
||||||
Caption = "Dalamud",
|
|
||||||
Icon = dialogIcon,
|
|
||||||
Buttons = { this.inProgressHideButton },
|
|
||||||
AllowMinimize = false,
|
|
||||||
AllowCancel = false,
|
|
||||||
Expander = new TaskDialogExpander
|
|
||||||
{
|
|
||||||
CollapsedButtonText = "What does this mean?",
|
|
||||||
ExpandedButtonText = "What does this mean?",
|
|
||||||
Text = "Some of the plugins you have installed through Dalamud are taking a long time to load.\n" +
|
|
||||||
"This is likely normal, please wait a little while longer.",
|
|
||||||
},
|
|
||||||
SizeToContent = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.UpdatePage();
|
|
||||||
|
|
||||||
// Call private TaskDialog ctor
|
return;
|
||||||
var ctor = typeof(TaskDialog).GetConstructor(
|
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
|
||||||
null,
|
|
||||||
Array.Empty<Type>(),
|
|
||||||
null);
|
|
||||||
|
|
||||||
var taskDialog = (TaskDialog)ctor!.Invoke(Array.Empty<object>())!;
|
[UnmanagedCallersOnly]
|
||||||
|
static HRESULT HResultFuncBinder(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam, nint user) =>
|
||||||
this.page.Created += (_, _) =>
|
((Func<HWND, uint, WPARAM, LPARAM, HRESULT>)GCHandle.FromIntPtr(user).Target!)
|
||||||
{
|
.Invoke(hwnd, msg, wParam, lParam);
|
||||||
var hwnd = new HWND(taskDialog.Handle);
|
|
||||||
|
|
||||||
// Bring to front
|
|
||||||
Windows.Win32.PInvoke.SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0,
|
|
||||||
SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOMOVE);
|
|
||||||
Windows.Win32.PInvoke.SetWindowPos(hwnd, HWND.HWND_NOTOPMOST, 0, 0, 0, 0,
|
|
||||||
SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW | SET_WINDOW_POS_FLAGS.SWP_NOSIZE |
|
|
||||||
SET_WINDOW_POS_FLAGS.SWP_NOMOVE);
|
|
||||||
Windows.Win32.PInvoke.SetForegroundWindow(hwnd);
|
|
||||||
Windows.Win32.PInvoke.SetFocus(hwnd);
|
|
||||||
Windows.Win32.PInvoke.SetActiveWindow(hwnd);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Call private "ShowDialogInternal"
|
|
||||||
var showDialogInternal = typeof(TaskDialog).GetMethod(
|
|
||||||
"ShowDialogInternal",
|
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
|
||||||
null,
|
|
||||||
[typeof(IntPtr), typeof(TaskDialogPage), typeof(TaskDialogStartupLocation)],
|
|
||||||
null);
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
_ = this.DialogStatePeriodicUpdate(cts.Token);
|
|
||||||
|
|
||||||
showDialogInternal!.Invoke(
|
|
||||||
taskDialog,
|
|
||||||
[IntPtr.Zero, this.page, TaskDialogStartupLocation.CenterScreen]);
|
|
||||||
|
|
||||||
Interlocked.Exchange(ref wasGloballyHidden, 1);
|
|
||||||
cts.Cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ internal static class ServiceManager
|
||||||
private static readonly List<Type> LoadedServices = new();
|
private static readonly List<Type> LoadedServices = new();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource =
|
||||||
|
new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
private static readonly CancellationTokenSource UnloadCancellationTokenSource = new();
|
private static readonly CancellationTokenSource UnloadCancellationTokenSource = new();
|
||||||
|
|
||||||
private static ManualResetEvent unloadResetEvent = new(false);
|
private static ManualResetEvent unloadResetEvent = new(false);
|
||||||
|
|
@ -126,7 +128,13 @@ internal static class ServiceManager
|
||||||
/// <param name="fs">Instance of <see cref="ReliableFileStorage"/>.</param>
|
/// <param name="fs">Instance of <see cref="ReliableFileStorage"/>.</param>
|
||||||
/// <param name="configuration">Instance of <see cref="DalamudConfiguration"/>.</param>
|
/// <param name="configuration">Instance of <see cref="DalamudConfiguration"/>.</param>
|
||||||
/// <param name="scanner">Instance of <see cref="TargetSigScanner"/>.</param>
|
/// <param name="scanner">Instance of <see cref="TargetSigScanner"/>.</param>
|
||||||
public static void InitializeProvidedServices(Dalamud dalamud, ReliableFileStorage fs, DalamudConfiguration configuration, TargetSigScanner scanner)
|
/// <param name="localization">Instance of <see cref="Localization"/>.</param>
|
||||||
|
public static void InitializeProvidedServices(
|
||||||
|
Dalamud dalamud,
|
||||||
|
ReliableFileStorage fs,
|
||||||
|
DalamudConfiguration configuration,
|
||||||
|
TargetSigScanner scanner,
|
||||||
|
Localization localization)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
lock (LoadedServices)
|
lock (LoadedServices)
|
||||||
|
|
@ -136,6 +144,7 @@ internal static class ServiceManager
|
||||||
ProvideService(configuration);
|
ProvideService(configuration);
|
||||||
ProvideService(new ServiceContainer());
|
ProvideService(new ServiceContainer());
|
||||||
ProvideService(scanner);
|
ProvideService(scanner);
|
||||||
|
ProvideService(localization);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
@ -152,6 +161,7 @@ internal static class ServiceManager
|
||||||
ProvideService(configuration);
|
ProvideService(configuration);
|
||||||
ProvideService(new ServiceContainer());
|
ProvideService(new ServiceContainer());
|
||||||
ProvideService(scanner);
|
ProvideService(scanner);
|
||||||
|
ProvideService(localization);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
void ProvideService<T>(T service) where T : IServiceType => Service<T>.Provide(service);
|
void ProvideService<T>(T service) where T : IServiceType => Service<T>.Provide(service);
|
||||||
|
|
@ -242,19 +252,20 @@ internal static class ServiceManager
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Wait for all blocking constructors to complete first.
|
// Wait for all blocking constructors to complete first.
|
||||||
await WaitWithTimeoutConsent(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]),
|
await WaitWithTimeoutConsent(
|
||||||
|
blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]),
|
||||||
LoadingDialog.State.LoadingDalamud);
|
LoadingDialog.State.LoadingDalamud);
|
||||||
|
|
||||||
// All the BlockingEarlyLoadedService constructors have been run,
|
// All the BlockingEarlyLoadedService constructors have been run,
|
||||||
// and blockerTasks now will not change. Now wait for them.
|
// and blockerTasks now will not change. Now wait for them.
|
||||||
// Note that ServiceManager.CallWhenServicesReady does not get to register a blocker.
|
// Note that ServiceManager.CallWhenServicesReady does not get to register a blocker.
|
||||||
await WaitWithTimeoutConsent(blockerTasks,
|
await WaitWithTimeoutConsent(
|
||||||
|
blockerTasks,
|
||||||
LoadingDialog.State.LoadingPlugins);
|
LoadingDialog.State.LoadingPlugins);
|
||||||
|
|
||||||
Log.Verbose("=============== BLOCKINGSERVICES & TASKS INITIALIZED ===============");
|
Log.Verbose("=============== BLOCKINGSERVICES & TASKS INITIALIZED ===============");
|
||||||
Timings.Event("BlockingServices Initialized");
|
Timings.Event("BlockingServices Initialized");
|
||||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||||
loadingDialog.HideAndJoin();
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -269,11 +280,16 @@ internal static class ServiceManager
|
||||||
|
|
||||||
Log.Error(e, "Failed resolving blocking services");
|
Log.Error(e, "Failed resolving blocking services");
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
loadingDialog.HideAndJoin();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
async Task WaitWithTimeoutConsent(IEnumerable<Task> tasksEnumerable, LoadingDialog.State state)
|
async Task WaitWithTimeoutConsent(IEnumerable<Task> tasksEnumerable, LoadingDialog.State state)
|
||||||
{
|
{
|
||||||
|
loadingDialog.CurrentState = state;
|
||||||
var tasks = tasksEnumerable.AsReadOnlyCollection();
|
var tasks = tasksEnumerable.AsReadOnlyCollection();
|
||||||
if (tasks.Count == 0)
|
if (tasks.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
@ -286,7 +302,6 @@ internal static class ServiceManager
|
||||||
{
|
{
|
||||||
loadingDialog.Show();
|
loadingDialog.Show();
|
||||||
loadingDialog.CanHide = true;
|
loadingDialog.CanHide = true;
|
||||||
loadingDialog.CurrentState = state;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ internal static class Service<T> where T : IServiceType
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceTcs = new TaskCompletionSource<T>();
|
instanceTcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
instanceTcs.SetException(new UnloadedException());
|
instanceTcs.SetException(new UnloadedException());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ internal sealed class DalamudAssetManager : IInternalDisposableService, IDalamud
|
||||||
.Where(x => x is not DalamudAsset.Empty4X4)
|
.Where(x => x is not DalamudAsset.Empty4X4)
|
||||||
.Where(x => x.GetAttribute<DalamudAssetAttribute>()?.Required is false)
|
.Where(x => x.GetAttribute<DalamudAssetAttribute>()?.Required is false)
|
||||||
.Select(this.CreateStreamAsync)
|
.Select(this.CreateStreamAsync)
|
||||||
.Select(x => x.ToContentDisposedTask()))
|
.Select(x => x.ToContentDisposedTask(true)))
|
||||||
.ContinueWith(r => Log.Verbose($"Optional assets load state: {r}"));
|
.ContinueWith(r => Log.Verbose($"Optional assets load state: {r}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,18 @@ internal static unsafe partial class CurrentProcessModules
|
||||||
{
|
{
|
||||||
t = 0;
|
t = 0;
|
||||||
process = null;
|
process = null;
|
||||||
Log.Verbose("{what}: Fetchling fresh copy of current process modules.", nameof(CurrentProcessModules));
|
Log.Verbose("{what}: Fetching fresh copy of current process modules.", nameof(CurrentProcessModules));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (process ??= Process.GetCurrentProcess()).Modules;
|
try
|
||||||
|
{
|
||||||
|
return (process ??= Process.GetCurrentProcess()).Modules;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Verbose(e, "{what}: Failed to fetch module list.", nameof(CurrentProcessModules));
|
||||||
|
return new([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public static class AsyncUtils
|
||||||
/// <returns>Returns the first task that completes, according to <see cref="Task.IsCompletedSuccessfully"/>.</returns>
|
/// <returns>Returns the first task that completes, according to <see cref="Task.IsCompletedSuccessfully"/>.</returns>
|
||||||
public static Task<T> FirstSuccessfulTask<T>(ICollection<Task<T>> tasks)
|
public static Task<T> FirstSuccessfulTask<T>(ICollection<Task<T>> tasks)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>();
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
var remainingTasks = tasks.Count;
|
var remainingTasks = tasks.Count;
|
||||||
|
|
||||||
foreach (var task in tasks)
|
foreach (var task in tasks)
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ internal class DynamicPriorityQueueLoader : IDisposable
|
||||||
params IDisposable?[] disposables)
|
params IDisposable?[] disposables)
|
||||||
: base(basis, cancellationToken, disposables)
|
: base(basis, cancellationToken, disposables)
|
||||||
{
|
{
|
||||||
this.taskCompletionSource = new();
|
this.taskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
this.immediateLoadFunction = immediateLoadFunction;
|
this.immediateLoadFunction = immediateLoadFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue