Add Option to notify instead of auto install.

And General Fixes
This commit is contained in:
Stoia 2025-10-22 18:20:44 +02:00
parent 60aa23efcd
commit f05cb52da2
4 changed files with 72 additions and 14 deletions

View file

@ -78,6 +78,7 @@ public class Configuration : IPluginConfiguration, ISavable, IService
public bool HideMachinistOffhandFromChangedItems { get; set; } = true;
public bool DefaultTemporaryMode { get; set; } = false;
public bool EnableDirectoryWatch { get; set; } = false;
public bool EnableAutomaticModImport { get; set; } = false;
public bool EnableCustomShapes { get; set; } = true;
public PcpSettings PcpSettings = new();
public RenameField ShowRename { get; set; } = RenameField.BothDataPrio;

View file

@ -1,5 +1,6 @@
using System.Threading.Channels;
using OtterGui.Services;
using Penumbra.Services;
using Penumbra.Mods.Manager;
namespace Penumbra.Services;
@ -11,16 +12,16 @@ public class FileWatcher : IDisposable, IService
private readonly Task _consumer;
private readonly ConcurrentDictionary<string, byte> _pending = new(StringComparer.OrdinalIgnoreCase);
private readonly ModImportManager _modImportManager;
private readonly MessageService _messageService;
private readonly Configuration _config;
private readonly bool _enabled;
public FileWatcher(ModImportManager modImportManager, Configuration config)
public FileWatcher(ModImportManager modImportManager, MessageService messageService, Configuration config)
{
_config = config;
_modImportManager = modImportManager;
_enabled = config.EnableDirectoryWatch;
_messageService = messageService;
_config = config;
if (!_enabled) return;
if (!_config.EnableDirectoryWatch) return;
_queue = Channel.CreateBounded<string>(new BoundedChannelOptions(256)
{
@ -55,13 +56,13 @@ public class FileWatcher : IDisposable, IService
private void OnPath(object? sender, FileSystemEventArgs e)
{
// Cheap de-dupe: only queue once per filename until processed
if (!_enabled || !_pending.TryAdd(e.FullPath, 0)) return;
if (!_config.EnableDirectoryWatch || !_pending.TryAdd(e.FullPath, 0)) return;
_ = _queue.Writer.TryWrite(e.FullPath);
}
private async Task ConsumerLoopAsync(CancellationToken token)
{
if (!_enabled) return;
if (!_config.EnableDirectoryWatch) return;
var reader = _queue.Reader;
while (await reader.WaitToReadAsync(token).ConfigureAwait(false))
{
@ -101,8 +102,27 @@ public class FileWatcher : IDisposable, IService
var len = fi.Length;
if (len > 0 && len == lastLen)
{
_modImportManager.AddUnpack(path);
return;
if (_config.EnableAutomaticModImport)
{
_modImportManager.AddUnpack(path);
return;
}
else
{
var invoked = false;
Action<bool> installRequest = args =>
{
if (invoked) return;
invoked = true;
_modImportManager.AddUnpack(path);
};
_messageService.PrintModFoundInfo(
Path.GetFileNameWithoutExtension(path),
installRequest);
return;
}
}
lastLen = len;
@ -116,7 +136,7 @@ public class FileWatcher : IDisposable, IService
public void UpdateDirectory(string newPath)
{
if (!_enabled || _fsw is null || !Directory.Exists(newPath) || string.IsNullOrWhiteSpace(newPath)) return;
if (!_config.EnableDirectoryWatch || _fsw is null || !Directory.Exists(newPath) || string.IsNullOrWhiteSpace(newPath)) return;
_fsw.EnableRaisingEvents = false;
_fsw.Path = newPath;
@ -125,7 +145,7 @@ public class FileWatcher : IDisposable, IService
public void Dispose()
{
if (!_enabled) return;
if (!_config.EnableDirectoryWatch) return;
_fsw.EnableRaisingEvents = false;
_cts.Cancel();
_fsw.Dispose();

View file

@ -1,19 +1,44 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.EventArgs;
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using OtterGui.Log;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Data;
using Penumbra.Mods.Manager;
using Penumbra.String.Classes;
using static OtterGui.Classes.MessageService;
using Notification = OtterGui.Classes.Notification;
namespace Penumbra.Services;
public class InstallNotification(string message, Action<bool> installRequest) : IMessage
{
private readonly Action<bool> _installRequest = installRequest;
private bool _invoked = false;
public string Message { get; } = message;
public NotificationType NotificationType => NotificationType.Info;
public uint NotificationDuration => 10000;
public void OnNotificationActions(INotificationDrawArgs args)
{
if (ImUtf8.ButtonEx("Install"u8, "Install this mod."u8, disabled: _invoked))
{
_installRequest(true);
_invoked = true;
}
}
}
public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INotificationManager notificationManager)
: OtterGui.Classes.MessageService(log, builder, chat, notificationManager), IService
{
@ -55,4 +80,11 @@ public class MessageService(Logger log, IUiBuilder builder, IChatGui chat, INoti
$"Cowardly refusing to load replacement for {originalGamePath.Filename().ToString().ToLowerInvariant()} by {mod.Name}{(messageComplement.Length > 0 ? ":\n" : ".")}{messageComplement}",
NotificationType.Warning, 10000));
}
public void PrintModFoundInfo(string fileName, Action<bool> installRequest)
{
AddMessage(
new InstallNotification($"A new mod has been found: {fileName}", installRequest)
);
}
}

View file

@ -53,6 +53,7 @@ public class SettingsTab : ITab, IUiService
private readonly MigrationSectionDrawer _migrationDrawer;
private readonly CollectionAutoSelector _autoSelector;
private readonly CleanupService _cleanupService;
private readonly MessageService _messageService;
private readonly AttributeHook _attributeHook;
private readonly PcpService _pcpService;
@ -69,7 +70,7 @@ public class SettingsTab : ITab, IUiService
CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, FileWatcher fileWatcher, HttpApi httpApi,
DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor, DalamudConfigService dalamudConfig,
IDataManager gameData, PredefinedTagManager predefinedTagConfig, CrashHandlerService crashService,
MigrationSectionDrawer migrationDrawer, CollectionAutoSelector autoSelector, CleanupService cleanupService,
MigrationSectionDrawer migrationDrawer, CollectionAutoSelector autoSelector, CleanupService cleanupService, MessageService messageService,
AttributeHook attributeHook, PcpService pcpService)
{
_pluginInterface = pluginInterface;
@ -96,6 +97,7 @@ public class SettingsTab : ITab, IUiService
_migrationDrawer = migrationDrawer;
_autoSelector = autoSelector;
_cleanupService = cleanupService;
_messageService = messageService;
_attributeHook = attributeHook;
_pcpService = pcpService;
}
@ -649,9 +651,12 @@ public class SettingsTab : ITab, IUiService
DrawDefaultModImportFolder();
DrawPcpFolder();
DrawDefaultModExportPath();
Checkbox("Enable Automatic Import of Mods from Directory",
"Enables a File Watcher that automatically listens for Mod files that enter, causing Penumbra to automatically import these mods.",
Checkbox("Enable Directory Watcher",
"Enables a File Watcher that automatically listens for Mod files that enter, causing Penumbra to open a Popup to import these mods.",
_config.EnableDirectoryWatch, v => _config.EnableDirectoryWatch = v);
Checkbox("Enable Fully Automatic Import",
"Uses the File Watcher in order to not just open a Popup, but fully automatically import new mods.",
_config.EnableAutomaticModImport, v => _config.EnableAutomaticModImport = v);
DrawFileWatcherPath();
}