Add configurable plugin load order with sync/async overrides

- add Dev Tools UI to manage a manual load order list
- show default load flavor and allow per-plugin override
- persist load order and overrides in config
- apply ordered loading while respecting sync/async flavor

Signed-off-by: msdx321 <msdx321@gmail.com>
This commit is contained in:
msdx321 2025-12-29 16:22:17 +01:00
parent fc130e325c
commit 6579884132
5 changed files with 479 additions and 6 deletions

View file

@ -337,6 +337,16 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
/// </summary>
public List<string>? DtrOrder { get; set; }
/// <summary>
/// Gets or sets the plugin load order, by internal name.
/// </summary>
public List<string>? PluginLoadOrder { get; set; }
/// <summary>
/// Gets or sets the plugin load order override mode, by internal name.
/// </summary>
public Dictionary<string, PluginLoadOrderMode>? PluginLoadOrderOverrides { get; set; }
/// <summary>
/// Gets or sets the list of ignored DTR elements, by title.
/// </summary>

View file

@ -0,0 +1,22 @@
namespace Dalamud.Configuration.Internal;
/// <summary>
/// Override for plugin load flavor in the load order list.
/// </summary>
internal enum PluginLoadOrderMode
{
/// <summary>
/// Use the plugin's default load flavor.
/// </summary>
Default = 0,
/// <summary>
/// Force synchronous load.
/// </summary>
ForceSync = 1,
/// <summary>
/// Force asynchronous load.
/// </summary>
ForceAsync = 2,
}

View file

