Get rid of OtterGui event wrappers.

This commit is contained in:
Ottermandias 2025-09-12 11:34:07 +02:00
parent 3ce02b506e
commit c423ce1d47
103 changed files with 1050 additions and 1024 deletions

2
Luna

@ -1 +1 @@
Subproject commit ece206edc96bc39844e4c6c314dd562fe1b1db55
Subproject commit f39b514304d8f30030310f75d83f99385105c271

View file

@ -1,6 +1,7 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Communication;
using Penumbra.Interop.Hooks.ResourceLoading;
using Penumbra.Interop.PathResolving;
using Penumbra.Interop.Structs;
@ -28,36 +29,24 @@ public class GameStateApi : IPenumbraApiGameState, Luna.IApiService, IDisposable
_resourceLoader.ResourceLoaded += OnResourceLoaded;
_resourceLoader.PapRequested += OnPapRequested;
_communicator.CreatedCharacterBase.Subscribe(OnCreatedCharacterBase, Communication.CreatedCharacterBase.Priority.Api);
_communicator.CreatingCharacterBase.Subscribe(OnCreatingCharacterBase, Communication.CreatingCharacterBase.Priority.Api);
}
private void OnCreatingCharacterBase(in CreatingCharacterBase.Arguments arguments)
=> CreatingCharacterBase?.Invoke(arguments.GameObject.Address, arguments.Collection.Identity.Id, arguments.ModelCharaId, arguments.Customize,
arguments.EquipData);
public unsafe void Dispose()
{
_resourceLoader.ResourceLoaded -= OnResourceLoaded;
_resourceLoader.PapRequested -= OnPapRequested;
_communicator.CreatedCharacterBase.Unsubscribe(OnCreatedCharacterBase);
_communicator.CreatingCharacterBase.Unsubscribe(OnCreatingCharacterBase);
}
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
public event CreatingCharacterBaseDelegate? CreatingCharacterBase
{
add
{
if (value == null)
return;
_communicator.CreatingCharacterBase.Subscribe(new Action<nint, Guid, nint, nint, nint>(value),
Communication.CreatingCharacterBase.Priority.Api);
}
remove
{
if (value == null)
return;
_communicator.CreatingCharacterBase.Unsubscribe(new Action<nint, Guid, nint, nint, nint>(value));
}
}
public event CreatingCharacterBaseDelegate? CreatingCharacterBase;
public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject)
{
@ -117,6 +106,6 @@ public class GameStateApi : IPenumbraApiGameState, Luna.IApiService, IDisposable
}
}
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
=> CreatedCharacterBase?.Invoke(gameObject, collection.Identity.Id, drawObject);
private void OnCreatedCharacterBase(in CreatedCharacterBase.Arguments arguments)
=> CreatedCharacterBase?.Invoke(arguments.GameObject, arguments.Collection.Identity.Id, arguments.DrawObject);
}

View file

@ -6,12 +6,10 @@ using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.Interop.PathResolving;
using Penumbra.Mods;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.Services;
namespace Penumbra.Api.Api;
@ -264,19 +262,18 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Identity.Id, mod.Identifier, parent != collection);
}
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
if (type == ModPathChangeType.Reloaded)
TriggerSettingEdited(mod);
if (arguments.Type is ModPathChangeType.Reloaded)
TriggerSettingEdited(arguments.Mod);
}
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
=> ModSettingChanged?.Invoke(type, collection.Identity.Id, mod?.ModPath.Name ?? string.Empty, inherited);
private void OnModSettingChange(in ModSettingChanged.Arguments arguments)
=> ModSettingChanged?.Invoke(arguments.Type, arguments.Collection.Identity.Id, arguments.Mod?.Identifier ?? string.Empty, arguments.Inherited);
private void OnModOptionEdited(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int moveIndex)
private void OnModOptionEdited(in ModOptionChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModOptionChangeType.GroupDeleted:
case ModOptionChangeType.GroupMoved:
@ -288,17 +285,17 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
case ModOptionChangeType.OptionFilesAdded:
case ModOptionChangeType.OptionSwapsChanged:
case ModOptionChangeType.OptionMetaChanged:
TriggerSettingEdited(mod);
TriggerSettingEdited(arguments.Mod);
break;
}
}
private void OnModFileChanged(Mod mod, FileRegistry file)
private void OnModFileChanged(in ModFileChanged.Arguments arguments)
{
if (file.CurrentUsage == 0)
if (arguments.File.CurrentUsage == 0)
return;
TriggerSettingEdited(mod);
TriggerSettingEdited(arguments.Mod);
}
public static PenumbraApiEc ConvertModSetting(Mod mod, string groupName, IReadOnlyList<string> optionNames, out int groupIndex,

View file

@ -2,7 +2,6 @@ using Luna;
using Newtonsoft.Json.Linq;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
@ -29,16 +28,24 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
_migrationManager = migrationManager;
_log = log;
_communicator.ModPathChanged.Subscribe(OnModPathChanged, ModPathChanged.Priority.ApiMods);
_communicator.PcpCreation.Subscribe(OnPcpCreation, PcpCreation.Priority.ApiMods);
_communicator.PcpParsing.Subscribe(OnPcpParsing, PcpParsing.Priority.ApiMods);
}
private void OnModPathChanged(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory)
private void OnPcpParsing(in PcpParsing.Arguments arguments)
=> ParsingPcp?.Invoke(arguments.JObject, arguments.Mod.Identifier, arguments.Collection?.Identity.Id ?? Guid.Empty);
private void OnPcpCreation(in PcpCreation.Arguments arguments)
=> CreatingPcp?.Invoke(arguments.JObject, arguments.ObjectIndex, arguments.DirectoryPath);
private void OnModPathChanged(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Deleted when oldDirectory != null: ModDeleted?.Invoke(oldDirectory.Name); break;
case ModPathChangeType.Added when newDirectory != null: ModAdded?.Invoke(newDirectory.Name); break;
case ModPathChangeType.Moved when newDirectory != null && oldDirectory != null:
ModMoved?.Invoke(oldDirectory.Name, newDirectory.Name);
case ModPathChangeType.Deleted when arguments.OldDirectory is not null: ModDeleted?.Invoke(arguments.OldDirectory.Name); break;
case ModPathChangeType.Added when arguments.NewDirectory is not null: ModAdded?.Invoke(arguments.NewDirectory.Name); break;
case ModPathChangeType.Moved when arguments is { NewDirectory: not null, OldDirectory: not null }:
ModMoved?.Invoke(arguments.OldDirectory.Name, arguments.NewDirectory.Name);
break;
}
}
@ -46,6 +53,8 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
public void Dispose()
{
_communicator.ModPathChanged.Unsubscribe(OnModPathChanged);
_communicator.PcpCreation.Unsubscribe(OnPcpCreation);
_communicator.PcpParsing.Unsubscribe(OnPcpParsing);
}
public Dictionary<string, string> GetModList()
@ -105,21 +114,11 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
return ApiHelpers.Return(PenumbraApiEc.Success, ApiHelpers.Args("ModDirectory", modDirectory, "ModName", modName));
}
public event Action<string>? ModDeleted;
public event Action<string>? ModAdded;
public event Action<string, string>? ModMoved;
public event Action<JObject, ushort, string>? CreatingPcp
{
add => _communicator.PcpCreation.Subscribe(value!, PcpCreation.Priority.ModsApi);
remove => _communicator.PcpCreation.Unsubscribe(value!);
}
public event Action<JObject, string, Guid>? ParsingPcp
{
add => _communicator.PcpParsing.Subscribe(value!, PcpParsing.Priority.ModsApi);
remove => _communicator.PcpParsing.Unsubscribe(value!);
}
public event Action<string>? ModDeleted;
public event Action<string>? ModAdded;
public event Action<string, string>? ModMoved;
public event Action<JObject, ushort, string>? CreatingPcp;
public event Action<JObject, string, Guid>? ParsingPcp;
public (PenumbraApiEc, string, bool, bool) GetModPath(string modDirectory, string modName)
{
@ -155,7 +154,7 @@ public class ModsApi : IPenumbraApiMods, IApiService, IDisposable
public Dictionary<string, object?> GetChangedItems(string modDirectory, string modName)
=> _modManager.TryGetMod(modDirectory, modName, out var mod)
? mod.ChangedItems.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToInternalObject())
? mod.ChangedItems.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToInternalObject())
: [];
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, object?>> GetChangedItemAdapterDictionary()

View file

@ -1,4 +1,5 @@
using System.Collections.Frozen;
using Luna;
using Newtonsoft.Json;
using Penumbra.Communication;
using Penumbra.Mods;
@ -6,32 +7,47 @@ using Penumbra.Services;
namespace Penumbra.Api.Api;
public class PluginStateApi(Configuration config, CommunicatorService communicator) : IPenumbraApiPluginState, Luna.IApiService
public class PluginStateApi : IPenumbraApiPluginState, IApiService, IDisposable
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
public PluginStateApi(Configuration config, CommunicatorService communicator)
{
_config = config;
_communicator = communicator;
_communicator.ModDirectoryChanged.Subscribe(OnModDirectoryChanged, Communication.ModDirectoryChanged.Priority.Api);
_communicator.EnabledChanged.Subscribe(OnEnabledChanged, EnabledChanged.Priority.Api);
}
private void OnEnabledChanged(in EnabledChanged.Arguments arguments)
=> EnabledChange?.Invoke(arguments.Enabled);
private void OnModDirectoryChanged(in ModDirectoryChanged.Arguments arguments)
=> ModDirectoryChanged?.Invoke(arguments.Directory, arguments.Valid);
public string GetModDirectory()
=> config.ModDirectory;
=> _config.ModDirectory;
public string GetConfiguration()
=> JsonConvert.SerializeObject(config, Formatting.Indented);
=> JsonConvert.SerializeObject(_config, Formatting.Indented);
public event Action<string, bool>? ModDirectoryChanged
{
add => communicator.ModDirectoryChanged.Subscribe(value!, Communication.ModDirectoryChanged.Priority.Api);
remove => communicator.ModDirectoryChanged.Unsubscribe(value!);
}
public event Action<string, bool>? ModDirectoryChanged;
public bool GetEnabledState()
=> config.EnableMods;
=> _config.EnableMods;
public event Action<bool>? EnabledChange
{
add => communicator.EnabledChanged.Subscribe(value!, EnabledChanged.Priority.Api);
remove => communicator.EnabledChanged.Unsubscribe(value!);
}
public event Action<bool>? EnabledChange;
public FrozenSet<string> SupportedFeatures
=> FeatureChecker.SupportedFeatures.ToFrozenSet();
public string[] CheckSupportedFeatures(IEnumerable<string> requiredFeatures)
=> requiredFeatures.Where(f => !FeatureChecker.Supported(f)).ToArray();
public void Dispose()
{
_communicator.ModDirectoryChanged.Unsubscribe(OnModDirectoryChanged);
_communicator.EnabledChanged.Unsubscribe(OnEnabledChanged);
}
}

View file

@ -1,6 +1,6 @@
using FFXIVClientStructs.FFXIV.Common.Lua;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.UI;
@ -20,41 +20,40 @@ public class UiApi : IPenumbraApiUi, Luna.IApiService, IDisposable
_modManager = modManager;
_communicator.ChangedItemHover.Subscribe(OnChangedItemHover, ChangedItemHover.Priority.Default);
_communicator.ChangedItemClick.Subscribe(OnChangedItemClick, ChangedItemClick.Priority.Default);
_communicator.PreSettingsTabBarDraw.Subscribe(OnPreSettingsTabBarDraw, Communication.PreSettingsTabBarDraw.Priority.Default);
_communicator.PreSettingsPanelDraw.Subscribe(OnPreSettingsPanelDraw, Communication.PreSettingsPanelDraw.Priority.Default);
_communicator.PostEnabledDraw.Subscribe(OnPostEnabledDraw, Communication.PostEnabledDraw.Priority.Default);
_communicator.PostSettingsPanelDraw.Subscribe(OnPostSettingsPanelDraw, Communication.PostSettingsPanelDraw.Priority.Default);
}
private void OnPostSettingsPanelDraw(in PostSettingsPanelDraw.Arguments arguments)
=> PostSettingsPanelDraw?.Invoke(arguments.Mod.Identifier);
private void OnPostEnabledDraw(in PostEnabledDraw.Arguments arguments)
=> PostEnabledDraw?.Invoke(arguments.Mod.Identifier);
private void OnPreSettingsPanelDraw(in PreSettingsPanelDraw.Arguments arguments)
=> PreSettingsPanelDraw?.Invoke(arguments.Mod.Identifier);
private void OnPreSettingsTabBarDraw(in PreSettingsTabBarDraw.Arguments arguments)
=> PreSettingsTabBarDraw?.Invoke(arguments.Mod.Identifier, arguments.HeaderWidth, arguments.TitleBoxWidth);
public void Dispose()
{
_communicator.ChangedItemHover.Unsubscribe(OnChangedItemHover);
_communicator.ChangedItemClick.Unsubscribe(OnChangedItemClick);
_communicator.PreSettingsTabBarDraw.Unsubscribe(OnPreSettingsTabBarDraw);
_communicator.PreSettingsPanelDraw.Unsubscribe(OnPreSettingsPanelDraw);
_communicator.PostEnabledDraw.Unsubscribe(OnPostEnabledDraw);
_communicator.PostSettingsPanelDraw.Unsubscribe(OnPostSettingsPanelDraw);
}
public event Action<ChangedItemType, uint>? ChangedItemTooltip;
public event Action<ChangedItemType, uint>? ChangedItemTooltip;
public event Action<MouseButton, ChangedItemType, uint>? ChangedItemClicked;
public event Action<string, float, float>? PreSettingsTabBarDraw
{
add => _communicator.PreSettingsTabBarDraw.Subscribe(value!, Communication.PreSettingsTabBarDraw.Priority.Default);
remove => _communicator.PreSettingsTabBarDraw.Unsubscribe(value!);
}
public event Action<string>? PreSettingsPanelDraw
{
add => _communicator.PreSettingsPanelDraw.Subscribe(value!, Communication.PreSettingsPanelDraw.Priority.Default);
remove => _communicator.PreSettingsPanelDraw.Unsubscribe(value!);
}
public event Action<string>? PostEnabledDraw
{
add => _communicator.PostEnabledDraw.Subscribe(value!, Communication.PostEnabledDraw.Priority.Default);
remove => _communicator.PostEnabledDraw.Unsubscribe(value!);
}
public event Action<string>? PostSettingsPanelDraw
{
add => _communicator.PostSettingsPanelDraw.Subscribe(value!, Communication.PostSettingsPanelDraw.Priority.Default);
remove => _communicator.PostSettingsPanelDraw.Unsubscribe(value!);
}
public event Action<string, float, float>? PreSettingsTabBarDraw;
public event Action<string>? PreSettingsPanelDraw;
public event Action<string>? PostEnabledDraw;
public event Action<string>? PostSettingsPanelDraw;
public PenumbraApiEc OpenMainWindow(TabType tab, string modDirectory, string modName)
{
@ -65,13 +64,13 @@ public class UiApi : IPenumbraApiUi, Luna.IApiService, IDisposable
if (tab == TabType.Mods && (modDirectory.Length > 0 || modName.Length > 0))
{
if (_modManager.TryGetMod(modDirectory, modName, out var mod))
_communicator.SelectTab.Invoke(tab, mod);
_communicator.SelectTab.Invoke(new SelectTab.Arguments(tab, mod));
else
return PenumbraApiEc.ModMissing;
}
else if (tab != TabType.None)
{
_communicator.SelectTab.Invoke(tab, null);
_communicator.SelectTab.Invoke(new SelectTab.Arguments(tab, null));
}
return PenumbraApiEc.Success;
@ -80,21 +79,21 @@ public class UiApi : IPenumbraApiUi, Luna.IApiService, IDisposable
public void CloseMainWindow()
=> _configWindow.IsOpen = false;
private void OnChangedItemClick(MouseButton button, IIdentifiedObjectData data)
private void OnChangedItemClick(in ChangedItemClick.Arguments arguments)
{
if (ChangedItemClicked == null)
return;
var (type, id) = data.ToApiObject();
ChangedItemClicked.Invoke(button, type, id);
var (type, id) = arguments.Data.ToApiObject();
ChangedItemClicked.Invoke(arguments.Button, type, id);
}
private void OnChangedItemHover(IIdentifiedObjectData data)
private void OnChangedItemHover(in ChangedItemHover.Arguments arguments)
{
if (ChangedItemTooltip == null)
return;
var (type, id) = data.ToApiObject();
var (type, id) = arguments.Data.ToApiObject();
ChangedItemTooltip.Invoke(type, id);
}
}

View file

@ -1,9 +1,7 @@
using Dalamud.Interface;
using Dalamud.Plugin.Services;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.Mods.Editor;
using Penumbra.Services;
using Penumbra.String.Classes;
@ -74,42 +72,41 @@ public class DalamudSubstitutionProvider : IDisposable, Luna.IApiService
public void Dispose()
=> Unsubscribe();
private void OnCollectionChange(CollectionType type, ModCollection? oldCollection, ModCollection? newCollection, string _)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (type is not CollectionType.Interface)
if (arguments.Type is not CollectionType.Interface)
return;
var enumerable = oldCollection?.ResolvedFiles.Keys ?? Array.Empty<Utf8GamePath>().AsEnumerable();
enumerable = enumerable.Concat(newCollection?.ResolvedFiles.Keys ?? Array.Empty<Utf8GamePath>().AsEnumerable());
var enumerable = arguments.OldCollection?.ResolvedFiles.Keys ?? Array.Empty<Utf8GamePath>().AsEnumerable();
enumerable = enumerable.Concat(arguments.NewCollection?.ResolvedFiles.Keys ?? Array.Empty<Utf8GamePath>().AsEnumerable());
ResetSubstitutions(enumerable);
}
private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath key, FullPath _1, FullPath _2,
IMod? _3)
private void OnResolvedFileChange(in ResolvedFileChanged.Arguments arguments)
{
if (_activeCollectionData.Interface != collection)
if (_activeCollectionData.Interface != arguments.Collection)
return;
switch (type)
switch (arguments.Type)
{
case ResolvedFileChanged.Type.Added:
case ResolvedFileChanged.Type.Removed:
case ResolvedFileChanged.Type.Replaced:
ResetSubstitutions([key]);
ResetSubstitutions([arguments.GamePath]);
break;
case ResolvedFileChanged.Type.FullRecomputeStart:
case ResolvedFileChanged.Type.FullRecomputeFinished:
ResetSubstitutions(collection.ResolvedFiles.Keys);
ResetSubstitutions(arguments.Collection.ResolvedFiles.Keys);
break;
}
}
private void OnEnabledChange(bool state)
private void OnEnabledChange(in EnabledChanged.Arguments arguments)
{
if (state)
OnCollectionChange(CollectionType.Interface, null, _activeCollectionData.Interface, string.Empty);
if (arguments.Enabled)
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Interface, null, _activeCollectionData.Interface, string.Empty));
else
OnCollectionChange(CollectionType.Interface, _activeCollectionData.Interface, null, string.Empty);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Interface, _activeCollectionData.Interface, null, string.Empty));
}
private void Substitute(string path, ref string? replacementPath)
@ -146,7 +143,7 @@ public class DalamudSubstitutionProvider : IDisposable, Luna.IApiService
_communicator.CollectionChange.Subscribe(OnCollectionChange, CollectionChange.Priority.DalamudSubstitutionProvider);
_communicator.ResolvedFileChanged.Subscribe(OnResolvedFileChange, ResolvedFileChanged.Priority.DalamudSubstitutionProvider);
_communicator.EnabledChanged.Subscribe(OnEnabledChange, EnabledChanged.Priority.DalamudSubstitutionProvider);
OnCollectionChange(CollectionType.Interface, null, _activeCollectionData.Interface, string.Empty);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Interface, null, _activeCollectionData.Interface, string.Empty));
}
private void Unsubscribe()
@ -155,6 +152,6 @@ public class DalamudSubstitutionProvider : IDisposable, Luna.IApiService
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
_communicator.ResolvedFileChanged.Unsubscribe(OnResolvedFileChange);
_communicator.EnabledChanged.Unsubscribe(OnEnabledChange);
OnCollectionChange(CollectionType.Interface, _activeCollectionData.Interface, null, string.Empty);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Interface, _activeCollectionData.Interface, null, string.Empty));
}
}

View file

@ -86,19 +86,20 @@ public class TempModManager : IDisposable, Luna.IService
{
Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.Identity.AnonymizedName}.");
collection.Remove(mod);
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
_communicator.ModSettingChanged.Invoke(new ModSettingChanged.Arguments(ModSettingChange.TemporaryMod, collection, null, Setting.False, 0, false));
}
else
{
Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.Identity.AnonymizedName}.");
Penumbra.Log.Verbose(
$"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.Identity.AnonymizedName}.");
collection.Apply(mod, created);
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false);
_communicator.ModSettingChanged.Invoke(new ModSettingChanged.Arguments(ModSettingChange.TemporaryMod, collection, null, Setting.True, 0, false));
}
}
else
{
Penumbra.Log.Verbose($"Triggering global mod change for {(created ? "new " : string.Empty)}temporary Mod {mod.Name}.");
_communicator.TemporaryGlobalModChange.Invoke(mod, created, removed);
_communicator.TemporaryGlobalModChange.Invoke(new TemporaryGlobalModChange.Arguments(mod, created, removed));
}
}
@ -153,10 +154,11 @@ public class TempModManager : IDisposable, Luna.IService
return mod;
}
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection,
string _)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (collectionType is CollectionType.Temporary or CollectionType.Inactive && newCollection == null && oldCollection != null)
_mods.Remove(oldCollection);
if (arguments.Type is CollectionType.Temporary or CollectionType.Inactive
&& arguments.NewCollection is null
&& arguments.OldCollection is not null)
_mods.Remove(arguments.OldCollection);
}
}

View file

@ -188,7 +188,7 @@ public sealed class CollectionCache : IDisposable
Penumbra.Log.Warning(
$"Invalid mod state, removing {mod.Name} and associated file {path} returned current mod {mp.Mod.Name}.");
else
_manager.ResolvedFileChanged.Invoke(_collection, ResolvedFileChanged.Type.Removed, path, FullPath.Empty, mp.Path, mp.Mod);
_manager.ResolvedFileChanged.Invoke(new ResolvedFileChanged.Arguments(ResolvedFileChanged.Type.Removed, _collection, path, FullPath.Empty, mp.Path, mp.Mod));
}
}
@ -275,7 +275,7 @@ public sealed class CollectionCache : IDisposable
FullPath old, IMod? mod)
{
if (Calculating == -1)
_manager.ResolvedFileChanged.Invoke(collection, type, key, value, old, mod);
_manager.ResolvedFileChanged.Invoke(new ResolvedFileChanged.Arguments(type, collection, key, value, old, mod));
}
private static bool IsRedirectionSupported(Utf8GamePath path, IMod mod)

View file

@ -7,11 +7,9 @@ using Penumbra.Communication;
using Penumbra.Interop.Hooks.ResourceLoading;
using Penumbra.Meta;
using Penumbra.Mods;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.Services;
using Penumbra.String.Classes;
@ -70,7 +68,8 @@ public class CollectionCacheManager : IDisposable, IService
_communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.CollectionCacheManager);
if (!MetaFileManager.CharacterUtility.Ready)
MetaFileManager.CharacterUtility.LoadingFinished.Subscribe(IncrementCounters, CharacterUtilityFinished.Priority.CollectionCacheManager);
MetaFileManager.CharacterUtility.LoadingFinished.Subscribe(IncrementCounters,
CharacterUtilityFinished.Priority.CollectionCacheManager);
}
public void Dispose()
@ -140,7 +139,8 @@ public class CollectionCacheManager : IDisposable, IService
if (collection.Identity.Index == 0)
return;
Penumbra.Log.Debug($"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName}");
Penumbra.Log.Debug(
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName}");
if (!collection.HasCache)
{
Penumbra.Log.Error(
@ -169,8 +169,9 @@ public class CollectionCacheManager : IDisposable, IService
cache.Calculating = Environment.CurrentManagedThreadId;
try
{
ResolvedFileChanged.Invoke(collection, ResolvedFileChanged.Type.FullRecomputeStart, Utf8GamePath.Empty, FullPath.Empty,
FullPath.Empty, null);
ResolvedFileChanged.Invoke(new ResolvedFileChanged.Arguments(ResolvedFileChanged.Type.FullRecomputeStart, collection,
Utf8GamePath.Empty, FullPath.Empty,
FullPath.Empty, null));
cache.ResolvedFiles.Clear();
cache.Meta.Reset();
cache.ConflictDict.Clear();
@ -188,9 +189,9 @@ public class CollectionCacheManager : IDisposable, IService
collection.Counters.IncrementChange();
MetaFileManager.ApplyDefaultFiles(collection);
ResolvedFileChanged.Invoke(collection, ResolvedFileChanged.Type.FullRecomputeFinished, Utf8GamePath.Empty, FullPath.Empty,
FullPath.Empty,
null);
ResolvedFileChanged.Invoke(new ResolvedFileChanged.Arguments(ResolvedFileChanged.Type.FullRecomputeFinished, collection,
Utf8GamePath.Empty, FullPath.Empty,
FullPath.Empty, null));
}
finally
{
@ -198,63 +199,69 @@ public class CollectionCacheManager : IDisposable, IService
}
}
private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? newCollection, string displayName)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (type is CollectionType.Temporary)
if (arguments.Type is CollectionType.Temporary)
{
if (newCollection != null && CreateCache(newCollection))
CalculateEffectiveFileList(newCollection);
if (arguments.NewCollection is not null && CreateCache(arguments.NewCollection))
CalculateEffectiveFileList(arguments.NewCollection);
if (old != null)
ClearCache(old);
if (arguments.OldCollection is not null)
ClearCache(arguments.OldCollection);
}
else
{
RemoveCache(old);
if (type is not CollectionType.Inactive && newCollection != null && newCollection.Identity.Index != 0 && CreateCache(newCollection))
CalculateEffectiveFileList(newCollection);
RemoveCache(arguments.OldCollection);
if (arguments.Type is not CollectionType.Inactive
&& arguments.NewCollection is not null
&& arguments.NewCollection.Identity.Index is not 0
&& CreateCache(arguments.NewCollection))
CalculateEffectiveFileList(arguments.NewCollection);
if (type is CollectionType.Default)
if (newCollection != null)
MetaFileManager.ApplyDefaultFiles(newCollection);
if (arguments.Type is CollectionType.Default)
if (arguments.NewCollection is not null)
MetaFileManager.ApplyDefaultFiles(arguments.NewCollection);
else
MetaFileManager.CharacterUtility.ResetAll();
}
}
private void OnModChangeRemoval(ModPathChangeType type, Mod mod, DirectoryInfo? oldModPath, DirectoryInfo? newModPath)
private void OnModChangeRemoval(in ModPathChanged.Arguments arguments)
{
switch (type)
var index = arguments.Mod.Index;
switch (arguments.Type)
{
case ModPathChangeType.Deleted:
case ModPathChangeType.StartingReload:
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(mod.Index).Settings?.Enabled == true))
collection._cache!.RemoveMod(mod, true);
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(index).Settings?.Enabled == true))
collection._cache!.RemoveMod(arguments.Mod, true);
break;
case ModPathChangeType.Moved:
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(mod.Index).Settings?.Enabled == true))
collection._cache!.ReloadMod(mod, true);
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(index).Settings?.Enabled == true))
collection._cache!.ReloadMod(arguments.Mod, true);
break;
}
}
private void OnModChangeAddition(ModPathChangeType type, Mod mod, DirectoryInfo? oldModPath, DirectoryInfo? newModPath)
private void OnModChangeAddition(in ModPathChanged.Arguments arguments)
{
if (type is not (ModPathChangeType.Added or ModPathChangeType.Reloaded))
if (arguments.Type is not ModPathChangeType.Added and not ModPathChangeType.Reloaded)
return;
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(mod.Index).Settings?.Enabled == true))
collection._cache!.AddMod(mod, true);
var index = arguments.Mod.Index;
foreach (var collection in _storage.Where(c => c.HasCache && c.GetActualSettings(index).Settings?.Enabled == true))
collection._cache!.AddMod(arguments.Mod, true);
}
/// <summary> Apply a mod change to all collections with a cache. </summary>
private void OnGlobalModChange(TemporaryMod mod, bool created, bool removed)
=> TempModManager.OnGlobalModChange(_storage.Where(c => c.HasCache), mod, created, removed);
private void OnGlobalModChange(in TemporaryGlobalModChange.Arguments arguments)
=> TempModManager.OnGlobalModChange(_storage.Where(c => c.HasCache), arguments.Mod, arguments.NewlyCreated, arguments.Deleted);
/// <summary> Remove a cache from a collection if it is active. </summary>
private void RemoveCache(ModCollection? collection)
{
// ReSharper disable InconsistentlySynchronizedField
if (collection != null
&& collection.Identity.Index > ModCollection.Empty.Identity.Index
&& collection.Identity.Index != _active.Default.Identity.Index
@ -263,31 +270,35 @@ public class CollectionCacheManager : IDisposable, IService
&& _active.SpecialAssignments.All(c => c.Value.Identity.Index != collection.Identity.Index)
&& _active.Individuals.All(c => c.Collection.Identity.Index != collection.Identity.Index))
ClearCache(collection);
// ReSharper restore InconsistentlySynchronizedField
}
/// <summary> Prepare Changes by removing mods from caches with collections or add or reload mods. </summary>
private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int movedToIdx)
private void OnModOptionChange(in ModOptionChanged.Arguments arguments)
{
if (type is ModOptionChangeType.PrepareChange)
if (arguments.Type is ModOptionChangeType.PrepareChange)
{
foreach (var collection in _storage.Where(collection => collection.HasCache && collection.GetActualSettings(mod.Index).Settings is { Enabled: true }))
collection._cache!.RemoveMod(mod, false);
return;
var index = arguments.Mod.Index;
foreach (var collection in _storage.Where(collection
=> collection.HasCache && collection.GetActualSettings(index).Settings is { Enabled: true }))
collection._cache!.RemoveMod(arguments.Mod, false);
}
type.HandlingInfo(out _, out var recomputeList, out var justAdd);
if (!recomputeList)
return;
foreach (var collection in _storage.Where(collection => collection.HasCache && collection.GetActualSettings(mod.Index).Settings is { Enabled: true }))
else
{
if (justAdd)
collection._cache!.AddMod(mod, true);
else
collection._cache!.ReloadMod(mod, true);
arguments.Type.HandlingInfo(out _, out var recomputeList, out var justAdd);
if (!recomputeList)
return;
var index = arguments.Mod.Index;
foreach (var collection in _storage.Where(collection
=> collection.HasCache && collection.GetActualSettings(index).Settings is { Enabled: true }))
{
if (justAdd)
collection._cache!.AddMod(arguments.Mod, true);
else
collection._cache!.ReloadMod(arguments.Mod, true);
}
}
}
@ -299,41 +310,38 @@ public class CollectionCacheManager : IDisposable, IService
MetaFileManager.CharacterUtility.LoadingFinished.Unsubscribe(IncrementCounters);
}
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool _)
private void OnModSettingChange(in ModSettingChanged.Arguments arguments)
{
var collection = arguments.Collection;
if (!collection.HasCache)
return;
var cache = collection._cache!;
switch (type)
switch (arguments.Type)
{
case ModSettingChange.Inheritance:
cache.ReloadMod(mod!, true);
break;
case ModSettingChange.Inheritance: cache.ReloadMod(arguments.Mod!, true); break;
case ModSettingChange.EnableState:
if (oldValue == Setting.False)
cache.AddMod(mod!, true);
else if (oldValue == Setting.True)
cache.RemoveMod(mod!, true);
else if (collection.GetActualSettings(mod!.Index).Settings?.Enabled == true)
cache.ReloadMod(mod!, true);
if (arguments.OldValue == Setting.False)
cache.AddMod(arguments.Mod!, true);
else if (arguments.OldValue == Setting.True)
cache.RemoveMod(arguments.Mod!, true);
else if (collection.GetActualSettings(arguments.Mod!.Index).Settings?.Enabled == true)
cache.ReloadMod(arguments.Mod!, true);
else
cache.RemoveMod(mod!, true);
cache.RemoveMod(arguments.Mod!, true);
break;
case ModSettingChange.Priority:
if (cache.Conflicts(mod!).Count > 0)
cache.ReloadMod(mod!, true);
if (cache.Conflicts(arguments.Mod!).Count > 0)
cache.ReloadMod(arguments.Mod!, true);
break;
case ModSettingChange.Setting:
if (collection.GetActualSettings(mod!.Index).Settings?.Enabled == true)
cache.ReloadMod(mod, true);
if (collection.GetActualSettings(arguments.Mod!.Index).Settings?.Enabled == true)
cache.ReloadMod(arguments.Mod, true);
break;
case ModSettingChange.TemporarySetting:
cache.ReloadMod(mod!, true);
break;
case ModSettingChange.TemporarySetting: cache.ReloadMod(arguments.Mod!, true); break;
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
FullRecalculation(collection);
@ -349,8 +357,8 @@ public class CollectionCacheManager : IDisposable, IService
/// Inheritance changes are too big to check for relevance,
/// just recompute everything.
/// </summary>
private void OnCollectionInheritanceChange(ModCollection collection, bool _)
=> FullRecalculation(collection);
private void OnCollectionInheritanceChange(in CollectionInheritanceChanged.Arguments arguments)
=> FullRecalculation(arguments.Collection);
/// <summary> Clear the current cache of a collection. </summary>
private void ClearCache(ModCollection collection)

View file

@ -126,7 +126,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
return false;
SpecialCollections[(int)collectionType] = Default;
_communicator.CollectionChange.Invoke(collectionType, null, Default, string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(collectionType, null, Default, string.Empty));
return true;
}
@ -141,14 +141,14 @@ public class ActiveCollections : ISavable, IDisposable, IService
return;
SpecialCollections[(int)collectionType] = null;
_communicator.CollectionChange.Invoke(collectionType, old, null, string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(collectionType, old, null, string.Empty));
}
/// <summary>Create an individual collection if possible. </summary>
public void CreateIndividualCollection(params ActorIdentifier[] identifiers)
{
if (Individuals.Add(identifiers, Default))
_communicator.CollectionChange.Invoke(CollectionType.Individual, null, Default, Individuals.Last().DisplayName);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Individual, null, Default, Individuals.Last().DisplayName));
}
/// <summary> Remove an individual collection if it exists. </summary>
@ -159,7 +159,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
var (name, old) = Individuals[individualIndex];
if (Individuals.Delete(individualIndex))
_communicator.CollectionChange.Invoke(CollectionType.Individual, old, null, name);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Individual, old, null, name));
}
/// <summary> Move an individual collection from one index to another. </summary>
@ -234,8 +234,8 @@ public class ActiveCollections : ISavable, IDisposable, IService
}
UpdateCurrentCollectionInUse();
_communicator.CollectionChange.Invoke(collectionType, oldCollection, collection,
collectionType == CollectionType.Individual ? Individuals[individualIndex].DisplayName : string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(collectionType, oldCollection, collection,
collectionType == CollectionType.Individual ? Individuals[individualIndex].DisplayName : string.Empty));
}
public string ToFilePath(FilenameService fileNames)
@ -275,38 +275,38 @@ public class ActiveCollections : ISavable, IDisposable, IService
.SelectMany(c => c.Inheritance.FlatHierarchy).Contains(Current);
/// <summary> Save if any of the active collections is changed and set new collections to Current. </summary>
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string _3)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (collectionType is CollectionType.Inactive)
if (arguments.Type is CollectionType.Inactive)
{
if (newCollection != null)
if (arguments.NewCollection is not null)
{
SetCollection(newCollection, CollectionType.Current);
SetCollection(arguments.NewCollection, CollectionType.Current);
}
else if (oldCollection != null)
else if (arguments.OldCollection != null)
{
if (oldCollection == Default)
if (arguments.OldCollection == Default)
SetCollection(ModCollection.Empty, CollectionType.Default);
if (oldCollection == Interface)
if (arguments.OldCollection == Interface)
SetCollection(ModCollection.Empty, CollectionType.Interface);
if (oldCollection == Current)
if (arguments.OldCollection == Current)
SetCollection(Default.Identity.Index > ModCollection.Empty.Identity.Index ? Default : _storage.DefaultNamed,
CollectionType.Current);
for (var i = 0; i < SpecialCollections.Length; ++i)
{
if (oldCollection == SpecialCollections[i])
if (arguments.OldCollection == SpecialCollections[i])
SetCollection(ModCollection.Empty, (CollectionType)i);
}
for (var i = 0; i < Individuals.Count; ++i)
{
if (oldCollection == Individuals[i].Collection)
if (arguments.OldCollection == Individuals[i].Collection)
SetCollection(ModCollection.Empty, CollectionType.Individual, i);
}
}
}
else if (collectionType is not CollectionType.Temporary)
else if (arguments.Type is not CollectionType.Temporary)
{
_saveService.DelaySave(this);
}

View file

@ -1,4 +1,5 @@
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Settings;
@ -208,7 +209,7 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
{
if (type is not ModSettingChange.TemporarySetting)
saveService.QueueSave(new ModCollectionSave(modStorage, changedCollection));
communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false);
communicator.ModSettingChanged.Invoke(new ModSettingChanged.Arguments(type, changedCollection, mod, oldValue, groupIdx, false));
if (type is not ModSettingChange.TemporarySetting)
RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx);
}
@ -223,11 +224,11 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
{
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
communicator.ModSettingChanged.Invoke(directInheritor, type, null, oldValue, groupIdx, true);
communicator.ModSettingChanged.Invoke(new ModSettingChanged.Arguments(type, directInheritor, null, oldValue, groupIdx, true));
break;
default:
if (directInheritor.GetOwnSettings(mod!.Index) == null)
communicator.ModSettingChanged.Invoke(directInheritor, type, mod, oldValue, groupIdx, true);
communicator.ModSettingChanged.Invoke(new ModSettingChanged.Arguments(type, directInheritor, mod, oldValue, groupIdx, true));
break;
}

View file

@ -1,13 +1,9 @@
using Dalamud.Interface.ImGuiNotification;
using Luna;
using Penumbra.Communication;
using Penumbra.Mods;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
using Penumbra.Services;
namespace Penumbra.Collections.Manager;
@ -21,7 +17,7 @@ public readonly record struct LocalCollectionId(int Id) : IAdditionOperators<Loc
=> new(left.Id + right);
}
public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, Luna.IService
public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, IService
{
private readonly CommunicatorService _communicator;
private readonly SaveService _saveService;
@ -157,7 +153,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, Luna
_collections.Add(newCollection);
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.Identity.AnonymizedName}.", NotificationType.Success, false);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Inactive, null, newCollection, string.Empty));
return true;
}
@ -187,7 +183,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, Luna
_collectionsByLocal.Remove(collection.Identity.LocalId);
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.Identity.AnonymizedName}.", NotificationType.Success, false);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Inactive, collection, null, string.Empty));
return true;
}
@ -321,29 +317,29 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, Luna
}
/// <summary> Add or remove a mod from all collections, or re-save all collections where the mod has settings. </summary>
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
DirectoryInfo? newDirectory)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Added:
foreach (var collection in this)
collection.Settings.AddMod(mod);
collection.Settings.AddMod(arguments.Mod);
break;
case ModPathChangeType.Deleted:
foreach (var collection in this)
collection.Settings.RemoveMod(mod);
collection.Settings.RemoveMod(arguments.Mod);
break;
case ModPathChangeType.Moved:
foreach (var collection in this.Where(collection => collection.GetOwnSettings(mod.Index) != null))
var index = arguments.Mod.Index;
foreach (var collection in this.Where(collection => collection.GetOwnSettings(index) is not null))
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
break;
case ModPathChangeType.Reloaded:
foreach (var collection in this)
{
if (collection.GetOwnSettings(mod.Index)?.Settings.FixAll(mod) ?? false)
if (collection.GetOwnSettings(arguments.Mod.Index)?.Settings.FixAll(arguments.Mod) ?? false)
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
collection.Settings.SetTemporary(mod.Index, null);
collection.Settings.SetTemporary(arguments.Mod.Index, null);
}
break;
@ -351,30 +347,29 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, Luna
}
/// <summary> Save all collections where the mod has settings and the change requires saving. </summary>
private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int movedToIdx)
private void OnModOptionChange(in ModOptionChanged.Arguments arguments)
{
type.HandlingInfo(out var requiresSaving, out _, out _);
arguments.Type.HandlingInfo(out var requiresSaving, out _, out _);
if (!requiresSaving)
return;
foreach (var collection in this)
{
if (collection.GetOwnSettings(mod.Index)?.HandleChanges(type, mod, group, option, movedToIdx) ?? false)
if (collection.GetOwnSettings(arguments.Mod.Index)?.HandleChanges(arguments.Type, arguments.Mod, arguments.Group, arguments.Option, arguments.DeletedIndex) ?? false)
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
collection.Settings.SetTemporary(mod.Index, null);
collection.Settings.SetTemporary(arguments.Mod.Index, null);
}
}
/// <summary> Update change counters when changing files. </summary>
private void OnModFileChanged(Mod mod, FileRegistry file)
private void OnModFileChanged(in ModFileChanged.Arguments arguments)
{
if (file.CurrentUsage == 0)
if (arguments.File.CurrentUsage == 0)
return;
foreach (var collection in this)
{
var (settings, _) = collection.GetActualSettings(mod.Index);
var (settings, _) = collection.GetActualSettings(arguments.Mod.Index);
if (settings is { Enabled: true })
collection.Counters.IncrementChange();
}

View file

@ -11,7 +11,7 @@ namespace Penumbra.Collections.Manager;
/// This is transitive, so a collection A inheriting from B also inherits from everything B inherits.
/// Circular dependencies are resolved by distinctness.
/// </summary>
public class InheritanceManager : IDisposable, Luna.IService
public class InheritanceManager : IDisposable, IService
{
public enum ValidInheritance
{
@ -82,7 +82,7 @@ public class InheritanceManager : IDisposable, Luna.IService
{
var parent = inheritor.Inheritance.RemoveInheritanceAt(inheritor, idx);
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
_communicator.CollectionInheritanceChanged.Invoke(new CollectionInheritanceChanged.Arguments(inheritor, false));
RecurseInheritanceChanges(inheritor, true);
Penumbra.Log.Debug($"Removed {parent.Identity.AnonymizedName} from {inheritor.Identity.AnonymizedName} inheritances.");
}
@ -94,7 +94,7 @@ public class InheritanceManager : IDisposable, Luna.IService
return;
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
_communicator.CollectionInheritanceChanged.Invoke(new CollectionInheritanceChanged.Arguments(inheritor, false));
RecurseInheritanceChanges(inheritor, true);
Penumbra.Log.Debug($"Moved {inheritor.Identity.AnonymizedName}s inheritance {from} to {to}.");
}
@ -109,7 +109,7 @@ public class InheritanceManager : IDisposable, Luna.IService
if (invokeEvent)
{
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
_communicator.CollectionInheritanceChanged.Invoke(new CollectionInheritanceChanged.Arguments(inheritor, false));
}
RecurseInheritanceChanges(inheritor, invokeEvent);
@ -167,18 +167,18 @@ public class InheritanceManager : IDisposable, Luna.IService
}
}
private void OnCollectionChange(CollectionType collectionType, ModCollection? old, ModCollection? newCollection, string _3)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (collectionType is not CollectionType.Inactive || old == null)
if (arguments.Type is not CollectionType.Inactive || arguments.OldCollection is null)
return;
foreach (var c in _storage)
{
var inheritedIdx = c.Inheritance.DirectlyInheritsFrom.IndexOf(old);
var inheritedIdx = c.Inheritance.DirectlyInheritsFrom.IndexOf(arguments.OldCollection);
if (inheritedIdx >= 0)
RemoveInheritance(c, inheritedIdx);
c.Inheritance.RemoveChild(old);
c.Inheritance.RemoveChild(arguments.OldCollection);
}
}
@ -189,7 +189,7 @@ public class InheritanceManager : IDisposable, Luna.IService
ModCollectionInheritance.UpdateFlattenedInheritance(inheritor);
RecurseInheritanceChanges(inheritor, invokeEvent);
if (invokeEvent)
_communicator.CollectionInheritanceChanged.Invoke(inheritor, true);
_communicator.CollectionInheritanceChanged.Invoke(new CollectionInheritanceChanged.Arguments(inheritor, true));
}
}
}

View file

@ -33,8 +33,8 @@ public class TempCollectionManager : IDisposable, Luna.IService
_communicator.TemporaryGlobalModChange.Unsubscribe(OnGlobalModChange);
}
private void OnGlobalModChange(TemporaryMod mod, bool created, bool removed)
=> TempModManager.OnGlobalModChange(_customCollections.Values, mod, created, removed);
private void OnGlobalModChange(in TemporaryGlobalModChange.Arguments arguments)
=> TempModManager.OnGlobalModChange(_customCollections.Values, arguments.Mod, arguments.NewlyCreated, arguments.Deleted);
public int Count
=> _customCollections.Count;
@ -57,7 +57,7 @@ public class TempCollectionManager : IDisposable, Luna.IService
if (_customCollections.TryAdd(collection.Identity.Id, collection))
{
// Temporary collection created.
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Temporary, null, collection, string.Empty));
return collection.Identity.Id;
}
@ -81,7 +81,7 @@ public class TempCollectionManager : IDisposable, Luna.IService
continue;
// Temporary collection assignment removed.
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Temporary, collection, null, Collections[i].DisplayName));
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Identity.Id} from {Collections[i].DisplayName}.");
Collections.Delete(i--);
}
@ -96,7 +96,7 @@ public class TempCollectionManager : IDisposable, Luna.IService
// Temporary collection assignment added.
Penumbra.Log.Verbose($"Assigned temporary collection {collection.Identity.AnonymizedName} to {Collections.Last().DisplayName}.");
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
_communicator.CollectionChange.Invoke(new CollectionChange.Arguments(CollectionType.Temporary, null, collection, Collections.Last().DisplayName));
return true;
}

View file

@ -1,18 +1,13 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Api.Enums;
using Penumbra.GameData.Data;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when a Changed Item in Penumbra is clicked.
/// <list type="number">
/// <item>Parameter is the clicked mouse button. </item>
/// <item>Parameter is the clicked object data if any. </item>
/// </list>
/// </summary>
public sealed class ChangedItemClick() : EventWrapper<MouseButton, IIdentifiedObjectData, ChangedItemClick.Priority>(nameof(ChangedItemClick))
/// <summary> Triggered when a Changed Item in Penumbra is clicked. </summary>
public sealed class ChangedItemClick(Logger log)
: EventBase<ChangedItemClick.Arguments, ChangedItemClick.Priority>(nameof(ChangedItemClick), log)
{
public enum Priority
{
@ -22,4 +17,9 @@ public sealed class ChangedItemClick() : EventWrapper<MouseButton, IIdentifiedOb
/// <seealso cref="Penumbra.SetupApi"/>
Link = 1,
}
/// <summary> The arguments for a changed item click event. </summary>
/// <param name="Button"> The clicked mouse button. </param>
/// <param name="Data"> The associated data for the clicked object, if any. </param>
public readonly record struct Arguments(MouseButton Button, IIdentifiedObjectData Data);
}

View file

@ -1,16 +1,12 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.GameData.Data;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when a Changed Item in Penumbra is hovered.
/// <list type="number">
/// <item>Parameter is the hovered object data if any. </item>
/// </list>
/// </summary>
public sealed class ChangedItemHover() : EventWrapper<IIdentifiedObjectData, ChangedItemHover.Priority>(nameof(ChangedItemHover))
/// <summary> Triggered when a Changed Item in Penumbra is hovered. </summary>
public sealed class ChangedItemHover(Logger log)
: EventBase<ChangedItemHover.Arguments, ChangedItemHover.Priority>(nameof(ChangedItemHover), log)
{
public enum Priority
{
@ -21,6 +17,11 @@ public sealed class ChangedItemHover() : EventWrapper<IIdentifiedObjectData, Cha
Link = 1,
}
/// <summary> Whether this event has any subscribers. </summary>
public bool HasTooltip
=> HasSubscribers;
/// <summary> The arguments for a changed item hover event. </summary>
/// <param name="Data"> The associated data for the hovered object, if any. </param>
public readonly record struct Arguments(IIdentifiedObjectData Data);
}

View file

@ -1,13 +1,11 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api;
using Penumbra.Interop.Services;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the Character Utility becomes ready.
/// </summary>
public sealed class CharacterUtilityFinished() : EventWrapper<CharacterUtilityFinished.Priority>(nameof(CharacterUtilityFinished))
/// <summary> Triggered when the Character Utility becomes ready. </summary>
public sealed class CharacterUtilityFinished(Logger log) : EventBase<CharacterUtilityFinished.Priority>(nameof(CharacterUtilityFinished), log)
{
public enum Priority
{

View file

@ -1,19 +1,12 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever collection setup is changed.
/// <list type="number">
/// <item>Parameter is the type of the changed collection. (Inactive or Temporary for additions or deletions)</item>
/// <item>Parameter is the old collection, or null on additions.</item>
/// <item>Parameter is the new collection, or null on deletions.</item>
/// <item>Parameter is the display name for Individual collections or an empty string otherwise.</item>
/// </list> </summary>
public sealed class CollectionChange()
: EventWrapper<CollectionType, ModCollection?, ModCollection?, string, CollectionChange.Priority>(nameof(CollectionChange))
/// <summary> Triggered whenever collection setup is changed. </summary>
public sealed class CollectionChange(Logger log)
: EventBase<CollectionChange.Arguments, CollectionChange.Priority>(nameof(CollectionChange), log)
{
public enum Priority
{
@ -32,7 +25,7 @@ public sealed class CollectionChange()
/// <seealso cref="Collections.Manager.InheritanceManager.OnCollectionChange" />
InheritanceManager = 0,
/// <seealso cref="Interop.PathResolving.IdentifiedCollectionCache.CollectionChangeClear" />
/// <seealso cref="global::Penumbra.Interop.PathResolving.IdentifiedCollectionCache.CollectionChangeClear" />
IdentifiedCollectionCache = 0,
/// <seealso cref="UI.AdvancedWindow.ItemSwapTab.OnCollectionChange" />
@ -50,4 +43,15 @@ public sealed class CollectionChange()
/// <seealso cref="Mods.ModSelection.OnCollectionChange"/>
ModSelection = 10,
}
/// <summary> The arguments for a collection change event. </summary>
/// <param name="Type"> The type of the changed collection (<see cref="CollectionType.Inactive"/> or <see cref="CollectionType.Temporary"/> for additions or deletions). </param>
/// <param name="OldCollection"> The old collection, or null on additions. </param>
/// <param name="NewCollection"> The new collection, or null on deletions. </param>
/// <param name="DisplayName"> The display name for Individual collections or an empty string otherwise. </param>
public readonly record struct Arguments(
CollectionType Type,
ModCollection? OldCollection,
ModCollection? NewCollection,
string DisplayName);
}

View file

@ -1,4 +1,4 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Collections;
namespace Penumbra.Communication;
@ -10,8 +10,8 @@ namespace Penumbra.Communication;
/// <item>Parameter is whether the change was itself inherited, i.e. if it happened in a direct parent (false) or a more removed ancestor (true). </item>
/// </list>
/// </summary>
public sealed class CollectionInheritanceChanged()
: EventWrapper<ModCollection, bool, CollectionInheritanceChanged.Priority>(nameof(CollectionInheritanceChanged))
public sealed class CollectionInheritanceChanged(Logger log)
: EventBase<CollectionInheritanceChanged.Arguments, CollectionInheritanceChanged.Priority>(nameof(CollectionInheritanceChanged), log)
{
public enum Priority
{
@ -27,4 +27,9 @@ public sealed class CollectionInheritanceChanged()
/// <seealso cref="Mods.ModSelection.OnInheritanceChange"/>
ModSelection = 10,
}
/// <summary> The arguments for a collection inheritance changed event. </summary>
/// <param name="Collection"> The collection whose ancestors were changed. </param>
/// <param name="Inherited"> Whether the change was itself inherited, i.e. if it happened in a direct parent (false) or a more removed ancestor (true). </param>
public readonly record struct Arguments(ModCollection Collection, bool Inherited);
}

View file

@ -1,21 +1,23 @@
using OtterGui.Classes;
using Penumbra.Api;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Collections;
using Penumbra.GameData.Interop;
namespace Penumbra.Communication;
/// <summary> <list type="number">
/// <item>Parameter is the game object for which a draw object is created. </item>
/// <item>Parameter is the applied collection. </item>
/// <item>Parameter is the created draw object. </item>
/// </list> </summary>
public sealed class CreatedCharacterBase()
: EventWrapper<nint, ModCollection, nint, CreatedCharacterBase.Priority>(nameof(CreatedCharacterBase))
/// <summary> Invoked whenever a draw object is created for a game object. </summary>
public sealed class CreatedCharacterBase(Logger log)
: EventBase<CreatedCharacterBase.Arguments, CreatedCharacterBase.Priority>(nameof(CreatedCharacterBase), log)
{
public enum Priority
{
/// <seealso cref="PenumbraApi.CreatedCharacterBase"/>
/// <seealso cref="GameStateApi.CreatedCharacterBase"/>
Api = int.MinValue,
}
/// <summary> The arguments for a created CharacterBase event. </summary>
/// <param name="GameObject"> The address of the game object for which a draw object was created. </param>
/// <param name="Collection"> The associated collection. </param>
/// <param name="DrawObject"> The newly created draw object for the game object. </param>
public readonly record struct Arguments(Actor GameObject, ModCollection Collection, Model DrawObject);
}

View file

@ -1,20 +1,14 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Collections;
using Penumbra.GameData.Interop;
using Penumbra.Services;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever a character base draw object is being created by the game.
/// <list type="number">
/// <item>Parameter is the game object for which a draw object is created. </item>
/// <item>Parameter is the name of the applied collection. </item>
/// <item>Parameter is a pointer to the model id (an uint). </item>
/// <item>Parameter is a pointer to the customize array. </item>
/// <item>Parameter is a pointer to the equip data array. </item>
/// </list> </summary>
public sealed class CreatingCharacterBase()
: EventWrapper<nint, Guid, nint, nint, nint, CreatingCharacterBase.Priority>(nameof(CreatingCharacterBase))
/// <summary> Triggered whenever a character base draw object is being created by the game. </summary>
public sealed class CreatingCharacterBase(Logger log)
: EventBase<CreatingCharacterBase.Arguments, CreatingCharacterBase.Priority>(nameof(CreatingCharacterBase), log)
{
public enum Priority
{
@ -24,4 +18,12 @@ public sealed class CreatingCharacterBase()
/// <seealso cref="CrashHandlerService.OnCreatingCharacterBase"/>
CrashHandler = 0,
}
/// <summary> The arguments for a created CharacterBase event. </summary>
/// <param name="GameObject"> The address of the game object for which a draw object is being created. </param>
/// <param name="Collection"> The associated collection. </param>
/// <param name="ModelCharaId"> The address of the model ID that is being used. </param>
/// <param name="Customize"> The address of the customize array that is being used. </param>
/// <param name="EquipData"> The address of the equip data array that is being used. </param>
public readonly record struct Arguments(Actor GameObject, ModCollection Collection, nint ModelCharaId, nint Customize, nint EquipData);
}

View file

@ -1,23 +1,20 @@
using OtterGui.Classes;
using Penumbra.Api;
using Penumbra.Api.IpcSubscribers;
using Luna;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the general Enabled state of Penumbra is changed.
/// <list type="number">
/// <item>Parameter is whether Penumbra is now Enabled (true) or Disabled (false). </item>
/// </list>
/// </summary>
public sealed class EnabledChanged() : EventWrapper<bool, EnabledChanged.Priority>(nameof(EnabledChanged))
/// <summary> Triggered when the general Enabled state of Penumbra is changed. </summary>
public sealed class EnabledChanged(Logger log) : EventBase<EnabledChanged.Arguments, EnabledChanged.Priority>(nameof(EnabledChanged), log)
{
public enum Priority
{
/// <seealso cref="Api.IpcSubscribers.Ipc.EnabledChange"/>
/// <seealso cref="Api.Api.PluginStateApi.EnabledChange"/>
Api = int.MinValue,
/// <seealso cref="Api.DalamudSubstitutionProvider.OnEnabledChange"/>
DalamudSubstitutionProvider = 0,
}
/// <summary> The arguments for a EnabledChanged event. </summary>
/// <param name="Enabled"> Whether Penumbra is now Enabled (true) or Disabled (false). </param>
public readonly record struct Arguments(bool Enabled);
}

View file

@ -1,17 +1,11 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever mod meta data or local data is changed.
/// <list type="number">
/// <item>Parameter is the type of data change for the mod, which can be multiple flags. </item>
/// <item>Parameter is the changed mod. </item>
/// <item>Parameter is the old name of the mod in case of a name change, and null otherwise. </item>
/// </list> </summary>
public sealed class ModDataChanged() : EventWrapper<ModDataChangeType, Mod, string?, ModDataChanged.Priority>(nameof(ModDataChanged))
/// <summary> Triggered whenever mod meta data or local data is changed. </summary>
public sealed class ModDataChanged(Logger log) : EventBase<ModDataChanged.Arguments, ModDataChanged.Priority>(nameof(ModDataChanged), log)
{
public enum Priority
{
@ -27,4 +21,10 @@ public sealed class ModDataChanged() : EventWrapper<ModDataChangeType, Mod, stri
/// <seealso cref="UI.ModsTab.ModPanelHeader.OnModDataChange"/>
ModPanelHeader = 0,
}
/// <summary> The arguments for a ModDataChanged event. </summary>
/// <param name="Type"> The type of data change for the mod, which can be multiple flags. </param>
/// <param name="Mod"> The changed mod. </param>
/// <param name="OldName"> The old name of the mod in case of a name change, and null otherwise. </param>
public readonly record struct Arguments(ModDataChangeType Type, Mod Mod, string? OldName);
}

View file

@ -1,16 +1,11 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever the mod root directory changes.
/// <list type="number">
/// <item>Parameter is the full path of the new directory. </item>
/// <item>Parameter is whether the new directory is valid. </item>
/// </list>
/// </summary>
public sealed class ModDirectoryChanged() : EventWrapper<string, bool, ModDirectoryChanged.Priority>(nameof(ModDirectoryChanged))
/// <summary> Triggered whenever the mod root directory changes. </summary>
public sealed class ModDirectoryChanged(Logger log)
: EventBase<ModDirectoryChanged.Arguments, ModDirectoryChanged.Priority>(nameof(ModDirectoryChanged), log)
{
public enum Priority
{
@ -20,4 +15,9 @@ public sealed class ModDirectoryChanged() : EventWrapper<string, bool, ModDirect
/// <seealso cref="UI.FileDialogService.OnModDirectoryChange"/>
FileDialogService = 0,
}
/// <summary> The arguments for a ModFileChanged event. </summary>
/// <param name="Directory"> The full path of the new mod directory. </param>
/// <param name="Valid"> Whether the directory is valid. </param>
public readonly record struct Arguments(string Directory, bool Valid);
}

View file

@ -1,9 +1,9 @@
using OtterGui.Classes;
using Luna;
namespace Penumbra.Communication;
/// <summary> Triggered whenever a new mod discovery has finished. </summary>
public sealed class ModDiscoveryFinished() : EventWrapper<ModDiscoveryFinished.Priority>(nameof(ModDiscoveryFinished))
public sealed class ModDiscoveryFinished(Logger log) : EventBase<ModDiscoveryFinished.Priority>(nameof(ModDiscoveryFinished), log)
{
public enum Priority
{

View file

@ -1,9 +1,9 @@
using OtterGui.Classes;
using Luna;
namespace Penumbra.Communication;
/// <summary> Triggered whenever mods are prepared to be rediscovered. </summary>
public sealed class ModDiscoveryStarted() : EventWrapper<ModDiscoveryStarted.Priority>(nameof(ModDiscoveryStarted))
public sealed class ModDiscoveryStarted(Logger log) : EventBase<ModDiscoveryStarted.Priority>(nameof(ModDiscoveryStarted), log)
{
public enum Priority
{

View file

@ -1,19 +1,12 @@
using OtterGui.Classes;
using Penumbra.Api;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
using Penumbra.Mods.Editor;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever an existing file in a mod is overwritten by Penumbra.
/// <list type="number">
/// <item>Parameter is the changed mod. </item>
/// <item>Parameter file registry of the changed file. </item>
/// </list> </summary>
public sealed class ModFileChanged()
: EventWrapper<Mod, FileRegistry, ModFileChanged.Priority>(nameof(ModFileChanged))
/// <summary> Triggered whenever an existing file in a mod is overwritten by Penumbra. </summary>
public sealed class ModFileChanged(Logger log) : EventBase<ModFileChanged.Arguments, ModFileChanged.Priority>(nameof(ModFileChanged), log)
{
public enum Priority
{
@ -26,4 +19,9 @@ public sealed class ModFileChanged()
/// <seealso cref="Collections.Manager.CollectionStorage.OnModFileChanged"/>
CollectionStorage = 0,
}
/// <summary> The arguments for a ModFileChanged event. </summary>
/// <param name="Mod"> The changed mod. </param>
/// <param name="File"> The file registry of the changed file. </param>
public readonly record struct Arguments(Mod Mod, FileRegistry File);
}

View file

@ -1,26 +1,16 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.SubMods;
using static Penumbra.Communication.ModOptionChanged;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever an option of a mod is changed inside the mod.
/// <list type="number">
/// <item>Parameter is the type option change. </item>
/// <item>Parameter is the changed mod. </item>
/// <item>Parameter is the changed group inside the mod. </item>
/// <item>Parameter is the changed option inside the group or null if it does not concern a specific option. </item>
/// <item>Parameter is the changed data container inside the group or null if it does not concern a specific data container. </item>
/// <item>Parameter is the index of the group or option moved or deleted from. </item>
/// </list> </summary>
public sealed class ModOptionChanged()
: EventWrapper<ModOptionChangeType, Mod, IModGroup?, IModOption?, IModDataContainer?, int, Priority>(nameof(ModOptionChanged))
/// <summary> Triggered whenever an option of a mod is changed inside the mod. </summary>
public sealed class ModOptionChanged(Logger log)
: EventBase<ModOptionChanged.Arguments, ModOptionChanged.Priority>(nameof(ModOptionChanged), log)
{
public enum Priority
{
@ -39,4 +29,19 @@ public sealed class ModOptionChanged()
/// <seealso cref="Collections.Manager.CollectionStorage.OnModOptionChange"/>
CollectionStorage = 100,
}
/// <summary> The arguments for a ModOptionChanged event. </summary>
/// <param name="Type"> The type of option change for the mod. </param>
/// <param name="Mod"> The changed mod. </param>
/// <param name="Group"> The changed group inside the mod, if any. </param>
/// <param name="Option"> The changed option inside the group or null if it does not concern a specific option. </param>
/// <param name="Container"> The changed data container inside the group or null if it does not concern a specific data container. </param>
/// <param name="DeletedIndex"> The index of the group or option moved or deleted from. </param>
public readonly record struct Arguments(
ModOptionChangeType Type,
Mod Mod,
IModGroup? Group,
IModOption? Option,
IModDataContainer? Container,
int DeletedIndex);
}

View file

@ -1,5 +1,4 @@
using OtterGui.Classes;
using Penumbra.Api;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
@ -7,17 +6,9 @@ using Penumbra.Services;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever a mod is added, deleted, moved or reloaded.
/// <list type="number">
/// <item>Parameter is the type of change. </item>
/// <item>Parameter is the changed mod. </item>
/// <item>Parameter is the old directory on deletion, move or reload and null on addition. </item>
/// <item>Parameter is the new directory on addition, move or reload and null on deletion. </item>
/// </list>
/// </summary>
public sealed class ModPathChanged()
: EventWrapper<ModPathChangeType, Mod, DirectoryInfo?, DirectoryInfo?, ModPathChanged.Priority>(nameof(ModPathChanged))
/// <summary> Triggered whenever a mod is added, deleted, moved or reloaded. </summary>
public sealed class ModPathChanged(Logger log)
: EventBase<ModPathChanged.Arguments, ModPathChanged.Priority>(nameof(ModPathChanged), log)
{
public enum Priority
{
@ -60,4 +51,11 @@ public sealed class ModPathChanged()
/// <seealso cref="Collections.Cache.CollectionCacheManager.OnModChangeRemoval"/>
CollectionCacheManagerRemoval = 100,
}
/// <summary> The arguments for a ModPathChanged event. </summary>
/// <param name="Type"> The type of change for the mod. </param>
/// <param name="Mod"> The changed mod. </param>
/// <param name="OldDirectory"> The old directory on deletion, move or reload and null on addition. </param>
/// <param name="NewDirectory"> The new directory on addition, move or reload and null on deletion. </param>
public readonly record struct Arguments(ModPathChangeType Type, Mod Mod, DirectoryInfo? OldDirectory, DirectoryInfo? NewDirectory);
}

View file

@ -1,4 +1,4 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Api.Enums;
using Penumbra.Collections;
@ -7,19 +7,9 @@ using Penumbra.Mods.Settings;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever a mod setting is changed.
/// <list type="number">
/// <item>Parameter is the collection in which the setting was changed. </item>
/// <item>Parameter is the type of change. </item>
/// <item>Parameter is the mod the setting was changed for, unless it was a multi-change. </item>
/// <item>Parameter is the old value of the setting before the change as Setting. </item>
/// <item>Parameter is the index of the changed group if the change type is Setting. </item>
/// <item>Parameter is whether the change was inherited from another collection. </item>
/// </list>
/// </summary>
public sealed class ModSettingChanged()
: EventWrapper<ModCollection, ModSettingChange, Mod?, Setting, int, bool, ModSettingChanged.Priority>(nameof(ModSettingChanged))
/// <summary> Triggered whenever a mod setting is changed. </summary>
public sealed class ModSettingChanged(Logger log)
: EventBase<ModSettingChanged.Arguments, ModSettingChanged.Priority>(nameof(ModSettingChanged), log)
{
public enum Priority
{
@ -38,4 +28,19 @@ public sealed class ModSettingChanged()
/// <seealso cref="Mods.ModSelection.OnSettingChange"/>
ModSelection = 10,
}
/// <summary> The arguments for a ModSettingChanged event. </summary>
/// <param name="Type"> The type of change for the mod settings. </param>
/// <param name="Collection"> The collection in which the settings were changed, unless it was a multi-change. </param>
/// <param name="Mod"> The changed mod. </param>
/// <param name="OldValue"> The old value of the setting before the change. </param>
/// <param name="GroupIndex"> The index of the changed group if the change type is Setting. </param>
/// <param name="Inherited"> Whether the change was inherited from another collection </param>
public readonly record struct Arguments(
ModSettingChange Type,
ModCollection Collection,
Mod? Mod,
Setting OldValue,
int GroupIndex,
bool Inherited);
}

View file

@ -1,16 +1,20 @@
using OtterGui.Classes;
using Luna;
using Penumbra.GameData.Interop;
using Penumbra.Interop.Hooks.PostProcessing;
namespace Penumbra.Communication;
/// <summary> <list type="number">
/// <item>Parameter is the material resource handle for which the shader package has been loaded. </item>
/// <item>Parameter is the associated game object. </item>
/// </list> </summary>
public sealed class MtrlLoaded() : EventWrapper<nint, nint, MtrlLoaded.Priority>(nameof(MtrlLoaded))
/// <summary> Invoked whenever a material is loaded. </summary>
public sealed class MtrlLoaded(Logger log) : EventBase<MtrlLoaded.Arguments, MtrlLoaded.Priority>(nameof(MtrlLoaded), log)
{
public enum Priority
{
/// <seealso cref="Interop.Hooks.PostProcessing.ShaderReplacementFixer.OnMtrlLoaded"/>
/// <seealso cref="ShaderReplacementFixer.OnMtrlLoaded"/>
ShaderReplacementFixer = 0,
}
/// <summary> The arguments for a MtrlLoaded event. </summary>
/// <param name="MaterialResourceHandle"> The material resource handle for which the shader package has been loaded. </param>
/// <param name="GameObject"> The associated game object </param>
public readonly record struct Arguments(nint MaterialResourceHandle, Actor GameObject);
}

View file

@ -1,21 +1,21 @@
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Structs;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the character.json file for a .pcp file is written.
/// <list type="number">
/// <item>Parameter is the JObject that gets written to file. </item>
/// <item>Parameter is the object index of the game object this is written for. </item>
/// <item>Parameter is the full path to the directory being set up for the PCP creation. </item>
/// </list>
/// </summary>
public sealed class PcpCreation() : EventWrapper<JObject, ushort, string, PcpCreation.Priority>(nameof(PcpCreation))
/// <summary> Triggered when the character.json file for a .pcp file is written. </summary>
public sealed class PcpCreation(Logger log) : EventBase<PcpCreation.Arguments, PcpCreation.Priority>(nameof(PcpCreation), log)
{
public enum Priority
{
/// <seealso cref="Api.Api.ModsApi"/>
ModsApi = int.MinValue,
ApiMods = int.MinValue,
}
/// <summary> The arguments for a PcpCreation event. </summary>
/// <param name="JObject"> The JObject that gets written to file. </param>
/// <param name="ObjectIndex"> The object index of the game object this is written for. </param>
/// <param name="DirectoryPath"> The full path to the directory being set up for the PCP creation. </param>
public readonly record struct Arguments(JObject JObject, ObjectIndex ObjectIndex, string DirectoryPath);
}

View file

@ -1,21 +1,22 @@
using Luna;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.Collections;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered when the character.json file for a .pcp file is parsed and applied.
/// <list type="number">
/// <item>Parameter is parsed JObject that contains the data. </item>
/// <item>Parameter is the identifier of the created mod. </item>
/// <item>Parameter is the GUID of the created collection. </item>
/// </list>
/// </summary>
public sealed class PcpParsing() : EventWrapper<JObject, string, Guid, PcpParsing.Priority>(nameof(PcpParsing))
/// <summary> Triggered when the character.json file for a .pcp file is parsed and applied. </summary>
public sealed class PcpParsing(Logger log) : EventBase<PcpParsing.Arguments, PcpParsing.Priority>(nameof(PcpParsing), log)
{
public enum Priority
{
/// <seealso cref="Api.Api.ModsApi"/>
ModsApi = int.MinValue,
ApiMods = int.MinValue,
}
/// <summary> The arguments for a PcpParsing event. </summary>
/// <param name="JObject"> The parsed JObject that contains the data. </param>
/// <param name="Mod"> The created mod. </param>
/// <param name="Collection"> The created collection, if any. </param>
public readonly record struct Arguments(JObject JObject, Mod Mod, ModCollection? Collection);
}

View file

@ -1,19 +1,19 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered after the Enabled Checkbox line in settings is drawn, but before options are drawn.
/// <list type="number">
/// <item>Parameter is the identifier (directory name) of the currently selected mod. </item>
/// </list>
/// </summary>
public sealed class PostEnabledDraw() : EventWrapper<string, PostEnabledDraw.Priority>(nameof(PostEnabledDraw))
/// <summary> Triggered after the Enabled Checkbox line in settings is drawn, but before options are drawn. </summary>
public sealed class PostEnabledDraw(Logger log) : EventBase<PostEnabledDraw.Arguments, PostEnabledDraw.Priority>(nameof(PostEnabledDraw), log)
{
public enum Priority
{
/// <seealso cref="PenumbraApi.PostEnabledDraw"/>
Default = 0,
}
/// <summary> The arguments for a PostEnabledDraw event. </summary>
/// <param name="Mod"> The mod currently being drawn. </param>
public readonly record struct Arguments(Mod Mod);
}

View file

@ -1,19 +1,19 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered after the settings panel is drawn.
/// <list type="number">
/// <item>Parameter is the identifier (directory name) of the currently selected mod. </item>
/// </list>
/// </summary>
public sealed class PostSettingsPanelDraw() : EventWrapper<string, PostSettingsPanelDraw.Priority>(nameof(PostSettingsPanelDraw))
/// <summary> Triggered after the settings panel is drawn. </summary>
public sealed class PostSettingsPanelDraw(Logger log) : EventBase<PostSettingsPanelDraw.Arguments, PostSettingsPanelDraw.Priority>(nameof(PostSettingsPanelDraw), log)
{
public enum Priority
{
/// <seealso cref="PenumbraApi.PostSettingsPanelDraw"/>
Default = 0,
}
/// <summary> The arguments for a PostSettingsPanelDraw event. </summary>
/// <param name="Mod"> The mod currently being drawn. </param>
public readonly record struct Arguments(Mod Mod);
}

View file

@ -1,19 +1,19 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Api;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered before the settings panel is drawn.
/// <list type="number">
/// <item>Parameter is the identifier (directory name) of the currently selected mod. </item>
/// </list>
/// </summary>
public sealed class PreSettingsPanelDraw() : EventWrapper<string, PreSettingsPanelDraw.Priority>(nameof(PreSettingsPanelDraw))
/// <summary> Triggered before the settings panel is drawn. </summary>
public sealed class PreSettingsPanelDraw(Logger log) : EventBase<PreSettingsPanelDraw.Arguments, PreSettingsPanelDraw.Priority>(nameof(PreSettingsPanelDraw), log)
{
public enum Priority
{
/// <seealso cref="PenumbraApi.PreSettingsPanelDraw"/>
Default = 0,
}
/// <summary> The arguments for a PreSettingsPanelDraw event. </summary>
/// <param name="Mod"> The mod currently being drawn. </param>
public readonly record struct Arguments(Mod Mod);
}

View file

@ -1,22 +1,21 @@
using OtterGui.Classes;
using Penumbra.Api.Api;
using Penumbra.Api.IpcSubscribers;
using Luna;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered before the settings tab bar for a mod is drawn, after the title group is drawn.
/// <list type="number">
/// <item>Parameter is the identifier (directory name) of the currently selected mod. </item>
/// <item>is the total width of the header group. </item>
/// <item>is the width of the title box. </item>
/// </list>
/// </summary>
public sealed class PreSettingsTabBarDraw() : EventWrapper<string, float, float, PreSettingsTabBarDraw.Priority>(nameof(PreSettingsTabBarDraw))
/// <summary> Triggered before the settings tab bar for a mod is drawn, after the title group is drawn. </summary>
public sealed class PreSettingsTabBarDraw(Logger log)
: EventBase<PreSettingsTabBarDraw.Arguments, PreSettingsTabBarDraw.Priority>(nameof(PreSettingsTabBarDraw), log)
{
public enum Priority
{
/// <seealso cref="Api.IpcSubscribers.PreSettingsTabBarDraw"/>
Default = 0,
}
/// <summary> The arguments for a PreSettingsTabBarDraw event. </summary>
/// <param name="Mod"> The mod currently being drawn. </param>
/// <param name="HeaderWidth"> The total width of the header group. </param>
/// <param name="TitleBoxWidth"> The width of the title box. </param>
public readonly record struct Arguments(Mod Mod, float HeaderWidth, float TitleBoxWidth);
}

View file

@ -1,23 +1,13 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Collections;
using Penumbra.Mods.Editor;
using Penumbra.String.Classes;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever a redirection in a mod collection cache is manipulated.
/// <list type="number">
/// <item>Parameter is collection with a changed cache. </item>
/// <item>Parameter is the type of change. </item>
/// <item>Parameter is the game path to be redirected or empty for FullRecompute. </item>
/// <item>Parameter is the new redirection path or empty for Removed or FullRecompute </item>
/// <item>Parameter is the old redirection path for Replaced, or empty. </item>
/// <item>Parameter is the mod responsible for the new redirection if any. </item>
/// </list> </summary>
public sealed class ResolvedFileChanged()
: EventWrapper<ModCollection, ResolvedFileChanged.Type, Utf8GamePath, FullPath, FullPath, IMod?, ResolvedFileChanged.Priority>(
nameof(ResolvedFileChanged))
/// <summary> Triggered whenever a redirection in a mod collection cache is manipulated. </summary>
public sealed class ResolvedFileChanged(Logger log) : EventBase<ResolvedFileChanged.Arguments, ResolvedFileChanged.Priority>(
nameof(ResolvedFileChanged), log)
{
public enum Type
{
@ -36,4 +26,19 @@ public sealed class ResolvedFileChanged()
/// <seealso cref="Interop.Services.SchedulerResourceManagementService.OnResolvedFileChange"/>
SchedulerResourceManagementService = 0,
}
/// <summary> The arguments for a ResolvedFileChanged event. </summary>
/// <param name="Type"> The type of the redirection change. </param>
/// <param name="Collection"> The collection with a changed cache. </param>
/// <param name="GamePath"> The game path to be redirected or empty for FullRecompute </param>
/// <param name="NewRedirection"> The new redirection path or empty for Removed or FullRecompute. </param>
/// <param name="OldRedirection"> The old redirection path for Replaced, or empty. </param>
/// <param name="Mod"> The mod responsible for the new redirection if any. </param>
public readonly record struct Arguments(
Type Type,
ModCollection Collection,
Utf8GamePath GamePath,
FullPath OldRedirection,
FullPath NewRedirection,
IMod? Mod);
}

View file

@ -1,21 +1,20 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Api.Enums;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Trigger to select a tab and mod in the Config Window.
/// <list type="number">
/// <item>Parameter is the selected tab. </item>
/// <item>Parameter is the selected mod, if any. </item>
/// </list>
/// </summary>
public sealed class SelectTab() : EventWrapper<TabType, Mod?, SelectTab.Priority>(nameof(SelectTab))
/// <summary> Trigger to select a tab and mod in the Config Window. </summary>
public sealed class SelectTab(Logger log) : EventBase<SelectTab.Arguments, SelectTab.Priority>(nameof(SelectTab), log)
{
public enum Priority
{
/// <seealso cref="UI.Tabs.ConfigTabBar.OnSelectTab"/>
ConfigTabBar = 0,
}
/// <summary> The arguments for a SelectTab event. </summary>
/// <param name="Tab"> The selected tab. </param>
/// <param name="Mod"> The selected mod, if any. </param>
public readonly record struct Arguments(TabType Tab, Mod? Mod);
}

View file

@ -1,17 +1,11 @@
using OtterGui.Classes;
using Luna;
using Penumbra.Mods;
namespace Penumbra.Communication;
/// <summary>
/// Triggered whenever a temporary mod for all collections is changed.
/// <list type="number">
/// <item>Parameter added, deleted or edited temporary mod.</item>
/// <item>Parameter is whether the mod was newly created.</item>
/// <item>Parameter is whether the mod was deleted.</item>
/// </list> </summary>
public sealed class TemporaryGlobalModChange()
: EventWrapper<TemporaryMod, bool, bool, TemporaryGlobalModChange.Priority>(nameof(TemporaryGlobalModChange))
/// <summary> Triggered whenever a temporary mod for all collections is changed. </summary>
public sealed class TemporaryGlobalModChange(Logger log)
: EventBase<TemporaryGlobalModChange.Arguments, TemporaryGlobalModChange.Priority>(nameof(TemporaryGlobalModChange), log)
{
public enum Priority
{
@ -21,4 +15,10 @@ public sealed class TemporaryGlobalModChange()
/// <seealso cref="Collections.Manager.TempCollectionManager.OnGlobalModChange"/>
TempCollectionManager = 0,
}
/// <summary> The arguments for a TemporaryGlobalModChange event. </summary>
/// <param name="Mod"> The changed mod. </param>
/// <param name="NewlyCreated"> The changed mod. </param>
/// <param name="Deleted"> The changed mod. </param>
public readonly record struct Arguments(TemporaryMod Mod, bool NewlyCreated, bool Deleted);
}

View file

@ -106,12 +106,13 @@ public class EphemeralConfig : ISavable, IDisposable, IService
}
/// <summary> Overwrite the last saved mod path if it changes. </summary>
private void OnModPathChanged(ModPathChangeType type, Mod mod, DirectoryInfo? old, DirectoryInfo? _)
private void OnModPathChanged(in ModPathChanged.Arguments arguments)
{
if (type is not ModPathChangeType.Moved || !string.Equals(old?.Name, LastModPath, StringComparison.OrdinalIgnoreCase))
if (arguments.Type is not ModPathChangeType.Moved
|| !string.Equals(arguments.OldDirectory?.Name, LastModPath, StringComparison.OrdinalIgnoreCase))
return;
LastModPath = mod.Identifier;
LastModPath = arguments.Mod.Identifier;
Save();
}
}

View file

@ -1,10 +1,12 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData.Interop;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr<CharacterBase, CharacterBaseDestructor.Priority>, Luna.IHookService
public sealed unsafe class CharacterBaseDestructor : EventBase<CharacterBaseDestructor.Arguments, CharacterBaseDestructor.Priority>,
IHookService
{
public enum Priority
{
@ -15,8 +17,8 @@ public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr<CharacterBa
MtrlTab = -1000,
}
public CharacterBaseDestructor(Luna.HookManager hooks)
: base("Destroy CharacterBase")
public CharacterBaseDestructor(Logger log, HookManager hooks)
: base("Destroy CharacterBase", log)
=> _task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.CharacterBaseDestructor);
private readonly Task<Hook<Delegate>> _task;
@ -41,7 +43,11 @@ public sealed unsafe class CharacterBaseDestructor : EventWrapperPtr<CharacterBa
private nint Detour(CharacterBase* characterBase)
{
Penumbra.Log.Excessive($"[{Name}] Triggered with 0x{(nint)characterBase:X}.");
Invoke(characterBase);
Invoke(new Arguments(characterBase));
return _task.Result.Original(characterBase);
}
/// <summary> The arguments for a character base destructor event. </summary>
/// <param name="CharacterBase"> The model that is being destroyed. </param>
public readonly record struct Arguments(Model CharacterBase);
}

View file

@ -1,11 +1,12 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData;
using Penumbra.GameData.Interop;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class CharacterDestructor : EventWrapperPtr<Character, CharacterDestructor.Priority>, Luna.IHookService
public sealed unsafe class CharacterDestructor : EventBase<CharacterDestructor.Arguments, CharacterDestructor.Priority>, IHookService
{
public enum Priority
{
@ -19,8 +20,8 @@ public sealed unsafe class CharacterDestructor : EventWrapperPtr<Character, Char
DrawObjectState = 0,
}
public CharacterDestructor(Luna.HookManager hooks)
: base("Character Destructor")
public CharacterDestructor(Logger log, HookManager hooks)
: base("Character Destructor", log)
=> _task = hooks.CreateHook<Delegate>(Name, Sigs.CharacterDestructor, Detour, !HookOverrides.Instance.Objects.CharacterDestructor);
private readonly Task<Hook<Delegate>> _task;
@ -45,7 +46,11 @@ public sealed unsafe class CharacterDestructor : EventWrapperPtr<Character, Char
private void Detour(Character* character)
{
Penumbra.Log.Excessive($"[{Name}] Triggered with 0x{(nint)character:X}.");
Invoke(character);
Invoke(new Arguments(character));
_task.Result.Original(character);
}
/// <summary> The arguments for a character destructor event. </summary>
/// <param name="Character"> The game object that is being destroyed. </param>
public readonly record struct Arguments(Actor Character);
}

View file

@ -1,13 +1,12 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class ConstructCutsceneCharacter : EventWrapperPtr<Character, ConstructCutsceneCharacter.Priority>, Luna.IHookService
public sealed unsafe class ConstructCutsceneCharacter : EventBase<ConstructCutsceneCharacter.Arguments, ConstructCutsceneCharacter.Priority>, IHookService
{
private readonly GameState _gameState;
private readonly ObjectManager _objects;
@ -18,8 +17,8 @@ public sealed unsafe class ConstructCutsceneCharacter : EventWrapperPtr<Characte
CutsceneService = 0,
}
public ConstructCutsceneCharacter(GameState gameState, Luna.HookManager hooks, ObjectManager objects)
: base("ConstructCutsceneCharacter")
public ConstructCutsceneCharacter(Logger log, GameState gameState, HookManager hooks, ObjectManager objects)
: base("ConstructCutsceneCharacter", log)
{
_gameState = gameState;
_objects = objects;
@ -42,7 +41,7 @@ public sealed unsafe class ConstructCutsceneCharacter : EventWrapperPtr<Characte
var character = _objects[ret + (int)ScreenActor.CutsceneStart].AsCharacter;
if (character != null)
{
Invoke(character);
Invoke(new Arguments(character));
Penumbra.Log.Verbose(
$"[{Name}] Created indirect copy of player character at 0x{(nint)character}, index {character->ObjectIndex}.");
}
@ -66,4 +65,8 @@ public sealed unsafe class ConstructCutsceneCharacter : EventWrapperPtr<Characte
public bool Finished
=> _task.IsCompletedSuccessfully;
/// <summary> The arguments for a construct cutscene character event. </summary>
/// <param name="Character"> The game object that is being destroyed. </param>
public readonly record struct Arguments(Actor Character);
}

View file

@ -1,19 +1,20 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using OtterGui.Classes;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class CopyCharacter : EventWrapperPtr<Character, Character, CopyCharacter.Priority>, Luna.IHookService
{
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Luna;
using Penumbra.GameData.Interop;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class CopyCharacter : EventBase<CopyCharacter.Arguments, CopyCharacter.Priority>, IHookService
{
public enum Priority
{
/// <seealso cref="PathResolving.CutsceneService.OnCharacterCopy"/>
CutsceneService = 0,
}
public CopyCharacter(Luna.HookManager hooks)
: base("Copy Character")
public CopyCharacter(Logger log, HookManager hooks)
: base("Copy Character", log)
=> _task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.CopyCharacter);
private readonly Task<Hook<Delegate>> _task;
@ -39,7 +40,12 @@ public sealed unsafe class CopyCharacter : EventWrapperPtr<Character, Character,
{
var character = target->OwnerObject;
Penumbra.Log.Verbose($"[{Name}] Triggered with target: 0x{(nint)target:X}, source : 0x{(nint)source:X} unk: {unk}.");
Invoke(character, source);
Invoke(new Arguments(character, source));
return _task.Result.Original(target, source, unk);
}
}
/// <summary> The arguments for a copy character event. </summary>
/// <param name="TargetCharacter"> The character that is being created by a copy. </param>
/// <param name="SourceCharacter"> The character that is being copied. </param>
public readonly record struct Arguments(Actor TargetCharacter, Actor SourceCharacter);
}

View file

@ -1,21 +1,25 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class CreateCharacterBase : EventWrapperPtr<ModelCharaId, CustomizeArray, CharacterArmor, CreateCharacterBase.Priority>, Luna.IHookService
{
public sealed unsafe class CreateCharacterBase : EventBase<CreateCharacterBase.Arguments, CreateCharacterBase.Priority>, IHookService
{
public enum Priority
{
/// <seealso cref="PathResolving.MetaState.OnCreatingCharacterBase"/>
MetaState = 0,
}
public CreateCharacterBase(Luna.HookManager hooks)
: base("Create CharacterBase")
=> _task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.CreateCharacterBase);
public CreateCharacterBase(Logger log, HookManager hooks)
: base("Create CharacterBase", log)
{
_postEvent = new PostEvent("Created CharacterBase", log);
_task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.CreateCharacterBase);
}
private readonly Task<Hook<Delegate>> _task;
@ -38,36 +42,52 @@ public sealed unsafe class CreateCharacterBase : EventWrapperPtr<ModelCharaId, C
private CharacterBase* Detour(ModelCharaId model, CustomizeArray* customize, CharacterArmor* equipment, byte unk)
{
Penumbra.Log.Verbose($"[{Name}] Triggered with model: {model.Id}, customize: 0x{(nint)customize:X}, equipment: 0x{(nint)equipment:X}, unk: {unk}.");
Invoke(&model, customize, equipment);
Penumbra.Log.Verbose(
$"[{Name}] Triggered with model: {model.Id}, customize: 0x{(nint)customize:X}, equipment: 0x{(nint)equipment:X}, unk: {unk}.");
Invoke(new Arguments(ref model, customize, equipment));
var ret = _task.Result.Original(model, customize, equipment, unk);
_postEvent.Invoke(model, customize, equipment, ret);
_postEvent.Invoke(new PostEvent.Arguments(model, customize, equipment, ret));
return ret;
}
public void Subscribe(ActionPtr234<ModelCharaId, CustomizeArray, CharacterArmor, CharacterBase> subscriber, PostEvent.Priority priority)
public void Subscribe(InAction<PostEvent.Arguments> subscriber, PostEvent.Priority priority)
=> _postEvent.Subscribe(subscriber, priority);
public void Unsubscribe(ActionPtr234<ModelCharaId, CustomizeArray, CharacterArmor, CharacterBase> subscriber)
public void Unsubscribe(InAction<PostEvent.Arguments> subscriber)
=> _postEvent.Unsubscribe(subscriber);
private readonly PostEvent _postEvent = new("Created CharacterBase");
private readonly PostEvent _postEvent;
protected override void Dispose(bool disposing)
{
_postEvent.Dispose();
}
public class PostEvent(string name) : EventWrapperPtr234<ModelCharaId, CustomizeArray, CharacterArmor, CharacterBase, PostEvent.Priority>(name)
{
public class PostEvent(string name, Logger log) : EventBase<PostEvent.Arguments, PostEvent.Priority>(name, log)
{
public enum Priority
{
/// <seealso cref="PathResolving.DrawObjectState.OnCharacterBaseCreated"/>
DrawObjectState = 0,
DrawObjectState = 0,
/// <seealso cref="PathResolving.MetaState.OnCharacterBaseCreated"/>
MetaState = 0,
}
public readonly struct Arguments(ModelCharaId modelCharaId, CustomizeArray* customize, CharacterArmor* equipment, Model characterBase)
{
public readonly ModelCharaId ModelCharaId = modelCharaId;
public readonly CustomizeArray* Customize = customize;
public readonly CharacterArmor* Equipment = equipment;
public readonly Model CharacterBase = characterBase;
}
}
public readonly ref struct Arguments(ref ModelCharaId modelCharaId, CustomizeArray* customize, CharacterArmor* equipment)
{
public readonly ref ModelCharaId ModelCharaId = ref modelCharaId;
public readonly CustomizeArray* Customize = customize;
public readonly CharacterArmor* Equipment = equipment;
}
}

View file

@ -1,11 +1,12 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
namespace Penumbra.Interop.Hooks.Objects;
public sealed unsafe class WeaponReload : EventWrapperPtr<DrawDataContainer, Character, CharacterWeapon, WeaponReload.Priority>, Luna.IHookService
public sealed unsafe class WeaponReload : EventBase<WeaponReload.Arguments, WeaponReload.Priority>, IHookService
{
public enum Priority
{
@ -13,9 +14,12 @@ public sealed unsafe class WeaponReload : EventWrapperPtr<DrawDataContainer, Cha
DrawObjectState = 0,
}
public WeaponReload(Luna.HookManager hooks)
: base("Reload Weapon")
=> _task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.WeaponReload);
public WeaponReload(Logger log, HookManager hooks)
: base("Reload Weapon", log)
{
_postEvent = new PostEvent("Created CharacterBase", log);
_task = hooks.CreateHook<Delegate>(Name, Address, Detour, !HookOverrides.Instance.Objects.WeaponReload);
}
private readonly Task<Hook<Delegate>> _task;
@ -40,31 +44,44 @@ public sealed unsafe class WeaponReload : EventWrapperPtr<DrawDataContainer, Cha
{
var gameObject = drawData->OwnerObject;
Penumbra.Log.Verbose($"[{Name}] Triggered with drawData: 0x{(nint)drawData:X}, {slot}, {weapon}, {d}, {e}, {f}, {g}, {h}.");
Invoke(drawData, gameObject, (CharacterWeapon*)(&weapon));
Invoke(new Arguments(ref *drawData, gameObject, ref *(CharacterWeapon*)(&weapon)));
_task.Result.Original(drawData, slot, weapon, d, e, f, g, h);
_postEvent.Invoke(drawData, gameObject);
_postEvent.Invoke(new PostEvent.Arguments(ref *drawData, gameObject));
}
public void Subscribe(ActionPtr<DrawDataContainer, Character> subscriber, PostEvent.Priority priority)
public void Subscribe(InAction<PostEvent.Arguments> subscriber, PostEvent.Priority priority)
=> _postEvent.Subscribe(subscriber, priority);
public void Unsubscribe(ActionPtr<DrawDataContainer, Character> subscriber)
public void Unsubscribe(InAction<PostEvent.Arguments> subscriber)
=> _postEvent.Unsubscribe(subscriber);
private readonly PostEvent _postEvent = new("Created CharacterBase");
private readonly PostEvent _postEvent;
protected override void Dispose(bool disposing)
{
_postEvent.Dispose();
}
public class PostEvent(string name) : EventWrapperPtr<DrawDataContainer, Character, PostEvent.Priority>(name)
{
public class PostEvent(string name, Logger log) : EventBase<PostEvent.Arguments, PostEvent.Priority>(name, log)
{
public enum Priority
{
/// <seealso cref="PathResolving.DrawObjectState"/>
DrawObjectState = 0,
}
public readonly ref struct Arguments(ref DrawDataContainer drawData, Actor owner)
{
public readonly ref DrawDataContainer DrawDataContainer = ref drawData;
public readonly Actor Owner = owner;
}
}
public readonly ref struct Arguments(ref DrawDataContainer drawData, Actor owner, ref CharacterWeapon weapon)
{
public readonly ref DrawDataContainer DrawDataContainer = ref drawData;
public readonly Actor Owner = owner;
public readonly ref CharacterWeapon Weapon = ref weapon;
}
}

View file

@ -1,6 +1,6 @@
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes;
using Luna;
using Penumbra.Collections;
using Penumbra.GameData;
using Penumbra.GameData.Interop;
@ -17,7 +17,7 @@ namespace Penumbra.Interop.Hooks.PostProcessing;
/// <item>Parameter is the collection associated with the game object. </item>
/// <item>Parameter is the slot that was recomputed. If this is Unknown, it is a general new update call. </item>
/// </list> </summary>
public sealed unsafe class AttributeHook : EventWrapper<Actor, Model, ModCollection, AttributeHook.Priority>, Luna.IHookService
public sealed unsafe class AttributeHook : EventBase<AttributeHook.Arguments, AttributeHook.Priority>, IHookService
{
public enum Priority
{
@ -28,8 +28,8 @@ public sealed unsafe class AttributeHook : EventWrapper<Actor, Model, ModCollect
private readonly CollectionResolver _resolver;
private readonly Configuration _config;
public AttributeHook(Luna.HookManager hooks, Configuration config, CollectionResolver resolver)
: base("Update Model Attributes")
public AttributeHook(Logger log, HookManager hooks, Configuration config, CollectionResolver resolver)
: base("Update Model Attributes", log)
{
_config = config;
_resolver = resolver;
@ -76,9 +76,11 @@ public sealed unsafe class AttributeHook : EventWrapper<Actor, Model, ModCollect
var identifiedActor = resolveData.AssociatedGameObject;
var identifiedCollection = resolveData.ModCollection;
Penumbra.Log.Excessive($"[{Name}] Invoked on 0x{(ulong)human:X} (0x{identifiedActor:X}).");
Invoke(identifiedActor, human, identifiedCollection);
Invoke(new Arguments(identifiedActor, human, identifiedCollection));
}
public readonly record struct Arguments(Actor Character, Model Human, ModCollection Collection);
protected override void Dispose(bool disposing)
=> _task.Result.Dispose();
}

View file

@ -185,9 +185,9 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
_characterOcclusionState.GetAndResetSlowPathCallDelta(),
_hairMaskState.GetAndResetSlowPathCallDelta());
private void OnMtrlLoaded(nint mtrlResourceHandle, nint gameObject)
private void OnMtrlLoaded(in MtrlLoaded.Arguments arguments)
{
var mtrl = (MaterialResourceHandle*)mtrlResourceHandle;
var mtrl = (MaterialResourceHandle*)arguments.MaterialResourceHandle;
var shpk = mtrl->ShaderPackageResourceHandle;
if (shpk == null)
return;
@ -199,20 +199,20 @@ public sealed unsafe class ShaderReplacementFixer : IDisposable, IRequiredServic
?? GetStateForModelRendererUnk(shpkName) ?? GetStateForColorTable(shpkName);
if (shpkState != null && shpk != shpkState.DefaultShaderPackage)
shpkState.TryAddMaterial(mtrlResourceHandle);
shpkState.TryAddMaterial(arguments.MaterialResourceHandle);
}
private void OnResourceHandleDestructor(Structs.ResourceHandle* handle)
private void OnResourceHandleDestructor(in ResourceHandleDestructor.Arguments arguments)
{
_skinState.TryRemoveMaterial(handle);
_characterStockingsState.TryRemoveMaterial(handle);
_characterLegacyState.TryRemoveMaterial(handle);
_irisState.TryRemoveMaterial(handle);
_characterGlassState.TryRemoveMaterial(handle);
_characterTransparencyState.TryRemoveMaterial(handle);
_characterTattooState.TryRemoveMaterial(handle);
_characterOcclusionState.TryRemoveMaterial(handle);
_hairMaskState.TryRemoveMaterial(handle);
_skinState.TryRemoveMaterial(arguments.ResourceHandle);
_characterStockingsState.TryRemoveMaterial(arguments.ResourceHandle);
_characterLegacyState.TryRemoveMaterial(arguments.ResourceHandle);
_irisState.TryRemoveMaterial(arguments.ResourceHandle);
_characterGlassState.TryRemoveMaterial(arguments.ResourceHandle);
_characterTransparencyState.TryRemoveMaterial(arguments.ResourceHandle);
_characterTattooState.TryRemoveMaterial(arguments.ResourceHandle);
_characterOcclusionState.TryRemoveMaterial(arguments.ResourceHandle);
_hairMaskState.TryRemoveMaterial(arguments.ResourceHandle);
}
private ModdedShaderPackageState? GetStateForHumanSetup(MaterialResourceHandle* mtrlResource)

View file

@ -347,9 +347,9 @@ public unsafe class ResourceLoader : IDisposable, Luna.IService
returnValue = 1;
}
private void ResourceDestructorHandler(ResourceHandle* handle)
private void ResourceDestructorHandler(in ResourceHandleDestructor.Arguments arguments)
{
_ongoingLoads.TryRemove((nint)handle, out _);
_ongoingLoads.TryRemove((nint)arguments.ResourceHandle, out _);
}
/// <summary> Compute the CRC32 hash for a given path together with potential resource parameters. </summary>

View file

@ -1,5 +1,6 @@
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using OtterGui.Services;
using Penumbra.Communication;
using Penumbra.GameData;
using Penumbra.Services;
@ -26,7 +27,7 @@ public sealed unsafe class LoadMtrl : FastHook<LoadMtrl.Delegate>
_gameState.MtrlData.Value = mtrlData;
var ret = Task.Result.Original(handle, unk1, unk2);
_gameState.MtrlData.Value = last;
_communicator.MtrlLoaded.Invoke((nint)handle, mtrlData.AssociatedGameObject);
_communicator.MtrlLoaded.Invoke(new MtrlLoaded.Arguments((nint)handle, mtrlData.AssociatedGameObject));
return ret;
}
}

View file

@ -1,12 +1,12 @@
using Dalamud.Hooking;
using OtterGui.Classes;
using Luna;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
using Penumbra.UI.ResourceWatcher;
namespace Penumbra.Interop.Hooks.Resources;
public sealed unsafe class ResourceHandleDestructor : EventWrapperPtr<ResourceHandle, ResourceHandleDestructor.Priority>, Luna.IHookService
public sealed unsafe class ResourceHandleDestructor : EventBase<ResourceHandleDestructor.Arguments, ResourceHandleDestructor.Priority>, IHookService
{
public enum Priority
{
@ -23,8 +23,8 @@ public sealed unsafe class ResourceHandleDestructor : EventWrapperPtr<ResourceHa
ResourceWatcher,
}
public ResourceHandleDestructor(Luna.HookManager hooks)
: base("Destroy ResourceHandle")
public ResourceHandleDestructor(Logger log, HookManager hooks)
: base("Destroy ResourceHandle", log)
=> _task = hooks.CreateHook<Delegate>(Name, Sigs.ResourceHandleDestructor, Detour,
!HookOverrides.Instance.Resources.ResourceHandleDestructor);
@ -50,7 +50,12 @@ public sealed unsafe class ResourceHandleDestructor : EventWrapperPtr<ResourceHa
private nint Detour(ResourceHandle* resourceHandle)
{
Penumbra.Log.Excessive($"[{Name}] Triggered with 0x{(nint)resourceHandle:X}.");
Invoke(resourceHandle);
Invoke(new Arguments(resourceHandle));
return _task.Result.Original(resourceHandle);
}
public readonly struct Arguments(ResourceHandle* resourceHandle)
{
public readonly ResourceHandle* ResourceHandle = resourceHandle;
}
}

View file

@ -1,6 +1,5 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.Interop.Hooks.Objects;
@ -25,7 +24,7 @@ public sealed class CutsceneService : Luna.IRequiredService, IDisposable
.Where(i => _objects[i].Valid)
.Select(i => KeyValuePair.Create(i, this[i] ?? _objects.GetDalamudObject(i)!));
public unsafe CutsceneService(ObjectManager objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor,
public CutsceneService(ObjectManager objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor,
ConstructCutsceneCharacter constructCutsceneCharacter, IClientState clientState)
{
_objects = objects;
@ -85,15 +84,16 @@ public sealed class CutsceneService : Luna.IRequiredService, IDisposable
return -1;
}
public unsafe void Dispose()
public void Dispose()
{
_copyCharacter.Unsubscribe(OnCharacterCopy);
_characterDestructor.Unsubscribe(OnCharacterDestructor);
_constructCutsceneCharacter.Unsubscribe(OnSetupPlayerNpc);
}
private unsafe void OnCharacterDestructor(Character* character)
private unsafe void OnCharacterDestructor(in CharacterDestructor.Arguments arguments)
{
var character = arguments.Character.AsCharacter;
if (character->GameObject.ObjectIndex < CutsceneStartIdx)
{
// Remove all associations for now non-existing actor.
@ -118,21 +118,21 @@ public sealed class CutsceneService : Luna.IRequiredService, IDisposable
}
}
private unsafe void OnCharacterCopy(Character* target, Character* source)
private void OnCharacterCopy(in CopyCharacter.Arguments arguments)
{
if (target == null || target->GameObject.ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx)
if (!arguments.TargetCharacter.Valid || arguments.TargetCharacter.Index.Index is < CutsceneStartIdx or >= CutsceneEndIdx)
return;
var idx = target->GameObject.ObjectIndex - CutsceneStartIdx;
_copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1);
var idx = arguments.TargetCharacter.Index.Index - CutsceneStartIdx;
_copiedCharacters[idx] = (short)(arguments.SourceCharacter.Valid ? arguments.SourceCharacter.Index : -1);
}
private unsafe void OnSetupPlayerNpc(Character* npc)
private void OnSetupPlayerNpc(in ConstructCutsceneCharacter.Arguments arguments)
{
if (npc == null || npc->ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx)
if (!arguments.Character.Valid || arguments.Character.Index.Index is < CutsceneStartIdx or >= CutsceneEndIdx)
return;
var idx = npc->GameObject.ObjectIndex - CutsceneStartIdx;
var idx = arguments.Character.Index.Index - CutsceneStartIdx;
_copiedCharacters[idx] = 0;
}

View file

@ -1,6 +1,4 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.GameData.Interop;
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
using Penumbra.GameData.Structs;
@ -22,7 +20,7 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<Model, (A
public nint LastGameObject
=> _gameState.LastGameObject;
public unsafe DrawObjectState(ObjectManager objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload,
public DrawObjectState(ObjectManager objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload,
CharacterBaseDestructor characterBaseDestructor, GameState gameState, IFramework framework, CharacterDestructor characterDestructor)
{
_objects = objects;
@ -76,7 +74,7 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<Model, (A
public IEnumerable<(Actor, ObjectIndex, bool)> Values
=> _drawObjectToGameObject.Values;
public unsafe void Dispose()
public void Dispose()
{
_weaponReload.Unsubscribe(OnWeaponReloading);
_weaponReload.Unsubscribe(OnWeaponReloaded);
@ -89,17 +87,16 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<Model, (A
/// Seems like sometimes the draw object of a game object is destroyed in frames after the original game object is already destroyed.
/// So protect against outdated game object pointers in the dictionary.
/// </remarks>
private unsafe void OnCharacterDestructor(Character* a)
private unsafe void OnCharacterDestructor(in CharacterDestructor.Arguments arguments)
{
if (a is null)
if (!arguments.Character.Valid)
return;
var character = (nint)a;
var delete = stackalloc nint[5];
var current = 0;
foreach (var (drawObject, (gameObject, _, _)) in _drawObjectToGameObject)
{
if (gameObject != character)
if (gameObject != arguments.Character.Address)
continue;
delete[current++] = drawObject;
@ -111,28 +108,27 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<Model, (A
{
_drawObjectToGameObject.Remove(*ptr, out var pair);
Penumbra.Log.Excessive(
$"[DrawObjectState] Removed draw object 0x{*ptr:X} -> 0x{(nint)a:X} (actual: 0x{pair.GameObject.Address:X}, {pair.IsChild}).");
$"[DrawObjectState] Removed draw object 0x{*ptr:X} -> 0x{arguments.Character.Address:X} (actual: 0x{pair.GameObject.Address:X}, {pair.IsChild}).");
}
}
private unsafe void OnWeaponReloading(DrawDataContainer* _, Character* character, CharacterWeapon* _2)
=> _gameState.QueueGameObject((nint)character);
private void OnWeaponReloading(in WeaponReload.Arguments arguments)
=> _gameState.QueueGameObject(arguments.Owner);
private unsafe void OnWeaponReloaded(DrawDataContainer* _, Character* character)
private unsafe void OnWeaponReloaded(in WeaponReload.PostEvent.Arguments arguments)
{
_gameState.DequeueGameObject();
IterateDrawObjectTree((Object*)character->GameObject.DrawObject, (nint)character, false, false);
IterateDrawObjectTree((Object*)arguments.Owner.Model.Address, arguments.Owner, false, false);
}
private unsafe void OnCharacterBaseDestructor(CharacterBase* characterBase)
=> _drawObjectToGameObject.Remove((nint)characterBase);
private void OnCharacterBaseDestructor(in CharacterBaseDestructor.Arguments arguments)
=> _drawObjectToGameObject.Remove(arguments.CharacterBase.Address);
private unsafe void OnCharacterBaseCreated(ModelCharaId modelCharaId, CustomizeArray* customize, CharacterArmor* equipment,
CharacterBase* drawObject)
private void OnCharacterBaseCreated(in CreateCharacterBase.PostEvent.Arguments arguments)
{
Actor gameObject = LastGameObject;
if (gameObject.Valid)
_drawObjectToGameObject[(nint)drawObject] = (gameObject, gameObject.Index, false);
_drawObjectToGameObject[arguments.CharacterBase] = (gameObject, gameObject.Index, false);
}
/// <summary>

View file

@ -1,5 +1,4 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
@ -80,15 +79,15 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(nint A
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
private void CollectionChangeClear(CollectionType type, ModCollection? _1, ModCollection? _2, string _3)
private void CollectionChangeClear(in CollectionChange.Arguments arguments)
{
if (type is not (CollectionType.Current or CollectionType.Interface or CollectionType.Inactive))
if (arguments.Type is not (CollectionType.Current or CollectionType.Interface or CollectionType.Inactive))
_dirty = _cache.Count > 0;
}
private void TerritoryClear(ushort _2)
=> _dirty = _cache.Count > 0;
private void OnCharacterDestructor(Character* character)
=> _cache.Remove((nint)character);
private void OnCharacterDestructor(in CharacterDestructor.Arguments arguments)
=> _cache.Remove(arguments.Character);
}

View file

@ -1,7 +1,7 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Luna;
using Penumbra.Collections;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Services;
using Penumbra.Services;
@ -34,7 +34,7 @@ namespace Penumbra.Interop.PathResolving;
// ChangeCustomize and RspSetupCharacter, which is hooked here, as well as Character.CalculateHeight.
// GMP Entries seem to be only used by "48 8B ?? 53 55 57 48 83 ?? ?? 48 8B", which is SetupVisor.
public sealed unsafe class MetaState : IDisposable, Luna.IService
public sealed unsafe class MetaState : IDisposable, IService
{
public readonly Configuration Config;
private readonly CommunicatorService _communicator;
@ -92,27 +92,27 @@ public sealed unsafe class MetaState : IDisposable, Luna.IService
_createCharacterBase.Unsubscribe(OnCharacterBaseCreated);
}
private void OnCreatingCharacterBase(ModelCharaId* modelCharaId, CustomizeArray* customize, CharacterArmor* equipData)
private void OnCreatingCharacterBase(in CreateCharacterBase.Arguments arguments)
{
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection.Identity.Id, (nint)modelCharaId, (nint)customize, (nint)equipData);
_communicator.CreatingCharacterBase.Invoke(new CreatingCharacterBase.Arguments(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection, (nint)Unsafe.AsPointer(ref arguments.ModelCharaId), (nint)arguments.Customize, (nint)arguments.Equipment));
var decal = new DecalReverter(Config, _characterUtility, _resources, _lastCreatedCollection,
UsesDecal(*(uint*)modelCharaId, (nint)customize));
UsesDecal(arguments.ModelCharaId, (nint)arguments.Customize));
RspCollection.Push(_lastCreatedCollection);
_characterBaseCreateMetaChanges.Dispose(); // Should always be empty.
_characterBaseCreateMetaChanges = new DisposableContainer(decal);
}
private void OnCharacterBaseCreated(ModelCharaId _1, CustomizeArray* _2, CharacterArmor* _3, CharacterBase* drawObject)
private void OnCharacterBaseCreated(in CreateCharacterBase.PostEvent.Arguments arguments)
{
_characterBaseCreateMetaChanges.Dispose();
_characterBaseCreateMetaChanges = DisposableContainer.Empty;
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && drawObject != null)
_communicator.CreatedCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection, (nint)drawObject);
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && arguments.CharacterBase.Valid)
_communicator.CreatedCharacterBase.Invoke(new CreatedCharacterBase.Arguments(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection, arguments.CharacterBase));
RspCollection.Pop();
_lastCreatedCollection = ResolveData.Invalid;
}

View file

@ -85,6 +85,6 @@ public sealed unsafe class SubfileHelper : IDisposable, IReadOnlyCollection<KeyV
}
}
private void ResourceDestroyed(ResourceHandle* handle)
=> _gameState.SubFileCollection.TryRemove((nint)handle, out _);
private void ResourceDestroyed(in ResourceHandleDestructor.Arguments arguments)
=> _gameState.SubFileCollection.TryRemove((nint)arguments.ResourceHandle, out _);
}

View file

@ -28,7 +28,7 @@ public unsafe class CharacterUtility : IDisposable, Luna.IRequiredService
public bool Ready { get; private set; }
public readonly CharacterUtilityFinished LoadingFinished = new();
public readonly CharacterUtilityFinished LoadingFinished;
public nint DefaultHumanPbdResource { get; private set; }
public nint DefaultTransparentResource { get; private set; }
@ -56,14 +56,16 @@ public unsafe class CharacterUtility : IDisposable, Luna.IRequiredService
private readonly IFramework _framework;
public CharacterUtility(IFramework framework, IGameInteropProvider interop)
public CharacterUtility(IFramework framework, IGameInteropProvider interop, CharacterUtilityFinished finished)
{
LoadingFinished = finished;
interop.InitializeFromAttributes(this);
_lists = Enumerable.Range(0, RelevantIndices.Length)
.Select(idx => new MetaList(new InternalIndex(idx)))
.ToArray();
_framework = framework;
LoadingFinished.Subscribe(() => Penumbra.Log.Debug("Loading of CharacterUtility finished."), CharacterUtilityFinished.Priority.OnFinishedLoading);
_framework = framework;
LoadingFinished.Subscribe(() => Penumbra.Log.Debug("Loading of CharacterUtility finished."),
CharacterUtilityFinished.Priority.OnFinishedLoading);
LoadDefaultResources(null!);
if (!Ready)
_framework.Update += LoadDefaultResources;

View file

@ -430,7 +430,7 @@ public sealed unsafe partial class RedrawService : IDisposable
}
}
private void OnModFileChanged(Mod _1, FileRegistry _2)
private void OnModFileChanged(in ModFileChanged.Arguments _)
{
if (!_config.Ephemeral.ForceRedrawOnFileChange)
return;

View file

@ -3,17 +3,16 @@ using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
using Lumina.Excel.Sheets;
using Penumbra.Collections;
using Luna;
using Penumbra.Communication;
using Penumbra.GameData;
using Penumbra.Mods.Editor;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.String.Classes;
namespace Penumbra.Interop.Services;
public unsafe class SchedulerResourceManagementService : Luna.IService, IDisposable
public unsafe class SchedulerResourceManagementService : IService, IDisposable
{
private static readonly CiByteString TmbExtension = new(".tmb"u8, MetaDataComputation.All);
private static readonly CiByteString FolderPrefix = new("chara/action/"u8, MetaDataComputation.All);
@ -40,16 +39,15 @@ public unsafe class SchedulerResourceManagementService : Luna.IService, IDisposa
interop.InitializeFromAttributes(this);
}
private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath gamePath, FullPath oldPath,
FullPath newPath, IMod? mod)
private void OnResolvedFileChange(in ResolvedFileChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ResolvedFileChanged.Type.Added:
CheckFile(gamePath);
CheckFile(arguments.GamePath);
return;
case ResolvedFileChanged.Type.FullRecomputeFinished:
foreach (var path in collection.ResolvedFiles.Keys)
foreach (var path in arguments.Collection.ResolvedFiles.Keys)
CheckFile(path);
return;
}

View file

@ -1,4 +1,3 @@
using Penumbra.Collections;
using Penumbra.Collections.Cache;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
@ -41,27 +40,27 @@ public unsafe class ShapeAttributeManager : Luna.IRequiredService, IDisposable
public void Dispose()
=> _attributeHook.Unsubscribe(OnAttributeComputed);
private void OnAttributeComputed(Actor actor, Model model, ModCollection collection)
private void OnAttributeComputed(in AttributeHook.Arguments arguments)
{
if (!collection.HasCache)
if (!arguments.Collection.HasCache)
return;
_genderRace = (GenderRace)model.AsHuman->RaceSexId;
_genderRace = (GenderRace)arguments.Human.AsHuman->RaceSexId;
for (_slotIndex = 0; _slotIndex < NumSlots; ++_slotIndex)
{
_modelIndex = UsedModels[_slotIndex];
_model = model.AsHuman->Models[_modelIndex.ToIndex()];
_model = arguments.Human.AsHuman->Models[_modelIndex.ToIndex()];
if (_model is null || _model->ModelResourceHandle is null)
continue;
_ids[(int)_modelIndex] = model.GetModelId(_modelIndex);
CheckShapes(collection.MetaCache!.Shp);
CheckAttributes(collection.MetaCache!.Atr);
_ids[(int)_modelIndex] = arguments.Human.GetModelId(_modelIndex);
CheckShapes(arguments.Collection.MetaCache!.Shp);
CheckAttributes(arguments.Collection.MetaCache!.Atr);
if (_modelIndex is <= HumanSlot.LFinger and >= HumanSlot.Ears)
AccessoryImcCheck(model);
AccessoryImcCheck(arguments.Human);
}
UpdateDefaultMasks(model, collection.MetaCache!.Shp);
UpdateDefaultMasks(arguments.Human, arguments.Collection.MetaCache!.Shp);
}
private void AccessoryImcCheck(Model model)

View file

@ -1,4 +1,4 @@
using Luna.Files;
using Luna;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;
using Penumbra.Mods.SubMods;

View file

@ -1,3 +1,4 @@
using Penumbra.Communication;
using Penumbra.Mods.Manager;
using Penumbra.Mods.SubMods;
using Penumbra.Services;
@ -137,7 +138,7 @@ public class ModFileEditor(ModFileCollection files, ModManager modManager, Commu
try
{
File.Delete(file.File.FullName);
communicator.ModFileChanged.Invoke(mod, file);
communicator.ModFileChanged.Invoke(new ModFileChanged.Arguments(mod, file));
Penumbra.Log.Debug($"[DeleteFiles] Deleted {file.File.FullName} from {mod.Name}.");
++deletions;
}

View file

@ -1,7 +1,6 @@
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Utility;
using Luna;
using Luna.Files;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Mods.Groups;
@ -478,27 +477,27 @@ public class ModMerger : IDisposable, IService
}
}
private void OnSelectionChange(Mod? oldSelection, Mod? newSelection)
private void OnSelectionChange(in ModSelection.Arguments arguments)
{
if (OptionGroupName == "Merges" && OptionName.Length == 0 || OptionName == oldSelection?.Name.Text)
OptionName = newSelection?.Name.Text ?? string.Empty;
if (OptionGroupName == "Merges" && OptionName.Length == 0 || OptionName == arguments.OldSelection?.Name.Text)
OptionName = arguments.NewSelection?.Name.Text ?? string.Empty;
if (MergeToMod == newSelection)
if (MergeToMod == arguments.NewSelection)
MergeToMod = null;
SelectedOptions.Clear();
}
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Deleted:
{
if (mod == MergeFromMod)
if (arguments.Mod == MergeFromMod)
SelectedOptions.Clear();
if (mod == MergeToMod)
if (arguments.Mod == MergeToMod)
MergeToMod = null;
break;
}

View file

@ -1,6 +1,5 @@
using Dalamud.Interface.ImGuiNotification;
using Luna;
using Luna.Files;
using OtterGui.Tasks;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager;

View file

@ -1,4 +1,4 @@
using Luna.Files;
using Luna;
using Penumbra.Collections;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;

View file

@ -1,8 +1,6 @@
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.SubMods;
using Penumbra.Services;
using Penumbra.Util;
@ -39,52 +37,51 @@ public class ModCacheManager : IDisposable, Luna.IService
_communicator.ModDiscoveryFinished.Unsubscribe(OnModDiscoveryFinished);
}
private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int fromIdx)
private void OnModOptionChange(in ModOptionChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModOptionChangeType.GroupAdded:
case ModOptionChangeType.GroupDeleted:
case ModOptionChangeType.OptionAdded:
case ModOptionChangeType.OptionDeleted:
UpdateChangedItems(mod);
UpdateCounts(mod);
UpdateChangedItems(arguments.Mod);
UpdateCounts(arguments.Mod);
break;
case ModOptionChangeType.GroupTypeChanged:
UpdateHasOptions(mod);
UpdateHasOptions(arguments.Mod);
break;
case ModOptionChangeType.OptionFilesChanged:
case ModOptionChangeType.OptionFilesAdded:
UpdateChangedItems(mod);
UpdateFileCount(mod);
UpdateChangedItems(arguments.Mod);
UpdateFileCount(arguments.Mod);
break;
case ModOptionChangeType.OptionSwapsChanged:
UpdateChangedItems(mod);
UpdateSwapCount(mod);
UpdateChangedItems(arguments.Mod);
UpdateSwapCount(arguments.Mod);
break;
case ModOptionChangeType.OptionMetaChanged:
UpdateChangedItems(mod);
UpdateMetaCount(mod);
UpdateChangedItems(arguments.Mod);
UpdateMetaCount(arguments.Mod);
break;
}
}
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? old, DirectoryInfo? @new)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Added:
case ModPathChangeType.Reloaded:
RefreshWithChangedItems(mod);
RefreshWithChangedItems(arguments.Mod);
break;
}
}
private static void OnModDataChange(ModDataChangeType type, Mod mod, string? _)
private static void OnModDataChange(in ModDataChanged.Arguments arguments)
{
if ((type & (ModDataChangeType.LocalTags | ModDataChangeType.ModTags)) != 0)
UpdateTags(mod);
if ((arguments.Type & (ModDataChangeType.LocalTags | ModDataChangeType.ModTags)) is not 0)
UpdateTags(arguments.Mod);
}
private void OnModDiscoveryFinished()

View file

@ -1,5 +1,6 @@
using Dalamud.Utility;
using OtterGui.Classes;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.GameData.Structs;
using Penumbra.Services;
@ -55,7 +56,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
var oldName = mod.Name;
mod.Name = newName;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Name, mod, oldName.Text);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Name, mod, oldName.Text));
}
public void ChangeModAuthor(Mod mod, string newAuthor)
@ -65,7 +66,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Author = newAuthor;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Author, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Author, mod, null));
}
public void ChangeModDescription(Mod mod, string newDescription)
@ -75,7 +76,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Description = newDescription;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Description, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Description, mod, null));
}
public void ChangeModVersion(Mod mod, string newVersion)
@ -85,7 +86,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Version = newVersion;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Version, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Version, mod, null));
}
public void ChangeModWebsite(Mod mod, string newWebsite)
@ -95,7 +96,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Website = newWebsite;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Website, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Website, mod, null));
}
public void ChangeRequiredFeatures(Mod mod, FeatureFlags flags)
@ -105,7 +106,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.RequiredFeatures = flags;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.RequiredFeatures, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.RequiredFeatures, mod, null));
}
public void ChangeModTag(Mod mod, int tagIdx, string newTag)
@ -121,7 +122,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Favorite = state;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Favorite, mod, null));
}
public void ResetModImportDate(Mod mod)
@ -132,7 +133,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.ImportDate = newDate;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.ImportDate, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.ImportDate, mod, null));
}
public void ChangeModNote(Mod mod, string newNote)
@ -142,7 +143,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.Note = newNote;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.Favorite, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.Favorite, mod, null));
}
private void ChangeTag(Mod mod, int tagIdx, string newTag, bool local)
@ -170,7 +171,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
saveService.QueueSave(new ModLocalData(mod));
if (flags != 0)
communicatorService.ModDataChanged.Invoke(flags, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(flags, mod, null));
}
public void MoveDataFile(DirectoryInfo oldMod, DirectoryInfo newMod)
@ -196,13 +197,13 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
{
++mod.LastChangedItemsUpdate;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.PreferredChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.PreferredChangedItems, mod, null));
}
if (toDefault && CleanExisting(mod.DefaultPreferredItems))
{
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.DefaultChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.DefaultChangedItems, mod, null));
}
bool CleanExisting(HashSet<CustomItemId> items)
@ -251,13 +252,13 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
{
++mod.LastChangedItemsUpdate;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.PreferredChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.PreferredChangedItems, mod, null));
}
if (fromDefault && mod.DefaultPreferredItems.Remove(id))
{
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.DefaultChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.DefaultChangedItems, mod, null));
}
}
@ -271,7 +272,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.PreferredChangedItems = newSet;
++mod.LastChangedItemsUpdate;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.PreferredChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.PreferredChangedItems, mod, null));
}
newSet = new HashSet<CustomItemId>(mod.DefaultPreferredItems.Count);
@ -279,7 +280,7 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
{
mod.DefaultPreferredItems = newSet;
saveService.QueueSave(new ModMeta(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.DefaultChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.DefaultChangedItems, mod, null));
}
return;
@ -308,6 +309,6 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic
mod.PreferredChangedItems.UnionWith(mod.DefaultPreferredItems);
++mod.LastChangedItemsUpdate;
saveService.QueueSave(new ModLocalData(mod));
communicatorService.ModDataChanged.Invoke(ModDataChangeType.PreferredChangedItems, mod, null);
communicatorService.ModDataChanged.Invoke(new ModDataChanged.Arguments(ModDataChangeType.PreferredChangedItems, mod, null));
}
}

View file

@ -79,14 +79,13 @@ public class ModExportManager : IDisposable, Luna.IService
=> _communicator.ModPathChanged.Unsubscribe(OnModPathChange);
/// <summary> Automatically migrate the backup file to the new name if any exists. </summary>
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
DirectoryInfo? newDirectory)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
if (type is not ModPathChangeType.Moved || oldDirectory == null || newDirectory == null)
if (arguments.Type is not ModPathChangeType.Moved || arguments.OldDirectory is null || arguments.NewDirectory is null)
return;
mod.ModPath = oldDirectory;
new ModBackup(this, mod).Move(null, newDirectory.Name);
mod.ModPath = newDirectory;
arguments.Mod.ModPath = arguments.OldDirectory;
new ModBackup(this, arguments.Mod).Move(null, arguments.NewDirectory.Name);
arguments.Mod.ModPath = arguments.NewDirectory;
}
}

View file

@ -77,21 +77,21 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, ISer
}
// Update sort order when defaulted mod names change.
private void OnModDataChange(ModDataChangeType type, Mod mod, string? oldName)
private void OnModDataChange(in ModDataChanged.Arguments arguments)
{
if (!type.HasFlag(ModDataChangeType.Name) || oldName == null || !TryGetValue(mod, out var leaf))
if (!arguments.Type.HasFlag(ModDataChangeType.Name) || arguments.OldName == null || !TryGetValue(arguments.Mod, out var leaf))
return;
var old = oldName.FixName();
var old = arguments.OldName.FixName();
if (old == leaf.Name || leaf.Name.IsDuplicateName(out var baseName, out _) && baseName == old)
RenameWithDuplicates(leaf, mod.Name.Text);
RenameWithDuplicates(leaf, arguments.Mod.Name.Text);
}
// Update the filesystem if a mod has been added or removed.
// Save it, if the mod directory has been moved, since this will change the save format.
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldPath, DirectoryInfo? newPath)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Added:
var parent = Root;
@ -103,14 +103,14 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable, ISer
catch (Exception e)
{
Penumbra.Messager.NotificationMessage(e,
$"Could not move newly imported mod {mod.Name} to default import folder {_config.DefaultImportFolder}.",
$"Could not move newly imported mod {arguments.Mod.Name} to default import folder {_config.DefaultImportFolder}.",
NotificationType.Warning);
}
CreateDuplicateLeaf(parent, mod.Name.Text, mod);
CreateDuplicateLeaf(parent, arguments.Mod.Name.Text, arguments.Mod);
break;
case ModPathChangeType.Deleted:
if (TryGetValue(mod, out var leaf))
if (TryGetValue(arguments.Mod, out var leaf))
Delete(leaf);
break;

View file

@ -93,7 +93,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
mod.Index = Count;
Mods.Add(mod);
_communicator.ModPathChanged.Invoke(ModPathChangeType.Added, mod, null, mod.ModPath);
_communicator.ModPathChanged.Invoke(new ModPathChanged.Arguments(ModPathChangeType.Added, mod, null, mod.ModPath));
Penumbra.Log.Debug($"Added new mod {mod.Name} from {modFolder.FullName}.");
}
@ -125,7 +125,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
/// </summary>
public void RemoveMod(Mod mod)
{
_communicator.ModPathChanged.Invoke(ModPathChangeType.Deleted, mod, mod.ModPath, null);
_communicator.ModPathChanged.Invoke(new ModPathChanged.Arguments(ModPathChangeType.Deleted, mod, mod.ModPath, null));
foreach (var remainingMod in Mods.Skip(mod.Index + 1))
--remainingMod.Index;
Mods.RemoveAt(mod.Index);
@ -140,7 +140,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
{
var oldName = mod.Name;
_communicator.ModPathChanged.Invoke(ModPathChangeType.StartingReload, mod, mod.ModPath, mod.ModPath);
_communicator.ModPathChanged.Invoke(new ModPathChanged.Arguments(ModPathChangeType.StartingReload, mod, mod.ModPath, mod.ModPath));
if (!Creator.ReloadMod(mod, true, false, out var metaChange))
{
if (mod.RequiredFeatures is not FeatureFlags.Invalid)
@ -151,9 +151,9 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
return;
}
_communicator.ModPathChanged.Invoke(ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath);
_communicator.ModPathChanged.Invoke(new ModPathChanged.Arguments(ModPathChangeType.Reloaded, mod, mod.ModPath, mod.ModPath));
if (metaChange != ModDataChangeType.None)
_communicator.ModDataChanged.Invoke(metaChange, mod, oldName);
_communicator.ModDataChanged.Invoke(new ModDataChanged.Arguments(metaChange, mod, oldName));
}
@ -213,9 +213,9 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
return;
}
_communicator.ModPathChanged.Invoke(ModPathChangeType.Moved, mod, oldDirectory, dir);
_communicator.ModPathChanged.Invoke(new ModPathChanged.Arguments(ModPathChangeType.Moved, mod, oldDirectory, dir));
if (metaChange != ModDataChangeType.None)
_communicator.ModDataChanged.Invoke(metaChange, mod, oldName);
_communicator.ModDataChanged.Invoke(new ModDataChanged.Arguments(metaChange, mod, oldName));
}
/// <summary> Return the state of the new potential name of a directory. </summary>
@ -247,16 +247,15 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
/// <summary> Add new mods to NewMods and remove deleted mods from NewMods. </summary>
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
DirectoryInfo? newDirectory)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case ModPathChangeType.Added: SetNew(mod); break;
case ModPathChangeType.Deleted: SetKnown(mod); break;
case ModPathChangeType.Added: SetNew(arguments.Mod); break;
case ModPathChangeType.Deleted: SetKnown(arguments.Mod); break;
case ModPathChangeType.Moved:
if (oldDirectory != null && newDirectory != null)
DataEditor.MoveDataFile(oldDirectory, newDirectory);
if (arguments.OldDirectory is not null && arguments.NewDirectory is not null)
DataEditor.MoveDataFile(arguments.OldDirectory, arguments.NewDirectory);
break;
}
@ -313,7 +312,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
_config.ModDirectory = newPath;
_config.Save();
Penumbra.Log.Information($"Set new mod base directory from {_config.ModDirectory} to {newPath}.");
_communicator.ModDirectoryChanged.Invoke(newPath, valid);
_communicator.ModDirectoryChanged.Invoke(new ModDirectoryChanged.Arguments(newPath, valid));
}
@ -346,7 +345,7 @@ public sealed class ModManager : ModStorage, IDisposable, Luna.IService
catch (Exception ex)
{
Valid = false;
_communicator.ModDirectoryChanged.Invoke(BasePath.FullName, false);
_communicator.ModDirectoryChanged.Invoke(new ModDirectoryChanged.Arguments(BasePath.FullName, false));
Penumbra.Log.Error($"Could not scan for mods:\n{ex}");
}
}

View file

@ -1,5 +1,5 @@
using Luna;
using Luna.Files;
using Penumbra.Communication;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
@ -42,6 +42,6 @@ public sealed class CombiningModGroupEditor(CommunicatorService communicator, Sa
container.Name = name;
SaveService.Save(saveType, new ModSaveGroup(container.Group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, container.Group.Mod, container.Group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.DisplayChange, container.Group.Mod, container.Group, null, null, -1));
}
}

View file

@ -1,5 +1,6 @@
using Luna.Files;
using Luna;
using OtterGui.Filesystem;
using Penumbra.Communication;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Groups;
@ -10,7 +11,7 @@ using Penumbra.Services;
namespace Penumbra.Mods.Manager.OptionEditor;
public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveService saveService, Configuration config)
: ModOptionEditor<ImcModGroup, ImcSubMod>(communicator, saveService, config), Luna.IService
: ModOptionEditor<ImcModGroup, ImcSubMod>(communicator, saveService, config), IService
{
/// <summary> Add a new, empty imc group with the given manipulation data. </summary>
public ImcModGroup? AddModGroup(Mod mod, string newName, ImcIdentifier identifier, ImcEntry defaultEntry,
@ -23,7 +24,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
var group = CreateGroup(mod, newName, identifier, defaultEntry, maxPriority);
mod.Groups.Add(group);
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupAdded, mod, group, null, null, -1));
return group;
}
@ -41,7 +42,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
};
group.OptionData.Add(subMod);
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, group.Mod, group, subMod, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionAdded, group.Mod, group, subMod, null, -1));
return subMod;
}
@ -55,7 +56,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
return;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1));
}
public void ChangeDefaultEntry(ImcModGroup group, in ImcEntry newEntry, SaveType saveType = SaveType.Queue)
@ -66,7 +67,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
group.DefaultEntry = entry;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1));
}
public void ChangeOptionAttribute(ImcSubMod option, in ImcAttributeCache cache, int idx, bool value, SaveType saveType = SaveType.Queue)
@ -75,7 +76,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
return;
SaveService.Save(saveType, new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, option.Mod, option.Group, option, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, option.Mod, option.Group, option, null, -1));
}
public void ChangeAllVariants(ImcModGroup group, bool allVariants, SaveType saveType = SaveType.Queue)
@ -85,7 +86,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
group.AllVariants = allVariants;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1));
}
public void ChangeOnlyAttributes(ImcModGroup group, bool onlyAttributes, SaveType saveType = SaveType.Queue)
@ -95,7 +96,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
group.OnlyAttributes = onlyAttributes;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1));
}
public void ChangeCanBeDisabled(ImcModGroup group, bool canBeDisabled, SaveType saveType = SaveType.Queue)
@ -105,7 +106,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
group.CanBeDisabled = canBeDisabled;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1));
}
protected override ImcModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
@ -116,8 +117,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
};
private static ImcModGroup CreateGroup(Mod mod, string newName, ImcIdentifier identifier, ImcEntry defaultEntry, ModPriority priority,
SaveType saveType = SaveType.ImmediateSync)
private static ImcModGroup CreateGroup(Mod mod, string newName, ImcIdentifier identifier, ImcEntry defaultEntry, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
=> new(mod)
{
Name = newName,
@ -137,7 +137,7 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
protected override bool MoveOption(ImcModGroup group, int optionIdxFrom, int optionIdxTo)
{
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo))
return false;
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);

View file

@ -1,8 +1,8 @@
using Dalamud.Interface.ImGuiNotification;
using Luna.Files;
using Luna;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
@ -62,7 +62,7 @@ public class ModGroupEditor(
group.DefaultSettings = defaultOption;
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DefaultOptionChanged, group.Mod, group, null, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.DefaultOptionChanged, group.Mod, group, null, null, -1));
}
/// <summary> Rename an option group if possible. </summary>
@ -75,7 +75,7 @@ public class ModGroupEditor(
saveService.ImmediateDelete(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
group.Name = newName;
saveService.ImmediateSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupRenamed, group.Mod, group, null, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupRenamed, group.Mod, group, null, null, -1));
}
/// <summary> Delete a given option group. Fires an event to prepare before actually deleting. </summary>
@ -83,10 +83,10 @@ public class ModGroupEditor(
{
var mod = group.Mod;
var idx = group.GetIndex();
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, null, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PrepareChange, mod, group, null, null, -1));
mod.Groups.RemoveAt(idx);
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupDeleted, mod, null, null, null, idx);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupDeleted, mod, null, null, null, idx));
}
/// <summary> Move the index of a given option group. </summary>
@ -98,7 +98,7 @@ public class ModGroupEditor(
return;
saveService.SaveAllOptionGroups(mod, false, config.ReplaceNonAsciiOnImport);
communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupMoved, mod, group, null, null, idxFrom);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupMoved, mod, group, null, null, idxFrom));
}
/// <summary> Change the internal priority of the given option group. </summary>
@ -109,7 +109,7 @@ public class ModGroupEditor(
group.Priority = newPriority;
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, group.Mod, group, null, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PriorityChanged, group.Mod, group, null, null, -1));
}
/// <summary> Change the description of the given option group. </summary>
@ -120,7 +120,7 @@ public class ModGroupEditor(
group.Description = newDescription;
saveService.QueueSave(new ModSaveGroup(group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, group.Mod, group, null, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.DisplayChange, group.Mod, group, null, null, -1));
}
/// <summary> Rename the given option. </summary>
@ -131,7 +131,7 @@ public class ModGroupEditor(
option.Name = newName;
saveService.QueueSave(new ModSaveGroup(option.Group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1));
}
/// <summary> Change the description of the given option. </summary>
@ -142,7 +142,7 @@ public class ModGroupEditor(
option.Description = newDescription;
saveService.QueueSave(new ModSaveGroup(option.Group, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.DisplayChange, option.Mod, option.Group, option, null, -1));
}
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
@ -151,10 +151,10 @@ public class ModGroupEditor(
if (subMod.Manipulations.Equals(manipulations))
return;
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
subMod.Manipulations.SetTo(manipulations);
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMetaChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
}
/// <summary> Set the file redirections for a given option. Replaces existing redirections. </summary>
@ -163,10 +163,10 @@ public class ModGroupEditor(
if (subMod.Files.SetEquals(replacements))
return;
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
subMod.Files.SetTo(replacements);
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionFilesChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
}
/// <summary> Forces a file save of the given container's group. </summary>
@ -181,7 +181,7 @@ public class ModGroupEditor(
if (oldCount != subMod.Files.Count)
{
saveService.QueueSave(new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionFilesAdded, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionFilesAdded, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
}
}
@ -191,10 +191,10 @@ public class ModGroupEditor(
if (subMod.FileSwaps.SetEquals(swaps))
return;
communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PrepareChange, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
subMod.FileSwaps.SetTo(swaps);
saveService.Save(saveType, new ModSaveGroup(subMod, config.ReplaceNonAsciiOnImport));
communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionSwapsChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1);
communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionSwapsChanged, (Mod)subMod.Mod, subMod.Group, null, subMod, -1));
}
/// <summary> Verify that a new option group name is unique in this mod. </summary>

View file

@ -1,5 +1,5 @@
using Luna;
using Luna.Files;
using Penumbra.Communication;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
@ -28,7 +28,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
var group = CreateGroup(mod, newName, maxPriority);
mod.Groups.Add(group);
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, group, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupAdded, mod, group, null, null, -1));
return group;
}
@ -57,7 +57,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
return null;
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, group.Mod, group, option, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionAdded, group.Mod, group, option, null, -1));
return option;
}
@ -85,7 +85,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
return null;
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, group.Mod, group, clonedOption, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionAdded, group.Mod, group, clonedOption, null, -1));
return clonedOption;
}
@ -95,10 +95,10 @@ public abstract class ModOptionEditor<TGroup, TOption>(
var mod = option.Mod;
var group = option.Group;
var optionIdx = option.GetIndex();
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, option, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PrepareChange, mod, group, option, null, -1));
RemoveOption((TGroup)group, optionIdx);
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionDeleted, mod, group, null, null, optionIdx);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionDeleted, mod, group, null, null, optionIdx));
}
/// <summary> Move an option inside the given option group. </summary>
@ -110,7 +110,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
return;
SaveService.QueueSave(new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMoved, group.Mod, group, option, null, idx);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.OptionMoved, group.Mod, group, option, null, idx));
}
protected abstract TGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync);

View file

@ -1,5 +1,6 @@
using Luna.Files;
using Luna;
using OtterGui.Filesystem;
using Penumbra.Communication;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
@ -16,7 +17,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
var singleGroup = group.ConvertToSingle();
group.Mod.Groups[idx] = singleGroup;
SaveService.QueueSave(new ModSaveGroup(singleGroup, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, singleGroup.Mod, singleGroup, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupTypeChanged, singleGroup.Mod, singleGroup, null, null, -1));
}
/// <summary> Change the internal priority of the given option. </summary>
@ -27,7 +28,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
option.Priority = newPriority;
SaveService.QueueSave(new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PriorityChanged, option.Mod, option.Group, option, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.PriorityChanged, option.Mod, option.Group, option, null, -1));
}
protected override MultiModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
@ -74,7 +75,7 @@ public sealed class MultiModGroupEditor(CommunicatorService communicator, SaveSe
protected override bool MoveOption(MultiModGroup group, int optionIdxFrom, int optionIdxTo)
{
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo))
return false;
group.DefaultSettings = group.DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);

View file

@ -1,5 +1,6 @@
using Luna.Files;
using Luna;
using OtterGui.Filesystem;
using Penumbra.Communication;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Settings;
using Penumbra.Mods.SubMods;
@ -8,7 +9,7 @@ using Penumbra.Services;
namespace Penumbra.Mods.Manager.OptionEditor;
public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveService saveService, Configuration config)
: ModOptionEditor<SingleModGroup, SingleSubMod>(communicator, saveService, config), Luna.IService
: ModOptionEditor<SingleModGroup, SingleSubMod>(communicator, saveService, config), IService
{
public void ChangeToMulti(SingleModGroup group)
{
@ -16,7 +17,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS
var multiGroup = group.ConvertToMulti();
group.Mod.Groups[idx] = multiGroup;
SaveService.QueueSave(new ModSaveGroup(multiGroup, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupTypeChanged, multiGroup.Mod, multiGroup, null, null, -1);
Communicator.ModOptionChanged.Invoke(new ModOptionChanged.Arguments(ModOptionChangeType.GroupTypeChanged, multiGroup.Mod, multiGroup, null, null, -1));
}
protected override SingleModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
@ -47,7 +48,7 @@ public sealed class SingleModGroupEditor(CommunicatorService communicator, SaveS
protected override bool MoveOption(SingleModGroup group, int optionIdxFrom, int optionIdxTo)
{
if (!group.OptionData.Move(ref optionIdxFrom, ref optionIdxTo))
if (!Extensions.Move(group.OptionData, ref optionIdxFrom, ref optionIdxTo))
return false;
group.DefaultSettings = group.DefaultSettings.MoveSingle(optionIdxFrom, optionIdxTo);

View file

@ -1,5 +1,4 @@
using OtterGui.Classes;
using Penumbra.Api.Enums;
using Luna;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
@ -16,14 +15,14 @@ namespace Penumbra.Mods;
/// <item>Parameter is the new selected mod </item>
/// </list>
/// </summary>
public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
public class ModSelection : EventBase<ModSelection.Arguments, ModSelection.Priority>
{
private readonly ActiveCollections _collections;
private readonly EphemeralConfig _config;
private readonly CommunicatorService _communicator;
public ModSelection(CommunicatorService communicator, ModManager mods, ActiveCollections collections, EphemeralConfig config)
: base(nameof(ModSelection))
public ModSelection(Logger log, CommunicatorService communicator, ModManager mods, ActiveCollections collections, EphemeralConfig config)
: base(nameof(ModSelection), log)
{
_communicator = communicator;
_collections = collections;
@ -49,8 +48,8 @@ public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
var oldMod = Mod;
Mod = mod;
OnCollectionChange(CollectionType.Current, null, _collections.Current, string.Empty);
Invoke(oldMod, Mod);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Current, null, _collections.Current, string.Empty));
Invoke(new Arguments(oldMod, Mod));
_config.LastModPath = mod?.ModPath.Name ?? string.Empty;
_config.Save();
}
@ -62,21 +61,21 @@ public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
_communicator.ModSettingChanged.Unsubscribe(OnSettingChange);
}
private void OnCollectionChange(CollectionType type, ModCollection? oldCollection, ModCollection? newCollection, string _2)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (type is CollectionType.Current && oldCollection != newCollection)
if (arguments.Type is CollectionType.Current && arguments.OldCollection != arguments.NewCollection)
UpdateSettings();
}
private void OnSettingChange(ModCollection collection, ModSettingChange _1, Mod? mod, Setting _2, int _3, bool _4)
private void OnSettingChange(in ModSettingChanged.Arguments arguments)
{
if (collection == _collections.Current && mod == Mod)
if (arguments.Collection == _collections.Current && arguments.Mod == Mod)
UpdateSettings();
}
private void OnInheritanceChange(ModCollection collection, bool arg2)
private void OnInheritanceChange(in CollectionInheritanceChanged.Arguments arguments)
{
if (collection == _collections.Current)
if (arguments.Collection == _collections.Current)
UpdateSettings();
}
@ -105,4 +104,6 @@ public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
/// <seealso cref="Editor.ModMerger.OnSelectionChange"/>
ModMerger = 0,
}
public readonly record struct Arguments(Mod? OldSelection, Mod? NewSelection);
}

View file

@ -19,6 +19,7 @@ using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManage
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using Luna;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.Interop;
using Penumbra.Interop.Hooks;
@ -31,9 +32,9 @@ namespace Penumbra;
public class Penumbra : IDalamudPlugin
{
public static readonly OtterGui.Log.Logger Log = new();
public static MessageService Messager { get; private set; } = null!;
public static DynamisIpc Dynamis { get; private set; } = null!;
public static readonly OtterGui.Log.Logger Log = new();
public static MessageService Messager { get; private set; } = null!;
public static DynamisIpc Dynamis { get; private set; } = null!;
private readonly ValidityChecker _validityChecker;
private readonly ResidentResourceManager _residentResources;
@ -49,14 +50,14 @@ public class Penumbra : IDalamudPlugin
private PenumbraWindowSystem? _windowSystem;
private bool _disposed;
private readonly Luna.ServiceManager _services;
private readonly ServiceManager _services;
public Penumbra(IDalamudPluginInterface pluginInterface)
{
try
{
HookOverrides.Instance = HookOverrides.LoadFile(pluginInterface);
_services = StaticServiceManager.CreateProvider(this, pluginInterface, new("Penumbra"));
_services = StaticServiceManager.CreateProvider(this, pluginInterface, new Logger("Penumbra"));
// Invoke the IPC Penumbra.Launching method before any hooks or other services are created.
_services.GetService<IpcLaunchingProvider>();
Messager = _services.GetService<MessageService>();
@ -64,7 +65,7 @@ public class Penumbra : IDalamudPlugin
_validityChecker = _services.GetService<ValidityChecker>();
_services.EnsureRequiredServices();
_services.EnsureRequiredServices<OtterGui.Services.IRequiredService>();
var startup = _services.GetService<DalamudConfigService>()
.GetDalamudConfig(DalamudConfigService.WaitingForPluginsOption, out bool s)
? s.ToString()
@ -87,21 +88,21 @@ public class Penumbra : IDalamudPlugin
_services.GetService<ModCacheManager>(); // Initialize because not required anywhere else.
_collectionManager.Caches.CreateNecessaryCaches();
_services.GetService<PathResolver>();
_services.GetService<DalamudSubstitutionProvider>(); // Initialize before Interface.
foreach (var service in _services.GetServicesImplementing<Luna.IAwaitedService>())
foreach (var service in _services.GetServicesImplementing<IAwaitedService>())
service.Awaiter.Wait();
SetupInterface();
SetupApi();
_validityChecker.LogExceptions();
Log.Information(
$"Penumbra Version {_validityChecker.Version}, Commit #{_validityChecker.CommitHash} successfully Loaded from {pluginInterface.SourceRepository}.");
OtterTex.NativeDll.Initialize(pluginInterface.AssemblyLocation.DirectoryName);
Log.Information($"Loading native OtterTex assembly from {OtterTex.NativeDll.Directory}.");
if (_characterUtility.Ready)
_residentResources.Reload();
}
@ -117,15 +118,15 @@ public class Penumbra : IDalamudPlugin
{
_services.GetService<IpcProviders>();
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>();
_communicatorService.ChangedItemHover.Subscribe(it =>
_communicatorService.ChangedItemHover.Subscribe((in ChangedItemHover.Arguments args) =>
{
if (it is IdentifiedItem { Item.Id.IsItem: true })
if (args.Data is IdentifiedItem { Item.Id.IsItem: true })
ImGui.TextUnformatted("Left Click to create an item link in chat.");
}, ChangedItemHover.Priority.Link);
_communicatorService.ChangedItemClick.Subscribe((button, it) =>
_communicatorService.ChangedItemClick.Subscribe((in ChangedItemClick.Arguments args) =>
{
if (button == MouseButton.Left && it is IdentifiedItem item && itemSheet.GetRow(item.Item.ItemId.Id) is { } i)
if (args is { Button: MouseButton.Left, Data: IdentifiedItem item } && itemSheet.GetRow(item.Item.ItemId.Id) is { } i)
Messager.LinkItem(i);
}, ChangedItemClick.Priority.Link);
}
@ -169,7 +170,7 @@ public class Penumbra : IDalamudPlugin
}
_config.Save();
_communicatorService.EnabledChanged.Invoke(enabled);
_communicatorService.EnabledChanged.Invoke(new EnabledChanged.Arguments(enabled));
return true;
}

View file

@ -3,44 +3,8 @@ using Newtonsoft.Json.Linq;
namespace Penumbra.Services;
public class BackupService : IAsyncService
public sealed class BackupService(Logger log, FilenameService provider) : BaseBackupService<FilenameService>(log, provider)
{
private readonly Logger _logger;
private readonly DirectoryInfo _configDirectory;
private readonly IReadOnlyList<FileInfo> _fileNames;
/// <inheritdoc/>
public Task Awaiter { get; }
/// <inheritdoc/>
public bool Finished
=> Awaiter.IsCompletedSuccessfully;
/// <summary> Start a backup process on the collected files. </summary>
public BackupService(Logger logger, FilenameService fileNames)
{
_logger = logger;
_fileNames = PenumbraFiles(fileNames);
_configDirectory = new DirectoryInfo(fileNames.ConfigurationDirectory);
Awaiter = Task.Run(() => Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigurationDirectory), _fileNames));
}
/// <summary> Create a permanent backup with a given name for migrations. </summary>
public void CreateMigrationBackup(string name)
=> Backup.CreatePermanentBackup(_logger, _configDirectory, _fileNames, name);
/// <summary> Collect all relevant files for penumbra configuration. </summary>
private static IReadOnlyList<FileInfo> PenumbraFiles(FilenameService fileNames)
{
var list = fileNames.CollectionFiles.ToList();
list.AddRange(fileNames.LocalDataFiles);
list.Add(new FileInfo(fileNames.ConfigurationFile));
list.Add(new FileInfo(fileNames.FilesystemFile));
list.Add(new FileInfo(fileNames.ActiveCollectionsFile));
list.Add(new FileInfo(fileNames.PredefinedTagFile));
return list;
}
/// <summary> Try to parse a file to JObject and check backups if this does not succeed. </summary>
public static JObject? GetJObjectForFile(FilenameService fileNames, string fileName)
{

View file

@ -1,116 +1,85 @@
using OtterGui.Classes;
using OtterGui.Log;
using Luna;
using Penumbra.Communication;
namespace Penumbra.Services;
public class CommunicatorService : IDisposable, Luna.IService
public class CommunicatorService(ServiceManager services) : IService
{
public CommunicatorService(Logger logger)
{
EventWrapperBase.ChangeLogger(logger);
}
/// <inheritdoc cref="Communication.CollectionChange"/>
public readonly CollectionChange CollectionChange = new();
public readonly CollectionChange CollectionChange = services.GetService<CollectionChange>();
/// <inheritdoc cref="Communication.TemporaryGlobalModChange"/>
public readonly TemporaryGlobalModChange TemporaryGlobalModChange = new();
public readonly TemporaryGlobalModChange TemporaryGlobalModChange = services.GetService<TemporaryGlobalModChange>();
/// <inheritdoc cref="Communication.CreatingCharacterBase"/>
public readonly CreatingCharacterBase CreatingCharacterBase = new();
public readonly CreatingCharacterBase CreatingCharacterBase = services.GetService<CreatingCharacterBase>();
/// <inheritdoc cref="Communication.CreatedCharacterBase"/>
public readonly CreatedCharacterBase CreatedCharacterBase = new();
public readonly CreatedCharacterBase CreatedCharacterBase = services.GetService<CreatedCharacterBase>();
/// <inheritdoc cref="Communication.MtrlLoaded"/>
public readonly MtrlLoaded MtrlLoaded = new();
public readonly MtrlLoaded MtrlLoaded = services.GetService<MtrlLoaded>();
/// <inheritdoc cref="Communication.ModDataChanged"/>
public readonly ModDataChanged ModDataChanged = new();
public readonly ModDataChanged ModDataChanged = services.GetService<ModDataChanged>();
/// <inheritdoc cref="Communication.ModOptionChanged"/>
public readonly ModOptionChanged ModOptionChanged = new();
public readonly ModOptionChanged ModOptionChanged = services.GetService<ModOptionChanged>();
/// <inheritdoc cref="Communication.ModDiscoveryStarted"/>
public readonly ModDiscoveryStarted ModDiscoveryStarted = new();
public readonly ModDiscoveryStarted ModDiscoveryStarted = services.GetService<ModDiscoveryStarted>();
/// <inheritdoc cref="Communication.ModDiscoveryFinished"/>
public readonly ModDiscoveryFinished ModDiscoveryFinished = new();
public readonly ModDiscoveryFinished ModDiscoveryFinished = services.GetService<ModDiscoveryFinished>();
/// <inheritdoc cref="Communication.ModDirectoryChanged"/>
public readonly ModDirectoryChanged ModDirectoryChanged = new();
public readonly ModDirectoryChanged ModDirectoryChanged = services.GetService<ModDirectoryChanged>();
/// <inheritdoc cref="Communication.ModFileChanged"/>
public readonly ModFileChanged ModFileChanged = new();
public readonly ModFileChanged ModFileChanged = services.GetService<ModFileChanged>();
/// <inheritdoc cref="Communication.ModPathChanged"/>
public readonly ModPathChanged ModPathChanged = new();
public readonly ModPathChanged ModPathChanged = services.GetService<ModPathChanged>();
/// <inheritdoc cref="Communication.ModSettingChanged"/>
public readonly ModSettingChanged ModSettingChanged = new();
public readonly ModSettingChanged ModSettingChanged = services.GetService<ModSettingChanged>();
/// <inheritdoc cref="Communication.CollectionInheritanceChanged"/>
public readonly CollectionInheritanceChanged CollectionInheritanceChanged = new();
public readonly CollectionInheritanceChanged CollectionInheritanceChanged = services.GetService<CollectionInheritanceChanged>();
/// <inheritdoc cref="Communication.EnabledChanged"/>
public readonly EnabledChanged EnabledChanged = new();
public readonly EnabledChanged EnabledChanged = services.GetService<EnabledChanged>();
/// <inheritdoc cref="Communication.PreSettingsTabBarDraw"/>
public readonly PreSettingsTabBarDraw PreSettingsTabBarDraw = new();
public readonly PreSettingsTabBarDraw PreSettingsTabBarDraw = services.GetService<PreSettingsTabBarDraw>();
/// <inheritdoc cref="Communication.PreSettingsPanelDraw"/>
public readonly PreSettingsPanelDraw PreSettingsPanelDraw = new();
public readonly PreSettingsPanelDraw PreSettingsPanelDraw = services.GetService<PreSettingsPanelDraw>();
/// <inheritdoc cref="Communication.PostEnabledDraw"/>
public readonly PostEnabledDraw PostEnabledDraw = new();
public readonly PostEnabledDraw PostEnabledDraw = services.GetService<PostEnabledDraw>();
/// <inheritdoc cref="Communication.PostSettingsPanelDraw"/>
public readonly PostSettingsPanelDraw PostSettingsPanelDraw = new();
public readonly PostSettingsPanelDraw PostSettingsPanelDraw = services.GetService<PostSettingsPanelDraw>();
/// <inheritdoc cref="Communication.ChangedItemHover"/>
public readonly ChangedItemHover ChangedItemHover = new();
public readonly ChangedItemHover ChangedItemHover = services.GetService<ChangedItemHover>();
/// <inheritdoc cref="Communication.ChangedItemClick"/>
public readonly ChangedItemClick ChangedItemClick = new();
public readonly ChangedItemClick ChangedItemClick = services.GetService<ChangedItemClick>();
/// <inheritdoc cref="Communication.SelectTab"/>
public readonly SelectTab SelectTab = new();
public readonly SelectTab SelectTab = services.GetService<SelectTab>();
/// <inheritdoc cref="Communication.ResolvedFileChanged"/>
public readonly ResolvedFileChanged ResolvedFileChanged = new();
public readonly ResolvedFileChanged ResolvedFileChanged = services.GetService<ResolvedFileChanged>();
/// <inheritdoc cref="Communication.PcpCreation"/>
public readonly PcpCreation PcpCreation = new();
public readonly PcpCreation PcpCreation = services.GetService<PcpCreation>();
/// <inheritdoc cref="Communication.PcpParsing"/>
public readonly PcpParsing PcpParsing = new();
public readonly PcpParsing PcpParsing = services.GetService<PcpParsing>();
public void Dispose()
{
CollectionChange.Dispose();
TemporaryGlobalModChange.Dispose();
CreatingCharacterBase.Dispose();
CreatedCharacterBase.Dispose();
MtrlLoaded.Dispose();
ModDataChanged.Dispose();
ModOptionChanged.Dispose();
ModDiscoveryStarted.Dispose();
ModDiscoveryFinished.Dispose();
ModDirectoryChanged.Dispose();
ModPathChanged.Dispose();
ModSettingChanged.Dispose();
CollectionInheritanceChanged.Dispose();
EnabledChanged.Dispose();
PreSettingsTabBarDraw.Dispose();
PreSettingsPanelDraw.Dispose();
PostEnabledDraw.Dispose();
PostSettingsPanelDraw.Dispose();
ChangedItemHover.Dispose();
ChangedItemClick.Dispose();
SelectTab.Dispose();
ResolvedFileChanged.Dispose();
PcpCreation.Dispose();
PcpParsing.Dispose();
}
/// <inheritdoc cref="Communication.CharacterUtilityFinished"/>
public readonly CharacterUtilityFinished LoadingFinished = services.GetService<CharacterUtilityFinished>();
}

View file

@ -248,18 +248,18 @@ public sealed class CrashHandlerService : IDisposable, Luna.IService
}
}
private void OnCreatingCharacterBase(nint address, Guid collection, nint _1, nint _2, nint _3)
private void OnCreatingCharacterBase(in CreatingCharacterBase.Arguments arguments)
{
if (_eventWriter == null)
if (_eventWriter is null)
return;
try
{
var name = GetActorName(address);
var name = GetActorName(arguments.GameObject);
lock (_eventWriter)
{
_eventWriter?.CharacterBase.WriteLine(address, name.Span, collection);
_eventWriter?.CharacterBase.WriteLine(arguments.GameObject, name.Span, arguments.Collection.Identity.Id);
}
}
catch (Exception ex)

View file

@ -1,5 +1,5 @@
using Dalamud.Plugin;
using Luna.Files;
using Luna;
using Penumbra.Collections;
using Penumbra.Mods;
@ -80,4 +80,16 @@ public sealed class FilenameService(IDalamudPluginInterface pi) : BaseFilePathPr
/// <summary> Enumerate all group files for a given mod. </summary>
public IEnumerable<FileInfo> GetOptionGroupFiles(Mod mod)
=> mod.ModPath.EnumerateFiles("group_*.json");
/// <summary> Collect all relevant files for penumbra configuration. </summary>
public override List<FileInfo> GetBackupFiles()
{
var list = CollectionFiles.ToList();
list.AddRange(LocalDataFiles);
list.Add(new FileInfo(ConfigurationFile));
list.Add(new FileInfo(FilesystemFile));
list.Add(new FileInfo(ActiveCollectionsFile));
list.Add(new FileInfo(PredefinedTagFile));
return list;
}
}

View file

@ -86,18 +86,18 @@ public class PcpService : IApiService, IDisposable
_collections.Storage.RemoveCollection(collection);
}
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory, DirectoryInfo? newDirectory)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
if (type is not ModPathChangeType.Added || _config.PcpSettings.DisableHandling || newDirectory is null)
if (arguments.Type is not ModPathChangeType.Added || _config.PcpSettings.DisableHandling || arguments.NewDirectory is null)
return;
try
{
var file = Path.Combine(newDirectory.FullName, "character.json");
var file = Path.Combine(arguments.NewDirectory.FullName, "character.json");
if (!File.Exists(file))
{
// First version had collection.json, changed.
var oldFile = Path.Combine(newDirectory.FullName, "collection.json");
var oldFile = Path.Combine(arguments.NewDirectory.FullName, "collection.json");
if (File.Exists(oldFile))
{
Penumbra.Log.Information("[PCPService] Renaming old PCP file from collection.json to character.json.");
@ -107,7 +107,7 @@ public class PcpService : IApiService, IDisposable
return;
}
Penumbra.Log.Information($"[PCPService] Found a PCP file for {mod.Name}, applying.");
Penumbra.Log.Information($"[PCPService] Found a PCP file for {arguments.Mod.Name}, applying.");
var text = File.ReadAllText(file);
var jObj = JObject.Parse(text);
var collection = ModCollection.Empty;
@ -121,7 +121,7 @@ public class PcpService : IApiService, IDisposable
if (_collections.Storage.AddCollection(name, null))
{
collection = _collections.Storage[^1];
_collections.Editor.SetModState(collection, mod, true);
_collections.Editor.SetModState(collection, arguments.Mod, true);
// Assign collection.
if (_config.PcpSettings.AssignCollection)
@ -134,7 +134,7 @@ public class PcpService : IApiService, IDisposable
}
// Move to folder.
if (_fileSystem.TryGetValue(mod, out var leaf))
if (_fileSystem.TryGetValue(arguments.Mod, out var leaf))
{
try
{
@ -149,11 +149,11 @@ public class PcpService : IApiService, IDisposable
// Invoke IPC.
if (_config.PcpSettings.AllowIpc)
_communicator.PcpParsing.Invoke(jObj, mod.Identifier, collection.Identity.Id);
_communicator.PcpParsing.Invoke(new PcpParsing.Arguments(jObj, arguments.Mod, collection));
}
catch (Exception ex)
{
Penumbra.Log.Error($"Error reading the character.json file from {mod.Identifier}:\n{ex}");
Penumbra.Log.Error($"Error reading the character.json file from {arguments.Mod.Identifier}:\n{ex}");
}
}
@ -220,7 +220,7 @@ public class PcpService : IApiService, IDisposable
if (note.Length > 0)
cancel.ThrowIfCancellationRequested();
if (_config.PcpSettings.AllowIpc)
await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(jObj, index.Index, directory.FullName));
await _framework.Framework.RunOnFrameworkThread(() => _communicator.PcpCreation.Invoke(new PcpCreation.Arguments(jObj, index.Index, directory.FullName)));
var filePath = Path.Combine(directory.FullName, "character.json");
await using var file = File.Open(filePath, File.Exists(filePath) ? FileMode.Truncate : FileMode.CreateNew);
await using var stream = new StreamWriter(file);

View file

@ -1,5 +1,4 @@
using Luna;
using Luna.Files;
using Penumbra.Mods;
using Penumbra.Mods.Groups;

View file

@ -8,6 +8,7 @@ using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.GameData.Files;
using Penumbra.Mods.Editor;
@ -216,7 +217,7 @@ public class FileEditor<T>(
{
compactor.WriteAllBytes(_currentPath!.File.FullName, _currentFile!.Write());
if (owner.Mod != null)
communicator.ModFileChanged.Invoke(owner.Mod, _currentPath);
communicator.ModFileChanged.Invoke(new ModFileChanged.Arguments(owner.Mod, _currentPath));
_changed = false;
}

View file

@ -6,7 +6,6 @@ using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.GameData.Data;
@ -28,7 +27,7 @@ using Penumbra.UI.ModsTab;
namespace Penumbra.UI.AdvancedWindow;
public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
public class ItemSwapTab : IDisposable, ITab, IUiService
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
@ -141,11 +140,16 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
{
var list = data.ByType[type];
var enumerable = selector?.Selected is { } mod && mod.ChangedItems.Values.Any(o => o is IdentifiedItem i && i.Item.Type == type)
? list.Select(i => (i, mod.ChangedItems.ContainsKey(i.Name), collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>()))
? list.Select(i => (i, mod.ChangedItems.ContainsKey(i.Name),
collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>()))
.OrderByDescending(p => p.Item2).ThenByDescending(p => p.Item3.Count)
: selector is null
? list.Select(i => (i, false, collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>())).OrderBy(p => p.Item3.Count)
: list.Select(i => (i, false, collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>())).OrderByDescending(p => p.Item3.Count);
? list.Select(i => (i, false,
collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>()))
.OrderBy(p => p.Item3.Count)
: list.Select(i => (i, false,
collections.Current.ChangedItems.TryGetValue(i.Name, out var m) ? m.Item1 : new SingleArray<IMod>()))
.OrderByDescending(p => p.Item3.Count);
return enumerable.ToList();
}, MouseWheelType.None, Penumbra.Log)
{
@ -526,7 +530,7 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
}
ImGui.TableNextColumn();
_dirty |= selector.Draw("##itemSource", selector.CurrentSelection.Item.Name ?? string.Empty, string.Empty,
_dirty |= selector.Draw("##itemSource", selector.CurrentSelection.Item.Name, string.Empty,
InputWidth * 2 * UiHelpers.Scale,
ImGui.GetTextLineHeightWithSpacing());
@ -638,19 +642,6 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
DrawGenderInput();
}
private void DrawFaceSwap()
{
using var disabled = ImRaii.Disabled();
using var tab = DrawTab(SwapType.Face);
if (!tab)
return;
using var table = ImRaii.Table("##settings", 2, ImGuiTableFlags.SizingFixedFit);
DrawTargetIdInput("Take this Face Type");
DrawSourceIdInput();
DrawGenderInput();
}
private void DrawTailSwap()
{
using var tab = DrawTab(SwapType.Tail);
@ -686,7 +677,7 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(InputWidth * UiHelpers.Scale);
if (ImGui.InputInt("##targetId", ref _targetId, 0, 0))
if (ImGui.InputInt("##targetId", ref _targetId))
_targetId = Math.Clamp(_targetId, 0, byte.MaxValue);
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
@ -700,7 +691,7 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(InputWidth * UiHelpers.Scale);
if (ImGui.InputInt("##sourceId", ref _sourceId, 0, 0))
if (ImGui.InputInt("##sourceId", ref _sourceId))
_sourceId = Math.Clamp(_sourceId, 0, byte.MaxValue);
_dirty |= ImGui.IsItemDeactivatedAfterEdit();
@ -713,7 +704,7 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
ImGui.TextUnformatted(text);
ImGui.TableNextColumn();
_dirty |= Combos.Gender("##Gender", _currentGender, out _currentGender, InputWidth);
_dirty |= Combos.Gender("##Gender", _currentGender, out _currentGender);
if (drawRace == 1)
{
ImGui.SameLine();
@ -725,12 +716,11 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
if (_currentRace is not ModelRace.Miqote and not ModelRace.AuRa and not ModelRace.Hrothgar)
_currentRace = ModelRace.Miqote;
_dirty |= ImGuiUtil.GenericEnumCombo("##Race", InputWidth, _currentRace, out _currentRace, new[]
{
_dirty |= ImGuiUtil.GenericEnumCombo("##Race", InputWidth, _currentRace, out _currentRace, [
ModelRace.Miqote,
ModelRace.AuRa,
ModelRace.Hrothgar,
},
],
RaceEnumExtensions.ToName);
}
}
@ -767,38 +757,40 @@ public class ItemSwapTab : IDisposable, ITab, Luna.IUiService
DrawSwap(child);
}
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection,
ModCollection? newCollection, string _)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (collectionType is not CollectionType.Current || _mod == null || newCollection == null)
if (arguments.Type is not CollectionType.Current || _mod is null || arguments.NewCollection is null)
return;
UpdateMod(_mod, _mod.Index < newCollection.Settings.Count ? newCollection.GetInheritedSettings(_mod.Index).Settings : null);
UpdateMod(_mod,
_mod.Index < arguments.NewCollection.Settings.Count ? arguments.NewCollection.GetInheritedSettings(_mod.Index).Settings : null);
}
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool inherited)
private void OnSettingChange(in ModSettingChanged.Arguments arguments)
{
if (collection != _collectionManager.Active.Current || mod != _mod || type is ModSettingChange.TemporarySetting)
if (arguments.Collection != _collectionManager.Active.Current
|| arguments.Mod != _mod
|| arguments.Type is ModSettingChange.TemporarySetting)
return;
_swapData.LoadMod(_mod, _modSettings);
_dirty = true;
}
private void OnInheritanceChange(ModCollection collection, bool _)
private void OnInheritanceChange(in CollectionInheritanceChanged.Arguments arguments)
{
if (collection != _collectionManager.Active.Current || _mod == null)
if (arguments.Collection != _collectionManager.Active.Current || _mod == null)
return;
UpdateMod(_mod, collection.GetInheritedSettings(_mod.Index).Settings);
UpdateMod(_mod, arguments.Collection.GetInheritedSettings(_mod.Index).Settings);
_swapData.LoadMod(_mod, _modSettings);
_dirty = true;
}
private void OnModOptionChange(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int fromIdx)
private void OnModOptionChange(in ModOptionChanged.Arguments arguments)
{
if (type is ModOptionChangeType.PrepareChange or ModOptionChangeType.GroupAdded or ModOptionChangeType.OptionAdded || mod != _mod)
if (arguments.Type is ModOptionChangeType.PrepareChange or ModOptionChangeType.GroupAdded or ModOptionChangeType.OptionAdded
|| arguments.Mod != _mod)
return;
_swapData.LoadMod(_mod, _modSettings);

View file

@ -1,9 +1,9 @@
using Dalamud.Bindings.ImGui;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Hooks.Objects;
using Penumbra.Interop.MaterialPreview;
using Penumbra.Services;
using Penumbra.UI.Classes;
@ -91,12 +91,12 @@ public partial class MtrlTab
_colorTablePreviewers.Clear();
}
private unsafe void UnbindFromDrawObjectMaterialInstances(CharacterBase* characterBase)
private unsafe void UnbindFromDrawObjectMaterialInstances(in CharacterBaseDestructor.Arguments arguments)
{
for (var i = _materialPreviewers.Count; i-- > 0;)
{
var previewer = _materialPreviewers[i];
if (previewer.DrawObject != characterBase)
if (previewer.DrawObject != arguments.CharacterBase)
continue;
previewer.Dispose();
@ -106,7 +106,7 @@ public partial class MtrlTab
for (var i = _colorTablePreviewers.Count; i-- > 0;)
{
var previewer = _colorTablePreviewers[i];
if (previewer.DrawObject != characterBase)
if (previewer.DrawObject != arguments.CharacterBase)
continue;
previewer.Dispose();

View file

@ -3,6 +3,7 @@ using Luna;
using OtterGui;
using OtterGui.Raii;
using OtterTex;
using Penumbra.Communication;
using Penumbra.Import.Textures;
using Penumbra.Mods;
using Penumbra.UI.Classes;
@ -229,7 +230,7 @@ public partial class ModEditWindow
out var registry))
return;
_communicator.ModFileChanged.Invoke(mod, registry);
_communicator.ModFileChanged.Invoke(new ModFileChanged.Arguments(mod, registry));
}
private void OpenSaveAsDialog(string defaultExtension)

View file

@ -681,12 +681,12 @@ public partial class ModEditWindow : Window, IDisposable, Luna.IUiService
_center.Dispose();
}
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
private void OnModPathChange(in ModPathChanged.Arguments arguments)
{
if (type is not (ModPathChangeType.Reloaded or ModPathChangeType.Moved) || mod != Mod)
if (arguments.Type is not (ModPathChangeType.Reloaded or ModPathChangeType.Moved) || arguments.Mod != Mod)
return;
Mod = null;
ChangeMod(mod);
ChangeMod(arguments.Mod);
}
}

View file

@ -10,6 +10,7 @@ using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.GameData.Files;
using Penumbra.GameData.Structs;
using Penumbra.Interop.ResourceTree;
@ -344,7 +345,7 @@ public class ResourceTreeViewer(
if (ImGui.IsItemClicked())
ImGui.SetClipboardText(resourceNode.FullPath.ToPath());
if (hasMod && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
communicator.SelectTab.Invoke(TabType.Mods, mod);
communicator.SelectTab.Invoke(new SelectTab.Arguments(TabType.Mods, mod));
ImGuiUtil.HoverTooltip(
$"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{(hasMod ? "\nControl + Right-Click to jump to mod." : string.Empty)}{GetAdditionalDataSuffix(resourceNode.AdditionalData)}");

View file

@ -10,6 +10,7 @@ using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.Services;
using Penumbra.UI.Classes;
@ -122,7 +123,7 @@ public class ChangedItemDrawer : IDisposable, Luna.IUiService
ret = ImGui.IsItemClicked(ImGuiMouseButton.Right) ? MouseButton.Right : ret;
ret = ImGui.IsItemClicked(ImGuiMouseButton.Middle) ? MouseButton.Middle : ret;
if (ret != MouseButton.None)
_communicator.ChangedItemClick.Invoke(ret, data);
_communicator.ChangedItemClick.Invoke(new ChangedItemClick.Arguments(ret, data));
if (!ImGui.IsItemHovered())
return;
@ -134,7 +135,7 @@ public class ChangedItemDrawer : IDisposable, Luna.IUiService
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3 * ImUtf8.GlobalScale);
ImGui.Separator();
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3 * ImUtf8.GlobalScale);
_communicator.ChangedItemHover.Invoke(data);
_communicator.ChangedItemHover.Invoke(new ChangedItemHover.Arguments(data));
}
/// <summary> Draw the model information, right-justified. </summary>

View file

@ -34,9 +34,9 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
_communicator.CollectionChange.Subscribe(OnCollectionChange, CollectionChange.Priority.CollectionSelector);
// Set items.
OnCollectionChange(CollectionType.Inactive, null, null, string.Empty);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Inactive, null, null, string.Empty));
// Set selection.
OnCollectionChange(CollectionType.Current, null, _active.Current, string.Empty);
OnCollectionChange(new CollectionChange.Arguments(CollectionType.Current, null, _active.Current, string.Empty));
}
protected override bool OnDelete(int idx)
@ -85,7 +85,7 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
if (source)
{
_dragging = Items[idx];
ImGui.SetDragDropPayload(PayloadString, null, 0);
ImGui.SetDragDropPayload(PayloadString, null);
ImGui.TextUnformatted($"Assigning {Name(_dragging)} to...");
}
@ -123,14 +123,14 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
SetCurrent(_active.Current);
}
private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? @new, string _3)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
switch (type)
switch (arguments.Type)
{
case CollectionType.Temporary: return;
case CollectionType.Current:
if (@new != null)
SetCurrent(@new);
if (arguments.NewCollection is not null)
SetCurrent(arguments.NewCollection);
SetFilterDirty();
return;
case CollectionType.Inactive:

View file

@ -135,9 +135,9 @@ public class IndividualAssignmentUi : IDisposable
_ready = true;
}
private void UpdateIdentifiers(CollectionType type, ModCollection? _1, ModCollection? _2, string _3)
private void UpdateIdentifiers(in CollectionChange.Arguments arguments)
{
if (type == CollectionType.Individual)
if (arguments.Type is CollectionType.Individual)
UpdateIdentifiersInternal();
}

View file

@ -140,18 +140,18 @@ public class FileDialogService : IDisposable, IUiService
}
/// <summary> Update the Root Directory link on changes. </summary>
private void OnModDirectoryChange(string directory, bool valid)
private void OnModDirectoryChange(in ModDirectoryChanged.Arguments arguments)
{
var idx = _manager.CustomSideBarItems.IndexOf(t => t.Name == "Root Directory");
if (idx >= 0)
_manager.CustomSideBarItems.RemoveAt(idx);
if (!valid)
if (!arguments.Valid)
return;
if (idx >= 0)
_manager.CustomSideBarItems.Insert(idx, ("Root Directory", directory, FontAwesomeIcon.Gamepad, 0));
_manager.CustomSideBarItems.Insert(idx, ("Root Directory", arguments.Directory, FontAwesomeIcon.Gamepad, 0));
else
_manager.CustomSideBarItems.Add(("Root Directory", directory, FontAwesomeIcon.Gamepad, 0));
_manager.CustomSideBarItems.Add(("Root Directory", arguments.Directory, FontAwesomeIcon.Gamepad, 0));
}
}

View file

@ -10,7 +10,6 @@ using OtterGui.FileSystem.Selector;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.Widget;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
@ -23,7 +22,7 @@ using MessageService = Penumbra.Services.MessageService;
namespace Penumbra.UI.ModsTab;
public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>, Luna.IUiService
public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>, IUiService
{
private readonly CommunicatorService _communicator;
private readonly Configuration _config;
@ -507,13 +506,13 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
#region Automatic cache update functions.
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool inherited)
private void OnSettingChange(in ModSettingChanged.Arguments arguments)
{
if (collection == _collectionManager.Active.Current)
if (arguments.Collection == _collectionManager.Active.Current)
SetFilterDirty();
}
private void OnModDataChange(ModDataChangeType type, Mod mod, string? oldName)
private void OnModDataChange(in ModDataChanged.Arguments arguments)
{
const ModDataChangeType relevantFlags =
ModDataChangeType.Name
@ -522,19 +521,19 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
| ModDataChangeType.LocalTags
| ModDataChangeType.Favorite
| ModDataChangeType.ImportDate;
if ((type & relevantFlags) != 0)
if ((arguments.Type & relevantFlags) is not 0)
SetFilterDirty();
}
private void OnInheritanceChange(ModCollection collection, bool _)
private void OnInheritanceChange(in CollectionInheritanceChanged.Arguments arguments)
{
if (collection == _collectionManager.Active.Current)
if (arguments.Collection == _collectionManager.Active.Current)
SetFilterDirty();
}
private void OnCollectionChange(CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string _)
private void OnCollectionChange(in CollectionChange.Arguments arguments)
{
if (collectionType is CollectionType.Current && oldCollection != newCollection)
if (arguments.Type is CollectionType.Current && arguments.OldCollection != arguments.NewCollection)
SetFilterDirty();
}
@ -636,7 +635,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
}
/// <summary> Apply the string filters. </summary>
private bool ApplyStringFilters(ModFileSystem.Leaf leaf, Mod mod)
private bool ApplyStringFilters(ModFileSystem.Leaf leaf, Mod _)
=> !_filter.IsVisible(leaf);
/// <summary> Only get the text color for a mod if no filters are set. </summary>

View file

@ -25,7 +25,7 @@ public class ModPanel : IDisposable, Luna.IUiService
_multiModPanel = multiModPanel;
_header = new ModPanelHeader(pi, communicator);
_selection.Subscribe(OnSelectionChange, ModSelection.Priority.ModPanel);
OnSelectionChange(null, _selection.Mod);
OnSelectionChange(new ModSelection.Arguments(null, _selection.Mod));
}
public void Draw()
@ -59,10 +59,10 @@ public class ModPanel : IDisposable, Luna.IUiService
private bool _valid;
private Mod _mod = null!;
private void OnSelectionChange(Mod? old, Mod? mod)
private void OnSelectionChange(in ModSelection.Arguments arguments)
{
_resetCursor = true;
if (mod == null || _selection.Mod == null)
if (arguments.NewSelection is null || _selection.Mod is null)
{
_editWindow.IsOpen = false;
_valid = false;
@ -70,9 +70,9 @@ public class ModPanel : IDisposable, Luna.IUiService
else
{
if (_editWindow.IsOpen)
_editWindow.ChangeMod(mod);
_editWindow.ChangeMod(arguments.NewSelection);
_valid = true;
_mod = mod;
_mod = arguments.NewSelection;
_header.ChangeMod(_mod);
_tabs.Settings.Reset();
_tabs.Edit.Reset();

View file

@ -47,7 +47,7 @@ public class ModPanelHeader : IDisposable
DrawSecondRow(offset);
}
_communicator.PreSettingsTabBarDraw.Invoke(_mod.Identifier, ImGui.GetItemRectSize().X, _nameWidth);
_communicator.PreSettingsTabBarDraw.Invoke(new PreSettingsTabBarDraw.Arguments(_mod, ImGui.GetItemRectSize().X, _nameWidth));
_lastPreSettingsHeight = ImGui.GetCursorPosY();
}
@ -260,10 +260,10 @@ public class ModPanelHeader : IDisposable
}
/// <summary> Just update the data when any relevant field changes. </summary>
private void OnModDataChange(ModDataChangeType changeType, Mod mod, string? _2)
private void OnModDataChange(in ModDataChanged.Arguments arguments)
{
const ModDataChangeType relevantChanges =
ModDataChangeType.Author | ModDataChangeType.Name | ModDataChangeType.Website | ModDataChangeType.Version;
_dirty = (changeType & relevantChanges) != 0;
_dirty = (arguments.Type & relevantChanges) is not 0;
}
}

View file

@ -4,6 +4,7 @@ using OtterGui.Text;
using OtterGui.Widgets;
using Penumbra.UI.Classes;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
@ -51,7 +52,7 @@ public class ModPanelSettingsTab(
DrawTemporaryWarning();
DrawInheritedWarning();
ImGui.Dummy(Vector2.Zero);
communicator.PreSettingsPanelDraw.Invoke(selection.Mod!.Identifier);
communicator.PreSettingsPanelDraw.Invoke(new PreSettingsPanelDraw.Arguments(selection.Mod!));
DrawEnabledInput();
tutorial.OpenTutorial(BasicTutorialSteps.EnablingMods);
ImGui.SameLine();
@ -60,11 +61,11 @@ public class ModPanelSettingsTab(
DrawRemoveSettings();
ImGui.TableNextColumn();
communicator.PostEnabledDraw.Invoke(selection.Mod!.Identifier);
communicator.PostEnabledDraw.Invoke(new PostEnabledDraw.Arguments(selection.Mod!));
modGroupDrawer.Draw(selection.Mod!, selection.Settings, selection.TemporarySettings);
UiHelpers.DefaultLineSpace();
communicator.PostSettingsPanelDraw.Invoke(selection.Mod!.Identifier);
communicator.PostSettingsPanelDraw.Invoke(new PostSettingsPanelDraw.Arguments(selection.Mod!));
}
/// <summary> Draw a big tinted bar if the current setting is temporary. </summary>
@ -195,7 +196,7 @@ public class ModPanelSettingsTab(
{
(true, false) => ImUtf8.ButtonEx("Inherit Settings"u8,
"Remove current settings from this collection so that it can inherit them.\n"u8
+ "If no inherited collection has settings for this mod, it will be disabled."u8, default, false),
+ "If no inherited collection has settings for this mod, it will be disabled."u8),
(false, false) => ImUtf8.ButtonEx("Inherit Settings"u8,
$"Remove current settings from this collection so that it can inherit them.\nHold {config.DeleteModModifier} to inherit.",
default, true),

Some files were not shown because too many files have changed in this diff Show more