mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Setup the CollectionsManager and introduced first version of character based collection loading and automatic redrawing of characters.
This commit is contained in:
parent
b0d14751cd
commit
3c9c892a83
15 changed files with 704 additions and 299 deletions
|
|
@ -19,7 +19,7 @@ namespace Penumbra.API
|
|||
public object? GetMods()
|
||||
{
|
||||
var modManager = Service< ModManager >.Get();
|
||||
return modManager.CurrentCollection.Cache?.AvailableMods.Select( x => new
|
||||
return modManager.Collections.CurrentCollection.Cache?.AvailableMods.Select( x => new
|
||||
{
|
||||
x.Settings.Enabled,
|
||||
x.Settings.Priority,
|
||||
|
|
@ -39,7 +39,7 @@ namespace Penumbra.API
|
|||
public object GetFiles()
|
||||
{
|
||||
var modManager = Service< ModManager >.Get();
|
||||
return modManager.CurrentCollection.Cache?.ResolvedFiles.ToDictionary(
|
||||
return modManager.Collections.CurrentCollection.Cache?.ResolvedFiles.ToDictionary(
|
||||
o => ( string )o.Key,
|
||||
o => o.Value.FullName
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Util;
|
||||
|
|
@ -24,7 +25,8 @@ namespace Penumbra
|
|||
public string ModDirectory { get; set; } = @"D:/ffxiv/fs_mods/";
|
||||
|
||||
public string CurrentCollection { get; set; } = "Default";
|
||||
|
||||
public string ForcedCollection { get; set; } = "";
|
||||
public Dictionary< string, string > CharacterCollections { get; set; } = new();
|
||||
public bool InvertModListOrder { internal get; set; }
|
||||
|
||||
public static Configuration Load( DalamudPluginInterface pi )
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Penumbra.Interop
|
|||
{
|
||||
private const int RenderModeOffset = 0x0104;
|
||||
private const int ModelInvisibilityFlag = 0b10;
|
||||
private const int ModelIsLoadingFlag = 0x800;
|
||||
private const int UnloadAllRedrawDelay = 250;
|
||||
private const int NpcActorId = -536870912;
|
||||
|
||||
|
|
@ -30,10 +31,11 @@ namespace Penumbra.Interop
|
|||
private readonly ModManager _mods;
|
||||
private readonly Queue< (int actorId, string name, Redraw s) > _actorIds = new();
|
||||
|
||||
private int _currentFrame = 0;
|
||||
private bool _changedSettings = false;
|
||||
private int _currentActorId = -1;
|
||||
private string? _currentActorName = null;
|
||||
private int _currentFrame = 0;
|
||||
private bool _changedSettings = false;
|
||||
private int _currentActorId = -1;
|
||||
private string? _currentActorName = null;
|
||||
private Redraw _currentActorRedraw = Redraw.Unload;
|
||||
|
||||
public ActorRefresher( DalamudPluginInterface pi, ModManager mods )
|
||||
{
|
||||
|
|
@ -42,10 +44,19 @@ namespace Penumbra.Interop
|
|||
}
|
||||
|
||||
private void ChangeSettings()
|
||||
=> _changedSettings = true;
|
||||
{
|
||||
if( _currentActorName != null && _mods.Collections.CharacterCollection.TryGetValue( _currentActorName, out var collection ) )
|
||||
{
|
||||
_changedSettings = true;
|
||||
_mods.Collections.ActiveCollection = collection;
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreSettings()
|
||||
=> _changedSettings = false;
|
||||
{
|
||||
_mods.Collections.ActiveCollection = _mods.Collections.CurrentCollection;
|
||||
_changedSettings = false;
|
||||
}
|
||||
|
||||
private static unsafe void WriteInvisible( IntPtr renderPtr )
|
||||
{
|
||||
|
|
@ -55,6 +66,16 @@ namespace Penumbra.Interop
|
|||
}
|
||||
}
|
||||
|
||||
private static unsafe bool StillLoading( IntPtr renderPtr )
|
||||
{
|
||||
if( renderPtr != IntPtr.Zero )
|
||||
{
|
||||
return *( int* )renderPtr != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static unsafe void WriteVisible( IntPtr renderPtr )
|
||||
{
|
||||
if( renderPtr != IntPtr.Zero )
|
||||
|
|
@ -81,54 +102,21 @@ namespace Penumbra.Interop
|
|||
private Actor? FindCurrentActor()
|
||||
=> _pi.ClientState.Actors.FirstOrDefault( CheckActor );
|
||||
|
||||
private void ChangeSettingsAndUndraw()
|
||||
private void PopActor()
|
||||
{
|
||||
if( _actorIds.Count > 0 )
|
||||
{
|
||||
var (id, name, s) = _actorIds.Dequeue();
|
||||
_currentActorName = name;
|
||||
_currentActorId = id;
|
||||
var (id, name, s) = _actorIds.Dequeue();
|
||||
_currentActorName = name;
|
||||
_currentActorId = id;
|
||||
_currentActorRedraw = s;
|
||||
var actor = FindCurrentActor();
|
||||
if( actor == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch( s )
|
||||
{
|
||||
case Redraw.Unload:
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
_currentFrame = 0;
|
||||
break;
|
||||
case Redraw.RedrawWithSettings:
|
||||
ChangeSettings();
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.RedrawWithoutSettings:
|
||||
WriteVisible( actor.Address + RenderModeOffset );
|
||||
_currentFrame = 0;
|
||||
break;
|
||||
case Redraw.WithoutSettings:
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.WithSettings:
|
||||
ChangeSettings();
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.OnlyWithSettings:
|
||||
ChangeSettings();
|
||||
if( !_changedSettings )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
default: throw new InvalidEnumArgumentException();
|
||||
}
|
||||
++_currentFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -136,7 +124,57 @@ namespace Penumbra.Interop
|
|||
}
|
||||
}
|
||||
|
||||
private void StartRedraw()
|
||||
private void ApplySettingsOrRedraw()
|
||||
{
|
||||
var actor = FindCurrentActor();
|
||||
if( actor == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( StillLoading( actor.Address + RenderModeOffset ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch( _currentActorRedraw )
|
||||
{
|
||||
case Redraw.Unload:
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
_currentFrame = 0;
|
||||
break;
|
||||
case Redraw.RedrawWithSettings:
|
||||
ChangeSettings();
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.RedrawWithoutSettings:
|
||||
WriteVisible( actor.Address + RenderModeOffset );
|
||||
_currentFrame = 0;
|
||||
break;
|
||||
case Redraw.WithoutSettings:
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.WithSettings:
|
||||
ChangeSettings();
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
case Redraw.OnlyWithSettings:
|
||||
ChangeSettings();
|
||||
if( !_changedSettings )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WriteInvisible( actor.Address + RenderModeOffset );
|
||||
++_currentFrame;
|
||||
break;
|
||||
default: throw new InvalidEnumArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartRedrawAndWait()
|
||||
{
|
||||
var actor = FindCurrentActor();
|
||||
if( actor == null )
|
||||
|
|
@ -151,8 +189,19 @@ namespace Penumbra.Interop
|
|||
|
||||
private void RevertSettings()
|
||||
{
|
||||
RestoreSettings();
|
||||
_currentFrame = 0;
|
||||
var actor = FindCurrentActor();
|
||||
if( actor != null )
|
||||
{
|
||||
if( !StillLoading( actor.Address + RenderModeOffset ) )
|
||||
{
|
||||
RestoreSettings();
|
||||
_currentFrame = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdateEvent( object framework )
|
||||
|
|
@ -160,12 +209,15 @@ namespace Penumbra.Interop
|
|||
switch( _currentFrame )
|
||||
{
|
||||
case 0:
|
||||
ChangeSettingsAndUndraw();
|
||||
PopActor();
|
||||
break;
|
||||
case 1:
|
||||
StartRedraw();
|
||||
ApplySettingsOrRedraw();
|
||||
break;
|
||||
case 2:
|
||||
StartRedrawAndWait();
|
||||
break;
|
||||
case 3:
|
||||
RevertSettings();
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Penumbra.Interop
|
|||
{
|
||||
public class PlayerWatcher : IDisposable
|
||||
{
|
||||
private const int ActorsPerFrame = 4;
|
||||
private const int ActorsPerFrame = 8;
|
||||
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
private readonly Dictionary< string, CharEquipment > _equip = new();
|
||||
|
|
@ -29,6 +29,11 @@ namespace Penumbra.Interop
|
|||
}
|
||||
}
|
||||
|
||||
public void RemovePlayerFromWatch( string playerName )
|
||||
{
|
||||
_equip.Remove( playerName );
|
||||
}
|
||||
|
||||
public void SetActorWatch( bool on )
|
||||
{
|
||||
if( on )
|
||||
|
|
@ -58,10 +63,10 @@ namespace Penumbra.Interop
|
|||
public void Dispose()
|
||||
=> DisableActorWatch();
|
||||
|
||||
public void OnTerritoryChange( object _1, ushort _2 )
|
||||
private void OnTerritoryChange( object _1, ushort _2 )
|
||||
=> Clear();
|
||||
|
||||
public void OnLogout( object _1, object _2 )
|
||||
private void OnLogout( object _1, object _2 )
|
||||
=> Clear();
|
||||
|
||||
public void Clear()
|
||||
|
|
@ -74,7 +79,7 @@ namespace Penumbra.Interop
|
|||
_frameTicker = 0;
|
||||
}
|
||||
|
||||
public void OnFrameworkUpdate( object framework )
|
||||
private void OnFrameworkUpdate( object framework )
|
||||
{
|
||||
var actors = _pi.ClientState.Actors;
|
||||
for( var i = 0; i < ActorsPerFrame; ++i )
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ namespace Penumbra.Interop
|
|||
|
||||
file = Marshal.PtrToStringAnsi( new IntPtr( pPath ) )!;
|
||||
var gameFsPath = GamePath.GenerateUncheckedLower( file );
|
||||
var replacementPath = modManager.CurrentCollection.ResolveSwappedOrReplacementPath( gameFsPath );
|
||||
var replacementPath = modManager.ResolveSwappedOrReplacementPath( gameFsPath );
|
||||
if( LogAllFiles && ( LogFileFilter == null || LogFileFilter.IsMatch( file ) ) )
|
||||
{
|
||||
PluginLog.Log( "[GetResourceHandler] {0}", file );
|
||||
|
|
|
|||
328
Penumbra/Mods/CollectionManager.cs
Normal file
328
Penumbra/Mods/CollectionManager.cs
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Interop;
|
||||
using Penumbra.Mod;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods
|
||||
{
|
||||
// Contains all collections and respective functions, as well as the collection settings.
|
||||
public class CollectionManager
|
||||
{
|
||||
private readonly Plugin _plugin;
|
||||
private readonly ModManager _manager;
|
||||
|
||||
public Dictionary< string, ModCollection > Collections { get; } = new();
|
||||
|
||||
public ModCollection CurrentCollection { get; private set; } = null!;
|
||||
public ModCollection ForcedCollection { get; private set; } = ModCollection.Empty;
|
||||
public Dictionary< string, ModCollection > CharacterCollection { get; } = new();
|
||||
|
||||
public ModCollection ActiveCollection { get; set; }
|
||||
|
||||
public CollectionManager( Plugin plugin, ModManager manager )
|
||||
{
|
||||
_plugin = plugin;
|
||||
_manager = manager;
|
||||
|
||||
ReadCollections();
|
||||
LoadConfigCollections( _plugin.Configuration );
|
||||
ActiveCollection = CurrentCollection;
|
||||
}
|
||||
|
||||
public void RecreateCaches()
|
||||
{
|
||||
foreach( var collection in Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
collection.CreateCache( _manager.BasePath, _manager.Mods, false );
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveModFromCaches( DirectoryInfo modDir )
|
||||
{
|
||||
foreach( var collection in Collections.Values )
|
||||
{
|
||||
collection.Cache?.RemoveMod( modDir );
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateCollections( ModData mod, bool metaChanges, ResourceChange fileChanges, bool nameChange, bool recomputeMeta )
|
||||
{
|
||||
foreach( var collection in Collections.Values )
|
||||
{
|
||||
if( metaChanges )
|
||||
{
|
||||
collection.UpdateSetting( mod );
|
||||
if( nameChange )
|
||||
{
|
||||
collection.Cache?.SortMods();
|
||||
}
|
||||
}
|
||||
|
||||
if( fileChanges.HasFlag( ResourceChange.Files )
|
||||
&& collection.Settings.TryGetValue( mod.BasePath.Name, out var settings )
|
||||
&& settings.Enabled )
|
||||
{
|
||||
collection.Cache?.CalculateEffectiveFileList();
|
||||
}
|
||||
|
||||
if( recomputeMeta )
|
||||
{
|
||||
collection.Cache?.UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddCollection( string name, Dictionary< string, ModSettings > settings )
|
||||
{
|
||||
var nameFixed = name.RemoveInvalidPathSymbols().ToLowerInvariant();
|
||||
if( Collections.Values.Any( c => c.Name.RemoveInvalidPathSymbols().ToLowerInvariant() == nameFixed ) )
|
||||
{
|
||||
PluginLog.Warning( $"The new collection {name} would lead to the same path as one that already exists." );
|
||||
return false;
|
||||
}
|
||||
|
||||
var newCollection = new ModCollection( name, settings );
|
||||
Collections.Add( name, newCollection );
|
||||
SaveCollection( newCollection );
|
||||
SetCurrentCollection( newCollection );
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveCollection( string name )
|
||||
{
|
||||
if( name == ModCollection.DefaultCollection )
|
||||
{
|
||||
PluginLog.Error( "Can not remove the default collection." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Collections.TryGetValue( name, out var collection ) )
|
||||
{
|
||||
if( CurrentCollection == collection )
|
||||
{
|
||||
SetCurrentCollection( Collections[ ModCollection.DefaultCollection ] );
|
||||
}
|
||||
|
||||
if( ForcedCollection == collection )
|
||||
{
|
||||
SetForcedCollection( ModCollection.Empty );
|
||||
}
|
||||
|
||||
foreach( var kvp in CharacterCollection.ToArray() )
|
||||
{
|
||||
if( kvp.Value == collection )
|
||||
{
|
||||
SetCharacterCollection( kvp.Key, ModCollection.Empty );
|
||||
}
|
||||
}
|
||||
|
||||
collection.Delete( _plugin.PluginInterface! );
|
||||
Collections.Remove( name );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddCache( ModCollection collection )
|
||||
{
|
||||
if( collection.Cache == null && collection.Name != string.Empty )
|
||||
{
|
||||
collection.CreateCache( _manager.BasePath, _manager.Mods, false );
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveCache( ModCollection collection )
|
||||
{
|
||||
if( collection.Name != ForcedCollection.Name
|
||||
&& collection.Name != CurrentCollection.Name
|
||||
&& CharacterCollection.All( kvp => kvp.Value.Name != collection.Name ) )
|
||||
{
|
||||
collection.ClearCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCollection( ModCollection newCollection, ModCollection oldCollection, Action< ModCollection > setter,
|
||||
Action< string > configSetter )
|
||||
{
|
||||
if( newCollection.Name == oldCollection.Name )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddCache( newCollection );
|
||||
|
||||
setter( newCollection );
|
||||
RemoveCache( oldCollection );
|
||||
configSetter( newCollection.Name );
|
||||
_plugin.Configuration.Save();
|
||||
}
|
||||
|
||||
public void SetCurrentCollection( ModCollection newCollection )
|
||||
=> SetCollection( newCollection, CurrentCollection, c =>
|
||||
{
|
||||
if( ActiveCollection == CurrentCollection )
|
||||
{
|
||||
ActiveCollection = c;
|
||||
}
|
||||
|
||||
CurrentCollection = c;
|
||||
var resourceManager = Service< GameResourceManagement >.Get();
|
||||
resourceManager.ReloadPlayerResources();
|
||||
}, s => _plugin.Configuration.CurrentCollection = s );
|
||||
|
||||
public void SetForcedCollection( ModCollection newCollection )
|
||||
=> SetCollection( newCollection, ForcedCollection, c => ForcedCollection = c, s => _plugin.Configuration.ForcedCollection = s );
|
||||
|
||||
public void SetCharacterCollection( string characterName, ModCollection newCollection )
|
||||
=> SetCollection( newCollection,
|
||||
CharacterCollection.TryGetValue( characterName, out var oldCollection ) ? oldCollection : ModCollection.Empty,
|
||||
c => CharacterCollection[ characterName ] = c, s => _plugin.Configuration.CharacterCollections[ characterName ] = s );
|
||||
|
||||
public bool CreateCharacterCollection( string characterName )
|
||||
{
|
||||
if( !CharacterCollection.ContainsKey( characterName ) )
|
||||
{
|
||||
CharacterCollection[ characterName ] = ModCollection.Empty;
|
||||
_plugin.Configuration.CharacterCollections[ characterName ] = string.Empty;
|
||||
_plugin.Configuration.Save();
|
||||
_plugin.PlayerWatcher.AddPlayerToWatch( characterName );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RemoveCharacterCollection( string characterName )
|
||||
{
|
||||
if( CharacterCollection.TryGetValue( characterName, out var collection ) )
|
||||
{
|
||||
RemoveCache( collection );
|
||||
CharacterCollection.Remove( characterName );
|
||||
_plugin.PlayerWatcher.RemovePlayerFromWatch( characterName );
|
||||
}
|
||||
|
||||
if( _plugin.Configuration.CharacterCollections.Remove( characterName ) )
|
||||
{
|
||||
_plugin.Configuration.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private bool LoadCurrentCollection( Configuration config )
|
||||
{
|
||||
if( Collections.TryGetValue( config.CurrentCollection, out var currentCollection ) )
|
||||
{
|
||||
CurrentCollection = currentCollection;
|
||||
AddCache( CurrentCollection );
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginLog.Error( $"Last choice of CurrentCollection {config.CurrentCollection} is not available, reset to Default." );
|
||||
CurrentCollection = Collections[ ModCollection.DefaultCollection ];
|
||||
config.CurrentCollection = ModCollection.DefaultCollection;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool LoadForcedCollection( Configuration config )
|
||||
{
|
||||
if( config.ForcedCollection == string.Empty )
|
||||
{
|
||||
ForcedCollection = ModCollection.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Collections.TryGetValue( config.ForcedCollection, out var forcedCollection ) )
|
||||
{
|
||||
ForcedCollection = forcedCollection;
|
||||
AddCache( ForcedCollection );
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginLog.Error( $"Last choice of ForcedCollection {config.ForcedCollection} is not available, reset to None." );
|
||||
ForcedCollection = ModCollection.Empty;
|
||||
config.ForcedCollection = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool LoadCharacterCollections( Configuration config )
|
||||
{
|
||||
var configChanged = false;
|
||||
foreach( var kvp in config.CharacterCollections.ToArray() )
|
||||
{
|
||||
_plugin.PlayerWatcher.AddPlayerToWatch( kvp.Key );
|
||||
if( kvp.Value == string.Empty )
|
||||
{
|
||||
CharacterCollection.Add( kvp.Key, ModCollection.Empty );
|
||||
}
|
||||
else if( Collections.TryGetValue( kvp.Value, out var charCollection ) )
|
||||
{
|
||||
AddCache( charCollection );
|
||||
CharacterCollection.Add( kvp.Key, charCollection );
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginLog.Error( $"Last choice of <{kvp.Key}>'s Collection {kvp.Value} is not available, reset to None." );
|
||||
CharacterCollection.Add( kvp.Key, ModCollection.Empty );
|
||||
config.CharacterCollections[ kvp.Key ] = string.Empty;
|
||||
configChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
return configChanged;
|
||||
}
|
||||
|
||||
private void LoadConfigCollections( Configuration config )
|
||||
{
|
||||
var configChanged = LoadCurrentCollection( config );
|
||||
configChanged |= LoadForcedCollection( config );
|
||||
configChanged |= LoadCharacterCollections( config );
|
||||
|
||||
if( configChanged )
|
||||
{
|
||||
config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCollections()
|
||||
{
|
||||
var collectionDir = ModCollection.CollectionDir( _plugin.PluginInterface! );
|
||||
if( collectionDir.Exists )
|
||||
{
|
||||
foreach( var file in collectionDir.EnumerateFiles( "*.json" ) )
|
||||
{
|
||||
var collection = ModCollection.LoadFromFile( file );
|
||||
if( collection != null )
|
||||
{
|
||||
if( file.Name != $"{collection.Name.RemoveInvalidPathSymbols()}.json" )
|
||||
{
|
||||
PluginLog.Warning( $"Collection {file.Name} does not correspond to {collection.Name}." );
|
||||
}
|
||||
|
||||
if( Collections.ContainsKey( collection.Name ) )
|
||||
{
|
||||
PluginLog.Warning( $"Duplicate collection found: {collection.Name} already exists." );
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.Add( collection.Name, collection );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !Collections.ContainsKey( ModCollection.DefaultCollection ) )
|
||||
{
|
||||
var defaultCollection = new ModCollection();
|
||||
SaveCollection( defaultCollection );
|
||||
Collections.Add( defaultCollection.Name, defaultCollection );
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveCollection( ModCollection collection )
|
||||
=> collection.Save( _plugin.PluginInterface! );
|
||||
}
|
||||
}
|
||||
|
|
@ -90,6 +90,9 @@ namespace Penumbra.Mods
|
|||
CalculateEffectiveFileList( modDirectory, true );
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
=> Cache = null;
|
||||
|
||||
public void UpdateSetting( ModData mod )
|
||||
{
|
||||
if( !Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
|
||||
|
|
@ -236,5 +239,7 @@ namespace Penumbra.Mods
|
|||
|
||||
public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath )
|
||||
=> Cache?.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
|
||||
public static readonly ModCollection Empty = new(){ Name = "" };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Mod;
|
||||
|
|
@ -9,132 +8,19 @@ using Penumbra.Util;
|
|||
|
||||
namespace Penumbra.Mods
|
||||
{
|
||||
// The ModManager handles the basic mods installed to the mod directory,
|
||||
// as well as all saved collections.
|
||||
// It also handles manual changes to mods that require changes in all collections,
|
||||
// updating the state of a mod from the filesystem,
|
||||
// and collection swapping.
|
||||
// The ModManager handles the basic mods installed to the mod directory.
|
||||
// It also contains the CollectionManager that handles all collections.
|
||||
public class ModManager
|
||||
{
|
||||
private readonly Plugin _plugin;
|
||||
public DirectoryInfo BasePath { get; private set; }
|
||||
|
||||
public Dictionary< string, ModData > Mods { get; } = new();
|
||||
public Dictionary< string, ModCollection > Collections { get; } = new();
|
||||
|
||||
public ModCollection CurrentCollection { get; private set; }
|
||||
public CollectionManager Collections { get; }
|
||||
|
||||
public ModManager( Plugin plugin )
|
||||
{
|
||||
_plugin = plugin;
|
||||
BasePath = new DirectoryInfo( plugin.Configuration!.ModDirectory );
|
||||
ReadCollections();
|
||||
CurrentCollection = Collections.Values.First();
|
||||
if( !SetCurrentCollection( plugin.Configuration!.CurrentCollection ) )
|
||||
{
|
||||
PluginLog.Debug( "Last choice of collection {Name} is not available, reset to Default.",
|
||||
plugin.Configuration!.CurrentCollection );
|
||||
|
||||
if( SetCurrentCollection( ModCollection.DefaultCollection ) )
|
||||
{
|
||||
PluginLog.Error( "Could not load any collection. Default collection unavailable." );
|
||||
CurrentCollection = new ModCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetCurrentCollection( string name )
|
||||
{
|
||||
if( Collections.TryGetValue( name, out var collection ) )
|
||||
{
|
||||
CurrentCollection = collection;
|
||||
if( CurrentCollection.Cache == null )
|
||||
{
|
||||
CurrentCollection.CreateCache( BasePath, Mods );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ReadCollections()
|
||||
{
|
||||
var collectionDir = ModCollection.CollectionDir( _plugin.PluginInterface! );
|
||||
if( collectionDir.Exists )
|
||||
{
|
||||
foreach( var file in collectionDir.EnumerateFiles( "*.json" ) )
|
||||
{
|
||||
var collection = ModCollection.LoadFromFile( file );
|
||||
if( collection != null )
|
||||
{
|
||||
if( file.Name != $"{collection.Name.RemoveInvalidPathSymbols()}.json" )
|
||||
{
|
||||
PluginLog.Warning( $"Collection {file.Name} does not correspond to {collection.Name}." );
|
||||
}
|
||||
|
||||
if( Collections.ContainsKey( collection.Name ) )
|
||||
{
|
||||
PluginLog.Warning( $"Duplicate collection found: {collection.Name} already exists." );
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.Add( collection.Name, collection );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !Collections.ContainsKey( ModCollection.DefaultCollection ) )
|
||||
{
|
||||
var defaultCollection = new ModCollection();
|
||||
SaveCollection( defaultCollection );
|
||||
Collections.Add( defaultCollection.Name, defaultCollection );
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveCollection( ModCollection collection )
|
||||
=> collection.Save( _plugin.PluginInterface! );
|
||||
|
||||
|
||||
public bool AddCollection( string name, Dictionary< string, ModSettings > settings )
|
||||
{
|
||||
var nameFixed = name.RemoveInvalidPathSymbols().ToLowerInvariant();
|
||||
if( Collections.Values.Any( c => c.Name.RemoveInvalidPathSymbols().ToLowerInvariant() == nameFixed ) )
|
||||
{
|
||||
PluginLog.Warning( $"The new collection {name} would lead to the same path as one that already exists." );
|
||||
return false;
|
||||
}
|
||||
|
||||
var newCollection = new ModCollection( name, settings );
|
||||
Collections.Add( name, newCollection );
|
||||
SaveCollection( newCollection );
|
||||
CurrentCollection = newCollection;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveCollection( string name )
|
||||
{
|
||||
if( name == ModCollection.DefaultCollection )
|
||||
{
|
||||
PluginLog.Error( "Can not remove the default collection." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Collections.TryGetValue( name, out var collection ) )
|
||||
{
|
||||
if( CurrentCollection == collection )
|
||||
{
|
||||
SetCurrentCollection( ModCollection.DefaultCollection );
|
||||
}
|
||||
|
||||
collection.Delete( _plugin.PluginInterface! );
|
||||
Collections.Remove( name );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
BasePath = new DirectoryInfo( plugin.Configuration.ModDirectory );
|
||||
Collections = new CollectionManager( plugin, this );
|
||||
}
|
||||
|
||||
public void DiscoverMods( DirectoryInfo basePath )
|
||||
|
|
@ -171,10 +57,7 @@ namespace Penumbra.Mods
|
|||
Mods.Add( modFolder.Name, mod );
|
||||
}
|
||||
|
||||
foreach( var collection in Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
collection.CreateCache( BasePath, Mods );
|
||||
}
|
||||
Collections.RecreateCaches();
|
||||
}
|
||||
|
||||
public void DeleteMod( DirectoryInfo modFolder )
|
||||
|
|
@ -192,10 +75,7 @@ namespace Penumbra.Mods
|
|||
}
|
||||
|
||||
Mods.Remove( modFolder.Name );
|
||||
foreach( var collection in Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
collection.Cache!.RemoveMod( modFolder );
|
||||
}
|
||||
Collections.RemoveModFromCaches( modFolder );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +93,7 @@ namespace Penumbra.Mods
|
|||
}
|
||||
|
||||
Mods.Add( modFolder.Name, mod );
|
||||
foreach( var collection in Collections.Values )
|
||||
foreach( var collection in Collections.Collections.Values )
|
||||
{
|
||||
collection.AddMod( mod );
|
||||
}
|
||||
|
|
@ -241,77 +121,62 @@ namespace Penumbra.Mods
|
|||
mod.Resources.MetaManipulations.SaveToFile( MetaCollection.FileName( mod.BasePath ) );
|
||||
}
|
||||
|
||||
foreach( var collection in Collections.Values )
|
||||
{
|
||||
if( metaChanges )
|
||||
{
|
||||
collection.UpdateSetting( mod );
|
||||
if( nameChange )
|
||||
{
|
||||
collection.Cache?.SortMods();
|
||||
}
|
||||
}
|
||||
|
||||
if( fileChanges.HasFlag( ResourceChange.Files )
|
||||
&& collection.Settings.TryGetValue( mod.BasePath.Name, out var settings )
|
||||
&& settings.Enabled )
|
||||
{
|
||||
collection.Cache?.CalculateEffectiveFileList();
|
||||
}
|
||||
|
||||
if( recomputeMeta )
|
||||
{
|
||||
collection.Cache?.UpdateMetaManipulations();
|
||||
}
|
||||
}
|
||||
Collections.UpdateCollections( mod, metaChanges, fileChanges, nameChange, recomputeMeta );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// private void FileSystemWatcherOnChanged( object sender, FileSystemEventArgs e )
|
||||
// {
|
||||
// #if DEBUG
|
||||
// PluginLog.Verbose( "file changed: {FullPath}", e.FullPath );
|
||||
// #endif
|
||||
//
|
||||
// if( _plugin.ImportInProgress )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if( _plugin.Configuration.DisableFileSystemNotifications )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var file = e.FullPath;
|
||||
//
|
||||
// if( !ResolvedFiles.Any( x => x.Value.FullName == file ) )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PluginLog.Log( "a loaded file has been modified - file: {FullPath}", file );
|
||||
// _plugin.GameUtils.ReloadPlayerResources();
|
||||
// }
|
||||
//
|
||||
// private void FileSystemPasta()
|
||||
// {
|
||||
// haha spaghet
|
||||
// _fileSystemWatcher?.Dispose();
|
||||
// _fileSystemWatcher = new FileSystemWatcher( _basePath.FullName )
|
||||
// {
|
||||
// NotifyFilter = NotifyFilters.LastWrite |
|
||||
// NotifyFilters.FileName |
|
||||
// NotifyFilters.DirectoryName,
|
||||
// IncludeSubdirectories = true,
|
||||
// EnableRaisingEvents = true
|
||||
// };
|
||||
//
|
||||
// _fileSystemWatcher.Changed += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Created += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Deleted += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Renamed += FileSystemWatcherOnChanged;
|
||||
// }
|
||||
public string? ResolveSwappedOrReplacementPath( GamePath gameResourcePath )
|
||||
{
|
||||
var ret = Collections.ActiveCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
ret ??= Collections.ForcedCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
return ret;
|
||||
}
|
||||
|
||||
// private void FileSystemWatcherOnChanged( object sender, FileSystemEventArgs e )
|
||||
// {
|
||||
// #if DEBUG
|
||||
// PluginLog.Verbose( "file changed: {FullPath}", e.FullPath );
|
||||
// #endif
|
||||
//
|
||||
// if( _plugin.ImportInProgress )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if( _plugin.Configuration.DisableFileSystemNotifications )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var file = e.FullPath;
|
||||
//
|
||||
// if( !ResolvedFiles.Any( x => x.Value.FullName == file ) )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PluginLog.Log( "a loaded file has been modified - file: {FullPath}", file );
|
||||
// _plugin.GameUtils.ReloadPlayerResources();
|
||||
// }
|
||||
//
|
||||
// private void FileSystemPasta()
|
||||
// {
|
||||
// haha spaghet
|
||||
// _fileSystemWatcher?.Dispose();
|
||||
// _fileSystemWatcher = new FileSystemWatcher( _basePath.FullName )
|
||||
// {
|
||||
// NotifyFilter = NotifyFilters.LastWrite |
|
||||
// NotifyFilters.FileName |
|
||||
// NotifyFilters.DirectoryName,
|
||||
// IncludeSubdirectories = true,
|
||||
// EnableRaisingEvents = true
|
||||
// };
|
||||
//
|
||||
// _fileSystemWatcher.Changed += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Created += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Deleted += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Renamed += FileSystemWatcherOnChanged;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ namespace Penumbra.Mods
|
|||
|
||||
mod.Meta.Name = newName;
|
||||
mod.SaveMeta();
|
||||
foreach( var collection in manager.Collections.Values.Where( c => c.Cache != null ) )
|
||||
foreach( var collection in manager.Collections.Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
collection.Cache!.SortMods();
|
||||
}
|
||||
|
|
@ -60,13 +60,13 @@ namespace Penumbra.Mods
|
|||
mod.MetaFile = ModData.MetaFileInfo( newDir );
|
||||
manager.UpdateMod( mod );
|
||||
|
||||
foreach( var collection in manager.Collections.Values )
|
||||
foreach( var collection in manager.Collections.Collections.Values )
|
||||
{
|
||||
if( collection.Settings.TryGetValue( oldBasePath.Name, out var settings ) )
|
||||
{
|
||||
collection.Settings[ newDir.Name ] = settings;
|
||||
collection.Settings.Remove( oldBasePath.Name );
|
||||
manager.SaveCollection( collection );
|
||||
manager.Collections.SaveCollection( collection );
|
||||
}
|
||||
|
||||
if( collection.Cache != null )
|
||||
|
|
@ -118,7 +118,7 @@ namespace Penumbra.Mods
|
|||
|
||||
mod.SaveMeta();
|
||||
|
||||
foreach( var collection in manager.Collections.Values )
|
||||
foreach( var collection in manager.Collections.Collections.Values )
|
||||
{
|
||||
if( !collection.Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
|
||||
{
|
||||
|
|
@ -131,7 +131,7 @@ namespace Penumbra.Mods
|
|||
}
|
||||
|
||||
settings.Settings.Remove( oldGroupName );
|
||||
manager.SaveCollection( collection );
|
||||
manager.Collections.SaveCollection( collection );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -154,7 +154,7 @@ namespace Penumbra.Mods
|
|||
return ( oldSetting & bitmaskFront ) | ( ( oldSetting & bitmaskBack ) >> 1 );
|
||||
}
|
||||
|
||||
foreach( var collection in manager.Collections.Values )
|
||||
foreach( var collection in manager.Collections.Collections.Values )
|
||||
{
|
||||
if( !collection.Settings.TryGetValue( mod.BasePath.Name, out var settings ) )
|
||||
{
|
||||
|
|
@ -176,7 +176,7 @@ namespace Penumbra.Mods
|
|||
if( newSetting != setting )
|
||||
{
|
||||
settings.Settings[ group.GroupName ] = newSetting;
|
||||
manager.SaveCollection( collection );
|
||||
manager.Collections.SaveCollection( collection );
|
||||
if( collection.Cache != null && settings.Enabled )
|
||||
{
|
||||
collection.CalculateEffectiveFileList( manager.BasePath, mod.Resources.MetaManipulations.Count > 0 );
|
||||
|
|
|
|||
|
|
@ -48,13 +48,13 @@ namespace Penumbra
|
|||
SoundShit.DisableStreaming();
|
||||
|
||||
var gameUtils = Service< GameResourceManagement >.Set( PluginInterface );
|
||||
PlayerWatcher = new PlayerWatcher( PluginInterface );
|
||||
Service< MetaDefaults >.Set( PluginInterface );
|
||||
var modManager = Service< ModManager >.Set( this );
|
||||
|
||||
modManager.DiscoverMods();
|
||||
|
||||
ActorRefresher = new ActorRefresher( PluginInterface, modManager );
|
||||
PlayerWatcher = new PlayerWatcher( PluginInterface );
|
||||
|
||||
ResourceLoader = new ResourceLoader( this );
|
||||
|
||||
|
|
@ -77,10 +77,16 @@ namespace Penumbra
|
|||
CreateWebServer();
|
||||
}
|
||||
|
||||
if( Configuration.EnableActorWatch )
|
||||
if( Configuration.EnableActorWatch && Configuration.IsEnabled )
|
||||
{
|
||||
PlayerWatcher.EnableActorWatch();
|
||||
}
|
||||
|
||||
PlayerWatcher.ActorChanged += a =>
|
||||
{
|
||||
PluginLog.Debug( "Triggered Redraw of {Actor}.", a.Name );
|
||||
ActorRefresher.RedrawActor( a, Redraw.OnlyWithSettings );
|
||||
};
|
||||
}
|
||||
|
||||
public void CreateWebServer()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
using Penumbra.Interop;
|
||||
|
|
@ -14,41 +16,76 @@ namespace Penumbra.UI
|
|||
{
|
||||
private class TabCollections
|
||||
{
|
||||
private readonly Selector _selector;
|
||||
private readonly ModManager _manager;
|
||||
private string[] _collectionNames = null!;
|
||||
private int _currentIndex = 0;
|
||||
private string _newCollectionName = string.Empty;
|
||||
private readonly Selector _selector;
|
||||
private readonly ModManager _manager;
|
||||
private string _collectionNames = null!;
|
||||
private string _collectionNamesWithNone = null!;
|
||||
private ModCollection[] _collections = null!;
|
||||
private int _currentCollectionIndex = 0;
|
||||
private int _currentForcedIndex = 0;
|
||||
private readonly Dictionary< string, int > _currentCharacterIndices = new();
|
||||
private string _newCollectionName = string.Empty;
|
||||
private string _newCharacterName = string.Empty;
|
||||
|
||||
private void UpdateNames()
|
||||
=> _collectionNames = _manager.Collections.Values.Select( c => c.Name ).ToArray();
|
||||
{
|
||||
_collections = _manager.Collections.Collections.Values.Prepend( ModCollection.Empty ).ToArray();
|
||||
_collectionNames = string.Join( "\0", _collections.Skip( 1 ).Select( c => c.Name ) );
|
||||
_collectionNamesWithNone = "None\0" + _collectionNames;
|
||||
UpdateIndices();
|
||||
}
|
||||
|
||||
|
||||
private int GetIndex( ModCollection collection )
|
||||
{
|
||||
var ret = _collections.IndexOf( c => c.Name == collection.Name );
|
||||
if( ret < 0 )
|
||||
{
|
||||
PluginLog.Error( $"Collection {collection.Name} is not found in collections." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void UpdateIndex()
|
||||
=> _currentCollectionIndex = GetIndex( _manager.Collections.CurrentCollection ) - 1;
|
||||
|
||||
private void UpdateForcedIndex()
|
||||
=> _currentForcedIndex = GetIndex( _manager.Collections.ForcedCollection );
|
||||
|
||||
private void UpdateCharacterIndices()
|
||||
{
|
||||
_currentIndex = _collectionNames.IndexOf( c => c == _manager.CurrentCollection.Name );
|
||||
if( _currentIndex < 0 )
|
||||
_currentCharacterIndices.Clear();
|
||||
foreach( var kvp in _manager.Collections.CharacterCollection )
|
||||
{
|
||||
PluginLog.Error( $"Current Collection {_manager.CurrentCollection.Name} is not found in collections." );
|
||||
_currentIndex = 0;
|
||||
_currentCharacterIndices[ kvp.Key ] = GetIndex( kvp.Value );
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIndices()
|
||||
{
|
||||
UpdateIndex();
|
||||
UpdateForcedIndex();
|
||||
UpdateCharacterIndices();
|
||||
}
|
||||
|
||||
public TabCollections( Selector selector )
|
||||
{
|
||||
_selector = selector;
|
||||
_manager = Service< ModManager >.Get();
|
||||
UpdateNames();
|
||||
UpdateIndex();
|
||||
}
|
||||
|
||||
|
||||
private void CreateNewCollection( Dictionary< string, ModSettings > settings )
|
||||
{
|
||||
_manager.AddCollection( _newCollectionName, settings );
|
||||
_manager.SetCurrentCollection( _newCollectionName );
|
||||
if( _manager.Collections.AddCollection( _newCollectionName, settings ) )
|
||||
{
|
||||
_manager.Collections.SetCurrentCollection( _manager.Collections.Collections[ _newCollectionName ] );
|
||||
UpdateNames();
|
||||
}
|
||||
|
||||
_newCollectionName = string.Empty;
|
||||
UpdateNames();
|
||||
UpdateIndex();
|
||||
}
|
||||
|
||||
private void DrawNewCollectionInput()
|
||||
|
|
@ -62,7 +99,6 @@ namespace Penumbra.UI
|
|||
ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f );
|
||||
}
|
||||
|
||||
|
||||
if( ImGui.Button( "Create New Empty Collection" ) && _newCollectionName.Length > 0 )
|
||||
{
|
||||
CreateNewCollection( new Dictionary< string, ModSettings >() );
|
||||
|
|
@ -71,7 +107,7 @@ namespace Penumbra.UI
|
|||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Duplicate Current Collection" ) && _newCollectionName.Length > 0 )
|
||||
{
|
||||
CreateNewCollection( _manager.CurrentCollection.Settings );
|
||||
CreateNewCollection( _manager.Collections.CurrentCollection.Settings );
|
||||
}
|
||||
|
||||
if( changedStyle )
|
||||
|
|
@ -79,18 +115,119 @@ namespace Penumbra.UI
|
|||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
if( _manager.Collections.Count > 1 )
|
||||
if( _manager.Collections.Collections.Count > 1
|
||||
&& _manager.Collections.CurrentCollection.Name != ModCollection.DefaultCollection )
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Delete Current Collection" ) )
|
||||
{
|
||||
_manager.RemoveCollection( _manager.CurrentCollection.Name );
|
||||
_manager.Collections.RemoveCollection( _manager.Collections.CurrentCollection.Name );
|
||||
UpdateNames();
|
||||
UpdateIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCurrentCollectionSelector()
|
||||
{
|
||||
var index = _currentCollectionIndex;
|
||||
if( !ImGui.Combo( "Current Collection", ref index, _collectionNames ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( index != _currentCollectionIndex )
|
||||
{
|
||||
_manager.Collections.SetCurrentCollection( _collections[ index + 1 ] );
|
||||
_currentCollectionIndex = index;
|
||||
_selector.ReloadSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawForcedCollectionSelector()
|
||||
{
|
||||
var index = _currentForcedIndex;
|
||||
if( ImGui.Combo( "##Forced Collection", ref index, _collectionNamesWithNone ) && index != _currentForcedIndex )
|
||||
{
|
||||
_manager.Collections.SetForcedCollection( _collections[ index ] );
|
||||
_currentForcedIndex = index;
|
||||
}
|
||||
|
||||
if( ImGui.IsItemHovered() )
|
||||
{
|
||||
ImGui.SetTooltip(
|
||||
"Mods in the forced collection are always loaded if not overwritten by anything in the current or character-based collection.\n"
|
||||
+ "Please avoid mixing meta-manipulating mods in Forced and other collections, as this will probably not work correctly." );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.Dummy( new Vector2( 24, 0 ) );
|
||||
ImGui.SameLine();
|
||||
ImGui.Text( "Forced Collection" );
|
||||
}
|
||||
|
||||
private void DrawNewCharacterCollection()
|
||||
{
|
||||
ImGui.InputTextWithHint( "##New Character", "New Character Name", ref _newCharacterName, 32 );
|
||||
|
||||
var changedStyle = false;
|
||||
if( _newCharacterName.Length == 0 )
|
||||
{
|
||||
changedStyle = true;
|
||||
ImGui.PushStyleVar( ImGuiStyleVar.Alpha, 0.5f );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Create New Character Collection" ) && _newCharacterName.Length > 0 )
|
||||
{
|
||||
_manager.Collections.CreateCharacterCollection( _newCharacterName );
|
||||
_currentCharacterIndices[ _newCharacterName ] = 0;
|
||||
}
|
||||
|
||||
if( changedStyle )
|
||||
{
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DrawCharacterCollectionSelectors()
|
||||
{
|
||||
if( !ImGui.BeginChild( "##CollectionChild", AutoFillSize, true ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawForcedCollectionSelector();
|
||||
|
||||
foreach( var name in _manager.Collections.CharacterCollection.Keys.ToArray() )
|
||||
{
|
||||
var idx = _currentCharacterIndices[ name ];
|
||||
var tmp = idx;
|
||||
if( ImGui.Combo( $"##{name}collection", ref tmp, _collectionNamesWithNone ) && idx != tmp )
|
||||
{
|
||||
_manager.Collections.SetCharacterCollection( name, _collections[ tmp ] );
|
||||
_currentCharacterIndices[ name ] = tmp;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont( UiBuilder.IconFont );
|
||||
|
||||
if( ImGui.Button( $"{FontAwesomeIcon.Trash.ToIconString()}##{name}" ) )
|
||||
{
|
||||
_manager.Collections.RemoveCharacterCollection( name );
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.Text( name );
|
||||
}
|
||||
|
||||
DrawNewCharacterCollection();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if( !ImGui.BeginTabItem( "Collections" ) )
|
||||
|
|
@ -98,20 +235,20 @@ namespace Penumbra.UI
|
|||
return;
|
||||
}
|
||||
|
||||
var index = _currentIndex;
|
||||
if( ImGui.Combo( "Current Collection", ref index, _collectionNames, _collectionNames.Length ) )
|
||||
if( !ImGui.BeginChild( "##CollectionHandling", new Vector2( -1, ImGui.GetTextLineHeightWithSpacing() * 6 ), true ) )
|
||||
{
|
||||
if( index != _currentIndex && _manager.SetCurrentCollection( _collectionNames[ index ] ) )
|
||||
{
|
||||
_currentIndex = index;
|
||||
_selector.ReloadSelection();
|
||||
var resourceManager = Service< GameResourceManagement >.Get();
|
||||
resourceManager.ReloadPlayerResources();
|
||||
}
|
||||
ImGui.EndTabItem();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.Dummy( new Vector2( 0, 5 ) );
|
||||
DrawCurrentCollectionSelector();
|
||||
|
||||
ImGui.Dummy( new Vector2( 0, 10 ) );
|
||||
DrawNewCollectionInput();
|
||||
ImGui.EndChild();
|
||||
|
||||
DrawCharacterCollectionSelectors();
|
||||
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace Penumbra.UI
|
|||
|
||||
if( ImGui.BeginTable( "##effective_changes", 3, flags, AutoFillSize ) )
|
||||
{
|
||||
var currentCollection = _modManager.CurrentCollection.Cache!;
|
||||
var currentCollection = _modManager.Collections.CurrentCollection.Cache!;
|
||||
foreach( var file in currentCollection.ResolvedFiles )
|
||||
{
|
||||
DrawFileLine( file.Value, file.Key );
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ namespace Penumbra.UI
|
|||
if( ImGui.InputInt( "Priority", ref priority, 0 ) && priority != Mod!.Settings.Priority )
|
||||
{
|
||||
Mod.Settings.Priority = priority;
|
||||
var collection = _modManager.CurrentCollection;
|
||||
var collection = _modManager.Collections.CurrentCollection;
|
||||
collection.Save( _base._plugin.PluginInterface! );
|
||||
collection.CalculateEffectiveFileList( _modManager.BasePath, Mod.Data.Resources.MetaManipulations.Count > 0 );
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ namespace Penumbra.UI
|
|||
if( ImGui.Checkbox( LabelModEnabled, ref enabled ) )
|
||||
{
|
||||
Mod.Settings.Enabled = enabled;
|
||||
var collection = _modManager.CurrentCollection;
|
||||
var collection = _modManager.Collections.CurrentCollection;
|
||||
collection.Save( _base._plugin.PluginInterface! );
|
||||
collection.CalculateEffectiveFileList( _modManager.BasePath, Mod.Data.Resources.MetaManipulations.Count > 0 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace Penumbra.UI
|
|||
private readonly ModManager _modManager;
|
||||
|
||||
private List< Mod.Mod >? Mods
|
||||
=> _modManager.CurrentCollection.Cache?.AvailableMods;
|
||||
=> _modManager.Collections.CurrentCollection.Cache?.AvailableMods;
|
||||
|
||||
public Mod.Mod? Mod { get; private set; }
|
||||
private int _index;
|
||||
|
|
@ -269,6 +269,7 @@ namespace Penumbra.UI
|
|||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_modManager.DeleteMod( Mod.Data.BasePath );
|
||||
ResetModNamesLower();
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,11 @@ namespace Penumbra.UI
|
|||
{
|
||||
_config.IsEnabled = enabled;
|
||||
_configChanged = true;
|
||||
_base._plugin.ActorRefresher.RedrawAll( );
|
||||
_base._plugin.ActorRefresher.RedrawAll( enabled ? Redraw.WithSettings : Redraw.WithoutSettings );
|
||||
if( _config.EnableActorWatch )
|
||||
{
|
||||
_base._plugin.PlayerWatcher.SetActorWatch( enabled );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue