mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-01 05:13:43 +01:00
tmp
This commit is contained in:
parent
48e442a9fd
commit
da73feacf4
59 changed files with 2115 additions and 3428 deletions
|
|
@ -275,7 +275,7 @@ public partial class ModCollection
|
|||
}
|
||||
}
|
||||
|
||||
private void OnModRemovedActive( bool meta, IEnumerable< ModSettings? > settings )
|
||||
private void OnModRemovedActive( bool meta, IEnumerable< ModSettings2? > settings )
|
||||
{
|
||||
foreach( var (collection, _) in this.Zip( settings ).Where( c => c.First.HasCache && c.Second?.Enabled == true ) )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public partial class ModCollection
|
|||
public delegate void CollectionChangeDelegate( Type type, ModCollection? oldCollection, ModCollection? newCollection,
|
||||
string? characterName = null );
|
||||
|
||||
private readonly Mod.Manager _modManager;
|
||||
private readonly Mod2.Manager _modManager;
|
||||
|
||||
// The empty collection is always available and always has index 0.
|
||||
// It can not be deleted or moved.
|
||||
|
|
@ -56,14 +56,15 @@ public partial class ModCollection
|
|||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public Manager( Mod.Manager manager )
|
||||
public Manager( Mod2.Manager manager )
|
||||
{
|
||||
_modManager = manager;
|
||||
|
||||
// The collection manager reacts to changes in mods by itself.
|
||||
_modManager.ModDiscoveryStarted += OnModDiscoveryStarted;
|
||||
_modManager.ModDiscoveryFinished += OnModDiscoveryFinished;
|
||||
_modManager.ModChange += OnModChanged;
|
||||
_modManager.ModOptionChanged += OnModOptionsChanged;
|
||||
_modManager.ModPathChanged += OnModPathChanged;
|
||||
CollectionChanged += SaveOnChange;
|
||||
ReadCollections();
|
||||
LoadCollections();
|
||||
|
|
@ -73,7 +74,8 @@ public partial class ModCollection
|
|||
{
|
||||
_modManager.ModDiscoveryStarted -= OnModDiscoveryStarted;
|
||||
_modManager.ModDiscoveryFinished -= OnModDiscoveryFinished;
|
||||
_modManager.ModChange -= OnModChanged;
|
||||
_modManager.ModOptionChanged -= OnModOptionsChanged;
|
||||
_modManager.ModPathChanged -= OnModPathChanged;
|
||||
}
|
||||
|
||||
// Add a new collection of the given name.
|
||||
|
|
@ -171,42 +173,64 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
|
||||
// A changed mod forces changes for all collections, active and inactive.
|
||||
private void OnModChanged( Mod.ChangeType type, Mod mod )
|
||||
// A changed mod path forces changes for all collections, active and inactive.
|
||||
private void OnModPathChanged( ModPathChangeType type, Mod2 mod, DirectoryInfo? oldDirectory,
|
||||
DirectoryInfo? newDirectory )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case Mod.ChangeType.Added:
|
||||
case ModPathChangeType.Added:
|
||||
foreach( var collection in this )
|
||||
{
|
||||
collection.AddMod( mod );
|
||||
}
|
||||
|
||||
OnModAddedActive( mod.Resources.MetaManipulations.Count > 0 );
|
||||
OnModAddedActive( mod.TotalManipulations > 0 );
|
||||
break;
|
||||
case Mod.ChangeType.Removed:
|
||||
var settings = new List< ModSettings? >( _collections.Count );
|
||||
case ModPathChangeType.Deleted:
|
||||
var settings = new List< ModSettings2? >( _collections.Count );
|
||||
foreach( var collection in this )
|
||||
{
|
||||
settings.Add( collection[ mod.Index ].Settings );
|
||||
collection.RemoveMod( mod, mod.Index );
|
||||
}
|
||||
|
||||
OnModRemovedActive( mod.Resources.MetaManipulations.Count > 0, settings );
|
||||
OnModRemovedActive( mod.TotalManipulations > 0, settings );
|
||||
break;
|
||||
case Mod.ChangeType.Changed:
|
||||
foreach( var collection in this.Where(
|
||||
collection => collection.Settings[ mod.Index ]?.FixInvalidSettings( mod.Meta ) ?? false ) )
|
||||
case ModPathChangeType.Moved:
|
||||
foreach( var collection in this.Where( collection => collection.Settings[ mod.Index ] != null ) )
|
||||
{
|
||||
collection.Save();
|
||||
}
|
||||
|
||||
OnModChangedActive( mod.Resources.MetaManipulations.Count > 0, mod.Index );
|
||||
OnModChangedActive( mod.TotalManipulations > 0, mod.Index );
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException( nameof( type ), type, null );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnModOptionsChanged( ModOptionChangeType type, Mod2 mod, int groupIdx, int optionIdx )
|
||||
{
|
||||
if( type == ModOptionChangeType.DisplayChange )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
switch( type )
|
||||
{
|
||||
case ModOptionChangeType.GroupRenamed:
|
||||
case ModOptionChangeType.GroupAdded:
|
||||
case ModOptionChangeType.GroupDeleted:
|
||||
case ModOptionChangeType.PriorityChanged:
|
||||
case ModOptionChangeType.OptionAdded:
|
||||
case ModOptionChangeType.OptionDeleted:
|
||||
case ModOptionChangeType.OptionChanged:
|
||||
default: throw new ArgumentOutOfRangeException( nameof( type ), type, null );
|
||||
}
|
||||
}
|
||||
|
||||
// Add the collection with the default name if it does not exist.
|
||||
// It should always be ensured that it exists, otherwise it will be created.
|
||||
// This can also not be deleted, so there are always at least the empty and a collection with default name.
|
||||
|
|
|
|||
168
Penumbra/Collections/ModCollection.Cache.Access.cs
Normal file
168
Penumbra/Collections/ModCollection.Cache.Access.cs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Dalamud.Logging;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Meta.Manager;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
public partial class ModCollection
|
||||
{
|
||||
// Only active collections need to have a cache.
|
||||
private Cache? _cache;
|
||||
|
||||
public bool HasCache
|
||||
=> _cache != null;
|
||||
|
||||
// Only create, do not update.
|
||||
public void CreateCache( bool isDefault )
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
CalculateEffectiveFileList( true, isDefault );
|
||||
}
|
||||
}
|
||||
|
||||
// Force an update with metadata for this cache.
|
||||
public void ForceCacheUpdate( bool isDefault )
|
||||
=> CalculateEffectiveFileList( true, isDefault );
|
||||
|
||||
|
||||
// Clear the current cache.
|
||||
public void ClearCache()
|
||||
{
|
||||
_cache?.Dispose();
|
||||
_cache = null;
|
||||
}
|
||||
|
||||
|
||||
public FullPath? ResolvePath( Utf8GamePath path )
|
||||
=> _cache?.ResolvePath( path );
|
||||
|
||||
// Force a file to be resolved to a specific path regardless of conflicts.
|
||||
internal void ForceFile( Utf8GamePath path, FullPath fullPath )
|
||||
=> _cache!.ResolvedFiles[ path ] = fullPath;
|
||||
|
||||
// Force a file resolve to be removed.
|
||||
internal void RemoveFile( Utf8GamePath path )
|
||||
=> _cache!.ResolvedFiles.Remove( path );
|
||||
|
||||
// Obtain data from the cache.
|
||||
internal MetaManager? MetaCache
|
||||
=> _cache?.MetaManipulations;
|
||||
|
||||
internal IReadOnlyDictionary< Utf8GamePath, FullPath > ResolvedFiles
|
||||
=> _cache?.ResolvedFiles ?? new Dictionary< Utf8GamePath, FullPath >();
|
||||
|
||||
internal IReadOnlySet< FullPath > MissingFiles
|
||||
=> _cache?.MissingFiles ?? new HashSet< FullPath >();
|
||||
|
||||
internal IReadOnlyDictionary< string, object? > ChangedItems
|
||||
=> _cache?.ChangedItems ?? new Dictionary< string, object? >();
|
||||
|
||||
internal IReadOnlyList< ConflictCache.Conflict > Conflicts
|
||||
=> _cache?.Conflicts.Conflicts ?? Array.Empty< ConflictCache.Conflict >();
|
||||
|
||||
internal IEnumerable< ConflictCache.Conflict > ModConflicts( int modIdx )
|
||||
=> _cache?.Conflicts.ModConflicts( modIdx ) ?? Array.Empty< ConflictCache.Conflict >();
|
||||
|
||||
// Update the effective file list for the given cache.
|
||||
// Creates a cache if necessary.
|
||||
public void CalculateEffectiveFileList( bool withMetaManipulations, bool reloadDefault )
|
||||
{
|
||||
// Skip the empty collection.
|
||||
if( Index == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{ReloadDefault}]", Name,
|
||||
withMetaManipulations, reloadDefault );
|
||||
_cache ??= new Cache( this );
|
||||
_cache.CalculateEffectiveFileList( withMetaManipulations );
|
||||
if( reloadDefault )
|
||||
{
|
||||
SetFiles();
|
||||
Penumbra.ResidentResources.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Set Metadata files.
|
||||
[Conditional( "USE_EQP" )]
|
||||
public void SetEqpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEqp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Eqp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_EQDP" )]
|
||||
public void SetEqdpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEqdp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Eqdp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_GMP" )]
|
||||
public void SetGmpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerGmp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Gmp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_EST" )]
|
||||
public void SetEstFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEst.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Est.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_CMP" )]
|
||||
public void SetCmpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerCmp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Cmp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
Penumbra.CharacterUtility.ResetAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.SetFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,193 +1,24 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Dalamud.Logging;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Meta.Manager;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
public partial class ModCollection
|
||||
{
|
||||
// Only active collections need to have a cache.
|
||||
private Cache? _cache;
|
||||
|
||||
public bool HasCache
|
||||
=> _cache != null;
|
||||
|
||||
// Only create, do not update.
|
||||
public void CreateCache( bool isDefault )
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
CalculateEffectiveFileList( true, isDefault );
|
||||
}
|
||||
}
|
||||
|
||||
// Force an update with metadata for this cache.
|
||||
public void ForceCacheUpdate( bool isDefault )
|
||||
=> CalculateEffectiveFileList( true, isDefault );
|
||||
|
||||
|
||||
// Clear the current cache.
|
||||
public void ClearCache()
|
||||
{
|
||||
_cache?.Dispose();
|
||||
_cache = null;
|
||||
}
|
||||
|
||||
|
||||
public FullPath? ResolvePath( Utf8GamePath path )
|
||||
=> _cache?.ResolvePath( path );
|
||||
|
||||
// Force a file to be resolved to a specific path regardless of conflicts.
|
||||
internal void ForceFile( Utf8GamePath path, FullPath fullPath )
|
||||
=> _cache!.ResolvedFiles[ path ] = fullPath;
|
||||
|
||||
// Force a file resolve to be removed.
|
||||
internal void RemoveFile( Utf8GamePath path )
|
||||
=> _cache!.ResolvedFiles.Remove( path );
|
||||
|
||||
// Obtain data from the cache.
|
||||
internal MetaManager? MetaCache
|
||||
=> _cache?.MetaManipulations;
|
||||
|
||||
internal IReadOnlyDictionary< Utf8GamePath, FullPath > ResolvedFiles
|
||||
=> _cache?.ResolvedFiles ?? new Dictionary< Utf8GamePath, FullPath >();
|
||||
|
||||
internal IReadOnlySet< FullPath > MissingFiles
|
||||
=> _cache?.MissingFiles ?? new HashSet< FullPath >();
|
||||
|
||||
internal IReadOnlyDictionary< string, object? > ChangedItems
|
||||
=> _cache?.ChangedItems ?? new Dictionary< string, object? >();
|
||||
|
||||
internal IReadOnlyList< ConflictCache.Conflict > Conflicts
|
||||
=> _cache?.Conflicts.Conflicts ?? Array.Empty< ConflictCache.Conflict >();
|
||||
|
||||
internal IEnumerable< ConflictCache.Conflict > ModConflicts( int modIdx )
|
||||
=> _cache?.Conflicts.ModConflicts( modIdx ) ?? Array.Empty< ConflictCache.Conflict >();
|
||||
|
||||
// Update the effective file list for the given cache.
|
||||
// Creates a cache if necessary.
|
||||
public void CalculateEffectiveFileList( bool withMetaManipulations, bool reloadDefault )
|
||||
{
|
||||
// Skip the empty collection.
|
||||
if( Index == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{ReloadDefault}]", Name,
|
||||
withMetaManipulations, reloadDefault );
|
||||
_cache ??= new Cache( this );
|
||||
_cache.CalculateEffectiveFileList();
|
||||
if( withMetaManipulations )
|
||||
{
|
||||
_cache.UpdateMetaManipulations();
|
||||
if( reloadDefault )
|
||||
{
|
||||
SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
if( reloadDefault )
|
||||
{
|
||||
Penumbra.ResidentResources.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Set Metadata files.
|
||||
[Conditional( "USE_EQP" )]
|
||||
public void SetEqpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEqp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Eqp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_EQDP" )]
|
||||
public void SetEqdpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEqdp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Eqdp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_GMP" )]
|
||||
public void SetGmpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerGmp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Gmp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_EST" )]
|
||||
public void SetEstFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerEst.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Est.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional( "USE_CMP" )]
|
||||
public void SetCmpFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
MetaManager.MetaManagerCmp.ResetFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.Cmp.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFiles()
|
||||
{
|
||||
if( _cache == null )
|
||||
{
|
||||
Penumbra.CharacterUtility.ResetAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.MetaManipulations.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The ModCollectionCache contains all required temporary data to use a collection.
|
||||
// The Cache contains all required temporary data to use a collection.
|
||||
// It will only be setup if a collection gets activated in any way.
|
||||
private class Cache : IDisposable
|
||||
{
|
||||
// Shared caches to avoid allocations.
|
||||
private static readonly BitArray FileSeen = new(256);
|
||||
private static readonly Dictionary< Utf8GamePath, int > RegisteredFiles = new(256);
|
||||
private static readonly List< ModSettings? > ResolvedSettings = new(128);
|
||||
private static readonly Dictionary< Utf8GamePath, FileRegister > RegisteredFiles = new(1024);
|
||||
private static readonly Dictionary< MetaManipulation, FileRegister > RegisteredManipulations = new(1024);
|
||||
private static readonly List< ModSettings2? > ResolvedSettings = new(128);
|
||||
|
||||
private readonly ModCollection _collection;
|
||||
private readonly SortedList< string, object? > _changedItems = new();
|
||||
|
|
@ -221,7 +52,24 @@ public partial class ModCollection
|
|||
_collection.InheritanceChanged -= OnInheritanceChange;
|
||||
}
|
||||
|
||||
private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, string? optionName, bool _ )
|
||||
// Resolve a given game path according to this collection.
|
||||
public FullPath? ResolvePath( Utf8GamePath gameResourcePath )
|
||||
{
|
||||
if( !ResolvedFiles.TryGetValue( gameResourcePath, out var candidate ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( candidate.InternalName.Length > Utf8GamePath.MaxGamePathLength
|
||||
|| candidate.IsRooted && !candidate.Exists )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
|
||||
{
|
||||
// Recompute the file list if it was not just a non-conflicting priority change
|
||||
// or a setting change for a disabled mod.
|
||||
|
|
@ -232,7 +80,7 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
var hasMeta = type is ModSettingChange.MultiEnableState or ModSettingChange.MultiInheritance
|
||||
|| Penumbra.ModManager[ modIdx ].Resources.MetaManipulations.Count > 0;
|
||||
|| Penumbra.ModManager[ modIdx ].AllManipulations.Any();
|
||||
_collection.CalculateEffectiveFileList( hasMeta, Penumbra.CollectionManager.Default == _collection );
|
||||
}
|
||||
|
||||
|
|
@ -241,22 +89,6 @@ public partial class ModCollection
|
|||
private void OnInheritanceChange( bool _ )
|
||||
=> _collection.CalculateEffectiveFileList( true, true );
|
||||
|
||||
// Reset the shared file-seen cache.
|
||||
private static void ResetFileSeen( int size )
|
||||
{
|
||||
if( size < FileSeen.Length )
|
||||
{
|
||||
FileSeen.Length = size;
|
||||
FileSeen.SetAll( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSeen.SetAll( false );
|
||||
FileSeen.Length = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clear all local and global caches to prepare for recomputation.
|
||||
private void ClearStorageAndPrepare()
|
||||
{
|
||||
|
|
@ -270,21 +102,27 @@ public partial class ModCollection
|
|||
ResolvedSettings.AddRange( _collection.ActualSettings );
|
||||
}
|
||||
|
||||
public void CalculateEffectiveFileList()
|
||||
// Recalculate all file changes from current settings. Include all fixed custom redirects.
|
||||
// Recalculate meta manipulations only if withManipulations is true.
|
||||
public void CalculateEffectiveFileList( bool withManipulations )
|
||||
{
|
||||
ClearStorageAndPrepare();
|
||||
if( withManipulations )
|
||||
{
|
||||
RegisteredManipulations.Clear();
|
||||
MetaManipulations.Reset();
|
||||
}
|
||||
|
||||
AddCustomRedirects();
|
||||
for( var i = 0; i < Penumbra.ModManager.Count; ++i )
|
||||
{
|
||||
if( ResolvedSettings[ i ]?.Enabled == true )
|
||||
{
|
||||
AddFiles( i );
|
||||
AddSwaps( i );
|
||||
}
|
||||
AddMod( i, withManipulations );
|
||||
}
|
||||
|
||||
AddMetaFiles();
|
||||
}
|
||||
|
||||
// Identify and record all manipulated objects for this entire collection.
|
||||
private void SetChangedItems()
|
||||
{
|
||||
if( _changedItems.Count > 0 || ResolvedFiles.Count + MetaManipulations.Count == 0 )
|
||||
|
|
@ -309,228 +147,185 @@ public partial class ModCollection
|
|||
}
|
||||
}
|
||||
|
||||
private void AddFiles( int idx )
|
||||
// Add a specific file redirection, handling potential conflicts.
|
||||
// For different mods, higher mod priority takes precedence before option group priority,
|
||||
// which takes precedence before option priority, which takes precedence before ordering.
|
||||
// Inside the same mod, conflicts are not recorded.
|
||||
private void AddFile( Utf8GamePath path, FullPath file, FileRegister priority )
|
||||
{
|
||||
var mod = Penumbra.ModManager.Mods[ idx ];
|
||||
ResetFileSeen( mod.Resources.ModFiles.Count );
|
||||
// Iterate in reverse so that later groups take precedence before earlier ones.
|
||||
// TODO: add group priorities.
|
||||
foreach( var group in mod.Meta.Groups.Values.Reverse() )
|
||||
if( RegisteredFiles.TryGetValue( path, out var register ) )
|
||||
{
|
||||
switch( group.SelectionType )
|
||||
if( register.SameMod( priority, out var less ) )
|
||||
{
|
||||
case SelectType.Single:
|
||||
AddFilesForSingle( group, mod, idx );
|
||||
break;
|
||||
case SelectType.Multi:
|
||||
AddFilesForMulti( group, mod, idx );
|
||||
break;
|
||||
default: throw new InvalidEnumArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
AddRemainingFiles( mod, idx );
|
||||
}
|
||||
|
||||
// If audio streaming is not disabled, replacing .scd files crashes the game,
|
||||
// so only add those files if it is disabled.
|
||||
private static bool FilterFile( Utf8GamePath gamePath )
|
||||
=> !Penumbra.Config.DisableSoundStreaming
|
||||
&& gamePath.Path.EndsWith( '.', 's', 'c', 'd' );
|
||||
|
||||
private void AddFile( int modIdx, Utf8GamePath gamePath, FullPath file )
|
||||
{
|
||||
if( FilterFile( gamePath ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !RegisteredFiles.TryGetValue( gamePath, out var oldModIdx ) )
|
||||
{
|
||||
// No current conflict, just add.
|
||||
RegisteredFiles.Add( gamePath, modIdx );
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conflict, check which mod has higher priority, replace if necessary, add conflict.
|
||||
var priority = ResolvedSettings[ modIdx ]!.Priority;
|
||||
var oldPriority = ResolvedSettings[ oldModIdx ]!.Priority;
|
||||
Conflicts.AddConflict( oldModIdx, modIdx, oldPriority, priority, gamePath );
|
||||
if( priority > oldPriority )
|
||||
{
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
RegisteredFiles[ gamePath ] = modIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMissingFile( FullPath file )
|
||||
{
|
||||
switch( file.Extension.ToLowerInvariant() )
|
||||
{
|
||||
// We do not care for those file types
|
||||
case ".scp" when !Penumbra.Config.DisableSoundStreaming:
|
||||
case ".meta":
|
||||
case ".rgsp":
|
||||
return;
|
||||
default:
|
||||
MissingFiles.Add( file );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPathsForOption( Option option, Mod mod, int modIdx, bool enabled )
|
||||
{
|
||||
foreach( var (file, paths) in option.OptionFiles )
|
||||
{
|
||||
// TODO: complete rework of options.
|
||||
var fullPath = new FullPath( mod.BasePath, file );
|
||||
var idx = mod.Resources.ModFiles.IndexOf( f => f.Equals( fullPath ) );
|
||||
if( idx < 0 )
|
||||
{
|
||||
AddMissingFile( fullPath );
|
||||
continue;
|
||||
}
|
||||
|
||||
var registeredFile = mod.Resources.ModFiles[ idx ];
|
||||
if( !registeredFile.Exists )
|
||||
{
|
||||
AddMissingFile( registeredFile );
|
||||
continue;
|
||||
}
|
||||
|
||||
FileSeen.Set( idx, true );
|
||||
if( enabled )
|
||||
{
|
||||
foreach( var path in paths )
|
||||
Conflicts.AddConflict( register.ModIdx, priority.ModIdx, register.ModPriority, priority.ModPriority, path );
|
||||
if( less )
|
||||
{
|
||||
AddFile( modIdx, path, registeredFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForSingle( OptionGroup singleGroup, Mod mod, int modIdx )
|
||||
{
|
||||
Debug.Assert( singleGroup.SelectionType == SelectType.Single );
|
||||
var settings = ResolvedSettings[ modIdx ]!;
|
||||
if( !settings.Settings.TryGetValue( singleGroup.GroupName, out var setting ) )
|
||||
{
|
||||
setting = 0;
|
||||
}
|
||||
|
||||
for( var i = 0; i < singleGroup.Options.Count; ++i )
|
||||
{
|
||||
AddPathsForOption( singleGroup.Options[ i ], mod, modIdx, setting == i );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFilesForMulti( OptionGroup multiGroup, Mod mod, int modIdx )
|
||||
{
|
||||
Debug.Assert( multiGroup.SelectionType == SelectType.Multi );
|
||||
var settings = ResolvedSettings[ modIdx ]!;
|
||||
if( !settings.Settings.TryGetValue( multiGroup.GroupName, out var setting ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Also iterate options in reverse so that later options take precedence before earlier ones.
|
||||
for( var i = multiGroup.Options.Count - 1; i >= 0; --i )
|
||||
{
|
||||
AddPathsForOption( multiGroup.Options[ i ], mod, modIdx, ( setting & ( 1 << i ) ) != 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRemainingFiles( Mod mod, int modIdx )
|
||||
{
|
||||
for( var i = 0; i < mod.Resources.ModFiles.Count; ++i )
|
||||
{
|
||||
if( FileSeen.Get( i ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = mod.Resources.ModFiles[ i ];
|
||||
if( file.Exists )
|
||||
{
|
||||
if( file.ToGamePath( mod.BasePath, out var gamePath ) )
|
||||
{
|
||||
AddFile( modIdx, gamePath, file );
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginLog.Warning( $"Could not convert {file} in {mod.BasePath.FullName} to GamePath." );
|
||||
RegisteredFiles[ path ] = priority;
|
||||
ResolvedFiles[ path ] = file;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MissingFiles.Add( file );
|
||||
// File seen before in the same mod:
|
||||
// use higher priority or earlier recurrences in case of same priority.
|
||||
// Do not add conflicts.
|
||||
if( less )
|
||||
{
|
||||
RegisteredFiles[ path ] = priority;
|
||||
ResolvedFiles[ path ] = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // File not seen before, just add it.
|
||||
{
|
||||
RegisteredFiles.Add( path, priority );
|
||||
ResolvedFiles.Add( path, file );
|
||||
}
|
||||
}
|
||||
|
||||
// Add a specific manipulation, handling potential conflicts.
|
||||
// For different mods, higher mod priority takes precedence before option group priority,
|
||||
// which takes precedence before option priority, which takes precedence before ordering.
|
||||
// Inside the same mod, conflicts are not recorded.
|
||||
private void AddManipulation( MetaManipulation manip, FileRegister priority )
|
||||
{
|
||||
if( RegisteredManipulations.TryGetValue( manip, out var register ) )
|
||||
{
|
||||
if( register.SameMod( priority, out var less ) )
|
||||
{
|
||||
Conflicts.AddConflict( register.ModIdx, priority.ModIdx, register.ModPriority, priority.ModPriority, manip );
|
||||
if( less )
|
||||
{
|
||||
RegisteredManipulations[ manip ] = priority;
|
||||
MetaManipulations.ApplyMod( manip, priority.ModIdx );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Manipulation seen before in the same mod:
|
||||
// use higher priority or earlier occurrences in case of same priority.
|
||||
// Do not add conflicts.
|
||||
if( less )
|
||||
{
|
||||
RegisteredManipulations[ manip ] = priority;
|
||||
MetaManipulations.ApplyMod( manip, priority.ModIdx );
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Manipulation not seen before, just add it.
|
||||
{
|
||||
RegisteredManipulations[ manip ] = priority;
|
||||
MetaManipulations.ApplyMod( manip, priority.ModIdx );
|
||||
}
|
||||
}
|
||||
|
||||
// Add all files and possibly manipulations of a specific submod with the given priorities.
|
||||
private void AddSubMod( ISubMod mod, FileRegister priority, bool withManipulations )
|
||||
{
|
||||
foreach( var (path, file) in mod.Files.Concat( mod.FileSwaps ) )
|
||||
{
|
||||
// Skip all filtered files
|
||||
if( Mod2.FilterFile( path ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddFile( path, file, priority );
|
||||
}
|
||||
|
||||
if( withManipulations )
|
||||
{
|
||||
foreach( var manip in mod.Manipulations )
|
||||
{
|
||||
AddManipulation( manip, priority );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all files and possibly manipulations of a given mod according to its settings in this collection.
|
||||
private void AddMod( int modIdx, bool withManipulations )
|
||||
{
|
||||
var settings = ResolvedSettings[ modIdx ];
|
||||
if( settings is not { Enabled: true } )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mod = Penumbra.ModManager.Mods[ modIdx ];
|
||||
AddSubMod( mod.Default, new FileRegister( modIdx, settings.Priority, 0, 0 ), withManipulations );
|
||||
for( var idx = 0; idx < mod.Groups.Count; ++idx )
|
||||
{
|
||||
var config = settings.Settings[ idx ];
|
||||
var group = mod.Groups[ idx ];
|
||||
switch( group.Type )
|
||||
{
|
||||
case SelectType.Single:
|
||||
var singlePriority = new FileRegister( modIdx, settings.Priority, group.Priority, group.Priority );
|
||||
AddSubMod( group[ ( int )config ], singlePriority, withManipulations );
|
||||
break;
|
||||
case SelectType.Multi:
|
||||
{
|
||||
for( var optionIdx = 0; optionIdx < group.Count; ++optionIdx )
|
||||
{
|
||||
if( ( ( 1 << optionIdx ) & config ) != 0 )
|
||||
{
|
||||
var priority = new FileRegister( modIdx, settings.Priority, group.Priority, group.OptionPriority( optionIdx ) );
|
||||
AddSubMod( group[ optionIdx ], priority, withManipulations );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all necessary meta file redirects.
|
||||
private void AddMetaFiles()
|
||||
=> MetaManipulations.Imc.SetFiles();
|
||||
|
||||
private void AddSwaps( int modIdx )
|
||||
// Add all API redirects.
|
||||
private void AddCustomRedirects()
|
||||
{
|
||||
var mod = Penumbra.ModManager.Mods[ modIdx ];
|
||||
foreach( var (gamePath, swapPath) in mod.Meta.FileSwaps.Where( kvp => !FilterFile( kvp.Key ) ) )
|
||||
Penumbra.Redirects.Apply( ResolvedFiles );
|
||||
foreach( var gamePath in ResolvedFiles.Keys )
|
||||
{
|
||||
AddFile( modIdx, gamePath, swapPath );
|
||||
RegisteredFiles.Add( gamePath, new FileRegister( -1, int.MaxValue, 0, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void AddManipulations( int modIdx )
|
||||
|
||||
// Struct to keep track of all priorities involved in a mod and register and compare accordingly.
|
||||
private readonly record struct FileRegister( int ModIdx, int ModPriority, int GroupPriority, int OptionPriority )
|
||||
{
|
||||
var mod = Penumbra.ModManager.Mods[ modIdx ];
|
||||
foreach( var manip in mod.Resources.MetaManipulations.GetManipulationsForConfig( ResolvedSettings[ modIdx ]!, mod.Meta ) )
|
||||
public readonly int ModIdx = ModIdx;
|
||||
public readonly int ModPriority = ModPriority;
|
||||
public readonly int GroupPriority = GroupPriority;
|
||||
public readonly int OptionPriority = OptionPriority;
|
||||
|
||||
public bool SameMod( FileRegister other, out bool less )
|
||||
{
|
||||
if( !MetaManipulations.TryGetValue( manip, out var oldModIdx ) )
|
||||
if( ModIdx != other.ModIdx )
|
||||
{
|
||||
MetaManipulations.ApplyMod( manip, modIdx );
|
||||
less = ModPriority < other.ModPriority;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( GroupPriority < other.GroupPriority )
|
||||
{
|
||||
less = true;
|
||||
}
|
||||
else if( GroupPriority == other.GroupPriority )
|
||||
{
|
||||
less = OptionPriority < other.OptionPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
var priority = ResolvedSettings[ modIdx ]!.Priority;
|
||||
var oldPriority = ResolvedSettings[ oldModIdx ]!.Priority;
|
||||
Conflicts.AddConflict( oldModIdx, modIdx, oldPriority, priority, manip );
|
||||
if( priority > oldPriority )
|
||||
{
|
||||
MetaManipulations.ApplyMod( manip, modIdx );
|
||||
}
|
||||
less = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMetaManipulations()
|
||||
{
|
||||
MetaManipulations.Reset();
|
||||
Conflicts.ClearMetaConflicts();
|
||||
|
||||
foreach( var mod in Penumbra.ModManager.Mods.Zip( ResolvedSettings )
|
||||
.Select( ( m, i ) => ( m.First, m.Second, i ) )
|
||||
.Where( m => m.Second?.Enabled == true && m.First.Resources.MetaManipulations.Count > 0 ) )
|
||||
{
|
||||
AddManipulations( mod.i );
|
||||
}
|
||||
}
|
||||
|
||||
public FullPath? ResolvePath( Utf8GamePath gameResourcePath )
|
||||
{
|
||||
if( !ResolvedFiles.TryGetValue( gameResourcePath, out var candidate ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( candidate.InternalName.Length > Utf8GamePath.MaxGamePathLength
|
||||
|| candidate.IsRooted && !candidate.Exists )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ public partial class ModCollection
|
|||
{
|
||||
// If the change type is a bool, oldValue will be 1 for true and 0 for false.
|
||||
// optionName will only be set for type == Setting.
|
||||
public delegate void ModSettingChangeDelegate( ModSettingChange type, int modIdx, int oldValue, string? optionName, bool inherited );
|
||||
public delegate void ModSettingChangeDelegate( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited );
|
||||
public event ModSettingChangeDelegate ModSettingChanged;
|
||||
|
||||
// Enable or disable the mod inheritance of mod idx.
|
||||
|
|
@ -28,7 +28,7 @@ public partial class ModCollection
|
|||
{
|
||||
if( FixInheritance( idx, inherit ) )
|
||||
{
|
||||
ModSettingChanged.Invoke( ModSettingChange.Inheritance, idx, inherit ? 0 : 1, null, false );
|
||||
ModSettingChanged.Invoke( ModSettingChange.Inheritance, idx, inherit ? 0 : 1, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,22 +41,22 @@ public partial class ModCollection
|
|||
{
|
||||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.Enabled = newValue;
|
||||
ModSettingChanged.Invoke( ModSettingChange.EnableState, idx, inheritance ? -1 : newValue ? 0 : 1, null, false );
|
||||
ModSettingChanged.Invoke( ModSettingChange.EnableState, idx, inheritance ? -1 : newValue ? 0 : 1, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
// Enable or disable the mod inheritance of every mod in mods.
|
||||
public void SetMultipleModInheritances( IEnumerable< Mod > mods, bool inherit )
|
||||
public void SetMultipleModInheritances( IEnumerable< Mod2 > mods, bool inherit )
|
||||
{
|
||||
if( mods.Aggregate( false, ( current, mod ) => current | FixInheritance( mod.Index, inherit ) ) )
|
||||
{
|
||||
ModSettingChanged.Invoke( ModSettingChange.MultiInheritance, -1, -1, null, false );
|
||||
ModSettingChanged.Invoke( ModSettingChange.MultiInheritance, -1, -1, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
// Set the enabled state of every mod in mods to the new value.
|
||||
// If the mod is currently inherited, stop the inheritance.
|
||||
public void SetMultipleModStates( IEnumerable< Mod > mods, bool newValue )
|
||||
public void SetMultipleModStates( IEnumerable< Mod2 > mods, bool newValue )
|
||||
{
|
||||
var changes = false;
|
||||
foreach( var mod in mods )
|
||||
|
|
@ -72,7 +72,7 @@ public partial class ModCollection
|
|||
|
||||
if( changes )
|
||||
{
|
||||
ModSettingChanged.Invoke( ModSettingChange.MultiEnableState, -1, -1, null, false );
|
||||
ModSettingChanged.Invoke( ModSettingChange.MultiEnableState, -1, -1, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,26 +85,21 @@ public partial class ModCollection
|
|||
{
|
||||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.Priority = newValue;
|
||||
ModSettingChanged.Invoke( ModSettingChange.Priority, idx, inheritance ? -1 : oldValue, null, false );
|
||||
ModSettingChanged.Invoke( ModSettingChange.Priority, idx, inheritance ? -1 : oldValue, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
// Set a given setting group settingName of mod idx to newValue if it differs from the current value and fix it if necessary.
|
||||
// If mod idx is currently inherited, stop the inheritance.
|
||||
public void SetModSetting( int idx, string settingName, int newValue )
|
||||
public void SetModSetting( int idx, int groupIdx, uint newValue )
|
||||
{
|
||||
var settings = _settings[ idx ] != null ? _settings[ idx ]!.Settings : this[ idx ].Settings?.Settings;
|
||||
var oldValue = settings != null
|
||||
? settings.TryGetValue( settingName, out var v ) ? v : newValue
|
||||
: Penumbra.ModManager.Mods[ idx ].Meta.Groups.ContainsKey( settingName )
|
||||
? 0
|
||||
: newValue;
|
||||
var oldValue = settings?[ groupIdx ] ?? 0;
|
||||
if( oldValue != newValue )
|
||||
{
|
||||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.Settings[ settingName ] = newValue;
|
||||
_settings[ idx ]!.FixSpecificSetting( settingName, Penumbra.ModManager.Mods[ idx ].Meta );
|
||||
ModSettingChanged.Invoke( ModSettingChange.Setting, idx, inheritance ? -1 : oldValue, settingName, false );
|
||||
_settings[ idx ]!.SetValue( Penumbra.ModManager.Mods[ idx ], groupIdx, newValue );
|
||||
ModSettingChanged.Invoke( ModSettingChange.Setting, idx, inheritance ? -1 : ( int )oldValue, groupIdx, false );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +107,7 @@ public partial class ModCollection
|
|||
// If type == Setting, settingName should be a valid setting for that mod, otherwise it will be ignored.
|
||||
// The setting will also be automatically fixed if it is invalid for that setting group.
|
||||
// For boolean parameters, newValue == 0 will be treated as false and != 0 as true.
|
||||
public void ChangeModSetting( ModSettingChange type, int idx, int newValue, string? settingName = null )
|
||||
public void ChangeModSetting( ModSettingChange type, int idx, int newValue, int groupIdx )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
|
|
@ -126,7 +121,7 @@ public partial class ModCollection
|
|||
SetModPriority( idx, newValue );
|
||||
break;
|
||||
case ModSettingChange.Setting:
|
||||
SetModSetting( idx, settingName ?? string.Empty, newValue );
|
||||
SetModSetting( idx, groupIdx, ( uint )newValue );
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException( nameof( type ), type, null );
|
||||
}
|
||||
|
|
@ -142,11 +137,11 @@ public partial class ModCollection
|
|||
return false;
|
||||
}
|
||||
|
||||
_settings[ idx ] = inherit ? null : this[ idx ].Settings ?? ModSettings.DefaultSettings( Penumbra.ModManager.Mods[ idx ].Meta );
|
||||
_settings[ idx ] = inherit ? null : this[ idx ].Settings ?? ModSettings2.DefaultSettings( Penumbra.ModManager.Mods[ idx ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SaveOnChange( ModSettingChange _1, int _2, int _3, string? _4, bool inherited )
|
||||
private void SaveOnChange( ModSettingChange _1, int _2, int _3, int _4, bool inherited )
|
||||
=> SaveOnChange( inherited );
|
||||
|
||||
private void SaveOnChange( bool inherited )
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public partial class ModCollection
|
|||
if( settings != null )
|
||||
{
|
||||
j.WritePropertyName( Penumbra.ModManager[ i ].BasePath.Name );
|
||||
x.Serialize( j, settings );
|
||||
x.Serialize( j, new ModSettings2.SavedSettings( settings, Penumbra.ModManager[ i ] ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +111,8 @@ public partial class ModCollection
|
|||
var name = obj[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty;
|
||||
var version = obj[ nameof( Version ) ]?.ToObject< int >() ?? 0;
|
||||
// Custom deserialization that is converted with the constructor.
|
||||
var settings = obj[ nameof( Settings ) ]?.ToObject< Dictionary< string, ModSettings > >()
|
||||
?? new Dictionary< string, ModSettings >();
|
||||
var settings = obj[ nameof( Settings ) ]?.ToObject< Dictionary< string, ModSettings2.SavedSettings > >()
|
||||
?? new Dictionary< string, ModSettings2.SavedSettings >();
|
||||
inheritance = obj[ nameof( Inheritance ) ]?.ToObject< List< string > >() ?? ( IReadOnlyList< string > )Array.Empty< string >();
|
||||
|
||||
return new ModCollection( name, version, settings );
|
||||
|
|
|
|||
|
|
@ -71,11 +71,11 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
// Carry changes in collections inherited from forward if they are relevant for this collection.
|
||||
private void OnInheritedModSettingChange( ModSettingChange type, int modIdx, int oldValue, string? optionName, bool _ )
|
||||
private void OnInheritedModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
|
||||
{
|
||||
if( _settings[ modIdx ] == null )
|
||||
{
|
||||
ModSettingChanged.Invoke( type, modIdx, oldValue, optionName, true );
|
||||
ModSettingChanged.Invoke( type, modIdx, oldValue, groupIdx, true );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ public partial class ModCollection
|
|||
// Obtain the actual settings for a given mod via index.
|
||||
// Also returns the collection the settings are taken from.
|
||||
// If no collection provides settings for this mod, this collection is returned together with null.
|
||||
public (ModSettings? Settings, ModCollection Collection) this[ Index idx ]
|
||||
public (ModSettings2? Settings, ModCollection Collection) this[ Index idx ]
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,10 +45,13 @@ public sealed partial class ModCollection
|
|||
}
|
||||
|
||||
// We treat every completely defaulted setting as inheritance-ready.
|
||||
private static bool SettingIsDefaultV0( ModSettings? setting )
|
||||
private static bool SettingIsDefaultV0( ModSettings2.SavedSettings setting )
|
||||
=> setting is { Enabled: false, Priority: 0 } && setting.Settings.Values.All( s => s == 0 );
|
||||
|
||||
private static bool SettingIsDefaultV0( ModSettings2? setting )
|
||||
=> setting is { Enabled: false, Priority: 0 } && setting.Settings.All( s => s == 0 );
|
||||
}
|
||||
|
||||
internal static ModCollection MigrateFromV0( string name, Dictionary< string, ModSettings > allSettings )
|
||||
internal static ModCollection MigrateFromV0( string name, Dictionary< string, ModSettings2.SavedSettings > allSettings )
|
||||
=> new(name, 0, allSettings);
|
||||
}
|
||||
|
|
@ -27,17 +27,17 @@ public partial class ModCollection
|
|||
|
||||
// If a ModSetting is null, it can be inherited from other collections.
|
||||
// If no collection provides a setting for the mod, it is just disabled.
|
||||
private readonly List< ModSettings? > _settings;
|
||||
private readonly List< ModSettings2? > _settings;
|
||||
|
||||
public IReadOnlyList< ModSettings? > Settings
|
||||
public IReadOnlyList< ModSettings2? > Settings
|
||||
=> _settings;
|
||||
|
||||
// Evaluates the settings along the whole inheritance tree.
|
||||
public IEnumerable< ModSettings? > ActualSettings
|
||||
public IEnumerable< ModSettings2? > ActualSettings
|
||||
=> Enumerable.Range( 0, _settings.Count ).Select( i => this[ i ].Settings );
|
||||
|
||||
// Settings for deleted mods will be kept via directory name.
|
||||
private readonly Dictionary< string, ModSettings > _unusedSettings;
|
||||
private readonly Dictionary< string, ModSettings2.SavedSettings > _unusedSettings;
|
||||
|
||||
// Constructor for duplication.
|
||||
private ModCollection( string name, ModCollection duplicate )
|
||||
|
|
@ -52,13 +52,13 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
// Constructor for reading from files.
|
||||
private ModCollection( string name, int version, Dictionary< string, ModSettings > allSettings )
|
||||
private ModCollection( string name, int version, Dictionary< string, ModSettings2.SavedSettings > allSettings )
|
||||
{
|
||||
Name = name;
|
||||
Version = version;
|
||||
_unusedSettings = allSettings;
|
||||
|
||||
_settings = new List< ModSettings? >();
|
||||
_settings = new List< ModSettings2? >();
|
||||
ApplyModSettings();
|
||||
|
||||
Migration.Migrate( this );
|
||||
|
|
@ -68,7 +68,7 @@ public partial class ModCollection
|
|||
|
||||
// Create a new, unique empty collection of a given name.
|
||||
public static ModCollection CreateNewEmpty( string name )
|
||||
=> new(name, CurrentVersion, new Dictionary< string, ModSettings >());
|
||||
=> new(name, CurrentVersion, new Dictionary< string, ModSettings2.SavedSettings >());
|
||||
|
||||
// Duplicate the calling collection to a new, unique collection of a given name.
|
||||
public ModCollection Duplicate( string name )
|
||||
|
|
@ -86,26 +86,27 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
// Add settings for a new appended mod, by checking if the mod had settings from a previous deletion.
|
||||
private void AddMod( Mod mod )
|
||||
private bool AddMod( Mod2 mod )
|
||||
{
|
||||
if( _unusedSettings.TryGetValue( mod.BasePath.Name, out var settings ) )
|
||||
if( _unusedSettings.TryGetValue( mod.BasePath.Name, out var save ) )
|
||||
{
|
||||
var ret = save.ToSettings( mod, out var settings );
|
||||
_settings.Add( settings );
|
||||
_unusedSettings.Remove( mod.BasePath.Name );
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.Add( null );
|
||||
}
|
||||
|
||||
_settings.Add( null );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move settings from the current mod list to the unused mod settings.
|
||||
private void RemoveMod( Mod mod, int idx )
|
||||
private void RemoveMod( Mod2 mod, int idx )
|
||||
{
|
||||
var settings = _settings[ idx ];
|
||||
if( settings != null )
|
||||
{
|
||||
_unusedSettings.Add( mod.BasePath.Name, settings );
|
||||
_unusedSettings.Add( mod.BasePath.Name, new ModSettings2.SavedSettings( settings, mod ) );
|
||||
}
|
||||
|
||||
_settings.RemoveAt( idx );
|
||||
|
|
@ -126,7 +127,7 @@ public partial class ModCollection
|
|||
{
|
||||
foreach( var (mod, setting) in Penumbra.ModManager.Zip( _settings ).Where( s => s.Second != null ) )
|
||||
{
|
||||
_unusedSettings[ mod.BasePath.Name ] = setting!;
|
||||
_unusedSettings[ mod.BasePath.Name ] = new ModSettings2.SavedSettings( setting!, mod );
|
||||
}
|
||||
|
||||
_settings.Clear();
|
||||
|
|
@ -137,22 +138,7 @@ public partial class ModCollection
|
|||
private void ApplyModSettings()
|
||||
{
|
||||
_settings.Capacity = Math.Max( _settings.Capacity, Penumbra.ModManager.Count );
|
||||
var changes = false;
|
||||
foreach( var mod in Penumbra.ModManager )
|
||||
{
|
||||
if( _unusedSettings.TryGetValue( mod.BasePath.Name, out var s ) )
|
||||
{
|
||||
changes |= s.FixInvalidSettings( mod.Meta );
|
||||
_settings.Add( s );
|
||||
_unusedSettings.Remove( mod.BasePath.Name );
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.Add( null );
|
||||
}
|
||||
}
|
||||
|
||||
if( changes )
|
||||
if( Penumbra.ModManager.Aggregate( false, ( current, mod ) => current | AddMod( mod ) ) )
|
||||
{
|
||||
Save();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue