mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
tmp
This commit is contained in:
parent
0108e51636
commit
85fb98b557
15 changed files with 230 additions and 293 deletions
|
|
@ -1,6 +1,5 @@
|
|||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.Meta.Manager;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using System;
|
||||
|
|
@ -9,24 +8,24 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.Mods.Manager;
|
||||
|
||||
using Penumbra.Mods.Manager;
|
||||
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public record struct ModPath(IMod Mod, FullPath Path);
|
||||
public record ModConflicts(IMod Mod2, List<object> Conflicts, bool HasPriority, bool Solved);
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// The Cache contains all required temporary data to use a collection.
|
||||
/// It will only be setup if a collection gets activated in any way.
|
||||
/// It will only be setup if a collection gets activated in any way.
|
||||
/// </summary>
|
||||
public class ModCollectionCache : IDisposable
|
||||
{
|
||||
private readonly ModCollection _collection;
|
||||
private readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
|
||||
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
|
||||
public readonly MetaManager MetaManipulations;
|
||||
private readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new();
|
||||
private readonly ModCollection _collection;
|
||||
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
|
||||
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
|
||||
public readonly MetaCache MetaManipulations;
|
||||
public readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new();
|
||||
|
||||
public IEnumerable<SingleArray<ModConflicts>> AllConflicts
|
||||
=> _conflicts.Values;
|
||||
|
|
@ -49,8 +48,8 @@ public class ModCollectionCache : IDisposable
|
|||
// The cache reacts through events on its collection changing.
|
||||
public ModCollectionCache(ModCollection collection)
|
||||
{
|
||||
_collection = collection;
|
||||
MetaManipulations = new MetaManager(_collection);
|
||||
_collection = collection;
|
||||
MetaManipulations = new MetaCache(_collection);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -62,15 +61,11 @@ public class ModCollectionCache : IDisposable
|
|||
public FullPath? ResolvePath(Utf8GamePath gameResourcePath)
|
||||
{
|
||||
if (!ResolvedFiles.TryGetValue(gameResourcePath, out var candidate))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (candidate.Path.InternalName.Length > Utf8GamePath.MaxGamePathLength
|
||||
|| candidate.Path.IsRooted && !candidate.Path.Exists)
|
||||
{
|
||||
|| candidate.Path.IsRooted && !candidate.Path.Exists)
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate.Path;
|
||||
}
|
||||
|
|
@ -80,9 +75,7 @@ public class ModCollectionCache : IDisposable
|
|||
{
|
||||
var needle = localFilePath.FullName.ToLower();
|
||||
if (localFilePath.IsRooted)
|
||||
{
|
||||
needle = needle.Replace('/', '\\');
|
||||
}
|
||||
|
||||
var iterator = ResolvedFiles
|
||||
.Where(f => string.Equals(f.Value.Path.FullName, needle, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -90,9 +83,7 @@ public class ModCollectionCache : IDisposable
|
|||
|
||||
// For files that are not rooted, try to add themselves.
|
||||
if (!localFilePath.IsRooted && Utf8GamePath.FromString(localFilePath.FullName, out var utf8))
|
||||
{
|
||||
iterator = iterator.Prepend(utf8);
|
||||
}
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
|
@ -103,7 +94,7 @@ public class ModCollectionCache : IDisposable
|
|||
if (fullPaths.Count == 0)
|
||||
return Array.Empty<HashSet<Utf8GamePath>>();
|
||||
|
||||
var ret = new HashSet<Utf8GamePath>[fullPaths.Count];
|
||||
var ret = new HashSet<Utf8GamePath>[fullPaths.Count];
|
||||
var dict = new Dictionary<FullPath, int>(fullPaths.Count);
|
||||
foreach (var (path, idx) in fullPaths.WithIndex())
|
||||
{
|
||||
|
|
@ -116,43 +107,12 @@ public class ModCollectionCache : IDisposable
|
|||
foreach (var (game, full) in ResolvedFiles)
|
||||
{
|
||||
if (dict.TryGetValue(full.Path, out var idx))
|
||||
{
|
||||
ret[idx].Add(game);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void FullRecalculation(bool isDefault)
|
||||
{
|
||||
ResolvedFiles.Clear();
|
||||
MetaManipulations.Reset();
|
||||
_conflicts.Clear();
|
||||
|
||||
// Add all forced redirects.
|
||||
foreach (var tempMod in Penumbra.TempMods.ModsForAllCollections.Concat(
|
||||
Penumbra.TempMods.Mods.TryGetValue(_collection, out var list) ? list : Array.Empty<TemporaryMod>()))
|
||||
{
|
||||
AddMod(tempMod, false);
|
||||
}
|
||||
|
||||
foreach (var mod in Penumbra.ModManager)
|
||||
{
|
||||
AddMod(mod, false);
|
||||
}
|
||||
|
||||
AddMetaFiles();
|
||||
|
||||
++_collection.ChangeCounter;
|
||||
|
||||
if (isDefault && Penumbra.CharacterUtility.Ready && Penumbra.Config.EnableMods)
|
||||
{
|
||||
Penumbra.ResidentResources.Reload();
|
||||
MetaManipulations.SetFiles();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadMod(IMod mod, bool addMetaChanges)
|
||||
{
|
||||
RemoveMod(mod, addMetaChanges);
|
||||
|
|
@ -166,22 +126,16 @@ public class ModCollectionCache : IDisposable
|
|||
foreach (var (path, _) in mod.AllSubMods.SelectMany(s => s.Files.Concat(s.FileSwaps)))
|
||||
{
|
||||
if (!ResolvedFiles.TryGetValue(path, out var modPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modPath.Mod == mod)
|
||||
{
|
||||
ResolvedFiles.Remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var manipulation in mod.AllSubMods.SelectMany(s => s.Manipulations))
|
||||
{
|
||||
if (MetaManipulations.TryGetValue(manipulation, out var registeredMod) && registeredMod == mod)
|
||||
{
|
||||
MetaManipulations.RevertMod(manipulation);
|
||||
}
|
||||
}
|
||||
|
||||
_conflicts.Remove(mod);
|
||||
|
|
@ -195,13 +149,9 @@ public class ModCollectionCache : IDisposable
|
|||
{
|
||||
var newConflicts = Conflicts(conflict.Mod2).Remove(c => c.Mod2 == mod);
|
||||
if (newConflicts.Count > 0)
|
||||
{
|
||||
_conflicts[conflict.Mod2] = newConflicts;
|
||||
}
|
||||
else
|
||||
{
|
||||
_conflicts.Remove(conflict.Mod2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,16 +174,12 @@ public class ModCollectionCache : IDisposable
|
|||
{
|
||||
var settings = _collection[mod.Index].Settings;
|
||||
if (settings is not { Enabled: true })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending(g => g.Item1.Priority))
|
||||
{
|
||||
if (group.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var config = settings.Settings[groupIndex];
|
||||
switch (group.Type)
|
||||
|
|
@ -241,17 +187,15 @@ public class ModCollectionCache : IDisposable
|
|||
case GroupType.Single:
|
||||
AddSubMod(group[(int)config], mod);
|
||||
break;
|
||||
case GroupType.Multi:
|
||||
{
|
||||
foreach (var (option, _) in group.WithIndex()
|
||||
.Where(p => (1 << p.Item2 & config) != 0)
|
||||
.OrderByDescending(p => group.OptionPriority(p.Item2)))
|
||||
{
|
||||
AddSubMod(option, mod);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GroupType.Multi:
|
||||
{
|
||||
foreach (var (option, _) in group.WithIndex()
|
||||
.Where(p => ((1 << p.Item2) & config) != 0)
|
||||
.OrderByDescending(p => group.OptionPriority(p.Item2)))
|
||||
AddSubMod(option, mod);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -262,9 +206,7 @@ public class ModCollectionCache : IDisposable
|
|||
{
|
||||
++_collection.ChangeCounter;
|
||||
if (Penumbra.ModCaches[mod.Index].TotalManipulations > 0)
|
||||
{
|
||||
AddMetaFiles();
|
||||
}
|
||||
|
||||
if (_collection == Penumbra.CollectionManager.Active.Default && Penumbra.CharacterUtility.Ready && Penumbra.Config.EnableMods)
|
||||
{
|
||||
|
|
@ -278,14 +220,10 @@ public class ModCollectionCache : IDisposable
|
|||
private void AddSubMod(ISubMod subMod, IMod parentMod)
|
||||
{
|
||||
foreach (var (path, file) in subMod.Files.Concat(subMod.FileSwaps))
|
||||
{
|
||||
AddFile(path, file, parentMod);
|
||||
}
|
||||
|
||||
foreach (var manip in subMod.Manipulations)
|
||||
{
|
||||
AddManipulation(manip, parentMod);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a specific file redirection, handling potential conflicts.
|
||||
|
|
@ -295,26 +233,18 @@ public class ModCollectionCache : IDisposable
|
|||
private void AddFile(Utf8GamePath path, FullPath file, IMod mod)
|
||||
{
|
||||
if (!ModCollection.CheckFullPath(path, file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ResolvedFiles.TryAdd(path, new ModPath(mod, file)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var modPath = ResolvedFiles[path];
|
||||
// Lower prioritized option in the same mod.
|
||||
if (mod == modPath.Mod)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddConflict(path, mod, modPath.Mod))
|
||||
{
|
||||
ResolvedFiles[path] = new ModPath(mod, file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -327,9 +257,7 @@ public class ModCollectionCache : IDisposable
|
|||
if (c.Conflicts.Count == 0)
|
||||
{
|
||||
if (transitive)
|
||||
{
|
||||
RemoveEmptyConflicts(c.Mod2, Conflicts(c.Mod2), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -337,13 +265,9 @@ public class ModCollectionCache : IDisposable
|
|||
return false;
|
||||
});
|
||||
if (changedConflicts.Count == 0)
|
||||
{
|
||||
_conflicts.Remove(mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
_conflicts[mod] = changedConflicts;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new conflict between the added mod and the existing mod.
|
||||
|
|
@ -351,7 +275,7 @@ public class ModCollectionCache : IDisposable
|
|||
// Returns if the added mod takes priority before the existing mod.
|
||||
private bool AddConflict(object data, IMod addedMod, IMod existingMod)
|
||||
{
|
||||
var addedPriority = addedMod.Index >= 0 ? _collection[addedMod.Index].Settings!.Priority : addedMod.Priority;
|
||||
var addedPriority = addedMod.Index >= 0 ? _collection[addedMod.Index].Settings!.Priority : addedMod.Priority;
|
||||
var existingPriority = existingMod.Index >= 0 ? _collection[existingMod.Index].Settings!.Priority : existingMod.Priority;
|
||||
|
||||
if (existingPriority < addedPriority)
|
||||
|
|
@ -360,16 +284,14 @@ public class ModCollectionCache : IDisposable
|
|||
foreach (var conflict in tmpConflicts)
|
||||
{
|
||||
if (data is Utf8GamePath path && conflict.Conflicts.RemoveAll(p => p is Utf8GamePath x && x.Equals(path)) > 0
|
||||
|| data is MetaManipulation meta && conflict.Conflicts.RemoveAll(m => m is MetaManipulation x && x.Equals(meta)) > 0)
|
||||
{
|
||||
|| data is MetaManipulation meta && conflict.Conflicts.RemoveAll(m => m is MetaManipulation x && x.Equals(meta)) > 0)
|
||||
AddConflict(data, addedMod, conflict.Mod2);
|
||||
}
|
||||
}
|
||||
|
||||
RemoveEmptyConflicts(existingMod, tmpConflicts, true);
|
||||
}
|
||||
|
||||
var addedConflicts = Conflicts(addedMod);
|
||||
var addedConflicts = Conflicts(addedMod);
|
||||
var existingConflicts = Conflicts(existingMod);
|
||||
if (addedConflicts.FindFirst(c => c.Mod2 == existingMod, out var oldConflicts))
|
||||
{
|
||||
|
|
@ -404,36 +326,23 @@ public class ModCollectionCache : IDisposable
|
|||
|
||||
// Lower prioritized option in the same mod.
|
||||
if (mod == existingMod)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddConflict(manip, mod, existingMod))
|
||||
{
|
||||
MetaManipulations.ApplyMod(manip, mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add all necessary meta file redirects.
|
||||
private void AddMetaFiles()
|
||||
public void AddMetaFiles()
|
||||
=> MetaManipulations.SetImcFiles();
|
||||
|
||||
// Increment the counter to ensure new files are loaded after applying meta changes.
|
||||
private void IncrementCounter()
|
||||
{
|
||||
++_collection.ChangeCounter;
|
||||
Penumbra.CharacterUtility.LoadingFinished -= IncrementCounter;
|
||||
}
|
||||
|
||||
|
||||
// Identify and record all manipulated objects for this entire collection.
|
||||
private void SetChangedItems()
|
||||
{
|
||||
if (_changedItemsSaveCounter == _collection.ChangeCounter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -442,24 +351,18 @@ public class ModCollectionCache : IDisposable
|
|||
// Skip IMCs because they would result in far too many false-positive items,
|
||||
// since they are per set instead of per item-slot/item/variant.
|
||||
var identifier = Penumbra.Identifier;
|
||||
var items = new SortedList<string, object?>(512);
|
||||
var items = new SortedList<string, object?>(512);
|
||||
|
||||
void AddItems(IMod mod)
|
||||
{
|
||||
foreach (var (name, obj) in items)
|
||||
{
|
||||
if (!_changedItems.TryGetValue(name, out var data))
|
||||
{
|
||||
_changedItems.Add(name, (new SingleArray<IMod>(mod), obj));
|
||||
}
|
||||
else if (!data.Item1.Contains(mod))
|
||||
{
|
||||
_changedItems[name] = (data.Item1.Append(mod), obj is int x && data.Item2 is int y ? x + y : obj);
|
||||
}
|
||||
else if (obj is int x && data.Item2 is int y)
|
||||
{
|
||||
_changedItems[name] = (data.Item1, x + y);
|
||||
}
|
||||
}
|
||||
|
||||
items.Clear();
|
||||
|
|
@ -482,4 +385,4 @@ public class ModCollectionCache : IDisposable
|
|||
Penumbra.Log.Error($"Unknown Error:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +1,53 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.Api;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections.Cache;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Collections.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollection, ModCollectionCache>
|
||||
public class CollectionCacheManager : IDisposable
|
||||
{
|
||||
private readonly ActiveCollections _active;
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CharacterUtility _characterUtility;
|
||||
private readonly FrameworkManager _framework;
|
||||
private readonly ActiveCollections _active;
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CharacterUtility _characterUtility;
|
||||
private readonly TempModManager _tempMods;
|
||||
private readonly ModStorage _modStorage;
|
||||
private readonly ModCacheManager _modCaches;
|
||||
private readonly Configuration _config;
|
||||
private readonly ResidentResourceManager _resources;
|
||||
|
||||
private readonly List<(ModCollectionCache, int ChangeCounter)>
|
||||
private readonly Dictionary<ModCollection, ModCollectionCache> _cache = new();
|
||||
private readonly Dictionary<ModCollection, CollectionCache> _caches = new();
|
||||
|
||||
public int Count
|
||||
=> _cache.Count;
|
||||
=> _caches.Count;
|
||||
|
||||
public IEnumerator<KeyValuePair<ModCollection, ModCollectionCache>> GetEnumerator()
|
||||
=> _cache.GetEnumerator();
|
||||
public IEnumerable<(ModCollection Collection, CollectionCache Cache)> Active
|
||||
=> _caches.Where(c => c.Key.Index > ModCollection.Empty.Index).Select(p => (p.Key, p.Value));
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public bool ContainsKey(ModCollection key)
|
||||
=> _cache.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(ModCollection key, [NotNullWhen(true)] out ModCollectionCache? value)
|
||||
=> _cache.TryGetValue(key, out value);
|
||||
|
||||
public ModCollectionCache this[ModCollection key]
|
||||
=> _cache[key];
|
||||
|
||||
public IEnumerable<ModCollection> Keys
|
||||
=> _cache.Keys;
|
||||
|
||||
public IEnumerable<ModCollectionCache> Values
|
||||
=> _cache.Values;
|
||||
|
||||
public IEnumerable<ModCollection> Active
|
||||
=> _cache.Keys.Where(c => c.Index > ModCollection.Empty.Index);
|
||||
|
||||
public CollectionCacheManager(ActiveCollections active, CommunicatorService communicator, CharacterUtility characterUtility)
|
||||
public CollectionCacheManager(FrameworkManager framework, ActiveCollections active, CommunicatorService communicator,
|
||||
CharacterUtility characterUtility, TempModManager tempMods, ModStorage modStorage, Configuration config,
|
||||
ResidentResourceManager resources, ModCacheManager modCaches)
|
||||
{
|
||||
_framework = framework;
|
||||
_active = active;
|
||||
_communicator = communicator;
|
||||
_characterUtility = characterUtility;
|
||||
_tempMods = tempMods;
|
||||
_modStorage = modStorage;
|
||||
_config = config;
|
||||
_resources = resources;
|
||||
_modCaches = modCaches;
|
||||
|
||||
_communicator.CollectionChange.Subscribe(OnCollectionChange);
|
||||
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
|
||||
|
|
@ -82,50 +74,103 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
_characterUtility.LoadingFinished -= IncrementCounters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache handling. Usually recreate caches on the next framework tick,
|
||||
/// but at launch create all of them at once.
|
||||
/// </summary>
|
||||
public void CreateNecessaryCaches()
|
||||
/// <summary> Only creates a new cache, does not update an existing one. </summary>
|
||||
public bool CreateCache(ModCollection collection)
|
||||
{
|
||||
var tasks = _active.SpecialAssignments.Select(p => p.Value)
|
||||
.Concat(_active.Individuals.Select(p => p.Collection))
|
||||
.Prepend(_active.Current)
|
||||
.Prepend(_active.Default)
|
||||
.Prepend(_active.Interface)
|
||||
.Distinct()
|
||||
.Select(c => Task.Run(() => c.CalculateEffectiveFileListInternal(c == _active.Default)))
|
||||
.ToArray();
|
||||
if (_caches.ContainsKey(collection) || collection.Index == ModCollection.Empty.Index)
|
||||
return false;
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
var cache = new CollectionCache(collection);
|
||||
_caches.Add(collection, cache);
|
||||
collection._cache = cache;
|
||||
Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}.");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the effective file list for the given cache.
|
||||
/// Does not create caches.
|
||||
/// </summary>
|
||||
public void CalculateEffectiveFileList(ModCollection collection)
|
||||
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Name,
|
||||
() => CalculateEffectiveFileListInternal(collection));
|
||||
|
||||
private void CalculateEffectiveFileListInternal(ModCollection collection)
|
||||
{
|
||||
// Skip the empty collection.
|
||||
if (collection.Index == 0)
|
||||
return;
|
||||
|
||||
Penumbra.Log.Debug($"[{Thread.CurrentThread.ManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName}");
|
||||
if (!_caches.TryGetValue(collection, out var cache))
|
||||
{
|
||||
Penumbra.Log.Error(
|
||||
$"[{Thread.CurrentThread.ManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName} failed, no cache exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
FullRecalculation(collection, cache);
|
||||
|
||||
Penumbra.Log.Debug(
|
||||
$"[{Thread.CurrentThread.ManagedThreadId}] Recalculation of effective file list for {collection.AnonymizedName} finished.");
|
||||
}
|
||||
|
||||
private void FullRecalculation(ModCollection collection, CollectionCache cache)
|
||||
{
|
||||
cache.ResolvedFiles.Clear();
|
||||
cache.MetaManipulations.Reset();
|
||||
cache._conflicts.Clear();
|
||||
|
||||
// Add all forced redirects.
|
||||
foreach (var tempMod in _tempMods.ModsForAllCollections.Concat(
|
||||
_tempMods.Mods.TryGetValue(collection, out var list) ? list : Array.Empty<TemporaryMod>()))
|
||||
cache.AddMod(tempMod, false);
|
||||
|
||||
foreach (var mod in _modStorage)
|
||||
cache.AddMod(mod, false);
|
||||
|
||||
cache.AddMetaFiles();
|
||||
|
||||
++collection.ChangeCounter;
|
||||
|
||||
if (_active.Default != collection || !_characterUtility.Ready || !_config.EnableMods)
|
||||
return;
|
||||
|
||||
_resources.Reload();
|
||||
cache.MetaManipulations.SetFiles();
|
||||
}
|
||||
|
||||
private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? newCollection, string displayName)
|
||||
{
|
||||
if (type is CollectionType.Inactive)
|
||||
return;
|
||||
|
||||
var isDefault = type is CollectionType.Default;
|
||||
if (newCollection?.Index > ModCollection.Empty.Index)
|
||||
if (type is CollectionType.Temporary)
|
||||
{
|
||||
newCollection.CreateCache(isDefault);
|
||||
_cache.TryAdd(newCollection, newCollection._cache!);
|
||||
}
|
||||
if (newCollection != null && CreateCache(newCollection))
|
||||
CalculateEffectiveFileList(newCollection);
|
||||
|
||||
RemoveCache(old);
|
||||
if (old != null)
|
||||
ClearCache(old);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveCache(old);
|
||||
|
||||
if (type is not CollectionType.Inactive && newCollection != null && newCollection.Index != 0 && CreateCache(newCollection))
|
||||
CalculateEffectiveFileList(newCollection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnModChangeRemoval(ModPathChangeType type, Mod mod, DirectoryInfo? oldModPath, DirectoryInfo? newModPath)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModPathChangeType.Deleted:
|
||||
case ModPathChangeType.StartingReload:
|
||||
foreach (var collection in _cache.Keys.Where(c => c[mod.Index].Settings?.Enabled == true))
|
||||
foreach (var collection in _caches.Keys.Where(c => c[mod.Index].Settings?.Enabled == true))
|
||||
collection._cache!.RemoveMod(mod, true);
|
||||
break;
|
||||
case ModPathChangeType.Moved:
|
||||
foreach (var collection in _cache.Keys.Where(c => c.HasCache && c[mod.Index].Settings?.Enabled == true))
|
||||
foreach (var collection in _caches.Keys.Where(c => c.HasCache && c[mod.Index].Settings?.Enabled == true))
|
||||
collection._cache!.ReloadMod(mod, true);
|
||||
break;
|
||||
}
|
||||
|
|
@ -136,13 +181,13 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
if (type is not (ModPathChangeType.Added or ModPathChangeType.Reloaded))
|
||||
return;
|
||||
|
||||
foreach (var collection in _cache.Keys.Where(c => c[mod.Index].Settings?.Enabled == true))
|
||||
foreach (var collection in _caches.Keys.Where(c => c[mod.Index].Settings?.Enabled == true))
|
||||
collection._cache!.AddMod(mod, true);
|
||||
}
|
||||
|
||||
/// <summary> Apply a mod change to all collections with a cache. </summary>
|
||||
private void OnGlobalModChange(TemporaryMod mod, bool created, bool removed)
|
||||
=> TempModManager.OnGlobalModChange(_cache.Keys, mod, created, removed);
|
||||
=> TempModManager.OnGlobalModChange(_caches.Keys, mod, created, removed);
|
||||
|
||||
/// <summary> Remove a cache from a collection if it is active. </summary>
|
||||
private void RemoveCache(ModCollection? collection)
|
||||
|
|
@ -154,10 +199,7 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
&& collection.Index != _active.Current.Index
|
||||
&& _active.SpecialAssignments.All(c => c.Value.Index != collection.Index)
|
||||
&& _active.Individuals.All(c => c.Collection.Index != collection.Index))
|
||||
{
|
||||
_cache.Remove(collection);
|
||||
collection.ClearCache();
|
||||
}
|
||||
ClearCache(collection);
|
||||
}
|
||||
|
||||
/// <summary> Prepare Changes by removing mods from caches with collections or add or reload mods. </summary>
|
||||
|
|
@ -165,7 +207,7 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
{
|
||||
if (type is ModOptionChangeType.PrepareChange)
|
||||
{
|
||||
foreach (var collection in _cache.Keys.Where(collection => collection[mod.Index].Settings is { Enabled: true }))
|
||||
foreach (var collection in _caches.Keys.Where(collection => collection[mod.Index].Settings is { Enabled: true }))
|
||||
collection._cache!.RemoveMod(mod, false);
|
||||
|
||||
return;
|
||||
|
|
@ -176,7 +218,7 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
if (!recomputeList)
|
||||
return;
|
||||
|
||||
foreach (var collection in _cache.Keys.Where(collection => collection[mod.Index].Settings is { Enabled: true }))
|
||||
foreach (var collection in _caches.Keys.Where(collection => collection[mod.Index].Settings is { Enabled: true }))
|
||||
{
|
||||
if (reload)
|
||||
collection._cache!.ReloadMod(mod, true);
|
||||
|
|
@ -188,45 +230,45 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
/// <summary> Increment the counter to ensure new files are loaded after applying meta changes. </summary>
|
||||
private void IncrementCounters()
|
||||
{
|
||||
foreach (var (collection, _) in _cache)
|
||||
foreach (var (collection, _) in _caches)
|
||||
++collection.ChangeCounter;
|
||||
_characterUtility.LoadingFinished -= IncrementCounters;
|
||||
}
|
||||
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool _)
|
||||
{
|
||||
if (collection._cache == null)
|
||||
if (!_caches.TryGetValue(collection, out var cache))
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ModSettingChange.Inheritance:
|
||||
collection._cache.ReloadMod(mod!, true);
|
||||
cache.ReloadMod(mod!, true);
|
||||
break;
|
||||
case ModSettingChange.EnableState:
|
||||
if (oldValue == 0)
|
||||
collection._cache.AddMod(mod!, true);
|
||||
cache.AddMod(mod!, true);
|
||||
else if (oldValue == 1)
|
||||
collection._cache.RemoveMod(mod!, true);
|
||||
cache.RemoveMod(mod!, true);
|
||||
else if (collection[mod!.Index].Settings?.Enabled == true)
|
||||
collection._cache.ReloadMod(mod!, true);
|
||||
cache.ReloadMod(mod!, true);
|
||||
else
|
||||
collection._cache.RemoveMod(mod!, true);
|
||||
cache.RemoveMod(mod!, true);
|
||||
|
||||
break;
|
||||
case ModSettingChange.Priority:
|
||||
if (collection._cache.Conflicts(mod!).Count > 0)
|
||||
collection._cache.ReloadMod(mod!, true);
|
||||
if (cache.Conflicts(mod!).Count > 0)
|
||||
cache.ReloadMod(mod!, true);
|
||||
|
||||
break;
|
||||
case ModSettingChange.Setting:
|
||||
if (collection[mod!.Index].Settings?.Enabled == true)
|
||||
collection._cache.ReloadMod(mod!, true);
|
||||
cache.ReloadMod(mod!, true);
|
||||
|
||||
break;
|
||||
case ModSettingChange.MultiInheritance:
|
||||
case ModSettingChange.MultiEnableState:
|
||||
collection._cache.FullRecalculation(collection == _active.Default);
|
||||
FullRecalculation(collection, cache);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -236,5 +278,37 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
|
|||
/// just recompute everything.
|
||||
/// </summary>
|
||||
private void OnCollectionInheritanceChange(ModCollection collection, bool _)
|
||||
=> collection._cache?.FullRecalculation(collection == _active.Default);
|
||||
{
|
||||
if (_caches.TryGetValue(collection, out var cache))
|
||||
FullRecalculation(collection, cache);
|
||||
}
|
||||
|
||||
/// <summary> Clear the current cache of a collection. </summary>
|
||||
private void ClearCache(ModCollection collection)
|
||||
{
|
||||
if (!_caches.Remove(collection, out var cache))
|
||||
return;
|
||||
|
||||
cache.Dispose();
|
||||
collection._cache = null;
|
||||
Penumbra.Log.Verbose($"Cleared cache of collection {collection.AnonymizedName}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache handling. Usually recreate caches on the next framework tick,
|
||||
/// but at launch create all of them at once.
|
||||
/// </summary>
|
||||
private void CreateNecessaryCaches()
|
||||
{
|
||||
var tasks = _active.SpecialAssignments.Select(p => p.Value)
|
||||
.Concat(_active.Individuals.Select(p => p.Collection))
|
||||
.Prepend(_active.Current)
|
||||
.Prepend(_active.Default)
|
||||
.Prepend(_active.Interface)
|
||||
.Distinct()
|
||||
.Select(c => CreateCache(c) ? Task.Run(() => CalculateEffectiveFileListInternal(c)) : Task.CompletedTask)
|
||||
.ToArray();
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ using Penumbra.Interop.Structs;
|
|||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private CmpFile? _cmpFile = null;
|
||||
private readonly List< RspManipulation > _cmpManipulations = new();
|
||||
|
|
@ -9,9 +9,9 @@ using Penumbra.Interop.Structs;
|
|||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile[CharacterUtilityData.EqdpIndices.Length]; // TODO: female Hrothgar
|
||||
|
||||
|
|
@ -6,9 +6,9 @@ using Penumbra.Interop.Structs;
|
|||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private ExpandedEqpFile? _eqpFile = null;
|
||||
private readonly List< EqpManipulation > _eqpManipulations = new();
|
||||
|
|
@ -7,9 +7,9 @@ using Penumbra.Interop.Structs;
|
|||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private EstFile? _estFaceFile = null;
|
||||
private EstFile? _estHairFile = null;
|
||||
|
|
@ -6,9 +6,9 @@ using Penumbra.Interop.Structs;
|
|||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private ExpandedGmpFile? _gmpFile = null;
|
||||
private readonly List< GmpManipulation > _gmpManipulations = new();
|
||||
|
|
@ -6,9 +6,9 @@ using Penumbra.Meta.Files;
|
|||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager
|
||||
public partial class MetaCache
|
||||
{
|
||||
private readonly Dictionary< Utf8GamePath, ImcFile > _imcFiles = new();
|
||||
private readonly List< ImcManipulation > _imcManipulations = new();
|
||||
|
|
@ -4,16 +4,15 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OtterGui;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
|
||||
namespace Penumbra.Meta.Manager;
|
||||
namespace Penumbra.Collections.Cache;
|
||||
|
||||
public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaManipulation, IMod > >
|
||||
public partial class MetaCache : IDisposable, IEnumerable< KeyValuePair< MetaManipulation, IMod > >
|
||||
{
|
||||
private readonly Dictionary< MetaManipulation, IMod > _manipulations = new();
|
||||
private readonly ModCollection _collection;
|
||||
|
|
@ -33,7 +32,7 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM
|
|||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public MetaManager( ModCollection collection )
|
||||
public MetaCache( ModCollection collection )
|
||||
{
|
||||
_collection = collection;
|
||||
if( !Penumbra.CharacterUtility.Ready )
|
||||
|
|
@ -116,12 +115,12 @@ public partial class MetaManager : IDisposable, IEnumerable< KeyValuePair< MetaM
|
|||
// but they do require the file space to be ready.
|
||||
return manip.ManipulationType switch
|
||||
{
|
||||
MetaManipulation.Type.Eqp => RevertMod( manip.Eqp ),
|
||||
MetaManipulation.Type.Gmp => RevertMod( manip.Gmp ),
|
||||
MetaManipulation.Type.Eqdp => RevertMod( manip.Eqdp ),
|
||||
MetaManipulation.Type.Est => RevertMod( manip.Est ),
|
||||
MetaManipulation.Type.Rsp => RevertMod( manip.Rsp ),
|
||||
MetaManipulation.Type.Imc => RevertMod( manip.Imc ),
|
||||
MetaManipulation.Type.Eqp => RevertMod( (MetaManipulation)manip.Eqp ),
|
||||
MetaManipulation.Type.Gmp => RevertMod( (MetaManipulation)manip.Gmp ),
|
||||
MetaManipulation.Type.Eqdp => RevertMod( (MetaManipulation)manip.Eqdp ),
|
||||
MetaManipulation.Type.Est => RevertMod( (MetaManipulation)manip.Est ),
|
||||
MetaManipulation.Type.Rsp => RevertMod( (MetaManipulation)manip.Rsp ),
|
||||
MetaManipulation.Type.Imc => RevertMod( (MetaManipulation)manip.Imc ),
|
||||
MetaManipulation.Type.Unknown => false,
|
||||
_ => false,
|
||||
};
|
||||
|
|
@ -262,7 +262,7 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
|
||||
/// <summary>
|
||||
/// Load default, current, special, and character collections from config.
|
||||
/// Then create caches. If a collection does not exist anymore, reset it to an appropriate default.
|
||||
/// If a collection does not exist anymore, reset it to an appropriate default.
|
||||
/// </summary>
|
||||
private void LoadCollections()
|
||||
{
|
||||
|
|
@ -338,7 +338,7 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
configChanged |= ActiveCollectionMigration.MigrateIndividualCollections(_storage, Individuals, jObject);
|
||||
configChanged |= Individuals.ReadJObject(jObject[nameof(Individuals)] as JArray, _storage);
|
||||
|
||||
// Save any changes and create all required caches.
|
||||
// Save any changes.
|
||||
if (configChanged)
|
||||
_saveService.ImmediateSave(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using Penumbra.Collections.Cache;
|
||||
|
||||
namespace Penumbra.Collections.Manager;
|
||||
|
||||
public class CollectionManager
|
||||
|
|
|
|||
|
|
@ -54,9 +54,12 @@ public class TempCollectionManager : IDisposable
|
|||
GlobalChangeCounter = 0;
|
||||
var collection = ModCollection.CreateTemporary(name, ~Count, GlobalChangeCounter++);
|
||||
if (_customCollections.TryAdd(collection.Name.ToLowerInvariant(), collection))
|
||||
{
|
||||
// Temporary collection created.
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
|
||||
return collection.Name;
|
||||
}
|
||||
|
||||
collection.ClearCache();
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
|
@ -66,12 +69,12 @@ public class TempCollectionManager : IDisposable
|
|||
return false;
|
||||
|
||||
GlobalChangeCounter += Math.Max(collection.ChangeCounter + 1 - GlobalChangeCounter, 0);
|
||||
collection.ClearCache();
|
||||
for (var i = 0; i < Collections.Count; ++i)
|
||||
{
|
||||
if (Collections[i].Collection != collection)
|
||||
continue;
|
||||
|
||||
// Temporary collection assignment removed.
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
|
||||
Collections.Delete(i--);
|
||||
}
|
||||
|
|
@ -81,13 +84,13 @@ public class TempCollectionManager : IDisposable
|
|||
|
||||
public bool AddIdentifier(ModCollection collection, params ActorIdentifier[] identifiers)
|
||||
{
|
||||
if (Collections.Add(identifiers, collection))
|
||||
{
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
|
||||
return true;
|
||||
}
|
||||
if (!Collections.Add(identifiers, collection))
|
||||
return false;
|
||||
|
||||
// Temporary collection assignment added.
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AddIdentifier(string collectionName, params ActorIdentifier[] identifiers)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta.Manager;
|
||||
using Penumbra.Mods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -25,25 +24,6 @@ public partial class ModCollection
|
|||
public bool HasCache
|
||||
=> _cache != null;
|
||||
|
||||
/// <summary>
|
||||
/// Count the number of changes of the effective file list.
|
||||
/// This is used for material and imc changes.
|
||||
/// </summary>
|
||||
public int ChangeCounter { get; internal set; }
|
||||
|
||||
// Only create, do not update.
|
||||
internal void CreateCache(bool isDefault)
|
||||
{
|
||||
if (_cache != null)
|
||||
return;
|
||||
|
||||
CalculateEffectiveFileList(isDefault);
|
||||
Penumbra.Log.Verbose($"Created new cache for collection {Name}.");
|
||||
}
|
||||
|
||||
// Force an update with metadata for this cache.
|
||||
internal void ForceCacheUpdate()
|
||||
=> CalculateEffectiveFileList(this == Penumbra.CollectionManager.Active.Default);
|
||||
|
||||
// Handle temporary mods for this collection.
|
||||
public void Apply(TemporaryMod tempMod, bool created)
|
||||
|
|
@ -59,15 +39,6 @@ public partial class ModCollection
|
|||
_cache?.RemoveMod(tempMod, tempMod.TotalManipulations > 0);
|
||||
}
|
||||
|
||||
|
||||
// Clear the current cache.
|
||||
internal void ClearCache()
|
||||
{
|
||||
_cache?.Dispose();
|
||||
_cache = null;
|
||||
Penumbra.Log.Verbose($"Cleared cache of collection {Name}.");
|
||||
}
|
||||
|
||||
public IEnumerable<Utf8GamePath> ReverseResolvePath(FullPath path)
|
||||
=> _cache?.ReverseResolvePath(path) ?? Array.Empty<Utf8GamePath>();
|
||||
|
||||
|
|
@ -99,7 +70,7 @@ public partial class ModCollection
|
|||
=> _cache!.ResolvedFiles.Remove(path);
|
||||
|
||||
// Obtain data from the cache.
|
||||
internal MetaManager? MetaCache
|
||||
internal MetaCache? MetaCache
|
||||
=> _cache?.MetaManipulations;
|
||||
|
||||
public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out ImcFile? file)
|
||||
|
|
@ -123,25 +94,6 @@ public partial class ModCollection
|
|||
internal SingleArray<ModConflicts> Conflicts(Mod mod)
|
||||
=> _cache?.Conflicts(mod) ?? new SingleArray<ModConflicts>();
|
||||
|
||||
// Update the effective file list for the given cache.
|
||||
// Creates a cache if necessary.
|
||||
public void CalculateEffectiveFileList(bool isDefault)
|
||||
=> Penumbra.Framework.RegisterImportant(nameof(CalculateEffectiveFileList) + Name, () =>
|
||||
CalculateEffectiveFileListInternal(isDefault));
|
||||
|
||||
internal void CalculateEffectiveFileListInternal(bool isDefault)
|
||||
{
|
||||
// Skip the empty collection.
|
||||
if (Index == 0)
|
||||
return;
|
||||
|
||||
Penumbra.Log.Debug($"[{Thread.CurrentThread.ManagedThreadId}] Recalculating effective file list for {AnonymizedName}");
|
||||
_cache ??= new ModCollectionCache(this);
|
||||
_cache.FullRecalculation(isDefault);
|
||||
|
||||
Penumbra.Log.Debug($"[{Thread.CurrentThread.ManagedThreadId}] Recalculation of effective file list for {AnonymizedName} finished.");
|
||||
}
|
||||
|
||||
public void SetFiles()
|
||||
{
|
||||
if (_cache == null)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ public partial class ModCollection
|
|||
/// <summary> The index of the collection is set and kept up-to-date by the CollectionManager. </summary>
|
||||
public int Index { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Count the number of changes of the effective file list.
|
||||
/// This is used for material and imc changes.
|
||||
/// </summary>
|
||||
public int ChangeCounter { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
|
|
@ -123,7 +129,6 @@ public partial class ModCollection
|
|||
Debug.Assert(index < 0, "Temporary collection created with non-negative index.");
|
||||
var ret = new ModCollection(name, index, changeCounter, CurrentVersion, new List<ModSettings?>(), new List<ModCollection>(),
|
||||
new Dictionary<string, ModSettings.SavedSettings>());
|
||||
ret.CreateCache(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using Penumbra.Api.Enums;
|
|||
using Penumbra.UI;
|
||||
using Penumbra.Util;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Cache;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.Interop.ResourceLoading;
|
||||
|
|
@ -49,7 +50,6 @@ public class Penumbra : IDalamudPlugin
|
|||
public static CollectionManager CollectionManager { get; private set; } = null!;
|
||||
public static TempCollectionManager TempCollections { get; private set; } = null!;
|
||||
public static TempModManager TempMods { get; private set; } = null!;
|
||||
public static FrameworkManager Framework { get; private set; } = null!;
|
||||
public static ActorManager Actors { get; private set; } = null!;
|
||||
public static IObjectIdentifier Identifier { get; private set; } = null!;
|
||||
public static IGamePathParser GamePathParser { get; private set; } = null!;
|
||||
|
|
@ -81,7 +81,6 @@ public class Penumbra : IDalamudPlugin
|
|||
Config = _tmp.Services.GetRequiredService<Configuration>();
|
||||
CharacterUtility = _tmp.Services.GetRequiredService<CharacterUtility>();
|
||||
MetaFileManager = _tmp.Services.GetRequiredService<MetaFileManager>();
|
||||
Framework = _tmp.Services.GetRequiredService<FrameworkManager>();
|
||||
Actors = _tmp.Services.GetRequiredService<ActorService>().AwaitedService;
|
||||
Identifier = _tmp.Services.GetRequiredService<IdentifierService>().AwaitedService;
|
||||
GamePathParser = _tmp.Services.GetRequiredService<IGamePathParser>();
|
||||
|
|
@ -251,7 +250,7 @@ public class Penumbra : IDalamudPlugin
|
|||
return name + ':';
|
||||
}
|
||||
|
||||
void PrintCollection(ModCollection c)
|
||||
void PrintCollection(ModCollection c, CollectionCache _)
|
||||
=> sb.Append($"**Collection {c.AnonymizedName}**\n"
|
||||
+ $"> **`Inheritances: `** {c.DirectlyInheritsFrom.Count}\n"
|
||||
+ $"> **`Enabled Mods: `** {c.ActualSettings.Count(s => s is { Enabled: true })}\n"
|
||||
|
|
@ -260,7 +259,7 @@ public class Penumbra : IDalamudPlugin
|
|||
sb.AppendLine("**Collections**");
|
||||
sb.Append($"> **`#Collections: `** {CollectionManager.Storage.Count - 1}\n");
|
||||
sb.Append($"> **`#Temp Collections: `** {TempCollections.Count}\n");
|
||||
sb.Append($"> **`Active Collections: `** {CollectionManager.Caches.Count}\n");
|
||||
sb.Append($"> **`Active Collections: `** {CollectionManager.Caches.Count - TempCollections.Count}\n");
|
||||
sb.Append($"> **`Base Collection: `** {CollectionManager.Active.Default.AnonymizedName}\n");
|
||||
sb.Append($"> **`Interface Collection: `** {CollectionManager.Active.Interface.AnonymizedName}\n");
|
||||
sb.Append($"> **`Selected Collection: `** {CollectionManager.Active.Current.AnonymizedName}\n");
|
||||
|
|
@ -274,8 +273,8 @@ public class Penumbra : IDalamudPlugin
|
|||
foreach (var (name, id, collection) in CollectionManager.Active.Individuals.Assignments)
|
||||
sb.Append($"> **`{CharacterName(id[0], name),-30}`** {collection.AnonymizedName}\n");
|
||||
|
||||
foreach (var collection in CollectionManager.Caches.Active)
|
||||
PrintCollection(collection);
|
||||
foreach (var (collection, cache) in CollectionManager.Caches.Active)
|
||||
PrintCollection(collection, cache);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue