mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-01 05:13:40 +01:00
Merge branch 'refs/heads/master' into apiX
This commit is contained in:
commit
cce11a82be
11 changed files with 548 additions and 229 deletions
|
|
@ -11,6 +11,7 @@ using Dalamud.Game.ClientState;
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.ImGuiNotification.EventArgs;
|
||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Internal.DesignSystem;
|
||||
|
|
@ -39,7 +40,13 @@ internal class AutoUpdateManager : IServiceType
|
|||
/// <summary>
|
||||
/// Time we should wait between scheduled update checks.
|
||||
/// </summary>
|
||||
private static readonly TimeSpan TimeBetweenUpdateChecks = TimeSpan.FromHours(1.5);
|
||||
private static readonly TimeSpan TimeBetweenUpdateChecks = TimeSpan.FromHours(2);
|
||||
|
||||
/// <summary>
|
||||
/// Time we should wait between scheduled update checks if the user has dismissed the notification,
|
||||
/// instead of updating. We don't want to spam the user with notifications.
|
||||
/// </summary>
|
||||
private static readonly TimeSpan TimeBetweenUpdateChecksIfDismissed = TimeSpan.FromHours(12);
|
||||
|
||||
/// <summary>
|
||||
/// Time we should wait after unblocking to nag the user.
|
||||
|
|
@ -62,12 +69,13 @@ internal class AutoUpdateManager : IServiceType
|
|||
private readonly IConsoleVariable<bool> isDryRun;
|
||||
|
||||
private DateTime? loginTime;
|
||||
private DateTime? lastUpdateCheckTime;
|
||||
private DateTime? nextUpdateCheckTime;
|
||||
private DateTime? unblockedSince;
|
||||
|
||||
private bool hasStartedInitialUpdateThisSession;
|
||||
|
||||
private IActiveNotification? updateNotification;
|
||||
private bool notificationHasStartedUpdate; // Used to track if the user has started an update from the notification.
|
||||
|
||||
private Task? autoUpdateTask;
|
||||
|
||||
|
|
@ -95,7 +103,7 @@ internal class AutoUpdateManager : IServiceType
|
|||
});
|
||||
console.AddCommand("dalamud.autoupdate.force_check", "Force a check for updates", () =>
|
||||
{
|
||||
this.lastUpdateCheckTime = DateTime.Now - TimeBetweenUpdateChecks;
|
||||
this.nextUpdateCheckTime = DateTime.Now + TimeSpan.FromSeconds(5);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
@ -127,6 +135,17 @@ internal class AutoUpdateManager : IServiceType
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(behavior), behavior, null),
|
||||
};
|
||||
}
|
||||
|
||||
private static void DrawOpenInstallerNotificationButton(bool primary, PluginInstallerOpenKind kind, IActiveNotification notification)
|
||||
{
|
||||
if (primary ?
|
||||
DalamudComponents.PrimaryButton(Locs.NotificationButtonOpenPluginInstaller) :
|
||||
DalamudComponents.SecondaryButton(Locs.NotificationButtonOpenPluginInstaller))
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenPluginInstallerTo(kind);
|
||||
notification.DismissNow();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdate(IFramework framework)
|
||||
{
|
||||
|
|
@ -170,11 +189,9 @@ internal class AutoUpdateManager : IServiceType
|
|||
// the only time we actually install updates automatically.
|
||||
if (!this.hasStartedInitialUpdateThisSession && DateTime.Now > this.loginTime.Value.Add(UpdateTimeAfterLogin))
|
||||
{
|
||||
this.lastUpdateCheckTime = DateTime.Now;
|
||||
this.hasStartedInitialUpdateThisSession = true;
|
||||
|
||||
var currentlyUpdatablePlugins = this.GetAvailablePluginUpdates(DecideUpdateListingRestriction(behavior));
|
||||
|
||||
if (currentlyUpdatablePlugins.Count == 0)
|
||||
{
|
||||
this.IsAutoUpdateComplete = true;
|
||||
|
|
@ -186,12 +203,13 @@ internal class AutoUpdateManager : IServiceType
|
|||
if (behavior == AutoUpdateBehavior.OnlyNotify)
|
||||
{
|
||||
// List all plugins in the notification
|
||||
Log.Verbose("Ran initial update, notifying for {Num} plugins", currentlyUpdatablePlugins.Count);
|
||||
Log.Verbose("Running initial auto-update, notifying for {Num} plugins", currentlyUpdatablePlugins.Count);
|
||||
this.NotifyUpdatesAreAvailable(currentlyUpdatablePlugins);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Verbose("Ran initial update, updating {Num} plugins", currentlyUpdatablePlugins.Count);
|
||||
Log.Verbose("Running initial auto-update, updating {Num} plugins", currentlyUpdatablePlugins.Count);
|
||||
this.notificationHasStartedUpdate = true;
|
||||
this.KickOffAutoUpdates(currentlyUpdatablePlugins);
|
||||
return;
|
||||
}
|
||||
|
|
@ -199,10 +217,13 @@ internal class AutoUpdateManager : IServiceType
|
|||
// 2. Continuously check for updates while the game is running. We run these every once in a while and
|
||||
// will only show a notification here that lets people start the update or open the installer.
|
||||
if (this.config.CheckPeriodicallyForUpdates &&
|
||||
this.lastUpdateCheckTime != null &&
|
||||
DateTime.Now - this.lastUpdateCheckTime > TimeBetweenUpdateChecks &&
|
||||
this.nextUpdateCheckTime != null &&
|
||||
DateTime.Now > this.nextUpdateCheckTime &&
|
||||
this.updateNotification == null)
|
||||
{
|
||||
this.nextUpdateCheckTime = null;
|
||||
|
||||
Log.Verbose("Starting periodic update check");
|
||||
this.pluginManager.ReloadPluginMastersAsync()
|
||||
.ContinueWith(
|
||||
t =>
|
||||
|
|
@ -216,8 +237,6 @@ internal class AutoUpdateManager : IServiceType
|
|||
this.GetAvailablePluginUpdates(
|
||||
DecideUpdateListingRestriction(behavior)));
|
||||
});
|
||||
|
||||
this.lastUpdateCheckTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,16 +245,34 @@ internal class AutoUpdateManager : IServiceType
|
|||
if (this.updateNotification != null)
|
||||
throw new InvalidOperationException("Already showing a notification");
|
||||
|
||||
if (this.notificationHasStartedUpdate)
|
||||
throw new InvalidOperationException("Lost track of notification state");
|
||||
|
||||
this.updateNotification = this.notificationManager.AddNotification(notification);
|
||||
this.updateNotification.Dismiss += _ => this.updateNotification = null;
|
||||
this.updateNotification.Dismiss += _ =>
|
||||
{
|
||||
this.updateNotification = null;
|
||||
|
||||
// If the user just clicked off the notification, we don't want to bother them again for quite a while.
|
||||
if (this.notificationHasStartedUpdate)
|
||||
{
|
||||
this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecks;
|
||||
Log.Verbose("User started update, next check at {Time}", this.nextUpdateCheckTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecksIfDismissed;
|
||||
Log.Verbose("User dismissed update notification, next check at {Time}", this.nextUpdateCheckTime);
|
||||
}
|
||||
};
|
||||
|
||||
return this.updateNotification!;
|
||||
}
|
||||
|
||||
private void KickOffAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins)
|
||||
private void KickOffAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins, IActiveNotification? notification = null)
|
||||
{
|
||||
this.autoUpdateTask =
|
||||
Task.Run(() => this.RunAutoUpdates(updatablePlugins))
|
||||
Task.Run(() => this.RunAutoUpdates(updatablePlugins, notification))
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsFaulted)
|
||||
|
|
@ -252,31 +289,29 @@ internal class AutoUpdateManager : IServiceType
|
|||
});
|
||||
}
|
||||
|
||||
private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins)
|
||||
private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins, IActiveNotification? notification = null)
|
||||
{
|
||||
Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count);
|
||||
|
||||
if (updatablePlugins.Count == 0)
|
||||
return;
|
||||
|
||||
var notification = this.GetBaseNotification(new Notification
|
||||
{
|
||||
Title = Locs.NotificationTitleUpdatingPlugins,
|
||||
Content = Locs.NotificationContentPreparingToUpdate(updatablePlugins.Count),
|
||||
Type = NotificationType.Info,
|
||||
InitialDuration = TimeSpan.MaxValue,
|
||||
ShowIndeterminateIfNoExpiry = false,
|
||||
UserDismissable = false,
|
||||
Progress = 0,
|
||||
Icon = INotificationIcon.From(FontAwesomeIcon.Download),
|
||||
Minimized = false,
|
||||
});
|
||||
notification ??= this.GetBaseNotification(new Notification());
|
||||
notification.Title = Locs.NotificationTitleUpdatingPlugins;
|
||||
notification.Content = Locs.NotificationContentPreparingToUpdate(updatablePlugins.Count);
|
||||
notification.Type = NotificationType.Info;
|
||||
notification.InitialDuration = TimeSpan.MaxValue;
|
||||
notification.ShowIndeterminateIfNoExpiry = false;
|
||||
notification.UserDismissable = false;
|
||||
notification.Progress = 0;
|
||||
notification.Icon = INotificationIcon.From(FontAwesomeIcon.Download);
|
||||
notification.Minimized = false;
|
||||
|
||||
var progress = new Progress<PluginManager.PluginUpdateProgress>();
|
||||
progress.ProgressChanged += (_, progress) =>
|
||||
progress.ProgressChanged += (_, updateProgress) =>
|
||||
{
|
||||
notification.Content = Locs.NotificationContentUpdating(progress.CurrentPluginManifest.Name);
|
||||
notification.Progress = (float)progress.PluginsProcessed / progress.TotalPlugins;
|
||||
notification.Content = Locs.NotificationContentUpdating(updateProgress.CurrentPluginManifest.Name);
|
||||
notification.Progress = (float)updateProgress.PluginsProcessed / updateProgress.TotalPlugins;
|
||||
};
|
||||
|
||||
var pluginStates = await this.pluginManager.UpdatePluginsAsync(updatablePlugins, this.isDryRun.Value, true, progress);
|
||||
|
|
@ -288,11 +323,7 @@ internal class AutoUpdateManager : IServiceType
|
|||
notification.DrawActions += _ =>
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(2);
|
||||
if (DalamudComponents.PrimaryButton(Locs.NotificationButtonOpenPluginInstaller))
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenPluginInstaller();
|
||||
notification.DismissNow();
|
||||
}
|
||||
DrawOpenInstallerNotificationButton(true, PluginInstallerOpenKind.InstalledPlugins, notification);
|
||||
};
|
||||
|
||||
// Update the notification to show the final state
|
||||
|
|
@ -328,6 +359,8 @@ internal class AutoUpdateManager : IServiceType
|
|||
{
|
||||
if (updatablePlugins.Count == 0)
|
||||
return;
|
||||
|
||||
this.notificationHasStartedUpdate = false;
|
||||
|
||||
var notification = this.GetBaseNotification(new Notification
|
||||
{
|
||||
|
|
@ -340,23 +373,22 @@ internal class AutoUpdateManager : IServiceType
|
|||
Icon = INotificationIcon.From(FontAwesomeIcon.Download),
|
||||
});
|
||||
|
||||
notification.DrawActions += _ =>
|
||||
void DrawNotificationContent(INotificationDrawArgs args)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(2);
|
||||
|
||||
if (DalamudComponents.PrimaryButton(Locs.NotificationButtonUpdate))
|
||||
{
|
||||
this.KickOffAutoUpdates(updatablePlugins);
|
||||
notification.DismissNow();
|
||||
notification.DrawActions -= DrawNotificationContent;
|
||||
this.KickOffAutoUpdates(updatablePlugins, notification);
|
||||
this.notificationHasStartedUpdate = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (DalamudComponents.SecondaryButton(Locs.NotificationButtonOpenPluginInstaller))
|
||||
{
|
||||
Service<DalamudInterface>.Get().OpenPluginInstaller();
|
||||
notification.DismissNow();
|
||||
}
|
||||
};
|
||||
DrawOpenInstallerNotificationButton(false, PluginInstallerOpenKind.UpdateablePlugins, notification);
|
||||
}
|
||||
|
||||
notification.DrawActions += DrawNotificationContent;
|
||||
}
|
||||
|
||||
private List<AvailablePluginUpdate> GetAvailablePluginUpdates(UpdateListingRestriction restriction)
|
||||
|
|
@ -440,18 +472,24 @@ internal class AutoUpdateManager : IServiceType
|
|||
public static string NotificationContentUpdatesFailedMinimized => Loc.Localize("AutoUpdateUpdatesFailedContentMinimized", "Plugins failed to update.");
|
||||
|
||||
public static string NotificationContentUpdatesAvailable(int numUpdates)
|
||||
=> string.Format(Loc.Localize("AutoUpdateUpdatesAvailableContent", "There are {0} plugins that can be updated."), numUpdates);
|
||||
=> numUpdates == 1 ?
|
||||
Loc.Localize("AutoUpdateUpdatesAvailableContentSingular", "There is a plugin that can be updated.") :
|
||||
string.Format(Loc.Localize("AutoUpdateUpdatesAvailableContentPlural", "There are {0} plugins that can be updated."), numUpdates);
|
||||
|
||||
public static string NotificationContentUpdatesAvailableMinimized(int numUpdates)
|
||||
=> string.Format(Loc.Localize("AutoUpdateUpdatesAvailableContent", "{0} updates available."), numUpdates);
|
||||
=> numUpdates == 1 ?
|
||||
Loc.Localize("AutoUpdateUpdatesAvailableContentMinimizedSingular", "1 plugin update available") :
|
||||
string.Format(Loc.Localize("AutoUpdateUpdatesAvailableContentMinimizedPlural", "{0} plugin updates available"), numUpdates);
|
||||
|
||||
public static string NotificationContentPreparingToUpdate(int numPlugins)
|
||||
=> string.Format(Loc.Localize("AutoUpdatePreparingToUpdate", "Preparing to update {0} plugins..."), numPlugins);
|
||||
=> numPlugins == 1 ?
|
||||
Loc.Localize("AutoUpdatePreparingToUpdateSingular", "Preparing to update 1 plugin...") :
|
||||
string.Format(Loc.Localize("AutoUpdatePreparingToUpdatePlural", "Preparing to update {0} plugins..."), numPlugins);
|
||||
|
||||
public static string NotificationContentUpdating(string name)
|
||||
=> string.Format(Loc.Localize("AutoUpdateUpdating", "Updating {0}..."), name);
|
||||
|
||||
public static string NotificationContentFailedPlugins(IEnumerable<string> failedPlugins)
|
||||
=> string.Format(Loc.Localize("AutoUpdateFailedPlugins", "Failed plugins: {0}"), string.Join(", ", failedPlugins));
|
||||
=> string.Format(Loc.Localize("AutoUpdateFailedPlugins", "Failed plugin(s): {0}"), string.Join(", ", failedPlugins));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1003,9 +1003,6 @@ internal class PluginManager : IInternalDisposableService
|
|||
if (plugin.InstalledPlugin.IsDev)
|
||||
continue;
|
||||
|
||||
if (!plugin.InstalledPlugin.IsWantedByAnyProfile)
|
||||
continue;
|
||||
|
||||
if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion)
|
||||
continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -483,6 +483,7 @@ internal class LocalPlugin : IDisposable
|
|||
{
|
||||
case PluginState.Unloaded:
|
||||
throw new InvalidPluginOperationException($"Unable to unload {this.Name}, already unloaded");
|
||||
case PluginState.DependencyResolutionFailed:
|
||||
case PluginState.UnloadError:
|
||||
if (!this.IsDev)
|
||||
{
|
||||
|
|
@ -501,31 +502,42 @@ internal class LocalPlugin : IDisposable
|
|||
}
|
||||
|
||||
this.State = PluginState.Unloading;
|
||||
Log.Information($"Unloading {this.DllFile.Name}");
|
||||
Log.Information("Unloading {PluginName}", this.InternalName);
|
||||
|
||||
if (this.manifest.CanUnloadAsync || framework == null)
|
||||
this.instance?.Dispose();
|
||||
else
|
||||
await framework.RunOnFrameworkThread(() => this.instance?.Dispose());
|
||||
|
||||
this.instance = null;
|
||||
this.UnloadAndDisposeState();
|
||||
|
||||
if (!reloading)
|
||||
try
|
||||
{
|
||||
if (waitBeforeLoaderDispose && this.loader != null)
|
||||
await Task.Delay(configuration.PluginWaitBeforeFree ?? PluginManager.PluginWaitBeforeFreeDefault);
|
||||
this.loader?.Dispose();
|
||||
this.loader = null;
|
||||
if (this.manifest.CanUnloadAsync || framework == null)
|
||||
this.instance?.Dispose();
|
||||
else
|
||||
await framework.RunOnFrameworkThread(() => this.instance?.Dispose());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.State = PluginState.UnloadError;
|
||||
Log.Error(e, "Could not unload {PluginName}, error in plugin dispose", this.InternalName);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.instance = null;
|
||||
this.UnloadAndDisposeState();
|
||||
|
||||
if (!reloading)
|
||||
{
|
||||
if (waitBeforeLoaderDispose && this.loader != null)
|
||||
await Task.Delay(configuration.PluginWaitBeforeFree ?? PluginManager.PluginWaitBeforeFreeDefault);
|
||||
this.loader?.Dispose();
|
||||
this.loader = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.State = PluginState.Unloaded;
|
||||
Log.Information($"Finished unloading {this.DllFile.Name}");
|
||||
Log.Information("Finished unloading {PluginName}", this.InternalName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.State = PluginState.UnloadError;
|
||||
Log.Error(ex, $"Error while unloading {this.Name}");
|
||||
Log.Error(ex, "Error while unloading {PluginName}", this.InternalName);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue