Rework around a saner import popup and decouple logic from interface.

This commit is contained in:
Ottermandias 2023-04-08 22:29:43 +02:00
parent bfb630d317
commit bbfc9a0a6f
16 changed files with 478 additions and 411 deletions

@ -1 +1 @@
Subproject commit 13ade28e21bed02e16bbd081b2e6567382cf69bd
Subproject commit d7e8c8c44d92bd50764394af51ac24cb07f362dc

View file

@ -1,22 +0,0 @@
using Dalamud.Game.ClientState.Keys;
using OtterGui.Filesystem;
using OtterGui.FileSystem.Selector;
using Penumbra.Mods;
using Penumbra.UI.Classes;
using Penumbra.UI.ModsTab;
using System;
using System.IO;
using System.Linq;
namespace Penumbra.Api {
public class ExternalModImporter {
private static ModFileSystemSelector modFileSystemSelectorInstance;
public static ModFileSystemSelector ModFileSystemSelectorInstance { get => modFileSystemSelectorInstance; set => modFileSystemSelectorInstance = value; }
public static void UnpackMod(string modPackagePath)
{
modFileSystemSelectorInstance.ImportStandaloneModPackage(modPackagePath);
}
}
}

View file

@ -12,12 +12,12 @@ public class HttpApi : IDisposable
private partial class Controller : WebApiController
{
// @formatter:off
[Route( HttpVerbs.Get, "/mods" )] public partial object? GetMods();
[Route( HttpVerbs.Post, "/redraw" )] public partial Task Redraw();
[Route( HttpVerbs.Post, "/redrawAll" )] public partial void RedrawAll();
[Route( HttpVerbs.Post, "/reloadmod" )] public partial Task ReloadMod();
[Route( HttpVerbs.Post, "/unpackmod" )] public partial Task UnpackMod();
[Route( HttpVerbs.Post, "/openwindow")] public partial Task OpenWindow();
[Route( HttpVerbs.Get, "/mods" )] public partial object? GetMods();
[Route( HttpVerbs.Post, "/redraw" )] public partial Task Redraw();
[Route( HttpVerbs.Post, "/redrawAll" )] public partial void RedrawAll();
[Route( HttpVerbs.Post, "/reloadmod" )] public partial Task ReloadMod();
[Route( HttpVerbs.Post, "/installmod" )] public partial Task InstallMod();
[Route( HttpVerbs.Post, "/openwindow" )] public partial void OpenWindow();
// @formatter:on
}
@ -103,16 +103,15 @@ public class HttpApi : IDisposable
_api.ReloadMod(data.Path, data.Name);
}
public async partial Task UnpackMod()
public async partial Task InstallMod()
{
var data = await HttpContext.GetRequestDataAsync<ModUnpackData>();
Penumbra.Log.Debug($"[HTTP] {nameof(UnpackMod)} triggered with {data}.");
// Unpack the mod package if its valid.
var data = await HttpContext.GetRequestDataAsync<ModInstallData>();
Penumbra.Log.Debug($"[HTTP] {nameof(InstallMod)} triggered with {data}.");
if (data.Path.Length != 0)
_api.UnpackMod(data.Path);
_api.InstallMod(data.Path);
}
public async partial Task OpenWindow()
public partial void OpenWindow()
{
Penumbra.Log.Debug($"[HTTP] {nameof(OpenWindow)} triggered.");
_api.OpenMainWindow(TabType.Mods, string.Empty, string.Empty);
@ -125,9 +124,9 @@ public class HttpApi : IDisposable
{ }
}
private record ModUnpackData(string Path)
private record ModInstallData(string Path)
{
public ModUnpackData()
public ModInstallData()
: this(string.Empty)
{ }
}

View file

@ -18,8 +18,8 @@ using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.UI;
using Penumbra.Collections.Manager;
using Penumbra.Collections.Manager;
namespace Penumbra.Api;
public class IpcTester : IDisposable
@ -847,13 +847,15 @@ public class IpcTester : IDisposable
{
private readonly DalamudPluginInterface _pi;
private string _modDirectory = string.Empty;
private string _modName = string.Empty;
private string _pathInput = string.Empty;
private string _modDirectory = string.Empty;
private string _modName = string.Empty;
private string _pathInput = string.Empty;
private string _newInstallPath = string.Empty;
private PenumbraApiEc _lastReloadEc;
private PenumbraApiEc _lastAddEc;
private PenumbraApiEc _lastDeleteEc;
private PenumbraApiEc _lastSetPathEc;
private PenumbraApiEc _lastInstallEc;
private IList<(string, string)> _mods = new List<(string, string)>();
public readonly EventSubscriber<string> DeleteSubscriber;
@ -895,9 +897,10 @@ public class IpcTester : IDisposable
if (!_)
return;
ImGui.InputTextWithHint("##modDir", "Mod Directory Name...", ref _modDirectory, 100);
ImGui.InputTextWithHint("##modName", "Mod Name...", ref _modName, 100);
ImGui.InputTextWithHint("##path", "New Path...", ref _pathInput, 100);
ImGui.InputTextWithHint("##install", "Install File Path...", ref _newInstallPath, 100);
ImGui.InputTextWithHint("##modDir", "Mod Directory Name...", ref _modDirectory, 100);
ImGui.InputTextWithHint("##modName", "Mod Name...", ref _modName, 100);
ImGui.InputTextWithHint("##path", "New Path...", ref _pathInput, 100);
using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit);
if (!table)
return;
@ -916,6 +919,13 @@ public class IpcTester : IDisposable
ImGui.SameLine();
ImGui.TextUnformatted(_lastReloadEc.ToString());
DrawIntro(Ipc.InstallMod.Label, "Install Mod");
if (ImGui.Button("Install"))
_lastInstallEc = Ipc.InstallMod.Subscriber(_pi).Invoke(_newInstallPath);
ImGui.SameLine();
ImGui.TextUnformatted(_lastInstallEc.ToString());
DrawIntro(Ipc.AddMod.Label, "Add Mod");
if (ImGui.Button("Add"))
_lastAddEc = Ipc.AddMod.Subscriber(_pi).Invoke(_modDirectory);
@ -1140,7 +1150,7 @@ public class IpcTester : IDisposable
private class Temporary
{
private readonly DalamudPluginInterface _pi;
private readonly ModManager _modManager;
private readonly ModManager _modManager;
public Temporary(DalamudPluginInterface pi, ModManager modManager)
{

View file

@ -109,23 +109,25 @@ public class PenumbraApi : IDisposable, IPenumbraApi
private ActorService _actors;
private CollectionResolver _collectionResolver;
private CutsceneService _cutsceneService;
private ModImportManager _modImportManager;
public unsafe PenumbraApi(CommunicatorService communicator, Penumbra penumbra, ModManager modManager, ResourceLoader resourceLoader,
Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections,
TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService)
TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, ModImportManager modImportManager)
{
_communicator = communicator;
_penumbra = penumbra;
_modManager = modManager;
_resourceLoader = resourceLoader;
_config = config;
_collectionManager = collectionManager;
_dalamud = dalamud;
_tempCollections = tempCollections;
_tempMods = tempMods;
_actors = actors;
_collectionResolver = collectionResolver;
_cutsceneService = cutsceneService;
_communicator = communicator;
_penumbra = penumbra;
_modManager = modManager;
_resourceLoader = resourceLoader;
_config = config;
_collectionManager = collectionManager;
_dalamud = dalamud;
_tempCollections = tempCollections;
_tempMods = tempMods;
_actors = actors;
_collectionResolver = collectionResolver;
_cutsceneService = cutsceneService;
_modImportManager = modImportManager;
_lumina = (Lumina.GameData?)_dalamud.GameData.GetType()
.GetField("gameData", BindingFlags.Instance | BindingFlags.NonPublic)
@ -602,11 +604,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return PenumbraApiEc.Success;
}
public PenumbraApiEc UnpackMod(string modFilePackagePath)
public PenumbraApiEc InstallMod(string modFilePackagePath)
{
if (File.Exists(modFilePackagePath))
{
ExternalModImporter.UnpackMod(modFilePackagePath);
_modImportManager.AddUnpack(modFilePackagePath);
return PenumbraApiEc.Success;
}
else

View file

@ -10,7 +10,7 @@ using Penumbra.Mods.Manager;
namespace Penumbra.Api;
using CurrentSettings = ValueTuple< PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)? >;
using CurrentSettings = ValueTuple<PenumbraApiEc, (bool, int, IDictionary<string, IList<string>>, bool)?>;
public class PenumbraIpcProviders : IDisposable
{
@ -18,210 +18,215 @@ public class PenumbraIpcProviders : IDisposable
internal readonly IpcTester Tester;
// Plugin State
internal readonly EventProvider Initialized;
internal readonly EventProvider Disposed;
internal readonly FuncProvider< int > ApiVersion;
internal readonly FuncProvider< (int Breaking, int Features) > ApiVersions;
internal readonly FuncProvider< bool > GetEnabledState;
internal readonly EventProvider< bool > EnabledChange;
internal readonly EventProvider Initialized;
internal readonly EventProvider Disposed;
internal readonly FuncProvider<int> ApiVersion;
internal readonly FuncProvider<(int Breaking, int Features)> ApiVersions;
internal readonly FuncProvider<bool> GetEnabledState;
internal readonly EventProvider<bool> EnabledChange;
// Configuration
internal readonly FuncProvider< string > GetModDirectory;
internal readonly FuncProvider< string > GetConfiguration;
internal readonly EventProvider< string, bool > ModDirectoryChanged;
internal readonly FuncProvider<string> GetModDirectory;
internal readonly FuncProvider<string> GetConfiguration;
internal readonly EventProvider<string, bool> ModDirectoryChanged;
// UI
internal readonly EventProvider< string > PreSettingsDraw;
internal readonly EventProvider< string > PostSettingsDraw;
internal readonly EventProvider< ChangedItemType, uint > ChangedItemTooltip;
internal readonly EventProvider< MouseButton, ChangedItemType, uint > ChangedItemClick;
internal readonly FuncProvider< TabType, string, string, PenumbraApiEc > OpenMainWindow;
internal readonly ActionProvider CloseMainWindow;
internal readonly EventProvider<string> PreSettingsDraw;
internal readonly EventProvider<string> PostSettingsDraw;
internal readonly EventProvider<ChangedItemType, uint> ChangedItemTooltip;
internal readonly EventProvider<MouseButton, ChangedItemType, uint> ChangedItemClick;
internal readonly FuncProvider<TabType, string, string, PenumbraApiEc> OpenMainWindow;
internal readonly ActionProvider CloseMainWindow;
// Redrawing
internal readonly ActionProvider< RedrawType > RedrawAll;
internal readonly ActionProvider< GameObject, RedrawType > RedrawObject;
internal readonly ActionProvider< int, RedrawType > RedrawObjectByIndex;
internal readonly ActionProvider< string, RedrawType > RedrawObjectByName;
internal readonly EventProvider< nint, int > GameObjectRedrawn;
internal readonly ActionProvider<RedrawType> RedrawAll;
internal readonly ActionProvider<GameObject, RedrawType> RedrawObject;
internal readonly ActionProvider<int, RedrawType> RedrawObjectByIndex;
internal readonly ActionProvider<string, RedrawType> RedrawObjectByName;
internal readonly EventProvider<nint, int> GameObjectRedrawn;
// Game State
internal readonly FuncProvider< nint, (nint, string) > GetDrawObjectInfo;
internal readonly FuncProvider< int, int > GetCutsceneParentIndex;
internal readonly EventProvider< nint, string, nint, nint, nint > CreatingCharacterBase;
internal readonly EventProvider< nint, string, nint > CreatedCharacterBase;
internal readonly EventProvider< nint, string, string > GameObjectResourcePathResolved;
internal readonly FuncProvider<nint, (nint, string)> GetDrawObjectInfo;
internal readonly FuncProvider<int, int> GetCutsceneParentIndex;
internal readonly EventProvider<nint, string, nint, nint, nint> CreatingCharacterBase;
internal readonly EventProvider<nint, string, nint> CreatedCharacterBase;
internal readonly EventProvider<nint, string, string> GameObjectResourcePathResolved;
// Resolve
internal readonly FuncProvider< string, string > ResolveDefaultPath;
internal readonly FuncProvider< string, string > ResolveInterfacePath;
internal readonly FuncProvider< string, string > ResolvePlayerPath;
internal readonly FuncProvider< string, int, string > ResolveGameObjectPath;
internal readonly FuncProvider< string, string, string > ResolveCharacterPath;
internal readonly FuncProvider< string, string, string[] > ReverseResolvePath;
internal readonly FuncProvider< string, int, string[] > ReverseResolveGameObjectPath;
internal readonly FuncProvider< string, string[] > ReverseResolvePlayerPath;
internal readonly FuncProvider< string[], string[], (string[], string[][]) > ResolvePlayerPaths;
internal readonly FuncProvider<string, string> ResolveDefaultPath;
internal readonly FuncProvider<string, string> ResolveInterfacePath;
internal readonly FuncProvider<string, string> ResolvePlayerPath;
internal readonly FuncProvider<string, int, string> ResolveGameObjectPath;
internal readonly FuncProvider<string, string, string> ResolveCharacterPath;
internal readonly FuncProvider<string, string, string[]> ReverseResolvePath;
internal readonly FuncProvider<string, int, string[]> ReverseResolveGameObjectPath;
internal readonly FuncProvider<string, string[]> ReverseResolvePlayerPath;
internal readonly FuncProvider<string[], string[], (string[], string[][])> ResolvePlayerPaths;
// Collections
internal readonly FuncProvider< IList< string > > GetCollections;
internal readonly FuncProvider< string > GetCurrentCollectionName;
internal readonly FuncProvider< string > GetDefaultCollectionName;
internal readonly FuncProvider< string > GetInterfaceCollectionName;
internal readonly FuncProvider< string, (string, bool) > GetCharacterCollectionName;
internal readonly FuncProvider< ApiCollectionType, string > GetCollectionForType;
internal readonly FuncProvider< ApiCollectionType, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForType;
internal readonly FuncProvider< int, (bool, bool, string) > GetCollectionForObject;
internal readonly FuncProvider< int, string, bool, bool, (PenumbraApiEc, string) > SetCollectionForObject;
internal readonly FuncProvider< string, IReadOnlyDictionary< string, object? > > GetChangedItems;
internal readonly FuncProvider<IList<string>> GetCollections;
internal readonly FuncProvider<string> GetCurrentCollectionName;
internal readonly FuncProvider<string> GetDefaultCollectionName;
internal readonly FuncProvider<string> GetInterfaceCollectionName;
internal readonly FuncProvider<string, (string, bool)> GetCharacterCollectionName;
internal readonly FuncProvider<ApiCollectionType, string> GetCollectionForType;
internal readonly FuncProvider<ApiCollectionType, string, bool, bool, (PenumbraApiEc, string)> SetCollectionForType;
internal readonly FuncProvider<int, (bool, bool, string)> GetCollectionForObject;
internal readonly FuncProvider<int, string, bool, bool, (PenumbraApiEc, string)> SetCollectionForObject;
internal readonly FuncProvider<string, IReadOnlyDictionary<string, object?>> GetChangedItems;
// Meta
internal readonly FuncProvider< string > GetPlayerMetaManipulations;
internal readonly FuncProvider< string, string > GetMetaManipulations;
internal readonly FuncProvider< int, string > GetGameObjectMetaManipulations;
internal readonly FuncProvider<string> GetPlayerMetaManipulations;
internal readonly FuncProvider<string, string> GetMetaManipulations;
internal readonly FuncProvider<int, string> GetGameObjectMetaManipulations;
// Mods
internal readonly FuncProvider< IList< (string, string) > > GetMods;
internal readonly FuncProvider< string, string, PenumbraApiEc > ReloadMod;
internal readonly FuncProvider< string, PenumbraApiEc > AddMod;
internal readonly FuncProvider< string, string, PenumbraApiEc > DeleteMod;
internal readonly FuncProvider< string, string, (PenumbraApiEc, string, bool) > GetModPath;
internal readonly FuncProvider< string, string, string, PenumbraApiEc > SetModPath;
internal readonly EventProvider< string > ModDeleted;
internal readonly EventProvider< string > ModAdded;
internal readonly EventProvider< string, string > ModMoved;
internal readonly FuncProvider<IList<(string, string)>> GetMods;
internal readonly FuncProvider<string, string, PenumbraApiEc> ReloadMod;
internal readonly FuncProvider<string, PenumbraApiEc> InstallMod;
internal readonly FuncProvider<string, PenumbraApiEc> AddMod;
internal readonly FuncProvider<string, string, PenumbraApiEc> DeleteMod;
internal readonly FuncProvider<string, string, (PenumbraApiEc, string, bool)> GetModPath;
internal readonly FuncProvider<string, string, string, PenumbraApiEc> SetModPath;
internal readonly EventProvider<string> ModDeleted;
internal readonly EventProvider<string> ModAdded;
internal readonly EventProvider<string, string> ModMoved;
// ModSettings
internal readonly FuncProvider< string, string, IDictionary< string, (IList< string >, GroupType) >? > GetAvailableModSettings;
internal readonly FuncProvider< string, string, string, bool, CurrentSettings > GetCurrentModSettings;
internal readonly FuncProvider< string, string, string, bool, PenumbraApiEc > TryInheritMod;
internal readonly FuncProvider< string, string, string, bool, PenumbraApiEc > TrySetMod;
internal readonly FuncProvider< string, string, string, int, PenumbraApiEc > TrySetModPriority;
internal readonly FuncProvider< string, string, string, string, string, PenumbraApiEc > TrySetModSetting;
internal readonly FuncProvider< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc > TrySetModSettings;
internal readonly EventProvider< ModSettingChange, string, string, bool > ModSettingChanged;
internal readonly FuncProvider< string, string, string, PenumbraApiEc > CopyModSettings;
internal readonly FuncProvider<string, string, IDictionary<string, (IList<string>, GroupType)>?> GetAvailableModSettings;
internal readonly FuncProvider<string, string, string, bool, CurrentSettings> GetCurrentModSettings;
internal readonly FuncProvider<string, string, string, bool, PenumbraApiEc> TryInheritMod;
internal readonly FuncProvider<string, string, string, bool, PenumbraApiEc> TrySetMod;
internal readonly FuncProvider<string, string, string, int, PenumbraApiEc> TrySetModPriority;
internal readonly FuncProvider<string, string, string, string, string, PenumbraApiEc> TrySetModSetting;
internal readonly FuncProvider<string, string, string, string, IReadOnlyList<string>, PenumbraApiEc> TrySetModSettings;
internal readonly EventProvider<ModSettingChange, string, string, bool> ModSettingChanged;
internal readonly FuncProvider<string, string, string, PenumbraApiEc> CopyModSettings;
// Temporary
internal readonly FuncProvider< string, string, bool, (PenumbraApiEc, string) > CreateTemporaryCollection;
internal readonly FuncProvider< string, PenumbraApiEc > RemoveTemporaryCollection;
internal readonly FuncProvider< string, PenumbraApiEc > CreateNamedTemporaryCollection;
internal readonly FuncProvider< string, PenumbraApiEc > RemoveTemporaryCollectionByName;
internal readonly FuncProvider< string, int, bool, PenumbraApiEc > AssignTemporaryCollection;
internal readonly FuncProvider< string, Dictionary< string, string >, string, int, PenumbraApiEc > AddTemporaryModAll;
internal readonly FuncProvider< string, string, Dictionary< string, string >, string, int, PenumbraApiEc > AddTemporaryMod;
internal readonly FuncProvider< string, int, PenumbraApiEc > RemoveTemporaryModAll;
internal readonly FuncProvider< string, string, int, PenumbraApiEc > RemoveTemporaryMod;
internal readonly FuncProvider<string, string, bool, (PenumbraApiEc, string)> CreateTemporaryCollection;
internal readonly FuncProvider<string, PenumbraApiEc> RemoveTemporaryCollection;
internal readonly FuncProvider<string, PenumbraApiEc> CreateNamedTemporaryCollection;
internal readonly FuncProvider<string, PenumbraApiEc> RemoveTemporaryCollectionByName;
internal readonly FuncProvider<string, int, bool, PenumbraApiEc> AssignTemporaryCollection;
internal readonly FuncProvider<string, Dictionary<string, string>, string, int, PenumbraApiEc> AddTemporaryModAll;
internal readonly FuncProvider<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> AddTemporaryMod;
internal readonly FuncProvider<string, int, PenumbraApiEc> RemoveTemporaryModAll;
internal readonly FuncProvider<string, string, int, PenumbraApiEc> RemoveTemporaryMod;
public PenumbraIpcProviders( DalamudPluginInterface pi, IPenumbraApi api, ModManager modManager )
public PenumbraIpcProviders(DalamudPluginInterface pi, IPenumbraApi api, ModManager modManager)
{
Api = api;
// Plugin State
Initialized = Ipc.Initialized.Provider( pi );
Disposed = Ipc.Disposed.Provider( pi );
ApiVersion = Ipc.ApiVersion.Provider( pi, DeprecatedVersion );
ApiVersions = Ipc.ApiVersions.Provider( pi, () => Api.ApiVersion );
GetEnabledState = Ipc.GetEnabledState.Provider( pi, Api.GetEnabledState );
EnabledChange = Ipc.EnabledChange.Provider( pi, () => Api.EnabledChange += EnabledChangeEvent, () => Api.EnabledChange -= EnabledChangeEvent );
Initialized = Ipc.Initialized.Provider(pi);
Disposed = Ipc.Disposed.Provider(pi);
ApiVersion = Ipc.ApiVersion.Provider(pi, DeprecatedVersion);
ApiVersions = Ipc.ApiVersions.Provider(pi, () => Api.ApiVersion);
GetEnabledState = Ipc.GetEnabledState.Provider(pi, Api.GetEnabledState);
EnabledChange =
Ipc.EnabledChange.Provider(pi, () => Api.EnabledChange += EnabledChangeEvent, () => Api.EnabledChange -= EnabledChangeEvent);
// Configuration
GetModDirectory = Ipc.GetModDirectory.Provider( pi, Api.GetModDirectory );
GetConfiguration = Ipc.GetConfiguration.Provider( pi, Api.GetConfiguration );
ModDirectoryChanged = Ipc.ModDirectoryChanged.Provider( pi, a => Api.ModDirectoryChanged += a, a => Api.ModDirectoryChanged -= a );
GetModDirectory = Ipc.GetModDirectory.Provider(pi, Api.GetModDirectory);
GetConfiguration = Ipc.GetConfiguration.Provider(pi, Api.GetConfiguration);
ModDirectoryChanged = Ipc.ModDirectoryChanged.Provider(pi, a => Api.ModDirectoryChanged += a, a => Api.ModDirectoryChanged -= a);
// UI
PreSettingsDraw = Ipc.PreSettingsDraw.Provider( pi, a => Api.PreSettingsPanelDraw += a, a => Api.PreSettingsPanelDraw -= a );
PostSettingsDraw = Ipc.PostSettingsDraw.Provider( pi, a => Api.PostSettingsPanelDraw += a, a => Api.PostSettingsPanelDraw -= a );
ChangedItemTooltip = Ipc.ChangedItemTooltip.Provider( pi, () => Api.ChangedItemTooltip += OnTooltip, () => Api.ChangedItemTooltip -= OnTooltip );
ChangedItemClick = Ipc.ChangedItemClick.Provider( pi, () => Api.ChangedItemClicked += OnClick, () => Api.ChangedItemClicked -= OnClick );
OpenMainWindow = Ipc.OpenMainWindow.Provider( pi, Api.OpenMainWindow );
CloseMainWindow = Ipc.CloseMainWindow.Provider( pi, Api.CloseMainWindow );
PreSettingsDraw = Ipc.PreSettingsDraw.Provider(pi, a => Api.PreSettingsPanelDraw += a, a => Api.PreSettingsPanelDraw -= a);
PostSettingsDraw = Ipc.PostSettingsDraw.Provider(pi, a => Api.PostSettingsPanelDraw += a, a => Api.PostSettingsPanelDraw -= a);
ChangedItemTooltip =
Ipc.ChangedItemTooltip.Provider(pi, () => Api.ChangedItemTooltip += OnTooltip, () => Api.ChangedItemTooltip -= OnTooltip);
ChangedItemClick = Ipc.ChangedItemClick.Provider(pi, () => Api.ChangedItemClicked += OnClick, () => Api.ChangedItemClicked -= OnClick);
OpenMainWindow = Ipc.OpenMainWindow.Provider(pi, Api.OpenMainWindow);
CloseMainWindow = Ipc.CloseMainWindow.Provider(pi, Api.CloseMainWindow);
// Redrawing
RedrawAll = Ipc.RedrawAll.Provider( pi, Api.RedrawAll );
RedrawObject = Ipc.RedrawObject.Provider( pi, Api.RedrawObject );
RedrawObjectByIndex = Ipc.RedrawObjectByIndex.Provider( pi, Api.RedrawObject );
RedrawObjectByName = Ipc.RedrawObjectByName.Provider( pi, Api.RedrawObject );
GameObjectRedrawn = Ipc.GameObjectRedrawn.Provider( pi, () => Api.GameObjectRedrawn += OnGameObjectRedrawn, () => Api.GameObjectRedrawn -= OnGameObjectRedrawn );
RedrawAll = Ipc.RedrawAll.Provider(pi, Api.RedrawAll);
RedrawObject = Ipc.RedrawObject.Provider(pi, Api.RedrawObject);
RedrawObjectByIndex = Ipc.RedrawObjectByIndex.Provider(pi, Api.RedrawObject);
RedrawObjectByName = Ipc.RedrawObjectByName.Provider(pi, Api.RedrawObject);
GameObjectRedrawn = Ipc.GameObjectRedrawn.Provider(pi, () => Api.GameObjectRedrawn += OnGameObjectRedrawn,
() => Api.GameObjectRedrawn -= OnGameObjectRedrawn);
// Game State
GetDrawObjectInfo = Ipc.GetDrawObjectInfo.Provider( pi, Api.GetDrawObjectInfo );
GetCutsceneParentIndex = Ipc.GetCutsceneParentIndex.Provider( pi, Api.GetCutsceneParentIndex );
CreatingCharacterBase = Ipc.CreatingCharacterBase.Provider( pi,
GetDrawObjectInfo = Ipc.GetDrawObjectInfo.Provider(pi, Api.GetDrawObjectInfo);
GetCutsceneParentIndex = Ipc.GetCutsceneParentIndex.Provider(pi, Api.GetCutsceneParentIndex);
CreatingCharacterBase = Ipc.CreatingCharacterBase.Provider(pi,
() => Api.CreatingCharacterBase += CreatingCharacterBaseEvent,
() => Api.CreatingCharacterBase -= CreatingCharacterBaseEvent );
CreatedCharacterBase = Ipc.CreatedCharacterBase.Provider( pi,
() => Api.CreatingCharacterBase -= CreatingCharacterBaseEvent);
CreatedCharacterBase = Ipc.CreatedCharacterBase.Provider(pi,
() => Api.CreatedCharacterBase += CreatedCharacterBaseEvent,
() => Api.CreatedCharacterBase -= CreatedCharacterBaseEvent );
GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Provider( pi,
() => Api.CreatedCharacterBase -= CreatedCharacterBaseEvent);
GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Provider(pi,
() => Api.GameObjectResourceResolved += GameObjectResourceResolvedEvent,
() => Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent );
() => Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent);
// Resolve
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider( pi, Api.ResolveDefaultPath );
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider( pi, Api.ResolveInterfacePath );
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider( pi, Api.ResolvePlayerPath );
ResolveGameObjectPath = Ipc.ResolveGameObjectPath.Provider( pi, Api.ResolveGameObjectPath );
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider( pi, Api.ResolvePath );
ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath );
ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider( pi, Api.ReverseResolveGameObjectPath );
ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath );
ResolvePlayerPaths = Ipc.ResolvePlayerPaths.Provider( pi, Api.ResolvePlayerPaths );
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider(pi, Api.ResolveDefaultPath);
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider(pi, Api.ResolveInterfacePath);
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider(pi, Api.ResolvePlayerPath);
ResolveGameObjectPath = Ipc.ResolveGameObjectPath.Provider(pi, Api.ResolveGameObjectPath);
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider(pi, Api.ResolvePath);
ReverseResolvePath = Ipc.ReverseResolvePath.Provider(pi, Api.ReverseResolvePath);
ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider(pi, Api.ReverseResolveGameObjectPath);
ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider(pi, Api.ReverseResolvePlayerPath);
ResolvePlayerPaths = Ipc.ResolvePlayerPaths.Provider(pi, Api.ResolvePlayerPaths);
// Collections
GetCollections = Ipc.GetCollections.Provider( pi, Api.GetCollections );
GetCurrentCollectionName = Ipc.GetCurrentCollectionName.Provider( pi, Api.GetCurrentCollection );
GetDefaultCollectionName = Ipc.GetDefaultCollectionName.Provider( pi, Api.GetDefaultCollection );
GetInterfaceCollectionName = Ipc.GetInterfaceCollectionName.Provider( pi, Api.GetInterfaceCollection );
GetCharacterCollectionName = Ipc.GetCharacterCollectionName.Provider( pi, Api.GetCharacterCollection );
GetCollectionForType = Ipc.GetCollectionForType.Provider( pi, Api.GetCollectionForType );
SetCollectionForType = Ipc.SetCollectionForType.Provider( pi, Api.SetCollectionForType );
GetCollectionForObject = Ipc.GetCollectionForObject.Provider( pi, Api.GetCollectionForObject );
SetCollectionForObject = Ipc.SetCollectionForObject.Provider( pi, Api.SetCollectionForObject );
GetChangedItems = Ipc.GetChangedItems.Provider( pi, Api.GetChangedItemsForCollection );
GetCollections = Ipc.GetCollections.Provider(pi, Api.GetCollections);
GetCurrentCollectionName = Ipc.GetCurrentCollectionName.Provider(pi, Api.GetCurrentCollection);
GetDefaultCollectionName = Ipc.GetDefaultCollectionName.Provider(pi, Api.GetDefaultCollection);
GetInterfaceCollectionName = Ipc.GetInterfaceCollectionName.Provider(pi, Api.GetInterfaceCollection);
GetCharacterCollectionName = Ipc.GetCharacterCollectionName.Provider(pi, Api.GetCharacterCollection);
GetCollectionForType = Ipc.GetCollectionForType.Provider(pi, Api.GetCollectionForType);
SetCollectionForType = Ipc.SetCollectionForType.Provider(pi, Api.SetCollectionForType);
GetCollectionForObject = Ipc.GetCollectionForObject.Provider(pi, Api.GetCollectionForObject);
SetCollectionForObject = Ipc.SetCollectionForObject.Provider(pi, Api.SetCollectionForObject);
GetChangedItems = Ipc.GetChangedItems.Provider(pi, Api.GetChangedItemsForCollection);
// Meta
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider( pi, Api.GetPlayerMetaManipulations );
GetMetaManipulations = Ipc.GetMetaManipulations.Provider( pi, Api.GetMetaManipulations );
GetGameObjectMetaManipulations = Ipc.GetGameObjectMetaManipulations.Provider( pi, Api.GetGameObjectMetaManipulations );
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider(pi, Api.GetPlayerMetaManipulations);
GetMetaManipulations = Ipc.GetMetaManipulations.Provider(pi, Api.GetMetaManipulations);
GetGameObjectMetaManipulations = Ipc.GetGameObjectMetaManipulations.Provider(pi, Api.GetGameObjectMetaManipulations);
// Mods
GetMods = Ipc.GetMods.Provider( pi, Api.GetModList );
ReloadMod = Ipc.ReloadMod.Provider( pi, Api.ReloadMod );
AddMod = Ipc.AddMod.Provider( pi, Api.AddMod );
DeleteMod = Ipc.DeleteMod.Provider( pi, Api.DeleteMod );
GetModPath = Ipc.GetModPath.Provider( pi, Api.GetModPath );
SetModPath = Ipc.SetModPath.Provider( pi, Api.SetModPath );
ModDeleted = Ipc.ModDeleted.Provider( pi, () => Api.ModDeleted += ModDeletedEvent, () => Api.ModDeleted -= ModDeletedEvent );
ModAdded = Ipc.ModAdded.Provider( pi, () => Api.ModAdded += ModAddedEvent, () => Api.ModAdded -= ModAddedEvent );
ModMoved = Ipc.ModMoved.Provider( pi, () => Api.ModMoved += ModMovedEvent, () => Api.ModMoved -= ModMovedEvent );
GetMods = Ipc.GetMods.Provider(pi, Api.GetModList);
ReloadMod = Ipc.ReloadMod.Provider(pi, Api.ReloadMod);
InstallMod = Ipc.InstallMod.Provider(pi, Api.InstallMod);
AddMod = Ipc.AddMod.Provider(pi, Api.AddMod);
DeleteMod = Ipc.DeleteMod.Provider(pi, Api.DeleteMod);
GetModPath = Ipc.GetModPath.Provider(pi, Api.GetModPath);
SetModPath = Ipc.SetModPath.Provider(pi, Api.SetModPath);
ModDeleted = Ipc.ModDeleted.Provider(pi, () => Api.ModDeleted += ModDeletedEvent, () => Api.ModDeleted -= ModDeletedEvent);
ModAdded = Ipc.ModAdded.Provider(pi, () => Api.ModAdded += ModAddedEvent, () => Api.ModAdded -= ModAddedEvent);
ModMoved = Ipc.ModMoved.Provider(pi, () => Api.ModMoved += ModMovedEvent, () => Api.ModMoved -= ModMovedEvent);
// ModSettings
GetAvailableModSettings = Ipc.GetAvailableModSettings.Provider( pi, Api.GetAvailableModSettings );
GetCurrentModSettings = Ipc.GetCurrentModSettings.Provider( pi, Api.GetCurrentModSettings );
TryInheritMod = Ipc.TryInheritMod.Provider( pi, Api.TryInheritMod );
TrySetMod = Ipc.TrySetMod.Provider( pi, Api.TrySetMod );
TrySetModPriority = Ipc.TrySetModPriority.Provider( pi, Api.TrySetModPriority );
TrySetModSetting = Ipc.TrySetModSetting.Provider( pi, Api.TrySetModSetting );
TrySetModSettings = Ipc.TrySetModSettings.Provider( pi, Api.TrySetModSettings );
ModSettingChanged = Ipc.ModSettingChanged.Provider( pi,
GetAvailableModSettings = Ipc.GetAvailableModSettings.Provider(pi, Api.GetAvailableModSettings);
GetCurrentModSettings = Ipc.GetCurrentModSettings.Provider(pi, Api.GetCurrentModSettings);
TryInheritMod = Ipc.TryInheritMod.Provider(pi, Api.TryInheritMod);
TrySetMod = Ipc.TrySetMod.Provider(pi, Api.TrySetMod);
TrySetModPriority = Ipc.TrySetModPriority.Provider(pi, Api.TrySetModPriority);
TrySetModSetting = Ipc.TrySetModSetting.Provider(pi, Api.TrySetModSetting);
TrySetModSettings = Ipc.TrySetModSettings.Provider(pi, Api.TrySetModSettings);
ModSettingChanged = Ipc.ModSettingChanged.Provider(pi,
() => Api.ModSettingChanged += ModSettingChangedEvent,
() => Api.ModSettingChanged -= ModSettingChangedEvent );
CopyModSettings = Ipc.CopyModSettings.Provider( pi, Api.CopyModSettings );
() => Api.ModSettingChanged -= ModSettingChangedEvent);
CopyModSettings = Ipc.CopyModSettings.Provider(pi, Api.CopyModSettings);
// Temporary
CreateTemporaryCollection = Ipc.CreateTemporaryCollection.Provider( pi, Api.CreateTemporaryCollection );
RemoveTemporaryCollection = Ipc.RemoveTemporaryCollection.Provider( pi, Api.RemoveTemporaryCollection );
CreateNamedTemporaryCollection = Ipc.CreateNamedTemporaryCollection.Provider( pi, Api.CreateNamedTemporaryCollection );
RemoveTemporaryCollectionByName = Ipc.RemoveTemporaryCollectionByName.Provider( pi, Api.RemoveTemporaryCollectionByName );
AssignTemporaryCollection = Ipc.AssignTemporaryCollection.Provider( pi, Api.AssignTemporaryCollection );
AddTemporaryModAll = Ipc.AddTemporaryModAll.Provider( pi, Api.AddTemporaryModAll );
AddTemporaryMod = Ipc.AddTemporaryMod.Provider( pi, Api.AddTemporaryMod );
RemoveTemporaryModAll = Ipc.RemoveTemporaryModAll.Provider( pi, Api.RemoveTemporaryModAll );
RemoveTemporaryMod = Ipc.RemoveTemporaryMod.Provider( pi, Api.RemoveTemporaryMod );
CreateTemporaryCollection = Ipc.CreateTemporaryCollection.Provider(pi, Api.CreateTemporaryCollection);
RemoveTemporaryCollection = Ipc.RemoveTemporaryCollection.Provider(pi, Api.RemoveTemporaryCollection);
CreateNamedTemporaryCollection = Ipc.CreateNamedTemporaryCollection.Provider(pi, Api.CreateNamedTemporaryCollection);
RemoveTemporaryCollectionByName = Ipc.RemoveTemporaryCollectionByName.Provider(pi, Api.RemoveTemporaryCollectionByName);
AssignTemporaryCollection = Ipc.AssignTemporaryCollection.Provider(pi, Api.AssignTemporaryCollection);
AddTemporaryModAll = Ipc.AddTemporaryModAll.Provider(pi, Api.AddTemporaryModAll);
AddTemporaryMod = Ipc.AddTemporaryMod.Provider(pi, Api.AddTemporaryMod);
RemoveTemporaryModAll = Ipc.RemoveTemporaryModAll.Provider(pi, Api.RemoveTemporaryModAll);
RemoveTemporaryMod = Ipc.RemoveTemporaryMod.Provider(pi, Api.RemoveTemporaryMod);
Tester = new IpcTester( pi, this, modManager );
Tester = new IpcTester(pi, this, modManager);
Initialized.Invoke();
}
@ -295,6 +300,7 @@ public class PenumbraIpcProviders : IDisposable
// Mods
GetMods.Dispose();
ReloadMod.Dispose();
InstallMod.Dispose();
AddMod.Dispose();
DeleteMod.Dispose();
GetModPath.Dispose();
@ -332,46 +338,46 @@ public class PenumbraIpcProviders : IDisposable
// Wrappers
private int DeprecatedVersion()
{
Penumbra.Log.Warning( $"{Ipc.ApiVersion.Label} is outdated. Please use {Ipc.ApiVersions.Label} instead." );
Penumbra.Log.Warning($"{Ipc.ApiVersion.Label} is outdated. Please use {Ipc.ApiVersions.Label} instead.");
return Api.ApiVersion.Breaking;
}
private void OnClick( MouseButton click, object? item )
private void OnClick(MouseButton click, object? item)
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ChangedItemClick.Invoke( click, type, id );
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(item);
ChangedItemClick.Invoke(click, type, id);
}
private void OnTooltip( object? item )
private void OnTooltip(object? item)
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ChangedItemTooltip.Invoke( type, id );
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId(item);
ChangedItemTooltip.Invoke(type, id);
}
private void EnabledChangeEvent( bool value )
=> EnabledChange.Invoke( value );
private void EnabledChangeEvent(bool value)
=> EnabledChange.Invoke(value);
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
=> GameObjectRedrawn.Invoke( objectAddress, objectTableIndex );
private void OnGameObjectRedrawn(IntPtr objectAddress, int objectTableIndex)
=> GameObjectRedrawn.Invoke(objectAddress, objectTableIndex);
private void CreatingCharacterBaseEvent( IntPtr gameObject, string collectionName, IntPtr modelId, IntPtr customize, IntPtr equipData )
=> CreatingCharacterBase.Invoke( gameObject, collectionName, modelId, customize, equipData );
private void CreatingCharacterBaseEvent(IntPtr gameObject, string collectionName, IntPtr modelId, IntPtr customize, IntPtr equipData)
=> CreatingCharacterBase.Invoke(gameObject, collectionName, modelId, customize, equipData);
private void CreatedCharacterBaseEvent( IntPtr gameObject, string collectionName, IntPtr drawObject )
=> CreatedCharacterBase.Invoke( gameObject, collectionName, drawObject );
private void CreatedCharacterBaseEvent(IntPtr gameObject, string collectionName, IntPtr drawObject)
=> CreatedCharacterBase.Invoke(gameObject, collectionName, drawObject);
private void GameObjectResourceResolvedEvent( IntPtr gameObject, string gamePath, string localPath )
=> GameObjectResourcePathResolved.Invoke( gameObject, gamePath, localPath );
private void GameObjectResourceResolvedEvent(IntPtr gameObject, string gamePath, string localPath)
=> GameObjectResourcePathResolved.Invoke(gameObject, gamePath, localPath);
private void ModSettingChangedEvent( ModSettingChange type, string collection, string mod, bool inherited )
=> ModSettingChanged.Invoke( type, collection, mod, inherited );
private void ModSettingChangedEvent(ModSettingChange type, string collection, string mod, bool inherited)
=> ModSettingChanged.Invoke(type, collection, mod, inherited);
private void ModDeletedEvent( string name )
=> ModDeleted.Invoke( name );
private void ModDeletedEvent(string name)
=> ModDeleted.Invoke(name);
private void ModAddedEvent( string name )
=> ModAdded.Invoke( name );
private void ModAddedEvent(string name)
=> ModAdded.Invoke(name);
private void ModMovedEvent( string from, string to )
=> ModMoved.Invoke( from, to );
}
private void ModMovedEvent(string from, string to)
=> ModMoved.Invoke(from, to);
}

View file

@ -39,10 +39,10 @@ public partial class TexToolsImporter : IDisposable
private readonly ModEditor _editor;
private readonly ModManager _modManager;
public TexToolsImporter( DirectoryInfo baseDirectory, int count, IEnumerable< FileInfo > modPackFiles,
public TexToolsImporter( int count, IEnumerable< FileInfo > modPackFiles,
Action< FileInfo, DirectoryInfo?, Exception? > handler, Configuration config, ModEditor editor, ModManager modManager)
{
_baseDirectory = baseDirectory;
_baseDirectory = modManager.BasePath;
_tmpFile = Path.Combine( _baseDirectory.FullName, TempFileName );
_modPackFiles = modPackFiles;
_config = config;

View file

@ -18,10 +18,10 @@ public class ModBackup
public readonly string Name;
public readonly bool Exists;
public ModBackup(ExportManager exportManager, Mod mod)
public ModBackup(ModExportManager modExportManager, Mod mod)
{
_mod = mod;
Name = Path.Combine(exportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp";
Name = Path.Combine(modExportManager.ExportDirectory.FullName, _mod.ModPath.Name) + ".pmp";
Exists = File.Exists(Name);
}

View file

@ -4,7 +4,7 @@ using Penumbra.Services;
namespace Penumbra.Mods.Manager;
public class ExportManager : IDisposable
public class ModExportManager : IDisposable
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
@ -15,7 +15,7 @@ public class ExportManager : IDisposable
public DirectoryInfo ExportDirectory
=> _exportDirectory ?? _modManager.BasePath;
public ExportManager(Configuration config, CommunicatorService communicator, ModManager modManager)
public ModExportManager(Configuration config, CommunicatorService communicator, ModManager modManager)
{
_config = config;
_communicator = communicator;
@ -89,4 +89,4 @@ public class ExportManager : IDisposable
new ModBackup(this, mod).Move(null, newDirectory.Name);
mod.ModPath = newDirectory;
}
}
}

View file

@ -0,0 +1,123 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Dalamud.Interface.Internal.Notifications;
using Penumbra.Import;
namespace Penumbra.Mods.Manager;
public class ModImportManager : IDisposable
{
private readonly ModManager _modManager;
private readonly Configuration _config;
private readonly ModEditor _modEditor;
private readonly ConcurrentQueue<string[]> _modsToUnpack = new();
/// <summary> Mods need to be added thread-safely outside of iteration. </summary>
private readonly ConcurrentQueue<DirectoryInfo> _modsToAdd = new();
private TexToolsImporter? _import;
public ModImportManager(ModManager modManager, Configuration config, ModEditor modEditor)
{
_modManager = modManager;
_config = config;
_modEditor = modEditor;
}
public void TryUnpacking()
{
if (Importing || !_modsToUnpack.TryDequeue(out var newMods))
return;
var files = newMods.Where(s =>
{
if (File.Exists(s))
return true;
Penumbra.ChatService.NotificationMessage($"Failed to import queued mod at {s}, the file does not exist.", "Warning",
NotificationType.Warning);
return false;
}).Select(s => new FileInfo(s)).ToArray();
if (files.Length == 0)
return;
_import = new TexToolsImporter(files.Length, files, AddNewMod, _config, _modEditor, _modManager);
}
public bool Importing
=> _import != null;
public bool IsImporting([NotNullWhen(true)] out TexToolsImporter? importer)
{
importer = _import;
return _import != null;
}
public void AddUnpack(IEnumerable<string> paths)
=> _modsToUnpack.Enqueue(paths.ToArray());
public void AddUnpack(params string[] paths)
=> _modsToUnpack.Enqueue(paths);
public void ClearImport()
{
_import?.Dispose();
_import = null;
}
public bool AddUnpackedMod([NotNullWhen(true)] out Mod? mod)
{
if (!_modsToAdd.TryDequeue(out var directory))
{
mod = null;
return false;
}
_modManager.AddMod(directory);
mod = _modManager.LastOrDefault();
return mod != null && mod.ModPath == directory;
}
public void Dispose()
{
ClearImport();
_modsToAdd.Clear();
_modsToUnpack.Clear();
}
/// <summary>
/// Clean up invalid directory if necessary.
/// Add successfully extracted mods.
/// </summary>
private void AddNewMod(FileInfo file, DirectoryInfo? dir, Exception? error)
{
if (error != null)
{
if (dir != null && Directory.Exists(dir.FullName))
try
{
Directory.Delete(dir.FullName, true);
}
catch (Exception e)
{
Penumbra.Log.Error($"Error cleaning up failed mod extraction of {file.FullName} to {dir.FullName}:\n{e}");
}
if (error is not OperationCanceledException)
Penumbra.Log.Error($"Error extracting {file.FullName}, mod skipped:\n{error}");
}
else if (dir != null)
{
_modsToAdd.Enqueue(dir);
}
}
}

View file

@ -21,8 +21,8 @@ using Penumbra.UI.Tabs;
using Penumbra.Util;
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
using ModFileSystemSelector = Penumbra.UI.ModsTab.ModFileSystemSelector;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager;
namespace Penumbra;
public class PenumbraNew
@ -61,7 +61,7 @@ public class PenumbraNew
.AddSingleton<StainService>()
.AddSingleton<ItemService>()
.AddSingleton<ActorService>();
// Add Game Services
services.AddSingleton<GameEventManager>()
.AddSingleton<FrameworkManager>()
@ -75,8 +75,8 @@ public class PenumbraNew
.AddSingleton<CreateFileWHook>()
.AddSingleton<ResidentResourceManager>()
.AddSingleton<FontReloader>()
.AddSingleton<RedrawService>();
.AddSingleton<RedrawService>();
// Add PathResolver
services.AddSingleton<CutsceneService>()
.AddSingleton<IdentifiedCollectionCache>();
@ -98,7 +98,8 @@ public class PenumbraNew
.AddSingleton<ModDataEditor>()
.AddSingleton<ModOptionEditor>()
.AddSingleton<ModManager>()
.AddSingleton<ExportManager>()
.AddSingleton<ModExportManager>()
.AddSingleton<ModImportManager>()
.AddSingleton<ModFileSystem>()
.AddSingleton<ModCacheManager>();
@ -106,7 +107,7 @@ public class PenumbraNew
services.AddSingleton<ResourceLoader>()
.AddSingleton<ResourceWatcher>()
.AddSingleton<ResourceTreeFactory>();
// Add Path Resolver
services.AddSingleton<AnimationHookService>()
.AddSingleton<CollectionResolver>()
@ -116,7 +117,7 @@ public class PenumbraNew
.AddSingleton<PathState>()
.AddSingleton<SubfileHelper>()
.AddSingleton<IdentifiedCollectionCache>()
.AddSingleton<PathResolver>();
.AddSingleton<PathResolver>();
// Add Interface
services.AddSingleton<FileDialogService>()
@ -131,6 +132,7 @@ public class PenumbraNew
.AddSingleton<ModsTab>()
.AddSingleton<ModPanel>()
.AddSingleton<ModFileSystemSelector>()
.AddSingleton<ImportPopup>()
.AddSingleton<ModPanelDescriptionTab>()
.AddSingleton<ModPanelSettingsTab>()
.AddSingleton<ModPanelEditTab>()
@ -147,7 +149,7 @@ public class PenumbraNew
.AddSingleton<ConfigTabBar>()
.AddSingleton<ResourceWatcher>()
.AddSingleton<ItemSwapTab>();
// Add Mod Editor
services.AddSingleton<ModFileCollection>()
.AddSingleton<DuplicateManager>()
@ -156,7 +158,7 @@ public class PenumbraNew
.AddSingleton<ModMetaEditor>()
.AddSingleton<ModSwapEditor>()
.AddSingleton<ModNormalizer>()
.AddSingleton<ModEditor>();
.AddSingleton<ModEditor>();
// Add API
services.AddSingleton<PenumbraApi>()

View file

@ -0,0 +1,64 @@
using System;
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
using OtterGui.Raii;
using Penumbra.Import.Structs;
using Penumbra.Mods.Manager;
namespace Penumbra.UI;
/// <summary> Draw the progress information for import. </summary>
public sealed class ImportPopup : Window
{
private readonly ModImportManager _modImportManager;
public ImportPopup(ModImportManager modImportManager)
: base("Penumbra Import Status",
ImGuiWindowFlags.Modal
| ImGuiWindowFlags.Popup
| ImGuiWindowFlags.NoCollapse
| ImGuiWindowFlags.NoDecoration
| ImGuiWindowFlags.NoBackground
| ImGuiWindowFlags.NoMove
| ImGuiWindowFlags.NoInputs
| ImGuiWindowFlags.NoFocusOnAppearing
| ImGuiWindowFlags.NoBringToFrontOnFocus, true)
{
_modImportManager = modImportManager;
IsOpen = true;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = Vector2.Zero,
MaximumSize = Vector2.Zero,
};
}
public override void Draw()
{
_modImportManager.TryUnpacking();
if (!_modImportManager.IsImporting(out var import))
return;
ImGui.OpenPopup("##importPopup");
var display = ImGui.GetIO().DisplaySize;
var height = Math.Max(display.Y / 4, 15 * ImGui.GetFrameHeightWithSpacing());
var width = display.X / 8;
var size = new Vector2(width * 2, height);
ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Always, Vector2.One / 2);
ImGui.SetNextWindowSize(size);
using var popup = ImRaii.Popup("##importPopup", ImGuiWindowFlags.Modal);
using (var child = ImRaii.Child("##import", new Vector2(-1, size.Y - ImGui.GetFrameHeight() * 2)))
{
if (child)
import.DrawProgressInfo(new Vector2(-1, ImGui.GetFrameHeight()));
}
if ((import.State != ImporterState.Done || !ImGui.Button("Close", -Vector2.UnitX))
&& (import.State == ImporterState.Done || !import.DrawCancelButton(-Vector2.UnitX)))
return;
_modImportManager.ClearImport();
}
}

View file

@ -1,7 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
@ -13,12 +10,9 @@ using OtterGui.Classes;
using OtterGui.Filesystem;
using OtterGui.FileSystem.Selector;
using OtterGui.Raii;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Import;
using Penumbra.Import.Structs;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
@ -27,7 +21,7 @@ using Penumbra.Util;
namespace Penumbra.UI.ModsTab;
public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>
public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSystemSelector.ModState>
{
private readonly CommunicatorService _communicator;
private readonly ChatService _chat;
@ -37,18 +31,13 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
private readonly ModCacheManager _modCaches;
private readonly CollectionManager _collectionManager;
private readonly TutorialService _tutorial;
private readonly ModEditor _modEditor;
private Queue<string> _modUnpackQueue = new Queue<string>();
private TexToolsImporter? _import;
public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty;
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;
private uint _infoPopupId = 0;
private readonly ModImportManager _modImportManager;
public ModSettings SelectedSettings { get; private set; } = ModSettings.Empty;
public ModCollection SelectedSettingCollection { get; private set; } = ModCollection.Empty;
public ModFileSystemSelector(CommunicatorService communicator, ModFileSystem fileSystem, ModManager modManager,
CollectionManager collectionManager, Configuration config, TutorialService tutorial, FileDialogService fileDialog, ChatService chat,
ModEditor modEditor, ModCacheManager modCaches)
ModCacheManager modCaches, ModImportManager modImportManager)
: base(fileSystem, DalamudServices.KeyState, HandleException)
{
_communicator = communicator;
@ -58,8 +47,8 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
_tutorial = tutorial;
_fileDialog = fileDialog;
_chat = chat;
_modEditor = modEditor;
_modCaches = modCaches;
_modImportManager = modImportManager;
// @formatter:off
SubscribeRightClickFolder(EnableDescendants, 10);
@ -85,13 +74,12 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
SelectionChanged += OnSelectionChange;
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_collectionManager.Active.Current.ModSettingChanged += OnSettingChange;
_collectionManager.Active.Current.ModSettingChanged += OnSettingChange;
_collectionManager.Active.Current.InheritanceChanged += OnInheritanceChange;
_communicator.ModDataChanged.Subscribe(OnModDataChange);
_communicator.ModDiscoveryStarted.Subscribe(StoreCurrentSelection);
_communicator.ModDiscoveryFinished.Subscribe(RestoreLastSelection);
OnCollectionChange(CollectionType.Current, null, _collectionManager.Active.Current, "");
ExternalModImporter.ModFileSystemSelectorInstance = this;
}
public override void Dispose()
@ -100,11 +88,9 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
_communicator.ModDiscoveryStarted.Unsubscribe(StoreCurrentSelection);
_communicator.ModDiscoveryFinished.Unsubscribe(RestoreLastSelection);
_communicator.ModDataChanged.Unsubscribe(OnModDataChange);
_collectionManager.Active.Current.ModSettingChanged -= OnSettingChange;
_collectionManager.Active.Current.ModSettingChanged -= OnSettingChange;
_collectionManager.Active.Current.InheritanceChanged -= OnInheritanceChange;
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
_import?.Dispose();
_import = null;
}
public new ModFileSystem.Leaf? SelectedLeaf
@ -131,7 +117,6 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
protected override void DrawPopups()
{
DrawHelpPopup();
DrawInfoPopup();
if (ImGuiUtil.OpenNameField("Create New Mod", ref _newModName))
try
@ -147,13 +132,8 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
Penumbra.Log.Error($"Could not create directory for new Mod {_newModName}:\n{e}");
}
while (_modsToAdd.TryDequeue(out var dir))
while (_modImportManager.AddUnpackedMod(out var mod))
{
_modManager.AddMod(dir);
var mod = _modManager.LastOrDefault();
if (mod == null)
continue;
MoveModToDefaultDirectory(mod);
SelectByValue(mod);
}
@ -234,8 +214,6 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
/// <summary> Add an import mods button that opens a file selector. </summary>
private void AddImportModButton(Vector2 size)
{
_infoPopupId = ImGui.GetID("Import Status");
ExternalImportListener();
var button = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileImport.ToIconString(), size,
"Import one or multiple mods from Tex Tools Mod Pack Files or Penumbra Mod Pack Files.", !Penumbra.ModManager.Valid, true);
_tutorial.OpenTutorial(BasicTutorialSteps.ModImport);
@ -251,105 +229,11 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
{
if (!s)
return;
_modsCurrentlyUnpacking = true;
_import = new TexToolsImporter(_modManager.BasePath, f.Count, f.Select(file => new FileInfo(file)),
AddNewMod, _config, _modEditor, _modManager);
ImGui.OpenPopup(_infoPopupId);
_modImportManager.AddUnpack(f);
}, 0, modPath, _config.AlwaysOpenDefaultImport);
}
private void ExternalImportListener()
{
if (_modUnpackQueue.Count > 0)
{
// Attempt to avoid triggering if other mods are already unpacking
if (!_modsCurrentlyUnpacking)
{
string modPackagePath = _modUnpackQueue.Dequeue();
if (File.Exists(modPackagePath))
{
_modsCurrentlyUnpacking = true;
var modPath = !_config.AlwaysOpenDefaultImport ? null
: _config.DefaultModImportPath.Length > 0 ? _config.DefaultModImportPath
: _config.ModDirectory.Length > 0 ? _config.ModDirectory : null;
_import = new TexToolsImporter(Penumbra.ModManager.BasePath, 1, new List<FileInfo>() { new FileInfo(modPackagePath) }, AddNewMod,
_config, _modEditor, _modManager);
ImGui.OpenPopup(_infoPopupId);
}
}
}
}
/// <summary>
/// Unpacks the specified standalone package
/// </summary>
/// <param name="modPackagePath">The package to unpack</param>
public void ImportStandaloneModPackage(string modPackagePath)
{
_modUnpackQueue.Enqueue(modPackagePath);
}
/// <summary> Draw the progress information for import. </summary>
private void DrawInfoPopup()
{
var display = ImGui.GetIO().DisplaySize;
var height = Math.Max(display.Y / 4, 15 * ImGui.GetFrameHeightWithSpacing());
var width = display.X / 8;
var size = new Vector2(width * 2, height);
ImGui.SetNextWindowPos(ImGui.GetMainViewport().GetCenter(), ImGuiCond.Always, Vector2.One / 2);
ImGui.SetNextWindowSize(size);
var infoPopupId = ImGui.GetID("Import Status");
using var popup = ImRaii.Popup("Import Status", ImGuiWindowFlags.Modal);
if (_import == null || !popup.Success)
return;
using (var child = ImRaii.Child("##import", new Vector2(-1, size.Y - ImGui.GetFrameHeight() * 2)))
{
if (child)
_import.DrawProgressInfo(new Vector2(-1, ImGui.GetFrameHeight()));
}
if ((_import.State != ImporterState.Done || !ImGui.Button("Close", -Vector2.UnitX))
&& (_import.State == ImporterState.Done || !_import.DrawCancelButton(-Vector2.UnitX)))
return;
_import?.Dispose();
_import = null;
ImGui.CloseCurrentPopup();
}
/// <summary> Mods need to be added thread-safely outside of iteration. </summary>
private readonly ConcurrentQueue<DirectoryInfo> _modsToAdd = new();
/// <summary>
/// Clean up invalid directory if necessary.
/// Add successfully extracted mods.
/// </summary>
private void AddNewMod(FileInfo file, DirectoryInfo? dir, Exception? error)
{
if (error != null)
{
if (dir != null && Directory.Exists(dir.FullName))
try
{
Directory.Delete(dir.FullName, true);
}
catch (Exception e)
{
Penumbra.Log.Error($"Error cleaning up failed mod extraction of {file.FullName} to {dir.FullName}:\n{e}");
}
if (error is not OperationCanceledException)
Penumbra.Log.Error($"Error extracting {file.FullName}, mod skipped:\n{error}");
}
else if (dir != null)
{
_modsToAdd.Enqueue(dir);
}
_modsCurrentlyUnpacking = false;
}
private void DeleteModButton(Vector2 size)
{
var keys = _config.DeleteModModifier.IsActive();
@ -573,7 +457,6 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector<Mod, ModF
private LowerString _modFilter = LowerString.Empty;
private int _filterType = -1;
private ModFilter _stateFilter = ModFilterExtensions.UnfilteredStateMods;
private bool _modsCurrentlyUnpacking;
private void SetFilterTooltip()
{

View file

@ -24,7 +24,7 @@ public class ModPanelEditTab : ITab
private readonly ChatService _chat;
private readonly FilenameService _filenames;
private readonly ModManager _modManager;
private readonly ExportManager _exportManager;
private readonly ModExportManager _modExportManager;
private readonly ModFileSystem _fileSystem;
private readonly ModFileSystemSelector _selector;
private readonly ModEditWindow _editWindow;
@ -38,16 +38,16 @@ public class ModPanelEditTab : ITab
private Mod _mod = null!;
public ModPanelEditTab(ModManager modManager, ModFileSystemSelector selector, ModFileSystem fileSystem, ChatService chat,
ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ExportManager exportManager)
ModEditWindow editWindow, ModEditor editor, FilenameService filenames, ModExportManager modExportManager)
{
_modManager = modManager;
_selector = selector;
_fileSystem = fileSystem;
_chat = chat;
_editWindow = editWindow;
_editor = editor;
_filenames = filenames;
_exportManager = exportManager;
_modManager = modManager;
_selector = selector;
_fileSystem = fileSystem;
_chat = chat;
_editWindow = editWindow;
_editor = editor;
_filenames = filenames;
_modExportManager = modExportManager;
}
public ReadOnlySpan<byte> Label
@ -150,7 +150,7 @@ public class ModPanelEditTab : ITab
private void BackupButtons(Vector2 buttonSize)
{
var backup = new ModBackup(_exportManager, _mod);
var backup = new ModBackup(_modExportManager, _mod);
var tt = ModBackup.CreatingBackup
? "Already exporting a mod."
: backup.Exists

View file

@ -32,7 +32,7 @@ public class SettingsTab : ITab
private readonly Penumbra _penumbra;
private readonly FileDialogService _fileDialog;
private readonly ModManager _modManager;
private readonly ExportManager _exportManager;
private readonly ModExportManager _modExportManager;
private readonly ModFileSystemSelector _selector;
private readonly CharacterUtility _characterUtility;
private readonly ResidentResourceManager _residentResources;
@ -40,7 +40,7 @@ public class SettingsTab : ITab
public SettingsTab(Configuration config, FontReloader fontReloader, TutorialService tutorial, Penumbra penumbra,
FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector, CharacterUtility characterUtility,
ResidentResourceManager residentResources, DalamudServices dalamud, ExportManager exportManager)
ResidentResourceManager residentResources, DalamudServices dalamud, ModExportManager modExportManager)
{
_config = config;
_fontReloader = fontReloader;
@ -52,7 +52,7 @@ public class SettingsTab : ITab
_characterUtility = characterUtility;
_residentResources = residentResources;
_dalamud = dalamud;
_exportManager = exportManager;
_modExportManager = modExportManager;
}
public void DrawHeader()
@ -116,7 +116,7 @@ public class SettingsTab : ITab
/// <summary> Check a potential new root directory for validity and return the button text and whether it is valid. </summary>
private static (string Text, bool Valid) CheckRootDirectoryPath(string newName, string old, bool selected)
{
{
static bool IsSubPathOf(string basePath, string subPath)
{
if (basePath.Length == 0)
@ -555,7 +555,7 @@ public class SettingsTab : ITab
_tempExportDirectory = tmp;
if (ImGui.IsItemDeactivatedAfterEdit())
_exportManager.UpdateExportDirectory(_tempExportDirectory);
_modExportManager.UpdateExportDirectory(_tempExportDirectory);
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Folder.ToIconString()}##export", UiHelpers.IconButtonSize,
@ -569,7 +569,7 @@ public class SettingsTab : ITab
_fileDialog.OpenFolderPicker("Choose Default Export Directory", (b, s) =>
{
if (b)
_exportManager.UpdateExportDirectory(s);
_modExportManager.UpdateExportDirectory(s);
}, startDir, false);
}

View file

@ -16,17 +16,17 @@ public class PenumbraWindowSystem : IDisposable
public readonly PenumbraChangelog Changelog;
public PenumbraWindowSystem(DalamudPluginInterface pi, Configuration config, PenumbraChangelog changelog, ConfigWindow window,
LaunchButton _,
ModEditWindow editWindow, FileDialogService fileDialog)
LaunchButton _, ModEditWindow editWindow, FileDialogService fileDialog, ImportPopup importPopup)
{
_uiBuilder = pi.UiBuilder;
_fileDialog = fileDialog;
Changelog = changelog;
Window = window;
_windowSystem = new WindowSystem("Penumbra");
_uiBuilder = pi.UiBuilder;
_fileDialog = fileDialog;
Changelog = changelog;
Window = window;
_windowSystem = new WindowSystem("Penumbra");
_windowSystem.AddWindow(changelog.Changelog);
_windowSystem.AddWindow(window);
_windowSystem.AddWindow(editWindow);
_windowSystem.AddWindow(importPopup);
_uiBuilder.OpenConfigUi += Window.Toggle;
_uiBuilder.Draw += _windowSystem.Draw;
_uiBuilder.Draw += _fileDialog.Draw;