@ -25,6 +25,7 @@ internal class PluginCategoryManager
new(CategoryKind.AvailableForTesting, "special.availableForTesting", () => Locs.Category_AvailableForTesting, CategoryInfo.AppearCondition.DoPluginTest),
new(CategoryKind.Hidden, "special.hidden", () => Locs.Category_Hidden, CategoryInfo.AppearCondition.AnyHiddenPlugins),
new(CategoryKind.DevInstalled, "special.devInstalled", () => Locs.Category_DevInstalled),
new(CategoryKind.PluginLoadOrder, "special.pluginLoadOrder", () => Locs.Category_PluginLoadOrder),
new(CategoryKind.IconTester, "special.devIconTester", () => Locs.Category_IconTester),
new(CategoryKind.DalamudChangelogs, "special.dalamud", () => Locs.Category_Dalamud),
new(CategoryKind.PluginChangelogs, "special.plugins", () => Locs.Category_Plugins),
@ -46,7 +47,7 @@ internal class PluginCategoryManager
private GroupInfo[] groupList =
[
new(GroupKind.DevTools, () => Locs.Group_DevTools, CategoryKind.DevInstalled, CategoryKind.IconTester),
new(GroupKind.DevTools, () => Locs.Group_DevTools, CategoryKind.DevInstalled, CategoryKind.PluginLoadOrder, CategoryKind.IconTester),
new(GroupKind.Installed, () => Locs.Group_Installed, CategoryKind.All, CategoryKind.IsTesting, CategoryKind.UpdateablePlugins, CategoryKind.PluginProfiles),
new(GroupKind.Available, () => Locs.Group_Available, CategoryKind.All),
new(GroupKind.Changelog, () => Locs.Group_Changelog, CategoryKind.All, CategoryKind.DalamudChangelogs, CategoryKind.PluginChangelogs)
@ -141,6 +142,11 @@ internal class PluginCategoryManager
/// Updateable plugins.
/// </summary>
UpdateablePlugins = 15,
/// <summary>
/// Customize plugin load order.
/// </summary>
PluginLoadOrder = 16,
/// <summary>
/// Plugins tagged as "other".
@ -550,6 +556,8 @@ internal class PluginCategoryManager
public static string Category_DevInstalled => Loc.Localize("InstallerInstalledDevPlugins", "Installed Dev Plugins");
public static string Category_PluginLoadOrder => Loc.Localize("InstallerCategoryPluginLoadOrder", "Plugin Load Order");
public static string Category_IconTester => "Image/Icon Tester";
public static string Category_PluginProfiles => Loc.Localize("InstallerCategoryPluginProfiles", "Plugin Collections");

View file

@ -6,6 +6,7 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@ -41,6 +42,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller;
/// </summary>
internal class PluginInstallerWindow : Window, IDisposable
{
private const string PluginLoadOrderPayload = "PLUGIN_LOAD_ORDER";
private static readonly ModuleLog Log = new("PLUGINW");
private readonly Vector4 changelogBgColor = new(0.114f, 0.584f, 0.192f, 0.678f);
@ -99,6 +101,7 @@ internal class PluginInstallerWindow : Window, IDisposable
private bool deletePluginConfigWarningModalExplainTesting = false;
private string deletePluginConfigWarningModalPluginName = string.Empty;
private TaskCompletionSource<bool>? deletePluginConfigWarningModalTaskCompletionSource;
private string loadOrderAddSelection = string.Empty;
private bool feedbackModalDrawing = true;
private bool feedbackModalOnNextFrame = false;
@ -1505,6 +1508,320 @@ internal class PluginInstallerWindow : Window, IDisposable
}
}
private void DrawPluginLoadOrder()
{
var configuration = Service<DalamudConfiguration>.Get();
var profileManager = Service<ProfileManager>.Get();
var enabledPlugins = this.GetEnabledPlugins(profileManager);
if (enabledPlugins.Count == 0)
{
DrawMutedBodyText(Locs.LoadOrder_NoEnabledPlugins, 60, 20);
return;
}
var orderNames = this.GetConfiguredPluginLoadOrder(configuration, enabledPlugins);
var orderedSet = new HashSet<string>(orderNames, StringComparer.OrdinalIgnoreCase);
var pluginLookup = enabledPlugins.ToDictionary(p => p.Manifest.InternalName, StringComparer.OrdinalIgnoreCase);
var availablePlugins = enabledPlugins
.Where(p => !orderedSet.Contains(p.Manifest.InternalName))
.OrderBy(p => p.Manifest.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
this.CleanupLoadOrderOverrides(configuration, orderedSet);
ImGui.TextWrapped(Locs.LoadOrder_Hint);
ImGuiHelpers.ScaledDummy(10);
if (availablePlugins.Count > 0)
{
ImGui.Text(Locs.LoadOrder_AddLabel);
var selected = availablePlugins.FirstOrDefault(p => p.Manifest.InternalName.Equals(this.loadOrderAddSelection, StringComparison.OrdinalIgnoreCase));
var preview = selected != null ? selected.Manifest.Name : Locs.LoadOrder_AddPlaceholder;
ImGui.SetNextItemWidth(-1);
if (ImGui.BeginCombo("##PluginLoadOrderAdd"u8, preview))
{
foreach (var plugin in availablePlugins)
{
var isSelected = plugin.Manifest.InternalName.Equals(this.loadOrderAddSelection, StringComparison.OrdinalIgnoreCase);
if (ImGui.Selectable(plugin.Manifest.Name, isSelected))
this.loadOrderAddSelection = plugin.Manifest.InternalName;
}
ImGui.EndCombo();
}
if (ImGui.Button(Locs.LoadOrder_AddButton) && selected != null)
{
orderNames.Add(selected.Manifest.InternalName);
this.ApplyPluginLoadOrder(configuration, orderNames);
this.loadOrderAddSelection = string.Empty;
}
ImGuiHelpers.ScaledDummy(10);
}
int? moveFrom = null;
int? moveTo = null;
int? removeIndex = null;
Span<byte> payloadData = stackalloc byte[sizeof(int)];
using var listChild = ImRaii.Child("PluginLoadOrderList"u8, new Vector2(-1, -1), true);
if (!listChild)
return;
if (orderNames.Count == 0)
{
DrawMutedBodyText(Locs.LoadOrder_NoneSelected, 20, 10);
return;
}
if (!ImGui.BeginTable("PluginLoadOrderTable"u8, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInnerV | ImGuiTableFlags.SizingStretchProp))
return;
ImGui.TableSetupColumn(Locs.LoadOrder_ColumnName, ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn(Locs.LoadOrder_ColumnDefault, ImGuiTableColumnFlags.WidthFixed, 90 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn(Locs.LoadOrder_ColumnOverride, ImGuiTableColumnFlags.WidthFixed, 130 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn(Locs.LoadOrder_ColumnRemove, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize(Locs.LoadOrder_RemoveButton).X + (ImGui.GetStyle().FramePadding.X * 2));
ImGui.TableHeadersRow();
for (var i = 0; i < orderNames.Count; i++)
{
var internalName = orderNames[i];
if (!pluginLookup.TryGetValue(internalName, out var plugin))
continue;
ImGui.PushID(internalName);
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.Selectable($"{plugin.Manifest.Name}###PluginLoadOrder_{internalName}", false);
if (ImGui.BeginDragDropSource())
{
unsafe
{
var payloadIndex = i;
MemoryMarshal.Write(payloadData, in payloadIndex);
ImGui.SetDragDropPayload(PluginLoadOrderPayload, payloadData);
}
ImGui.Text(plugin.Manifest.Name);
ImGui.EndDragDropSource();
}
if (ImGui.BeginDragDropTarget())
{
unsafe
{
var payload = ImGui.AcceptDragDropPayload(PluginLoadOrderPayload);
if (!payload.IsNull && payload.Data != null && payload.DataSize >= sizeof(int))
{
var sourceIndex = MemoryMarshal.Read<int>(new ReadOnlySpan<byte>(payload.Data, payload.DataSize));
moveFrom = sourceIndex;
moveTo = i;
}
}
ImGui.EndDragDropTarget();
}
var defaultIsSync = plugin.Manifest is LocalPluginManifest localManifest && localManifest.LoadSync;
ImGui.TableSetColumnIndex(1);
ImGui.Text(defaultIsSync ? Locs.LoadOrder_DefaultSync : Locs.LoadOrder_DefaultAsync);
ImGui.TableSetColumnIndex(2);
var overrideMode = this.GetLoadOrderOverride(configuration, internalName);
var overridePreview = overrideMode switch
{
PluginLoadOrderMode.ForceSync => Locs.LoadOrder_OverrideSync,
PluginLoadOrderMode.ForceAsync => Locs.LoadOrder_OverrideAsync,
_ => Locs.LoadOrder_OverrideDefault,
};
ImGui.SetNextItemWidth(-1);
if (ImGui.BeginCombo("##PluginLoadOrderOverride"u8, overridePreview))
{
if (ImGui.Selectable(Locs.LoadOrder_OverrideDefault, overrideMode == PluginLoadOrderMode.Default))
this.SetLoadOrderOverride(configuration, internalName, PluginLoadOrderMode.Default);
if (ImGui.Selectable(Locs.LoadOrder_OverrideSync, overrideMode == PluginLoadOrderMode.ForceSync))
this.SetLoadOrderOverride(configuration, internalName, PluginLoadOrderMode.ForceSync);
if (ImGui.Selectable(Locs.LoadOrder_OverrideAsync, overrideMode == PluginLoadOrderMode.ForceAsync))
this.SetLoadOrderOverride(configuration, internalName, PluginLoadOrderMode.ForceAsync);
ImGui.EndCombo();
}
ImGui.TableSetColumnIndex(3);
if (ImGui.SmallButton(Locs.LoadOrder_RemoveButton))
removeIndex = i;
ImGui.PopID();
}
ImGui.EndTable();
if (removeIndex.HasValue)
{
var removedInternalName = orderNames[removeIndex.Value];
orderNames.RemoveAt(removeIndex.Value);
this.SetLoadOrderOverride(configuration, removedInternalName, PluginLoadOrderMode.Default);
this.ApplyPluginLoadOrder(configuration, orderNames);
}
if (moveFrom.HasValue && moveTo.HasValue && moveFrom != moveTo)
{
var item = orderNames[moveFrom.Value];
orderNames.RemoveAt(moveFrom.Value);
if (moveFrom.Value < moveTo.Value)
moveTo--;
orderNames.Insert(moveTo.Value, item);
this.ApplyPluginLoadOrder(configuration, orderNames);
}
}
private List<LocalPlugin> GetEnabledPlugins(ProfileManager profileManager)
{
var enabledPlugins = new List<LocalPlugin>();
using var scope = profileManager.GetSyncScope();
foreach (var plugin in this.pluginListInstalled)
{
if (plugin.State == PluginState.Loaded)
{
enabledPlugins.Add(plugin);
continue;
}
foreach (var profile in profileManager.Profiles)
{
if (!profile.IsEnabled)
continue;
var wants = profile.WantsPlugin(plugin.EffectiveWorkingPluginId);
if (wants == true)
{
enabledPlugins.Add(plugin);
break;
}
}
}
return enabledPlugins;
}
private List<string> GetConfiguredPluginLoadOrder(DalamudConfiguration configuration, IReadOnlyList<LocalPlugin> enabledPlugins)
{
var enabledNames = new HashSet<string>(enabledPlugins.Select(p => p.Manifest.InternalName), StringComparer.OrdinalIgnoreCase);
var normalized = new List<string>(enabledPlugins.Count);
var normalizedSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var changed = false;
if (configuration.PluginLoadOrder != null)
{
foreach (var internalName in configuration.PluginLoadOrder)
{
if (internalName.IsNullOrEmpty())
{
changed = true;
continue;
}
if (!enabledNames.Contains(internalName))
{
changed = true;
continue;
}
if (normalizedSet.Add(internalName))
{
normalized.Add(internalName);
}
else
{
changed = true;
}
}
}
if (changed)
{
this.ApplyPluginLoadOrder(configuration, normalized);
}
return normalized;
}
private void ApplyPluginLoadOrder(DalamudConfiguration configuration, IReadOnlyList<string> orderNames)
{
if (configuration.PluginLoadOrder != null &&
configuration.PluginLoadOrder.SequenceEqual(orderNames, StringComparer.OrdinalIgnoreCase))
{
return;
}
configuration.PluginLoadOrder = orderNames.ToList();
configuration.QueueSave();
}
private void CleanupLoadOrderOverrides(DalamudConfiguration configuration, HashSet<string> orderedSet)
{
if (configuration.PluginLoadOrderOverrides == null || configuration.PluginLoadOrderOverrides.Count == 0)
return;
var removedAny = false;
var keysToRemove = configuration.PluginLoadOrderOverrides.Keys
.Where(key => !orderedSet.Contains(key))
.ToList();
foreach (var key in keysToRemove)
{
configuration.PluginLoadOrderOverrides.Remove(key);
removedAny = true;
}
if (configuration.PluginLoadOrderOverrides.Count == 0)
configuration.PluginLoadOrderOverrides = null;
if (removedAny)
configuration.QueueSave();
}
private PluginLoadOrderMode GetLoadOrderOverride(DalamudConfiguration configuration, string internalName)
{
if (configuration.PluginLoadOrderOverrides == null)
return PluginLoadOrderMode.Default;
return configuration.PluginLoadOrderOverrides.TryGetValue(internalName, out var mode)
? mode
: PluginLoadOrderMode.Default;
}
private void SetLoadOrderOverride(DalamudConfiguration configuration, string internalName, PluginLoadOrderMode mode)
{
if (mode == PluginLoadOrderMode.Default)
{
if (configuration.PluginLoadOrderOverrides == null)
return;
if (configuration.PluginLoadOrderOverrides.Remove(internalName))
{
if (configuration.PluginLoadOrderOverrides.Count == 0)
configuration.PluginLoadOrderOverrides = null;
configuration.QueueSave();
}
return;
}
configuration.PluginLoadOrderOverrides ??= new Dictionary<string, PluginLoadOrderMode>(StringComparer.OrdinalIgnoreCase);
if (configuration.PluginLoadOrderOverrides.TryGetValue(internalName, out var existing) && existing == mode)
return;
configuration.PluginLoadOrderOverrides[internalName] = mode;
configuration.QueueSave();
}
private void DrawPluginCategories()
{
var useContentHeight = -40f; // button height + spacing
@ -1700,6 +2017,10 @@ internal class PluginInstallerWindow : Window, IDisposable
this.DrawInstalledPluginList(InstalledPluginListFilter.Dev);
break;
case PluginCategoryManager.CategoryKind.PluginLoadOrder:
this.DrawPluginLoadOrder();
break;
case PluginCategoryManager.CategoryKind.IconTester:
this.DrawImageTester();
break;
@ -4099,6 +4420,42 @@ internal class PluginInstallerWindow : Window, IDisposable
#endregion
#region Load order
public static string LoadOrder_Hint => Loc.Localize("InstallerPluginLoadOrderHint", "Plugins added here load in the order shown. Default load flavor is shown and can be overridden. Changes apply the next time Dalamud starts.");
public static string LoadOrder_NoEnabledPlugins => Loc.Localize("InstallerPluginLoadOrderNone", "No enabled plugins were found.");
public static string LoadOrder_NoneSelected => Loc.Localize("InstallerPluginLoadOrderEmpty", "No plugins are selected yet.");
public static string LoadOrder_AddLabel => Loc.Localize("InstallerPluginLoadOrderAddLabel", "Add an enabled plugin:");
public static string LoadOrder_AddPlaceholder => Loc.Localize("InstallerPluginLoadOrderAddPlaceholder", "Select a plugin...");
public static string LoadOrder_AddButton => Loc.Localize("InstallerPluginLoadOrderAddButton", "Add to load order");
public static string LoadOrder_RemoveButton => Loc.Localize("InstallerPluginLoadOrderRemoveButton", "Remove");
public static string LoadOrder_ColumnName => Loc.Localize("InstallerPluginLoadOrderColumnName", "Name");
public static string LoadOrder_ColumnDefault => Loc.Localize("InstallerPluginLoadOrderColumnDefault", "Default");
public static string LoadOrder_ColumnOverride => Loc.Localize("InstallerPluginLoadOrderColumnOverride", "Override");
public static string LoadOrder_ColumnRemove => Loc.Localize("InstallerPluginLoadOrderColumnRemove", "Remove");
public static string LoadOrder_DefaultSync => Loc.Localize("InstallerPluginLoadOrderDefaultSync", "Sync");
public static string LoadOrder_DefaultAsync => Loc.Localize("InstallerPluginLoadOrderDefaultAsync", "Async");
public static string LoadOrder_OverrideDefault => Loc.Localize("InstallerPluginLoadOrderOverrideDefault", "Default");
public static string LoadOrder_OverrideSync => Loc.Localize("InstallerPluginLoadOrderOverrideSync", "Force Sync");
public static string LoadOrder_OverrideAsync => Loc.Localize("InstallerPluginLoadOrderOverrideAsync", "Force Async");
#endregion
#region Search text
public static string TabBody_SearchNoMatching => Loc.Localize("InstallerNoMatching", "No plugins were found matching your search.");

View file

@ -620,6 +620,21 @@ internal class PluginManager : IInternalDisposableService
Log.Information($"============= LoadPluginsAsync({logPrefix}) END =============");
}
async Task LoadPluginsAsyncOrdered(string logPrefix, IEnumerable<PluginDef> pluginDefsList, CancellationToken token)
{
Log.Information($"============= LoadPluginsAsyncOrdered({logPrefix}) START =============");
await Task.Run(
async () =>
{
foreach (var pluginDef in pluginDefsList)
await LoadPluginOnBoot(logPrefix, pluginDef, token).ConfigureAwait(false);
},
token).ConfigureAwait(false);
Log.Information($"============= LoadPluginsAsyncOrdered({logPrefix}) END =============");
}
// Initialize the startup load tracker for all LoadSync plugins
{
this.StartupLoadTracking = new();
@ -629,8 +644,15 @@ internal class PluginManager : IInternalDisposableService
}
}
var syncPlugins = pluginDefs.Where(def => def.Manifest?.LoadSync == true).ToList();
var asyncPlugins = pluginDefs.Where(def => def.Manifest?.LoadSync != true).ToList();
// Respect the original load flavor for ordered plugins.
var (orderedSyncPluginDefs, orderedAsyncPluginDefs) = this.GetOrderedPluginDefsByLoadFlavor(pluginDefs);
var orderedSet = new HashSet<string>(
orderedSyncPluginDefs.Select(def => def.Manifest.InternalName)
.Concat(orderedAsyncPluginDefs.Select(def => def.Manifest.InternalName)),
StringComparer.OrdinalIgnoreCase);
var syncPlugins = pluginDefs.Where(def => def.Manifest?.LoadSync == true && !orderedSet.Contains(def.Manifest.InternalName)).ToList();
var asyncPlugins = pluginDefs.Where(def => def.Manifest?.LoadSync != true && !orderedSet.Contains(def.Manifest.InternalName)).ToList();
var loadTasks = new List<Task>();
var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
@ -638,8 +660,13 @@ internal class PluginManager : IInternalDisposableService
// Load plugins that can be loaded anytime
await LoadPluginsSync(
"AnytimeSync",
syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2),
orderedSyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState == 2)
.Concat(syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2)),
tokenSource.Token);
loadTasks.Add(LoadPluginsAsyncOrdered(
"AnytimeOrderedAsync",
orderedAsyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState == 2),
tokenSource.Token));
loadTasks.Add(LoadPluginsAsync(
"AnytimeAsync",
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2),
@ -656,11 +683,16 @@ internal class PluginManager : IInternalDisposableService
await framework.RunOnTick(
() => LoadPluginsSync(
"FrameworkTickSync",
syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1),
orderedSyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState == 1)
.Concat(syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1)),
tokenSource.Token),
cancellationToken: tokenSource.Token).ConfigureAwait(false);
Log.Verbose("Loaded FrameworkTickSync plugins (LoadRequiredState == 1)");
loadTasks.Add(LoadPluginsAsyncOrdered(
"FrameworkTickOrderedAsync",
orderedAsyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState == 1),
tokenSource.Token));
loadTasks.Add(LoadPluginsAsync(
"FrameworkTickAsync",
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1),
@ -673,11 +705,16 @@ internal class PluginManager : IInternalDisposableService
await framework.RunOnTick(
() => LoadPluginsSync(
"DrawAvailableSync",
syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null),
orderedSyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState is 0 or null)
.Concat(syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)),
tokenSource.Token),
cancellationToken: tokenSource.Token);
Log.Verbose("Loaded DrawAvailableSync plugins (LoadRequiredState == 0 or null)");
loadTasks.Add(LoadPluginsAsyncOrdered(
"DrawAvailableOrderedAsync",
orderedAsyncPluginDefs.Where(def => def.Manifest?.LoadRequiredState is 0 or null),
tokenSource.Token));
loadTasks.Add(LoadPluginsAsync(
"DrawAvailableAsync",
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null),
@ -1362,6 +1399,45 @@ internal class PluginManager : IInternalDisposableService
}
}
private (List<PluginDef> Sync, List<PluginDef> Async) GetOrderedPluginDefsByLoadFlavor(List<PluginDef> pluginDefs)
{
var configuredOrder = this.configuration.PluginLoadOrder;
if (configuredOrder == null || configuredOrder.Count == 0)
return (new List<PluginDef>(), new List<PluginDef>());
var pluginLookup = pluginDefs.ToDictionary(def => def.Manifest.InternalName, StringComparer.OrdinalIgnoreCase);
var overrides = this.configuration.PluginLoadOrderOverrides;
var orderedSync = new List<PluginDef>(configuredOrder.Count);
var orderedAsync = new List<PluginDef>(configuredOrder.Count);
foreach (var internalName in configuredOrder)
{
if (internalName.IsNullOrEmpty())
continue;
if (!pluginLookup.TryGetValue(internalName, out var pluginDef))
continue;
var mode = PluginLoadOrderMode.Default;
if (overrides != null && overrides.TryGetValue(internalName, out var overrideMode))
mode = overrideMode;
var isSync = mode switch
{
PluginLoadOrderMode.ForceSync => true,
PluginLoadOrderMode.ForceAsync => false,
_ => pluginDef.Manifest.LoadSync,
};
if (isSync)
orderedSync.Add(pluginDef);
else
orderedAsync.Add(pluginDef);
}
return (orderedSync, orderedAsync);
}
/// <summary>
/// Check if there are any inconsistencies with our plugins, their IDs, and our profiles.
/// </summary>