mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-01 05:13:40 +01:00
merge net5
This commit is contained in:
commit
23c73da5c8
61 changed files with 2749 additions and 533 deletions
|
|
@ -332,6 +332,11 @@ namespace Dalamud.Configuration.Internal
|
|||
/// </summary>
|
||||
public bool ShowDevBarInfo { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last-used contact details for the plugin feedback form.
|
||||
/// </summary>
|
||||
public string LastFeedbackContactDetails { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Load a configuration from the provided path.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ namespace Dalamud
|
|||
#region Internals
|
||||
|
||||
private readonly ManualResetEvent unloadSignal;
|
||||
private bool hasDisposedPlugins = false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -117,8 +116,6 @@ namespace Dalamud
|
|||
/// </summary>
|
||||
public void DisposePlugins()
|
||||
{
|
||||
this.hasDisposedPlugins = true;
|
||||
|
||||
// this must be done before unloading interface manager, in order to do rebuild
|
||||
// the correct cascaded WndProc (IME -> RawDX11Scene -> Game). Otherwise the game
|
||||
// will not receive any windows messages
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<DalamudVersion>6.4.0.36</DalamudVersion>
|
||||
<DalamudVersion>6.4.0.42</DalamudVersion>
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Reloaded.Hooks" Version="4.0.1" />
|
||||
<PackageReference Include="goaaats.Reloaded.Hooks" Version="4.2.0-goat.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Dalamud.Configuration.Internal;
|
|||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Support;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using Newtonsoft.Json;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
|
|
@ -41,10 +40,8 @@ namespace Dalamud
|
|||
/// <summary>
|
||||
/// A delegate used from VEH handler on exception which CoreCLR will fast fail by default.
|
||||
/// </summary>
|
||||
/// <param name="dumpPath">Path to minidump file created in UTF-16.</param>
|
||||
/// <param name="logPath">Path to log file to create in UTF-16.</param>
|
||||
/// <param name="log">Log text in UTF-16.</param>
|
||||
public delegate void VehDelegate(IntPtr dumpPath, IntPtr logPath, IntPtr log);
|
||||
/// <returns>HGLOBAL for message.</returns>
|
||||
public delegate IntPtr VehDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// Initialize Dalamud.
|
||||
|
|
@ -63,54 +60,19 @@ namespace Dalamud
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show error message along with stack trace and exit.
|
||||
/// Returns stack trace.
|
||||
/// </summary>
|
||||
/// <param name="dumpPath">Path to minidump file created in UTF-16.</param>
|
||||
/// <param name="logPath">Path to log file to create in UTF-16.</param>
|
||||
/// <param name="log">Log text in UTF-16.</param>
|
||||
public static void VehCallback(IntPtr dumpPath, IntPtr logPath, IntPtr log)
|
||||
/// <returns>HGlobal to wchar_t* stack trace c-string.</returns>
|
||||
public static IntPtr VehCallback()
|
||||
{
|
||||
string stackTrace;
|
||||
try
|
||||
{
|
||||
stackTrace = Environment.StackTrace;
|
||||
return Marshal.StringToHGlobalUni(Environment.StackTrace);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
stackTrace = "Fail: " + e.ToString();
|
||||
return Marshal.StringToHGlobalUni("Fail: " + e);
|
||||
}
|
||||
|
||||
var msg = "An error within the game has occurred.\n\n"
|
||||
+ "This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n"
|
||||
+ "Please try \"Start Over\" or \"Download Index Backup\" in TexTools, an integrity check in the XIVLauncher settings, and disabling plugins you don't need.\n\n"
|
||||
+ "The log file is located at:\n"
|
||||
+ "{1}\n\n"
|
||||
+ "Press OK to exit the application.\n\nStack trace:\n{2}";
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(
|
||||
Marshal.PtrToStringUni(logPath),
|
||||
"Stack trace:\n" + stackTrace + "\n\n" + Marshal.PtrToStringUni(log));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
msg += "\n\nAdditionally, failed to write file: " + e.ToString();
|
||||
}
|
||||
|
||||
// Show in another thread to prevent messagebox from pumping messages of current thread.
|
||||
var msgThread = new Thread(() =>
|
||||
{
|
||||
Utility.Util.Fatal(
|
||||
msg.Format(
|
||||
Marshal.PtrToStringUni(dumpPath),
|
||||
Marshal.PtrToStringUni(logPath),
|
||||
stackTrace),
|
||||
"Dalamud Error",
|
||||
false);
|
||||
});
|
||||
msgThread.Start();
|
||||
msgThread.Join();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ namespace Dalamud.Game
|
|||
|
||||
this.hasAutoUpdatedPlugins = true;
|
||||
|
||||
Task.Run(() => pluginManager.UpdatePluginsAsync(!this.configuration.AutoUpdatePlugins)).ContinueWith(task =>
|
||||
Task.Run(() => pluginManager.UpdatePluginsAsync(true, !this.configuration.AutoUpdatePlugins)).ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
|
|
@ -301,11 +301,11 @@ namespace Dalamud.Game
|
|||
}
|
||||
|
||||
var updatedPlugins = task.Result;
|
||||
if (updatedPlugins != null && updatedPlugins.Any())
|
||||
if (updatedPlugins.Any())
|
||||
{
|
||||
if (this.configuration.AutoUpdatePlugins)
|
||||
{
|
||||
PluginManager.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
|
||||
Service<PluginManager>.Get().PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
|
||||
notifications.AddNotification(Loc.Localize("NotificationUpdatedPlugins", "{0} of your plugins were updated.").Format(updatedPlugins.Count), Loc.Localize("NotificationAutoUpdate", "Auto-Update"), NotificationType.Info);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -326,7 +326,9 @@ namespace Dalamud.Game
|
|||
if (insnByte == 0xE8 || insnByte == 0xE9)
|
||||
scanRet = ReadJmpCallSig(scanRet);
|
||||
|
||||
if (this.textCache != null)
|
||||
// If this is below the module, there's bound to be a problem with the sig/resolution... Let's not save it
|
||||
// TODO: THIS IS A HACK! FIX THE ROOT CAUSE!
|
||||
if (this.textCache != null && scanRet.ToInt64() >= this.Module.BaseAddress.ToInt64())
|
||||
{
|
||||
this.textCache[signature] = scanRet.ToInt64() - this.Module.BaseAddress.ToInt64();
|
||||
}
|
||||
|
|
@ -377,7 +379,7 @@ namespace Dalamud.Game
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, "Failed to save cache to {0}", this.cacheFile);
|
||||
Log.Warning(e, "Failed to save cache to {CachePath}", this.cacheFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +534,17 @@ namespace Dalamud.Game
|
|||
return;
|
||||
}
|
||||
|
||||
this.textCache = JsonConvert.DeserializeObject<ConcurrentDictionary<string, long>>(File.ReadAllText(this.cacheFile.FullName)) ?? new ConcurrentDictionary<string, long>();
|
||||
try
|
||||
{
|
||||
this.textCache =
|
||||
JsonConvert.DeserializeObject<ConcurrentDictionary<string, long>>(
|
||||
File.ReadAllText(this.cacheFile.FullName)) ?? new ConcurrentDictionary<string, long>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.textCache = new ConcurrentDictionary<string, long>();
|
||||
Log.Error(ex, "Couldn't load cached sigs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Hooking.Internal;
|
||||
|
||||
namespace Dalamud.Hooking
|
||||
|
|
@ -59,6 +60,9 @@ namespace Dalamud.Hooking
|
|||
[Obsolete("Use Hook<T>.FromAddress instead.")]
|
||||
private Hook(IntPtr address, T detour, bool useMinHook, Assembly callingAssembly)
|
||||
{
|
||||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
useMinHook = true;
|
||||
|
||||
address = HookManager.FollowJmp(address);
|
||||
if (useMinHook)
|
||||
this.compatHookImpl = new MinHookHook<T>(address, detour, callingAssembly);
|
||||
|
|
@ -214,6 +218,9 @@ namespace Dalamud.Hooking
|
|||
/// <returns>The hook with the supplied parameters.</returns>
|
||||
public static Hook<T> FromSymbol(string moduleName, string exportName, T detour, bool useMinHook)
|
||||
{
|
||||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
useMinHook = true;
|
||||
|
||||
var moduleHandle = NativeFunctions.GetModuleHandleW(moduleName);
|
||||
if (moduleHandle == IntPtr.Zero)
|
||||
throw new Exception($"Could not get a handle to module {moduleName}");
|
||||
|
|
@ -240,6 +247,9 @@ namespace Dalamud.Hooking
|
|||
/// <returns>The hook with the supplied parameters.</returns>
|
||||
public static Hook<T> FromAddress(IntPtr procAddress, T detour, bool useMinHook = false)
|
||||
{
|
||||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
useMinHook = true;
|
||||
|
||||
procAddress = HookManager.FollowJmp(procAddress);
|
||||
if (useMinHook)
|
||||
return new MinHookHook<T>(procAddress, detour, Assembly.GetCallingAssembly());
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ namespace Dalamud.Hooking.Internal
|
|||
return address;
|
||||
}
|
||||
|
||||
if (address.ToInt64() <= 0)
|
||||
throw new InvalidOperationException($"Address was <= 0, this can't be happening?! ({address:X})");
|
||||
|
||||
var bytes = MemoryHelper.ReadRaw(address, 8);
|
||||
|
||||
var codeReader = new ByteArrayCodeReader(bytes);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ namespace Dalamud.Hooking.Internal
|
|||
}
|
||||
|
||||
this.hookImpl = ReloadedHooks.Instance.CreateHook<T>(detour, address.ToInt64());
|
||||
this.hookImpl.Activate();
|
||||
this.hookImpl.Disable();
|
||||
|
||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
||||
}
|
||||
|
|
@ -75,9 +77,6 @@ namespace Dalamud.Hooking.Internal
|
|||
|
||||
lock (HookManager.HookEnableSyncRoot)
|
||||
{
|
||||
if (!this.hookImpl.IsHookActivated)
|
||||
this.hookImpl.Activate();
|
||||
|
||||
if (!this.hookImpl.IsHookEnabled)
|
||||
this.hookImpl.Enable();
|
||||
}
|
||||
|
|
|
|||
71
Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs
Normal file
71
Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System.Numerics;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Component for toggle buttons.
|
||||
/// </summary>
|
||||
public static partial class ImGuiComponents
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw a toggle button.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the button.</param>
|
||||
/// <param name="v">The state of the switch.</param>
|
||||
/// <returns>If the button has been interacted with this frame.</returns>
|
||||
public static bool ToggleButton(string id, ref bool v)
|
||||
{
|
||||
var colors = ImGui.GetStyle().Colors;
|
||||
var p = ImGui.GetCursorScreenPos();
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
var height = ImGui.GetFrameHeight();
|
||||
var width = height * 1.55f;
|
||||
var radius = height * 0.50f;
|
||||
|
||||
// TODO: animate
|
||||
|
||||
var changed = false;
|
||||
ImGui.InvisibleButton(id, new Vector2(width, height));
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
v = !v;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.ButtonActive] : new Vector4(0.78f, 0.78f, 0.78f, 1.0f)), height * 0.5f);
|
||||
else
|
||||
drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.Button] * 0.6f : new Vector4(0.35f, 0.35f, 0.35f, 1.0f)), height * 0.50f);
|
||||
drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1)));
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a disabled toggle button.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the button.</param>
|
||||
/// <param name="v">The state of the switch.</param>
|
||||
public static void DisabledToggleButton(string id, bool v)
|
||||
{
|
||||
var colors = ImGui.GetStyle().Colors;
|
||||
var p = ImGui.GetCursorScreenPos();
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
var height = ImGui.GetFrameHeight();
|
||||
var width = height * 1.55f;
|
||||
var radius = height * 0.50f;
|
||||
|
||||
// TODO: animate
|
||||
ImGui.InvisibleButton(id, new Vector2(width, height));
|
||||
|
||||
var dimFactor = 0.5f;
|
||||
|
||||
drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(v ? colors[(int)ImGuiCol.Button] * dimFactor : new Vector4(0.55f, 0.55f, 0.55f, 1.0f) * dimFactor), height * 0.50f);
|
||||
drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1) * dimFactor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Utility.Timing;
|
||||
using ImGuiNET;
|
||||
|
|
@ -184,16 +185,9 @@ namespace Dalamud.Interface.GameFonts
|
|||
needRebuild = !this.fonts.ContainsKey(style);
|
||||
if (needRebuild)
|
||||
{
|
||||
if (interfaceManager.IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
|
||||
{
|
||||
Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString());
|
||||
this.EnsureFont(style);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("[GameFontManager] NewFontRef: Calling RebuildFonts because {0} has been requested.", style.ToString());
|
||||
interfaceManager.RebuildFonts();
|
||||
}
|
||||
Log.Information("[GameFontManager] NewFontRef: Queueing RebuildFonts because {0} has been requested.", style.ToString());
|
||||
Service<Framework>.GetAsync()
|
||||
.ContinueWith(task => task.Result.RunOnTick(() => interfaceManager.RebuildFonts()));
|
||||
}
|
||||
|
||||
return new(this, style);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ using System.Diagnostics;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
|
||||
namespace Dalamud.Interface
|
||||
{
|
||||
|
|
@ -222,6 +224,56 @@ namespace Dalamud.Interface
|
|||
target.Value!.BuildLookupTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a VirtualKey keycode to an ImGuiKey enum value.
|
||||
/// </summary>
|
||||
/// <param name="key">The VirtualKey value to retrieve the ImGuiKey counterpart for.</param>
|
||||
/// <returns>The ImGuiKey that corresponds to this VirtualKey, or <c>ImGuiKey.None</c> otherwise.</returns>
|
||||
public static ImGuiKey VirtualKeyToImGuiKey(VirtualKey key)
|
||||
{
|
||||
return ImGui_Input_Impl_Direct.VirtualKeyToImGuiKey((int)key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map an ImGuiKey enum value to a VirtualKey code.
|
||||
/// </summary>
|
||||
/// <param name="key">The ImGuiKey value to retrieve the VirtualKey counterpart for.</param>
|
||||
/// <returns>The VirtualKey that corresponds to this ImGuiKey, or <c>VirtualKey.NO_KEY</c> otherwise.</returns>
|
||||
public static VirtualKey ImGuiKeyToVirtualKey(ImGuiKey key)
|
||||
{
|
||||
return (VirtualKey)ImGui_Input_Impl_Direct.ImGuiKeyToVirtualKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show centered text.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to show.</param>
|
||||
public static void CenteredText(string text)
|
||||
{
|
||||
CenterCursorForText(text);
|
||||
ImGui.TextUnformatted(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Center the ImGui cursor for a certain text.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to center for.</param>
|
||||
public static void CenterCursorForText(string text)
|
||||
{
|
||||
var textWidth = ImGui.CalcTextSize(text).X;
|
||||
CenterCursorFor((int)textWidth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Center the ImGui cursor for an item with a certain width.
|
||||
/// </summary>
|
||||
/// <param name="itemWidth">The width to center for.</param>
|
||||
public static void CenterCursorFor(int itemWidth)
|
||||
{
|
||||
var window = (int)ImGui.GetWindowWidth();
|
||||
ImGui.SetCursorPosX((window / 2) - (itemWidth / 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get data needed for each new frame.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -72,6 +72,18 @@ namespace Dalamud.Interface.Internal
|
|||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xlstats", new CommandInfo(this.OnTogglePluginStats)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudPluginStats", "Draw plugin statistics window"),
|
||||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xlbranch", new CommandInfo(this.OnToggleBranchSwitcher)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudBranchSwitcher", "Draw branch switcher"),
|
||||
ShowInHelp = false,
|
||||
});
|
||||
|
||||
commandManager.AddHandler("/xldata", new CommandInfo(this.OnDebugDrawDataMenu)
|
||||
{
|
||||
HelpMessage = Loc.Localize("DalamudDevDataMenuHelp", "Draw dev data menu DEBUG. Usage: /xldata [Data Dropdown Type]"),
|
||||
|
|
@ -267,6 +279,16 @@ namespace Dalamud.Interface.Internal
|
|||
Service<DalamudInterface>.Get().ToggleDevMenu();
|
||||
}
|
||||
|
||||
private void OnTogglePluginStats(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().TogglePluginStatsWindow();
|
||||
}
|
||||
|
||||
private void OnToggleBranchSwitcher(string command, string arguments)
|
||||
{
|
||||
Service<DalamudInterface>.Get().ToggleBranchSwitcher();
|
||||
}
|
||||
|
||||
private void OnDebugDrawDataMenu(string command, string arguments)
|
||||
{
|
||||
var dalamudInterface = Service<DalamudInterface>.Get();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Dalamud.Configuration.Internal;
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
using Dalamud.Interface.Internal.Windows;
|
||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||
|
|
@ -249,6 +250,11 @@ namespace Dalamud.Interface.Internal
|
|||
/// </summary>
|
||||
public void OpenPluginInstaller() => this.pluginWindow.IsOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="PluginInstallerWindow"/> on the plugin changelogs.
|
||||
/// </summary>
|
||||
public void OpenPluginInstallerPluginChangelogs() => this.pluginWindow.OpenPluginChangelogs();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the <see cref="SettingsWindow"/>.
|
||||
/// </summary>
|
||||
|
|
@ -369,12 +375,17 @@ namespace Dalamud.Interface.Internal
|
|||
/// Toggles the <see cref="StyleEditorWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleStyleEditorWindow() => this.selfTestWindow.Toggle();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="ProfilerWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleProfilerWindow() => this.profilerWindow.Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the <see cref="BranchSwitcherWindow"/>.
|
||||
/// </summary>
|
||||
public void ToggleBranchSwitcher() => this.branchSwitcherWindow.Toggle();
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnDraw()
|
||||
|
|
@ -438,12 +449,9 @@ namespace Dalamud.Interface.Internal
|
|||
|
||||
if (!this.isImGuiDrawDevMenu && !condition.Any())
|
||||
{
|
||||
var config = Service<DalamudConfiguration>.Get();
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 0, 0, 1));
|
||||
ImGui.PushStyleColor(ImGuiCol.TextSelectedBg, new Vector4(0, 0, 0, 1));
|
||||
ImGui.PushStyleColor(ImGuiCol.Border, new Vector4(0, 0, 0, 1));
|
||||
ImGui.PushStyleColor(ImGuiCol.BorderShadow, new Vector4(0, 0, 0, 1));
|
||||
|
|
@ -467,8 +475,26 @@ namespace Dalamud.Interface.Internal
|
|||
ImGui.End();
|
||||
}
|
||||
|
||||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
{
|
||||
ImGui.SetNextWindowPos(windowPos, ImGuiCond.Always);
|
||||
ImGui.SetNextWindowBgAlpha(1);
|
||||
|
||||
if (ImGui.Begin(
|
||||
"Disclaimer",
|
||||
ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBackground |
|
||||
ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove |
|
||||
ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoMouseInputs |
|
||||
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoSavedSettings))
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed, "Is force MinHook!");
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
ImGui.PopStyleVar(4);
|
||||
ImGui.PopStyleColor(8);
|
||||
ImGui.PopStyleColor(7);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -594,6 +620,16 @@ namespace Dalamud.Interface.Internal
|
|||
Service<Dalamud>.Get().Unload();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Restart game"))
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern void RaiseException(uint dwExceptionCode, uint dwExceptionFlags, uint nNumberOfArguments, IntPtr lpArguments);
|
||||
|
||||
RaiseException(0x12345678, 0, 0, IntPtr.Zero);
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Kill game"))
|
||||
{
|
||||
Process.GetCurrentProcess().Kill();
|
||||
|
|
|
|||
|
|
@ -1030,22 +1030,43 @@ namespace Dalamud.Interface.Internal
|
|||
|
||||
if (gamepadEnabled && (ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.NavEnableGamepad) > 0)
|
||||
{
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Activate] = gamepadState.Raw(GamepadButtons.South);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Cancel] = gamepadState.Raw(GamepadButtons.East);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Input] = gamepadState.Raw(GamepadButtons.North);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Menu] = gamepadState.Raw(GamepadButtons.West);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadLeft] = gamepadState.Raw(GamepadButtons.DpadLeft);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadRight] = gamepadState.Raw(GamepadButtons.DpadRight);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadUp] = gamepadState.Raw(GamepadButtons.DpadUp);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadDown] = gamepadState.Raw(GamepadButtons.DpadDown);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickLeft] = gamepadState.LeftStickLeft;
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickRight] = gamepadState.LeftStickRight;
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickUp] = gamepadState.LeftStickUp;
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickDown] = gamepadState.LeftStickDown;
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.FocusPrev] = gamepadState.Raw(GamepadButtons.L1);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.FocusNext] = gamepadState.Raw(GamepadButtons.R1);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.TweakSlow] = gamepadState.Raw(GamepadButtons.L2);
|
||||
ImGui.GetIO().NavInputs[(int)ImGuiNavInput.TweakFast] = gamepadState.Raw(GamepadButtons.R2);
|
||||
var northButton = gamepadState.Raw(GamepadButtons.North) != 0;
|
||||
var eastButton = gamepadState.Raw(GamepadButtons.East) != 0;
|
||||
var southButton = gamepadState.Raw(GamepadButtons.South) != 0;
|
||||
var westButton = gamepadState.Raw(GamepadButtons.West) != 0;
|
||||
var dPadUp = gamepadState.Raw(GamepadButtons.DpadUp) != 0;
|
||||
var dPadRight = gamepadState.Raw(GamepadButtons.DpadRight) != 0;
|
||||
var dPadDown = gamepadState.Raw(GamepadButtons.DpadDown) != 0;
|
||||
var dPadLeft = gamepadState.Raw(GamepadButtons.DpadLeft) != 0;
|
||||
var leftStickUp = gamepadState.LeftStickUp;
|
||||
var leftStickRight = gamepadState.LeftStickRight;
|
||||
var leftStickDown = gamepadState.LeftStickDown;
|
||||
var leftStickLeft = gamepadState.LeftStickLeft;
|
||||
var l1Button = gamepadState.Raw(GamepadButtons.L1) != 0;
|
||||
var l2Button = gamepadState.Raw(GamepadButtons.L2) != 0;
|
||||
var r1Button = gamepadState.Raw(GamepadButtons.R1) != 0;
|
||||
var r2Button = gamepadState.Raw(GamepadButtons.R2) != 0;
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
io.AddKeyEvent(ImGuiKey.GamepadFaceUp, northButton);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadFaceRight, eastButton);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadFaceDown, southButton);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadFaceLeft, westButton);
|
||||
|
||||
io.AddKeyEvent(ImGuiKey.GamepadDpadUp, dPadUp);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadDpadRight, dPadRight);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadDpadDown, dPadDown);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadDpadLeft, dPadLeft);
|
||||
|
||||
io.AddKeyAnalogEvent(ImGuiKey.GamepadLStickUp, leftStickUp != 0, leftStickUp);
|
||||
io.AddKeyAnalogEvent(ImGuiKey.GamepadLStickRight, leftStickRight != 0, leftStickRight);
|
||||
io.AddKeyAnalogEvent(ImGuiKey.GamepadLStickDown, leftStickDown != 0, leftStickDown);
|
||||
io.AddKeyAnalogEvent(ImGuiKey.GamepadLStickLeft, leftStickLeft != 0, leftStickLeft);
|
||||
|
||||
io.AddKeyEvent(ImGuiKey.GamepadL1, l1Button);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadL2, l2Button);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadR1, r1Button);
|
||||
io.AddKeyEvent(ImGuiKey.GamepadR2, r2Button);
|
||||
|
||||
if (gamepadState.Pressed(GamepadButtons.R3) > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
public const int PluginIconHeight = 512;
|
||||
|
||||
private const string MainRepoImageUrl = "https://raw.githubusercontent.com/goatcorp/DalamudPlugins/api6/{0}/{1}/images/{2}";
|
||||
private const string MainRepoDip17ImageUrl = "https://raw.githubusercontent.com/goatcorp/PluginDistD17/main/{0}/{1}/images/{2}";
|
||||
|
||||
private readonly BlockingCollection<Tuple<ulong, Func<Task>>> downloadQueue = new();
|
||||
private readonly BlockingCollection<Func<Task>> loadQueue = new();
|
||||
|
|
@ -54,6 +55,7 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
private readonly ConcurrentDictionary<string, TextureWrap?[]?> pluginImagesMap = new();
|
||||
|
||||
private readonly Task<TextureWrap> emptyTextureTask;
|
||||
private readonly Task<TextureWrap> disabledIconTask;
|
||||
private readonly Task<TextureWrap> defaultIconTask;
|
||||
private readonly Task<TextureWrap> troubleIconTask;
|
||||
private readonly Task<TextureWrap> updateIconTask;
|
||||
|
|
@ -71,6 +73,7 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
|
||||
this.emptyTextureTask = imwst.ContinueWith(task => task.Result.Manager.LoadImageRaw(new byte[64], 8, 8, 4)!);
|
||||
this.defaultIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "defaultIcon.png"))) ?? this.emptyTextureTask).Unwrap();
|
||||
this.disabledIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "disabledIcon.png"))) ?? this.emptyTextureTask).Unwrap();
|
||||
this.troubleIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "troubleIcon.png"))) ?? this.emptyTextureTask).Unwrap();
|
||||
this.updateIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "updateIcon.png"))) ?? this.emptyTextureTask).Unwrap();
|
||||
this.installedIconTask = imwst.ContinueWith(task => TaskWrapIfNonNull(task.Result.Manager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "installedIcon.png"))) ?? this.emptyTextureTask).Unwrap();
|
||||
|
|
@ -91,6 +94,13 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
? this.emptyTextureTask.Result
|
||||
: this.emptyTextureTask.GetAwaiter().GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default plugin icon.
|
||||
/// </summary>
|
||||
public TextureWrap DisabledIcon => this.disabledIconTask.IsCompleted
|
||||
? this.disabledIconTask.Result
|
||||
: this.disabledIconTask.GetAwaiter().GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default plugin icon.
|
||||
/// </summary>
|
||||
|
|
@ -644,6 +654,9 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
if (isThirdParty)
|
||||
return manifest.IconUrl;
|
||||
|
||||
if (manifest.IsDip17Plugin)
|
||||
return MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, "icon.png");
|
||||
|
||||
return MainRepoImageUrl.Format(isTesting ? "testing" : "plugins", manifest.InternalName, "icon.png");
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +676,14 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
var output = new List<string>();
|
||||
for (var i = 1; i <= 5; i++)
|
||||
{
|
||||
output.Add(MainRepoImageUrl.Format(isTesting ? "testing" : "plugins", manifest.InternalName, $"image{i}.png"));
|
||||
if (manifest.IsDip17Plugin)
|
||||
{
|
||||
output.Add(MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, $"image{i}.png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(MainRepoImageUrl.Format(isTesting ? "testing" : "plugins", manifest.InternalName, $"image{i}.png"));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (plugin.Manifest.Changelog.IsNullOrEmpty())
|
||||
throw new ArgumentException("Manifest has no changelog.");
|
||||
|
||||
var version = plugin.AssemblyName?.Version;
|
||||
version ??= plugin.Manifest.Testing
|
||||
? plugin.Manifest.TestingAssemblyVersion
|
||||
: plugin.Manifest.AssemblyVersion;
|
||||
var version = plugin.Manifest.EffectiveVersion;
|
||||
|
||||
this.Version = version!.ToString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using Dalamud.Game.Command;
|
|||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Style;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin;
|
||||
|
|
@ -59,14 +60,17 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
private bool errorModalDrawing = true;
|
||||
private bool errorModalOnNextFrame = false;
|
||||
private string errorModalMessage = string.Empty;
|
||||
private TaskCompletionSource? errorModalTaskCompletionSource;
|
||||
|
||||
private bool feedbackModalDrawing = true;
|
||||
private bool feedbackModalOnNextFrame = false;
|
||||
private bool feedbackModalOnNextFrameDontClear = false;
|
||||
private string feedbackModalBody = string.Empty;
|
||||
private string feedbackModalContact = string.Empty;
|
||||
private bool feedbackModalIncludeException = false;
|
||||
private PluginManifest? feedbackPlugin = null;
|
||||
private bool feedbackIsTesting = false;
|
||||
private bool feedbackIsAnonymous = false;
|
||||
|
||||
private int updatePluginCount = 0;
|
||||
private List<PluginUpdateStatus>? updatedPlugins;
|
||||
|
|
@ -83,6 +87,9 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
private OperationStatus installStatus = OperationStatus.Idle;
|
||||
private OperationStatus updateStatus = OperationStatus.Idle;
|
||||
private OperationStatus enableDisableStatus = OperationStatus.Idle;
|
||||
|
||||
private LoadingIndicatorKind loadingIndicatorKind = LoadingIndicatorKind.Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginInstallerWindow"/> class.
|
||||
|
|
@ -130,6 +137,17 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
Complete,
|
||||
}
|
||||
|
||||
private enum LoadingIndicatorKind
|
||||
{
|
||||
Unknown,
|
||||
EnablingSingle,
|
||||
DisablingSingle,
|
||||
UpdatingSingle,
|
||||
UpdatingAll,
|
||||
Installing,
|
||||
Manager,
|
||||
}
|
||||
|
||||
private enum PluginSortKind
|
||||
{
|
||||
Alphabetical,
|
||||
|
|
@ -139,6 +157,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
NotInstalled,
|
||||
}
|
||||
|
||||
private bool AnyOperationInProgress => this.installStatus == OperationStatus.InProgress ||
|
||||
this.updateStatus == OperationStatus.InProgress ||
|
||||
this.enableDisableStatus == OperationStatus.InProgress;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
@ -184,6 +206,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
this.DrawFooter();
|
||||
this.DrawErrorModal();
|
||||
this.DrawFeedbackModal();
|
||||
this.DrawProgressOverlay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -194,6 +217,119 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
this.imageCache.ClearIconCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the window on the plugin changelogs.
|
||||
/// </summary>
|
||||
public void OpenPluginChangelogs()
|
||||
{
|
||||
this.categoryManager.CurrentGroupIdx = 3;
|
||||
this.categoryManager.CurrentCategoryIdx = 2;
|
||||
this.IsOpen = true;
|
||||
}
|
||||
|
||||
private void DrawProgressOverlay()
|
||||
{
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
|
||||
var isWaitingManager = !pluginManager.PluginsReady ||
|
||||
!pluginManager.ReposReady;
|
||||
var isLoading = this.AnyOperationInProgress ||
|
||||
isWaitingManager;
|
||||
|
||||
if (isWaitingManager)
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.Manager;
|
||||
|
||||
if (!isLoading)
|
||||
return;
|
||||
|
||||
ImGui.SetCursorPos(Vector2.Zero);
|
||||
|
||||
var windowSize = ImGui.GetWindowSize();
|
||||
var titleHeight = ImGui.GetFontSize() + (ImGui.GetStyle().FramePadding.Y * 2);
|
||||
|
||||
if (ImGui.BeginChild("###installerLoadingFrame", new Vector2(-1, -1), false))
|
||||
{
|
||||
ImGui.GetWindowDrawList().PushClipRectFullScreen();
|
||||
ImGui.GetWindowDrawList().AddRectFilled(
|
||||
ImGui.GetWindowPos() + new Vector2(0, titleHeight),
|
||||
ImGui.GetWindowPos() + windowSize,
|
||||
0xCC000000,
|
||||
ImGui.GetStyle().WindowRounding,
|
||||
ImDrawFlags.RoundCornersBottom);
|
||||
ImGui.PopClipRect();
|
||||
|
||||
ImGui.SetCursorPosY(windowSize.Y / 2);
|
||||
|
||||
switch (this.loadingIndicatorKind)
|
||||
{
|
||||
case LoadingIndicatorKind.Unknown:
|
||||
ImGuiHelpers.CenteredText("Doing something, not sure what!");
|
||||
break;
|
||||
case LoadingIndicatorKind.EnablingSingle:
|
||||
ImGuiHelpers.CenteredText("Enabling plugin...");
|
||||
break;
|
||||
case LoadingIndicatorKind.DisablingSingle:
|
||||
ImGuiHelpers.CenteredText("Disabling plugin...");
|
||||
break;
|
||||
case LoadingIndicatorKind.UpdatingSingle:
|
||||
ImGuiHelpers.CenteredText("Updating plugin...");
|
||||
break;
|
||||
case LoadingIndicatorKind.UpdatingAll:
|
||||
ImGuiHelpers.CenteredText("Updating plugins...");
|
||||
break;
|
||||
case LoadingIndicatorKind.Installing:
|
||||
ImGuiHelpers.CenteredText("Installing plugin...");
|
||||
break;
|
||||
case LoadingIndicatorKind.Manager:
|
||||
{
|
||||
if (pluginManager.PluginsReady && !pluginManager.ReposReady)
|
||||
{
|
||||
ImGuiHelpers.CenteredText("Loading repositories...");
|
||||
}
|
||||
else if (!pluginManager.PluginsReady && pluginManager.ReposReady)
|
||||
{
|
||||
ImGuiHelpers.CenteredText("Loading installed plugins...");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiHelpers.CenteredText("Loading repositories and plugins...");
|
||||
}
|
||||
|
||||
var currentProgress = 0;
|
||||
var total = 0;
|
||||
|
||||
var pendingRepos = pluginManager.Repos.ToArray()
|
||||
.Where(x => (x.State != PluginRepositoryState.Success &&
|
||||
x.State != PluginRepositoryState.Fail) &&
|
||||
x.IsEnabled)
|
||||
.ToArray();
|
||||
var allRepoCount =
|
||||
pluginManager.Repos.Count(x => x.State != PluginRepositoryState.Fail && x.IsEnabled);
|
||||
|
||||
foreach (var repo in pendingRepos)
|
||||
{
|
||||
ImGuiHelpers.CenteredText($"{repo.PluginMasterUrl}: {repo.State}");
|
||||
}
|
||||
|
||||
currentProgress += allRepoCount - pendingRepos.Length;
|
||||
total += allRepoCount;
|
||||
|
||||
if (currentProgress != total)
|
||||
{
|
||||
ImGui.SetCursorPosX(windowSize.X / 3);
|
||||
ImGui.ProgressBar(currentProgress / (float)total, new Vector2(windowSize.X / 3, 50));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
var style = ImGui.GetStyle();
|
||||
|
|
@ -201,8 +337,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - (5 * ImGuiHelpers.GlobalScale));
|
||||
|
||||
var searchInputWidth = 240 * ImGuiHelpers.GlobalScale;
|
||||
var searchClearButtonWidth = 40 * ImGuiHelpers.GlobalScale;
|
||||
var searchInputWidth = 180 * ImGuiHelpers.GlobalScale;
|
||||
var searchClearButtonWidth = 25 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
var sortByText = Locs.SortBy_Label;
|
||||
var sortByTextWidth = ImGui.CalcTextSize(sortByText).X;
|
||||
|
|
@ -225,9 +361,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
ImGui.SameLine();
|
||||
|
||||
// Shift down a little to align with the middle of the header text
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (headerTextSize.Y / 4) - 2);
|
||||
var downShift = ImGui.GetCursorPosY() + (headerTextSize.Y / 4) - 2;
|
||||
ImGui.SetCursorPosY(downShift);
|
||||
|
||||
ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - style.ItemSpacing.X - searchInputWidth - searchClearButtonWidth);
|
||||
ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - (style.ItemSpacing.X * 2) - searchInputWidth - searchClearButtonWidth);
|
||||
|
||||
var searchTextChanged = false;
|
||||
ImGui.SetNextItemWidth(searchInputWidth);
|
||||
|
|
@ -238,6 +375,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
100);
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosY(downShift);
|
||||
|
||||
ImGui.SetNextItemWidth(searchClearButtonWidth);
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Times))
|
||||
|
|
@ -250,7 +388,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
this.UpdateCategoriesOnSearchChange();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosX(windowSize.X - sortSelectWidth);
|
||||
ImGui.SetCursorPosY(downShift);
|
||||
ImGui.SetNextItemWidth(selectableWidth);
|
||||
if (ImGui.BeginCombo(sortByText, this.filterText, ImGuiComboFlags.NoArrowButton))
|
||||
{
|
||||
|
|
@ -336,8 +474,9 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (ImGui.Button(Locs.FooterButton_UpdatePlugins))
|
||||
{
|
||||
this.updateStatus = OperationStatus.InProgress;
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.UpdatingAll;
|
||||
|
||||
Task.Run(() => pluginManager.UpdatePluginsAsync())
|
||||
Task.Run(() => pluginManager.UpdatePluginsAsync(true, false))
|
||||
.ContinueWith(task =>
|
||||
{
|
||||
this.updateStatus = OperationStatus.Complete;
|
||||
|
|
@ -372,7 +511,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
if (this.updatePluginCount > 0)
|
||||
{
|
||||
PluginManager.PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
|
||||
Service<PluginManager>.Get().PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox);
|
||||
notifications.AddNotification(Locs.Notifications_UpdatesInstalled(this.updatePluginCount), Locs.Notifications_UpdatesInstalledTitle, NotificationType.Success);
|
||||
|
||||
var installedGroupIdx = this.categoryManager.GroupList.TakeWhile(
|
||||
|
|
@ -404,6 +543,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (ImGui.Button(Locs.ErrorModalButton_Ok, new Vector2(buttonWidth, 40)))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
errorModalTaskCompletionSource?.SetResult();
|
||||
}
|
||||
|
||||
ImGui.EndPopup();
|
||||
|
|
@ -446,7 +586,43 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGui.InputText(Locs.FeedbackModal_ContactInformation, ref this.feedbackModalContact, 100);
|
||||
if (ImGui.Checkbox(Locs.FeedbackModal_ContactAnonymous, ref this.feedbackIsAnonymous))
|
||||
{
|
||||
if (this.feedbackIsAnonymous)
|
||||
this.feedbackModalContact = string.Empty;
|
||||
}
|
||||
|
||||
if (this.feedbackIsAnonymous)
|
||||
{
|
||||
ImGui.BeginDisabled();
|
||||
ImGui.InputText(Locs.FeedbackModal_ContactInformation, ref this.feedbackModalContact, 0);
|
||||
ImGui.EndDisabled();
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
|
||||
ImGui.Text(Locs.FeedbackModal_ContactAnonymousWarning);
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.InputText(Locs.FeedbackModal_ContactInformation, ref this.feedbackModalContact, 100);
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button(Locs.FeedbackModal_ContactInformationDiscordButton))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(Locs.FeedbackModal_ContactInformationDiscordUrl)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
});
|
||||
}
|
||||
|
||||
ImGui.Text(Locs.FeedbackModal_ContactInformationHelp);
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
|
||||
ImGui.Text(Locs.FeedbackModal_ContactInformationWarning);
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGui.Checkbox(Locs.FeedbackModal_IncludeLastError, ref this.feedbackModalIncludeException);
|
||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Locs.FeedbackModal_IncludeLastErrorHint);
|
||||
|
|
@ -460,25 +636,57 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
if (ImGui.Button(Locs.ErrorModalButton_Ok, new Vector2(buttonWidth, 40)))
|
||||
{
|
||||
if (this.feedbackPlugin != null)
|
||||
if (!this.feedbackIsAnonymous && string.IsNullOrWhiteSpace(this.feedbackModalContact))
|
||||
{
|
||||
Task.Run(async () => await BugBait.SendFeedback(this.feedbackPlugin, this.feedbackIsTesting, this.feedbackModalBody, this.feedbackModalContact, this.feedbackModalIncludeException))
|
||||
.ContinueWith(
|
||||
t =>
|
||||
{
|
||||
var notif = Service<NotificationManager>.Get();
|
||||
if (t.IsCanceled || t.IsFaulted)
|
||||
notif.AddNotification(Locs.FeedbackModal_NotificationError, Locs.FeedbackModal_Title, NotificationType.Error);
|
||||
else
|
||||
notif.AddNotification(Locs.FeedbackModal_NotificationSuccess, Locs.FeedbackModal_Title, NotificationType.Success);
|
||||
});
|
||||
this.ShowErrorModal(Locs.FeedbackModal_ContactInformationRequired)
|
||||
.ContinueWith(_ =>
|
||||
{
|
||||
this.feedbackModalOnNextFrameDontClear = true;
|
||||
this.feedbackModalOnNextFrame = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("FeedbackPlugin was null.");
|
||||
}
|
||||
if (this.feedbackPlugin != null)
|
||||
{
|
||||
Task.Run(async () => await BugBait.SendFeedback(
|
||||
this.feedbackPlugin,
|
||||
this.feedbackIsTesting,
|
||||
this.feedbackModalBody,
|
||||
this.feedbackModalContact,
|
||||
this.feedbackModalIncludeException))
|
||||
.ContinueWith(
|
||||
t =>
|
||||
{
|
||||
var notif = Service<NotificationManager>.Get();
|
||||
if (t.IsCanceled || t.IsFaulted)
|
||||
{
|
||||
notif.AddNotification(
|
||||
Locs.FeedbackModal_NotificationError,
|
||||
Locs.FeedbackModal_Title,
|
||||
NotificationType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
notif.AddNotification(
|
||||
Locs.FeedbackModal_NotificationSuccess,
|
||||
Locs.FeedbackModal_Title,
|
||||
NotificationType.Success);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("FeedbackPlugin was null.");
|
||||
}
|
||||
|
||||
ImGui.CloseCurrentPopup();
|
||||
if (!string.IsNullOrWhiteSpace(this.feedbackModalContact))
|
||||
{
|
||||
Service<DalamudConfiguration>.Get().LastFeedbackContactDetails = this.feedbackModalContact;
|
||||
}
|
||||
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndPopup();
|
||||
|
|
@ -489,9 +697,17 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
ImGui.OpenPopup(modalTitle);
|
||||
this.feedbackModalOnNextFrame = false;
|
||||
this.feedbackModalDrawing = true;
|
||||
this.feedbackModalBody = string.Empty;
|
||||
this.feedbackModalContact = string.Empty;
|
||||
this.feedbackModalIncludeException = false;
|
||||
if (!this.feedbackModalOnNextFrameDontClear)
|
||||
{
|
||||
this.feedbackModalBody = string.Empty;
|
||||
this.feedbackModalContact = Service<DalamudConfiguration>.Get().LastFeedbackContactDetails;
|
||||
this.feedbackModalIncludeException = false;
|
||||
this.feedbackIsAnonymous = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.feedbackModalOnNextFrameDontClear = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1104,6 +1320,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
ImGui.SetCursorPos(startCursor);
|
||||
|
||||
var pluginDisabled = plugin is { IsDisabled: true };
|
||||
|
||||
var iconSize = ImGuiHelpers.ScaledVector2(64, 64);
|
||||
var cursorBeforeImage = ImGui.GetCursorPos();
|
||||
var rectOffset = ImGui.GetWindowContentRegionMin() + ImGui.GetWindowPos();
|
||||
|
|
@ -1116,7 +1334,18 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
iconTex = cachedIconTex;
|
||||
}
|
||||
|
||||
if (pluginDisabled)
|
||||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
|
||||
}
|
||||
|
||||
ImGui.Image(iconTex.ImGuiHandle, iconSize);
|
||||
|
||||
if (pluginDisabled)
|
||||
{
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPos(cursorBeforeImage);
|
||||
}
|
||||
|
|
@ -1125,8 +1354,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
if (updateAvailable)
|
||||
ImGui.Image(this.imageCache.UpdateIcon.ImGuiHandle, iconSize);
|
||||
else if (trouble)
|
||||
else if (trouble && !pluginDisabled)
|
||||
ImGui.Image(this.imageCache.TroubleIcon.ImGuiHandle, iconSize);
|
||||
else if (pluginDisabled)
|
||||
ImGui.Image(this.imageCache.DisabledIcon.ImGuiHandle, iconSize);
|
||||
else if (isLoaded && isThirdParty)
|
||||
ImGui.Image(this.imageCache.ThirdInstalledIcon.ImGuiHandle, iconSize);
|
||||
else if (isThirdParty)
|
||||
|
|
@ -1357,6 +1588,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (ImGui.Button($"{buttonText}##{buttonText}{index}"))
|
||||
{
|
||||
this.installStatus = OperationStatus.InProgress;
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.Installing;
|
||||
|
||||
Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting, PluginLoadReason.Installer))
|
||||
.ContinueWith(task =>
|
||||
|
|
@ -1450,14 +1682,18 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
{
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
var commandManager = Service<CommandManager>.Get();
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
var startInfo = Service<DalamudStartInfo>.Get();
|
||||
|
||||
var trouble = false;
|
||||
|
||||
// Name
|
||||
var label = plugin.Manifest.Name;
|
||||
|
||||
// Dev
|
||||
if (plugin.IsDev)
|
||||
{
|
||||
label += Locs.PluginTitleMod_DevPlugin;
|
||||
}
|
||||
|
||||
// Testing
|
||||
if (plugin.Manifest.Testing)
|
||||
{
|
||||
|
|
@ -1478,7 +1714,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
}
|
||||
|
||||
// Load error
|
||||
if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed && plugin.CheckPolicy())
|
||||
if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed && plugin.CheckPolicy()
|
||||
&& !plugin.IsOutdated && !plugin.IsBanned && !plugin.IsOrphaned)
|
||||
{
|
||||
label += Locs.PluginTitleMod_LoadError;
|
||||
trouble = true;
|
||||
|
|
@ -1538,6 +1775,12 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
trouble = true;
|
||||
}
|
||||
|
||||
// Scheduled for deletion
|
||||
if (plugin.Manifest.ScheduledForDeletion)
|
||||
{
|
||||
label += Locs.PluginTitleMod_ScheduledForDeletion;
|
||||
}
|
||||
|
||||
ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}");
|
||||
var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty();
|
||||
|
||||
|
|
@ -1616,17 +1859,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
this.DrawUpdateSinglePluginButton(availablePluginUpdate);
|
||||
|
||||
ImGui.SameLine();
|
||||
var version = plugin.AssemblyName?.Version;
|
||||
version ??= plugin.Manifest.Testing
|
||||
? plugin.Manifest.TestingAssemblyVersion
|
||||
: plugin.Manifest.AssemblyVersion;
|
||||
ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{version}");
|
||||
|
||||
if (plugin.IsDev)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed, Locs.PluginBody_DeleteDevPlugin);
|
||||
}
|
||||
ImGui.TextColored(ImGuiColors.DalamudGrey3, $" v{plugin.Manifest.EffectiveVersion}");
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
|
|
@ -1637,7 +1870,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
if (hasChangelog)
|
||||
{
|
||||
if (ImGui.TreeNode($"Changelog (v{plugin.Manifest.AssemblyVersion})"))
|
||||
if (ImGui.TreeNode($"Changelog (v{plugin.Manifest.EffectiveVersion})"))
|
||||
{
|
||||
this.DrawInstalledPluginChangelog(plugin.Manifest);
|
||||
}
|
||||
|
|
@ -1701,10 +1934,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
private void DrawPluginControlButton(LocalPlugin plugin)
|
||||
{
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
var notifications = Service<NotificationManager>.Get();
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
var startInfo = Service<DalamudStartInfo>.Get();
|
||||
|
||||
// Disable everything if the updater is running or another plugin is operating
|
||||
var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress;
|
||||
|
|
@ -1718,92 +1949,101 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
// Disable everything if the plugin failed to load
|
||||
disabled = disabled || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed;
|
||||
|
||||
if (plugin.State == PluginState.Loading || plugin.State == PluginState.Unloading)
|
||||
{
|
||||
ImGuiComponents.DisabledButton(Locs.PluginButton_Working);
|
||||
}
|
||||
else if (plugin.State == PluginState.Loaded || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed)
|
||||
{
|
||||
if (pluginManager.SafeMode)
|
||||
{
|
||||
ImGuiComponents.DisabledButton(Locs.PluginButton_SafeMode);
|
||||
}
|
||||
else if (disabled)
|
||||
{
|
||||
ImGuiComponents.DisabledButton(Locs.PluginButton_Disable);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui.Button(Locs.PluginButton_Disable))
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var unloadTask = Task.Run(() => plugin.UnloadAsync())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_UnloadFail(plugin.Name));
|
||||
// Disable everything if we're working
|
||||
disabled = disabled || plugin.State == PluginState.Loading || plugin.State == PluginState.Unloading;
|
||||
|
||||
unloadTask.Wait();
|
||||
if (!unloadTask.Result)
|
||||
return;
|
||||
var toggleId = plugin.Manifest.InternalName;
|
||||
var isLoadedAndUnloadable = plugin.State == PluginState.Loaded ||
|
||||
plugin.State == PluginState.DependencyResolutionFailed;
|
||||
|
||||
var disableTask = Task.Run(() => plugin.Disable())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_DisableFail(plugin.Name));
|
||||
StyleModelV1.DalamudStandard.Push();
|
||||
|
||||
disableTask.Wait();
|
||||
if (!disableTask.Result)
|
||||
return;
|
||||
|
||||
if (!plugin.IsDev)
|
||||
{
|
||||
pluginManager.RemovePlugin(plugin);
|
||||
}
|
||||
|
||||
notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.State == PluginState.Loaded)
|
||||
{
|
||||
// Only if the plugin isn't broken.
|
||||
this.DrawOpenPluginSettingsButton(plugin);
|
||||
}
|
||||
}
|
||||
else if (plugin.State == PluginState.Unloaded)
|
||||
{
|
||||
if (disabled)
|
||||
{
|
||||
ImGuiComponents.DisabledButton(Locs.PluginButton_Load);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui.Button(Locs.PluginButton_Load))
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var enableTask = Task.Run(() => plugin.Enable())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_EnableFail(plugin.Name));
|
||||
|
||||
enableTask.Wait();
|
||||
if (!enableTask.Result)
|
||||
return;
|
||||
|
||||
var loadTask = Task.Run(() => plugin.LoadAsync(PluginLoadReason.Installer))
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_LoadFail(plugin.Name));
|
||||
|
||||
loadTask.Wait();
|
||||
if (!loadTask.Result)
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (plugin.State == PluginState.UnloadError)
|
||||
if (plugin.State == PluginState.UnloadError)
|
||||
{
|
||||
ImGuiComponents.DisabledButton(FontAwesomeIcon.Frown);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip(Locs.PluginButtonToolTip_UnloadFailed);
|
||||
}
|
||||
else if (disabled)
|
||||
{
|
||||
ImGuiComponents.DisabledToggleButton(toggleId, isLoadedAndUnloadable);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGuiComponents.ToggleButton(toggleId, ref isLoadedAndUnloadable))
|
||||
{
|
||||
if (!isLoadedAndUnloadable)
|
||||
{
|
||||
this.enableDisableStatus = OperationStatus.InProgress;
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.DisablingSingle;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var unloadTask = Task.Run(() => plugin.UnloadAsync())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_UnloadFail(plugin.Name));
|
||||
|
||||
unloadTask.Wait();
|
||||
if (!unloadTask.Result)
|
||||
{
|
||||
this.enableDisableStatus = OperationStatus.Complete;
|
||||
return;
|
||||
}
|
||||
|
||||
var disableTask = Task.Run(() => plugin.Disable())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_DisableFail(plugin.Name));
|
||||
|
||||
disableTask.Wait();
|
||||
this.enableDisableStatus = OperationStatus.Complete;
|
||||
|
||||
if (!disableTask.Result)
|
||||
return;
|
||||
|
||||
notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.enableDisableStatus = OperationStatus.InProgress;
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.EnablingSingle;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
var enableTask = Task.Run(() => plugin.Enable())
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_EnableFail(plugin.Name));
|
||||
|
||||
enableTask.Wait();
|
||||
if (!enableTask.Result)
|
||||
{
|
||||
this.enableDisableStatus = OperationStatus.Complete;
|
||||
return;
|
||||
}
|
||||
|
||||
var loadTask = Task.Run(() => plugin.LoadAsync(PluginLoadReason.Installer))
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_LoadFail(plugin.Name));
|
||||
|
||||
loadTask.Wait();
|
||||
this.enableDisableStatus = OperationStatus.Complete;
|
||||
|
||||
if (!loadTask.Result)
|
||||
return;
|
||||
|
||||
notifications.AddNotification(Locs.Notifications_PluginEnabled(plugin.Manifest.Name), Locs.Notifications_PluginEnabledTitle, NotificationType.Success);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyleModelV1.DalamudStandard.Pop();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(15, 0);
|
||||
|
||||
if (plugin.State == PluginState.Loaded)
|
||||
{
|
||||
// Only if the plugin isn't broken.
|
||||
this.DrawOpenPluginSettingsButton(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUpdateSinglePluginButton(AvailablePluginUpdate update)
|
||||
|
|
@ -1815,6 +2055,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Download))
|
||||
{
|
||||
this.installStatus = OperationStatus.InProgress;
|
||||
this.loadingIndicatorKind = LoadingIndicatorKind.UpdatingSingle;
|
||||
|
||||
Task.Run(async () => await pluginManager.UpdateSinglePluginAsync(update, true, false))
|
||||
.ContinueWith(task =>
|
||||
|
|
@ -1883,6 +2124,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
private void DrawDevPluginButtons(LocalPlugin localPlugin)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
if (localPlugin is LocalDevPlugin plugin)
|
||||
|
|
@ -1931,18 +2174,20 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
private void DrawDeletePluginButton(LocalPlugin plugin)
|
||||
{
|
||||
var unloaded = plugin.State == PluginState.Unloaded || plugin.State == PluginState.LoadError;
|
||||
/*var unloaded = plugin.State == PluginState.Unloaded || plugin.State == PluginState.LoadError;
|
||||
|
||||
// When policy check fails, the plugin is never loaded
|
||||
var showButton = unloaded && (plugin.IsDev || plugin.IsOutdated || plugin.IsBanned || plugin.IsOrphaned || !plugin.CheckPolicy());
|
||||
|
||||
if (!showButton)
|
||||
return;
|
||||
return;*/
|
||||
|
||||
var pluginManager = Service<PluginManager>.Get();
|
||||
|
||||
var devNotDeletable = plugin.IsDev && plugin.State != PluginState.Unloaded && plugin.State != PluginState.DependencyResolutionFailed;
|
||||
|
||||
ImGui.SameLine();
|
||||
if (plugin.HasEverStartedLoad)
|
||||
if (plugin.State == PluginState.Loaded || devNotDeletable)
|
||||
{
|
||||
ImGui.PushFont(InterfaceManager.IconFont);
|
||||
ImGuiComponents.DisabledButton(FontAwesomeIcon.TrashAlt.ToIconString());
|
||||
|
|
@ -1950,7 +2195,9 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePluginRestricted);
|
||||
ImGui.SetTooltip(plugin.State == PluginState.Loaded
|
||||
? Locs.PluginButtonToolTip_DeletePluginLoaded
|
||||
: Locs.PluginButtonToolTip_DeletePluginRestricted);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1959,22 +2206,45 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
{
|
||||
try
|
||||
{
|
||||
plugin.DllFile.Delete();
|
||||
pluginManager.RemovePlugin(plugin);
|
||||
if (plugin.IsDev)
|
||||
{
|
||||
plugin.DllFile.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.ScheduleDeletion(!plugin.Manifest.ScheduledForDeletion);
|
||||
}
|
||||
|
||||
if (plugin.State is PluginState.Unloaded or PluginState.DependencyResolutionFailed)
|
||||
{
|
||||
pluginManager.RemovePlugin(plugin);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Plugin installer threw an error during removal of {plugin.Name}");
|
||||
|
||||
this.errorModalMessage = Locs.ErrorModal_DeleteFail(plugin.Name);
|
||||
this.errorModalDrawing = true;
|
||||
this.errorModalOnNextFrame = true;
|
||||
this.ShowErrorModal(Locs.ErrorModal_DeleteFail(plugin.Name));
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePlugin);
|
||||
string tooltipMessage;
|
||||
if (plugin.Manifest.ScheduledForDeletion)
|
||||
{
|
||||
tooltipMessage = Locs.PluginButtonToolTip_DeletePluginScheduledCancel;
|
||||
}
|
||||
else if (plugin.State is PluginState.Unloaded or PluginState.DependencyResolutionFailed)
|
||||
{
|
||||
tooltipMessage = Locs.PluginButtonToolTip_DeletePlugin;
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltipMessage = Locs.PluginButtonToolTip_DeletePluginScheduled;
|
||||
}
|
||||
|
||||
ImGui.SetTooltip(tooltipMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2202,11 +2472,13 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
return true;
|
||||
}
|
||||
|
||||
private void ShowErrorModal(string message)
|
||||
private Task ShowErrorModal(string message)
|
||||
{
|
||||
this.errorModalMessage = message;
|
||||
this.errorModalDrawing = true;
|
||||
this.errorModalOnNextFrame = true;
|
||||
this.errorModalTaskCompletionSource = new TaskCompletionSource();
|
||||
return this.errorModalTaskCompletionSource!.Task;
|
||||
}
|
||||
|
||||
private void UpdateCategoriesOnSearchChange()
|
||||
|
|
@ -2299,6 +2571,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
public static string PluginTitleMod_TestingVersion => Loc.Localize("InstallerTestingVersion", " (testing version)");
|
||||
|
||||
public static string PluginTitleMod_DevPlugin => Loc.Localize("InstallerDevPlugin", " (dev plugin)");
|
||||
|
||||
public static string PluginTitleMod_UpdateFailed => Loc.Localize("InstallerUpdateFailed", " (update failed)");
|
||||
|
||||
public static string PluginTitleMod_LoadError => Loc.Localize("InstallerLoadError", " (load error)");
|
||||
|
|
@ -2308,9 +2582,11 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
public static string PluginTitleMod_OutdatedError => Loc.Localize("InstallerOutdatedError", " (outdated)");
|
||||
|
||||
public static string PluginTitleMod_BannedError => Loc.Localize("InstallerBannedError", " (automatically disabled)");
|
||||
|
||||
|
||||
public static string PluginTitleMod_OrphanedError => Loc.Localize("InstallerOrphanedError", " (unknown repository)");
|
||||
|
||||
public static string PluginTitleMod_ScheduledForDeletion => Loc.Localize("InstallerScheduledForDeletion", " (scheduled for deletion)");
|
||||
|
||||
public static string PluginTitleMod_New => Loc.Localize("InstallerNewPlugin ", " New!");
|
||||
|
||||
#endregion
|
||||
|
|
@ -2340,9 +2616,6 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
public static string PluginBody_Plugin3rdPartyRepo(string url) => Loc.Localize("InstallerPlugin3rdPartyRepo", "From custom plugin repository {0}").Format(url);
|
||||
|
||||
public static string PluginBody_AvailableDevPlugin => Loc.Localize("InstallerDevPlugin", " This plugin is available in one of your repos, please remove it from the devPlugins folder.");
|
||||
|
||||
public static string PluginBody_DeleteDevPlugin => Loc.Localize("InstallerDeleteDevPlugin ", " To delete this plugin, please remove it from the devPlugins folder.");
|
||||
|
||||
public static string PluginBody_Outdated => Loc.Localize("InstallerOutdatedPluginBody ", "This plugin is outdated and incompatible at the moment. Please wait for it to be updated by its author.");
|
||||
|
||||
public static string PluginBody_Orphaned => Loc.Localize("InstallerOrphanedPluginBody ", "This plugin's source repository is no longer available. You may need to reinstall it from its repository, or re-add the repository.");
|
||||
|
|
@ -2384,7 +2657,13 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
public static string PluginButtonToolTip_DeletePlugin => Loc.Localize("InstallerDeletePlugin ", "Delete plugin");
|
||||
|
||||
public static string PluginButtonToolTip_DeletePluginRestricted => Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete - please try restarting the game.");
|
||||
public static string PluginButtonToolTip_DeletePluginRestricted => Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete right now - please restart the game.");
|
||||
|
||||
public static string PluginButtonToolTip_DeletePluginScheduled => Loc.Localize("InstallerDeletePluginScheduled", "Delete plugin on next restart");
|
||||
|
||||
public static string PluginButtonToolTip_DeletePluginScheduledCancel => Loc.Localize("InstallerDeletePluginScheduledCancel", "Cancel scheduled deletion");
|
||||
|
||||
public static string PluginButtonToolTip_DeletePluginLoaded => Loc.Localize("InstallerDeletePluginLoaded", "Disable this plugin before deleting it.");
|
||||
|
||||
public static string PluginButtonToolTip_VisitPluginUrl => Loc.Localize("InstallerVisitPluginUrl", "Visit plugin URL");
|
||||
|
||||
|
|
@ -2416,6 +2695,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
public static string Notifications_PluginDisabled(string name) => Loc.Localize("NotificationsPluginDisabled", "'{0}' was disabled.").Format(name);
|
||||
|
||||
public static string Notifications_PluginEnabledTitle => Loc.Localize("NotificationsPluginEnabledTitle", "Plugin enabled!");
|
||||
|
||||
public static string Notifications_PluginEnabled(string name) => Loc.Localize("NotificationsPluginEnabled", "'{0}' was enabled.").Format(name);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Footer
|
||||
|
|
@ -2478,12 +2761,26 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
|
||||
public static string FeedbackModal_Title => Loc.Localize("InstallerFeedback", "Send Feedback");
|
||||
|
||||
public static string FeedbackModal_Text(string pluginName) => Loc.Localize("InstallerFeedbackInfo", "You can send feedback to the developer of \"{0}\" here.\nYou can include your Discord tag or email address if you wish to give them the opportunity to answer.\n\nIf you put your Discord name into the contact information field, please join the XIVLauncher discord server so we can get in touch.").Format(pluginName);
|
||||
public static string FeedbackModal_Text(string pluginName) => Loc.Localize("InstallerFeedbackInfo", "You can send feedback to the developer of \"{0}\" here.").Format(pluginName);
|
||||
|
||||
public static string FeedbackModal_HasUpdate => Loc.Localize("InstallerFeedbackHasUpdate", "A new version of this plugin is available, please update before reporting bugs.");
|
||||
|
||||
public static string FeedbackModal_ContactAnonymous => Loc.Localize("InstallerFeedbackContactAnonymous", "Submit feedback anonymously");
|
||||
|
||||
public static string FeedbackModal_ContactAnonymousWarning => Loc.Localize("InstallerFeedbackContactAnonymousWarning", "No response will be forthcoming.\nUntick \"{0}\" and provide contact information if you need help.").Format(FeedbackModal_ContactAnonymous);
|
||||
|
||||
public static string FeedbackModal_ContactInformation => Loc.Localize("InstallerFeedbackContactInfo", "Contact information");
|
||||
|
||||
public static string FeedbackModal_ContactInformationHelp => Loc.Localize("InstallerFeedbackContactInfoHelp", "Discord usernames and e-mail addresses are accepted.\nIf you submit a Discord username, please join our discord server so that we can reach out to you easier.");
|
||||
|
||||
public static string FeedbackModal_ContactInformationWarning => Loc.Localize("InstallerFeedbackContactInfoWarning", "Do not submit in-game character names.");
|
||||
|
||||
public static string FeedbackModal_ContactInformationRequired => Loc.Localize("InstallerFeedbackContactInfoRequired", "Contact information has not been provided. If you do not want to provide contact information, tick on \"{0}\" above.").Format(FeedbackModal_ContactAnonymous);
|
||||
|
||||
public static string FeedbackModal_ContactInformationDiscordButton => Loc.Localize("ContactInformationDiscordButton", "Join Goat Place Discord");
|
||||
|
||||
public static string FeedbackModal_ContactInformationDiscordUrl => Loc.Localize("ContactInformationDiscordUrl", "https://goat.place/");
|
||||
|
||||
public static string FeedbackModal_IncludeLastError => Loc.Localize("InstallerFeedbackIncludeLastError", "Include last error message");
|
||||
|
||||
public static string FeedbackModal_IncludeLastErrorHint => Loc.Localize("InstallerFeedbackIncludeLastErrorHint", "This option can give the plugin developer useful feedback on what exactly went wrong.");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using System.Numerics;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -15,6 +16,10 @@ namespace Dalamud.Interface.Style
|
|||
/// </summary>
|
||||
public abstract class StyleModel
|
||||
{
|
||||
private static int NumPushedStyles = 0;
|
||||
private static int NumPushedColors = 0;
|
||||
private static bool HasPushedOnce = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the style model.
|
||||
/// </summary>
|
||||
|
|
@ -84,7 +89,7 @@ namespace Dalamud.Interface.Style
|
|||
configuration.SavedStyles = new List<StyleModel>();
|
||||
configuration.SavedStyles.AddRange(configuration.SavedStylesOld);
|
||||
|
||||
Log.Information("Transferred {0} styles", configuration.SavedStyles.Count);
|
||||
Log.Information("Transferred {NumStyles} styles", configuration.SavedStyles.Count);
|
||||
|
||||
configuration.SavedStylesOld = null;
|
||||
configuration.Save();
|
||||
|
|
@ -123,6 +128,60 @@ namespace Dalamud.Interface.Style
|
|||
/// <summary>
|
||||
/// Pop this style model from the ImGui style/color stack.
|
||||
/// </summary>
|
||||
public abstract void Pop();
|
||||
public void Pop()
|
||||
{
|
||||
if (!HasPushedOnce)
|
||||
throw new InvalidOperationException("Wasn't pushed at least once.");
|
||||
|
||||
ImGui.PopStyleVar(NumPushedStyles);
|
||||
ImGui.PopStyleColor(NumPushedColors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push a style var.
|
||||
/// </summary>
|
||||
/// <param name="style">Style kind.</param>
|
||||
/// <param name="arg">Style var.</param>
|
||||
protected void PushStyleHelper(ImGuiStyleVar style, float arg)
|
||||
{
|
||||
ImGui.PushStyleVar(style, arg);
|
||||
|
||||
if (!HasPushedOnce)
|
||||
NumPushedStyles++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push a style var.
|
||||
/// </summary>
|
||||
/// <param name="style">Style kind.</param>
|
||||
/// <param name="arg">Style var.</param>
|
||||
protected void PushStyleHelper(ImGuiStyleVar style, Vector2 arg)
|
||||
{
|
||||
ImGui.PushStyleVar(style, arg);
|
||||
|
||||
if (!HasPushedOnce)
|
||||
NumPushedStyles++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push a style color.
|
||||
/// </summary>
|
||||
/// <param name="color">Color kind.</param>
|
||||
/// <param name="value">Color value.</param>
|
||||
protected void PushColorHelper(ImGuiCol color, Vector4 value)
|
||||
{
|
||||
ImGui.PushStyleColor(color, value);
|
||||
|
||||
if (!HasPushedOnce)
|
||||
NumPushedColors++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate that you have pushed.
|
||||
/// </summary>
|
||||
protected void DonePushing()
|
||||
{
|
||||
HasPushedOnce = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,13 +487,41 @@ namespace Dalamud.Interface.Style
|
|||
/// <inheritdoc/>
|
||||
public override void Push()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
this.PushStyleHelper(ImGuiStyleVar.Alpha, this.Alpha);
|
||||
this.PushStyleHelper(ImGuiStyleVar.WindowPadding, this.WindowPadding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.WindowRounding, this.WindowRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.WindowBorderSize, this.WindowBorderSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.WindowTitleAlign, this.WindowTitleAlign);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ChildRounding, this.ChildRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ChildBorderSize, this.ChildBorderSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.PopupRounding, this.PopupRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.PopupBorderSize, this.PopupBorderSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.FramePadding, this.FramePadding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.FrameRounding, this.FrameRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.FrameBorderSize, this.FrameBorderSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ItemSpacing, this.ItemSpacing);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ItemInnerSpacing, this.ItemInnerSpacing);
|
||||
this.PushStyleHelper(ImGuiStyleVar.CellPadding, this.CellPadding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.IndentSpacing, this.IndentSpacing);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ScrollbarSize, this.ScrollbarSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ScrollbarRounding, this.ScrollbarRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.GrabMinSize, this.GrabMinSize);
|
||||
this.PushStyleHelper(ImGuiStyleVar.GrabRounding, this.GrabRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.TabRounding, this.TabRounding);
|
||||
this.PushStyleHelper(ImGuiStyleVar.ButtonTextAlign, this.ButtonTextAlign);
|
||||
this.PushStyleHelper(ImGuiStyleVar.SelectableTextAlign, this.SelectableTextAlign);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Pop()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
foreach (var imGuiCol in Enum.GetValues<ImGuiCol>())
|
||||
{
|
||||
if (imGuiCol == ImGuiCol.COUNT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.PushColorHelper(imGuiCol, this.Colors[imGuiCol.ToString()]);
|
||||
}
|
||||
|
||||
this.DonePushing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ using Dalamud.Game;
|
|||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Gui.Dtr;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal.Exceptions;
|
||||
|
|
@ -50,6 +52,8 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
private readonly DirectoryInfo devPluginDirectory;
|
||||
private readonly BannedPlugin[]? bannedPlugins;
|
||||
|
||||
private readonly DalamudLinkPayload openInstallerWindowPluginChangelogsLink;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
|
|
@ -69,6 +73,23 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
this.devPluginDirectory.Create();
|
||||
|
||||
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins;
|
||||
|
||||
try
|
||||
{
|
||||
var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var safeModeFile = Path.Combine(appdata, "XIVLauncher", ".dalamud_safemode");
|
||||
|
||||
if (File.Exists(safeModeFile))
|
||||
{
|
||||
this.SafeMode = true;
|
||||
File.Delete(safeModeFile);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Couldn't check safe mode file");
|
||||
}
|
||||
|
||||
if (this.SafeMode)
|
||||
{
|
||||
this.configuration.PluginSafeMode = false;
|
||||
|
|
@ -84,6 +105,11 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
throw new InvalidDataException("Couldn't deserialize banned plugins manifest.");
|
||||
}
|
||||
|
||||
this.openInstallerWindowPluginChangelogsLink = Service<ChatGui>.Get().AddChatLinkHandler("Dalamud", 1003, (i, m) =>
|
||||
{
|
||||
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerPluginChangelogs();
|
||||
});
|
||||
|
||||
this.ApplyPatches();
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +151,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// <summary>
|
||||
/// Gets a value indicating whether all added repos are not in progress.
|
||||
/// </summary>
|
||||
public bool ReposReady => this.Repos.All(repo => repo.State != PluginRepositoryState.InProgress);
|
||||
public bool ReposReady => this.Repos.All(repo => repo.State != PluginRepositoryState.InProgress || repo.State != PluginRepositoryState.Fail);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the plugin manager started in safe mode.
|
||||
|
|
@ -152,25 +178,38 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
/// <param name="updateMetadata">The list of updated plugin metadata.</param>
|
||||
/// <param name="header">The header text to send to chat prior to any update info.</param>
|
||||
public static void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
|
||||
public void PrintUpdatedPlugins(List<PluginUpdateStatus>? updateMetadata, string header)
|
||||
{
|
||||
var chatGui = Service<ChatGui>.Get();
|
||||
|
||||
if (updateMetadata is { Count: > 0 })
|
||||
{
|
||||
chatGui.Print(header);
|
||||
chatGui.PrintChat(new XivChatEntry
|
||||
{
|
||||
Message = new SeString(new List<Payload>()
|
||||
{
|
||||
new TextPayload(header),
|
||||
new TextPayload(" ["),
|
||||
new UIForegroundPayload(500),
|
||||
this.openInstallerWindowPluginChangelogsLink,
|
||||
new TextPayload(Loc.Localize("DalamudInstallerPluginChangelogHelp", "Open plugin changelogs") + " "),
|
||||
RawPayload.LinkTerminator,
|
||||
new UIForegroundPayload(0),
|
||||
new TextPayload("]"),
|
||||
}),
|
||||
});
|
||||
|
||||
foreach (var metadata in updateMetadata)
|
||||
{
|
||||
if (metadata.WasUpdated)
|
||||
{
|
||||
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version));
|
||||
chatGui.Print(Locs.DalamudPluginUpdateSuccessful(metadata.Name, metadata.Version) + (metadata.HasChangelog ? " " : string.Empty));
|
||||
}
|
||||
else
|
||||
{
|
||||
chatGui.PrintChat(new XivChatEntry
|
||||
{
|
||||
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version),
|
||||
Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version) + (metadata.HasChangelog ? " " : string.Empty),
|
||||
Type = XivChatType.Urgent,
|
||||
});
|
||||
}
|
||||
|
|
@ -226,10 +265,12 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.InstalledPlugins.Any())
|
||||
var disposablePlugins =
|
||||
this.InstalledPlugins.Where(plugin => plugin.State is PluginState.Loaded or PluginState.LoadError).ToArray();
|
||||
if (disposablePlugins.Any())
|
||||
{
|
||||
// Unload them first, just in case some of plugin codes are still running via callbacks initiated externally.
|
||||
foreach (var plugin in this.InstalledPlugins.Where(plugin => !plugin.Manifest.CanUnloadAsync))
|
||||
foreach (var plugin in disposablePlugins.Where(plugin => !plugin.Manifest.CanUnloadAsync))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -241,7 +282,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
}
|
||||
}
|
||||
|
||||
Task.WaitAll(this.InstalledPlugins
|
||||
Task.WaitAll(disposablePlugins
|
||||
.Where(plugin => plugin.Manifest.CanUnloadAsync)
|
||||
.Select(plugin => Task.Run(async () =>
|
||||
{
|
||||
|
|
@ -261,7 +302,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
|
||||
// Now that we've waited enough, dispose the whole plugin.
|
||||
// Since plugins should have been unloaded above, this should be done quickly.
|
||||
foreach (var plugin in this.InstalledPlugins)
|
||||
foreach (var plugin in disposablePlugins)
|
||||
plugin.ExplicitDisposeIgnoreExceptions($"Error disposing {plugin.Name}", Log);
|
||||
}
|
||||
|
||||
|
|
@ -307,6 +348,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
// Add installed plugins. These are expected to be in a specific format so we can look for exactly that.
|
||||
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
|
||||
{
|
||||
var versionsDefs = new List<PluginDef>();
|
||||
foreach (var versionDir in pluginDir.GetDirectories())
|
||||
{
|
||||
var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll"));
|
||||
|
|
@ -317,7 +359,16 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
|
||||
var manifest = LocalPluginManifest.Load(manifestFile);
|
||||
|
||||
pluginDefs.Add(new PluginDef(dllFile, manifest, false));
|
||||
versionsDefs.Add(new PluginDef(dllFile, manifest, false));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pluginDefs.Add(versionsDefs.OrderByDescending(x => x.Manifest!.EffectiveVersion).First());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Couldn't choose best version for plugin: {Name}", pluginDir.Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +533,9 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public async Task ReloadPluginMastersAsync(bool notify = true)
|
||||
{
|
||||
Log.Information("Now reloading all PluginMasters...");
|
||||
await Task.WhenAll(this.Repos.Select(repo => repo.ReloadPluginMasterAsync()));
|
||||
Log.Information("PluginMasters reloaded, now refiltering...");
|
||||
|
||||
this.RefilterPluginMasters(notify);
|
||||
}
|
||||
|
|
@ -712,10 +765,14 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
{
|
||||
try
|
||||
{
|
||||
if (plugin.IsDisabled)
|
||||
plugin.Enable();
|
||||
|
||||
await plugin.LoadAsync(reason);
|
||||
if (!plugin.IsDisabled)
|
||||
{
|
||||
await plugin.LoadAsync(reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Verbose($"{name} was disabled");
|
||||
}
|
||||
}
|
||||
catch (InvalidPluginException)
|
||||
{
|
||||
|
|
@ -832,10 +889,17 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
}
|
||||
else
|
||||
{
|
||||
foreach (var versionDir in versionDirs)
|
||||
for (var i = 0; i < versionDirs.Length; i++)
|
||||
{
|
||||
var versionDir = versionDirs[i];
|
||||
try
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
Log.Information($"Old version: cleaning up {versionDir.FullName}");
|
||||
versionDir.Delete(true);
|
||||
continue;
|
||||
}
|
||||
var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll"));
|
||||
if (!dllFile.Exists)
|
||||
{
|
||||
|
|
@ -849,6 +913,21 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
{
|
||||
Log.Information($"Missing manifest: cleaning up {versionDir.FullName}");
|
||||
versionDir.Delete(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (manifestFile.Length == 0)
|
||||
{
|
||||
Log.Information($"Manifest empty: cleaning up {versionDir.FullName}");
|
||||
versionDir.Delete(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
var manifest = LocalPluginManifest.Load(manifestFile);
|
||||
if (manifest.ScheduledForDeletion)
|
||||
{
|
||||
Log.Information($"Scheduled deletion: cleaning up {versionDir.FullName}");
|
||||
versionDir.Delete(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -870,7 +949,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
/// </summary>
|
||||
/// <param name="dryRun">Perform a dry run, don't install anything.</param>
|
||||
/// <returns>Success or failure and a list of updated plugin metadata.</returns>
|
||||
public async Task<List<PluginUpdateStatus>> UpdatePluginsAsync(bool dryRun = false)
|
||||
public async Task<List<PluginUpdateStatus>> UpdatePluginsAsync(bool ignoreDisabled, bool dryRun)
|
||||
{
|
||||
Log.Information("Starting plugin update");
|
||||
|
||||
|
|
@ -883,6 +962,12 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
if (plugin.InstalledPlugin.IsDev)
|
||||
continue;
|
||||
|
||||
if (plugin.InstalledPlugin.Manifest.Disabled && ignoreDisabled)
|
||||
continue;
|
||||
|
||||
if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion)
|
||||
continue;
|
||||
|
||||
var result = await this.UpdateSinglePluginAsync(plugin, false, dryRun);
|
||||
if (result != null)
|
||||
updatedList.Add(result);
|
||||
|
|
@ -914,6 +999,7 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
? metadata.UpdateManifest.TestingAssemblyVersion
|
||||
: metadata.UpdateManifest.AssemblyVersion)!,
|
||||
WasUpdated = true,
|
||||
HasChangelog = !metadata.UpdateManifest.Changelog.IsNullOrWhitespace(),
|
||||
};
|
||||
|
||||
if (!dryRun)
|
||||
|
|
@ -954,7 +1040,9 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
{
|
||||
try
|
||||
{
|
||||
plugin.Disable();
|
||||
if (!plugin.IsDisabled)
|
||||
plugin.Disable();
|
||||
|
||||
lock (this.pluginListLock)
|
||||
{
|
||||
this.InstalledPlugins = this.InstalledPlugins.Remove(plugin);
|
||||
|
|
|
|||
|
|
@ -138,9 +138,9 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.State != PluginState.Loaded)
|
||||
if (this.State != PluginState.Loaded && this.State != PluginState.LoadError)
|
||||
{
|
||||
Log.Debug($"Skipping reload of {this.Name}, state ({this.State}) is not {PluginState.Loaded}.");
|
||||
Log.Debug($"Skipping reload of {this.Name}, state ({this.State}) is not {PluginState.Loaded} nor {PluginState.LoadError}.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -560,6 +560,7 @@ internal class LocalPlugin : IDisposable
|
|||
throw new InvalidPluginOperationException($"Unable to enable {this.Name}, not disabled");
|
||||
|
||||
this.Manifest.Disabled = false;
|
||||
this.Manifest.ScheduledForDeletion = false;
|
||||
this.SaveManifest();
|
||||
}
|
||||
|
||||
|
|
@ -614,6 +615,16 @@ internal class LocalPlugin : IDisposable
|
|||
this.SaveManifest();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule the deletion of this plugin on next cleanup.
|
||||
/// </summary>
|
||||
/// <param name="status">Schedule or cancel the deletion.</param>
|
||||
public void ScheduleDeletion(bool status = true)
|
||||
{
|
||||
this.Manifest.ScheduledForDeletion = status;
|
||||
this.SaveManifest();
|
||||
}
|
||||
|
||||
private static void SetupLoaderConfig(LoaderConfig config)
|
||||
{
|
||||
config.IsUnloadable = true;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -23,6 +24,11 @@ internal record LocalPluginManifest : PluginManifest
|
|||
/// </summary>
|
||||
public bool Testing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the plugin should be deleted during the next cleanup.
|
||||
/// </summary>
|
||||
public bool ScheduledForDeletion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 3rd party repo URL that this plugin was installed from. Used to display where the plugin was
|
||||
/// sourced from on the installed plugin view. This should not be included in the plugin master. This value is null
|
||||
|
|
@ -36,6 +42,11 @@ internal record LocalPluginManifest : PluginManifest
|
|||
/// </summary>
|
||||
public bool IsThirdParty => !this.InstalledFromUrl.IsNullOrEmpty() && this.InstalledFromUrl != PluginRepository.MainRepoUrl;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective version of this plugin.
|
||||
/// </summary>
|
||||
public Version EffectiveVersion => this.Testing && this.TestingAssemblyVersion != null ? this.TestingAssemblyVersion : this.AssemblyVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Save a plugin manifest to file.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -181,4 +181,17 @@ internal record PluginManifest
|
|||
/// Gets a message that is shown to users when sending feedback.
|
||||
/// </summary>
|
||||
public string? FeedbackMessage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this plugin is DIP17.
|
||||
/// To be removed.
|
||||
/// </summary>
|
||||
[JsonProperty("_isDip17Plugin")]
|
||||
public bool IsDip17Plugin { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DIP17 channel name.
|
||||
/// </summary>
|
||||
[JsonProperty("_Dip17Channel")]
|
||||
public string? Dip17Channel { get; init; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal class PluginRepository
|
|||
|
||||
private static readonly HttpClient HttpClient = new()
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(20),
|
||||
DefaultRequestHeaders =
|
||||
{
|
||||
CacheControl = new CacheControlHeaderValue
|
||||
|
|
@ -109,7 +110,7 @@ internal class PluginRepository
|
|||
|
||||
this.PluginMaster = pluginMaster.AsReadOnly();
|
||||
|
||||
Log.Debug($"Successfully fetched repo: {this.PluginMasterUrl}");
|
||||
Log.Information($"Successfully fetched repo: {this.PluginMasterUrl}");
|
||||
this.State = PluginRepositoryState.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -26,4 +26,9 @@ internal class PluginUpdateStatus
|
|||
/// Gets or sets a value indicating whether the plugin was updated.
|
||||
/// </summary>
|
||||
public bool WasUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the plugin has a changelog if it was updated.
|
||||
/// </summary>
|
||||
public bool HasChangelog { get; init; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue