diff --git a/Dalamud/Interface/Fools24.cs b/Dalamud/Interface/Fools24.cs new file mode 100644 index 000000000..9fa3eb7b9 --- /dev/null +++ b/Dalamud/Interface/Fools24.cs @@ -0,0 +1,96 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Dalamud.Interface.Internal.Windows; +using Dalamud.Networking.Http; + +using Serilog; + +namespace Dalamud.Interface; + +[ServiceManager.EarlyLoadedService] +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "One-off")] +internal class Fools24 : IServiceType +{ + private readonly HappyHttpClient httpClient; + + private CancellationTokenSource? cancellation; + private Task? horseIconTask = null; + + private string[]? applicableIcons = null; + + [ServiceManager.ServiceConstructor] + public Fools24(HappyHttpClient httpClient) + { + this.httpClient = httpClient; + } + + public bool IsWaitingForIconList => this.horseIconTask?.IsCompleted == false; + + public bool Failed { get; private set; } + + public static bool IsDayApplicable() + { + var utcNow = DateTime.UtcNow; + + var dateAhead = utcNow.AddHours(14); + var dateBehind = utcNow.AddHours(-12); + + return dateAhead is { Day: 1, Month: 4 } || dateBehind is { Day: 1, Month: 4 } || DateTime.Now is { Day: 1, Month: 4 }; + } + + public string? GetHorseIconLink(string internalName) + { + if (this.applicableIcons == null || this.applicableIcons.All(x => x != $"{internalName}.png")) + return null; + + return $"https://raw.githubusercontent.com/goaaats/horse-icons/main/icons/{internalName}.png"; + } + + public void NotifyInstallerWindowOpened() + { + if (!IsDayApplicable()) + return; + + Service.Get().ClearIconCache(); + + if (this.horseIconTask?.IsCompleted == false) + return; + + this.Failed = false; + try + { + this.cancellation = new CancellationTokenSource(); + this.horseIconTask = this.httpClient.SharedHttpClient.GetStringAsync("https://raw.githubusercontent.com/goaaats/horse-icons/main/iconlist.txt", this.cancellation.Token) + .ContinueWith( + f => + { + if (!f.IsCompletedSuccessfully) + { + this.Failed = true; + this.applicableIcons = null; + return; + } + + if (f.Result is not { Length: > 0 }) + { + this.Failed = true; + this.applicableIcons = null; + return; + } + + this.applicableIcons = f.Result.Split( + '\n', + StringSplitOptions.RemoveEmptyEntries); + }); + this.cancellation.CancelAfter(10000); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to fetch horse icons"); + this.Failed = true; + } + } +} diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs index 634999143..9e0ef4df7 100644 --- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs +++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs @@ -191,6 +191,11 @@ internal class PluginImageCache : IInternalDisposableService { iconTexture = null; loadedSince = null; + + // Wait for the horse icon list to be there, if applicable + var fools = Service.Get(); + if (Fools24.IsDayApplicable() && fools.IsWaitingForIconList && !fools.Failed) + return false; if (manifest == null || manifest.InternalName == null) { @@ -638,6 +643,14 @@ internal class PluginImageCache : IInternalDisposableService { if (isThirdParty) return manifest.IconUrl; + + var fools = Service.Get(); + if (Fools24.IsDayApplicable()) + { + var iconLink = fools.GetHorseIconLink(manifest.InternalName); + if (iconLink != null) + return iconLink; + } return MainRepoDip17ImageUrl.Format(manifest.Dip17Channel!, manifest.InternalName, "icon.png"); } diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 5df1694d7..52b65f111 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -268,6 +268,8 @@ internal class PluginInstallerWindow : Window, IDisposable /// public override void OnOpen() { + Service.Get().NotifyInstallerWindowOpened(); + var pluginManager = Service.Get(); _ = pluginManager.ReloadPluginMastersAsync();