mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 13:14:17 +01:00
Make ChatWarningService part of the MessageService.
This commit is contained in:
parent
f0c034c84d
commit
fe4a046cc9
4 changed files with 32 additions and 69 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit c53955cb6199dd418c5a9538d3251ac5942e7067
|
Subproject commit d9486ae54b5a4b61cf74f79ed27daa659eb1ce5b
|
||||||
|
|
@ -4,23 +4,26 @@ using Penumbra.Collections;
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
using Penumbra.GameData.Files.Utility;
|
using Penumbra.GameData.Files.Utility;
|
||||||
using Penumbra.Interop.Hooks.ResourceLoading;
|
using Penumbra.Interop.Hooks.ResourceLoading;
|
||||||
|
using Penumbra.Mods.Manager;
|
||||||
|
using Penumbra.Services;
|
||||||
using Penumbra.String;
|
using Penumbra.String;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.UI;
|
|
||||||
|
|
||||||
namespace Penumbra.Interop.Processing;
|
namespace Penumbra.Interop.Processing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path pre-processor for shader packages that reverts redirects to known invalid files, as bad ShPks can crash the game.
|
/// Path pre-processor for shader packages that reverts redirects to known invalid files, as bad ShPks can crash the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager, ChatWarningService chatWarningService) : IPathPreProcessor
|
public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager, MessageService messager, ModManager modManager)
|
||||||
|
: IPathPreProcessor
|
||||||
{
|
{
|
||||||
public ResourceType Type
|
public ResourceType Type
|
||||||
=> ResourceType.Shpk;
|
=> ResourceType.Shpk;
|
||||||
|
|
||||||
public unsafe FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath originalGamePath, bool nonDefault, FullPath? resolved)
|
public unsafe FullPath? PreProcess(ResolveData resolveData, CiByteString path, Utf8GamePath originalGamePath, bool nonDefault,
|
||||||
|
FullPath? resolved)
|
||||||
{
|
{
|
||||||
chatWarningService.CleanLastFileWarnings(false);
|
messager.CleanTaggedMessages(false);
|
||||||
|
|
||||||
if (!resolved.HasValue)
|
if (!resolved.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -31,7 +34,8 @@ public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager,
|
||||||
return resolvedPath;
|
return resolvedPath;
|
||||||
|
|
||||||
// If the ShPk is already loaded, it means that it already passed the sanity check.
|
// If the ShPk is already loaded, it means that it already passed the sanity check.
|
||||||
var existingResource = resourceManager.FindResource(ResourceCategory.Shader, ResourceType.Shpk, unchecked((uint)resolvedPath.InternalName.Crc32));
|
var existingResource =
|
||||||
|
resourceManager.FindResource(ResourceCategory.Shader, ResourceType.Shpk, unchecked((uint)resolvedPath.InternalName.Crc32));
|
||||||
if (existingResource != null)
|
if (existingResource != null)
|
||||||
return resolvedPath;
|
return resolvedPath;
|
||||||
|
|
||||||
|
|
@ -39,8 +43,7 @@ public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager,
|
||||||
if (checkResult == SanityCheckResult.Success)
|
if (checkResult == SanityCheckResult.Success)
|
||||||
return resolvedPath;
|
return resolvedPath;
|
||||||
|
|
||||||
Penumbra.Log.Warning($"Refusing to honor file redirection because of failed sanity check (result: {checkResult}). Original path: {originalGamePath} Redirected path: {resolvedPath}");
|
messager.PrintFileWarning(modManager, resolvedPath.FullName, originalGamePath, WarningMessageComplement(checkResult));
|
||||||
chatWarningService.PrintFileWarning(resolvedPath.FullName, originalGamePath, WarningMessageComplement(checkResult));
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -49,8 +52,8 @@ public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager,
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var file = MmioMemoryManager.CreateFromFile(path);
|
using var file = MmioMemoryManager.CreateFromFile(path);
|
||||||
var bytes = file.GetSpan();
|
var bytes = file.GetSpan();
|
||||||
|
|
||||||
return ShpkFile.FastIsLegacy(bytes)
|
return ShpkFile.FastIsLegacy(bytes)
|
||||||
? SanityCheckResult.Legacy
|
? SanityCheckResult.Legacy
|
||||||
|
|
@ -69,9 +72,9 @@ public sealed class ShpkPathPreProcessor(ResourceManagerService resourceManager,
|
||||||
private static string WarningMessageComplement(SanityCheckResult result)
|
private static string WarningMessageComplement(SanityCheckResult result)
|
||||||
=> result switch
|
=> result switch
|
||||||
{
|
{
|
||||||
SanityCheckResult.IoError => "cannot read the modded file.",
|
SanityCheckResult.IoError => "Cannot read the modded file.",
|
||||||
SanityCheckResult.NotFound => "the modded file does not exist.",
|
SanityCheckResult.NotFound => "The modded file does not exist.",
|
||||||
SanityCheckResult.Legacy => "this mod is not compatible with Dawntrail. Get an updated version, if possible, or disable it.",
|
SanityCheckResult.Legacy => "This mod is not compatible with Dawntrail. Get an updated version, if possible, or disable it.",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@ using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
using Penumbra.Mods.Manager;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
using Notification = OtterGui.Classes.Notification;
|
||||||
|
|
||||||
namespace Penumbra.Services;
|
namespace Penumbra.Services;
|
||||||
|
|
||||||
|
|
@ -38,4 +42,16 @@ public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INoti
|
||||||
Message = payload,
|
Message = payload,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PrintFileWarning(ModManager modManager, string fullPath, Utf8GamePath originalGamePath, string messageComplement)
|
||||||
|
{
|
||||||
|
// Don't warn for files managed by other plugins, or files we aren't sure about.
|
||||||
|
if (!modManager.TryIdentifyPath(fullPath, out var mod, out _))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AddTaggedMessage($"{fullPath}.{messageComplement}",
|
||||||
|
new Notification(
|
||||||
|
$"Cowardly refusing to load replacement for {originalGamePath.Filename().ToString().ToLowerInvariant()} by {mod.Name}{(messageComplement.Length > 0 ? ":\n" : ".")}{messageComplement}",
|
||||||
|
NotificationType.Warning, 10000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using OtterGui.Services;
|
|
||||||
using Penumbra.Mods.Manager;
|
|
||||||
using Penumbra.String.Classes;
|
|
||||||
|
|
||||||
namespace Penumbra.UI;
|
|
||||||
|
|
||||||
public sealed class ChatWarningService(IChatGui chatGui, IClientState clientState, ModManager modManager) : IUiService
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, (DateTime, string)> _lastFileWarnings = [];
|
|
||||||
private int _lastFileWarningsCleanCounter;
|
|
||||||
|
|
||||||
private const int LastFileWarningsCleanCycle = 100;
|
|
||||||
private static readonly TimeSpan LastFileWarningsMaxAge = new(1, 0, 0);
|
|
||||||
|
|
||||||
public void CleanLastFileWarnings(bool force)
|
|
||||||
{
|
|
||||||
if (!force)
|
|
||||||
{
|
|
||||||
_lastFileWarningsCleanCounter = (_lastFileWarningsCleanCounter + 1) % LastFileWarningsCleanCycle;
|
|
||||||
if (_lastFileWarningsCleanCounter != 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expiredDate = DateTime.Now - LastFileWarningsMaxAge;
|
|
||||||
var toRemove = new HashSet<string>();
|
|
||||||
foreach (var (key, value) in _lastFileWarnings)
|
|
||||||
{
|
|
||||||
if (value.Item1 <= expiredDate)
|
|
||||||
toRemove.Add(key);
|
|
||||||
}
|
|
||||||
foreach (var key in toRemove)
|
|
||||||
_lastFileWarnings.Remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PrintFileWarning(string fullPath, Utf8GamePath originalGamePath, string messageComplement)
|
|
||||||
{
|
|
||||||
CleanLastFileWarnings(true);
|
|
||||||
|
|
||||||
// Don't warn twice for the same file within a certain time interval unless the reason changed.
|
|
||||||
if (_lastFileWarnings.TryGetValue(fullPath, out var lastWarning) && lastWarning.Item2 == messageComplement)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't warn for files managed by other plugins, or files we aren't sure about.
|
|
||||||
if (!modManager.TryIdentifyPath(fullPath, out var mod, out _))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't warn if there's no local player (as an approximation of no chat), so as not to trigger the cooldown.
|
|
||||||
if (clientState.LocalPlayer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// The wording is an allusion to tar's "Cowardly refusing to create an empty archive"
|
|
||||||
chatGui.PrintError($"Cowardly refusing to load replacement for {originalGamePath.Filename().ToString().ToLowerInvariant()} by {mod.Name}{(messageComplement.Length > 0 ? ": " : ".")}{messageComplement}", "Penumbra");
|
|
||||||
_lastFileWarnings[fullPath] = (DateTime.Now, messageComplement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue